1 /* <!-- copyright */
2 /*
3 * aria2 - The high speed download utility
4 *
5 * Copyright (C) 2009 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 "RpcMethodImpl.h"
36
37 #include <cassert>
38 #include <algorithm>
39 #include <sstream>
40
41 #include "Logger.h"
42 #include "LogFactory.h"
43 #include "DlAbortEx.h"
44 #include "Option.h"
45 #include "OptionParser.h"
46 #include "OptionHandler.h"
47 #include "DownloadEngine.h"
48 #include "RequestGroup.h"
49 #include "download_helper.h"
50 #include "util.h"
51 #include "fmt.h"
52 #include "RpcRequest.h"
53 #include "PieceStorage.h"
54 #include "DownloadContext.h"
55 #include "DiskAdaptor.h"
56 #include "FileEntry.h"
57 #include "prefs.h"
58 #include "message.h"
59 #include "FeatureConfig.h"
60 #include "array_fun.h"
61 #include "RpcMethodFactory.h"
62 #include "RpcResponse.h"
63 #include "SegmentMan.h"
64 #include "TimedHaltCommand.h"
65 #include "PeerStat.h"
66 #include "base64.h"
67 #include "BitfieldMan.h"
68 #include "SessionSerializer.h"
69 #include "MessageDigest.h"
70 #include "message_digest_helper.h"
71 #include "OpenedFileCounter.h"
72 #ifdef ENABLE_BITTORRENT
73 # include "bittorrent_helper.h"
74 # include "BtRegistry.h"
75 # include "PeerStorage.h"
76 # include "Peer.h"
77 # include "BtRuntime.h"
78 # include "BtAnnounce.h"
79 #endif // ENABLE_BITTORRENT
80 #include "CheckIntegrityEntry.h"
81
82 namespace aria2 {
83
84 namespace rpc {
85
86 namespace {
87 const char VLB_TRUE[] = "true";
88 const char VLB_FALSE[] = "false";
89 const char VLB_ACTIVE[] = "active";
90 const char VLB_WAITING[] = "waiting";
91 const char VLB_PAUSED[] = "paused";
92 const char VLB_REMOVED[] = "removed";
93 const char VLB_ERROR[] = "error";
94 const char VLB_COMPLETE[] = "complete";
95 const char VLB_USED[] = "used";
96 const char VLB_ZERO[] = "0";
97
98 const char KEY_GID[] = "gid";
99 const char KEY_ERROR_CODE[] = "errorCode";
100 const char KEY_ERROR_MESSAGE[] = "errorMessage";
101 const char KEY_STATUS[] = "status";
102 const char KEY_TOTAL_LENGTH[] = "totalLength";
103 const char KEY_COMPLETED_LENGTH[] = "completedLength";
104 const char KEY_DOWNLOAD_SPEED[] = "downloadSpeed";
105 const char KEY_UPLOAD_SPEED[] = "uploadSpeed";
106 const char KEY_UPLOAD_LENGTH[] = "uploadLength";
107 const char KEY_CONNECTIONS[] = "connections";
108 const char KEY_BITFIELD[] = "bitfield";
109 const char KEY_PIECE_LENGTH[] = "pieceLength";
110 const char KEY_NUM_PIECES[] = "numPieces";
111 const char KEY_FOLLOWED_BY[] = "followedBy";
112 const char KEY_FOLLOWING[] = "following";
113 const char KEY_BELONGS_TO[] = "belongsTo";
114 const char KEY_INFO_HASH[] = "infoHash";
115 const char KEY_NUM_SEEDERS[] = "numSeeders";
116 const char KEY_PEER_ID[] = "peerId";
117 const char KEY_IP[] = "ip";
118 const char KEY_PORT[] = "port";
119 const char KEY_AM_CHOKING[] = "amChoking";
120 const char KEY_PEER_CHOKING[] = "peerChoking";
121 const char KEY_SEEDER[] = "seeder";
122 const char KEY_INDEX[] = "index";
123 const char KEY_PATH[] = "path";
124 const char KEY_SELECTED[] = "selected";
125 const char KEY_LENGTH[] = "length";
126 const char KEY_URI[] = "uri";
127 const char KEY_CURRENT_URI[] = "currentUri";
128 const char KEY_VERSION[] = "version";
129 const char KEY_ENABLED_FEATURES[] = "enabledFeatures";
130 const char KEY_METHOD_NAME[] = "methodName";
131 const char KEY_PARAMS[] = "params";
132 const char KEY_SESSION_ID[] = "sessionId";
133 const char KEY_FILES[] = "files";
134 const char KEY_DIR[] = "dir";
135 const char KEY_URIS[] = "uris";
136 const char KEY_BITTORRENT[] = "bittorrent";
137 const char KEY_INFO[] = "info";
138 const char KEY_NAME[] = "name";
139 const char KEY_ANNOUNCE_LIST[] = "announceList";
140 const char KEY_COMMENT[] = "comment";
141 const char KEY_CREATION_DATE[] = "creationDate";
142 const char KEY_MODE[] = "mode";
143 const char KEY_SERVERS[] = "servers";
144 const char KEY_NUM_WAITING[] = "numWaiting";
145 const char KEY_NUM_STOPPED[] = "numStopped";
146 const char KEY_NUM_ACTIVE[] = "numActive";
147 const char KEY_NUM_STOPPED_TOTAL[] = "numStoppedTotal";
148 const char KEY_VERIFIED_LENGTH[] = "verifiedLength";
149 const char KEY_VERIFY_PENDING[] = "verifyIntegrityPending";
150 } // namespace
151
152 namespace {
createGIDResponse(a2_gid_t gid)153 std::unique_ptr<ValueBase> createGIDResponse(a2_gid_t gid)
154 {
155 return String::g(GroupId::toHex(gid));
156 }
157 } // namespace
158
159 namespace {
createOKResponse()160 std::unique_ptr<ValueBase> createOKResponse() { return String::g("OK"); }
161 } // namespace
162
163 namespace {
164 std::unique_ptr<ValueBase>
addRequestGroup(const std::shared_ptr<RequestGroup> & group,DownloadEngine * e,bool posGiven,int pos)165 addRequestGroup(const std::shared_ptr<RequestGroup>& group, DownloadEngine* e,
166 bool posGiven, int pos)
167 {
168 if (posGiven) {
169 e->getRequestGroupMan()->insertReservedGroup(pos, group);
170 }
171 else {
172 e->getRequestGroupMan()->addReservedGroup(group);
173 }
174 return createGIDResponse(group->getGID());
175 }
176 } // namespace
177
178 namespace {
checkPosParam(const Integer * posParam)179 bool checkPosParam(const Integer* posParam)
180 {
181 if (posParam) {
182 if (posParam->i() >= 0) {
183 return true;
184 }
185 else {
186 throw DL_ABORT_EX("Position must be greater than or equal to 0.");
187 }
188 }
189 return false;
190 }
191 } // namespace
192
193 namespace {
str2Gid(const String * str)194 a2_gid_t str2Gid(const String* str)
195 {
196 assert(str);
197 if (str->s().size() > sizeof(a2_gid_t) * 2) {
198 throw DL_ABORT_EX(fmt("Invalid GID %s", str->s().c_str()));
199 }
200 a2_gid_t n;
201 switch (GroupId::expandUnique(n, str->s().c_str())) {
202 case GroupId::ERR_NOT_UNIQUE:
203 throw DL_ABORT_EX(fmt("GID %s is not unique", str->s().c_str()));
204 case GroupId::ERR_NOT_FOUND:
205 throw DL_ABORT_EX(fmt("GID %s is not found", str->s().c_str()));
206 case GroupId::ERR_INVALID:
207 throw DL_ABORT_EX(fmt("Invalid GID %s", str->s().c_str()));
208 }
209 return n;
210 }
211 } // namespace
212
213 namespace {
214 template <typename OutputIterator>
extractUris(OutputIterator out,const List * src)215 void extractUris(OutputIterator out, const List* src)
216 {
217 if (src) {
218 for (auto& elem : *src) {
219 const String* uri = downcast<String>(elem);
220 if (uri) {
221 out++ = uri->s();
222 }
223 }
224 }
225 }
226 } // namespace
227
process(const RpcRequest & req,DownloadEngine * e)228 std::unique_ptr<ValueBase> AddUriRpcMethod::process(const RpcRequest& req,
229 DownloadEngine* e)
230 {
231 const List* urisParam = checkRequiredParam<List>(req, 0);
232 const Dict* optsParam = checkParam<Dict>(req, 1);
233 const Integer* posParam = checkParam<Integer>(req, 2);
234
235 std::vector<std::string> uris;
236 extractUris(std::back_inserter(uris), urisParam);
237 if (uris.empty()) {
238 throw DL_ABORT_EX("URI is not provided.");
239 }
240
241 auto requestOption = std::make_shared<Option>(*e->getOption());
242 gatherRequestOption(requestOption.get(), optsParam);
243
244 bool posGiven = checkPosParam(posParam);
245 size_t pos = posGiven ? posParam->i() : 0;
246
247 std::vector<std::shared_ptr<RequestGroup>> result;
248 createRequestGroupForUri(result, requestOption, uris,
249 /* ignoreForceSeq = */ true,
250 /* ignoreLocalPath = */ true);
251
252 if (!result.empty()) {
253 return addRequestGroup(result.front(), e, posGiven, pos);
254 }
255 else {
256 throw DL_ABORT_EX("No URI to download.");
257 }
258 }
259
260 namespace {
getHexSha1(const std::string & s)261 std::string getHexSha1(const std::string& s)
262 {
263 unsigned char hash[20];
264 message_digest::digest(hash, sizeof(hash), MessageDigest::sha1().get(),
265 s.data(), s.size());
266 return util::toHex(hash, sizeof(hash));
267 }
268 } // namespace
269
270 #ifdef ENABLE_BITTORRENT
process(const RpcRequest & req,DownloadEngine * e)271 std::unique_ptr<ValueBase> AddTorrentRpcMethod::process(const RpcRequest& req,
272 DownloadEngine* e)
273 {
274 const String* torrentParam = checkRequiredParam<String>(req, 0);
275 const List* urisParam = checkParam<List>(req, 1);
276 const Dict* optsParam = checkParam<Dict>(req, 2);
277 const Integer* posParam = checkParam<Integer>(req, 3);
278
279 std::unique_ptr<String> tempTorrentParam;
280 if (req.jsonRpc) {
281 tempTorrentParam = String::g(
282 base64::decode(torrentParam->s().begin(), torrentParam->s().end()));
283 torrentParam = tempTorrentParam.get();
284 }
285 std::vector<std::string> uris;
286 extractUris(std::back_inserter(uris), urisParam);
287
288 auto requestOption = std::make_shared<Option>(*e->getOption());
289 gatherRequestOption(requestOption.get(), optsParam);
290
291 bool posGiven = checkPosParam(posParam);
292 size_t pos = posGiven ? posParam->i() : 0;
293
294 std::string filename;
295 if (requestOption->getAsBool(PREF_RPC_SAVE_UPLOAD_METADATA)) {
296 filename = util::applyDir(requestOption->get(PREF_DIR),
297 getHexSha1(torrentParam->s()) + ".torrent");
298 // Save uploaded data in order to save this download in
299 // --save-session file.
300 if (util::saveAs(filename, torrentParam->s(), true)) {
301 A2_LOG_INFO(
302 fmt("Uploaded torrent data was saved as %s", filename.c_str()));
303 requestOption->put(PREF_TORRENT_FILE, filename);
304 }
305 else {
306 A2_LOG_INFO(fmt("Uploaded torrent data was not saved."
307 " Failed to write file %s",
308 filename.c_str()));
309 filename.clear();
310 }
311 }
312 std::vector<std::shared_ptr<RequestGroup>> result;
313 createRequestGroupForBitTorrent(result, requestOption, uris, filename,
314 torrentParam->s());
315
316 if (!result.empty()) {
317 return addRequestGroup(result.front(), e, posGiven, pos);
318 }
319 else {
320 throw DL_ABORT_EX("No Torrent to download.");
321 }
322 }
323 #endif // ENABLE_BITTORRENT
324
325 #ifdef ENABLE_METALINK
process(const RpcRequest & req,DownloadEngine * e)326 std::unique_ptr<ValueBase> AddMetalinkRpcMethod::process(const RpcRequest& req,
327 DownloadEngine* e)
328 {
329 const String* metalinkParam = checkRequiredParam<String>(req, 0);
330 const Dict* optsParam = checkParam<Dict>(req, 1);
331 const Integer* posParam = checkParam<Integer>(req, 2);
332
333 std::unique_ptr<String> tempMetalinkParam;
334 if (req.jsonRpc) {
335 tempMetalinkParam = String::g(
336 base64::decode(metalinkParam->s().begin(), metalinkParam->s().end()));
337 metalinkParam = tempMetalinkParam.get();
338 }
339 auto requestOption = std::make_shared<Option>(*e->getOption());
340 gatherRequestOption(requestOption.get(), optsParam);
341
342 bool posGiven = checkPosParam(posParam);
343 size_t pos = posGiven ? posParam->i() : 0;
344
345 std::vector<std::shared_ptr<RequestGroup>> result;
346 std::string filename;
347 if (requestOption->getAsBool(PREF_RPC_SAVE_UPLOAD_METADATA)) {
348 // TODO RFC5854 Metalink has the extension .meta4 and Metalink
349 // Version 3 uses .metalink extension. We use .meta4 for both
350 // RFC5854 Metalink and Version 3. aria2 can detect which of which
351 // by reading content rather than extension.
352 filename = util::applyDir(requestOption->get(PREF_DIR),
353 getHexSha1(metalinkParam->s()) + ".meta4");
354 // Save uploaded data in order to save this download in
355 // --save-session file.
356 if (util::saveAs(filename, metalinkParam->s(), true)) {
357 A2_LOG_INFO(
358 fmt("Uploaded metalink data was saved as %s", filename.c_str()));
359 requestOption->put(PREF_METALINK_FILE, filename);
360 createRequestGroupForMetalink(result, requestOption);
361 }
362 else {
363 A2_LOG_INFO(fmt("Uploaded metalink data was not saved."
364 " Failed to write file %s",
365 filename.c_str()));
366 createRequestGroupForMetalink(result, requestOption, metalinkParam->s());
367 }
368 }
369 else {
370 createRequestGroupForMetalink(result, requestOption, metalinkParam->s());
371 }
372 auto gids = List::g();
373 if (!result.empty()) {
374 if (posGiven) {
375 e->getRequestGroupMan()->insertReservedGroup(pos, result);
376 }
377 else {
378 e->getRequestGroupMan()->addReservedGroup(result);
379 }
380 for (auto& i : result) {
381 gids->append(GroupId::toHex(i->getGID()));
382 }
383 }
384 return std::move(gids);
385 }
386 #endif // ENABLE_METALINK
387
388 namespace {
removeDownload(const RpcRequest & req,DownloadEngine * e,bool forceRemove)389 std::unique_ptr<ValueBase> removeDownload(const RpcRequest& req,
390 DownloadEngine* e, bool forceRemove)
391 {
392 const String* gidParam = checkRequiredParam<String>(req, 0);
393
394 a2_gid_t gid = str2Gid(gidParam);
395 auto group = e->getRequestGroupMan()->findGroup(gid);
396 if (group) {
397 if (group->getState() == RequestGroup::STATE_ACTIVE) {
398 if (forceRemove) {
399 group->setForceHaltRequested(true, RequestGroup::USER_REQUEST);
400 }
401 else {
402 group->setHaltRequested(true, RequestGroup::USER_REQUEST);
403 }
404 e->setRefreshInterval(std::chrono::milliseconds(0));
405 }
406 else {
407 if (group->isDependencyResolved()) {
408 e->getRequestGroupMan()->removeReservedGroup(gid);
409 }
410 else {
411 throw DL_ABORT_EX(
412 fmt("GID#%s cannot be removed now", GroupId::toHex(gid).c_str()));
413 }
414 }
415 }
416 else {
417 throw DL_ABORT_EX(fmt("Active Download not found for GID#%s",
418 GroupId::toHex(gid).c_str()));
419 }
420 return createGIDResponse(gid);
421 }
422 } // namespace
423
process(const RpcRequest & req,DownloadEngine * e)424 std::unique_ptr<ValueBase> RemoveRpcMethod::process(const RpcRequest& req,
425 DownloadEngine* e)
426 {
427 return removeDownload(req, e, false);
428 }
429
process(const RpcRequest & req,DownloadEngine * e)430 std::unique_ptr<ValueBase> ForceRemoveRpcMethod::process(const RpcRequest& req,
431 DownloadEngine* e)
432 {
433 return removeDownload(req, e, true);
434 }
435
436 namespace {
pauseDownload(const RpcRequest & req,DownloadEngine * e,bool forcePause)437 std::unique_ptr<ValueBase> pauseDownload(const RpcRequest& req,
438 DownloadEngine* e, bool forcePause)
439 {
440 const String* gidParam = checkRequiredParam<String>(req, 0);
441
442 a2_gid_t gid = str2Gid(gidParam);
443 auto group = e->getRequestGroupMan()->findGroup(gid);
444 if (group) {
445 bool reserved = group->getState() == RequestGroup::STATE_WAITING;
446 if (pauseRequestGroup(group, reserved, forcePause)) {
447 e->setRefreshInterval(std::chrono::milliseconds(0));
448 return createGIDResponse(gid);
449 }
450 }
451 throw DL_ABORT_EX(
452 fmt("GID#%s cannot be paused now", GroupId::toHex(gid).c_str()));
453 }
454 } // namespace
455
process(const RpcRequest & req,DownloadEngine * e)456 std::unique_ptr<ValueBase> PauseRpcMethod::process(const RpcRequest& req,
457 DownloadEngine* e)
458 {
459 return pauseDownload(req, e, false);
460 }
461
process(const RpcRequest & req,DownloadEngine * e)462 std::unique_ptr<ValueBase> ForcePauseRpcMethod::process(const RpcRequest& req,
463 DownloadEngine* e)
464 {
465 return pauseDownload(req, e, true);
466 }
467
468 namespace {
469 template <typename InputIterator>
pauseRequestGroups(InputIterator first,InputIterator last,bool reserved,bool forcePause)470 void pauseRequestGroups(InputIterator first, InputIterator last, bool reserved,
471 bool forcePause)
472 {
473 for (; first != last; ++first) {
474 pauseRequestGroup(*first, reserved, forcePause);
475 }
476 }
477 } // namespace
478
479 namespace {
pauseAllDownloads(const RpcRequest & req,DownloadEngine * e,bool forcePause)480 std::unique_ptr<ValueBase> pauseAllDownloads(const RpcRequest& req,
481 DownloadEngine* e, bool forcePause)
482 {
483 auto& groups = e->getRequestGroupMan()->getRequestGroups();
484 pauseRequestGroups(groups.begin(), groups.end(), false, forcePause);
485 auto& reservedGroups = e->getRequestGroupMan()->getReservedGroups();
486 pauseRequestGroups(reservedGroups.begin(), reservedGroups.end(), true,
487 forcePause);
488 return createOKResponse();
489 }
490 } // namespace
491
process(const RpcRequest & req,DownloadEngine * e)492 std::unique_ptr<ValueBase> PauseAllRpcMethod::process(const RpcRequest& req,
493 DownloadEngine* e)
494 {
495 return pauseAllDownloads(req, e, false);
496 }
497
498 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)499 ForcePauseAllRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
500 {
501 return pauseAllDownloads(req, e, true);
502 }
503
process(const RpcRequest & req,DownloadEngine * e)504 std::unique_ptr<ValueBase> UnpauseRpcMethod::process(const RpcRequest& req,
505 DownloadEngine* e)
506 {
507 const String* gidParam = checkRequiredParam<String>(req, 0);
508
509 a2_gid_t gid = str2Gid(gidParam);
510 auto group = e->getRequestGroupMan()->findGroup(gid);
511 if (!group || group->getState() != RequestGroup::STATE_WAITING ||
512 !group->isPauseRequested()) {
513 throw DL_ABORT_EX(
514 fmt("GID#%s cannot be unpaused now", GroupId::toHex(gid).c_str()));
515 }
516 else {
517 group->setPauseRequested(false);
518 e->getRequestGroupMan()->requestQueueCheck();
519 }
520 return createGIDResponse(gid);
521 }
522
process(const RpcRequest & req,DownloadEngine * e)523 std::unique_ptr<ValueBase> UnpauseAllRpcMethod::process(const RpcRequest& req,
524 DownloadEngine* e)
525 {
526 auto& groups = e->getRequestGroupMan()->getReservedGroups();
527 for (auto& group : groups) {
528 group->setPauseRequested(false);
529 }
530 e->getRequestGroupMan()->requestQueueCheck();
531 return createOKResponse();
532 }
533
534 namespace {
535 template <typename InputIterator>
createUriEntry(List * uriList,InputIterator first,InputIterator last,const std::string & status)536 void createUriEntry(List* uriList, InputIterator first, InputIterator last,
537 const std::string& status)
538 {
539 for (; first != last; ++first) {
540 auto entry = Dict::g();
541 entry->put(KEY_URI, *first);
542 entry->put(KEY_STATUS, status);
543 uriList->append(std::move(entry));
544 }
545 }
546 } // namespace
547
548 namespace {
createUriEntry(List * uriList,const std::shared_ptr<FileEntry> & file)549 void createUriEntry(List* uriList, const std::shared_ptr<FileEntry>& file)
550 {
551 createUriEntry(uriList, std::begin(file->getSpentUris()),
552 std::end(file->getSpentUris()), VLB_USED);
553 createUriEntry(uriList, std::begin(file->getRemainingUris()),
554 std::end(file->getRemainingUris()), VLB_WAITING);
555 }
556 } // namespace
557
558 namespace {
559 template <typename InputIterator>
createFileEntry(List * files,InputIterator first,InputIterator last,const BitfieldMan * bf)560 void createFileEntry(List* files, InputIterator first, InputIterator last,
561 const BitfieldMan* bf)
562 {
563 size_t index = 1;
564 for (; first != last; ++first, ++index) {
565 auto entry = Dict::g();
566 entry->put(KEY_INDEX, util::uitos(index));
567 entry->put(KEY_PATH, (*first)->getPath());
568 entry->put(KEY_SELECTED, (*first)->isRequested() ? VLB_TRUE : VLB_FALSE);
569 entry->put(KEY_LENGTH, util::itos((*first)->getLength()));
570 int64_t completedLength = bf->getOffsetCompletedLength(
571 (*first)->getOffset(), (*first)->getLength());
572 entry->put(KEY_COMPLETED_LENGTH, util::itos(completedLength));
573
574 auto uriList = List::g();
575 createUriEntry(uriList.get(), *first);
576 entry->put(KEY_URIS, std::move(uriList));
577 files->append(std::move(entry));
578 }
579 }
580 } // namespace
581
582 namespace {
583 template <typename InputIterator>
createFileEntry(List * files,InputIterator first,InputIterator last,int64_t totalLength,int32_t pieceLength,const std::string & bitfield)584 void createFileEntry(List* files, InputIterator first, InputIterator last,
585 int64_t totalLength, int32_t pieceLength,
586 const std::string& bitfield)
587 {
588 BitfieldMan bf(pieceLength, totalLength);
589 bf.setBitfield(reinterpret_cast<const unsigned char*>(bitfield.data()),
590 bitfield.size());
591 createFileEntry(files, first, last, &bf);
592 }
593 } // namespace
594
595 namespace {
596 template <typename InputIterator>
createFileEntry(List * files,InputIterator first,InputIterator last,int64_t totalLength,int32_t pieceLength,const std::shared_ptr<PieceStorage> & ps)597 void createFileEntry(List* files, InputIterator first, InputIterator last,
598 int64_t totalLength, int32_t pieceLength,
599 const std::shared_ptr<PieceStorage>& ps)
600 {
601 BitfieldMan bf(pieceLength, totalLength);
602 if (ps) {
603 bf.setBitfield(ps->getBitfield(), ps->getBitfieldLength());
604 }
605 createFileEntry(files, first, last, &bf);
606 }
607 } // namespace
608
609 namespace {
requested_key(const std::vector<std::string> & keys,const std::string & k)610 bool requested_key(const std::vector<std::string>& keys, const std::string& k)
611 {
612 return keys.empty() || std::find(keys.begin(), keys.end(), k) != keys.end();
613 }
614 } // namespace
615
gatherProgressCommon(Dict * entryDict,const std::shared_ptr<RequestGroup> & group,const std::vector<std::string> & keys)616 void gatherProgressCommon(Dict* entryDict,
617 const std::shared_ptr<RequestGroup>& group,
618 const std::vector<std::string>& keys)
619 {
620 auto& ps = group->getPieceStorage();
621 if (requested_key(keys, KEY_GID)) {
622 entryDict->put(KEY_GID, GroupId::toHex(group->getGID()).c_str());
623 }
624 if (requested_key(keys, KEY_TOTAL_LENGTH)) {
625 // This is "filtered" total length if --select-file is used.
626 entryDict->put(KEY_TOTAL_LENGTH, util::itos(group->getTotalLength()));
627 }
628 if (requested_key(keys, KEY_COMPLETED_LENGTH)) {
629 // This is "filtered" total length if --select-file is used.
630 entryDict->put(KEY_COMPLETED_LENGTH,
631 util::itos(group->getCompletedLength()));
632 }
633 TransferStat stat = group->calculateStat();
634 if (requested_key(keys, KEY_DOWNLOAD_SPEED)) {
635 entryDict->put(KEY_DOWNLOAD_SPEED, util::itos(stat.downloadSpeed));
636 }
637 if (requested_key(keys, KEY_UPLOAD_SPEED)) {
638 entryDict->put(KEY_UPLOAD_SPEED, util::itos(stat.uploadSpeed));
639 }
640 if (requested_key(keys, KEY_UPLOAD_LENGTH)) {
641 entryDict->put(KEY_UPLOAD_LENGTH, util::itos(stat.allTimeUploadLength));
642 }
643 if (requested_key(keys, KEY_CONNECTIONS)) {
644 entryDict->put(KEY_CONNECTIONS, util::itos(group->getNumConnection()));
645 }
646 if (requested_key(keys, KEY_BITFIELD)) {
647 if (ps) {
648 if (ps->getBitfieldLength() > 0) {
649 entryDict->put(KEY_BITFIELD,
650 util::toHex(ps->getBitfield(), ps->getBitfieldLength()));
651 }
652 }
653 }
654 auto& dctx = group->getDownloadContext();
655 if (requested_key(keys, KEY_PIECE_LENGTH)) {
656 entryDict->put(KEY_PIECE_LENGTH, util::itos(dctx->getPieceLength()));
657 }
658 if (requested_key(keys, KEY_NUM_PIECES)) {
659 entryDict->put(KEY_NUM_PIECES, util::uitos(dctx->getNumPieces()));
660 }
661 if (requested_key(keys, KEY_FOLLOWED_BY)) {
662 if (!group->followedBy().empty()) {
663 auto list = List::g();
664 // The element is GID.
665 for (auto& gid : group->followedBy()) {
666 list->append(GroupId::toHex(gid));
667 }
668 entryDict->put(KEY_FOLLOWED_BY, std::move(list));
669 }
670 }
671 if (requested_key(keys, KEY_FOLLOWING)) {
672 if (group->following()) {
673 entryDict->put(KEY_FOLLOWING, GroupId::toHex(group->following()));
674 }
675 }
676 if (requested_key(keys, KEY_BELONGS_TO)) {
677 if (group->belongsTo()) {
678 entryDict->put(KEY_BELONGS_TO, GroupId::toHex(group->belongsTo()));
679 }
680 }
681 if (requested_key(keys, KEY_FILES)) {
682 auto files = List::g();
683 createFileEntry(files.get(), std::begin(dctx->getFileEntries()),
684 std::end(dctx->getFileEntries()), dctx->getTotalLength(),
685 dctx->getPieceLength(), ps);
686 entryDict->put(KEY_FILES, std::move(files));
687 }
688 if (requested_key(keys, KEY_DIR)) {
689 entryDict->put(KEY_DIR, group->getOption()->get(PREF_DIR));
690 }
691 }
692
693 #ifdef ENABLE_BITTORRENT
gatherBitTorrentMetadata(Dict * btDict,TorrentAttribute * torrentAttrs)694 void gatherBitTorrentMetadata(Dict* btDict, TorrentAttribute* torrentAttrs)
695 {
696 if (!torrentAttrs->comment.empty()) {
697 btDict->put(KEY_COMMENT, torrentAttrs->comment);
698 }
699 if (torrentAttrs->creationDate) {
700 btDict->put(KEY_CREATION_DATE, Integer::g(torrentAttrs->creationDate));
701 }
702 if (torrentAttrs->mode) {
703 btDict->put(KEY_MODE, bittorrent::getModeString(torrentAttrs->mode));
704 }
705 auto destAnnounceList = List::g();
706 for (auto& annlist : torrentAttrs->announceList) {
707 auto destAnnounceTier = List::g();
708 for (auto& ann : annlist) {
709 destAnnounceTier->append(ann);
710 }
711 destAnnounceList->append(std::move(destAnnounceTier));
712 }
713 btDict->put(KEY_ANNOUNCE_LIST, std::move(destAnnounceList));
714 if (!torrentAttrs->metadata.empty()) {
715 auto infoDict = Dict::g();
716 infoDict->put(KEY_NAME, torrentAttrs->name);
717 btDict->put(KEY_INFO, std::move(infoDict));
718 }
719 }
720
721 namespace {
gatherProgressBitTorrent(Dict * entryDict,const std::shared_ptr<RequestGroup> & group,TorrentAttribute * torrentAttrs,BtObject * btObject,const std::vector<std::string> & keys)722 void gatherProgressBitTorrent(Dict* entryDict,
723 const std::shared_ptr<RequestGroup>& group,
724 TorrentAttribute* torrentAttrs,
725 BtObject* btObject,
726 const std::vector<std::string>& keys)
727 {
728 if (requested_key(keys, KEY_INFO_HASH)) {
729 entryDict->put(KEY_INFO_HASH, util::toHex(torrentAttrs->infoHash));
730 }
731 if (requested_key(keys, KEY_BITTORRENT)) {
732 auto btDict = Dict::g();
733 gatherBitTorrentMetadata(btDict.get(), torrentAttrs);
734 entryDict->put(KEY_BITTORRENT, std::move(btDict));
735 }
736 if (requested_key(keys, KEY_NUM_SEEDERS)) {
737 if (!btObject) {
738 entryDict->put(KEY_NUM_SEEDERS, VLB_ZERO);
739 }
740 else {
741 auto& peerStorage = btObject->peerStorage;
742 assert(peerStorage);
743 auto& peers = peerStorage->getUsedPeers();
744 entryDict->put(KEY_NUM_SEEDERS,
745 util::uitos(countSeeder(peers.begin(), peers.end())));
746 }
747 }
748 if (requested_key(keys, KEY_SEEDER)) {
749 entryDict->put(KEY_SEEDER, group->isSeeder() ? VLB_TRUE : VLB_FALSE);
750 }
751 }
752 } // namespace
753
754 namespace {
gatherPeer(List * peers,const std::shared_ptr<PeerStorage> & ps)755 void gatherPeer(List* peers, const std::shared_ptr<PeerStorage>& ps)
756 {
757 auto& usedPeers = ps->getUsedPeers();
758 for (auto& peer : usedPeers) {
759 if (!peer->isActive()) {
760 continue;
761 }
762 auto peerEntry = Dict::g();
763 peerEntry->put(KEY_PEER_ID, util::torrentPercentEncode(peer->getPeerId(),
764 PEER_ID_LENGTH));
765 peerEntry->put(KEY_IP, peer->getIPAddress());
766 if (peer->isIncomingPeer()) {
767 peerEntry->put(KEY_PORT, VLB_ZERO);
768 }
769 else {
770 peerEntry->put(KEY_PORT, util::uitos(peer->getPort()));
771 }
772 peerEntry->put(KEY_BITFIELD,
773 util::toHex(peer->getBitfield(), peer->getBitfieldLength()));
774 peerEntry->put(KEY_AM_CHOKING, peer->amChoking() ? VLB_TRUE : VLB_FALSE);
775 peerEntry->put(KEY_PEER_CHOKING,
776 peer->peerChoking() ? VLB_TRUE : VLB_FALSE);
777 peerEntry->put(KEY_DOWNLOAD_SPEED,
778 util::itos(peer->calculateDownloadSpeed()));
779 peerEntry->put(KEY_UPLOAD_SPEED, util::itos(peer->calculateUploadSpeed()));
780 peerEntry->put(KEY_SEEDER, peer->isSeeder() ? VLB_TRUE : VLB_FALSE);
781 peers->append(std::move(peerEntry));
782 }
783 }
784 } // namespace
785 #endif // ENABLE_BITTORRENT
786
787 namespace {
gatherProgress(Dict * entryDict,const std::shared_ptr<RequestGroup> & group,DownloadEngine * e,const std::vector<std::string> & keys)788 void gatherProgress(Dict* entryDict, const std::shared_ptr<RequestGroup>& group,
789 DownloadEngine* e, const std::vector<std::string>& keys)
790 {
791 gatherProgressCommon(entryDict, group, keys);
792 #ifdef ENABLE_BITTORRENT
793 if (group->getDownloadContext()->hasAttribute(CTX_ATTR_BT)) {
794 gatherProgressBitTorrent(
795 entryDict, group,
796 bittorrent::getTorrentAttrs(group->getDownloadContext()),
797 e->getBtRegistry()->get(group->getGID()), keys);
798 }
799 #endif // ENABLE_BITTORRENT
800 if (e->getCheckIntegrityMan()) {
801 if (e->getCheckIntegrityMan()->isPicked(
802 [&group](const CheckIntegrityEntry& ent) {
803 return ent.getRequestGroup() == group.get();
804 })) {
805 entryDict->put(
806 KEY_VERIFIED_LENGTH,
807 util::itos(
808 e->getCheckIntegrityMan()->getPickedEntry()->getCurrentLength()));
809 }
810 if (e->getCheckIntegrityMan()->isQueued(
811 [&group](const CheckIntegrityEntry& ent) {
812 return ent.getRequestGroup() == group.get();
813 })) {
814 entryDict->put(KEY_VERIFY_PENDING, VLB_TRUE);
815 }
816 }
817 }
818 } // namespace
819
gatherStoppedDownload(Dict * entryDict,const std::shared_ptr<DownloadResult> & ds,const std::vector<std::string> & keys)820 void gatherStoppedDownload(Dict* entryDict,
821 const std::shared_ptr<DownloadResult>& ds,
822 const std::vector<std::string>& keys)
823 {
824 if (requested_key(keys, KEY_GID)) {
825 entryDict->put(KEY_GID, ds->gid->toHex());
826 }
827 if (requested_key(keys, KEY_ERROR_CODE)) {
828 entryDict->put(KEY_ERROR_CODE, util::itos(static_cast<int>(ds->result)));
829 }
830 if (requested_key(keys, KEY_ERROR_MESSAGE)) {
831 entryDict->put(KEY_ERROR_MESSAGE, ds->resultMessage);
832 }
833 if (requested_key(keys, KEY_STATUS)) {
834 if (ds->result == error_code::REMOVED) {
835 entryDict->put(KEY_STATUS, VLB_REMOVED);
836 }
837 else if (ds->result == error_code::FINISHED) {
838 entryDict->put(KEY_STATUS, VLB_COMPLETE);
839 }
840 else {
841 entryDict->put(KEY_STATUS, VLB_ERROR);
842 }
843 }
844 if (requested_key(keys, KEY_FOLLOWED_BY)) {
845 if (!ds->followedBy.empty()) {
846 auto list = List::g();
847 // The element is GID.
848 for (auto gid : ds->followedBy) {
849 list->append(GroupId::toHex(gid));
850 }
851 entryDict->put(KEY_FOLLOWED_BY, std::move(list));
852 }
853 }
854 if (requested_key(keys, KEY_FOLLOWING)) {
855 if (ds->following) {
856 entryDict->put(KEY_FOLLOWING, GroupId::toHex(ds->following));
857 }
858 }
859 if (requested_key(keys, KEY_BELONGS_TO)) {
860 if (ds->belongsTo) {
861 entryDict->put(KEY_BELONGS_TO, GroupId::toHex(ds->belongsTo));
862 }
863 }
864 if (requested_key(keys, KEY_FILES)) {
865 auto files = List::g();
866 createFileEntry(files.get(), std::begin(ds->fileEntries),
867 std::end(ds->fileEntries), ds->totalLength, ds->pieceLength,
868 ds->bitfield);
869 entryDict->put(KEY_FILES, std::move(files));
870 }
871 if (requested_key(keys, KEY_TOTAL_LENGTH)) {
872 entryDict->put(KEY_TOTAL_LENGTH, util::itos(ds->totalLength));
873 }
874 if (requested_key(keys, KEY_COMPLETED_LENGTH)) {
875 entryDict->put(KEY_COMPLETED_LENGTH, util::itos(ds->completedLength));
876 }
877 if (requested_key(keys, KEY_UPLOAD_LENGTH)) {
878 entryDict->put(KEY_UPLOAD_LENGTH, util::itos(ds->uploadLength));
879 }
880 if (requested_key(keys, KEY_BITFIELD)) {
881 if (!ds->bitfield.empty()) {
882 entryDict->put(KEY_BITFIELD, util::toHex(ds->bitfield));
883 }
884 }
885 if (requested_key(keys, KEY_DOWNLOAD_SPEED)) {
886 entryDict->put(KEY_DOWNLOAD_SPEED, VLB_ZERO);
887 }
888 if (requested_key(keys, KEY_UPLOAD_SPEED)) {
889 entryDict->put(KEY_UPLOAD_SPEED, VLB_ZERO);
890 }
891 if (!ds->infoHash.empty()) {
892 if (requested_key(keys, KEY_INFO_HASH)) {
893 entryDict->put(KEY_INFO_HASH, util::toHex(ds->infoHash));
894 }
895 if (requested_key(keys, KEY_NUM_SEEDERS)) {
896 entryDict->put(KEY_NUM_SEEDERS, VLB_ZERO);
897 }
898 }
899 if (requested_key(keys, KEY_PIECE_LENGTH)) {
900 entryDict->put(KEY_PIECE_LENGTH, util::itos(ds->pieceLength));
901 }
902 if (requested_key(keys, KEY_NUM_PIECES)) {
903 entryDict->put(KEY_NUM_PIECES, util::uitos(ds->numPieces));
904 }
905 if (requested_key(keys, KEY_CONNECTIONS)) {
906 entryDict->put(KEY_CONNECTIONS, VLB_ZERO);
907 }
908 if (requested_key(keys, KEY_DIR)) {
909 entryDict->put(KEY_DIR, ds->dir);
910 }
911
912 #ifdef ENABLE_BITTORRENT
913 if (ds->attrs.size() > CTX_ATTR_BT && ds->attrs[CTX_ATTR_BT]) {
914 const auto attrs =
915 static_cast<TorrentAttribute*>(ds->attrs[CTX_ATTR_BT].get());
916 if (requested_key(keys, KEY_BITTORRENT)) {
917 auto btDict = Dict::g();
918 gatherBitTorrentMetadata(btDict.get(), attrs);
919 entryDict->put(KEY_BITTORRENT, std::move(btDict));
920 }
921 }
922 #endif // ENABLE_BITTORRENT
923 }
924
process(const RpcRequest & req,DownloadEngine * e)925 std::unique_ptr<ValueBase> GetFilesRpcMethod::process(const RpcRequest& req,
926 DownloadEngine* e)
927 {
928 const String* gidParam = checkRequiredParam<String>(req, 0);
929
930 a2_gid_t gid = str2Gid(gidParam);
931 auto files = List::g();
932 auto group = e->getRequestGroupMan()->findGroup(gid);
933 if (!group) {
934 auto dr = e->getRequestGroupMan()->findDownloadResult(gid);
935 if (!dr) {
936 throw DL_ABORT_EX(fmt("No file data is available for GID#%s",
937 GroupId::toHex(gid).c_str()));
938 }
939 else {
940 createFileEntry(files.get(), std::begin(dr->fileEntries),
941 std::end(dr->fileEntries), dr->totalLength,
942 dr->pieceLength, dr->bitfield);
943 }
944 }
945 else {
946 auto& dctx = group->getDownloadContext();
947 createFileEntry(files.get(),
948 std::begin(group->getDownloadContext()->getFileEntries()),
949 std::end(group->getDownloadContext()->getFileEntries()),
950 dctx->getTotalLength(), dctx->getPieceLength(),
951 group->getPieceStorage());
952 }
953 return std::move(files);
954 }
955
process(const RpcRequest & req,DownloadEngine * e)956 std::unique_ptr<ValueBase> GetUrisRpcMethod::process(const RpcRequest& req,
957 DownloadEngine* e)
958 {
959 const String* gidParam = checkRequiredParam<String>(req, 0);
960
961 a2_gid_t gid = str2Gid(gidParam);
962 auto group = e->getRequestGroupMan()->findGroup(gid);
963 if (!group) {
964 throw DL_ABORT_EX(fmt("No URI data is available for GID#%s",
965 GroupId::toHex(gid).c_str()));
966 }
967 auto uriList = List::g();
968 // TODO Current implementation just returns first FileEntry's URIs.
969 if (!group->getDownloadContext()->getFileEntries().empty()) {
970 createUriEntry(uriList.get(),
971 group->getDownloadContext()->getFirstFileEntry());
972 }
973 return std::move(uriList);
974 }
975
976 #ifdef ENABLE_BITTORRENT
process(const RpcRequest & req,DownloadEngine * e)977 std::unique_ptr<ValueBase> GetPeersRpcMethod::process(const RpcRequest& req,
978 DownloadEngine* e)
979 {
980 const String* gidParam = checkRequiredParam<String>(req, 0);
981
982 a2_gid_t gid = str2Gid(gidParam);
983 auto group = e->getRequestGroupMan()->findGroup(gid);
984 if (!group) {
985 throw DL_ABORT_EX(fmt("No peer data is available for GID#%s",
986 GroupId::toHex(gid).c_str()));
987 }
988 auto peers = List::g();
989 auto btObject = e->getBtRegistry()->get(group->getGID());
990 if (btObject) {
991 assert(btObject->peerStorage);
992 gatherPeer(peers.get(), btObject->peerStorage);
993 }
994 return std::move(peers);
995 }
996 #endif // ENABLE_BITTORRENT
997
process(const RpcRequest & req,DownloadEngine * e)998 std::unique_ptr<ValueBase> TellStatusRpcMethod::process(const RpcRequest& req,
999 DownloadEngine* e)
1000 {
1001 const String* gidParam = checkRequiredParam<String>(req, 0);
1002 const List* keysParam = checkParam<List>(req, 1);
1003
1004 a2_gid_t gid = str2Gid(gidParam);
1005 std::vector<std::string> keys;
1006 toStringList(std::back_inserter(keys), keysParam);
1007
1008 auto group = e->getRequestGroupMan()->findGroup(gid);
1009 auto entryDict = Dict::g();
1010 if (!group) {
1011 auto ds = e->getRequestGroupMan()->findDownloadResult(gid);
1012 if (!ds) {
1013 throw DL_ABORT_EX(
1014 fmt("No such download for GID#%s", GroupId::toHex(gid).c_str()));
1015 }
1016 gatherStoppedDownload(entryDict.get(), ds, keys);
1017 }
1018 else {
1019 if (requested_key(keys, KEY_STATUS)) {
1020 if (group->getState() == RequestGroup::STATE_ACTIVE) {
1021 entryDict->put(KEY_STATUS, VLB_ACTIVE);
1022 }
1023 else {
1024 if (group->isPauseRequested()) {
1025 entryDict->put(KEY_STATUS, VLB_PAUSED);
1026 }
1027 else {
1028 entryDict->put(KEY_STATUS, VLB_WAITING);
1029 }
1030 }
1031 }
1032 gatherProgress(entryDict.get(), group, e, keys);
1033 }
1034 return std::move(entryDict);
1035 }
1036
process(const RpcRequest & req,DownloadEngine * e)1037 std::unique_ptr<ValueBase> TellActiveRpcMethod::process(const RpcRequest& req,
1038 DownloadEngine* e)
1039 {
1040 const List* keysParam = checkParam<List>(req, 0);
1041 std::vector<std::string> keys;
1042 toStringList(std::back_inserter(keys), keysParam);
1043 auto list = List::g();
1044 bool statusReq = requested_key(keys, KEY_STATUS);
1045 for (auto& group : e->getRequestGroupMan()->getRequestGroups()) {
1046 auto entryDict = Dict::g();
1047 if (statusReq) {
1048 entryDict->put(KEY_STATUS, VLB_ACTIVE);
1049 }
1050 gatherProgress(entryDict.get(), group, e, keys);
1051 list->append(std::move(entryDict));
1052 }
1053 return std::move(list);
1054 }
1055
getItems(DownloadEngine * e) const1056 const RequestGroupList& TellWaitingRpcMethod::getItems(DownloadEngine* e) const
1057 {
1058 return e->getRequestGroupMan()->getReservedGroups();
1059 }
1060
createEntry(Dict * entryDict,const std::shared_ptr<RequestGroup> & item,DownloadEngine * e,const std::vector<std::string> & keys) const1061 void TellWaitingRpcMethod::createEntry(
1062 Dict* entryDict, const std::shared_ptr<RequestGroup>& item,
1063 DownloadEngine* e, const std::vector<std::string>& keys) const
1064 {
1065 if (requested_key(keys, KEY_STATUS)) {
1066 if (item->isPauseRequested()) {
1067 entryDict->put(KEY_STATUS, VLB_PAUSED);
1068 }
1069 else {
1070 entryDict->put(KEY_STATUS, VLB_WAITING);
1071 }
1072 }
1073 gatherProgress(entryDict, item, e, keys);
1074 }
1075
1076 const DownloadResultList&
getItems(DownloadEngine * e) const1077 TellStoppedRpcMethod::getItems(DownloadEngine* e) const
1078 {
1079 return e->getRequestGroupMan()->getDownloadResults();
1080 }
1081
createEntry(Dict * entryDict,const std::shared_ptr<DownloadResult> & item,DownloadEngine * e,const std::vector<std::string> & keys) const1082 void TellStoppedRpcMethod::createEntry(
1083 Dict* entryDict, const std::shared_ptr<DownloadResult>& item,
1084 DownloadEngine* e, const std::vector<std::string>& keys) const
1085 {
1086 gatherStoppedDownload(entryDict, item, keys);
1087 }
1088
1089 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1090 PurgeDownloadResultRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1091 {
1092 e->getRequestGroupMan()->purgeDownloadResult();
1093 return createOKResponse();
1094 }
1095
1096 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1097 RemoveDownloadResultRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1098 {
1099 const String* gidParam = checkRequiredParam<String>(req, 0);
1100
1101 a2_gid_t gid = str2Gid(gidParam);
1102 if (!e->getRequestGroupMan()->removeDownloadResult(gid)) {
1103 throw DL_ABORT_EX(fmt("Could not remove download result of GID#%s",
1104 GroupId::toHex(gid).c_str()));
1105 }
1106 return createOKResponse();
1107 }
1108
process(const RpcRequest & req,DownloadEngine * e)1109 std::unique_ptr<ValueBase> ChangeOptionRpcMethod::process(const RpcRequest& req,
1110 DownloadEngine* e)
1111 {
1112 const String* gidParam = checkRequiredParam<String>(req, 0);
1113 const Dict* optsParam = checkRequiredParam<Dict>(req, 1);
1114
1115 a2_gid_t gid = str2Gid(gidParam);
1116 auto group = e->getRequestGroupMan()->findGroup(gid);
1117 if (group) {
1118 Option option;
1119 std::shared_ptr<Option> pendingOption;
1120 if (group->getState() == RequestGroup::STATE_ACTIVE) {
1121 pendingOption = std::make_shared<Option>();
1122 gatherChangeableOption(&option, pendingOption.get(), optsParam);
1123 if (!pendingOption->emptyLocal()) {
1124 group->setPendingOption(pendingOption);
1125 // pauseRequestGroup() may fail if group has been told to
1126 // stop/pause already. In that case, we can still apply the
1127 // pending options on pause.
1128 if (pauseRequestGroup(group, false, false)) {
1129 group->setRestartRequested(true);
1130 e->setRefreshInterval(std::chrono::milliseconds(0));
1131 }
1132 }
1133 }
1134 else {
1135 gatherChangeableOptionForReserved(&option, optsParam);
1136 }
1137 changeOption(group, option, e);
1138 }
1139 else {
1140 throw DL_ABORT_EX(
1141 fmt("Cannot change option for GID#%s", GroupId::toHex(gid).c_str()));
1142 }
1143 return createOKResponse();
1144 }
1145
1146 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1147 ChangeGlobalOptionRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1148 {
1149 const Dict* optsParam = checkRequiredParam<Dict>(req, 0);
1150
1151 Option option;
1152 gatherChangeableGlobalOption(&option, optsParam);
1153 changeGlobalOption(option, e);
1154 return createOKResponse();
1155 }
1156
process(const RpcRequest & req,DownloadEngine * e)1157 std::unique_ptr<ValueBase> GetVersionRpcMethod::process(const RpcRequest& req,
1158 DownloadEngine* e)
1159 {
1160 auto result = Dict::g();
1161 result->put(KEY_VERSION, PACKAGE_VERSION);
1162 auto featureList = List::g();
1163 for (int feat = 0; feat < MAX_FEATURE; ++feat) {
1164 const char* name = strSupportedFeature(feat);
1165 if (name) {
1166 featureList->append(name);
1167 }
1168 }
1169 result->put(KEY_ENABLED_FEATURES, std::move(featureList));
1170 return std::move(result);
1171 }
1172
1173 namespace {
pushRequestOption(Dict * dict,const std::shared_ptr<Option> & option,const std::shared_ptr<OptionParser> & oparser)1174 void pushRequestOption(Dict* dict, const std::shared_ptr<Option>& option,
1175 const std::shared_ptr<OptionParser>& oparser)
1176 {
1177 for (size_t i = 1, len = option::countOption(); i < len; ++i) {
1178 PrefPtr pref = option::i2p(i);
1179 const OptionHandler* h = oparser->find(pref);
1180 if (h && h->getInitialOption() && option->defined(pref)) {
1181 dict->put(pref->k, option->get(pref));
1182 }
1183 }
1184 }
1185 } // namespace
1186
process(const RpcRequest & req,DownloadEngine * e)1187 std::unique_ptr<ValueBase> GetOptionRpcMethod::process(const RpcRequest& req,
1188 DownloadEngine* e)
1189 {
1190 const String* gidParam = checkRequiredParam<String>(req, 0);
1191
1192 a2_gid_t gid = str2Gid(gidParam);
1193 auto group = e->getRequestGroupMan()->findGroup(gid);
1194 auto result = Dict::g();
1195 if (!group) {
1196 auto dr = e->getRequestGroupMan()->findDownloadResult(gid);
1197 if (!dr) {
1198 throw DL_ABORT_EX(
1199 fmt("Cannot get option for GID#%s", GroupId::toHex(gid).c_str()));
1200 }
1201 pushRequestOption(result.get(), dr->option, getOptionParser());
1202 }
1203 else {
1204 pushRequestOption(result.get(), group->getOption(), getOptionParser());
1205 }
1206 return std::move(result);
1207 }
1208
1209 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1210 GetGlobalOptionRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1211 {
1212 auto result = Dict::g();
1213 for (size_t i = 0, len = e->getOption()->getTable().size(); i < len; ++i) {
1214 PrefPtr pref = option::i2p(i);
1215 if (pref == PREF_RPC_SECRET || !e->getOption()->defined(pref)) {
1216 continue;
1217 }
1218 const OptionHandler* h = getOptionParser()->find(pref);
1219 if (h) {
1220 result->put(pref->k, e->getOption()->get(pref));
1221 }
1222 }
1223 return std::move(result);
1224 }
1225
1226 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1227 ChangePositionRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1228 {
1229 const String* gidParam = checkRequiredParam<String>(req, 0);
1230 const Integer* posParam = checkRequiredParam<Integer>(req, 1);
1231 const String* howParam = checkRequiredParam<String>(req, 2);
1232
1233 a2_gid_t gid = str2Gid(gidParam);
1234 int pos = posParam->i();
1235 const std::string& howStr = howParam->s();
1236 OffsetMode how;
1237 if (howStr == "POS_SET") {
1238 how = OFFSET_MODE_SET;
1239 }
1240 else if (howStr == "POS_CUR") {
1241 how = OFFSET_MODE_CUR;
1242 }
1243 else if (howStr == "POS_END") {
1244 how = OFFSET_MODE_END;
1245 }
1246 else {
1247 throw DL_ABORT_EX("Illegal argument.");
1248 }
1249 size_t destPos =
1250 e->getRequestGroupMan()->changeReservedGroupPosition(gid, pos, how);
1251 return Integer::g(destPos);
1252 }
1253
1254 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1255 GetSessionInfoRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1256 {
1257 auto result = Dict::g();
1258 result->put(KEY_SESSION_ID, util::toHex(e->getSessionId()));
1259 return std::move(result);
1260 }
1261
process(const RpcRequest & req,DownloadEngine * e)1262 std::unique_ptr<ValueBase> GetServersRpcMethod::process(const RpcRequest& req,
1263 DownloadEngine* e)
1264 {
1265 const String* gidParam = checkRequiredParam<String>(req, 0);
1266
1267 a2_gid_t gid = str2Gid(gidParam);
1268 auto group = e->getRequestGroupMan()->findGroup(gid);
1269 if (!group || group->getState() != RequestGroup::STATE_ACTIVE) {
1270 throw DL_ABORT_EX(
1271 fmt("No active download for GID#%s", GroupId::toHex(gid).c_str()));
1272 }
1273 auto result = List::g();
1274 size_t index = 1;
1275 for (auto& fe : group->getDownloadContext()->getFileEntries()) {
1276 auto fileEntry = Dict::g();
1277 fileEntry->put(KEY_INDEX, util::uitos(index++));
1278 auto servers = List::g();
1279 for (auto& req : fe->getInFlightRequests()) {
1280 auto ps = req->getPeerStat();
1281 if (ps) {
1282 auto serverEntry = Dict::g();
1283 serverEntry->put(KEY_URI, req->getUri());
1284 serverEntry->put(KEY_CURRENT_URI, req->getCurrentUri());
1285 serverEntry->put(KEY_DOWNLOAD_SPEED,
1286 util::itos(ps->calculateDownloadSpeed()));
1287 servers->append(std::move(serverEntry));
1288 }
1289 }
1290 fileEntry->put(KEY_SERVERS, std::move(servers));
1291 result->append(std::move(fileEntry));
1292 }
1293 return std::move(result);
1294 }
1295
process(const RpcRequest & req,DownloadEngine * e)1296 std::unique_ptr<ValueBase> ChangeUriRpcMethod::process(const RpcRequest& req,
1297 DownloadEngine* e)
1298 {
1299 const String* gidParam = checkRequiredParam<String>(req, 0);
1300 const Integer* indexParam = checkRequiredInteger(req, 1, IntegerGE(1));
1301 const List* delUrisParam = checkRequiredParam<List>(req, 2);
1302 const List* addUrisParam = checkRequiredParam<List>(req, 3);
1303 const Integer* posParam = checkParam<Integer>(req, 4);
1304
1305 a2_gid_t gid = str2Gid(gidParam);
1306 bool posGiven = checkPosParam(posParam);
1307 size_t pos = posGiven ? posParam->i() : 0;
1308 size_t index = indexParam->i() - 1;
1309 auto group = e->getRequestGroupMan()->findGroup(gid);
1310 if (!group) {
1311 throw DL_ABORT_EX(
1312 fmt("Cannot remove URIs from GID#%s", GroupId::toHex(gid).c_str()));
1313 }
1314 auto& files = group->getDownloadContext()->getFileEntries();
1315 if (files.size() <= index) {
1316 throw DL_ABORT_EX(fmt("fileIndex is out of range"));
1317 }
1318 auto& s = files[index];
1319 size_t delcount = 0;
1320 for (auto& elem : *delUrisParam) {
1321 const String* uri = downcast<String>(elem);
1322 if (uri && s->removeUri(uri->s())) {
1323 ++delcount;
1324 }
1325 }
1326 size_t addcount = 0;
1327 if (posGiven) {
1328 for (auto& elem : *addUrisParam) {
1329 const String* uri = downcast<String>(elem);
1330 if (uri && s->insertUri(uri->s(), pos)) {
1331 ++addcount;
1332 ++pos;
1333 }
1334 }
1335 }
1336 else {
1337 for (auto& elem : *addUrisParam) {
1338 const String* uri = downcast<String>(elem);
1339 if (uri && s->addUri(uri->s())) {
1340 ++addcount;
1341 }
1342 }
1343 }
1344 if (addcount && group->getPieceStorage()) {
1345 std::vector<std::unique_ptr<Command>> commands;
1346 group->createNextCommand(commands, e);
1347 e->addCommand(std::move(commands));
1348 group->getSegmentMan()->recognizeSegmentFor(s);
1349 }
1350 auto res = List::g();
1351 res->append(Integer::g(delcount));
1352 res->append(Integer::g(addcount));
1353 return std::move(res);
1354 }
1355
1356 namespace {
goingShutdown(const RpcRequest & req,DownloadEngine * e,bool forceHalt)1357 std::unique_ptr<ValueBase> goingShutdown(const RpcRequest& req,
1358 DownloadEngine* e, bool forceHalt)
1359 {
1360 // Schedule shutdown after 3seconds to give time to client to
1361 // receive RPC response.
1362 e->addRoutineCommand(
1363 make_unique<TimedHaltCommand>(e->newCUID(), e, 3_s, forceHalt));
1364 A2_LOG_INFO("Scheduled shutdown in 3 seconds.");
1365 return createOKResponse();
1366 }
1367 } // namespace
1368
process(const RpcRequest & req,DownloadEngine * e)1369 std::unique_ptr<ValueBase> ShutdownRpcMethod::process(const RpcRequest& req,
1370 DownloadEngine* e)
1371 {
1372 return goingShutdown(req, e, false);
1373 }
1374
1375 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1376 ForceShutdownRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1377 {
1378 return goingShutdown(req, e, true);
1379 }
1380
1381 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1382 GetGlobalStatRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1383 {
1384 auto& rgman = e->getRequestGroupMan();
1385 auto ts = rgman->calculateStat();
1386 auto res = Dict::g();
1387 res->put(KEY_DOWNLOAD_SPEED, util::itos(ts.downloadSpeed));
1388 res->put(KEY_UPLOAD_SPEED, util::itos(ts.uploadSpeed));
1389 res->put(KEY_NUM_WAITING, util::uitos(rgman->getReservedGroups().size()));
1390 res->put(KEY_NUM_STOPPED, util::uitos(rgman->getDownloadResults().size()));
1391 res->put(KEY_NUM_STOPPED_TOTAL, util::uitos(rgman->getNumStoppedTotal()));
1392 res->put(KEY_NUM_ACTIVE, util::uitos(rgman->getRequestGroups().size()));
1393 return std::move(res);
1394 }
1395
process(const RpcRequest & req,DownloadEngine * e)1396 std::unique_ptr<ValueBase> SaveSessionRpcMethod::process(const RpcRequest& req,
1397 DownloadEngine* e)
1398 {
1399 const std::string& filename = e->getOption()->get(PREF_SAVE_SESSION);
1400 if (filename.empty()) {
1401 throw DL_ABORT_EX("Filename is not given.");
1402 }
1403 SessionSerializer sessionSerializer(e->getRequestGroupMan().get());
1404 if (sessionSerializer.save(filename)) {
1405 A2_LOG_NOTICE(
1406 fmt(_("Serialized session to '%s' successfully."), filename.c_str()));
1407 return createOKResponse();
1408 }
1409 throw DL_ABORT_EX(
1410 fmt("Failed to serialize session to '%s'.", filename.c_str()));
1411 }
1412
1413 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1414 SystemMulticallRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1415 {
1416 // Should never get here, since SystemMulticallRpcMethod overrides execute().
1417 assert(false);
1418 return nullptr;
1419 }
1420
execute(RpcRequest req,DownloadEngine * e)1421 RpcResponse SystemMulticallRpcMethod::execute(RpcRequest req, DownloadEngine* e)
1422 {
1423 auto authorized = RpcResponse::AUTHORIZED;
1424 try {
1425 const List* methodSpecs = checkRequiredParam<List>(req, 0);
1426 auto list = List::g();
1427 for (auto& methodSpec : *methodSpecs) {
1428 Dict* methodDict = downcast<Dict>(methodSpec);
1429 if (!methodDict) {
1430 list->append(createErrorResponse(
1431 DL_ABORT_EX("system.multicall expected struct."), req));
1432 continue;
1433 }
1434 const String* methodName =
1435 downcast<String>(methodDict->get(KEY_METHOD_NAME));
1436 if (!methodName) {
1437 list->append(
1438 createErrorResponse(DL_ABORT_EX("Missing methodName."), req));
1439 continue;
1440 }
1441 if (methodName->s() == getMethodName()) {
1442 list->append(createErrorResponse(
1443 DL_ABORT_EX("Recursive system.multicall forbidden."), req));
1444 continue;
1445 }
1446 // TODO what if params missing?
1447 auto tempParamsList = methodDict->get(KEY_PARAMS);
1448 std::unique_ptr<List> paramsList;
1449 if (downcast<List>(tempParamsList)) {
1450 paramsList.reset(
1451 static_cast<List*>(methodDict->popValue(KEY_PARAMS).release()));
1452 }
1453 else {
1454 paramsList = List::g();
1455 }
1456 RpcRequest r = {methodName->s(), std::move(paramsList), nullptr,
1457 req.jsonRpc};
1458 RpcResponse res = getMethod(methodName->s())->execute(std::move(r), e);
1459 if (rpc::not_authorized(res)) {
1460 authorized = RpcResponse::NOTAUTHORIZED;
1461 }
1462 if (res.code == 0) {
1463 auto l = List::g();
1464 l->append(std::move(res.param));
1465 list->append(std::move(l));
1466 }
1467 else {
1468 list->append(std::move(res.param));
1469 }
1470 }
1471 return RpcResponse(0, authorized, std::move(list), std::move(req.id));
1472 }
1473 catch (RecoverableException& ex) {
1474 A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, ex);
1475 return RpcResponse(1, authorized, createErrorResponse(ex, req),
1476 std::move(req.id));
1477 }
1478 }
1479
1480 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1481 SystemListMethodsRpcMethod::process(const RpcRequest& req, DownloadEngine* e)
1482 {
1483 auto list = List::g();
1484 for (auto& s : allMethodNames()) {
1485 list->append(s);
1486 }
1487
1488 return std::move(list);
1489 }
1490
execute(RpcRequest req,DownloadEngine * e)1491 RpcResponse SystemListMethodsRpcMethod::execute(RpcRequest req,
1492 DownloadEngine* e)
1493 {
1494 auto r = process(req, e);
1495 return RpcResponse(0, RpcResponse::AUTHORIZED, std::move(r),
1496 std::move(req.id));
1497 }
1498
1499 std::unique_ptr<ValueBase>
process(const RpcRequest & req,DownloadEngine * e)1500 SystemListNotificationsRpcMethod::process(const RpcRequest& req,
1501 DownloadEngine* e)
1502 {
1503 auto list = List::g();
1504 for (auto& s : allNotificationsNames()) {
1505 list->append(s);
1506 }
1507
1508 return std::move(list);
1509 }
1510
execute(RpcRequest req,DownloadEngine * e)1511 RpcResponse SystemListNotificationsRpcMethod::execute(RpcRequest req,
1512 DownloadEngine* e)
1513 {
1514 auto r = process(req, e);
1515 return RpcResponse(0, RpcResponse::AUTHORIZED, std::move(r),
1516 std::move(req.id));
1517 }
1518
process(const RpcRequest & req,DownloadEngine * e)1519 std::unique_ptr<ValueBase> NoSuchMethodRpcMethod::process(const RpcRequest& req,
1520 DownloadEngine* e)
1521 {
1522 throw DL_ABORT_EX(fmt("No such method: %s", req.methodName.c_str()));
1523 }
1524
1525 } // namespace rpc
1526
pauseRequestGroup(const std::shared_ptr<RequestGroup> & group,bool reserved,bool forcePause)1527 bool pauseRequestGroup(const std::shared_ptr<RequestGroup>& group,
1528 bool reserved, bool forcePause)
1529 {
1530 if ((reserved && !group->isPauseRequested()) ||
1531 (!reserved && !group->isForceHaltRequested() &&
1532 ((forcePause && group->isHaltRequested() && group->isPauseRequested()) ||
1533 (!group->isHaltRequested() && !group->isPauseRequested())))) {
1534 if (!reserved) {
1535 // Call setHaltRequested before setPauseRequested because
1536 // setHaltRequested calls setPauseRequested(false) internally.
1537 if (forcePause) {
1538 group->setForceHaltRequested(true, RequestGroup::NONE);
1539 }
1540 else {
1541 group->setHaltRequested(true, RequestGroup::NONE);
1542 }
1543 }
1544 group->setPauseRequested(true);
1545 return true;
1546 }
1547 else {
1548 return false;
1549 }
1550 }
1551
changeOption(const std::shared_ptr<RequestGroup> & group,const Option & option,DownloadEngine * e)1552 void changeOption(const std::shared_ptr<RequestGroup>& group,
1553 const Option& option, DownloadEngine* e)
1554 {
1555 const std::shared_ptr<DownloadContext>& dctx = group->getDownloadContext();
1556 const std::shared_ptr<Option>& grOption = group->getOption();
1557 grOption->merge(option);
1558 if (option.defined(PREF_CHECKSUM)) {
1559 const std::string& checksum = grOption->get(PREF_CHECKSUM);
1560 auto p = util::divide(std::begin(checksum), std::end(checksum), '=');
1561 std::string hashType(p.first.first, p.first.second);
1562 util::lowercase(hashType);
1563 dctx->setDigest(hashType, util::fromHex(p.second.first, p.second.second));
1564 }
1565 if (option.defined(PREF_SELECT_FILE)) {
1566 auto sgl = util::parseIntSegments(grOption->get(PREF_SELECT_FILE));
1567 sgl.normalize();
1568 dctx->setFileFilter(std::move(sgl));
1569 }
1570 if (option.defined(PREF_SPLIT)) {
1571 group->setNumConcurrentCommand(grOption->getAsInt(PREF_SPLIT));
1572 }
1573 if (option.defined(PREF_MAX_CONNECTION_PER_SERVER)) {
1574 int maxConn = grOption->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
1575 const std::vector<std::shared_ptr<FileEntry>>& files =
1576 dctx->getFileEntries();
1577 for (auto& file : files) {
1578 (file)->setMaxConnectionPerServer(maxConn);
1579 }
1580 }
1581 if (option.defined(PREF_DIR) || option.defined(PREF_OUT)) {
1582 if (!group->getMetadataInfo()) {
1583
1584 assert(dctx->getFileEntries().size() == 1);
1585
1586 auto& fileEntry = dctx->getFirstFileEntry();
1587
1588 if (!grOption->blank(PREF_OUT)) {
1589 fileEntry->setPath(
1590 util::applyDir(grOption->get(PREF_DIR), grOption->get(PREF_OUT)));
1591 fileEntry->setSuffixPath(A2STR::NIL);
1592 }
1593 else if (fileEntry->getSuffixPath().empty()) {
1594 fileEntry->setPath(A2STR::NIL);
1595 }
1596 else {
1597 fileEntry->setPath(util::applyDir(grOption->get(PREF_DIR),
1598 fileEntry->getSuffixPath()));
1599 }
1600 }
1601 else if (group->getMetadataInfo()
1602 #ifdef ENABLE_BITTORRENT
1603 && !dctx->hasAttribute(CTX_ATTR_BT)
1604 #endif // ENABLE_BITTORRENT
1605 ) {
1606 // In case of Metalink
1607 for (auto& fileEntry : dctx->getFileEntries()) {
1608 // PREF_OUT is not applicable to Metalink. We have always
1609 // suffixPath set.
1610 fileEntry->setPath(util::applyDir(grOption->get(PREF_DIR),
1611 fileEntry->getSuffixPath()));
1612 }
1613 }
1614 }
1615 #ifdef ENABLE_BITTORRENT
1616 if (option.defined(PREF_DIR) || option.defined(PREF_INDEX_OUT)) {
1617 if (dctx->hasAttribute(CTX_ATTR_BT)) {
1618 std::istringstream indexOutIn(grOption->get(PREF_INDEX_OUT));
1619 std::vector<std::pair<size_t, std::string>> indexPaths =
1620 util::createIndexPaths(indexOutIn);
1621 for (std::vector<std::pair<size_t, std::string>>::const_iterator
1622 i = indexPaths.begin(),
1623 eoi = indexPaths.end();
1624 i != eoi; ++i) {
1625 dctx->setFilePathWithIndex(
1626 (*i).first, util::applyDir(grOption->get(PREF_DIR), (*i).second));
1627 }
1628 }
1629 }
1630 #endif // ENABLE_BITTORRENT
1631 if (option.defined(PREF_MAX_DOWNLOAD_LIMIT)) {
1632 group->setMaxDownloadSpeedLimit(
1633 grOption->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
1634 }
1635 if (option.defined(PREF_MAX_UPLOAD_LIMIT)) {
1636 group->setMaxUploadSpeedLimit(grOption->getAsInt(PREF_MAX_UPLOAD_LIMIT));
1637 }
1638 #ifdef ENABLE_BITTORRENT
1639 auto btObject = e->getBtRegistry()->get(group->getGID());
1640 if (btObject) {
1641 if (option.defined(PREF_BT_MAX_PEERS)) {
1642 btObject->btRuntime->setMaxPeers(grOption->getAsInt(PREF_BT_MAX_PEERS));
1643 }
1644 }
1645 #endif // ENABLE_BITTORRENT
1646 }
1647
changeGlobalOption(const Option & option,DownloadEngine * e)1648 void changeGlobalOption(const Option& option, DownloadEngine* e)
1649 {
1650 e->getOption()->merge(option);
1651 if (option.defined(PREF_MAX_OVERALL_DOWNLOAD_LIMIT)) {
1652 e->getRequestGroupMan()->setMaxOverallDownloadSpeedLimit(
1653 option.getAsInt(PREF_MAX_OVERALL_DOWNLOAD_LIMIT));
1654 }
1655 if (option.defined(PREF_MAX_OVERALL_UPLOAD_LIMIT)) {
1656 e->getRequestGroupMan()->setMaxOverallUploadSpeedLimit(
1657 option.getAsInt(PREF_MAX_OVERALL_UPLOAD_LIMIT));
1658 }
1659 if (option.defined(PREF_MAX_CONCURRENT_DOWNLOADS)) {
1660 e->getRequestGroupMan()->setMaxConcurrentDownloads(
1661 option.getAsInt(PREF_MAX_CONCURRENT_DOWNLOADS));
1662 e->getRequestGroupMan()->requestQueueCheck();
1663 }
1664 if (option.defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS)) {
1665 e->getRequestGroupMan()->setupOptimizeConcurrentDownloads();
1666 e->getRequestGroupMan()->requestQueueCheck();
1667 }
1668 if (option.defined(PREF_MAX_DOWNLOAD_RESULT)) {
1669 e->getRequestGroupMan()->setMaxDownloadResult(
1670 option.getAsInt(PREF_MAX_DOWNLOAD_RESULT));
1671 }
1672 if (option.defined(PREF_LOG_LEVEL)) {
1673 LogFactory::setLogLevel(option.get(PREF_LOG_LEVEL));
1674 }
1675 if (option.defined(PREF_LOG)) {
1676 LogFactory::setLogFile(option.get(PREF_LOG));
1677 try {
1678 LogFactory::reconfigure();
1679 }
1680 catch (RecoverableException& e) {
1681 // TODO no exception handling
1682 }
1683 }
1684 if (option.defined(PREF_BT_MAX_OPEN_FILES)) {
1685 auto& openedFileCounter = e->getRequestGroupMan()->getOpenedFileCounter();
1686 openedFileCounter->setMaxOpenFiles(option.getAsInt(PREF_BT_MAX_OPEN_FILES));
1687 }
1688 }
1689
1690 } // namespace aria2
1691