1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
4     SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
5     SPDX-FileCopyrightText: 2000-2009 Waldo Bastian <bastian@kde.org>
6     SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
7     SPDX-FileCopyrightText: 2013 Dawit Alemayehu <adawit@kde.org>
8 
9     SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 
12 #ifndef KIO_JOB_P_H
13 #define KIO_JOB_P_H
14 
15 #include "commands_p.h"
16 #include "global.h"
17 #include "kiocoredebug.h"
18 #include "simplejob.h"
19 #include "slave.h"
20 #include "transferjob.h"
21 #include <KJobTrackerInterface>
22 #include <QDataStream>
23 #include <QPointer>
24 #include <QUrl>
25 #include <kio/jobuidelegateextension.h>
26 #include <kio/jobuidelegatefactory.h>
27 
28 /* clang-format off */
29 #define KIO_ARGS \
30     QByteArray packedArgs; \
31     QDataStream stream(&packedArgs, QIODevice::WriteOnly); \
32     stream
33 /* clang-format on */
34 
35 namespace KIO
36 {
37 static constexpr filesize_t invalidFilesize = static_cast<KIO::filesize_t>(-1);
38 
39 // Exported for KIOWidgets jobs
40 class KIOCORE_EXPORT JobPrivate
41 {
42 public:
JobPrivate()43     JobPrivate()
44         : m_parentJob(nullptr)
45         , m_extraFlags(0)
46         , m_uiDelegateExtension(KIO::defaultJobUiDelegateExtension())
47         , m_privilegeExecutionEnabled(false)
48     {
49     }
50 
51     virtual ~JobPrivate();
52 
53     /**
54      * Some extra storage space for jobs that don't have their own
55      * private d pointer.
56      */
57     enum {
58         EF_TransferJobAsync = (1 << 0),
59         EF_TransferJobNeedData = (1 << 1),
60         EF_TransferJobDataSent = (1 << 2),
61         EF_ListJobUnrestricted = (1 << 3),
62         EF_KillCalled = (1 << 4),
63     };
64 
65     enum FileOperationType {
66         ChangeAttr, // chmod(), chown(), setModificationTime()
67         Copy,
68         Delete,
69         MkDir,
70         Move,
71         Rename,
72         Symlink,
73         Transfer, // put() and get()
74         Other, // if other file operation set message, caption inside the job.
75     };
76 
77     // Maybe we could use the QObject parent/child mechanism instead
78     // (requires a new ctor, and moving the ctor code to some init()).
79     Job *m_parentJob;
80     int m_extraFlags;
81     MetaData m_incomingMetaData;
82     MetaData m_internalMetaData;
83     MetaData m_outgoingMetaData;
84     JobUiDelegateExtension *m_uiDelegateExtension;
85     Job *q_ptr;
86     // For privilege operation
87     bool m_privilegeExecutionEnabled;
88     QString m_caption, m_message;
89     FileOperationType m_operationType;
90 
91     QByteArray privilegeOperationData();
92     void slotSpeed(KJob *job, unsigned long speed);
93 
94     static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest);
95     static void emitCopying(KIO::Job *, const QUrl &src, const QUrl &dest);
96     static void emitCreatingDir(KIO::Job *, const QUrl &dir);
97     static void emitDeleting(KIO::Job *, const QUrl &url);
98     static void emitStating(KIO::Job *, const QUrl &url);
99     static void emitTransferring(KIO::Job *, const QUrl &url);
100     static void emitMounting(KIO::Job *, const QString &dev, const QString &point);
101     static void emitUnmounting(KIO::Job *, const QString &point);
102 
103     Q_DECLARE_PUBLIC(Job)
104 };
105 
106 class SimpleJobPrivate : public JobPrivate
107 {
108 public:
109     /**
110      * Creates a new simple job.
111      * @param url the url of the job
112      * @param command the command of the job
113      * @param packedArgs the arguments
114      */
SimpleJobPrivate(const QUrl & url,int command,const QByteArray & packedArgs)115     SimpleJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
116         : m_slave(nullptr)
117         , m_packedArgs(packedArgs)
118         , m_url(url)
119         , m_command(command)
120         , m_schedSerial(0)
121         , m_redirectionHandlingEnabled(true)
122     {
123     }
124 
125     QPointer<Slave> m_slave;
126     QByteArray m_packedArgs;
127     QUrl m_url;
128     QUrl m_subUrl;
129     int m_command;
130 
131     // for use in KIO::Scheduler
132     //
133     // There are two kinds of protocol:
134     // (1) The protocol of the url
135     // (2) The actual protocol that the io-slave uses.
136     //
137     // These two often match, but not necessarily. Most notably, they don't
138     // match when doing ftp via a proxy.
139     // In that case (1) is ftp, but (2) is http.
140     //
141     // JobData::protocol stores (2) while Job::url().protocol() returns (1).
142     // The ProtocolInfoDict is indexed with (2).
143     //
144     // We schedule slaves based on (2) but tell the slave about (1) via
145     // Slave::setProtocol().
146     QString m_protocol;
147     QStringList m_proxyList;
148     int m_schedSerial;
149     bool m_redirectionHandlingEnabled;
150 
151     void simpleJobInit();
152 
153     /**
154      * Called on a slave's connected signal.
155      * @see connected()
156      */
157     void slotConnected();
158     /**
159      * Forward signal from the slave.
160      * @param data_size the processed size in bytes
161      * @see processedSize()
162      */
163     void slotProcessedSize(KIO::filesize_t data_size);
164     /**
165      * Forward signal from the slave.
166      * @param speed the speed in bytes/s
167      * @see speed()
168      */
169     void slotSpeed(unsigned long speed);
170     /**
171      * Forward signal from the slave
172      * Can also be called by the parent job, when it knows the size.
173      * @param data_size the total size
174      */
175     void slotTotalSize(KIO::filesize_t data_size);
176 
177     /**
178      * Called on a slave's info message.
179      * @param s the info message
180      * @see infoMessage()
181      */
182     void _k_slotSlaveInfoMessage(const QString &s);
183 
184     /**
185      * Called when privilegeOperationRequested() is emitted by slave.
186      */
187     void slotPrivilegeOperationRequested();
188 
189     /**
190      * @internal
191      * Called by the scheduler when a slave gets to
192      * work on this job.
193      **/
194     virtual void start(KIO::Slave *slave);
195 
196     /**
197      * @internal
198      * Called to detach a slave from a job.
199      **/
200     void slaveDone();
201 
202     /**
203      * Called by subclasses to restart the job after a redirection was signalled.
204      * The m_redirectionURL data member can appear in several subclasses, so we have it
205      * passed in. The regular URL will be set to the redirection URL which is then cleared.
206      */
207     void restartAfterRedirection(QUrl *redirectionUrl);
208 
209     /**
210      * Request the ui delegate to show a message box.
211      * @internal
212      */
213     int requestMessageBox(int type,
214                           const QString &text,
215                           const QString &caption,
216                           const QString &buttonYes,
217                           const QString &buttonNo,
218                           const QString &iconYes = QString(),
219                           const QString &iconNo = QString(),
220                           const QString &dontAskAgainName = QString(),
221                           const KIO::MetaData &sslMetaData = KIO::MetaData());
222 
Q_DECLARE_PUBLIC(SimpleJob)223     Q_DECLARE_PUBLIC(SimpleJob)
224 
225     static inline SimpleJobPrivate *get(KIO::SimpleJob *job)
226     {
227         return job->d_func();
228     }
newJobNoUi(const QUrl & url,int command,const QByteArray & packedArgs)229     static inline SimpleJob *newJobNoUi(const QUrl &url, int command, const QByteArray &packedArgs)
230     {
231         SimpleJob *job = new SimpleJob(*new SimpleJobPrivate(url, command, packedArgs));
232         return job;
233     }
234     static inline SimpleJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, JobFlags flags = HideProgressInfo)
235     {
236         SimpleJob *job = new SimpleJob(*new SimpleJobPrivate(url, command, packedArgs));
237         job->setUiDelegate(KIO::createDefaultJobUiDelegate());
238         if (!(flags & HideProgressInfo)) {
239             KIO::getJobTracker()->registerJob(job);
240         }
241         if (!(flags & NoPrivilegeExecution)) {
242             job->d_func()->m_privilegeExecutionEnabled = true;
243             // Only delete, rename and symlink operation accept JobFlags.
244             FileOperationType opType;
245             switch (command) {
246             case CMD_DEL:
247                 opType = Delete;
248                 break;
249             case CMD_RENAME:
250                 opType = Rename;
251                 break;
252             case CMD_SYMLINK:
253                 opType = Symlink;
254                 break;
255             default:
256                 return job;
257             }
258             job->d_func()->m_operationType = opType;
259         }
260         return job;
261     }
262 };
263 
264 class TransferJobPrivate : public SimpleJobPrivate
265 {
266 public:
TransferJobPrivate(const QUrl & url,int command,const QByteArray & packedArgs,const QByteArray & _staticData)267     inline TransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &_staticData)
268         : SimpleJobPrivate(url, command, packedArgs)
269         , m_internalSuspended(false)
270         , m_errorPage(false)
271         , staticData(_staticData)
272         , m_isMimetypeEmitted(false)
273         , m_closedBeforeStart(false)
274         , m_subJob(nullptr)
275     {
276     }
277 
TransferJobPrivate(const QUrl & url,int command,const QByteArray & packedArgs,QIODevice * ioDevice)278     inline TransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice)
279         : SimpleJobPrivate(url, command, packedArgs)
280         , m_internalSuspended(false)
281         , m_errorPage(false)
282         , m_isMimetypeEmitted(false)
283         , m_closedBeforeStart(false)
284         , m_subJob(nullptr)
285         , m_outgoingDataSource(QPointer<QIODevice>(ioDevice))
286     {
287     }
288 
289     bool m_internalSuspended;
290     bool m_errorPage;
291     QByteArray staticData;
292     QUrl m_redirectionURL;
293     QList<QUrl> m_redirectionList;
294     QString m_mimetype;
295     bool m_isMimetypeEmitted;
296     bool m_closedBeforeStart;
297     TransferJob *m_subJob;
298     QPointer<QIODevice> m_outgoingDataSource;
299     QMetaObject::Connection m_readChannelFinishedConnection;
300 
301     /**
302      * Flow control. Suspend data processing from the slave.
303      */
304     void internalSuspend();
305     /**
306      * Flow control. Resume data processing from the slave.
307      */
308     void internalResume();
309     /**
310      * @internal
311      * Called by the scheduler when a slave gets to
312      * work on this job.
313      * @param slave the slave that works on the job
314      */
315     void start(KIO::Slave *slave) override;
316     /**
317      * @internal
318      * Called when the ioslave needs the data to send the server. This slot
319      * is invoked when the data is to be sent is read from a QIODevice rather
320      * instead of a QByteArray buffer.
321      */
322     virtual void slotDataReqFromDevice();
323     void slotIODeviceClosed();
324     void slotIODeviceClosedBeforeStart();
325     void slotPostRedirection();
326     void slotNeedSubUrlData();
327     void slotSubUrlData(KIO::Job *, const QByteArray &);
328 
Q_DECLARE_PUBLIC(TransferJob)329     Q_DECLARE_PUBLIC(TransferJob)
330     static inline TransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &_staticData, JobFlags flags)
331     {
332         TransferJob *job = new TransferJob(*new TransferJobPrivate(url, command, packedArgs, _staticData));
333         job->setUiDelegate(KIO::createDefaultJobUiDelegate());
334         if (!(flags & HideProgressInfo)) {
335             KIO::getJobTracker()->registerJob(job);
336         }
337         if (!(flags & NoPrivilegeExecution)) {
338             job->d_func()->m_privilegeExecutionEnabled = true;
339             job->d_func()->m_operationType = Transfer;
340         }
341         return job;
342     }
343 
newJob(const QUrl & url,int command,const QByteArray & packedArgs,QIODevice * ioDevice,JobFlags flags)344     static inline TransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice, JobFlags flags)
345     {
346         TransferJob *job = new TransferJob(*new TransferJobPrivate(url, command, packedArgs, ioDevice));
347         job->setUiDelegate(KIO::createDefaultJobUiDelegate());
348         if (!(flags & HideProgressInfo)) {
349             KIO::getJobTracker()->registerJob(job);
350         }
351         if (!(flags & NoPrivilegeExecution)) {
352             job->d_func()->m_privilegeExecutionEnabled = true;
353             job->d_func()->m_operationType = Transfer;
354         }
355         return job;
356     }
357 };
358 
359 class DirectCopyJobPrivate;
360 /**
361  * @internal
362  * Used for direct copy from or to the local filesystem (i.e. SlaveBase::copy())
363  */
364 class DirectCopyJob : public SimpleJob
365 {
366     Q_OBJECT
367 
368 public:
369     DirectCopyJob(const QUrl &url, const QByteArray &packedArgs);
370     ~DirectCopyJob() override;
371 
372 public Q_SLOTS:
373     void slotCanResume(KIO::filesize_t offset);
374 
375 Q_SIGNALS:
376     /**
377      * @internal
378      * Emitted if the job found an existing partial file
379      * and supports resuming. Used by FileCopyJob.
380      */
381     void canResume(KIO::Job *job, KIO::filesize_t offset);
382 
383 private:
384     Q_DECLARE_PRIVATE(DirectCopyJob)
385 };
386 }
387 
388 #endif
389