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