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 "RpcMethod.h"
36 #include "DownloadEngine.h"
37 #include "LogFactory.h"
38 #include "RecoverableException.h"
39 #include "message.h"
40 #include "OptionParser.h"
41 #include "OptionHandler.h"
42 #include "Option.h"
43 #include "array_fun.h"
44 #include "download_helper.h"
45 #include "RpcRequest.h"
46 #include "RpcResponse.h"
47 #include "prefs.h"
48 #include "fmt.h"
49 #include "DlAbortEx.h"
50 #include "a2functional.h"
51 #include "util.h"
52 
53 namespace aria2 {
54 
55 namespace rpc {
56 
RpcMethod()57 RpcMethod::RpcMethod() : optionParser_(OptionParser::getInstance()) {}
58 
59 RpcMethod::~RpcMethod() = default;
60 
createErrorResponse(const Exception & e,const RpcRequest & req)61 std::unique_ptr<ValueBase> RpcMethod::createErrorResponse(const Exception& e,
62                                                           const RpcRequest& req)
63 {
64   auto params = Dict::g();
65   params->put((req.jsonRpc ? "code" : "faultCode"), Integer::g(1));
66   params->put((req.jsonRpc ? "message" : "faultString"), std::string(e.what()));
67   return std::move(params);
68 }
69 
authorize(RpcRequest & req,DownloadEngine * e)70 void RpcMethod::authorize(RpcRequest& req, DownloadEngine* e)
71 {
72   std::string token;
73   // We always treat first parameter as token if it is string and
74   // starts with "token:" and remove it from parameter list, so that
75   // we don't have to add conditionals to all RPCMethod
76   // implementations.
77   if (req.params && !req.params->empty()) {
78     auto t = downcast<String>(req.params->get(0));
79     if (t) {
80       if (util::startsWith(t->s(), "token:")) {
81         token = t->s().substr(6);
82         req.params->pop_front();
83       }
84     }
85   }
86   if (!e || !e->validateToken(token)) {
87     throw DL_ABORT_EX("Unauthorized");
88   }
89 }
90 
execute(RpcRequest req,DownloadEngine * e)91 RpcResponse RpcMethod::execute(RpcRequest req, DownloadEngine* e)
92 {
93   auto authorized = RpcResponse::NOTAUTHORIZED;
94   try {
95     authorize(req, e);
96     authorized = RpcResponse::AUTHORIZED;
97     auto r = process(req, e);
98     return RpcResponse(0, authorized, std::move(r), std::move(req.id));
99   }
100   catch (RecoverableException& ex) {
101     A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, ex);
102     return RpcResponse(1, authorized, createErrorResponse(ex, req),
103                        std::move(req.id));
104   }
105 }
106 
107 namespace {
108 template <typename InputIterator, typename Pred>
gatherOption(InputIterator first,InputIterator last,Pred pred,Option * option,const std::shared_ptr<OptionParser> & optionParser)109 void gatherOption(InputIterator first, InputIterator last, Pred pred,
110                   Option* option,
111                   const std::shared_ptr<OptionParser>& optionParser)
112 {
113   for (; first != last; ++first) {
114     const std::string& optionName = (*first).first;
115     PrefPtr pref = option::k2p(optionName);
116     const OptionHandler* handler = optionParser->find(pref);
117     if (!handler || !pred(handler)) {
118       // Just ignore the unacceptable options in this context.
119       continue;
120     }
121     const String* opval = downcast<String>((*first).second);
122     if (opval) {
123       handler->parse(*option, opval->s());
124     }
125     else if (handler->getCumulative()) {
126       // header and index-out option can take array as value
127       const List* oplist = downcast<List>((*first).second);
128       if (oplist) {
129         for (auto& elem : *oplist) {
130           const String* opval = downcast<String>(elem);
131           if (opval) {
132             handler->parse(*option, opval->s());
133           }
134         }
135       }
136     }
137   }
138 }
139 } // namespace
140 
gatherRequestOption(Option * option,const Dict * optionsDict)141 void RpcMethod::gatherRequestOption(Option* option, const Dict* optionsDict)
142 {
143   if (optionsDict) {
144     gatherOption(optionsDict->begin(), optionsDict->end(),
145                  std::mem_fn(&OptionHandler::getInitialOption), option,
146                  optionParser_);
147   }
148 }
149 
gatherChangeableOption(Option * option,Option * pendingOption,const Dict * optionsDict)150 void RpcMethod::gatherChangeableOption(Option* option, Option* pendingOption,
151                                        const Dict* optionsDict)
152 {
153   if (!optionsDict) {
154     return;
155   }
156 
157   auto first = optionsDict->begin();
158   auto last = optionsDict->end();
159 
160   for (; first != last; ++first) {
161     const auto& optionName = (*first).first;
162     auto pref = option::k2p(optionName);
163     auto handler = optionParser_->find(pref);
164     if (!handler) {
165       // Just ignore the unacceptable options in this context.
166       continue;
167     }
168 
169     Option* dst = nullptr;
170     if (handler->getChangeOption()) {
171       dst = option;
172     }
173     else if (handler->getChangeOptionForReserved()) {
174       dst = pendingOption;
175     }
176 
177     if (!dst) {
178       continue;
179     }
180 
181     const auto opval = downcast<String>((*first).second);
182     if (opval) {
183       handler->parse(*dst, opval->s());
184     }
185     else if (handler->getCumulative()) {
186       // header and index-out option can take array as value
187       const auto oplist = downcast<List>((*first).second);
188       if (oplist) {
189         for (auto& elem : *oplist) {
190           const auto opval = downcast<String>(elem);
191           if (opval) {
192             handler->parse(*dst, opval->s());
193           }
194         }
195       }
196     }
197   }
198 }
199 
gatherChangeableOptionForReserved(Option * option,const Dict * optionsDict)200 void RpcMethod::gatherChangeableOptionForReserved(Option* option,
201                                                   const Dict* optionsDict)
202 {
203   if (optionsDict) {
204     gatherOption(optionsDict->begin(), optionsDict->end(),
205                  std::mem_fn(&OptionHandler::getChangeOptionForReserved),
206                  option, optionParser_);
207   }
208 }
209 
gatherChangeableGlobalOption(Option * option,const Dict * optionsDict)210 void RpcMethod::gatherChangeableGlobalOption(Option* option,
211                                              const Dict* optionsDict)
212 {
213   if (optionsDict) {
214     gatherOption(optionsDict->begin(), optionsDict->end(),
215                  std::mem_fn(&OptionHandler::getChangeGlobalOption), option,
216                  optionParser_);
217   }
218 }
219 
220 } // namespace rpc
221 
222 } // namespace aria2
223