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