#ifndef FUNCTION_MAP_HH #define FUNCTION_MAP_HH #include "inout.hh" #include "json_spirit/json_spirit_value.h" #include "json_spirit/json_spirit_writer.h" #include "context.hh" #include "logger.hh" #include // Just for debugging: #include #include #include template struct Return { typedef R return_type; typedef Out out_type; }; template struct Return< Out > { typedef R return_type; typedef Out out_type; }; // heloer class for generic calls: // R : return type of the called function // U : number of the parameter which is being extracted // MAX: maximum number of parameters. if U==MAX the function call is executed // Args... the function's parameter types template class helper; // specialization for U==MAX: do the function call here template class helper { public: typedef typename Return::return_type ReturnType; enum { nr_of_output_params = 0 }; enum { nr_of_input_params = 0 }; static void copyParam( js::Array& dest, const js::Array& src, unsigned index ) { // do nothing. :-) } static js::Value call( const std::function< ReturnType(typename Args::c_type...)>& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args) { typename Return::out_type o{ fn(args.get_value()...) }; return to_json( o ); } }; // specialization for Return type == void template class helper { public: enum { nr_of_output_params = 0 }; enum { nr_of_input_params = 0 }; static void copyParam( js::Array& dest, const js::Array& src, unsigned index ) { // do nothing. :-) } static js::Value call( const std::function& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args) { fn(args.get_value()...); return js::Value{}; } }; // recursive helper class: // It is used with U==0 in Func<>::call() and calls itself recursively until U==MAX, where the real function calls occurs, // and the output parameters are collected during unwinding of the recursion template class helper { public: typedef typename Return::return_type ReturnType; typedef std::tuple Tuple; typedef typename std::tuple_element::type Element; // The type of the U'th parameter typedef helper NextHelper; enum { nr_of_output_params = int(Element::is_output) + NextHelper::nr_of_output_params }; enum { nr_of_input_params = int(Element::need_input) + NextHelper::nr_of_input_params }; static void copyParam( js::Array& dest, const js::Array& src, unsigned index ) { if(Element::need_input) { dest.push_back( src.at(index) ); ++index; }else{ dest.push_back( js::Value{} ); // insert dummy parameter } NextHelper::copyParam( dest, src, index ); } // A2... a2 are the alredy pealed-off paremeters template static js::Value call( const std::function< ReturnType(typename Args::c_type...)>& fn, Context* ctx, js::Array& out_parameters, const js::Array& parameters, const A2&... a2) { // extract the U'th element of the parameter list const Element element(parameters[U], ctx, U); const js::Value ret = NextHelper::call(fn, ctx, out_parameters, parameters, a2..., element ); if(Element::is_output) { js::Value out = element.to_json(); // std::cerr << "|$ Out #" << U << " : " << js::write(out) << "\n"; out_parameters.push_back(std::move(out)); } else { // std::cerr << "|$ Param #" << U << " is not for output.\n"; } #ifdef _MSC_BUILD if (ret.type() == js::array_type && !ret.get_array().empty()) { try { const js::Array ret_ = ret.get_array()[0].get_array(); return ret_; } catch (std::runtime_error&) { return ret; } } else #endif return ret; } }; // abstract base class for all Func<...> types below class FuncBase { public: virtual ~FuncBase() = default; virtual bool isSeparator() const = 0; virtual void setJavaScriptSignature(js::Object& o) const = 0; virtual js::Value call(const js::Array& params, Context* context) const = 0; }; template class Func : public FuncBase { public: typedef typename Return::return_type ReturnType; virtual ~Func() = default; virtual bool isSeparator() const noexcept override { return false; } explicit Func( const std::function& _f ) : fn(_f) {} Func(const Func&) = delete; void operator=(const Func&) = delete; std::function fn; js::Value call(const js::Array& parameters, Context* context) const override { typedef helper Helper; if(parameters.size() != Helper::nr_of_input_params) throw std::runtime_error("Size mismatch: " "Array has " + std::to_string( parameters.size() ) + " element(s), " "but I expect " + std::to_string( Helper::nr_of_input_params) + " element(s)! " ); const js::Array* p_params = ¶meters; // create a copy of the parameters only if necessary js::Array param_copy; if( Helper::nr_of_input_params != sizeof...(Args) ) { param_copy.reserve( Helper::nr_of_input_params ); Helper::copyParam( param_copy, parameters, 0u ); p_params = ¶m_copy; // use the copy instead of 'parameters' } // recursive template magic breaks loose: // recursively extract the JSON parameters, call 'fn' and collect its return value // and all output parameters into a tuple<> and return it as JSON array js::Array out_params; out_params.reserve( Helper::nr_of_output_params ); js::Value ret = Helper::call(fn, context, out_params, *p_params); std::string _ret = js::value_type_to_string(ret.type()); js::Object rs; rs.emplace_back("outParams", std::move(out_params)); rs.emplace_back("return", std::move(ret)); return rs; } void setJavaScriptSignature(js::Object& o) const override { js::Array params; Type2Json::get(params); o.emplace_back( "return", Type2String::get() ); o.emplace_back( "params", std::move(params) ); o.emplace_back( "separator", false ); } }; // wrap the function with passphrase_cache.api() template class FuncPC : public Func { public: typedef Func Base; typedef typename Return::return_type ReturnType; typedef helper Helper; FuncPC( ReturnType(*_f)(typename Args::c_type ...) ) : Base( [_f](typename Args::c_type... args) { return pEp::passphrase_cache.api( _f, args...); } ) {} }; // add the function & its parameters in the context->cache // (where they are cached and applied to all PEP_SESSIONs of the client) template class FuncCache : public Func { public: typedef Func Base; typedef typename Return::return_type ReturnType; typedef helper Helper; FuncCache(const std::string& _func_name, const std::function& _f ) : Base(_f) , func_name(_func_name) {} js::Value call(const js::Array& parameters, Context* context) const override { Logger Log("FuncCache::call"); typedef std::tuple param_tuple_t; //param_tuple_t param_tuple; // FIXME: Does only work with functions with type: void(PEP_SESSION, T): const auto p1 = from_json< typename std::tuple_element<1, param_tuple_t>::type >(parameters[0]); Log << Logger::Debug << "func_name=\"" << func_name << "\", value=" << p1 << "."; std::function func = std::bind(Base::fn, std::placeholders::_1, p1); context->cache(func_name, func); return Base::call(parameters, context); } private: const std::string func_name; }; template class FuncCachePassphrase : public Func { public: typedef Func Base; typedef typename Return::return_type ReturnType; typedef helper Helper; FuncCachePassphrase(const std::string& _func_name ) : Base( &config_passphrase ) , func_name(_func_name) {} js::Value call(const js::Array& parameters, Context* context) const override { Logger Log("FuncCachePasswd::call"); const std::string& passphrase = parameters.at(0).get_str(); Log << Logger::Debug << "func_name=\"" << func_name << "\", value is confidential. "; pEp::passphrase_cache.add(passphrase); // for the current PEP_SESSION std::function func = [passphrase](PEP_SESSION session) { config_passphrase(session, pEp::passphrase_cache.add(passphrase)); // for all other existing and future PEP_SESSIONs }; context->cache(func_name, func); return Base::call(parameters, context); } private: const std::string func_name; }; template class FuncCachePassphrase4NewKeys : public Func { public: typedef Func Base; typedef typename Return::return_type ReturnType; typedef helper Helper; FuncCachePassphrase4NewKeys(const std::string& _func_name ) : Base( &config_passphrase_for_new_keys ) , func_name(_func_name) {} js::Value call(const js::Array& parameters, Context* context) const override { Logger Log("FuncCachePasswd4NK::call"); bool enable = parameters.at(0).get_bool(); const std::string& passphrase = parameters.at(1).get_str(); Log << Logger::Debug << "func_name=\"" << func_name << "\", value is confidential. "; pEp::passphrase_cache.add_stored(passphrase); // for the current PEP_SESSION std::function func = [enable, passphrase](PEP_SESSION session) { // for all other existing and future PEP_SESSIONs config_passphrase_for_new_keys(session, enable, pEp::passphrase_cache.add_stored(passphrase)); }; context->cache(func_name, func); return Base::call(parameters, context); } private: const std::string func_name; }; // Just a separating placeholder in the drop-down list. Does not calls anything. class Separator : public FuncBase { public: Separator() = default; virtual bool isSeparator() const noexcept override { return true; } virtual void setJavaScriptSignature(js::Object& o) const override { o.emplace_back("separator", true); } virtual js::Value call(const js::Array&, Context*) const override { return js::Value{}; } }; //typedef std::map< std::string, FuncBase* > FunctionMap; typedef std::vector< std::pair< std::string, FuncBase*> > FunctionMapBase; class FunctionMap { public: typedef FunctionMapBase::value_type value_type; typedef FunctionMapBase::const_iterator const_iterator; const_iterator begin() const noexcept { return v.begin(); } const_iterator end() const noexcept { return v.end(); } const_iterator find(const std::string&) const noexcept; FunctionMap(std::initializer_list il); ~FunctionMap(); private: FunctionMapBase v; }; typedef FunctionMap::value_type FP; #endif // FUNCTION_MAP_HH