1// Copyright 2015 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "libwebserv/dbus_protocol_handler.h"
16
17#include <tuple>
18
19#include <base/logging.h>
20#include <brillo/map_utils.h>
21#include <brillo/streams/file_stream.h>
22#include <brillo/streams/stream_utils.h>
23
24#include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
25#include "libwebserv/dbus_server.h"
26#include "libwebserv/protocol_handler.h"
27#include "libwebserv/request.h"
28#include "libwebserv/request_handler_callback.h"
29#include "libwebserv/response_impl.h"
30#include "webservd/dbus-proxies.h"
31
32namespace libwebserv {
33
34namespace {
35
36// Dummy callback for async D-Bus errors.
37void IgnoreDBusError(brillo::Error* /* error */) {}
38
39// Copies the data from |src_stream| to the destination stream represented
40// by a file descriptor |fd|.
41void WriteResponseData(brillo::StreamPtr src_stream,
42                       const dbus::FileDescriptor& fd) {
43  int dupfd = dup(fd.value());
44  auto dest_stream =
45      brillo::FileStream::FromFileDescriptor(dupfd, true, nullptr);
46  CHECK(dest_stream);
47  // Dummy callbacks for success/error of data-copy operation. We ignore both
48  // notifications here.
49  auto on_success = [](brillo::StreamPtr, brillo::StreamPtr, uint64_t) {};
50  auto on_error = [](brillo::StreamPtr, brillo::StreamPtr,
51                     const brillo::Error*) {};
52  brillo::stream_utils::CopyData(
53      std::move(src_stream), std::move(dest_stream), base::Bind(on_success),
54      base::Bind(on_error));
55}
56
57}  // anonymous namespace
58
59DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
60                                         DBusServer* server)
61    : name_{name}, server_{server} {
62}
63
64DBusProtocolHandler::~DBusProtocolHandler() {
65  // Remove any existing handlers, so the web server knows that we don't
66  // need them anymore.
67
68  // We need to get a copy of the map keys since removing the handlers will
69  // modify the map in the middle of the loop and that's not a good thing.
70  auto handler_ids = brillo::GetMapKeys(request_handlers_);
71  for (int handler_id : handler_ids) {
72    RemoveHandler(handler_id);
73  }
74}
75bool DBusProtocolHandler::IsConnected() const {
76  return !proxies_.empty();
77}
78
79std::string DBusProtocolHandler::GetName() const {
80  return name_;
81}
82
83std::set<uint16_t> DBusProtocolHandler::GetPorts() const {
84  std::set<uint16_t> ports;
85  for (const auto& pair : proxies_)
86    ports.insert(pair.second->port());
87  return ports;
88}
89
90std::set<std::string> DBusProtocolHandler::GetProtocols() const {
91  std::set<std::string> protocols;
92  for (const auto& pair : proxies_)
93    protocols.insert(pair.second->protocol());
94  return protocols;
95}
96
97brillo::Blob DBusProtocolHandler::GetCertificateFingerprint() const {
98  brillo::Blob fingerprint;
99  for (const auto& pair : proxies_) {
100    fingerprint = pair.second->certificate_fingerprint();
101    if (!fingerprint.empty())
102      break;
103  }
104  return fingerprint;
105}
106
107int DBusProtocolHandler::AddHandler(
108    const std::string& url,
109    const std::string& method,
110    std::unique_ptr<RequestHandlerInterface> handler) {
111  request_handlers_.emplace(
112      ++last_handler_id_,
113      HandlerMapEntry{url, method,
114                      std::map<ProtocolHandlerProxyInterface*, std::string>{},
115                      std::move(handler)});
116  // For each instance of remote protocol handler object sharing the same name,
117  // add the request handler.
118  for (const auto& pair : proxies_) {
119    pair.second->AddRequestHandlerAsync(
120        url,
121        method,
122        server_->service_name_,
123        base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
124                   weak_ptr_factory_.GetWeakPtr(),
125                   last_handler_id_,
126                   pair.second),
127        base::Bind(&DBusProtocolHandler::AddHandlerError,
128                   weak_ptr_factory_.GetWeakPtr(),
129                   last_handler_id_));
130  }
131  return last_handler_id_;
132}
133
134int DBusProtocolHandler::AddHandlerCallback(
135    const std::string& url,
136    const std::string& method,
137    const base::Callback<RequestHandlerInterface::HandlerSignature>&
138        handler_callback) {
139  std::unique_ptr<RequestHandlerInterface> handler{
140      new RequestHandlerCallback{handler_callback}};
141  return AddHandler(url, method, std::move(handler));
142}
143
144bool DBusProtocolHandler::RemoveHandler(int handler_id) {
145  auto p = request_handlers_.find(handler_id);
146  if (p == request_handlers_.end())
147    return false;
148
149  for (const auto& pair : p->second.remote_handler_ids) {
150    pair.first->RemoveRequestHandlerAsync(
151        pair.second,
152        base::Bind(&base::DoNothing),
153        base::Bind(&IgnoreDBusError));
154  }
155
156  request_handlers_.erase(p);
157  return true;
158}
159
160void DBusProtocolHandler::Connect(ProtocolHandlerProxyInterface* proxy) {
161  proxies_.emplace(proxy->GetObjectPath(), proxy);
162  for (const auto& pair : request_handlers_) {
163    proxy->AddRequestHandlerAsync(
164        pair.second.url,
165        pair.second.method,
166        server_->service_name_,
167        base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
168                   weak_ptr_factory_.GetWeakPtr(),
169                   pair.first,
170                   proxy),
171        base::Bind(&DBusProtocolHandler::AddHandlerError,
172                   weak_ptr_factory_.GetWeakPtr(),
173                   pair.first));
174  }
175}
176
177void DBusProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
178  proxies_.erase(object_path);
179  if (proxies_.empty())
180    remote_handler_id_map_.clear();
181  for (auto& pair : request_handlers_)
182    pair.second.remote_handler_ids.clear();
183}
184
185void DBusProtocolHandler::AddHandlerSuccess(
186    int handler_id,
187    ProtocolHandlerProxyInterface* proxy,
188    const std::string& remote_handler_id) {
189  auto p = request_handlers_.find(handler_id);
190  CHECK(p != request_handlers_.end());
191  p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
192
193  remote_handler_id_map_.emplace(remote_handler_id, handler_id);
194}
195
196void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
197                                          brillo::Error* /* error */) {
198  // Nothing to do at the moment.
199}
200
201bool DBusProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
202                                     const std::string& remote_handler_id,
203                                     const std::string& request_id,
204                                     std::unique_ptr<Request> request,
205                                     brillo::ErrorPtr* error) {
206  request_id_map_.emplace(request_id, protocol_handler_id);
207  auto id_iter = remote_handler_id_map_.find(remote_handler_id);
208  if (id_iter == remote_handler_id_map_.end()) {
209    brillo::Error::AddToPrintf(error, FROM_HERE,
210                               brillo::errors::dbus::kDomain,
211                               DBUS_ERROR_FAILED,
212                               "Unknown request handler '%s'",
213                               remote_handler_id.c_str());
214    return false;
215  }
216  auto handler_iter = request_handlers_.find(id_iter->second);
217  if (handler_iter == request_handlers_.end()) {
218    brillo::Error::AddToPrintf(error, FROM_HERE,
219                               brillo::errors::dbus::kDomain,
220                               DBUS_ERROR_FAILED,
221                               "Handler # %d is no longer available",
222                               id_iter->second);
223    return false;
224  }
225  handler_iter->second.handler->HandleRequest(
226      std::move(request),
227      std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
228  return true;
229}
230
231void DBusProtocolHandler::CompleteRequest(
232    const std::string& request_id,
233    int status_code,
234    const std::multimap<std::string, std::string>& headers,
235    brillo::StreamPtr data_stream) {
236  ProtocolHandlerProxyInterface* proxy =
237      GetRequestProtocolHandlerProxy(request_id);
238  if (!proxy)
239    return;
240
241  std::vector<std::tuple<std::string, std::string>> header_list;
242  header_list.reserve(headers.size());
243  for (const auto& pair : headers)
244    header_list.emplace_back(pair.first, pair.second);
245
246  int64_t data_size = -1;
247  if (data_stream->CanGetSize())
248    data_size = data_stream->GetRemainingSize();
249  proxy->CompleteRequestAsync(
250      request_id, status_code, header_list, data_size,
251      base::Bind(&WriteResponseData, base::Passed(&data_stream)),
252      base::Bind(&IgnoreDBusError));
253}
254
255void DBusProtocolHandler::GetFileData(
256    const std::string& request_id,
257    int file_id,
258    const base::Callback<void(brillo::StreamPtr)>& success_callback,
259    const base::Callback<void(brillo::Error*)>& error_callback) {
260  ProtocolHandlerProxyInterface* proxy =
261      GetRequestProtocolHandlerProxy(request_id);
262  CHECK(proxy);
263
264  // Store the success/error callback in a shared object so it can be referenced
265  // by the two wrapper callbacks. Since the original callbacks MAY contain
266  // move-only types, copying the base::Callback object is generally unsafe and
267  // may destroy the source object of the copy (despite the fact that it is
268  // constant). So, here we move both callbacks to |Callbacks| structure and
269  // use a shared pointer to it in both success and error callback wrappers.
270  struct Callbacks {
271    base::Callback<void(brillo::StreamPtr)> on_success;
272    base::Callback<void(brillo::Error*)> on_error;
273  };
274  auto callbacks = std::make_shared<Callbacks>();
275  callbacks->on_success = success_callback;
276  callbacks->on_error = error_callback;
277
278  auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
279    brillo::ErrorPtr error;
280    // Unfortunately there is no way to take ownership of the file descriptor
281    // since |fd| is a const reference, so duplicate the descriptor.
282    int dupfd = dup(fd.value());
283    auto stream = brillo::FileStream::FromFileDescriptor(dupfd, true, &error);
284    if (!stream)
285      return callbacks->on_error.Run(error.get());
286    callbacks->on_success.Run(std::move(stream));
287  };
288  auto on_error = [callbacks](brillo::Error* error) {
289    callbacks->on_error.Run(error);
290  };
291
292  proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
293                                 base::Bind(on_error));
294}
295
296DBusProtocolHandler::ProtocolHandlerProxyInterface*
297DBusProtocolHandler::GetRequestProtocolHandlerProxy(
298    const std::string& request_id) const {
299  auto iter = request_id_map_.find(request_id);
300  if (iter == request_id_map_.end()) {
301    LOG(ERROR) << "Can't find pending request with ID " << request_id;
302    return nullptr;
303  }
304  std::string handler_id = iter->second;
305  auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
306    return pair.second->id() == handler_id;
307  };
308  auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
309                                 find_proxy_by_id);
310  if (proxy_iter == proxies_.end()) {
311    LOG(WARNING) << "Completing a request after the handler proxy is removed";
312    return nullptr;
313  }
314  return proxy_iter->second;
315}
316
317
318}  // namespace libwebserv
319