1e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// Copyright 2014 The Chromium OS Authors. All rights reserved.
2e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// Use of this source code is governed by a BSD-style license that can be
3e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// found in the LICENSE file.
4e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
5e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This file provides generic method to parse function call arguments from
6e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// D-Bus message buffer and subsequently invokes a provided native C++ callback
7e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// with the parameter values passed as the callback arguments.
8e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
9e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This functionality is achieved by parsing method arguments one by one,
10e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// left to right from the C++ callback's type signature, and moving the parsed
11e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// arguments to the back to the next call to DBusInvoke::Invoke's arguments as
12e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// const refs.  Each iteration has one fewer template specialization arguments,
13e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// until there is only the return type remaining and we fall through to either
14e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// the void or the non-void final specialization.
15e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
16fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#ifndef LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
17fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#define LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
18e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
19e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko#include <type_traits>
20e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
219ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/dbus/data_serialization.h>
229ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/dbus/utils.h>
239ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/errors/error.h>
249ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko#include <brillo/errors/error_codes.h>
25e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko#include <dbus/message.h>
26e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
279ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenkonamespace brillo {
28e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenkonamespace dbus_utils {
29e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
30e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// A generic DBusParamReader stub class which allows us to specialize on
31e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// a variable list of expected function parameters later on.
32e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// This struct in itself is not used. But its concrete template specializations
33e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// defined below are.
34f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko// |allow_out_params| controls whether DBusParamReader allows the parameter
35f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko// list to contain OUT parameters (pointers).
36f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params, typename...>
37e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenkostruct DBusParamReader;
38e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
39e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// A generic specialization of DBusParamReader to handle variable function
40e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// parameters. This specialization pops one parameter off the D-Bus message
41e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// buffer and calls other specializations of DBusParamReader with fewer
42e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// parameters to pop the remaining parameters.
4390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko//  CurrentParam  - the type of the current method parameter we are processing.
44e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko//  RestOfParams  - the types of remaining parameters to be processed.
45f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params, typename CurrentParam, typename... RestOfParams>
46f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkostruct DBusParamReader<allow_out_params, CurrentParam, RestOfParams...> {
47e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  // DBusParamReader::Invoke() is a member function that actually extracts the
48e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  // current parameter from the message buffer.
49e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  //  handler     - the C++ callback functor to be called when all the
50e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  //                parameters are processed.
51e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  //  method_call - D-Bus method call object we are processing.
52e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  //  reader      - D-Bus message reader to pop the current argument value from.
53e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  //  args...     - the callback parameters processed so far.
54e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  template<typename CallbackType, typename... Args>
55e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  static bool Invoke(const CallbackType& handler,
56e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                     dbus::MessageReader* reader,
57e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko                     ErrorPtr* error,
58e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                     const Args&... args) {
5990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    return InvokeHelper<CurrentParam, CallbackType, Args...>(
6090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko        handler, reader, error, static_cast<const Args&>(args)...);
6190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  }
6290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko
6390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  //
6490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // There are two specializations of this function:
6590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  //  1. For the case where ParamType is a value type (D-Bus IN parameter).
6690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  //  2. For the case where ParamType is a pointer (D-Bus OUT parameter).
6790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // In the second case, the parameter is not popped off the message reader,
6890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // since we do not expect the client to provide any data for it.
6990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // However after the final handler is called, the values for the OUT
7090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // parameters should be sent back in the method call response message.
7190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko
7290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // Overload 1: ParamType is not a pointer.
7390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  template<typename ParamType, typename CallbackType, typename... Args>
7490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  static typename std::enable_if<!std::is_pointer<ParamType>::value, bool>::type
7505d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko  InvokeHelper(const CallbackType& handler,
7605d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko               dbus::MessageReader* reader,
7705d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko               ErrorPtr* error,
7805d29044d14a60775ed6c51c75a414eb0cb50347Alex Vakulenko               const Args&... args) {
79e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    if (!reader->HasMoreData()) {
808f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko      Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
818f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko                   DBUS_ERROR_INVALID_ARGS,
82e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                   "Too few parameters in a method call");
83e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko      return false;
84e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    }
85e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // ParamType could be a reference type (e.g. 'const std::string&').
86e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // Here we need a value type so we can create an object of this type and
87e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // pop the value off the message buffer into. Using std::decay<> to get
88e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // the value type. If ParamType is already a value type, ParamValueType will
89e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // be the same as ParamType.
90e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    using ParamValueType = typename std::decay<ParamType>::type;
91e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // The variable to hold the value of the current parameter we reading from
92e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // the message buffer.
93e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    ParamValueType current_param;
94003e3bb092961d8d0236fd09efaf0ded404828cbAlex Vakulenko    if (!DBusType<ParamValueType>::Read(reader, &current_param)) {
958f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko      Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
968f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko                   DBUS_ERROR_INVALID_ARGS,
97e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                   "Method parameter type mismatch");
98e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko      return false;
99e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    }
100e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // Call DBusParamReader::Invoke() to process the rest of parameters.
101e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // Note that this is not a recursive call because it is calling a different
102e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // method of a different class. We exclude the current parameter type
103e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // (ParamType) from DBusParamReader<> template parameter list and forward
104e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // all the parameters to the arguments of Invoke() and append the current
105e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // parameter to the end of the parameter list. We pass it as a const
106e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // reference to allow to use move-only types such as std::unique_ptr<> and
107e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    // to eliminate unnecessarily copying data.
108f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko    return DBusParamReader<allow_out_params, RestOfParams...>::Invoke(
109e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko        handler, reader, error,
110e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko        static_cast<const Args&>(args)...,
111e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko        static_cast<const ParamValueType&>(current_param));
112e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  }
11390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko
11490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  // Overload 2: ParamType is a pointer.
11590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  template<typename ParamType, typename CallbackType, typename... Args>
116f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko  static typename std::enable_if<allow_out_params &&
117f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko                                 std::is_pointer<ParamType>::value, bool>::type
11890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko      InvokeHelper(const CallbackType& handler,
11990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko                   dbus::MessageReader* reader,
12090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko                   ErrorPtr* error,
12190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko                   const Args&... args) {
12290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // ParamType is a pointer. This is expected to be an output parameter.
12390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // Create storage for it and the handler will provide a value for it.
12490f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    using ParamValueType = typename std::remove_pointer<ParamType>::type;
12590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // The variable to hold the value of the current parameter we are passing
12690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // to the handler.
12790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    ParamValueType current_param{};  // Default-initialize the value.
12890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // Call DBusParamReader::Invoke() to process the rest of parameters.
12990f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // Note that this is not a recursive call because it is calling a different
13090f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // method of a different class. We exclude the current parameter type
13190f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // (ParamType) from DBusParamReader<> template parameter list and forward
13290f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // all the parameters to the arguments of Invoke() and append the current
13390f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko    // parameter to the end of the parameter list.
134f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenko    return DBusParamReader<allow_out_params, RestOfParams...>::Invoke(
13590f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko        handler, reader, error,
13690f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko        static_cast<const Args&>(args)...,
13790f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko        &current_param);
13890f61fd6ba7ccff59e28b09a3f75e8626b483934Alex Vakulenko  }
139e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko};  // struct DBusParamReader<ParamType, RestOfParams...>
140e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
141e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// The final specialization of DBusParamReader<> used when no more parameters
142e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// are expected in the message buffer. Actually dispatches the call to the
143e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko// handler with all the accumulated arguments.
144f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkotemplate<bool allow_out_params>
145f437e3b367869af7cb5a53153284566164b48a7cAlex Vakulenkostruct DBusParamReader<allow_out_params> {
146e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  template<typename CallbackType, typename... Args>
147e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  static bool Invoke(const CallbackType& handler,
148e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                     dbus::MessageReader* reader,
149e10a7035d989ec11686641ff75db31119d606af2Alex Vakulenko                     ErrorPtr* error,
150e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                     const Args&... args) {
151e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    if (reader->HasMoreData()) {
1528f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko      Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
1538f815f563c71645fd218977f16de8e746bec28a6Alex Vakulenko                   DBUS_ERROR_INVALID_ARGS,
154e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko                   "Too many parameters in a method call");
155e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko      return false;
156e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    }
157e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    handler(args...);
158e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko    return true;
159e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko  }
160e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko};  // struct DBusParamReader<>
161e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
162e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko}  // namespace dbus_utils
1639ed0cab99f18acb3570a35e9408f24355f6b8324Alex Vakulenko}  // namespace brillo
164e46e51f0af1c4b1f49da9019a2d71e96e484273bAlex Vakulenko
165fed60b0c640828b320f56293c8bebc43fd2b1da8Alex Vakulenko#endif  // LIBBRILLO_BRILLO_DBUS_DBUS_PARAM_READER_H_
166