merge JSON-2 into default branch. #close JSON-2

JSON-15
Roker 2016-09-20 16:20:02 +02:00
commit d0d3968346
7 changed files with 352 additions and 112 deletions

14
server/context.hh Normal file
View File

@ -0,0 +1,14 @@
#ifndef JSON_ADAPTER_CONTEXT_HH
#define JSON_ADAPTER_CONTEXT_HH
#include <string>
class Context
{
public:
virtual ~Context() = default;
virtual bool verify_security_token(const std::string& token) const = 0;
};
#endif // JSON_ADAPTER_CONTEXT_HH

View File

@ -3,6 +3,8 @@
#include "json_spirit/json_spirit_value.h"
#include "json_spirit/json_spirit_writer.h"
#include "context.hh"
#include <type_traits>
// Just for debugging:
@ -12,8 +14,8 @@
namespace js = json_spirit;
template<class T> struct In;
template<class T> struct Out;
template<class T, bool NeedInput> struct In;
template<class T, bool NeedInput> struct Out;
// "params" and "position" might be used to fetch additional parameters from the array.
template<class T>
@ -25,21 +27,21 @@ js::Value to_json(const T& t);
// helper classes to specify in- and out-parameters
template<class T>
template<class T, bool NeedInput=true>
struct In
{
typedef T c_type; // the according type in C function parameter
enum { is_output = false };
enum { is_output = false, need_input = NeedInput };
explicit In(const T& t) : value(t) {}
~In();
In(const In<T>& other) = delete;
In(In<T>&& victim) = delete;
In<T>& operator=(const In<T>&) = delete;
In(const In<T,NeedInput>& other) = delete;
In(In<T,NeedInput>&& victim) = delete;
In<T,NeedInput>& operator=(const In<T,NeedInput>&) = delete;
// default implementation:
In(const js::Value& v, const js::Array& params, unsigned position)
In(const js::Value& v, Context*)
: In( from_json<T>(v) )
{ }
@ -53,21 +55,21 @@ struct In
// to call functions that operate directly on the JSON data type
template<class T>
template<class T, bool NeedInput=true>
struct InRaw
{
typedef js::Value c_type; // do not unwrap JSON data type
enum { is_output = false };
enum { is_output = false, need_input = NeedInput };
explicit InRaw(const js::Value& t) : value(t) {}
~InRaw() = default;
InRaw(const InRaw<T>& other) = delete;
InRaw(InRaw<T>&& victim) = delete;
InRaw<T>& operator=(const InRaw<T>&) = delete;
InRaw(const InRaw<T,NeedInput>& other) = delete;
InRaw(InRaw<T,NeedInput>&& victim) = delete;
InRaw<T,NeedInput>& operator=(const InRaw<T,NeedInput>&) = delete;
// default implementation:
InRaw(const js::Value& v, const js::Array& params, unsigned position)
InRaw(const js::Value& v, Context*)
: InRaw(v)
{ }
@ -81,19 +83,19 @@ struct InRaw
// helper classes to specify in- and out-parameters
template<class T>
struct InOut : public In<T>
template<class T, bool NeedInput=true>
struct InOut : public In<T,NeedInput>
{
typedef In<T> Base;
enum { is_output = true };
typedef In<T,NeedInput> Base;
enum { is_output = true, need_input = NeedInput };
explicit InOut(const T& t) : Base(t) {}
~InOut() = default;
InOut<T>& operator=(const InOut<T>&) = delete;
InOut<T,NeedInput>& operator=(const InOut<T,NeedInput>&) = delete;
// default implementation:
InOut(const js::Value& v, const js::Array& params, unsigned position)
InOut(const js::Value& v, Context*)
: Base( from_json<T>(v) )
{ }
@ -104,11 +106,11 @@ struct InOut : public In<T>
};
template<class T>
template<class T, bool NeedInput = true>
struct Out
{
typedef T* c_type; // the according type in C function parameter
enum { is_output = true };
enum { is_output = true, need_input = NeedInput }; // if need_input=false it would no longer consume an element in the input parameter array.
explicit Out() : value{ new T{} }
{
@ -120,14 +122,14 @@ struct Out
~Out();
Out(const Out<T>& other) = delete;
Out(Out<T>&& victim) = delete;
Out(const Out<T,NeedInput>& other) = delete;
Out(Out<T,NeedInput>&& victim) = delete;
// just to be sure they are not implicitly defined:
Out<T>& operator=(const Out<T>& other) = delete;
Out<T>& operator=(Out<T>&& victim) = delete;
Out<T,NeedInput>& operator=(const Out<T,NeedInput>& other) = delete;
Out<T,NeedInput>& operator=(Out<T,NeedInput>&& victim) = delete;
Out(const js::Value& v, const js::Array& params, unsigned position)
Out(const js::Value& v, Context*)
: Out()
{ }
@ -139,7 +141,7 @@ struct Out
T* value = nullptr;
friend
std::ostream& operator<<(std::ostream& o, const Out<T>& out)
std::ostream& operator<<(std::ostream& o, const Out<T,NeedInput>& out)
{
o << (const void*)&out;
@ -159,14 +161,14 @@ struct Out
};
template<class T>
js::Value to_json(const Out<T>& o)
template<class T, bool NeedInput>
js::Value to_json(const Out<T,NeedInput>& o)
{
return ::to_json(*o.value);
}
template<class T>
js::Value to_json(const InOut<T>& o)
template<class T, bool NeedInput>
js::Value to_json(const InOut<T,NeedInput>& o)
{
return ::to_json(o.value);
}
@ -186,7 +188,15 @@ template<class R, unsigned U, class... Args>
class helper<R, U, U, Args...>
{
public:
static js::Value call( const std::function<R(typename Args::c_type...)>& fn, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
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<R(typename Args::c_type...)>& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
{
return to_json( fn(args.value...) );
}
@ -198,7 +208,15 @@ template<unsigned U, class... Args>
class helper<void, U, U, Args...>
{
public:
static js::Value call( const std::function<void(typename Args::c_type...)>& fn, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
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<void(typename Args::c_type...)>& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
{
fn(args.value...);
return js::Value{};
@ -217,16 +235,29 @@ public:
typedef typename std::tuple_element<U, Tuple>::type Element; // The type of the U'th parameter
typedef helper<R, U+1, MAX, Args...> NextHelper;
public:
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<class... A2>
static js::Value call( const std::function<R(typename Args::c_type...)>& fn, js::Array& out_parameters, const js::Array& parameters, const A2&... a2)
static js::Value call( const std::function<R(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], parameters, U);
const Element element(parameters[U], ctx);
const js::Value ret = NextHelper::call(fn, out_parameters, parameters, a2..., element );
const js::Value ret = NextHelper::call(fn, ctx, out_parameters, parameters, a2..., element );
if(Element::is_output)
{
js::Value out = element.to_json();
@ -247,25 +278,25 @@ struct Type2String
};
template<class T>
struct Type2String<In<T>>
struct Type2String<In<T, true>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T>
struct Type2String<InRaw<T>>
struct Type2String<InRaw<T, true>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T>
struct Type2String<Out<T>>
struct Type2String<Out<T, true>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "Out"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T>
struct Type2String<InOut<T>>
struct Type2String<InOut<T, true>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
@ -277,7 +308,10 @@ struct Type2Json<T, Args...>
{
static js::Array& get(js::Array& a)
{
a.push_back( Type2String<T>::get() );
if(T::need_input)
{
a.push_back( Type2String<T>::get() );
}
Type2Json<Args...>::get(a);
return a;
}
@ -298,7 +332,7 @@ 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) const = 0;
virtual js::Value call(const js::Array& params, Context* context) const = 0;
};
@ -306,9 +340,6 @@ template<class R, class... Args>
class Func : public FuncBase
{
public:
// typedef std::tuple<Args...> arg_t;
enum { Size = sizeof...(Args) };
virtual ~Func() = default;
virtual bool isSeparator() const override
{
@ -323,21 +354,34 @@ public:
std::function<R(typename Args::c_type ...)> fn;
js::Value call(const js::Array& parameters) const override
js::Value call(const js::Array& parameters, Context* context) const override
{
if(parameters.size() != sizeof...(Args))
typedef helper<R, 0, sizeof...(Args), Args...> 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( sizeof...(Args) ) + " element(s)! "
"but I expect " + std::to_string( Helper::nr_of_input_params) + " element(s)! "
);
const js::Array* p_params = &parameters;
// 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 = &param_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( 1 + sizeof...(Args) ); // too big, but who cares?
out_params.reserve( 1 + Helper::nr_of_output_params );
js::Value ret = helper<R, 0, sizeof...(Args), Args...>::call(fn, out_params, parameters);
js::Value ret = Helper::call(fn, context, out_params, *p_params);
out_params.push_back( ret );
return out_params;
}
@ -348,7 +392,7 @@ public:
Type2Json<Args...>::get(params);
o.emplace_back( "return", Type2String<R>::get() );
o.emplace_back( "params", params );
o.emplace_back( "params", std::move(params) );
o.emplace_back( "separator", false );
}
};
@ -359,9 +403,9 @@ class Separator : public FuncBase
{
public:
Separator() = default;
virtual bool isSeparator() const override { return true; }
virtual void setJavaScriptSignature(js::Object& o) const override { o.emplace_back("separator", true); }
virtual js::Value call(const js::Array& params) const override { return js::Value{}; }
virtual bool isSeparator() const 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;

View File

@ -27,6 +27,21 @@
#include "json_spirit/json_spirit_utils.h"
template<>
In<Context*, false>::~In()
{
// do nothing
}
template<>
In<Context*, false>::In(const js::Value&, Context* ctx)
: value( ctx )
{
}
namespace {
static const unsigned API_VERSION = 0x0002;
@ -79,15 +94,28 @@ PEP_STATUS get_gpg_path(const char** path)
}
PEP_STATUS registerEventListener(std::string address, unsigned port, std::string securityContext)
PEP_STATUS registerEventListener(Context* ctx, std::string address, unsigned port, std::string securityContext)
{
// TODO: implement it!
JsonAdapter* ja = dynamic_cast<JsonAdapter*>(ctx);
if(!ja)
{
return PEP_STATUS(-42);
}
ja->registerEventListener(address, port, securityContext);
return PEP_STATUS_OK;
}
PEP_STATUS unregisterEventListener(std::string address, unsigned port, std::string securityContext)
PEP_STATUS unregisterEventListener(Context* ctx, std::string address, unsigned port, std::string securityContext)
{
// TODO: implement it!
JsonAdapter* ja = dynamic_cast<JsonAdapter*>(ctx);
if(!ja)
{
return PEP_STATUS(-42);
}
ja->unregisterEventListener(address, port, securityContext);
return PEP_STATUS_OK;
}
@ -98,44 +126,44 @@ const FunctionMap functions = {
// from message_api.h
FP( "—— Message API ——", new Separator ),
FP( "encrypt_message", new Func<PEP_STATUS, In<PEP_SESSION>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>>( &encrypt_message ) ),
FP( "decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION>, In<message*>, Out<message*>, Out<stringlist_t*>, Out<PEP_color>, Out<PEP_decrypt_flags_t>>( &decrypt_message ) ),
FP( "outgoing_message_color", new Func<PEP_STATUS, In<PEP_SESSION>, In<message*>, Out<PEP_color>>( &outgoing_message_color ) ),
FP( "identity_color" , new Func<PEP_STATUS, In<PEP_SESSION>, In<pEp_identity*>, Out<PEP_color>>( &identity_color) ),
FP( "encrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>>( &encrypt_message ) ),
FP( "decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<message*>, Out<message*>, Out<stringlist_t*>, Out<PEP_color>, Out<PEP_decrypt_flags_t>>( &decrypt_message ) ),
FP( "outgoing_message_color", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<message*>, Out<PEP_color>>( &outgoing_message_color ) ),
FP( "identity_color" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, Out<PEP_color>>( &identity_color) ),
FP( "get_gpg_path", new Func<PEP_STATUS, Out<const char*>>(&get_gpg_path) ),
FP( "—— pEp Engine Core API ——", new Separator),
FP( "log_event", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<const char*>, In<const char*>, In<const char*>>( &log_event) ),
FP( "trustwords", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<const char*>, Out<char*>, Out<size_t>, In<int>>( &trustwords) ),
FP( "get_languagelist", new Func<PEP_STATUS, In<PEP_SESSION>, Out<char*>>( &get_languagelist) ),
FP( "get_phrase" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<int>, Out<char*>> ( &get_phrase) ),
FP( "log_event", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<const char*>, In<const char*>, In<const char*>>( &log_event) ),
FP( "trustwords", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<const char*>, Out<char*>, Out<size_t>, In<int>>( &trustwords) ),
FP( "get_languagelist", new Func<PEP_STATUS, In<PEP_SESSION,false>, Out<char*>>( &get_languagelist) ),
FP( "get_phrase" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<int>, Out<char*>> ( &get_phrase) ),
FP( "get_engine_version", new Func<const char*> ( &get_engine_version) ),
FP( "—— Identity Management API ——", new Separator),
FP( "get_identity" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<const char*>, Out<pEp_identity*>>( &get_identity) ),
FP( "set_identity" , new Func<PEP_STATUS, In<PEP_SESSION>, In<pEp_identity*>> ( &set_identity) ),
FP( "mark_as_comprimized", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>> ( &mark_as_compromized) ),
FP( "get_identity" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<const char*>, Out<pEp_identity*>>( &get_identity) ),
FP( "set_identity" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>> ( &set_identity) ),
FP( "mark_as_comprimized", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>> ( &mark_as_compromized) ),
FP( "—— Low level Key Management API ——", new Separator),
FP( "generate_keypair", new Func<PEP_STATUS, In<PEP_SESSION>, InOut<pEp_identity*>> ( &generate_keypair) ),
FP( "delete_keypair", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>> ( &delete_keypair) ),
FP( "import_key" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<std::size_t>, Out<identity_list*>> ( &import_key) ),
FP( "export_key" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, Out<char*>, Out<std::size_t>> ( &export_key) ),
FP( "find_keys" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, Out<stringlist_t*>> ( &find_keys) ),
FP( "get_trust" , new Func<PEP_STATUS, In<PEP_SESSION>, InOut<pEp_identity*>> ( &get_trust) ),
FP( "own_key_is_listed", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, Out<bool>> ( &own_key_is_listed) ),
FP( "own_key_retrieve" , new Func<PEP_STATUS, In<PEP_SESSION>, Out<stringlist_t*>> ( &own_key_retrieve) ),
FP( "generate_keypair", new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &generate_keypair) ),
FP( "delete_keypair", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>> ( &delete_keypair) ),
FP( "import_key" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<std::size_t>, Out<identity_list*>> ( &import_key) ),
FP( "export_key" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, Out<char*>, Out<std::size_t>> ( &export_key) ),
FP( "find_keys" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, Out<stringlist_t*>> ( &find_keys) ),
FP( "get_trust" , new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &get_trust) ),
FP( "own_key_is_listed", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, Out<bool>> ( &own_key_is_listed) ),
FP( "own_key_retrieve" , new Func<PEP_STATUS, In<PEP_SESSION,false>, Out<stringlist_t*>> ( &own_key_retrieve) ),
FP( "least_trust" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, Out<PEP_comm_type>> ( &least_trust) ),
FP( "get_key_rating", new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, Out<PEP_comm_type>> ( &get_key_rating) ),
FP( "renew_key" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<const timestamp*>> ( &renew_key) ),
FP( "revoke" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<const char*>> ( &revoke_key) ),
FP( "key_expired" , new Func<PEP_STATUS, In<PEP_SESSION>, In<const char*>, In<time_t>, Out<bool>> ( &key_expired) ),
FP( "least_trust" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, Out<PEP_comm_type>> ( &least_trust) ),
FP( "get_key_rating", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, Out<PEP_comm_type>> ( &get_key_rating) ),
FP( "renew_key" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<const timestamp*>> ( &renew_key) ),
FP( "revoke" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<const char*>> ( &revoke_key) ),
FP( "key_expired" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const char*>, In<time_t>, Out<bool>> ( &key_expired) ),
FP( "-- Event Listener & Results", new Separator ),
FP( "registerEventListener" , new Func<PEP_STATUS, In<std::string>, In<unsigned>, In<std::string>> ( &registerEventListener) ),
FP( "unregisterEventListener", new Func<PEP_STATUS, In<std::string>, In<unsigned>, In<std::string>> ( &unregisterEventListener) ),
FP( "deliverHandshakeResult" , new Func<PEP_STATUS, In<PEP_SESSION>, In<sync_handshake_result>> (&deliverHandshakeResult) ),
FP( "registerEventListener" , new Func<PEP_STATUS, In<Context*, false>, In<std::string>, In<unsigned>, In<std::string>> ( &registerEventListener) ),
FP( "unregisterEventListener", new Func<PEP_STATUS, In<Context*, false>, In<std::string>, In<unsigned>, In<std::string>> ( &unregisterEventListener) ),
FP( "deliverHandshakeResult" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<sync_handshake_result>> (&deliverHandshakeResult) ),
// my own example function that does something useful. :-)
FP( "—— Other ——", new Separator ),
@ -279,7 +307,7 @@ void OnApiRequest(evhttp_request* req, void* obj)
try
{
const JsonAdapter* ja = static_cast<const JsonAdapter*>(obj);
JsonAdapter* ja = static_cast<JsonAdapter*>(obj);
std::vector<char> data(length);
ssize_t nr = evbuffer_copyout(inbuf, data.data(), data.size());
@ -291,7 +319,7 @@ void OnApiRequest(evhttp_request* req, void* obj)
if(p.type() == js::obj_type)
{
const js::Object& request = p.get_obj();
answer = call( functions, request, ja->sec_token() );
answer = call( functions, request, ja );
}else{
answer = make_error( JSON_RPC::PARSE_ERROR, "evbuffer_copyout does not return a JSON string. b=" + std::to_string(b), js::Value{data_string}, 42 );
}
@ -382,6 +410,13 @@ auto ThreadDeleter = [](std::thread *t)
typedef std::unique_ptr<std::thread, decltype(ThreadDeleter)> ThreadPtr;
typedef std::vector<ThreadPtr> ThreadPool;
typedef std::pair<std::string, unsigned> EventListenerKey;
struct EventListenerValue
{
std::string securityContext;
std::unique_ptr<evhttp_connection, decltype(&evhttp_connection_free)> connection = { nullptr, &evhttp_connection_free};
};
struct JsonAdapter::Internal
{
@ -389,6 +424,7 @@ struct JsonAdapter::Internal
std::unique_ptr<evhttp, decltype(&evhttp_free)> evHttp = {nullptr, &evhttp_free};
std::string address;
std::string token;
std::map<EventListenerKey, EventListenerValue> eventListener;
unsigned start_port = 0;
unsigned end_port = 0;
@ -397,9 +433,87 @@ struct JsonAdapter::Internal
evutil_socket_t sock = -1;
bool running = false;
ThreadPool threads;
static
void requestDone(evhttp_request* req, void* userdata)
{
// Hum, what is to do here?
}
PEP_STATUS deliverRequest(std::pair<const EventListenerKey,EventListenerValue>& e, const js::Object& request)
{
const std::string uri = "http://" + e.first.first + ":" + std::to_string(e.first.second) + "/";
const std::string request_s = js::write(request, js::raw_utf8);
evhttp_request* ereq = evhttp_request_new( &requestDone, &e ); // ownership goes to the connection in evhttp_make_request() below.
evhttp_add_header(ereq->output_headers, "Host", e.first.first.c_str());
evhttp_add_header(ereq->output_headers, "Content-Length", std::to_string(request_s.length()).c_str());
auto output_buffer = evhttp_request_get_output_buffer(ereq);
evbuffer_add(output_buffer, request_s.data(), request_s.size());
const int ret = evhttp_make_request(e.second.connection.get(), ereq, EVHTTP_REQ_POST, uri.c_str() );
return (ret == 0) ? PEP_STATUS_OK : PEP_UNKNOWN_ERROR;
}
PEP_STATUS messageToSend(const message* msg)
{
js::Value js_msg = to_json(msg);
js::Array param;
param.push_back( std::move(js_msg) );
PEP_STATUS status = PEP_STATUS_OK;
for(auto& e : eventListener)
{
js::Object request = make_request( "messageToSend", param, e.second.securityContext );
const PEP_STATUS s2 = deliverRequest( e, request );
if(s2!=PEP_STATUS_OK)
{
status = s2;
}
}
return status;
}
PEP_STATUS showHandshake(const pEp_identity* self, const pEp_identity* partner)
{
// TODO: eliminate redundancy to messageToSend() above
js::Array param;
param.emplace_back( to_json(self) );
param.emplace_back( to_json(partner) );
PEP_STATUS status = PEP_STATUS_OK;
for(auto& e : eventListener)
{
js::Object request = make_request( "showHandshake", param, e.second.securityContext );
const PEP_STATUS s2 = deliverRequest( e, request );
if(s2!=PEP_STATUS_OK)
{
status = s2;
}
}
return status;
}
};
PEP_STATUS JsonAdapter::messageToSend(void* obj, const message* msg)
{
JsonAdapter* ja = static_cast<JsonAdapter*>(obj);
return ja->i->messageToSend(msg);
}
PEP_STATUS JsonAdapter::showHandshake(void* obj, const pEp_identity* self, const pEp_identity* partner)
{
JsonAdapter* ja = static_cast<JsonAdapter*>(obj);
return ja->i->showHandshake(self, partner);
}
JsonAdapter::JsonAdapter(const std::string& address, unsigned start_port, unsigned end_port)
: i(new Internal)
{
@ -447,6 +561,8 @@ try
session_registry.emplace( id, session);
std::cerr << "\tcreated new session for this thread: " << static_cast<void*>(session) << ".\n";
register_sync_callbacks( session, this, &messageToSend, &showHandshake );
}else{
std::cerr << "\tsession for this thread: " << static_cast<void*>(q->second) << ".\n";
}
@ -542,18 +658,42 @@ void JsonAdapter::shutdown(timeval* t)
}
const std::string& JsonAdapter::sec_token() const
{
return i->token;
}
// returns 'true' if 's' is the security token created by the function above.
bool JsonAdapter::verify_security_token(const std::string& s) const
{
if(s!=i->token)
{
std::cerr << "sec_token=\"" << i->token << "\" is unequal to \"" << s << "\"!\n";
std::cerr << "sec_token=\"" << i->token << "\" (len=" << i->token.size() << ") is unequal to \"" << s << "\" (len=" << s.size() << ")!\n";
}
return s == i->token;
}
void JsonAdapter::registerEventListener(const std::string& address, unsigned port, const std::string& securityContext)
{
const auto key = std::make_pair(address, port);
const auto q = i->eventListener.find(key);
if( q != i->eventListener.end() && q->second.securityContext != securityContext)
{
throw std::runtime_error("EventListener at host \"" + address + "\":" + std::to_string(port) + " is already registered with different securityContext." );
}
EventListenerValue v;
v.securityContext = securityContext;
v.connection.reset( evhttp_connection_base_new( i->eventBase.get(), nullptr, address.c_str(), port ) );
i->eventListener[key] = std::move(v);
}
void JsonAdapter::unregisterEventListener(const std::string& address, unsigned port, const std::string& securityContext)
{
const auto key = std::make_pair(address, port);
const auto q = i->eventListener.find(key);
if( q == i->eventListener.end() || q->second.securityContext != securityContext)
{
throw std::runtime_error("Cannot unregister EventListener at host \"" + address + "\":" + std::to_string(port) + ". Not registered or wrong securityContext." );
}
i->eventListener.erase(q);
}

View File

@ -1,10 +1,11 @@
#ifndef JSON_ADAPTER_HH
#define JSON_ADAPTER_HH
#include <pEp/pEpEngine.h>
#include <pEp/message.h>
#include "registry.hh"
#include "context.hh"
class JsonAdapter
class JsonAdapter : public Context
{
public:
// creates an instance of the JSON adapter. It tries to bind the first available port in the given range
@ -12,12 +13,15 @@ public:
JsonAdapter(const std::string& address, unsigned start_port, unsigned end_port);
// calls abort() on the instance if it is still running().
~JsonAdapter();
virtual ~JsonAdapter();
// don't allow copies
JsonAdapter(const JsonAdapter&) = delete;
void operator=(const JsonAdapter&) = delete;
void registerEventListener(const std::string& address, unsigned port, const std::string& securityContext);
void unregisterEventListener(const std::string& address, unsigned port, const std::string& securityContext);
// run the server in another thread and returns immediately.
void run();
@ -37,9 +41,7 @@ public:
unsigned request_count() const;
// returns 'true' if 's' is the security token created by the function above.
bool verify_security_token(const std::string& s) const;
const std::string& sec_token() const;
virtual bool verify_security_token(const std::string& s) const override;
static
unsigned apiVersion();
@ -48,6 +50,9 @@ public:
static
std::string version();
static PEP_STATUS messageToSend(void* obj, const message* msg);
static PEP_STATUS showHandshake(void* obj, const pEp_identity* self, const pEp_identity* partner);
private:
struct Internal;
Internal* i; // pimpl for stable interface.

View File

@ -4,6 +4,7 @@
#include "json-adapter.hh"
#include "security-token.hh"
// Server side:
js::Object make_result(const js::Value& result, int id)
{
@ -33,6 +34,22 @@
return ret;
}
// Client side:
js::Object make_request(const std::string& functionName, const js::Array& parameters, const std::string& securityContext)
{
static int request_id = 2000;
js::Object request;
request.emplace_back( "jsonrpc", "2.0" );
request.emplace_back( "id" , ++request_id );
request.emplace_back( "security_token", securityContext );
request.emplace_back( "method", functionName );
request.emplace_back( "params", parameters );
return request;
}
namespace
{
@ -45,7 +62,7 @@ namespace
using json_spirit::find_value;
js::Object call(const FunctionMap& fm, const js::Object& request, const std::string& sec_token_orig)
js::Object call(const FunctionMap& fm, const js::Object& request, Context* context)
{
int request_id = -1;
try
@ -57,7 +74,7 @@ js::Object call(const FunctionMap& fm, const js::Object& request, const std::str
}
const auto sec_token = find_value(request, "security_token");
if(sec_token.type()!=js::str_type || (sec_token.get_str()!=sec_token_orig) )
if(sec_token.type()!=js::str_type || context->verify_security_token(sec_token.get_str())==false )
{
return make_error(JSON_RPC::INVALID_REQUEST, "Invalid request: Wrong security token.", request, request_id);
}
@ -98,7 +115,8 @@ js::Object call(const FunctionMap& fm, const js::Object& request, const std::str
std::cerr << "=== Now I do the call!\n"
"\tmethod_name=\"" << method_name << "\","
"\tparams=" << js::write(params) << ". ===\n";
const js::Value result = fn->second->call(p);
const js::Value result = fn->second->call(p, context);
std::cerr << "=== Result of call: " << js::write(result, js::raw_utf8) << ". ===\n";
std::cerr << "\tSessions: " << getSessions() << "\n";

View File

@ -2,6 +2,7 @@
#define JSON_RPC_HH
#include "json_spirit/json_spirit_value.h"
#include "context.hh"
#include "function_map.hh"
namespace js = json_spirit;
@ -16,10 +17,11 @@ enum class JSON_RPC
INTERNAL_ERROR = -32603,
};
// Server side:
// parse the JSON-RPC 2.0 compatible "request", call the function
// parse the JSON-RPC 2.0 compatible "request", call the C function
// and create an appropiate "response" object (containing a result or an error)
js::Object call(const FunctionMap& fm, const js::Object& request, const std::string& sec_token_orig);
js::Object call(const FunctionMap& fm, const js::Object& request, Context* context);
// create a JSON-RPC 2.0 compatible result response object
js::Object make_result(const js::Value& result, int id);
@ -28,4 +30,7 @@ js::Object make_result(const js::Value& result, int id);
js::Object make_error(JSON_RPC error_code, const std::string& error_message, const js::Value& data, int id);
// Client side:
js::Object make_request(const std::string& functionName, const js::Array& parameters, const std::string& securityContext);
#endif // JSON_RPC_HH

View File

@ -50,7 +50,7 @@ std::string status_to_string(PEP_STATUS status)
template<>
In<PEP_SESSION>::~In()
In<PEP_SESSION, false>::~In()
{
// no automatic release!
}
@ -226,13 +226,14 @@ pEp_identity* from_json<pEp_identity*>(const js::Value& v)
free(lang);
}
ident->me = from_json_object<bool, js::bool_type>(o, "me");
ident->flags = from_json_object<unsigned, js::int_type>(o, "flags");
return ident;
}
template<>
js::Value to_json<message*>(message* const& msg)
js::Value to_json<message const*>(message const* const& msg)
{
if(msg == nullptr)
{
@ -270,6 +271,12 @@ js::Value to_json<message*>(message* const& msg)
return js::Value( std::move(o) );
}
template<>
js::Value to_json<message*>(message* const& msg)
{
return to_json( const_cast<const message*>(msg) );
}
template<>
stringlist_t* from_json<stringlist_t*>(const js::Value& v)
@ -449,7 +456,7 @@ js::Value to_json<stringlist_t*>(stringlist_t* const& osl)
template<>
js::Value to_json<pEp_identity*>(pEp_identity* const& id)
js::Value to_json<const pEp_identity*>(const pEp_identity* const& id)
{
if(id == nullptr)
{
@ -468,11 +475,18 @@ js::Value to_json<pEp_identity*>(pEp_identity* const& id)
if(id->lang[0] && id->lang[1])
o.emplace_back( "lang", js::Value( std::string( id->lang, id->lang+2) ));
o.emplace_back( "me", js::Value( id->me ));
o.emplace_back( "me", bool( id->me ));
o.emplace_back( "flags", uint64_t( id->me ));
return js::Value( std::move(o) );
}
template<>
js::Value to_json<pEp_identity*>(pEp_identity* const& id)
{
return to_json( const_cast<const pEp_identity*>(id) );
}
template<>
js::Value to_json<identity_list*>(identity_list* const& idl)