1 /* <!-- copyright */
2 /*
3  * aria2 - The high speed download utility
4  *
5  * Copyright (C) 2006 Tatsuhiro Tsujikawa
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of portions of this program with the
23  * OpenSSL library under certain conditions as described in each
24  * individual source file, and distribute linked combinations
25  * including the two.
26  * You must obey the GNU General Public License in all respects
27  * for all of the code used other than OpenSSL.  If you modify
28  * file(s) with this exception, you may extend this exception to your
29  * version of the file(s), but you are not obligated to do so.  If you
30  * do not wish to do so, delete this exception statement from your
31  * version.  If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  */
34 /* copyright --> */
35 #include "DownloadContext.h"
36 
37 #include <algorithm>
38 
39 #include "FileEntry.h"
40 #include "fmt.h"
41 #include "util.h"
42 #include "wallclock.h"
43 #include "DlAbortEx.h"
44 #include "a2functional.h"
45 #include "Signature.h"
46 #include "RequestGroupMan.h"
47 
48 namespace aria2 {
49 
DownloadContext()50 DownloadContext::DownloadContext()
51     : ownerRequestGroup_(nullptr),
52       attrs_(MAX_CTX_ATTR),
53       downloadStopTime_(Timer::zero()),
54       pieceLength_(0),
55       checksumVerified_(false),
56       knowsTotalLength_(true),
57 #ifdef ENABLE_METALINK
58       acceptMetalink_(true)
59 #else  // !ENABLE_METALINK
60       acceptMetalink_(false)
61 #endif // !ENABLE_METALINK
62 {
63 }
64 
DownloadContext(int32_t pieceLength,int64_t totalLength,std::string path)65 DownloadContext::DownloadContext(int32_t pieceLength, int64_t totalLength,
66                                  std::string path)
67     : ownerRequestGroup_(nullptr),
68       attrs_(MAX_CTX_ATTR),
69       downloadStopTime_(Timer::zero()),
70       pieceLength_(pieceLength),
71       checksumVerified_(false),
72       knowsTotalLength_(true),
73 #ifdef ENABLE_METALINK
74       acceptMetalink_(true)
75 #else  // !ENABLE_METALINK
76       acceptMetalink_(false)
77 #endif // !ENABLE_METALINK
78 {
79   fileEntries_.push_back(
80       std::make_shared<FileEntry>(std::move(path), totalLength, 0));
81 }
82 
83 DownloadContext::~DownloadContext() = default;
84 
resetDownloadStartTime()85 void DownloadContext::resetDownloadStartTime()
86 {
87   downloadStopTime_ = Timer::zero();
88   netStat_.downloadStart();
89 }
90 
resetDownloadStopTime()91 void DownloadContext::resetDownloadStopTime()
92 {
93   downloadStopTime_ = global::wallclock();
94   netStat_.downloadStop();
95 }
96 
calculateSessionTime() const97 Timer::Clock::duration DownloadContext::calculateSessionTime() const
98 {
99   const Timer& startTime = netStat_.getDownloadStartTime();
100   return startTime.difference(downloadStopTime_);
101 }
102 
103 std::shared_ptr<FileEntry>
findFileEntryByOffset(int64_t offset) const104 DownloadContext::findFileEntryByOffset(int64_t offset) const
105 {
106   if (fileEntries_.empty() ||
107       (offset > 0 && fileEntries_.back()->getLastOffset() <= offset)) {
108     return nullptr;
109   }
110 
111   auto obj = std::make_shared<FileEntry>();
112   obj->setOffset(offset);
113   auto i = std::upper_bound(fileEntries_.begin(), fileEntries_.end(), obj,
114                             DerefLess<std::shared_ptr<FileEntry>>());
115   if (i != fileEntries_.end() && (*i)->getOffset() == offset) {
116     return *i;
117   }
118   else {
119     return *(--i);
120   }
121 }
122 
setFilePathWithIndex(size_t index,const std::string & path)123 void DownloadContext::setFilePathWithIndex(size_t index,
124                                            const std::string& path)
125 {
126   if (0 < index && index <= fileEntries_.size()) {
127     // We don't escape path because path may come from users.
128     fileEntries_[index - 1]->setPath(path);
129   }
130   else {
131     throw DL_ABORT_EX(
132         fmt("No such file with index=%u", static_cast<unsigned int>(index)));
133   }
134 }
135 
setFileFilter(SegList<int> sgl)136 void DownloadContext::setFileFilter(SegList<int> sgl)
137 {
138   using namespace std::placeholders;
139 
140   if (!sgl.hasNext() || fileEntries_.size() == 1) {
141     std::for_each(fileEntries_.begin(), fileEntries_.end(),
142                   std::bind(&FileEntry::setRequested, _1, true));
143     return;
144   }
145   assert(sgl.peek() >= 1);
146   size_t len = fileEntries_.size();
147   size_t i = 0;
148   for (; i < len && sgl.hasNext(); ++i) {
149     size_t idx = sgl.peek() - 1;
150     if (i == idx) {
151       fileEntries_[i]->setRequested(true);
152       sgl.next();
153     }
154     else if (i < idx) {
155       fileEntries_[i]->setRequested(false);
156     }
157   }
158   for (; i < len; ++i) {
159     fileEntries_[i]->setRequested(false);
160   }
161 }
162 
setAttribute(ContextAttributeType key,std::shared_ptr<ContextAttribute> value)163 void DownloadContext::setAttribute(ContextAttributeType key,
164                                    std::shared_ptr<ContextAttribute> value)
165 {
166   assert(key < MAX_CTX_ATTR);
167   attrs_[key] = std::move(value);
168 }
169 
170 const std::shared_ptr<ContextAttribute>&
getAttribute(ContextAttributeType key)171 DownloadContext::getAttribute(ContextAttributeType key)
172 {
173   assert(key < MAX_CTX_ATTR);
174   const auto& attr = attrs_[key];
175   if (!attr) {
176     throw DL_ABORT_EX(
177         fmt("No attribute named %s", strContextAttributeType(key)));
178   }
179 
180   return attr;
181 }
182 
hasAttribute(ContextAttributeType key) const183 bool DownloadContext::hasAttribute(ContextAttributeType key) const
184 {
185   assert(key < MAX_CTX_ATTR);
186   return attrs_[key].get();
187 }
188 
189 const std::vector<std::shared_ptr<ContextAttribute>>&
getAttributes() const190 DownloadContext::getAttributes() const
191 {
192   return attrs_;
193 }
194 
releaseRuntimeResource()195 void DownloadContext::releaseRuntimeResource()
196 {
197   for (std::vector<std::shared_ptr<FileEntry>>::const_iterator
198            i = fileEntries_.begin(),
199            eoi = fileEntries_.end();
200        i != eoi; ++i) {
201     (*i)->putBackRequest();
202     (*i)->releaseRuntimeResource();
203   }
204 }
205 
getNumPieces() const206 size_t DownloadContext::getNumPieces() const
207 {
208   if (pieceLength_ == 0) {
209     return 0;
210   }
211   else {
212     assert(!fileEntries_.empty());
213     return (fileEntries_.back()->getLastOffset() + pieceLength_ - 1) /
214            pieceLength_;
215   }
216 }
217 
getTotalLength() const218 int64_t DownloadContext::getTotalLength() const
219 {
220   if (fileEntries_.empty()) {
221     return 0;
222   }
223   else {
224     return fileEntries_.back()->getLastOffset();
225   }
226 }
227 
getBasePath() const228 const std::string& DownloadContext::getBasePath() const
229 {
230   if (basePath_.empty()) {
231     assert(!fileEntries_.empty());
232     return getFirstFileEntry()->getPath();
233   }
234   else {
235     return basePath_;
236   }
237 }
238 
getFirstRequestedFileEntry() const239 std::shared_ptr<FileEntry> DownloadContext::getFirstRequestedFileEntry() const
240 {
241   for (auto& e : fileEntries_) {
242     if (e->isRequested()) {
243       return e;
244     }
245   }
246   return nullptr;
247 }
248 
countRequestedFileEntry() const249 size_t DownloadContext::countRequestedFileEntry() const
250 {
251   size_t numFiles = 0;
252   for (const auto& e : fileEntries_) {
253     if (e->isRequested()) {
254       ++numFiles;
255     }
256   }
257   return numFiles;
258 }
259 
isChecksumVerificationNeeded() const260 bool DownloadContext::isChecksumVerificationNeeded() const
261 {
262   return pieceHashType_.empty() && !digest_.empty() && !hashType_.empty() &&
263          !checksumVerified_;
264 }
265 
isChecksumVerificationAvailable() const266 bool DownloadContext::isChecksumVerificationAvailable() const
267 {
268   return !digest_.empty() && !hashType_.empty();
269 }
270 
isPieceHashVerificationAvailable() const271 bool DownloadContext::isPieceHashVerificationAvailable() const
272 {
273   return !pieceHashType_.empty() && pieceHashes_.size() > 0 &&
274          pieceHashes_.size() == getNumPieces();
275 }
276 
getPieceHash(size_t index) const277 const std::string& DownloadContext::getPieceHash(size_t index) const
278 {
279   if (index < pieceHashes_.size()) {
280     return pieceHashes_[index];
281   }
282   else {
283     return A2STR::NIL;
284   }
285 }
286 
setDigest(const std::string & hashType,const std::string & digest)287 void DownloadContext::setDigest(const std::string& hashType,
288                                 const std::string& digest)
289 {
290   hashType_ = hashType;
291   digest_ = digest;
292 }
293 
setBasePath(const std::string & basePath)294 void DownloadContext::setBasePath(const std::string& basePath)
295 {
296   basePath_ = basePath;
297 }
298 
setSignature(std::unique_ptr<Signature> signature)299 void DownloadContext::setSignature(std::unique_ptr<Signature> signature)
300 {
301   signature_ = std::move(signature);
302 }
303 
updateDownload(size_t bytes)304 void DownloadContext::updateDownload(size_t bytes)
305 {
306   netStat_.updateDownload(bytes);
307   RequestGroupMan* rgman = ownerRequestGroup_->getRequestGroupMan();
308   if (rgman) {
309     rgman->getNetStat().updateDownload(bytes);
310   }
311 }
312 
updateUploadSpeed(size_t bytes)313 void DownloadContext::updateUploadSpeed(size_t bytes)
314 {
315   netStat_.updateUploadSpeed(bytes);
316   auto rgman = ownerRequestGroup_->getRequestGroupMan();
317   if (rgman) {
318     rgman->getNetStat().updateUploadSpeed(bytes);
319   }
320 }
321 
updateUploadLength(size_t bytes)322 void DownloadContext::updateUploadLength(size_t bytes)
323 {
324   netStat_.updateUploadLength(bytes);
325   auto rgman = ownerRequestGroup_->getRequestGroupMan();
326   if (rgman) {
327     rgman->getNetStat().updateUploadLength(bytes);
328   }
329 }
330 
331 } // namespace aria2
332