1 /* This file is part of the KDE project
2 
3    Copyright (C) 2008 Lukas Appelhans <l.appelhans@gmx.de>
4    Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10 */
11 #ifndef DATASOURCEFACTORY_H
12 #define DATASOURCEFACTORY_H
13 
14 #include "kget_export.h"
15 
16 #include "transferdatasource.h"
17 #include "job.h"
18 
19 #include <kio/job.h>
20 
21 #include <QDomElement>
22 
23 class BitSet;
24 class TransferDataSource;
25 class QTimer;
26 class Signature;
27 class Verifier;
28 
29 namespace KIO
30 {
31     class FileJob;
32 }
33 
34 /**
35  This class manages multiple DataSources and saves the received data to the file
36  */
37 class KGET_EXPORT DataSourceFactory : public QObject
38 {
39     Q_OBJECT
40 
41     public:
42         /**
43          * In general use this constructor, if the size is 0, the datasourcefactory will try to
44          * find the filesize
45          * @note when you want to load a datasourcefactory you do not have to specify the url and segSize
46          */
47         explicit DataSourceFactory(QObject *parent, const QUrl &dest = QUrl(), KIO::filesize_t size = 0, KIO::fileoffset_t segSize = 512000);
48 
49         ~DataSourceFactory() override;
50 
51         /**
52          * The capabilities the DataSourceFactory supports
53          */
capabilities()54         Transfer::Capabilities capabilities() const {return m_capabilities;}
55 
56         /**
57          * Deletes the created (downloadInitialized() is true) file if the download was not finished
58          * Does not delete anything if the download never got started
59          * @see downloadInitialized()
60          */
61         void deinit();
62 
63         /**
64          * @return true if the DataSourceFactory has enough information to start a download
65          */
66         bool isValid() const;
67 
68         void start();
69         void stop();
size()70         KIO::filesize_t size() const {return m_size;}
downloadedSize()71         KIO::filesize_t downloadedSize() const {return m_downloadedSize;}
currentSpeed()72         ulong currentSpeed() const {return m_speed;}
percent()73         ulong percent() const {return m_percent;}
74 
dest()75         QUrl dest() const {return m_dest;}
76 
77         /**
78          * The maximum number of mirrors that will be used for downloading, default is 3
79          */
maxMirrorsUsed()80         int maxMirrorsUsed() const {return m_maxMirrorsUsed;}
81 
82         /**
83          * Change the maximum number off mirrors that will be used for downloading,
84          * if the download started already some mirrors might be added or removed automatically
85          */
setMaxMirrorsUsed(int maxMirrorsUsed)86         void setMaxMirrorsUsed(int maxMirrorsUsed) {m_maxMirrorsUsed = maxMirrorsUsed;}
87 
88         /**
89          * Add a mirror that can be used for downloading
90          * @param url the url to the file
91          * @param used defines whether the mirror should initially be used for downloading or not,
92          * if true m_maxMirrorsUsed might be increased if needed
93          * @param numParallelConnections the number of simultaneous connections allowed to that mirror,
94          * minimum is 1
95          * @note when you add an already existing mirror only the numParallelConnections are adapted
96          * to the new value, so to change the number of parallel connections of a mirror you are already
97          * using simply call addMirror again
98          */
99         void addMirror(const QUrl &url, bool used, int numParallelConnections = 1);
100 
101         /**
102          * Add a mirror that can be used for downloading, if it will be used depends if maxMirrorsUsed
103          * has been reached yet
104          * @param url the url to the file
105          * @param numParallelConnections the number of simultaneous connections allowed to that mirror,
106          * minimum is 1
107          * @note when you add an already existing mirror only the numParallelConnections are adapted
108          * to the new value, so to change the number of parallel connections of a mirror you are already
109          * using simply call addMirror again
110         */
111         void addMirror(const QUrl &url, int numParallelConnections = 1);
112 
113         /**
114          * Does not use the specified mirror for downloading the file
115          * @note if the mirror has been used for downloading it will be moved to m_unusedMirrors,
116          * otherwise nohting will happen
117          * @param url the mirror that should not be used anymore
118          */
119         void removeMirror(const QUrl &url);
120 
121         /**
122          * Sets the mirrors that should be used/not used for downloading
123          * @param mirrors url of the mirror, if it should be used and its number of parallel connections
124          * (minimum is 1)
125          * @note if you want the download to work at least one entry should be set to true
126          */
127         void setMirrors(const QHash<QUrl, QPair<bool, int> > &mirrors);
128 
129         /**
130          * Return all mirrors, where bool defines if the mirror is used,
131          * while in defines the number of parallel connections for that mirror
132          */
133         QHash<QUrl, QPair<bool, int> > mirrors() const;
134 
135         /**
136          * Returns whether the datasourcefactory should download the file or not,
137          * true by default
138          * @note can be used for multiple datasourcefactory downloads
139          */
doDownload()140         bool doDownload() const {return m_doDownload;}
141 
142         /**
143          * Set if the datasourcefactory should download the file or not,
144          * if set to false the download will be stopped if needed
145          * @note can be used for multiple datasourcefactory downloads
146          */
147         void setDoDownload(bool doDownload);
148 
149         bool setNewDestination(const QUrl &newDest);
150 
status()151         Job::Status status() const {return m_status;}
152 
153         /**
154          * @return true if the download was already initialized, i.e. a file has been
155          * created and maybe even written to
156          * @see deinit()
157          */
downloadInitialized()158         bool downloadInitialized() const {return m_downloadInitialized;}
159 
160         /**
161          * Tries to repair a broken download, via completely redownloading it
162          * or only the borken parts
163          * @note call this if verification returned NotVerified
164          */
165         void repair();
166 
167         Verifier *verifier();
168         Signature *signature();
169 
170     Q_SIGNALS:
171         void capabilitiesChanged();
172         void dataSourceFactoryChange(Transfer::ChangesFlags change);
173         void log(const QString &message, Transfer::LogLevel logLevel);
174 
175     public Q_SLOTS:
176         void save(const QDomElement &element);
177         void load(const QDomElement *e);
178 
179     private Q_SLOTS:
180         void slotUpdateCapabilities();
181 
182         void slotRemovedFile();
183 
184         /**
185          * Tries to find the size of the file, automatically called
186          * by start if no file size has been specified
187          */
188         void findFileSize();
189 
190         void slotFoundFileSize(TransferDataSource *source, KIO::filesize_t fileSize, const QPair<int,int> &segmentRange);
191 
192         void assignSegments(TransferDataSource *source);
193         /**
194          * Called when segments are broken
195          */
196         void brokenSegments(TransferDataSource *source, const QPair<int, int> &segmentRange);
197         void finishedSegment(TransferDataSource *source, int segmentNumber, bool connectionFinished = true);
198 
199         /**
200          * A TransferDataSource is broken
201          */
202         void broken(TransferDataSource *source, TransferDataSource::Error error);
203         /**
204          * Emitted when a Datasource itself decides to not download a specific segmentRange,
205          * e.g. when there are too many connections for this TransferDataSource
206          */
207         void slotFreeSegments(TransferDataSource *source, QPair<int, int> segmentRange);
208         void slotWriteData(KIO::fileoffset_t offset, const QByteArray &data, bool &worked);
209         void slotOffset(KIO::Job *job, KIO::filesize_t offset);
210         void slotDataWritten(KIO::Job *job, KIO::filesize_t offset);
211         void slotPercent(KJob *job, ulong percent);
212         void slotOpen(KIO::Job *job);
213         void speedChanged();
214         /**
215          * Kills the putjob and starts the moving of files
216          */
217         void startMove();
218         void slotPutJobDestroyed(QObject *job);
219         void newDestResult(KJob *job);
220 
221         void slotRepair(const QList<KIO::fileoffset_t> &offsets, KIO::filesize_t length);
222 
223         void slotFinishedDownload(TransferDataSource *source, KIO::filesize_t size);
224 
225         void slotUrlChanged(const QUrl &, const QUrl &);
226 
227     private:
228         /**
229          * Add a mirror that can be used for downloading
230          * @param used always true if usedDefined is false
231          * @param usedDefined true if the user defined used, otherwise false,
232          * needed to know if m_maxMirrorsUsed should be changed or not
233          */
234         void addMirror(const QUrl &url, bool used, int numParallelConnections, bool usedDefined);
235 
236         /**
237          * Checks if an assign is needed, i.e. there are no (running) TransferDataSources,
238          * yet some segments are still not finished
239          */
240         bool assignNeeded() const;
241 
242         bool checkLocalFile();
243 
244         void init();
245         void killPutJob();
246         void changeStatus(Job::Status status);
247 
248     private:
249         Transfer::Capabilities m_capabilities;
250         QUrl m_dest;
251         QUrl m_newDest;
252         KIO::filesize_t m_size;
253         KIO::filesize_t m_downloadedSize;
254         QList<KIO::filesize_t> m_prevDownloadedSizes;
255         KIO::fileoffset_t m_segSize;
256         ulong m_speed;
257         ulong m_percent;
258 
259         /**
260          * the cache of data that could not be written yet
261          */
262         QHash<KIO::fileoffset_t, QByteArray> m_cache;
263 
264         KIO::filesize_t m_tempOffset;
265         QByteArray m_tempData;
266 
267         BitSet *m_startedChunks;
268         BitSet *m_finishedChunks;
269         KIO::FileJob* m_putJob;
270         bool m_doDownload;
271         bool m_open;
272         /**
273          * the write access is currently blocked, the data gets cached in m_cache
274          */
275         bool m_blocked;
276         /**
277          * If start() was called but did not work this is true, once the conditions changed
278          * start() could be recalled
279          */
280         bool m_startTried;
281         bool m_findFilesizeTried;
282 
283         bool m_assignTried;
284         bool m_movingFile;
285 
286         bool m_finished;
287 
288         /**
289          * True if download gets started the first time, if it gets never started there
290          * is no reason to remove any -- maybe preexisting -- file
291          */
292         bool m_downloadInitialized;
293 
294         /**
295          * Whether the file-size has been initially defined (it is to be trusted) or not
296          */
297         bool m_sizeInitiallyDefined;
298 
299         /**
300          * Downloadsize has only been found out once the download was finished
301          */
302         bool m_sizeFoundOnFinish;
303 
304         int m_maxMirrorsUsed;
305         QHash<QUrl, TransferDataSource*> m_sources;
306         QList<QUrl> m_unusedUrls;
307         QList<int> m_unusedConnections;
308         QTimer *m_speedTimer;
309         Job::Status m_status;
310         Job::Status m_statusBeforeMove;
311 
312         Verifier *m_verifier;
313         Signature *m_signature;
314 };
315 
316 #endif
317