merge JSON-93 (to support InLength<>) into "default"
authorRoker <roker@pep-project.org>
Thu, 07 Jun 2018 13:54:10 +0200
changeset 5430d96e14247fc
parent 509 18e1355d488f
parent 542 42ff394ca203
child 544 4d8c8c338918
merge JSON-93 (to support InLength<>) into "default"
     1.1 --- a/.hgignore	Tue May 15 10:58:43 2018 +0200
     1.2 +++ b/.hgignore	Thu Jun 07 13:54:10 2018 +0200
     1.3 @@ -5,6 +5,7 @@
     1.4  *.Po
     1.5  *.Plo
     1.6  *.pc
     1.7 +*~
     1.8  
     1.9  libevent-*-stable/Makefile
    1.10  libevent-*-stable/config.h
     2.1 --- a/README.md	Tue May 15 10:58:43 2018 +0200
     2.2 +++ b/README.md	Thu Jun 07 13:54:10 2018 +0200
     2.3 @@ -141,7 +141,7 @@
     2.4  You can use `make run` to start the server.
     2.5  
     2.6  1. Run ./pep-json-server.  This creates a file that is readable only by the
     2.7 -   current user (/tmp/pEp-json-token-${USER}) and contains the address and
     2.8 +   current user (~/.pEp/json-token-${USER}) and contains the address and
     2.9     port the JSON adapter is listening on, normally 127.0.0.1:4223 and a
    2.10     "security-token" that must be given in each function call to authenticate
    2.11     you as the valid user.
    2.12 @@ -266,9 +266,10 @@
    2.13     check for the right ownership and access rights of the token file and its
    2.14     directory. (TODO: What shall be done if that check fails?)
    2.15  
    2.16 -1. The server creates a "server token file" containing a "server token" and
    2.17 -   the IP address and port where the server listens on.  This file can only
    2.18 -   be read by client programs that run with the same user rights.
    2.19 +1. The server creates a "server token file" containing a "server token" (a
    2.20 +   random-generated string of printable ASCII characters) and the IP address
    2.21 +   and port where the server listens on.  This file can only be read by
    2.22 +   client programs that run with the same user rights.
    2.23  
    2.24  2. The client checks the path, reads the "server token" from the file and
    2.25     authenticates itself to the server in each JSON RPC call with that "server
    2.26 @@ -285,7 +286,7 @@
    2.27  * The `FunctionMap function` in `ev_server.cc` defines which functions
    2.28    are callable via the JSON-RPC interface.  The existing entries show the
    2.29    syntax of that map.
    2.30 -  Non-static member functions can be called, too. Thanks to std::function<>
    2.31 +  Non-static member functions can be called, too. Thanks to `std::function<>`
    2.32    a member function `Foo::func(Params...)` is handled like a free-standing
    2.33    function `func(Foo* f, Params...)`.
    2.34  
    2.35 @@ -298,17 +299,129 @@
    2.36  * The specializations for "p≡p-specific types" are in `pep-types.cc`.
    2.37  
    2.38  
    2.39 +#### Parameter directions (In, Out, InOut)
    2.40 +
    2.41 +The p≡p JSON Server Adapter supports Input, Output and two ways of "In/Out"
    2.42 +parameters.  You have to annotate the direction in the FunctionMap with
    2.43 +`In<>` for input, `Out<>` for output and InOut<> or InOutP<> for in/out
    2.44 +parameters.  These wrapper classes have an optional second template
    2.45 +parameter (parameter type flag) that is explained below.
    2.46 +
    2.47 +Return values are always "output" parameters, so they don't have to be
    2.48 +wrapped with `Out<>`, but this wrapper is necessary when you need
    2.49 +non-default wrapper semantics, see below.
    2.50 +
    2.51 +
    2.52 +Input parameters of fundamental or simple struct types are
    2.53 +usually by-value parameters. Complex structs (or structs that are only
    2.54 +forward-declared in the public API) are usually pointer
    2.55 +parameters. Both ways are supported. You have to specialize `In<T>` or
    2.56 +`In<T*>`, depending how your type is used.
    2.57 +
    2.58 +Output parameters of fundamental or simple struct types `T` are usually
    2.59 +declared as a paremeter of type `T*`. The p≡p JSON Server Adapter manages
    2.60 +the memory allocated by the called C function automatically and calls the
    2.61 +appropriate de-allocating function after use.
    2.62 +
    2.63 +Calling a function with output parameters requires a dummy value (`null` or
    2.64 +empty string is fine) at the JSON side for each output parameter to keep the
    2.65 +number of parameters at the JSON side the same with the C side.
    2.66 +
    2.67 +For In/Out parameters there exist two calling conventions for
    2.68 +call-by-pointer types:
    2.69 +
    2.70 +1. caller allocates object and fills with input values, callee can only change members.
    2.71 +The C type of the parameter is usually `struct T*`. Use the wrapper `InOut<>`
    2.72 +for these parameters.
    2.73 +
    2.74 +2. caller allocates object and fills with input values, callee might
    2.75 +change/reallocate the whole object. The C type of the parameter is
    2.76 +`struct T**`. Use the wrapper `InOutP<>` in these cases.
    2.77 +
    2.78 +`InOutP<>` is also the right wrapper for in/out parameters of fundamental or
    2.79 +enum types due to the additional indirection in the C function call
    2.80 +signature.
    2.81 +
    2.82 +
    2.83 +#### Parameter type flags
    2.84 +
    2.85 +The wrapper classes might be instantiated with special "parameter type
    2.86 +flags". If no flag is given the `DefaultFlag` is used with means the
    2.87 +semantics described already above.
    2.88 +
    2.89 +At the moment there exist two parameter type flags which are interpreted as
    2.90 +bitfield, so they can be combined:
    2.91 +
    2.92 +* NoInput : This flags a parameter at the C side that shall not be exposed
    2.93 +  at the JSON side. So the value cannot be specified by the client, it is
    2.94 +  provided by the JSON Server Adapter internally (e.g. for PEP_SESSION)
    2.95 +
    2.96 +* DontOwn : Used for pointer types who don't "own" the referred ressource,
    2.97 +  so it is not released automatically by the JSON Server Adapter after the
    2.98 +  call.
    2.99 +
   2.100 +More flags will be added when different semantics will be needed.
   2.101 +
   2.102 +
   2.103 +### Automatic parameter value generation
   2.104 +
   2.105 +For some parameters or parameter combinations the JSON Server Adapter is
   2.106 +able to generate the values automatically either from the environment or
   2.107 +from other parameters.
   2.108 +
   2.109 +These automatic parameter value generators are supported at the moment:
   2.110 +
   2.111 +#### InLength
   2.112 +
   2.113 +For functions that have a string parameter of type `const char*` followed by
   2.114 +a `size_t` that specifies the length of the string, the JSON Adapter can
   2.115 +calculate the value of that length parameter automatically because in the
   2.116 +JSON API the lengths of strings are always known.
   2.117 +
   2.118 +Moreover, the "length" that has to be given here means the length of the
   2.119 +string seen by the C API side, after processing of all JSON escaping
   2.120 +mechanisms, so it might be difficult to calculate that value at client side.
   2.121 +
   2.122 +Example:
   2.123 +```
   2.124 +// C function declaration:
   2.125 +char* tohex(const char* input, size_t length);
   2.126 +
   2.127 +// API definition:
   2.128 +// with implicit length parameter, with dummy JSON parameter
   2.129 +FP( "tohex", new Func<char*, In<c_string>, InLength<>>( &tohex ))
   2.130 +```
   2.131 +
   2.132 +To be compatible with previous API versions the `InLength` parameter still
   2.133 +needs a dummy placeholder in the JSON interface, but its value is no longer
   2.134 +relevant:
   2.135 +
   2.136 +```
   2.137 +{"jsonrpc":"2.0", "id":28,
   2.138 + "method":"tohex", "params":["some string","dummy_parameter"]
   2.139 +}
   2.140 +```
   2.141 +
   2.142 +It is possible to specifiy `InLength<ParamFlag::NoInput>` so no
   2.143 +parameter is exposed to the JSON API anymore:
   2.144 +
   2.145 +```
   2.146 +FP( "tohex", new Func<char*, In<c_string>, InLength<ParamFlag::NoInput>>( &tohex ))
   2.147 +```
   2.148 +
   2.149 +Now the 2nd parameter is omitted:
   2.150 +```
   2.151 +{"jsonrpc":"2.0", "id":28,
   2.152 + "method":"tohex", "params":["some string"]
   2.153 +}
   2.154 +```
   2.155 +
   2.156 +
   2.157  ## TODOs
   2.158  
   2.159  The following issues are planned but not yet implemented.
   2.160  
   2.161 -* Windows build:
   2.162 -    * implement get_token_filename() for MS Windows (security-token.cc line 43)
   2.163 -    * do the Windows-specific stuff to build the software on Windows
   2.164 -
   2.165 -* Add unit tests
   2.166 -
   2.167 -* Fix the bugs that are found by the Unit tests, if any.
   2.168 +* More sensible unit tests
   2.169  
   2.170  * Generate all the tedious boiler plate code
   2.171      * the content of pep-types.cc
     3.1 --- a/server/c_string.cc	Tue May 15 10:58:43 2018 +0200
     3.2 +++ b/server/c_string.cc	Thu Jun 07 13:54:10 2018 +0200
     3.3 @@ -1,7 +1,18 @@
     3.4  #include "c_string.hh"
     3.5  #include <pEp/pEpEngine.h>
     3.6  
     3.7 -Out<c_string, true>::~Out()
     3.8 +template<>
     3.9 +Out<c_string, ParamFlag::Default>::~Out()
    3.10  {
    3.11  	pEp_free(value);
    3.12  }
    3.13 +
    3.14 +template<>
    3.15 +Out<c_string, ParamFlag::DontOwn>::~Out()
    3.16 +{
    3.17 +	// don't pEp_free() the value!
    3.18 +}
    3.19 +
    3.20 +template<>
    3.21 +In<size_t, ParamFlag::NoInput>::~In()
    3.22 +{}
     4.1 --- a/server/c_string.hh	Tue May 15 10:58:43 2018 +0200
     4.2 +++ b/server/c_string.hh	Thu Jun 07 13:54:10 2018 +0200
     4.3 @@ -12,13 +12,13 @@
     4.4  { };
     4.5  
     4.6  
     4.7 -template<>
     4.8 -struct In<c_string, true>
     4.9 +template<ParamFlag PF>
    4.10 +struct In<c_string, PF>
    4.11  {
    4.12 -	typedef In<c_string, true> Self;
    4.13 +	typedef In<c_string, PF> Self;
    4.14  	
    4.15  	typedef const char* c_type;
    4.16 -	enum { is_output = false, need_input = true };
    4.17 +	enum { is_output = false, need_input = !(PF & ParamFlag::NoInput) };
    4.18  	
    4.19  	~In() = default;
    4.20  	
    4.21 @@ -26,9 +26,11 @@
    4.22  	In(Self&& victim) = delete;
    4.23  	Self& operator=(const Self&) = delete;
    4.24  	
    4.25 -	In(const js::Value& v, Context*)
    4.26 +	In(const js::Value& v, Context* ctx, unsigned param_nr)
    4.27  	: value( from_json<std::string>(v) )
    4.28 -	{ }
    4.29 +	{
    4.30 +		ctx->store(param_nr, value.length());
    4.31 +	}
    4.32  	
    4.33  	js::Value to_json() const
    4.34  	{
    4.35 @@ -41,17 +43,17 @@
    4.36  };
    4.37  
    4.38  
    4.39 -template<>
    4.40 -struct Out<c_string, true>
    4.41 +template<ParamFlag PF>
    4.42 +struct Out<c_string, PF>
    4.43  {
    4.44 -	typedef Out<c_string, true> Self;
    4.45 +	typedef Out<c_string, PF> Self;
    4.46  	
    4.47  	typedef char** c_type;
    4.48 -	enum { is_output = true, need_input = true };
    4.49 +	enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) };
    4.50  	
    4.51 -	Out(const js::Value&, Context*) // ignore dummy value, ignore context
    4.52 +	Out(const js::Value&, Context*, unsigned) // ignore dummy value, ignore context, ignore param_nr
    4.53  	{ }
    4.54 -
    4.55 +	
    4.56  	~Out();
    4.57  	
    4.58  	Out(const Self& other) = delete;
    4.59 @@ -71,11 +73,31 @@
    4.60  
    4.61  // forward declare specializations (to avoid selecting of the default implementation),
    4.62  // but don't implement them, because not needed, yet.
    4.63 -template<>
    4.64 -struct InOut<c_string,true>;
    4.65 +template<ParamFlag PF>
    4.66 +struct InOut<c_string, PF>;
    4.67  
    4.68 -template<>
    4.69 -struct InOutP<c_string,true>;
    4.70 +template<ParamFlag PF>
    4.71 +struct InOutP<c_string, PF>;
    4.72  
    4.73  
    4.74 +// Holds the length of the string in the previous c_string parameter
    4.75 +template<ParamFlag PF = ParamFlag::Default>
    4.76 +struct InLength : In<size_t, PF>
    4.77 +{
    4.78 +	typedef In<size_t, PF> Base;
    4.79 +	
    4.80 +	InLength(const js::Value& v, Context* ctx, unsigned param_nr)
    4.81 +	: Base( ctx->retrieve(param_nr-1) )
    4.82 +	{}
    4.83 +	
    4.84 +	~InLength() = default;
    4.85 +};
    4.86 +
    4.87 +
    4.88 +template<ParamFlag PF>
    4.89 +struct Type2String<InLength<PF>>
    4.90 +{
    4.91 +	static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<size_t>::get() ); return ret; }
    4.92 +};
    4.93 +
    4.94  #endif // PEP_JSON_ADAPTER_C_STRING_HH
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/server/context.cc	Thu Jun 07 13:54:10 2018 +0200
     5.3 @@ -0,0 +1,17 @@
     5.4 +#include "context.hh"
     5.5 +
     5.6 +void Context::store(int position, size_t value)
     5.7 +{
     5.8 +	obj_store.emplace( position, value );
     5.9 +}
    5.10 +
    5.11 +size_t Context::retrieve(int position)
    5.12 +{
    5.13 +	return obj_store.at(position);
    5.14 +}
    5.15 +
    5.16 +
    5.17 +void Context::clear()
    5.18 +{
    5.19 +	obj_store.clear();
    5.20 +}
     6.1 --- a/server/context.hh	Tue May 15 10:58:43 2018 +0200
     6.2 +++ b/server/context.hh	Thu Jun 07 13:54:10 2018 +0200
     6.3 @@ -3,6 +3,7 @@
     6.4  
     6.5  #include <string>
     6.6  #include "json_spirit/json_spirit_value.h"
     6.7 +#include <map>
     6.8  
     6.9  class Context
    6.10  {
    6.11 @@ -11,6 +12,16 @@
    6.12  	
    6.13  	virtual bool verify_security_token(const std::string& token) const = 0;
    6.14  	virtual void augment(json_spirit::Object& returnObject) = 0;
    6.15 +	
    6.16 +	// store and retrieve other parameters into the context.
    6.17 +	// that allows semantic actions based on other function parameters
    6.18 +	// KISS: at the moment only "size_t" objects are supported.
    6.19 +	virtual void store(int position, size_t value);
    6.20 +	virtual size_t retrieve(int position);
    6.21 +	virtual void clear();
    6.22 +
    6.23 +private:
    6.24 +	std::map<int, size_t> obj_store;
    6.25  };
    6.26  
    6.27  #endif // JSON_ADAPTER_CONTEXT_HH
     7.1 --- a/server/ev_server.cc	Tue May 15 10:58:43 2018 +0200
     7.2 +++ b/server/ev_server.cc	Thu Jun 07 13:54:10 2018 +0200
     7.3 @@ -21,13 +21,13 @@
     7.4  
     7.5  
     7.6  template<>
     7.7 -In<Context*, false>::~In()
     7.8 +In<Context*, ParamFlag::Default>::~In()
     7.9  {
    7.10  	// do nothing
    7.11  }
    7.12  
    7.13  template<>
    7.14 -In<Context*, false>::In(const js::Value&, Context* ctx)
    7.15 +In<Context*, ParamFlag::Default>::In(const js::Value&, Context* ctx, unsigned)
    7.16  : value( ctx )
    7.17  {
    7.18  
    7.19 @@ -51,87 +51,90 @@
    7.20  }
    7.21  
    7.22  
    7.23 +using In_Pep_Session = In<PEP_SESSION, ParamFlag::NoInput>;
    7.24 +
    7.25 +
    7.26  // these are the pEp functions that are callable by the client
    7.27  const FunctionMap functions = {
    7.28  		// from message_api.h
    7.29  		FP( "Message API", new Separator ),
    7.30 -		FP( "MIME_encrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<c_string>, In<size_t>, In<stringlist_t*>,
    7.31 +		FP( "MIME_encrypt_message", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, InLength<>, In<stringlist_t*>,
    7.32  			Out<char*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &MIME_encrypt_message ) ),
    7.33 -		FP( "MIME_encrypt_message_for_self", new Func<PEP_STATUS, In<PEP_SESSION, false>, 
    7.34 -			In<pEp_identity*>, In<c_string>, In<size_t>, In<stringlist_t*>,
    7.35 +		FP( "MIME_encrypt_message_for_self", new Func<PEP_STATUS, In_Pep_Session, 
    7.36 +			In<pEp_identity*>, In<c_string>, InLength<>, In<stringlist_t*>,
    7.37  			Out<char*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &MIME_encrypt_message_for_self ) ),
    7.38  			
    7.39 -		FP( "MIME_decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<c_string>, In<size_t>,
    7.40 +		FP( "MIME_decrypt_message", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, InLength<>,
    7.41  			Out<char*>, InOutP<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>, Out<c_string>>( &MIME_decrypt_message ) ),
    7.42  		
    7.43 -		FP( "startKeySync", new Func<void, In<JsonAdapter*, false>>( &JsonAdapter::startSync) ),
    7.44 -		FP( "stopKeySync",  new Func<void, In<JsonAdapter*, false>>( &JsonAdapter::stopSync ) ),
    7.45 +		FP( "startKeySync", new Func<void, In<JsonAdapter*,ParamFlag::NoInput>>( &JsonAdapter::startSync) ),
    7.46 +		FP( "stopKeySync",  new Func<void, In<JsonAdapter*,ParamFlag::NoInput>>( &JsonAdapter::stopSync ) ),
    7.47  		FP( "startKeyserverLookup", new Func<void>( &JsonAdapter::startKeyserverLookup) ),
    7.48  		FP( "stopKeyserverLookup",  new Func<void>( &JsonAdapter::stopKeyserverLookup ) ),
    7.49  		
    7.50 -		FP( "encrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message ) ),
    7.51 -		FP( "encrypt_message_for_self", new Func<PEP_STATUS, In<PEP_SESSION, false>,
    7.52 +		FP( "encrypt_message", new Func<PEP_STATUS, In_Pep_Session, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message ) ),
    7.53 +		FP( "encrypt_message_for_self", new Func<PEP_STATUS, In_Pep_Session,
    7.54  			In<pEp_identity*>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message_for_self ) ),
    7.55  		
    7.56 -		FP( "decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, InOut<message*>, Out<message*>, InOutP<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>>(  &decrypt_message ) ),
    7.57 -		FP( "outgoing_message_rating", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<message*>, Out<PEP_rating>>( &outgoing_message_rating ) ),
    7.58 -		FP( "identity_rating" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),
    7.59 +		FP( "decrypt_message", new Func<PEP_STATUS, In_Pep_Session, InOut<message*>, Out<message*>, InOutP<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>>(  &decrypt_message ) ),
    7.60 +		FP( "outgoing_message_rating", new Func<PEP_STATUS, In_Pep_Session, In<message*>, Out<PEP_rating>>( &outgoing_message_rating ) ),
    7.61 +		FP( "identity_rating" , new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),
    7.62  		
    7.63  		FP( "pEp Engine Core API", new Separator),
    7.64 -//		FP( "log_event",  new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<c_string>, In<c_string>, In<c_string>>( &log_event) ),
    7.65 -		FP( "get_trustwords", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<const pEp_identity*>, In<const pEp_identity*>, In<Language>, Out<char*>, Out<size_t>, In<bool>>( &get_trustwords) ),
    7.66 -		FP( "get_languagelist", new Func<PEP_STATUS, In<PEP_SESSION,false>, Out<char*>>( &get_languagelist) ),
    7.67 -//		FP( "get_phrase"      , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<Language>, In<int>, Out<char*>> ( &get_phrase) ),
    7.68 +//		FP( "log_event",  new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>, In<c_string>, In<c_string>>( &log_event) ),
    7.69 +		FP( "get_trustwords", new Func<PEP_STATUS, In_Pep_Session, In<const pEp_identity*>, In<const pEp_identity*>, In<Language>, Out<char*>, Out<size_t>, In<bool>>( &get_trustwords) ),
    7.70 +		FP( "get_languagelist", new Func<PEP_STATUS, In_Pep_Session, Out<char*>>( &get_languagelist) ),
    7.71 +//		FP( "get_phrase"      , new Func<PEP_STATUS, In_Pep_Session, In<Language>, In<int>, Out<char*>> ( &get_phrase) ),
    7.72  //		FP( "get_engine_version", new Func<const char*> ( &get_engine_version) ),
    7.73 -		FP( "is_pep_user"     , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, Out<bool>>( &is_pep_user) ),
    7.74 -		FP( "config_passive_mode", new Func<void, In<PEP_SESSION,false>, In<bool>>( &config_passive_mode) ),
    7.75 -		FP( "config_unencrypted_subject", new Func<void, In<PEP_SESSION,false>, In<bool>>( &config_unencrypted_subject) ),
    7.76 +		FP( "is_pep_user"     , new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, Out<bool>>( &is_pep_user) ),
    7.77 +		FP( "config_passive_mode", new Func<void, In_Pep_Session, In<bool>>( &config_passive_mode) ),
    7.78 +		FP( "config_unencrypted_subject", new Func<void, In_Pep_Session, In<bool>>( &config_unencrypted_subject) ),
    7.79  		
    7.80  		FP( "Identity Management API", new Separator),
    7.81 -		FP( "get_identity"       , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<c_string>, Out<pEp_identity*>>( &get_identity) ),
    7.82 -		FP( "set_identity"       , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>> ( &set_identity) ),
    7.83 -		FP( "mark_as_comprimized", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>> ( &mark_as_compromized) ),
    7.84 -		FP( "identity_rating"    , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),
    7.85 -		FP( "outgoing_message_rating", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<message*>, Out<PEP_rating>>( &outgoing_message_rating) ),
    7.86 -		FP( "set_identity_flags"     , new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>, In<identity_flags_t>>( &set_identity_flags) ),
    7.87 -		FP( "unset_identity_flags"   , new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>, In<identity_flags_t>>( &unset_identity_flags) ),
    7.88 +		FP( "get_identity"       , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>, Out<pEp_identity*>>( &get_identity) ),
    7.89 +		FP( "set_identity"       , new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>> ( &set_identity) ),
    7.90 +		FP( "mark_as_comprimized", new Func<PEP_STATUS, In_Pep_Session, In<c_string>> ( &mark_as_compromized) ),
    7.91 +		FP( "identity_rating"    , new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),
    7.92 +		FP( "outgoing_message_rating", new Func<PEP_STATUS, In_Pep_Session, In<message*>, Out<PEP_rating>>( &outgoing_message_rating) ),
    7.93 +		FP( "set_identity_flags"     , new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<identity_flags_t>>( &set_identity_flags) ),
    7.94 +		FP( "unset_identity_flags"   , new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<identity_flags_t>>( &unset_identity_flags) ),
    7.95  		
    7.96  		FP( "Low level Key Management API", new Separator),
    7.97 -		FP( "generate_keypair", new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &generate_keypair) ),
    7.98 -		FP( "delete_keypair", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>> ( &delete_keypair) ),
    7.99 -		FP( "import_key"    , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<std::size_t>, Out<identity_list*>> ( &import_key) ),
   7.100 -		FP( "export_key"    , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<char*>, Out<std::size_t>> ( &export_key) ),
   7.101 -		FP( "find_keys"     , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<stringlist_t*>> ( &find_keys) ),
   7.102 -		FP( "get_trust"     , new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &get_trust) ),
   7.103 -		FP( "own_key_is_listed", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<bool>> ( &own_key_is_listed) ),
   7.104 -		FP( "own_identities_retrieve", new Func<PEP_STATUS, In<PEP_SESSION,false>, Out<identity_list*>>( &own_identities_retrieve ) ),
   7.105 -		FP( "set_own_key", new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>, In<c_string>>( &set_own_key ) ),
   7.106 -		FP( "undo_last_mistrust", new Func<PEP_STATUS, In<PEP_SESSION,false>>( &undo_last_mistrust ) ),
   7.107 +		FP( "generate_keypair", new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &generate_keypair) ),
   7.108 +		FP( "delete_keypair", new Func<PEP_STATUS, In_Pep_Session, In<c_string>> ( &delete_keypair) ),
   7.109 +		FP( "import_key"    , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<std::size_t>, Out<identity_list*>> ( &import_key) ),
   7.110 +		FP( "export_key"    , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<char*>, Out<std::size_t>> ( &export_key) ),
   7.111 +		FP( "find_keys"     , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<stringlist_t*>> ( &find_keys) ),
   7.112 +		FP( "get_trust"     , new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &get_trust) ),
   7.113 +		FP( "own_key_is_listed", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<bool>> ( &own_key_is_listed) ),
   7.114 +		FP( "own_identities_retrieve", new Func<PEP_STATUS, In_Pep_Session, Out<identity_list*>>( &own_identities_retrieve ) ),
   7.115 +		FP( "set_own_key", new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<c_string>>( &set_own_key ) ),
   7.116 +		FP( "undo_last_mistrust", new Func<PEP_STATUS, In_Pep_Session>( &undo_last_mistrust ) ),
   7.117  		
   7.118 -		FP( "myself"        , new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &myself) ),
   7.119 -		FP( "update_identity", new Func<PEP_STATUS, In<PEP_SESSION,false>, InOut<pEp_identity*>> ( &update_identity) ),
   7.120 +		FP( "myself"        , new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &myself) ),
   7.121 +		FP( "update_identity", new Func<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &update_identity) ),
   7.122  		
   7.123 -		FP( "trust_personal_key", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>>( &trust_personal_key) ),
   7.124 -		FP( "key_mistrusted",     new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>>( &key_mistrusted) ),
   7.125 -		FP( "key_reset_trust",    new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>>( &key_reset_trust) ),
   7.126 +		FP( "trust_personal_key", new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &trust_personal_key) ),
   7.127 +		FP( "key_mistrusted",     new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &key_mistrusted) ),
   7.128 +		FP( "key_reset_trust",    new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &key_reset_trust) ),
   7.129  		
   7.130 -		FP( "least_trust"   , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<PEP_comm_type>> ( &least_trust) ),
   7.131 -		FP( "get_key_rating", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<PEP_comm_type>> ( &get_key_rating) ),
   7.132 -		FP( "renew_key"     , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<const timestamp*>> ( &renew_key) ),
   7.133 -		FP( "revoke"        , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<c_string>> ( &revoke_key) ),
   7.134 -		FP( "key_expired"   , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, In<time_t>, Out<bool>> ( &key_expired) ),
   7.135 +		FP( "least_trust"   , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<PEP_comm_type>> ( &least_trust) ),
   7.136 +		FP( "get_key_rating", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<PEP_comm_type>> ( &get_key_rating) ),
   7.137 +		FP( "renew_key"     , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<const timestamp*>> ( &renew_key) ),
   7.138 +		FP( "revoke"        , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>> ( &revoke_key) ),
   7.139 +		FP( "key_expired"   , new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<time_t>, Out<bool>> ( &key_expired) ),
   7.140  		
   7.141  		FP( "from blacklist.h & OpenPGP_compat.h", new Separator),
   7.142 -		FP( "blacklist_add"   , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>> ( &blacklist_add) ),
   7.143 -		FP( "blacklist_delete", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>> ( &blacklist_delete) ),
   7.144 -		FP( "blacklist_is_listed", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<bool>> ( &blacklist_is_listed) ),
   7.145 -		FP( "blacklist_retrieve" , new Func<PEP_STATUS, In<PEP_SESSION,false>, Out<stringlist_t*>> ( &blacklist_retrieve) ),
   7.146 -		FP( "OpenPGP_list_keyinfo", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<c_string>, Out<stringpair_list_t*>> ( &OpenPGP_list_keyinfo) ),
   7.147 +		FP( "blacklist_add"   , new Func<PEP_STATUS, In_Pep_Session, In<c_string>> ( &blacklist_add) ),
   7.148 +		FP( "blacklist_delete", new Func<PEP_STATUS, In_Pep_Session, In<c_string>> ( &blacklist_delete) ),
   7.149 +		FP( "blacklist_is_listed", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<bool>> ( &blacklist_is_listed) ),
   7.150 +		FP( "blacklist_retrieve" , new Func<PEP_STATUS, In_Pep_Session, Out<stringlist_t*>> ( &blacklist_retrieve) ),
   7.151 +		FP( "OpenPGP_list_keyinfo", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, Out<stringpair_list_t*>> ( &OpenPGP_list_keyinfo) ),
   7.152  		
   7.153  		FP( "Event Listener & Results", new Separator ),
   7.154 -		FP( "registerEventListener"  , new Func<void, In<JsonAdapter*, false>, In<std::string>, In<unsigned>, In<std::string>> ( &JsonAdapter::registerEventListener) ),
   7.155 -		FP( "unregisterEventListener", new Func<void, In<JsonAdapter*, false>, In<std::string>, In<unsigned>, In<std::string>> ( &JsonAdapter::unregisterEventListener) ),
   7.156 -		FP( "deliverHandshakeResult" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, In<sync_handshake_result>> (&deliverHandshakeResult) ),
   7.157 +		FP( "registerEventListener"  , new Func<void, In<JsonAdapter*,ParamFlag::NoInput>, In<std::string>, In<unsigned>, In<std::string>> ( &JsonAdapter::registerEventListener) ),
   7.158 +		FP( "unregisterEventListener", new Func<void, In<JsonAdapter*,ParamFlag::NoInput>, In<std::string>, In<unsigned>, In<std::string>> ( &JsonAdapter::unregisterEventListener) ),
   7.159 +		FP( "deliverHandshakeResult" , new Func<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, In<sync_handshake_result>> (&deliverHandshakeResult) ),
   7.160  		
   7.161  		// my own example function that does something useful. :-)
   7.162  		FP( "Other", new Separator ),
   7.163 @@ -139,9 +142,9 @@
   7.164  		FP( "version",           new Func<std::string>( &version_as_a_string ) ),
   7.165  		FP( "getGpgEnvironment", new Func<GpgEnvironment>( &getGpgEnvironment ) ),
   7.166  
   7.167 -		FP( "shutdown",  new Func<void, In<JsonAdapter*, false>>( &JsonAdapter::shutdown_now ) ),
   7.168 +		FP( "shutdown",  new Func<void, In<JsonAdapter*,ParamFlag::NoInput>>( &JsonAdapter::shutdown_now ) ),
   7.169  	};
   7.170 -
   7.171 + 
   7.172  
   7.173  	bool add_sharks = false;
   7.174  
     8.1 --- a/server/function_map.hh	Tue May 15 10:58:43 2018 +0200
     8.2 +++ b/server/function_map.hh	Thu Jun 07 13:54:10 2018 +0200
     8.3 @@ -14,6 +14,20 @@
     8.4  #include <pEp/message_api.h>
     8.5  
     8.6  
     8.7 +template<class R>
     8.8 +struct Return
     8.9 +{
    8.10 +	typedef R return_type;
    8.11 +	typedef Out<R> out_type;
    8.12 +};
    8.13 +
    8.14 +template<class R, ParamFlag PF>
    8.15 +struct Return< Out<R, PF> >
    8.16 +{
    8.17 +	typedef R return_type;
    8.18 +	typedef Out<R, PF> out_type;
    8.19 +};
    8.20 +
    8.21  // heloer class for generic calls:
    8.22  // R : return type of the called function
    8.23  // U : number of the parameter which is being extracted
    8.24 @@ -28,6 +42,8 @@
    8.25  class helper<R, U, U, Args...>
    8.26  {
    8.27  public:
    8.28 +	typedef typename Return<R>::return_type ReturnType;
    8.29 +	
    8.30  	enum { nr_of_output_params = 0 };
    8.31  	enum { nr_of_input_params = 0 };
    8.32  
    8.33 @@ -36,9 +52,9 @@
    8.34  		// do nothing. :-)
    8.35  	}
    8.36  
    8.37 -	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)
    8.38 +	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)
    8.39  	{
    8.40 -		Out<R> o{ fn(args.get_value()...) };
    8.41 +		typename Return<R>::out_type o{ fn(args.get_value()...) };
    8.42  		return to_json( o );
    8.43  	}
    8.44  };
    8.45 @@ -72,6 +88,7 @@
    8.46  class helper
    8.47  {
    8.48  public:
    8.49 +	typedef typename Return<R>::return_type ReturnType;
    8.50  	typedef std::tuple<Args...> Tuple;
    8.51  	typedef typename std::tuple_element<U, Tuple>::type Element; // The type of the U'th parameter
    8.52  	typedef helper<R, U+1, MAX, Args...> NextHelper;
    8.53 @@ -93,19 +110,19 @@
    8.54  	
    8.55  	// A2... a2 are the alredy pealed-off paremeters
    8.56  	template<class... A2>
    8.57 -	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)
    8.58 +	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)
    8.59  	{
    8.60  		// extract the U'th element of the parameter list
    8.61 -		const Element element(parameters[U], ctx);
    8.62 +		const Element element(parameters[U], ctx, U);
    8.63  		
    8.64  		const js::Value ret = NextHelper::call(fn, ctx, out_parameters, parameters, a2..., element );
    8.65  		if(Element::is_output)
    8.66  		{
    8.67  			js::Value out = element.to_json();
    8.68 -			std::cerr << "|$ Out #" << U << " : " << js::write(out) << "\n";
    8.69 +//			std::cerr << "|$ Out #" << U << " : " << js::write(out) << "\n";
    8.70  			out_parameters.push_back( std::move(out) );
    8.71  		}else{
    8.72 -			std::cerr << "|$ Param #" << U << " is not for output.\n";
    8.73 +//			std::cerr << "|$ Param #" << U << " is not for output.\n";
    8.74  		}
    8.75  		return ret;
    8.76  	}
    8.77 @@ -127,6 +144,8 @@
    8.78  class Func : public FuncBase
    8.79  {
    8.80  public:
    8.81 +	typedef typename Return<R>::return_type ReturnType;
    8.82 +	
    8.83  	virtual ~Func() = default;
    8.84  	virtual bool isSeparator() const override
    8.85  	{
    8.86 @@ -135,11 +154,11 @@
    8.87  	
    8.88  	Func() : fn() {}
    8.89  
    8.90 -	Func( const std::function<R(typename Args::c_type ...)>& _f )
    8.91 +	Func( const std::function<ReturnType(typename Args::c_type ...)>& _f )
    8.92  	: fn(_f)
    8.93  	{}
    8.94  
    8.95 -	std::function<R(typename Args::c_type ...)> fn;
    8.96 +	std::function<ReturnType(typename Args::c_type ...)> fn;
    8.97  
    8.98  	js::Value call(const js::Array& parameters, Context* context) const override
    8.99  	{
   8.100 @@ -174,10 +193,9 @@
   8.101  		rs.emplace_back("outParams", std::move(out_params));
   8.102  		rs.emplace_back("return", std::move(ret));
   8.103  		
   8.104 -		if(context)
   8.105 -		{
   8.106 -			context->augment(rs);  // used e.g. add some debug infos to the status return value
   8.107 -		}
   8.108 +		context->augment(rs);  // used e.g. add some debug infos to the status return value
   8.109 +		context->clear(); // clear all stored values, if any.
   8.110 +		
   8.111  		return rs;
   8.112  	}
   8.113  
     9.1 --- a/server/inout.hh	Tue May 15 10:58:43 2018 +0200
     9.2 +++ b/server/inout.hh	Thu Jun 07 13:54:10 2018 +0200
     9.3 @@ -10,11 +10,37 @@
     9.4  // Just for debugging:
     9.5  #include <iostream>
     9.6  
     9.7 +// is a bitfield that controls the In<> / Out<> parameter types
     9.8 +enum class ParamFlag : unsigned
     9.9 +{
    9.10 +	Default = 0,
    9.11 +	NoInput = 1,        // has no input parameter in the JSON API. Value comes from Context.
    9.12 +	DontOwn = 2,        // parameter holds a "shared resource". Don't free() it in destructor.
    9.13 +};
    9.14 +
    9.15 +inline constexpr
    9.16 +ParamFlag operator|(ParamFlag a, ParamFlag b)
    9.17 +{
    9.18 +	return ParamFlag( unsigned(a) | unsigned(b) );
    9.19 +}
    9.20 +
    9.21 +inline constexpr
    9.22 +ParamFlag operator&(ParamFlag a, ParamFlag b)
    9.23 +{
    9.24 +	return ParamFlag( unsigned(a) & unsigned(b) );
    9.25 +}
    9.26 +
    9.27 +inline constexpr
    9.28 +bool operator!(ParamFlag pf)
    9.29 +{
    9.30 +	return !unsigned(pf);
    9.31 +}
    9.32 +
    9.33  
    9.34  namespace js = json_spirit;
    9.35  
    9.36 -template<class T, bool NeedInput> struct In;
    9.37 -template<class T, bool NeedInput> struct Out;
    9.38 +template<class T, ParamFlag PF> struct In;
    9.39 +template<class T, ParamFlag PF> struct Out;
    9.40  
    9.41  // "params" and "position" might be used to fetch additional parameters from the array.
    9.42  template<class T>
    9.43 @@ -26,21 +52,21 @@
    9.44  
    9.45  
    9.46  // helper classes to specify in- and out-parameters
    9.47 -template<class T, bool NeedInput=true>
    9.48 +template<class T, ParamFlag PF=ParamFlag::Default>
    9.49  struct In
    9.50  {
    9.51  	typedef T c_type; // the according type in C function parameter
    9.52 -	enum { is_output = false, need_input = NeedInput };
    9.53 +	enum { is_output = false, need_input = !(PF & ParamFlag::NoInput) };
    9.54  	
    9.55  	explicit In(const T& t) : value(t) {}
    9.56  	~In();
    9.57  	
    9.58 -	In(const In<T,NeedInput>& other) = delete;
    9.59 -	In(In<T,NeedInput>&& victim) = delete;
    9.60 -	In<T,NeedInput>& operator=(const In<T,NeedInput>&) = delete;
    9.61 +	In(const In<T,PF>& other) = delete;
    9.62 +	In(In<T,PF>&& victim) = delete;
    9.63 +	In<T,PF>& operator=(const In<T,PF>&) = delete;
    9.64  	
    9.65  	// default implementation:
    9.66 -	In(const js::Value& v, Context*)
    9.67 +	In(const js::Value& v, Context*, unsigned param_nr)
    9.68  	: In( from_json<T>(v) )
    9.69  	{ }
    9.70  	
    9.71 @@ -56,21 +82,21 @@
    9.72  
    9.73  
    9.74  // to call functions that operate directly on the JSON data type
    9.75 -template<class T, bool NeedInput=true>
    9.76 +template<class T, ParamFlag PF=ParamFlag::Default>
    9.77  struct InRaw
    9.78  {
    9.79  	typedef js::Value c_type; // do not unwrap JSON data type
    9.80 -	enum { is_output = false, need_input = NeedInput };
    9.81 +	enum { is_output = false, need_input = !(PF & ParamFlag::NoInput) };
    9.82  	
    9.83  	explicit InRaw(const js::Value& t) : value(t) {}
    9.84  	~InRaw() = default;
    9.85  	
    9.86 -	InRaw(const InRaw<T,NeedInput>& other) = delete;
    9.87 -	InRaw(InRaw<T,NeedInput>&& victim) = delete;
    9.88 -	InRaw<T,NeedInput>& operator=(const InRaw<T,NeedInput>&) = delete;
    9.89 +	InRaw(const InRaw<T,PF>& other) = delete;
    9.90 +	InRaw(InRaw<T,PF>&& victim) = delete;
    9.91 +	InRaw<T,PF>& operator=(const InRaw<T,PF>&) = delete;
    9.92  	
    9.93  	// default implementation:
    9.94 -	InRaw(const js::Value& v, Context*)
    9.95 +	InRaw(const js::Value& v, Context*, unsigned param_nr)
    9.96  	: InRaw(v)
    9.97  	{ }
    9.98  
    9.99 @@ -87,19 +113,19 @@
   9.100  
   9.101  // helper classes to specify in- and out-parameters whose output is in-place.
   9.102  // Use InOutP<T> for in/out parameters where the function might change the object and expects a pointer
   9.103 -template<class T, bool NeedInput=true>
   9.104 -struct InOut : public In<T,NeedInput>
   9.105 +template<class T, ParamFlag PF=ParamFlag::Default>
   9.106 +struct InOut : public In<T,PF>
   9.107  {
   9.108 -	typedef In<T,NeedInput> Base;
   9.109 -	enum { is_output = true, need_input = NeedInput };
   9.110 +	typedef In<T,PF> Base;
   9.111 +	enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) };
   9.112  
   9.113  	explicit InOut(const T& t) : Base(t) {}
   9.114  	~InOut() = default;
   9.115  	
   9.116 -	InOut<T,NeedInput>& operator=(const InOut<T,NeedInput>&) = delete;
   9.117 +	InOut<T,PF>& operator=(const InOut<T,PF>&) = delete;
   9.118  	
   9.119  	// default implementation:
   9.120 -	InOut(const js::Value& v, Context*)
   9.121 +	InOut(const js::Value& v, Context*, unsigned param_nr)
   9.122  	: Base( from_json<T>(v) )
   9.123  	{ }
   9.124  	
   9.125 @@ -110,11 +136,11 @@
   9.126  };
   9.127  
   9.128  
   9.129 -template<class T, bool NeedInput = true>
   9.130 +template<class T, ParamFlag PF = ParamFlag::Default>
   9.131  struct Out
   9.132  {
   9.133  	typedef T* c_type; // the according type in C function parameter
   9.134 -	enum { is_output = true, need_input = NeedInput }; // if need_input=false it would no longer consume an element in the input parameter array.
   9.135 +	enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) }; // if need_input=false it would no longer consume an element in the input parameter array.
   9.136  	
   9.137  	explicit Out() : value{}
   9.138  	{ }
   9.139 @@ -124,14 +150,15 @@
   9.140  
   9.141  	~Out();
   9.142  	
   9.143 -	Out(const Out<T,NeedInput>& other) = delete;
   9.144 -	Out(Out<T,NeedInput>&& victim) = delete;
   9.145 +	Out(const Out<T,PF>& other) = delete;
   9.146 +	Out(Out<T,PF>&& victim) = delete;
   9.147  	
   9.148  	// just to be sure they are not implicitly defined:
   9.149 -	Out<T,NeedInput>& operator=(const Out<T,NeedInput>& other) = delete;
   9.150 -	Out<T,NeedInput>& operator=(Out<T,NeedInput>&& victim) = delete;
   9.151 +	Out<T,PF>& operator=(const Out<T,PF>& other) = delete;
   9.152 +	Out<T,PF>& operator=(Out<T,PF>&& victim) = delete;
   9.153  	
   9.154 -	Out(const js::Value& v, Context*)
   9.155 +	// dummy Value v is ignored for output parameters
   9.156 +	Out(const js::Value& v, Context*, unsigned param_nr)
   9.157  	: Out()
   9.158  	{ }
   9.159  	
   9.160 @@ -146,7 +173,7 @@
   9.161  	
   9.162  	/*
   9.163  	friend
   9.164 -	std::ostream& operator<<(std::ostream& o, const Out<T,NeedInput>& out)
   9.165 +	std::ostream& operator<<(std::ostream& o, const Out<T,PF>& out)
   9.166  	{
   9.167  		o << (const void*)out;
   9.168  		
   9.169 @@ -159,31 +186,31 @@
   9.170  
   9.171  // helper classes to specify in- and out-parameters whose output might change by the called function.
   9.172  // Use InOut<T> for in/out parameters where the function only makes in-place changes.
   9.173 -template<class T, bool NeedInput=true>
   9.174 -struct InOutP : public Out<T,NeedInput>
   9.175 +template<class T, ParamFlag PF=ParamFlag::Default>
   9.176 +struct InOutP : public Out<T,PF>
   9.177  {
   9.178 -	typedef Out<T,NeedInput> Base;
   9.179 -	enum { is_output = true, need_input = NeedInput };
   9.180 +	typedef Out<T,PF> Base;
   9.181 +	enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) };
   9.182  
   9.183  	explicit InOutP(const T& t) : Base(t) {}
   9.184  	
   9.185 -	InOutP<T,NeedInput>& operator=(const InOutP<T,NeedInput>&) = delete;
   9.186 +	InOutP<T,PF>& operator=(const InOutP<T,PF>&) = delete;
   9.187  	
   9.188  	// default implementation:
   9.189 -	InOutP(const js::Value& v, Context*)
   9.190 +	InOutP(const js::Value& v, Context*, unsigned param_nr)
   9.191  	: Base( from_json<T>(v) )
   9.192  	{ }
   9.193  };
   9.194  
   9.195  
   9.196 -template<class T, bool NeedInput>
   9.197 -js::Value to_json(const Out<T,NeedInput>& o)
   9.198 +template<class T, ParamFlag PF>
   9.199 +js::Value to_json(const Out<T,PF>& o)
   9.200  {
   9.201  	return ::to_json(o.value);
   9.202  }
   9.203  
   9.204 -template<class T, bool NeedInput>
   9.205 -js::Value to_json(const InOut<T,NeedInput>& o)
   9.206 +template<class T, ParamFlag PF>
   9.207 +js::Value to_json(const InOut<T,PF>& o)
   9.208  {
   9.209  	return ::to_json(o.value);
   9.210  }
   9.211 @@ -198,40 +225,33 @@
   9.212  };
   9.213  
   9.214  
   9.215 -template<class T>
   9.216 -struct Type2String<In<T, true>>
   9.217 +template<class T, ParamFlag PF>
   9.218 +struct Type2String<In<T, PF>>
   9.219  {
   9.220  	static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
   9.221  };
   9.222  
   9.223 -template<class T>
   9.224 -struct Type2String<In<T, false>>
   9.225 -{
   9.226 -	static js::Value get() { throw "MSVC is b0rken"; }
   9.227 -};
   9.228 -
   9.229 -
   9.230  
   9.231  template<class T>
   9.232 -struct Type2String<InRaw<T, true>>
   9.233 +struct Type2String<InRaw<T, ParamFlag::Default>>
   9.234  {
   9.235  	static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
   9.236  };
   9.237  
   9.238 -template<class T>
   9.239 -struct Type2String<Out<T, true>>
   9.240 +template<class T, ParamFlag PF>
   9.241 +struct Type2String<Out<T, PF>>
   9.242  {
   9.243  	static js::Value get() { js::Object ret; ret.emplace_back("direction", "Out"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
   9.244  };
   9.245  
   9.246 -template<class T>
   9.247 -struct Type2String<InOut<T, true>>
   9.248 +template<class T, ParamFlag PF>
   9.249 +struct Type2String<InOut<T, PF>>
   9.250  {
   9.251  	static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
   9.252  };
   9.253  
   9.254 -template<class T>
   9.255 -struct Type2String<InOutP<T, true>>
   9.256 +template<class T, ParamFlag PF>
   9.257 +struct Type2String<InOutP<T, PF>>
   9.258  {
   9.259  	static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
   9.260  };
    10.1 --- a/server/json-adapter.cc	Tue May 15 10:58:43 2018 +0200
    10.2 +++ b/server/json-adapter.cc	Thu Jun 07 13:54:10 2018 +0200
    10.3 @@ -317,7 +317,7 @@
    10.4  
    10.5  
    10.6  template<>
    10.7 -In<JsonAdapter*, false>::~In()
    10.8 +In<JsonAdapter*, ParamFlag::NoInput>::~In()
    10.9  {
   10.10  	// nothing to do here. :-D
   10.11  }
    11.1 --- a/server/json_rpc.cc	Tue May 15 10:58:43 2018 +0200
    11.2 +++ b/server/json_rpc.cc	Thu Jun 07 13:54:10 2018 +0200
    11.3 @@ -62,7 +62,7 @@
    11.4  using json_spirit::find_value;
    11.5  
    11.6  
    11.7 -js::Object call(const FunctionMap& fm, const js::Object& request, Context* context, bool check_security_token)
    11.8 +js::Object call(const FunctionMap& fm, const js::Object& request, Context* context)
    11.9  {
   11.10  	int request_id = -1;
   11.11  	try
   11.12 @@ -74,7 +74,8 @@
   11.13  		}
   11.14  		
   11.15  		const auto sec_token = find_value(request, "security_token");
   11.16 -		if(check_security_token && (sec_token.type()!=js::str_type || context->verify_security_token(sec_token.get_str())==false) )
   11.17 +		const std::string sec_token_s = (sec_token.type()==js::str_type ? sec_token.get_str() : std::string() ); // missing or non-string "security_token" --> empty string.
   11.18 +		if( context->verify_security_token(sec_token_s)==false )
   11.19  		{
   11.20  			return make_error(JSON_RPC::INVALID_REQUEST, "Invalid request: Wrong security token.", request, request_id);
   11.21  		}
   11.22 @@ -112,13 +113,13 @@
   11.23  		
   11.24  		const js::Array p = ( params.type()==js::array_type ? params.get_array() : js::Array{} );
   11.25  		
   11.26 -		std::cerr << "=== Now I do the call!\n"
   11.27 -			"\tmethod_name=\"" << method_name << "\","
   11.28 -			"\tparams=" << js::write(params) << ". ===\n";
   11.29 +//		std::cerr << "=== Now I do the call!\n"
   11.30 +//			"\tmethod_name=\"" << method_name << "\","
   11.31 +//			"\tparams=" << js::write(params) << ". ===\n";
   11.32  		
   11.33  		const js::Value result = fn->second->call(p, context);
   11.34 -		std::cerr << "=== Result of call: " << js::write(result, js::raw_utf8) << ". ===\n";
   11.35 -		std::cerr << "\tSessions: " << getSessions() << "\n";
   11.36 +//		std::cerr << "=== Result of call: " << js::write(result, js::raw_utf8) << ". ===\n";
   11.37 +//		std::cerr << "\tSessions: " << getSessions() << "\n";
   11.38  		
   11.39  		return make_result(result, request_id);
   11.40  	}
    12.1 --- a/server/json_rpc.hh	Tue May 15 10:58:43 2018 +0200
    12.2 +++ b/server/json_rpc.hh	Thu Jun 07 13:54:10 2018 +0200
    12.3 @@ -21,7 +21,7 @@
    12.4  
    12.5  // parse the JSON-RPC 2.0 compatible "request", call the C function
    12.6  // and create an appropiate "response" object (containing a result or an error)
    12.7 -js::Object call(const FunctionMap& fm, const js::Object& request, Context* context, bool check_security_token = true);
    12.8 +js::Object call(const FunctionMap& fm, const js::Object& request, Context* context);
    12.9  
   12.10  // create a JSON-RPC 2.0 compatible result response object
   12.11  //js::Object make_result(const js::Value& result, int id);
    13.1 --- a/server/json_spirit/json_spirit_reader.cpp	Tue May 15 10:58:43 2018 +0200
    13.2 +++ b/server/json_spirit/json_spirit_reader.cpp	Thu Jun 07 13:54:10 2018 +0200
    13.3 @@ -48,17 +48,26 @@
    13.4              return std::string( 1, char(u) );
    13.5          }else if( u<= 0x7FF )
    13.6          {
    13.7 -            char buf[2] = { char( 0xC0 + (u>>6) ), char( 0x80 + (u & 63) ) };
    13.8 +            char buf[2] = { char( 0xC0 + (u>>6) ),
    13.9 +                            char( 0x80 + (u & 63) )
   13.10 +                          };
   13.11              return std::string( buf, buf+2 );
   13.12          }else if( u<= 0xFFFF )
   13.13          {
   13.14 -            char buf[3] = { char( 0xE0 + (u>>12) ), char( 0x80 + ((u>>6) & 63) ), char( 0x80 + (u & 63) ) };
   13.15 +            char buf[3] = { char( 0xE0 + ( u>>12     ) ),
   13.16 +                            char( 0x80 + ((u>>6) & 63) ),
   13.17 +                            char( 0x80 + ( u     & 63) )
   13.18 +                          };
   13.19              return std::string( buf, buf+3 );
   13.20  
   13.21          }else if( u<= 0x10FFFF )
   13.22          {
   13.23 -            char buf[4] = { char( 0xF0 + (u>>18) ), char( 0x80 + ((u>>12) & 63) ), char( 0x80 + ((u>>6) & 63) ), char( 0x80 + (u & 63) ) };
   13.24 -            return std::string( buf, buf+3 );
   13.25 +            char buf[4] = { char( 0xF0 + ( u>>18      ) ),
   13.26 +                            char( 0x80 + ((u>>12) & 63) ),
   13.27 +                            char( 0x80 + ((u>> 6) & 63) ),
   13.28 +                            char( 0x80 + ( u      & 63) )
   13.29 +                          };
   13.30 +            return std::string( buf, buf+4 );
   13.31          }
   13.32          
   13.33          throw std::runtime_error( "Unicode codepoint " + std::to_string(u) + " is too big!");
    14.1 --- a/server/json_spirit/json_spirit_reader_template.h	Tue May 15 10:58:43 2018 +0200
    14.2 +++ b/server/json_spirit/json_spirit_reader_template.h	Thu Jun 07 13:54:10 2018 +0200
    14.3 @@ -59,7 +59,7 @@
    14.4          if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0';
    14.5          if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10;
    14.6          if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10;
    14.7 -        return 0;
    14.8 +        throw std::runtime_error(std::string("Char \"") + char(c) + "\" is not a hex digit!");
    14.9      }
   14.10  
   14.11      template< class Char_type, class Iter_type >
   14.12 @@ -133,11 +133,41 @@
   14.13                      {
   14.14                          s += Char_type(c);
   14.15                      }else{
   14.16 -                        s += encode_utf<String_type>(c);
   14.17 +                        if(c>=0xD800 && c<=0xDBFF) // high surrogate from UTF-16 pair
   14.18 +                        {
   14.19 +                            const unsigned high_surrogate = c;
   14.20 +                            if(end-begin<7)
   14.21 +                                throw std::runtime_error("Missing low surrogate at end of string. E0");
   14.22 +                            
   14.23 +                            if(*++begin != '\\')
   14.24 +                                throw std::runtime_error("Missing low surrogate at end of string. E1");
   14.25 +                            
   14.26 +                            if(*++begin != 'u')
   14.27 +                                throw std::runtime_error("Missing low surrogate at end of string. E2");
   14.28 +                            
   14.29 +                            const unsigned low_surrogate = unicode_str_to_char< Char_type >( begin );
   14.30 +                            if( (low_surrogate < 0xDC00) || (low_surrogate > 0xDFFF) )
   14.31 +                                throw std::runtime_error("Missing low surrogate at end of string. E3");
   14.32 +                            
   14.33 +                            // combine the two escaped \u sequences into one Non-BMP character:
   14.34 +                            const unsigned u32 = (high_surrogate-0xD800) * 1024 + (low_surrogate-0xDC00) + 0x10000;
   14.35 +                            s += encode_utf<String_type>(u32);
   14.36 +                        }else if(c>=0xDC00 && c<=0xDFFF)
   14.37 +                        {
   14.38 +                            throw std::runtime_error("Unexpected low surrogate.");
   14.39 +                        }else{
   14.40 +                            s += encode_utf<String_type>(c); // normal \u escaped BMP character.
   14.41 +                        }
   14.42                      }
   14.43 +                }else{
   14.44 +                    throw std::runtime_error("After \\u I expect at least 4 hex digits.");
   14.45                  }
   14.46                  break;
   14.47              }
   14.48 +            default :
   14.49 +            {
   14.50 +                throw std::runtime_error(std::string("Unknown char \"") + char(c2) + "\" after backslash.");
   14.51 +            }
   14.52          }
   14.53      }
   14.54  
   14.55 @@ -147,14 +177,12 @@
   14.56      {
   14.57          typedef typename String_type::const_iterator Iter_type;
   14.58  
   14.59 -        if( end - begin < 2 ) return String_type( begin, end );
   14.60 +//        if( end - begin < 2 ) return String_type( begin, end );
   14.61  
   14.62          String_type result;
   14.63 -        
   14.64          result.reserve( end - begin );
   14.65  
   14.66          const Iter_type end_minus_1( end - 1 );
   14.67 -
   14.68          Iter_type substr_start = begin;
   14.69          Iter_type i = begin;
   14.70  
   14.71 @@ -163,17 +191,17 @@
   14.72              if( *i == '\\' )
   14.73              {
   14.74                  result.append( substr_start, i );
   14.75 -
   14.76                  ++i;  // skip the '\'
   14.77 -
   14.78 +                if(i == end)
   14.79 +                {
   14.80 +                    throw std::runtime_error("Backslash at the end is not allowed.");
   14.81 +                }
   14.82                  append_esc_char_and_incr_iter( result, i, end );
   14.83 -
   14.84                  substr_start = i + 1;
   14.85              }
   14.86          }
   14.87  
   14.88          result.append( substr_start, end );
   14.89 -
   14.90          return result;
   14.91      }
   14.92  
    15.1 --- a/server/pep-types.cc	Tue May 15 10:58:43 2018 +0200
    15.2 +++ b/server/pep-types.cc	Thu Jun 07 13:54:10 2018 +0200
    15.3 @@ -156,7 +156,7 @@
    15.4  
    15.5  
    15.6  template<>
    15.7 -In<PEP_SESSION, false>::~In()
    15.8 +In<PEP_SESSION, ParamFlag::NoInput>::~In()
    15.9  {
   15.10  	// no automatic release!
   15.11  }
   15.12 @@ -176,11 +176,16 @@
   15.13  }
   15.14  
   15.15  template<>
   15.16 -In<stringlist_t*>::~In()
   15.17 +In<stringlist_t*, ParamFlag::Default>::~In()
   15.18  {
   15.19  	free_stringlist(value);
   15.20  }
   15.21  
   15.22 +template<>
   15.23 +In<stringlist_t*, ParamFlag::DontOwn>::~In()
   15.24 +{
   15.25 +}
   15.26 +
   15.27  
   15.28  template<>
   15.29  In<pEp_identity*>::~In()
   15.30 @@ -237,12 +242,18 @@
   15.31  
   15.32  
   15.33  template<>
   15.34 -Out<stringlist_t*>::~Out()
   15.35 +Out<stringlist_t*, ParamFlag::Default>::~Out()
   15.36  {
   15.37  	free_stringlist(value);
   15.38  }
   15.39  
   15.40  template<>
   15.41 +Out<stringlist_t*, ParamFlag::DontOwn>::~Out()
   15.42 +{
   15.43 +	// don't call free_stringlist()!
   15.44 +}
   15.45 +
   15.46 +template<>
   15.47  Out<stringpair_list_t*>::~Out()
   15.48  {
   15.49  	free_stringpair_list(value);
    16.1 --- a/server/pep-types.hh	Tue May 15 10:58:43 2018 +0200
    16.2 +++ b/server/pep-types.hh	Thu Jun 07 13:54:10 2018 +0200
    16.3 @@ -26,7 +26,7 @@
    16.4  	void operator=(const In<Language>&) = delete;
    16.5  	
    16.6  	// default implementation:
    16.7 -	In<Language>(const js::Value& v, Context*)
    16.8 +	In<Language>(const js::Value& v, Context*, unsigned)
    16.9  	{
   16.10  		const std::string vs = v.get_str();
   16.11  		if(vs.size() != 2)
    17.1 --- a/server/server_version.cc	Tue May 15 10:58:43 2018 +0200
    17.2 +++ b/server/server_version.cc	Thu Jun 07 13:54:10 2018 +0200
    17.3 @@ -54,7 +54,8 @@
    17.4  //	"(35) Bad Berleburg";    // fix the fork() problem on MacOS. daemonize() now got a function parameter. \o/
    17.5  //	"(36) Hatzfeld";         // JSON-81: add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
    17.6  //	"(37) Battenberg";       // JSON-75: change debonize() behavior, especially on MS Windows
    17.7 -	"(38) Frankenberg";      // JSON-92: API CHANGE: decrypt_message() has InOut src message, MIME_decrypt_message() returns changed src msg, too.
    17.8 +//	"(38) Frankenberg";      // JSON-92: API CHANGE: decrypt_message() has InOut src message, MIME_decrypt_message() returns changed src msg, too.
    17.9 +	"(39) Gemünden";         // JSON-93: support for InLengt<> to calculate string lengths automatically.
   17.10  
   17.11  //const ServerVersion sv{0, 10, 0, version_name};  // first version defined.
   17.12  //const ServerVersion sv{0, 11, 0, version_name};  // add set_own_key()
   17.13 @@ -65,7 +66,8 @@
   17.14  //const ServerVersion sv(0,13,1);  // JSON-91: add MIME_encrypt_message_for_self() and encrypt_message_for_self()
   17.15  //const ServerVersion sv(0,14,0);  // JSON-75: incompatible behavior of daemonize() especially in MS Windows
   17.16  //const ServerVersion sv(0,15,0);  // JSON-92: API CHANGE.
   17.17 -const ServerVersion sv(0,15,1);  // JSON-92 again: Change "keylist" in (MIME_)decrypt_message() from Out to InOutP. Is a compatible API change for JSON/JavaScript due to the handling of output parameters. :-)
   17.18 +//const ServerVersion sv(0,15,1);  // JSON-92 again: Change "keylist" in (MIME_)decrypt_message() from Out to InOutP. Is a compatible API change for JSON/JavaScript due to the handling of output parameters. :-)
   17.19 +const ServerVersion sv(0,15,2);  // JSON-93 InLength<> is a compatible API change, because length parameter is still there but ignored. :-)
   17.20  
   17.21  } // end of anonymous namespace
   17.22  ////////////////////////////////////////////////////////////////////////////
    18.1 --- a/server/unittest_json.cc	Tue May 15 10:58:43 2018 +0200
    18.2 +++ b/server/unittest_json.cc	Thu Jun 07 13:54:10 2018 +0200
    18.3 @@ -3,10 +3,21 @@
    18.4  #include "inout.hh" // for to_json() and from_json()
    18.5  #include "nfc.hh"   // for illegal_utf8 exception
    18.6  #include "json_spirit/json_spirit_writer.h"
    18.7 +#include "json_spirit/json_spirit_reader.h"
    18.8  #include <vector>
    18.9  
   18.10  namespace js = json_spirit;
   18.11  
   18.12 +namespace {
   18.13 +
   18.14 +// for JSON input
   18.15 +struct TestPair
   18.16 +{
   18.17 +	std::string input;
   18.18 +	std::string output;
   18.19 +};
   18.20 +
   18.21 +// for JSON output
   18.22  struct TestTriple
   18.23  {
   18.24  	std::string input;
   18.25 @@ -15,6 +26,11 @@
   18.26  };
   18.27  
   18.28  
   18.29 +std::ostream& operator<<(std::ostream& o, const TestPair& tp)
   18.30 +{
   18.31 +	return o << "input=«" << tp.input << "», output=«" << tp.output << "». ";
   18.32 +}
   18.33 +
   18.34  std::ostream& operator<<(std::ostream& o, const TestTriple& tt)
   18.35  {
   18.36  	return o << "input=«" << tt.input << "», esc=«" << tt.output_esc << "», raw=«" << tt.output_raw << "». ";
   18.37 @@ -24,7 +40,32 @@
   18.38  const char nullo[4] = {0,0,0,0};
   18.39  const char null_x[4] = { '\0', 'X', '\0', '\n' };
   18.40  
   18.41 -const std::vector<TestTriple> testValues =
   18.42 +const std::vector<TestPair> testValuesInput =
   18.43 +	{
   18.44 +		{ R"("")"                   , ""       },  // always start with the simple case ;-)
   18.45 +		{ R"("123")"                , "123"    },  // some ASCII digits. Still easy.
   18.46 +		{ R"("\n\\\b")"             , "\n\\\b" },  // backslash escapes for ASCII and control chars
   18.47 +		{ R"("\u001F")"             , "\x1f"   },  // C compiler knows \x##, but JSON does not
   18.48 +		{ R"("\u007F")"             , "\x7f"   },  // C compiler knows \x##, but JSON does not
   18.49 +		
   18.50 +		{ R"("\u00E4\u00F6\u00FC")" , "äöü"    },  // German umlauts from Unicode block "Latin-1 Supplement"
   18.51 +		{ R"("äöü")"                , "äöü"    },  // German umlauts from Unicode block "Latin-1 Supplement"
   18.52 +		
   18.53 +		{ R"("\u041C\u043E\u0441\u043A\u0432\u0430")" , "Москва" },  // some Cyrillic
   18.54 +		{ R"("Москва")"                               , "Москва" },  // some Cyrillic
   18.55 +		
   18.56 +		{ R"("\uD83D\uDCA3")"   , "\xF0\x9f\x92\xA3"  }, // Unicode Bomb <U+1F4A3>, an example for char outside of BMP
   18.57 +		{ "\"\xF0\x9f\x92\xA3\"", "\xF0\x9f\x92\xA3"  }, // Unicode Bomb <U+1F4A3>, an example for char outside of BMP
   18.58 +		
   18.59 +		{ R"("\u0000")"         , std::string(nullo, nullo+1)   },  // Yeah, 1 NUL byte
   18.60 +		{ R"("\u0000\u0000")"   , std::string(nullo, nullo+2)   },  // Yeah, 2 NUL bytes
   18.61 +		{ R"("\u0000X\u0000\n")", std::string(null_x, null_x+4) },  // guess what...
   18.62 +		
   18.63 +		{ "\"EOF\"", "EOF" }
   18.64 +	};
   18.65 +
   18.66 +
   18.67 +const std::vector<TestTriple> testValuesOutput =
   18.68  	{
   18.69  		{ ""      , R"("")"                   , R"("")"        },  // always start with the simple case ;-)
   18.70  		{ "123"   , R"("123")"                , R"("123")"     },  // some ASCII digits. Still easy.
   18.71 @@ -49,13 +90,31 @@
   18.72  		{ "EOF", "\"EOF\"", "\"EOF\"" }
   18.73  	};
   18.74  
   18.75 +} // end of anonymous namespace
   18.76 +
   18.77 +class FromJsonTest : public ::testing::TestWithParam<TestPair>
   18.78 +{
   18.79 +	// intentionally left blank for now.
   18.80 +};
   18.81 +
   18.82 +INSTANTIATE_TEST_CASE_P(FromJsonTestInstance, FromJsonTest, testing::ValuesIn(testValuesInput) );
   18.83 +
   18.84  
   18.85  class ToJsonTest : public ::testing::TestWithParam<TestTriple>
   18.86  {
   18.87  	// intentionally left blank for now.
   18.88  };
   18.89  
   18.90 -INSTANTIATE_TEST_CASE_P(ToJsonTestInstance, ToJsonTest, testing::ValuesIn(testValues) );
   18.91 +INSTANTIATE_TEST_CASE_P(ToJsonTestInstance, ToJsonTest, testing::ValuesIn(testValuesOutput) );
   18.92 +
   18.93 +
   18.94 +TEST_P( FromJsonTest, Meh )
   18.95 +{
   18.96 +	const auto param = GetParam();
   18.97 +	js::Value v;
   18.98 +	js::read_or_throw( param.input, v);
   18.99 +	EXPECT_EQ( param.output,  from_json<std::string>(v) );
  18.100 +}
  18.101  
  18.102  TEST_P( ToJsonTest, Meh )
  18.103  {
  18.104 @@ -82,3 +141,33 @@
  18.105  	EXPECT_THROW( to_json<std::string>( "\xF4\x90\x80\x80" ), illegal_utf8 ); // bigger than U+10FFFF
  18.106  	EXPECT_THROW( to_json<std::string>( "\xED\xA0\x81\xED\xB0\x90" ), illegal_utf8 ); // CESU-8. Correct UTF-8 whoild be F0 90 90 80.
  18.107  }
  18.108 +
  18.109 +
  18.110 +TEST( FromJsonTest, IllegalSequences )
  18.111 +{
  18.112 +	js::Value v;
  18.113 +	
  18.114 +	// too short \u escape sequences
  18.115 +	EXPECT_ANY_THROW( js::read_or_throw( R"("\")", v) );
  18.116 +	EXPECT_THROW( js::read_or_throw( R"("\q")", v), std::runtime_error );
  18.117 +	EXPECT_THROW( js::read_or_throw( R"("\u")", v), std::runtime_error );
  18.118 +	EXPECT_THROW( js::read_or_throw( R"("\u1")", v), std::runtime_error );
  18.119 +	EXPECT_THROW( js::read_or_throw( R"("\u12")", v), std::runtime_error );
  18.120 +	EXPECT_THROW( js::read_or_throw( R"("\u123")", v), std::runtime_error );
  18.121 +	
  18.122 +	// high surrogate without following legal low surrogate:
  18.123 +	EXPECT_THROW( js::read_or_throw( R"("\uD801")", v), std::runtime_error );
  18.124 +	EXPECT_THROW( js::read_or_throw( R"("\uD801D")", v), std::runtime_error );
  18.125 +	
  18.126 +	EXPECT_ANY_THROW( js::read_or_throw( R"("\uD801\")", v) );
  18.127 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\u")", v), std::runtime_error );
  18.128 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\uD")", v), std::runtime_error );
  18.129 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\uDC")", v), std::runtime_error );
  18.130 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\uDC0")", v), std::runtime_error );
  18.131 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\u1234")", v), std::runtime_error );
  18.132 +
  18.133 +	EXPECT_NO_THROW( js::read_or_throw( R"("\uD801\uDC02")", v) ); // legal UTF-16 sequence
  18.134 +	
  18.135 +	EXPECT_THROW( js::read_or_throw( R"("\uD801\uD801")", v), std::runtime_error ); // two high surrogates
  18.136 +	EXPECT_THROW( js::read_or_throw( R"("\uDC01\uDC01")", v), std::runtime_error ); // low surrogate without high surrogate before
  18.137 +}
    19.1 --- a/server/unittest_rpc.cc	Tue May 15 10:58:43 2018 +0200
    19.2 +++ b/server/unittest_rpc.cc	Thu Jun 07 13:54:10 2018 +0200
    19.3 @@ -24,6 +24,19 @@
    19.4  
    19.5  namespace {
    19.6  
    19.7 +class DummyContext : public Context
    19.8 +{
    19.9 +public:
   19.10 +	virtual bool verify_security_token(const std::string& token) const override { return true; }
   19.11 +	virtual void augment(js::Object&) override { /* do nothing */ }
   19.12 +};
   19.13 +
   19.14 +
   19.15 +DummyContext dummyContext;
   19.16 +
   19.17 +
   19.18 +// some example & test functions:
   19.19 +
   19.20  int add_mul_simple(int x, int y, int z)
   19.21  {
   19.22  	return (x+y) * z;
   19.23 @@ -45,10 +58,29 @@
   19.24  	return new_string( ("x" + rs + "x").c_str(), 0);
   19.25  }
   19.26  
   19.27 +
   19.28 +char* tohex(const char* input, size_t length)
   19.29 +{
   19.30 +	std::string h; h.reserve(length*3);
   19.31 +	char buffer[8] = { 0 };
   19.32 +	const char* end = input+length;
   19.33 +	for(; input<end; ++input)
   19.34 +	{
   19.35 +		snprintf(buffer,7, "%02hhx", (unsigned char)*input );
   19.36 +		if(!h.empty()) h += ' ';
   19.37 +		h += buffer;
   19.38 +	}
   19.39 +	return new_string( h.c_str(), 0 );
   19.40 +}
   19.41 +
   19.42 +
   19.43  const FunctionMap test_functions = {
   19.44  		FP( "add_mul_simple", new Func<int, In<int>, In<int>, In<int>>( &add_mul_simple )),
   19.45 -		FP( "add_mul_inout", new Func<char*, In<int>, In<c_string>, InOutP<int>, Out<char*>>( &add_mul_inout )),
   19.46 -		FP( "stringlist_add", new Func<stringlist_t*, In<stringlist_t*>, In<c_string>>( &stringlist_add )),
   19.47 +		FP( "add_mul_inout" , new Func<char*, In<int>, In<c_string>, InOutP<int>, Out<char*>>( &add_mul_inout )),
   19.48 +		FP( "stringlist_add", new Func<Out<stringlist_t*, ParamFlag::DontOwn>, InOut<stringlist_t*>, In<c_string>>( &stringlist_add )),
   19.49 +		FP( "tohex_1",        new Func<char*, In<c_string>, In<size_t>>( &tohex )), // with explicit length parameter
   19.50 +		FP( "tohex_2",        new Func<char*, In<c_string>, InLength<>>( &tohex )), // with implicit length parameter, with dummy JSON parameter
   19.51 +		FP( "tohex_3",        new Func<char*, In<c_string>, InLength<ParamFlag::NoInput>>( &tohex )), // with implicit length parameter, without JSON parameter
   19.52  	};
   19.53  
   19.54  
   19.55 @@ -78,11 +110,32 @@
   19.56  		{ "{\"jsonrpc\":\"2.0\", \"id\":23, \"method\":\"add_mul_inout\", \"params\":[100,\"111\",123, \"dummy\"]}",
   19.57  		  "{\"jsonrpc\":\"2.0\", \"id\":23, \"result\":{ \"outParams\":[\"25953\",25953], \"return\":\"x25953x\"}}"
   19.58  		},
   19.59 -/* does not work, yet. JSON-93 will fix that:
   19.60  		{ "{\"jsonrpc\":\"2.0\", \"id\":24, \"method\":\"stringlist_add\", \"params\":[[\"abc\",\"def\"], \"ADD\"]}",
   19.61 -		  "{\"jsonrpc\":\"2.0\", \"id\":24, \"result\":{ \"outParams\":[], \"return\":[\"abc\", \"def\", \"ADD\"]}}"
   19.62 +		  "{\"jsonrpc\":\"2.0\", \"id\":24, \"result\":{ \"outParams\":[[\"abc\", \"def\", \"ADD\"]], \"return\":[\"ADD\"]}}"
   19.63  		},
   19.64 -*/
   19.65 +		// tohex:
   19.66 +		{ "{\"jsonrpc\":\"2.0\", \"id\":25, \"method\":\"tohex_1\", \"params\":[\"tohex\",3]}",
   19.67 +		  "{\"jsonrpc\":\"2.0\", \"id\":25, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68\"}}"
   19.68 +		},
   19.69 +		{ "{\"jsonrpc\":\"2.0\", \"id\":26, \"method\":\"tohex_1\", \"params\":[\"tohex\",5]}",
   19.70 +		  "{\"jsonrpc\":\"2.0\", \"id\":26, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
   19.71 +		},
   19.72 +		{ "{\"jsonrpc\":\"2.0\", \"id\":27, \"method\":\"tohex_2\", \"params\":[\"tohex\",0]}",
   19.73 +		  "{\"jsonrpc\":\"2.0\", \"id\":27, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
   19.74 +		},
   19.75 +		{ "{\"jsonrpc\":\"2.0\", \"id\":28, \"method\":\"tohex_2\", \"params\":[\"tohex\",\"dummy_parameter\"]}",
   19.76 +		  "{\"jsonrpc\":\"2.0\", \"id\":28, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
   19.77 +		},
   19.78 +		{ "{\"jsonrpc\":\"2.0\", \"id\":29, \"method\":\"tohex_3\", \"params\":[\"tohex\"]}",
   19.79 +		  "{\"jsonrpc\":\"2.0\", \"id\":29, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
   19.80 +		},
   19.81 +		{ "{\"jsonrpc\":\"2.0\", \"id\":30, \"method\":\"tohex_3\", \"params\":[\"Größe\"]}", // some non-ASCII BMP chars
   19.82 +		  "{\"jsonrpc\":\"2.0\", \"id\":30, \"result\":{ \"outParams\":[], \"return\":\"47 72 c3 b6 c3 9f 65\"}}"
   19.83 +		},
   19.84 +		{ "{\"jsonrpc\":\"2.0\", \"id\":30, \"method\":\"tohex_3\", \"params\":[\"\\u0000\\uD83D\\uDE47\"]}", // all hell breaks loose: Non-BMP characters
   19.85 +		  "{\"jsonrpc\":\"2.0\", \"id\":30, \"result\":{ \"outParams\":[], \"return\":\"00 f0 9f 99 87\"}}"
   19.86 +		},
   19.87 +
   19.88  	};
   19.89  
   19.90  } // end of anonymous namespace
   19.91 @@ -103,7 +156,8 @@
   19.92  
   19.93  	js::Value expected_result;
   19.94  	js::read_or_throw(v.result, expected_result);
   19.95 +	auto r = request;
   19.96  	
   19.97 -	const js::Value actual_result = call( test_functions, request.get_obj(), nullptr, false ); // don't check for security token in this unittest
   19.98 +	const js::Value actual_result = call( test_functions, request.get_obj(), &dummyContext);
   19.99  	EXPECT_EQ( expected_result, actual_result );
  19.100  }