1 /*
2  * Copyright 2021 (c) Matthieu Gallien <matthieu.gallien@nextcloud.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  * for more details.
13  */
14 
15 #pragma once
16 
17 #include "owncloudpropagator.h"
18 #include "abstractnetworkjob.h"
19 
20 #include <QLoggingCategory>
21 #include <QVector>
22 #include <QMap>
23 #include <QByteArray>
24 #include <deque>
25 
26 namespace OCC {
27 
28 Q_DECLARE_LOGGING_CATEGORY(lcBulkPropagatorJob)
29 
30 class ComputeChecksum;
31 class PutMultiFileJob;
32 
33 class BulkPropagatorJob : public PropagatorJob
34 {
35     Q_OBJECT
36 
37     /* This is a minified version of the SyncFileItem,
38      * that holds only the specifics about the file that's
39      * being uploaded.
40      *
41      * This is needed if we wanna apply changes on the file
42      * that's being uploaded while keeping the original on disk.
43      */
44     struct UploadFileInfo {
45       QString _file; /// I'm still unsure if I should use a SyncFilePtr here.
46       QString _path; /// the full path on disk.
47       qint64 _size;
48     };
49 
50     struct BulkUploadItem
51     {
52         AccountPtr _account;
53         SyncFileItemPtr _item;
54         UploadFileInfo _fileToUpload;
55         QString _remotePath;
56         QString _localPath;
57         qint64 _fileSize;
58         QMap<QByteArray, QByteArray> _headers;
59     };
60 
61 public:
62     explicit BulkPropagatorJob(OwncloudPropagator *propagator,
63                                const std::deque<SyncFileItemPtr> &items);
64 
65     bool scheduleSelfOrChild() override;
66 
67     JobParallelism parallelism() override;
68 
69 private slots:
70     void startUploadFile(SyncFileItemPtr item, UploadFileInfo fileToUpload);
71 
72     // Content checksum computed, compute the transmission checksum
73     void slotComputeTransmissionChecksum(SyncFileItemPtr item,
74                                          UploadFileInfo fileToUpload);
75 
76     // transmission checksum computed, prepare the upload
77     void slotStartUpload(SyncFileItemPtr item,
78                          UploadFileInfo fileToUpload,
79                          const QByteArray &transmissionChecksumType,
80                          const QByteArray &transmissionChecksum);
81 
82     // invoked on internal error to unlock a folder and faile
83     void slotOnErrorStartFolderUnlock(SyncFileItemPtr item,
84                                       SyncFileItem::Status status,
85                                       const QString &errorString);
86 
87     void slotPutFinished();
88 
89     void slotUploadProgress(SyncFileItemPtr item, qint64 sent, qint64 total);
90 
91     void slotJobDestroyed(QObject *job);
92 
93 private:
94     void doStartUpload(SyncFileItemPtr item,
95                        UploadFileInfo fileToUpload,
96                        QByteArray transmissionChecksumHeader);
97 
98     void adjustLastJobTimeout(AbstractNetworkJob *job,
99                               qint64 fileSize) const;
100 
101     void finalize(const QJsonObject &fullReply);
102 
103     void finalizeOneFile(const BulkUploadItem &oneFile);
104 
105     void slotPutFinishedOneFile(const BulkUploadItem &singleFile,
106                                 OCC::PutMultiFileJob *job,
107                                 const QJsonObject &fullReplyObject);
108 
109     void done(SyncFileItemPtr item,
110               SyncFileItem::Status status,
111               const QString &errorString);
112 
113     /** Bases headers that need to be sent on the PUT, or in the MOVE for chunking-ng */
114     QMap<QByteArray, QByteArray> headers(SyncFileItemPtr item) const;
115 
116     void abortWithError(SyncFileItemPtr item,
117                         SyncFileItem::Status status,
118                         const QString &error);
119 
120     /**
121      * Checks whether the current error is one that should reset the whole
122      * transfer if it happens too often. If so: Bump UploadInfo::errorCount
123      * and maybe perform the reset.
124      */
125     void checkResettingErrors(SyncFileItemPtr item) const;
126 
127     /**
128      * Error handling functionality that is shared between jobs.
129      */
130     void commonErrorHandling(SyncFileItemPtr item,
131                              const QString &errorMessage);
132 
133     bool checkFileStillExists(SyncFileItemPtr item,
134                               const bool finished,
135                               const QString &fullFilePath);
136 
137     bool checkFileChanged(SyncFileItemPtr item,
138                           const bool finished,
139                           const QString &fullFilePath);
140 
141     void computeFileId(SyncFileItemPtr item,
142                        const QJsonObject &fileReply) const;
143 
144     void handleFileRestoration(SyncFileItemPtr item,
145                                const QString &errorString) const;
146 
147     void handleBulkUploadBlackList(SyncFileItemPtr item) const;
148 
149     void handleJobDoneErrors(SyncFileItemPtr item,
150                              SyncFileItem::Status status);
151 
152     void triggerUpload();
153 
154     void checkPropagationIsDone();
155 
156     std::deque<SyncFileItemPtr> _items;
157 
158     QVector<AbstractNetworkJob *> _jobs; /// network jobs that are currently in transit
159 
160     QSet<QString> _pendingChecksumFiles;
161 
162     std::vector<BulkUploadItem> _filesToUpload;
163 
164     SyncFileItem::Status _finalStatus = SyncFileItem::Status::NoStatus;
165 };
166 
167 }
168