blob: 18e65f6f2e58d7b441fcb96024736a9e1dd0a6e3 [file] [log] [blame]
Alex Vakulenkob3813652014-08-27 15:04:221// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <chromeos/dbus/utils.h>
6
Alex Vakulenko00ceab82014-11-04 22:21:597#include <tuple>
8#include <vector>
9
Alex Vakulenkob3813652014-08-27 15:04:2210#include <base/bind.h>
11#include <base/memory/scoped_ptr.h>
Alex Vakulenko15104662014-08-27 18:00:5712#include <chromeos/errors/error_codes.h>
Alex Vakulenko00ceab82014-11-04 22:21:5913#include <chromeos/strings/string_utils.h>
Alex Vakulenkob3813652014-08-27 15:04:2214
15namespace chromeos {
16namespace dbus_utils {
17
18namespace {
19
20// Passes |method_call| to |handler| and passes the response to
Alex Vakulenko9d99bc62014-08-28 20:42:0121// |response_sender|. If |handler| returns nullptr, an empty response is created
Alex Vakulenkob3813652014-08-27 15:04:2222// and sent.
23void HandleSynchronousDBusMethodCall(
24 const MethodCallHandler& handler,
25 dbus::MethodCall* method_call,
26 dbus::ExportedObject::ResponseSender response_sender) {
27 scoped_ptr<dbus::Response> response(handler.Run(method_call).release());
28 if (!response)
29 response = dbus::Response::FromMethodCall(method_call);
30
31 response_sender.Run(response.Pass());
32}
33
34} // namespace
35
Alex Vakulenko1363ed62014-09-22 21:38:4636// Do not inline ScopedDBusErrorWrapper constructor/destructor.
37ScopedDBusErrorWrapper::ScopedDBusErrorWrapper() {}
38ScopedDBusErrorWrapper::~ScopedDBusErrorWrapper() {}
39
40// Delegate the call to dbus::ScopedDBusError::is_set().
41bool ScopedDBusErrorWrapper::is_set() const {
42 return dbus::ScopedDBusError::is_set();
43}
44
Alex Vakulenkob3813652014-08-27 15:04:2245std::unique_ptr<dbus::Response> CreateDBusErrorResponse(
46 dbus::MethodCall* method_call,
47 const std::string& code,
48 const std::string& message) {
49 auto resp = dbus::ErrorResponse::FromMethodCall(method_call, code, message);
50 return std::unique_ptr<dbus::Response>(resp.release());
51}
52
53std::unique_ptr<dbus::Response> GetDBusError(dbus::MethodCall* method_call,
54 const chromeos::Error* error) {
55 std::string error_code = DBUS_ERROR_FAILED; // Default error code.
56 std::string error_message;
57
58 // Special case for "dbus" error domain.
59 // Pop the error code and message from the error chain and use them as the
60 // actual D-Bus error message.
61 if (error->GetDomain() == errors::dbus::kDomain) {
62 error_code = error->GetCode();
63 error_message = error->GetMessage();
64 error = error->GetInnerError();
65 }
66
67 // Append any inner errors to the error message.
68 while (error) {
69 // Format error string as "domain/code:message".
70 if (!error_message.empty())
71 error_message += ';';
72 error_message += error->GetDomain() + '/' + error->GetCode() + ':' +
73 error->GetMessage();
74 error = error->GetInnerError();
75 }
76 return CreateDBusErrorResponse(method_call, error_code, error_message);
77}
78
79dbus::ExportedObject::MethodCallCallback GetExportableDBusMethod(
80 const MethodCallHandler& handler) {
81 return base::Bind(&HandleSynchronousDBusMethodCall, handler);
82}
83
Alex Vakulenko00ceab82014-11-04 22:21:5984void AddDBusError(chromeos::ErrorPtr* error,
85 const std::string& dbus_error_code,
86 const std::string& dbus_error_message) {
87 std::vector<std::string> parts = string_utils::Split(dbus_error_message, ';');
88 std::vector<std::tuple<std::string, std::string, std::string>> errors;
89 for (const std::string& part : parts) {
90 // Each part should be in format of "domain/code:message"
91 size_t slash_pos = part.find('/');
92 size_t colon_pos = part.find(':');
93 if (slash_pos != std::string::npos &&
94 colon_pos != std::string::npos &&
95 slash_pos < colon_pos) {
96 // If we have both '/' and ':' and in proper oder, then we have a
97 // correctly encoded error object.
98 std::string domain = part.substr(0, slash_pos);
99 std::string code = part.substr(slash_pos + 1, colon_pos - slash_pos - 1);
100 std::string message = part.substr(colon_pos + 1);
101 errors.emplace_back(domain, code, message);
102 } else if (slash_pos == std::string::npos &&
103 colon_pos == std::string::npos &&
104 errors.empty()) {
105 // If we don't have both '/' and ':' and this is the first error object,
106 // then we had a D-Bus error at the top of the error chain.
107 errors.emplace_back(errors::dbus::kDomain, dbus_error_code, part);
108 } else {
109 // We have a malformed part. The whole D-Bus error was most likely
110 // not generated by GetDBusError(). To be safe, stop parsing it
111 // and return the error as received from D-Bus.
112 errors.clear(); // Remove any errors accumulated so far.
113 errors.emplace_back(errors::dbus::kDomain,
114 dbus_error_code,
115 dbus_error_message);
116 break;
117 }
118 }
119
120 // Go backwards and add the parsed errors to the error chain.
121 for (auto it = errors.crbegin(); it != errors.crend(); ++it) {
122 Error::AddTo(error, std::get<0>(*it), std::get<1>(*it), std::get<2>(*it));
123 }
124}
125
Alex Vakulenkob3813652014-08-27 15:04:22126} // namespace dbus_utils
127} // namespace chromeos