1/* ============================================================
2*
3* This file is a part of the rekonq project
4*
5* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com>
6* Copyright (C) 2011 by Pierre Rossi <pierre dot rossi at gmail dot com>
7*
8*
9* This program is free software; you can redistribute it and/or
10* modify it under the terms of the GNU General Public License as
11* published by the Free Software Foundation; either version 2 of
12* the License or (at your option) version 3 or any later version
13* accepted by the membership of KDE e.V. (or its successor approved
14* by the membership of KDE e.V.), which shall act as a proxy
15* defined in Section 14 of version 3 of the license.
16*
17* This program is distributed in the hope that it will be useful,
18* but WITHOUT ANY WARRANTY; without even the implied warranty of
19* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20* GNU General Public License for more details.
21*
22* You should have received a copy of the GNU General Public License
23* along with this program. If not, see <http://www.gnu.org/licenses/>.
24*
25* ============================================================ */
26
27
28// Self Includes
29#include "downloadmanager.h"
30#include "downloadmanager.moc"
31
32// Auto Includes
33#include "rekonq.h"
34
35// KDE Includes
36#include <KStandardDirs>
37#include <KToolInvocation>
38#include <KFileDialog>
39#include <krecentdirs.h>
40
41
42#include <kio/scheduler.h>
43
44#include <KIO/Job>
45#include <KIO/CopyJob>
46#include <KIO/JobUiDelegate>
47
48// Qt Includes
49#include <QApplication>
50#include <QDataStream>
51#include <QDateTime>
52#include <QFile>
53#include <QFileInfo>
54#include <QString>
55#include <QWebSettings>
56#include <QNetworkReply>
57
58#include <QDBusConnection>
59#include <QDBusConnectionInterface>
60#include <QDBusInterface>
61
62
63QWeakPointer<DownloadManager> DownloadManager::s_downloadManager;
64
65
66DownloadManager *DownloadManager::self()
67{
68 if (s_downloadManager.isNull())
69 {
70 s_downloadManager = new DownloadManager(qApp);
71 }
72 return s_downloadManager.data();
73}
74
75
76// ----------------------------------------------------------------------------------------------
77
78
79DownloadManager::DownloadManager(QObject *parent)
80 : QObject(parent)
81 , m_needToSave(false)
82{
83 init();
84}
85
86
87DownloadManager::~DownloadManager()
88{
89 if (!m_needToSave)
90 return;
91
92 QString downloadFilePath = KStandardDirs::locateLocal("appdata" , "downloads");
93 QFile downloadFile(downloadFilePath);
94
95 if (!downloadFile.open(QFile::WriteOnly))
96 {
97 kDebug() << "Unable to open download file (WRITE mode)..";
98 return;
99 }
100
101 QDataStream out(&downloadFile);
102 Q_FOREACH(DownloadItem * item, m_downloadList)
103 {
104 out << item->originUrl();
105 out << item->destinationUrlString();
106 out << item->dateTime();
107 }
108
109 downloadFile.close();
110}
111
112
113void DownloadManager::init()
114{
115 QString downloadFilePath = KStandardDirs::locateLocal("appdata" , "downloads");
116 QFile downloadFile(downloadFilePath);
117 if (!downloadFile.open(QFile::ReadOnly))
118 {
119 kDebug() << "Unable to open download file (READ mode)..";
120 return;
121 }
122
123 QDataStream in(&downloadFile);
124 while (!in.atEnd())
125 {
126 QString srcUrl;
127 in >> srcUrl;
128 QString destUrl;
129 in >> destUrl;
130 QDateTime dt;
131 in >> dt;
132 DownloadItem *item = new DownloadItem(srcUrl, destUrl, dt, this);
133 m_downloadList.append(item);
134 }
135}
136
137
138DownloadItem* DownloadManager::addDownload(KIO::CopyJob *job)
139{
140 KIO::CopyJob *cJob = qobject_cast<KIO::CopyJob *>(job);
141
142 QString downloadFilePath = KStandardDirs::locateLocal("appdata" , "downloads");
143 QFile downloadFile(downloadFilePath);
144 if (!downloadFile.open(QFile::WriteOnly | QFile::Append))
145 {
146 kDebug() << "Unable to open download file (WRITE mode)..";
147 return 0;
148 }
149 QDataStream out(&downloadFile);
150 out << cJob->srcUrls().at(0).url();
151 out << cJob->destUrl().url();
152 out << QDateTime::currentDateTime();
153 downloadFile.close();
154 DownloadItem *item = new DownloadItem(job, QDateTime::currentDateTime(), this);
155 m_downloadList.append(item);
156 emit newDownloadAdded(item);
157 return item;
158}
159
160
161bool DownloadManager::clearDownloadsHistory()
162{
163 m_downloadList.clear();
164 QString downloadFilePath = KStandardDirs::locateLocal("appdata" , "downloads");
165 QFile downloadFile(downloadFilePath);
166 return downloadFile.remove();
167}
168
169
170void DownloadManager::downloadLinksWithKGet(const QVariant &contentList)
171{
172 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget"))
173 {
174 KToolInvocation::kdeinitExecWait("kget");
175 }
176 QDBusInterface kget("org.kde.kget", "/KGet", "org.kde.kget.main");
177 if (kget.isValid())
178 {
179 kget.call("importLinks", contentList);
180 }
181}
182
183
184void DownloadManager::removeDownloadItem(int index)
185{
186 DownloadItem *item = m_downloadList.takeAt(index);
187 delete item;
188
189 m_needToSave = true;
190}
191
192
193// NOTE
194// These 2 functions have been copied from the KWebPage class to implement a local version of the downloadResponse method.
195// In this way, we can easily provide the extra functionality we need:
196// 1. KGet Integration
197// 2. Save downloads history
198bool DownloadManager::downloadResource(const KUrl &srcUrl, const KIO::MetaData &metaData,
199 QWidget *parent, bool forceDirRequest, const QString &suggestedName, bool registerDownload)
200{
201 // manage downloads with KGet if found
202 if (ReKonfig::kgetDownload() && !KStandardDirs::findExe("kget").isNull())
203 {
204 //KGet integration:
205 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget"))
206 {
207 KToolInvocation::kdeinitExecWait("kget");
208 }
209 QDBusInterface kget("org.kde.kget", "/KGet", "org.kde.kget.main");
210 if (!kget.isValid())
211 return false;
212
213 QDBusMessage transfer = kget.call(QL1S("addTransfer"), srcUrl.prettyUrl(), QString(), true);
214
215 return true;
216 }
217
218 KUrl destUrl;
219
220 const QString fileName((suggestedName.isEmpty() ? srcUrl.fileName() : suggestedName));
221
222 if (forceDirRequest || ReKonfig::askDownloadPath())
223 {
224 // follow bug:184202 fixes
225
226 // Downloads should default to the default download directory. At the
227 // same time when the user has been using a different directory
228 // previously, it should be used instead.
229 // To enable this behavior we inject the default download path into
230 // KRecentDirs (which is internally used by KFileDialog to get the
231 // most recently used directory of a fileclass).
232 // If a user then uses a different directory it will replace the
233 // downloads directory in KRecentDirs and become the new default when
234 // trying to save a file. Also see KFileDialog, KFileWidget and
235 // KRecentDirs documentation.
236
237 // If this is the first invocation insert the defaults downloads directory.
238 static const QString fileClass = QL1S(":download");
239 if (KRecentDirs::list(fileClass).count() <= 1) // Always has one entry by default.
240 KRecentDirs::add(fileClass, KGlobalSettings::downloadPath());
241
242 const KUrl startDir(QString("kfiledialog:///download/%1").arg(fileName));
243
244 // NOTE: We used to use getSaveFileName here but it proved unable to
245 // handle remote URLs, which we need to handle here, making the use of
246 // getSaveUrl deliberate.
247 destUrl = KFileDialog::getSaveUrl(startDir, QString(), parent);
248 }
249 else
250 {
251 destUrl = KUrl(ReKonfig::downloadPath().path() + QL1C('/') + fileName);
252 }
253
254 kDebug() << "DEST URL: " << destUrl;
255
256 if (!destUrl.isValid())
257 return false;
258
259 KIO::CopyJob *job = KIO::copy(srcUrl, destUrl);
260
261 if (!metaData.isEmpty())
262 job->setMetaData(metaData);
263
264 job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
265 job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
266 job->ui()->setWindow((parent ? parent->window() : 0));
267 job->ui()->setAutoErrorHandlingEnabled(true);
268
269 if (registerDownload)
270 addDownload(job);
271
272 return true;
273}
274