File indexing completed on 2024-12-01 07:38:56

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2005 Dario Massarin <nekkar@libero.it>
0004    Copyright (C) 2007 by Javier Goday <jgoday@gmail.com>
0005    Copyright (C) 2008 - 2009 by Lukas Appelhans <l.appelhans@gmx.de>
0006    Copyright (C) 2010 by Matthias Fuchs <mat69@gmx.net>
0007 
0008    This program is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 */
0013 
0014 #include "newtransferdialog.h"
0015 
0016 #include "core/filedeleter.h"
0017 #include "core/kget.h"
0018 #include "core/mostlocalurl.h"
0019 #include "core/plugin/transferfactory.h"
0020 #include "core/transfergrouphandler.h"
0021 #include "core/transfertreemodel.h"
0022 #include "core/urlchecker.h"
0023 #include "mainwindow.h"
0024 #include "settings.h"
0025 
0026 #include "kget_debug.h"
0027 #include <QDebug>
0028 
0029 #include <QApplication>
0030 #include <QClipboard>
0031 #include <QDir>
0032 #include <QFileInfo>
0033 #include <QTimer>
0034 
0035 #include <KColorScheme>
0036 #include <KLineEditUrlDropEventFilter>
0037 #include <KLocalizedString>
0038 #include <KWindowSystem>
0039 #include <QListWidgetItem>
0040 #include <QStandardPaths>
0041 
0042 Q_GLOBAL_STATIC(NewTransferDialogHandler, newTransferDialogHandler)
0043 
0044 NewTransferDialog::NewTransferDialog(QWidget *parent)
0045     : QDialog(parent)
0046     , m_window(nullptr)
0047     , m_existingTransfer(nullptr)
0048     , m_multiple(false)
0049     , m_overWriteSingle(false)
0050 {
0051     setModal(true);
0052     setWindowTitle(i18n("New Download"));
0053 
0054     ui.setupUi(this);
0055 
0056     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0057 
0058     // timer to avoid constant checking of the input
0059     m_timer = new QTimer(this);
0060     m_timer->setInterval(350);
0061     m_timer->setSingleShot(true);
0062     connect(m_timer, &QTimer::timeout, this, &NewTransferDialog::checkInput);
0063 
0064     const KColorScheme scheme = KColorScheme(QPalette::Active, KColorScheme::View);
0065     m_existingFileBackground = scheme.background(KColorScheme::NeutralBackground);
0066     m_normalBackground = scheme.background();
0067 
0068     // properties of the m_destRequester combobox
0069     auto *dropUrlEventFilter = new KLineEditUrlDropEventFilter(this);
0070     dropUrlEventFilter->installEventFilter(ui.destRequester->comboBox());
0071     ui.destRequester->comboBox()->setDuplicatesEnabled(false);
0072     ui.destRequester->comboBox()->setEditable(true);
0073     ui.destRequester->setAcceptMode(QFileDialog::AcceptSave);
0074 
0075     ui.errorWidget->setCloseButtonVisible(false);
0076 
0077     connect(ui.groupComboBox, &QComboBox::currentIndexChanged, this, &NewTransferDialog::setDefaultDestination);
0078 
0079     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::setDefaultDestination);
0080     connect(ui.destRequester, &KUrlRequester::textChanged, this, &NewTransferDialog::inputTimer);
0081     connect(ui.urlRequester, &QLineEdit::textChanged, this, &NewTransferDialog::inputTimer);
0082     connect(ui.listWidget, &QListWidget::itemChanged, this, &NewTransferDialog::inputTimer);
0083     connect(this, &QDialog::finished, this, &NewTransferDialog::slotFinished);
0084     connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0085     connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0086 }
0087 
0088 NewTransferDialog::~NewTransferDialog()
0089 {
0090 }
0091 
0092 void NewTransferDialog::setMultiple(bool useMultiple)
0093 {
0094     m_multiple = useMultiple;
0095 
0096     const Qt::Alignment alignment = Qt::AlignLeft | (m_multiple ? Qt::AlignTop : Qt::AlignVCenter);
0097     ui.urlLabel->setAlignment(alignment);
0098     ui.urlRequester->setVisible(!m_multiple);
0099     ui.listWidget->setVisible(m_multiple);
0100     ui.destRequester->setMode(m_multiple ? KFile::Directory : KFile::File);
0101 }
0102 
0103 void NewTransferDialog::clear()
0104 {
0105     ui.urlRequester->clear();
0106     ui.urlRequester->setFocus();
0107     ui.listWidget->clear();
0108     ui.destRequester->comboBox()->clear();
0109     ui.destRequester->clear();
0110     m_destination.clear();
0111     m_sources.clear();
0112     m_existingTransfer = nullptr;
0113     m_overWriteSingle = false;
0114 
0115     // add all destinations
0116     QStringList list;
0117     QString downloadPath = KGet::generalDestDir();
0118     if (!downloadPath.isEmpty()) {
0119         if (!downloadPath.endsWith('/')) {
0120             downloadPath.append('/');
0121         }
0122         list << downloadPath;
0123     }
0124     foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) {
0125         const QString folder = handler->defaultFolder();
0126         if (!folder.isEmpty()) {
0127             list << (folder.endsWith('/') ? folder : folder + '/');
0128         }
0129     }
0130 
0131     list.removeDuplicates();
0132     ui.destRequester->comboBox()->insertItems(0, list);
0133 
0134     // add all transfer groups
0135     ui.groupComboBox->clear();
0136     foreach (TransferGroupHandler *group, KGet::allTransferGroups()) {
0137         ui.groupComboBox->addItem(QIcon::fromTheme(group->iconName()), group->name());
0138     }
0139     ui.groupComboBox->setCurrentItem(Settings::lastGroup());
0140     if (ui.groupComboBox->currentIndex() == -1) {
0141         ui.groupComboBox->setCurrentIndex(0);
0142     }
0143 
0144     const bool multipleGroups = KGet::transferGroupNames().count();
0145     ui.groupComboBox->setVisible(multipleGroups);
0146     ui.groupLabel->setVisible(multipleGroups);
0147 }
0148 
0149 void NewTransferDialog::setSource(const QList<QUrl> &sources)
0150 {
0151     if (sources.isEmpty()) {
0152         return;
0153     }
0154 
0155     if (sources.count() == 1) {
0156         QUrl m_srcUrl = sources.first();
0157         ui.urlRequester->clear();
0158         if (m_srcUrl.isEmpty()) {
0159             m_srcUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed());
0160         }
0161 
0162         if (UrlChecker::checkSource(m_srcUrl) == UrlChecker::NoError) {
0163             ui.urlRequester->insert(m_srcUrl.toString());
0164         }
0165     } else {
0166         foreach (const QUrl &sourceUrl, sources) {
0167             if (sourceUrl.url()
0168                 != QUrl(sourceUrl.url())
0169                        .fileName()) { // TODO simplify, whatfor is this check anyway, shouldn't the sources be checked already and if not add this to UrlChecker
0170                 qCDebug(KGET_DEBUG) << "Insert" << sourceUrl;
0171                 auto *newItem = new QListWidgetItem(sourceUrl.toString(), ui.listWidget);
0172                 newItem->setCheckState(Qt::Checked);
0173             }
0174         }
0175     }
0176 
0177     const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(sources.first());
0178     if (!groups.isEmpty()) {
0179         ui.groupComboBox->setCurrentIndex(ui.groupComboBox->findText(groups.first()->name()));
0180     }
0181 }
0182 
0183 void NewTransferDialog::setDestinationFileName(const QString &filename)
0184 {
0185     ui.destRequester->setUrl(QUrl(ui.destRequester->url().adjusted(QUrl::RemoveFilename).toString() + filename));
0186 }
0187 
0188 void NewTransferDialog::setDestination()
0189 {
0190     // sets destRequester to either display the defaultFolder of group or the generalDestDir
0191     QString group = ui.groupComboBox->currentText();
0192     TransferGroupHandler *current = nullptr;
0193     foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) {
0194         if (handler->name() == group) {
0195             current = handler;
0196             break;
0197         }
0198     }
0199 
0200     if (current) {
0201         QString groupFolder = current->defaultFolder();
0202         if (groupFolder.isEmpty()) {
0203             groupFolder = KGet::generalDestDir();
0204         }
0205         if (!groupFolder.endsWith('/')) {
0206             groupFolder.append('/');
0207         }
0208         ui.destRequester->comboBox()->setCurrentItem(groupFolder, true);
0209     }
0210 }
0211 
0212 void NewTransferDialog::showDialog(QList<QUrl> list, const QString &suggestedFileName)
0213 {
0214     // TODO handle the case where for some there are suggested file names --> own file name column in multiple setting
0215     // the dialog is already in use, adapt it
0216     if (isVisible()) {
0217         list << m_sources;
0218     }
0219     clear(); // Let's clear the old stuff
0220     m_sources << list;
0221     UrlChecker::removeDuplicates(m_sources);
0222     const int size = m_sources.size();
0223     qCDebug(KGET_DEBUG) << "SET SOURCES " << m_sources << " MULTIPLE " << (size > 1);
0224     setMultiple(size > 1);
0225 
0226     if (size) {
0227         if (size == 1 && !suggestedFileName.isEmpty()) {
0228             setDestinationFileName(suggestedFileName);
0229         }
0230 
0231         setSource(m_sources);
0232     }
0233 
0234     prepareDialog();
0235 }
0236 
0237 void NewTransferDialog::setDefaultDestination()
0238 {
0239     // NOTE if the user enters a file name manually and the changes the group the manually entered file name will be overwritten
0240     setDestination();
0241 
0242     // set a file name
0243     if (!m_multiple) {
0244         const QUrl url(ui.urlRequester->text().trimmed());
0245         if ((UrlChecker::checkSource(url) == UrlChecker::NoError) && QFileInfo(ui.destRequester->url().toLocalFile()).isDir()) {
0246             setDestinationFileName(url.fileName());
0247         }
0248     }
0249 }
0250 
0251 void NewTransferDialog::prepareDialog()
0252 {
0253     qCDebug(KGET_DEBUG) << "Show the dialog!";
0254     show();
0255 }
0256 
0257 bool NewTransferDialog::isEmpty()
0258 {
0259     return (m_multiple ? !ui.listWidget->count() : ui.urlRequester->text().trimmed().isEmpty());
0260 }
0261 
0262 void NewTransferDialog::inputTimer()
0263 {
0264     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0265     m_timer->start();
0266 }
0267 
0268 void NewTransferDialog::checkInput()
0269 {
0270     qDebug() << "Check input";
0271     QUrl source = QUrl(ui.urlRequester->text().trimmed());
0272     const QUrl dest = ui.destRequester->url();
0273 
0274     // check the destination folder
0275     UrlChecker::UrlError error = UrlChecker::checkFolder(dest);
0276     const bool folderValid = (error == UrlChecker::NoError);
0277     bool destinationValid = false;
0278     QString infoText;
0279     QString warningText;
0280     if (!folderValid) {
0281         if (m_multiple) {
0282             infoText = UrlChecker::message(QUrl(), UrlChecker::Folder, error);
0283         } else {
0284             // might be a destination instead of a folder
0285             destinationValid = (UrlChecker::checkDestination(dest) == UrlChecker::NoError);
0286         }
0287     } else {
0288         m_destination = dest;
0289     }
0290 
0291     // check the source
0292     if (!m_multiple) {
0293         source = mostLocalUrl(source);
0294     }
0295     error = UrlChecker::checkSource(source);
0296     const bool sourceValid = (error == UrlChecker::NoError);
0297     if (!m_multiple && !sourceValid) {
0298         infoText = UrlChecker::message(QUrl(), UrlChecker::Source, error);
0299     }
0300 
0301     // check if any sources are checked and for existing transfers or destinations
0302     bool filesChecked = false;
0303     if (m_multiple && folderValid) {
0304         QListWidget *list = ui.listWidget;
0305 
0306         // check if some sources have been checked
0307         for (int i = 0; i < list->count(); ++i) {
0308             QListWidgetItem *item = list->item(i);
0309             if (item->checkState() == Qt::Checked) {
0310                 filesChecked = true;
0311                 break;
0312             }
0313         }
0314         if (!filesChecked) {
0315             infoText = i18n("Select at least one source url.");
0316         }
0317 
0318         // check if there are existing files
0319         if (filesChecked) {
0320             bool existingFile = false;
0321             for (int i = 0; i < list->count(); ++i) {
0322                 QListWidgetItem *item = list->item(i);
0323                 const QUrl source = QUrl(item->text());
0324                 const QUrl destUrl = UrlChecker::destUrl(dest, source);
0325                 if (UrlChecker::wouldOverwrite(source, destUrl)) {
0326                     item->setBackground(m_existingFileBackground);
0327                     existingFile = true;
0328                 } else {
0329                     item->setBackground(m_normalBackground);
0330                 }
0331             }
0332             if (existingFile) {
0333                 warningText = i18n("Files that exist already in the current folder have been marked."); // TODO better message
0334             }
0335         }
0336     }
0337 
0338     // single file
0339     UrlChecker::UrlWarning warning = UrlChecker::NoWarning;
0340     if (!m_multiple && sourceValid && (folderValid || destinationValid)) {
0341         m_destination = UrlChecker::destUrl(dest, source);
0342         // show only one message for existing transfers
0343         m_existingTransfer = UrlChecker::existingTransfer(source, UrlChecker::Source, &warning);
0344         if (m_existingTransfer) {
0345             warningText = UrlChecker::message(QUrl(), UrlChecker::Source, warning);
0346         } else {
0347             m_existingTransfer = UrlChecker::existingTransfer(m_destination, UrlChecker::Destination, &warning);
0348             if (m_existingTransfer) {
0349                 warningText = UrlChecker::message(QUrl(), UrlChecker::Destination, warning);
0350             }
0351         }
0352 
0353         if (UrlChecker::wouldOverwrite(QUrl(ui.urlRequester->text().trimmed()), m_destination)) {
0354             m_overWriteSingle = true;
0355             if (!warningText.isEmpty()) {
0356                 warningText += '\n';
0357             }
0358             warningText += UrlChecker::message(QUrl(), UrlChecker::Destination, UrlChecker::ExistingFile);
0359         } else {
0360             m_overWriteSingle = false;
0361         }
0362     }
0363 
0364     if (!infoText.isEmpty()) {
0365         setInformation(infoText);
0366     } else if (!warningText.isEmpty()) {
0367         setWarning(warningText);
0368     } else {
0369         ui.errorWidget->hide();
0370     }
0371 
0372     // activate the ok button
0373     if (m_multiple) {
0374         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(folderValid && filesChecked);
0375     } else {
0376         ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled((folderValid || destinationValid) && sourceValid);
0377     }
0378 
0379     qCDebug(KGET_DEBUG) << source << source.fileName() << dest << dest.fileName() << "Folder valid:" << folderValid << "Destination valid:" << destinationValid
0380                         << "Source valid:" << sourceValid;
0381 }
0382 
0383 void NewTransferDialog::slotFinished(int resultCode)
0384 {
0385     if (resultCode == QDialog::Accepted) {
0386         dialogAccepted();
0387     }
0388     clear();
0389 }
0390 
0391 void NewTransferDialog::dialogAccepted()
0392 {
0393     qCDebug(KGET_DEBUG) << "Dialog accepted.";
0394 
0395     // an existing transfer has been specified and since ok was clicked, it was chosen to be overwritten
0396     if (m_existingTransfer) {
0397         qCDebug(KGET_DEBUG) << "Removing existing transfer:" << m_existingTransfer;
0398         KGet::delTransfer(m_existingTransfer);
0399     }
0400 
0401     // set the last directory
0402     QString dir = m_destination.toLocalFile();
0403     if (!QFileInfo(dir).isDir()) {
0404         dir = m_destination.adjusted(QUrl::RemoveFilename).toLocalFile();
0405     }
0406     Settings::setLastDirectory(dir);
0407     Settings::self()->save();
0408 
0409     const QString group = ui.groupComboBox->currentText();
0410 
0411     /// add data to create transfers
0412     QList<KGet::TransferData> data;
0413     if (!m_multiple) {
0414         if (m_overWriteSingle) {
0415             qCDebug(KGET_DEBUG) << "Removing existing file:" << m_destination;
0416             // removes m_destination if it exists, do that here so that it is removed no matter if a transfer could be created or not
0417             // as the user decided to throw the file away
0418             FileDeleter::deleteFile(m_destination);
0419         }
0420 
0421         // sourceUrl is valid, has been checked before
0422         const QUrl sourceUrl = QUrl(ui.urlRequester->text().trimmed());
0423         qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << m_destination;
0424         data << KGet::TransferData(sourceUrl, m_destination, group, true);
0425     } else {
0426         QList<QUrl> list;
0427         for (int i = 0; i != ui.listWidget->count(); ++i) {
0428             QListWidgetItem *item = ui.listWidget->item(i);
0429 
0430             // find selected sources
0431             if (item->checkState() == Qt::Checked) {
0432                 // both sourceUrl and destUrl are valid, they have been tested in checkInput
0433                 const QUrl sourceUrl = QUrl(item->text().trimmed());
0434                 const QUrl destUrl = UrlChecker::destUrl(m_destination, sourceUrl);
0435                 qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << destUrl;
0436 
0437                 // file exists already, remove it
0438                 if (item->background() == m_existingFileBackground) {
0439                     qCDebug(KGET_DEBUG) << "Removing existing file:" << destUrl;
0440                     // removes destUrl if it exists, do that here so that it is removed no matter if a transfer could be created or not
0441                     // as the user decided to throw the file away
0442                     FileDeleter::deleteFile(destUrl);
0443                 }
0444 
0445                 data << KGet::TransferData(sourceUrl, destUrl, group, true);
0446             }
0447         }
0448     }
0449 
0450     if (!data.isEmpty()) {
0451         Settings::setLastGroup(ui.groupComboBox->currentText());
0452         KGet::createTransfers(data);
0453     }
0454 }
0455 
0456 void NewTransferDialog::setInformation(const QString &information)
0457 {
0458     ui.errorWidget->setMessageType(KMessageWidget::Information);
0459     ui.errorWidget->setText(information);
0460     ui.errorWidget->setVisible(!information.isEmpty());
0461 }
0462 
0463 void NewTransferDialog::setWarning(const QString &warning)
0464 {
0465     ui.errorWidget->setMessageType(KMessageWidget::Warning);
0466     ui.errorWidget->setText(warning);
0467     ui.errorWidget->setVisible(!warning.isEmpty());
0468 }
0469 
0470 /**
0471  * NOTE some checks in this class might seem redundant, though target is to display as few dialogs, and then preferable
0472  * the NewTransferDialog, to the user as possible i.e. if not enough information -- e.g. no destination folder
0473  * determinable, ...-- is present for a url or a group of urls they won't be added as transfer,
0474  * instead the NewTransferDialog will be shown
0475  *
0476  * This also tries to add as many transfers as possible with one run, to ensure a high speed
0477  */
0478 NewTransferDialogHandler::NewTransferDialogHandler(QObject *parent)
0479     : QObject(parent)
0480     , m_nextJobId(0)
0481 {
0482 }
0483 
0484 NewTransferDialogHandler::~NewTransferDialogHandler()
0485 {
0486 }
0487 
0488 void NewTransferDialogHandler::showNewTransferDialog(const QUrl &url)
0489 {
0490     showNewTransferDialog(url.isEmpty() ? QList<QUrl>() : QList<QUrl>() << url);
0491 }
0492 
0493 void NewTransferDialogHandler::showNewTransferDialog(QList<QUrl> urls)
0494 {
0495     if (urls.isEmpty()) {
0496         newTransferDialogHandler->createDialog(urls, QString());
0497         return;
0498     }
0499 
0500     QHash<int, UrlData>::iterator itUrls = newTransferDialogHandler->m_urls.insert(newTransferDialogHandler->m_nextJobId, UrlData());
0501     QString folder;
0502     QString suggestedFileName;
0503 
0504     /// Only two urls defined, check if second one is a path or a file name
0505     if (urls.count() == 2) {
0506         const QUrl lastUrl = urls.last();
0507         QDir dir(lastUrl.toLocalFile());
0508 
0509         // check if last url is a file path, either absolute or relative
0510         if (lastUrl.isLocalFile()) {
0511             if (QDir::isAbsolutePath(lastUrl.toLocalFile())) {
0512                 if (dir.exists()) {
0513                     // second url is a folder path
0514                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
0515                 } else {
0516                     // second url is a file path, use this one
0517                     folder = lastUrl.adjusted(QUrl::RemoveFilename).toString();
0518                     suggestedFileName = lastUrl.fileName();
0519                 }
0520                 urls.removeLast();
0521             } else {
0522                 // second url is just a file name
0523                 suggestedFileName = lastUrl.fileName();
0524                 urls.removeLast();
0525             }
0526         } else if (!lastUrl.isValid() || (lastUrl.scheme().isEmpty() && lastUrl.adjusted(QUrl::RemoveFilename).isEmpty())) {
0527             // Sometimes valid filenames are not recognized by KURL::isLocalFile(), they are marked as invalid then
0528             suggestedFileName = lastUrl.url();
0529             urls.removeLast();
0530         }
0531     }
0532 
0533     /// More than two urls defined, and last is local and will be used as destination directory
0534     if (urls.count() > 2 && urls.last().isLocalFile()) {
0535         /**
0536          * FIXME should the code be uncommented again, though then inputting a wrong destination like
0537          * ~/Downloads/folderNotExisting would result in ~/Downloads/ instead of informing the user
0538          * and giving them the possibility to improve their mistake
0539          */
0540         //         if (!QFileInfo(urls.last().toLocalFile()).isDir()) {
0541         //             folder = urls.last().directory(QUrl::AppendTrailingSlash);
0542         //         } else {
0543         folder = urls.last().adjusted(QUrl::RemoveFilename).toString(); // checks if that folder is correct happen later
0544         //         }
0545         urls.removeLast();
0546     }
0547 
0548     // add a folder or suggestedFileName if they are valid
0549     if (!folder.isEmpty() && KGet::isValidDestDirectory(folder)) {
0550         (*itUrls).folder = folder;
0551     }
0552     if (!suggestedFileName.isEmpty()) {
0553         (*itUrls).suggestedFileName = QUrl(suggestedFileName).toString(); // pathOrUrl to get a non percent encoded url
0554     }
0555 
0556     newTransferDialogHandler->m_numJobs[newTransferDialogHandler->m_nextJobId] = urls.count();
0557     foreach (const QUrl &url, urls) {
0558         // needed to avoid when protocols like the desktop protocol is used, see bko:185283
0559         KIO::Job *job = mostLocalUrlJob(url);
0560         job->setProperty("jobId", (newTransferDialogHandler->m_nextJobId));
0561         connect(job, &KJob::result, newTransferDialogHandler, &NewTransferDialogHandler::slotMostLocalUrlResult);
0562         job->start();
0563     }
0564 
0565     ++(newTransferDialogHandler->m_nextJobId);
0566 }
0567 
0568 void NewTransferDialogHandler::slotMostLocalUrlResult(KJob *j)
0569 {
0570     auto *job = static_cast<MostLocalUrlJob *>(j);
0571     const int jobId = job->property("jobId").toInt();
0572 
0573     if (job->error()) {
0574         qCWarning(KGET_DEBUG) << "An error happened for" << job->url();
0575     } else {
0576         m_urls[jobId].urls << job->mostLocalUrl();
0577     }
0578     --m_numJobs[jobId];
0579 
0580     if (m_numJobs[jobId] <= 0) {
0581         handleUrls(jobId);
0582     }
0583 }
0584 
0585 void NewTransferDialogHandler::handleUrls(const int jobId)
0586 {
0587     QHash<int, UrlData>::iterator itUrls = m_urls.find(jobId);
0588     if (itUrls == m_urls.end()) {
0589         qCWarning(KGET_DEBUG) << "JobId" << jobId << "was not defined, could not handle urls for it.";
0590         return;
0591     }
0592 
0593     QList<QUrl> urls = (*itUrls).urls;
0594     UrlChecker::removeDuplicates(urls);
0595 
0596     QString folder = (*itUrls).folder;
0597     if (!folder.isEmpty() && (UrlChecker::checkFolder(QUrl::fromLocalFile(folder), true) != UrlChecker::NoError)) {
0598         folder.clear();
0599     }
0600 
0601     const QString suggestedFileName = (*itUrls).suggestedFileName;
0602     QUrl newDest;
0603     const QUrl folderUrl = QUrl::fromLocalFile(folder);
0604 
0605     // check if the sources are correct
0606     UrlChecker check(UrlChecker::Source);
0607     check.addUrls(urls);
0608     check.displayErrorMessages();
0609     check.existingTransfers();
0610     urls = check.correctUrls();
0611 
0612     QList<KGet::TransferData> data;
0613 
0614     /// Just one file to download, with a specified suggestedFileName, handle if possible
0615     if (!suggestedFileName.isEmpty() && (urls.count() == 1)) {
0616         const QUrl sourceUrl = urls.first();
0617         const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(sourceUrl);
0618         const QString groupName = (groups.isEmpty() ? QString() : groups.first()->name());
0619         QString defaultFolder;
0620         if (groups.isEmpty()) {
0621             defaultFolder = (Settings::askForDestination() ? QString() : QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
0622         } else {
0623             defaultFolder = groups.first()->defaultFolder();
0624         }
0625 
0626         if (!folder.isEmpty()) {
0627             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(folder), sourceUrl, suggestedFileName);
0628             newDest = check.checkExistingFile(sourceUrl, destUrl);
0629             if (!newDest.isEmpty()) {
0630                 data << KGet::TransferData(sourceUrl, newDest, groupName);
0631             }
0632             urls.removeFirst();
0633         } else if (((!groups.isEmpty() && !Settings::directoriesAsSuggestion()) || !Settings::askForDestination())
0634                    && (UrlChecker::checkFolder(QUrl::fromLocalFile(defaultFolder)) == UrlChecker::NoError)) {
0635             const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(defaultFolder), sourceUrl, suggestedFileName);
0636             newDest = check.checkExistingFile(sourceUrl, destUrl);
0637             if (!newDest.isEmpty()) {
0638                 data << KGet::TransferData(sourceUrl, newDest, groupName);
0639             }
0640             urls.removeFirst();
0641         }
0642     }
0643 
0644     /// A valid folder has been defined, use that for downloading
0645     if (!folder.isEmpty()) {
0646         // find the associated groups first, we just need the first matching group though
0647         const QList<TransferGroupHandler *> groups = KGet::allTransferGroups();
0648         foreach (TransferGroupHandler *group, groups) {
0649             if (urls.isEmpty()) {
0650                 break;
0651             }
0652 
0653             const QString groupName = group->name();
0654             const QStringList patterns = group->regExp().pattern().split(',');
0655 
0656             // find all urls where a group can be identified
0657             QList<QUrl>::iterator it = urls.begin();
0658             while (it != urls.end()) {
0659                 const QUrl sourceUrl = *it;
0660                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
0661                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0662                     newDest = check.checkExistingFile(sourceUrl, destUrl);
0663                     if (!newDest.isEmpty()) {
0664                         data << KGet::TransferData(sourceUrl, newDest, groupName);
0665                     }
0666                     it = urls.erase(it);
0667                 } else {
0668                     ++it;
0669                 }
0670             }
0671         }
0672 
0673         // there are still some unhandled urls, i.e. for those no group could be found, add them with an empty group
0674         foreach (const QUrl &sourceUrl, urls) {
0675             const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0676             newDest = check.checkExistingFile(sourceUrl, destUrl);
0677             if (!newDest.isEmpty()) {
0678                 data << KGet::TransferData(sourceUrl, newDest);
0679             }
0680         }
0681 
0682         // all urls have been handled
0683         urls.clear();
0684     }
0685 
0686     /// Now handle default folders/groups
0687     qCDebug(KGET_DEBUG) << "DIRECTORIES AS SUGGESTION" << Settings::directoriesAsSuggestion();
0688     if (!Settings::directoriesAsSuggestion() && !urls.isEmpty()) {
0689         qCDebug(KGET_DEBUG) << "No, Directories not as suggestion";
0690 
0691         // find the associated groups first, we just need the first matching group though
0692         const QList<TransferGroupHandler *> groups = KGet::allTransferGroups();
0693         foreach (TransferGroupHandler *group, groups) {
0694             if (urls.isEmpty()) {
0695                 break;
0696             }
0697 
0698             const QUrl folderUrl = QUrl::fromLocalFile(group->defaultFolder());
0699             if (UrlChecker::checkFolder(folderUrl) != UrlChecker::NoError) {
0700                 continue;
0701             }
0702 
0703             const QString groupName = group->name();
0704             const QStringList patterns = group->regExp().pattern().split(',');
0705 
0706             QList<QUrl>::iterator it = urls.begin();
0707             while (it != urls.end()) {
0708                 const QUrl sourceUrl = *it;
0709                 if (KGet::matchesExceptions(sourceUrl, patterns)) {
0710                     const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl);
0711                     newDest = check.checkExistingFile(sourceUrl, destUrl);
0712                     if (!newDest.isEmpty()) {
0713                         data << KGet::TransferData(sourceUrl, newDest, groupName);
0714                     }
0715 
0716                     it = urls.erase(it);
0717                 } else {
0718                     ++it;
0719                 }
0720             }
0721         }
0722     }
0723 
0724     /// Download the rest of the urls to QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) if the user is not aksed for a destination
0725     if (!Settings::askForDestination()) {
0726         // the download path will be always used
0727         const QString dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
0728         if (!dir.isEmpty()) {
0729             QList<QUrl>::iterator it = urls.begin();
0730             while (it != urls.end()) {
0731                 const QUrl sourceUrl = *it;
0732                 const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(dir), sourceUrl);
0733                 newDest = check.checkExistingFile(sourceUrl, destUrl);
0734                 if (!newDest.isEmpty()) {
0735                     data << KGet::TransferData(sourceUrl, newDest);
0736                 }
0737 
0738                 it = urls.erase(it);
0739             }
0740         }
0741     }
0742 
0743     /// Now create transfers for the urls that provided enough data
0744     if (!data.isEmpty()) {
0745         KGet::createTransfers(data);
0746     }
0747 
0748     /// Handle custom newtransferdialogs...
0749     if ((!m_dialog || m_dialog->isEmpty()) && urls.count() == 1) { // FIXME why the m_dialog check? whenever a dialog has been created this would not be shown?
0750         QUrl url = urls.first();
0751         QPointer<QDialog> dialog;
0752         foreach (TransferFactory *factory, KGet::factories()) {
0753             const QList<TransferGroupHandler *> groups = KGet::groupsFromExceptions(url);
0754             dialog = factory->createNewTransferDialog(url, suggestedFileName, !groups.isEmpty() ? groups.first() : nullptr);
0755             if (dialog) {
0756                 dialog->exec();
0757                 delete dialog;
0758             }
0759         }
0760     }
0761 
0762     m_numJobs.remove(jobId);
0763     m_urls.erase(itUrls);
0764 
0765     /// Display default NewTransferDialog
0766     if (!urls.isEmpty()) {
0767         createDialog(urls, suggestedFileName);
0768     }
0769 }
0770 
0771 void NewTransferDialogHandler::createDialog(const QList<QUrl> &urls, const QString &suggestedFileName)
0772 {
0773     if (!m_dialog) {
0774         m_dialog = new NewTransferDialog(KGet::m_mainWindow);
0775     }
0776 
0777     m_dialog->m_window = KGet::m_mainWindow;
0778     m_dialog->showDialog(urls, suggestedFileName);
0779 }
0780 
0781 #include "moc_newtransferdialog.cpp"