Browse Source

merge JSON-92 into default.

JSON-95
Roker 4 years ago
parent
commit
05a9cdfc42
13 changed files with 288 additions and 77 deletions
  1. +1
    -1
      server/Makefile
  2. +7
    -0
      server/c_string.cc
  3. +38
    -0
      server/c_string.hh
  4. +2
    -2
      server/ev_server.cc
  5. +6
    -2
      server/function_map.hh
  6. +7
    -0
      server/gpg_environment.cc
  7. +4
    -14
      server/inout.cc
  8. +40
    -18
      server/inout.hh
  9. +3
    -3
      server/json_rpc.cc
  10. +1
    -1
      server/json_rpc.hh
  11. +60
    -34
      server/pep-types.cc
  12. +10
    -2
      server/server_version.cc
  13. +109
    -0
      server/unittest_rpc.cc

+ 1
- 1
server/Makefile View File

@ -86,7 +86,7 @@ $(TARGET)-static: main.o libjson-adapter.a
$(TARGET_TEST): servertest.o libjson-adapter.a
$(CXX) $(CPPFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@
$(TARGET_GTEST): unittest_json.o unittest_nfc.o unittest_decompose.o gtest-all.o gtest_main.o libjson-adapter.a
$(TARGET_GTEST): unittest_json.o unittest_nfc.o unittest_decompose.o unittest_rpc.o gtest-all.o gtest_main.o libjson-adapter.a
$(CXX) $(CPPFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@
libjson-adapter.a: $(ALL_OBJECTS)


+ 7
- 0
server/c_string.cc View File

@ -0,0 +1,7 @@
#include "c_string.hh"
#include <pEp/pEpEngine.h>
Out<c_string, true>::~Out()
{
pEp_free(value);
}

+ 38
- 0
server/c_string.hh View File

@ -40,4 +40,42 @@ struct In<c_string, true>
const std::string value;
};
template<>
struct Out<c_string, true>
{
typedef Out<c_string, true> Self;
typedef char** c_type;
enum { is_output = true, need_input = true };
Out(const js::Value&, Context*) // ignore dummy value, ignore context
{ }
~Out();
Out(const Self& other) = delete;
Out(Self&& victim) = delete;
Self& operator=(const Self&) = delete;
js::Value to_json() const
{
return value ? ::to_json<std::string>(value) : js::Value();
}
c_type get_value() const { return &value; }
mutable char* value = nullptr;
};
// forward declare specializations (to avoid selecting of the default implementation),
// but don't implement them, because not needed, yet.
template<>
struct InOut<c_string,true>;
template<>
struct InOutP<c_string,true>;
#endif // PEP_JSON_ADAPTER_C_STRING_HH

+ 2
- 2
server/ev_server.cc View File

@ -62,7 +62,7 @@ const FunctionMap functions = {
Out<char*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &MIME_encrypt_message_for_self ) ),
FP( "MIME_decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<c_string>, In<size_t>,
Out<char*>, Out<stringlist_t*>, Out<PEP_rating>, Out<PEP_decrypt_flags_t>>( &MIME_decrypt_message ) ),
Out<char*>, Out<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>, Out<c_string>>( &MIME_decrypt_message ) ),
FP( "startKeySync", new Func<void, In<JsonAdapter*, false>>( &JsonAdapter::startSync) ),
FP( "stopKeySync", new Func<void, In<JsonAdapter*, false>>( &JsonAdapter::stopSync ) ),
@ -73,7 +73,7 @@ const FunctionMap functions = {
FP( "encrypt_message_for_self", new Func<PEP_STATUS, In<PEP_SESSION, false>,
In<pEp_identity*>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message_for_self ) ),
FP( "decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, In<message*>, Out<message*>, Out<stringlist_t*>, Out<PEP_rating>, Out<PEP_decrypt_flags_t>>( &decrypt_message ) ),
FP( "decrypt_message", new Func<PEP_STATUS, In<PEP_SESSION, false>, InOut<message*>, Out<message*>, Out<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>>( &decrypt_message ) ),
FP( "outgoing_message_rating", new Func<PEP_STATUS, In<PEP_SESSION,false>, In<message*>, Out<PEP_rating>>( &outgoing_message_rating ) ),
FP( "identity_rating" , new Func<PEP_STATUS, In<PEP_SESSION,false>, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),


+ 6
- 2
server/function_map.hh View File

@ -38,7 +38,8 @@ public:
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.get_value()...) );
Out<R> o{ fn(args.get_value()...) };
return to_json( o );
}
};
@ -173,7 +174,10 @@ public:
rs.emplace_back("outParams", std::move(out_params));
rs.emplace_back("return", std::move(ret));
context->augment(rs); // used e.g. add some debug infos to the status return value
if(context)
{
context->augment(rs); // used e.g. add some debug infos to the status return value
}
return rs;
}


+ 7
- 0
server/gpg_environment.cc View File

@ -31,6 +31,12 @@ GpgEnvironment getGpgEnvironment()
}
template<>
Out<GpgEnvironment>::~Out()
{
}
template<>
js::Value to_json<GpgEnvironment>(const GpgEnvironment& ge)
{
@ -41,5 +47,6 @@ js::Value to_json<GpgEnvironment>(const GpgEnvironment& ge)
return obj;
}
template<>
js::Value Type2String<GpgEnvironment>::get() { return "GpgEnvironment"; }

+ 4
- 14
server/inout.cc View File

@ -11,9 +11,7 @@
\
template<> \
Out< TYPE >::~Out() \
{ \
delete value; \
} \
{ } \
SIMPLE_TYPE( bool )
@ -33,23 +31,15 @@ SIMPLE_TYPE( std::string )
template<>
Out<char const*>::~Out()
{
if(value)
{
pEp_free(const_cast<char*>(*value));
}
delete value;
pEp_free(const_cast<char*>(value));
}
template<>
Out<char*>::~Out()
{
if(value)
{
pEp_free(*value);
*value = nullptr;
}
delete value;
pEp_free(value);
}


+ 40
- 18
server/inout.hh View File

@ -85,7 +85,8 @@ struct InRaw
};
// helper classes to specify in- and out-parameters
// helper classes to specify in- and out-parameters whose output is in-place.
// Use InOutP<T> for in/out parameters where the function might change the object and expects a pointer
template<class T, bool NeedInput=true>
struct InOut : public In<T,NeedInput>
{
@ -115,9 +116,12 @@ struct Out
typedef T* c_type; // the according type in C function parameter
enum { is_output = true, need_input = NeedInput }; // if need_input=false it would no longer consume an element in the input parameter array.
Out() : value{ new T{} }
explicit Out() : value{}
{ }
explicit Out(const T& t) : value{t}
{ }
~Out();
Out(const Out<T,NeedInput>& other) = delete;
@ -133,38 +137,49 @@ struct Out
js::Value to_json() const
{
return ::to_json<T>(*value);
return ::to_json<T>(value);
}
c_type get_value() const { return value; }
c_type get_value() const { return &value; }
T* value = nullptr;
mutable T value;
/*
friend
std::ostream& operator<<(std::ostream& o, const Out<T,NeedInput>& out)
{
o << (const void*)&out;
// the if() was added to avoid crashes on memory corruptuon. But clang++ warns, that this check is always true on "well-formed" programs, and he is right. In an ideal world there are no memory corruptions. ;-(
// if(&out)
{
o << ", value=" << (const void*)out.value;
if(out.value)
{
o << ", *value=" << *(out.value);
}
}
o << (const void*)out;
o << ", value=" << (const void*)out.value;
return o;
}
*/
};
// helper classes to specify in- and out-parameters whose output might change by the called function.
// Use InOut<T> for in/out parameters where the function only makes in-place changes.
template<class T, bool NeedInput=true>
struct InOutP : public Out<T,NeedInput>
{
typedef Out<T,NeedInput> Base;
enum { is_output = true, need_input = NeedInput };
explicit InOutP(const T& t) : Base(t) {}
InOutP<T,NeedInput>& operator=(const InOutP<T,NeedInput>&) = delete;
// default implementation:
InOutP(const js::Value& v, Context*)
: Base( from_json<T>(v) )
{ }
};
template<class T, bool NeedInput>
js::Value to_json(const Out<T,NeedInput>& o)
{
return ::to_json(*o.value);
return ::to_json(o.value);
}
template<class T, bool NeedInput>
@ -215,6 +230,13 @@ 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; }
};
template<class T>
struct Type2String<InOutP<T, true>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class... Args> struct Type2Json;
template<class T, class... Args>


+ 3
- 3
server/json_rpc.cc View File

@ -10,8 +10,8 @@
{
js::Object ret;
ret.emplace_back( "jsonrpc", "2.0" );
ret.emplace_back( "result" , result );
ret.emplace_back( "id" , id );
ret.emplace_back( "result" , result );
return ret;
}
@ -62,7 +62,7 @@ namespace
using json_spirit::find_value;
js::Object call(const FunctionMap& fm, const js::Object& request, Context* context)
js::Object call(const FunctionMap& fm, const js::Object& request, Context* context, bool check_security_token)
{
int request_id = -1;
try
@ -74,7 +74,7 @@ js::Object call(const FunctionMap& fm, const js::Object& request, Context* conte
}
const auto sec_token = find_value(request, "security_token");
if(sec_token.type()!=js::str_type || context->verify_security_token(sec_token.get_str())==false )
if(check_security_token && (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);
}


+ 1
- 1
server/json_rpc.hh View File

@ -21,7 +21,7 @@ enum class JSON_RPC
// 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, Context* context);
js::Object call(const FunctionMap& fm, const js::Object& request, Context* context, bool check_security_token = true);
// create a JSON-RPC 2.0 compatible result response object
//js::Object make_result(const js::Value& result, int id);


+ 60
- 34
server/pep-types.cc View File

@ -97,6 +97,7 @@ std::string status_to_string(PEP_STATUS status)
case PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH : status_string = "PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH"; break;
case PEP_VERIFY_NO_KEY : status_string = "PEP_VERIFY_NO_KEY"; break;
case PEP_VERIFIED_AND_TRUSTED : status_string = "PEP_VERIFIED_AND_TRUSTED"; break;
case PEP_CANNOT_REENCRYPT : status_string = "PEP_CANNOT_REENCRYPT"; break;
case PEP_CANNOT_DECRYPT_UNKNOWN : status_string = "PEP_CANNOT_DECRYPT_UNKNOWN"; break;
case PEP_TRUSTWORD_NOT_FOUND : status_string = "PEP_TRUSTWORD_NOT_FOUND"; break;
@ -196,21 +197,13 @@ In<const pEp_identity*>::~In()
template<>
Out<pEp_identity*>::~Out()
{
if(value)
{
free_identity(*value);
}
delete value;
free_identity(value);
}
template<>
Out<identity_list*>::~Out()
{
if(value)
{
free_identity_list(*value);
}
delete value;
free_identity_list(value);
}
@ -246,43 +239,38 @@ In<sync_handshake_result>::~In()
template<>
Out<stringlist_t*>::~Out()
{
if(value) free_stringlist(*value);
delete value;
free_stringlist(value);
}
template<>
Out<stringpair_list_t*>::~Out()
{
if(value) free_stringpair_list(*value);
delete value;
free_stringpair_list(value);
}
template<>
Out<_message*>::~Out()
{
///////////////////////////////////////////////////////////////////////////////
// FIXME: due to memory corruption(?) the free_message() call crashes! :-( //
// Without it we leak memory but at least it works for now... :-/ //
///////////////////////////////////////////////////////////////////////////////
std::cerr << "$| ~Out<message*>: this=" << *this << ".\n";
if(value) free_message(*value);
delete value;
free_message(value);
}
template<>
Out<PEP_rating>::~Out()
{
delete value;
}
template<>
Out<PEP_comm_type>::~Out()
{
delete value;
}
template<>
Out<PEP_STATUS>::~Out()
{
}
@ -290,6 +278,11 @@ Out<PEP_comm_type>::~Out()
template<>
char* from_json<char*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const std::string& ss = v.get_str();
char* s = new_string(ss.c_str(), ss.size() );
return s;
@ -299,10 +292,15 @@ char* from_json<char*>(const js::Value& v)
template<>
message* from_json<message*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Object& o = v.get_obj();
auto msg = pEp::utility::make_c_ptr(new_message(PEP_dir_incoming), &free_message );
// fetch values from v and put them into msg
msg->dir = from_json_object<PEP_msg_direction, js::int_type>(o, "dir");
msg->id = from_json_object<char*, js::str_type>(o, "id");
@ -339,6 +337,11 @@ message* from_json<message*>(const js::Value& v)
template<>
pEp_identity* from_json<pEp_identity*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Object& o = v.get_obj();
auto address = pEp::utility::make_c_ptr(from_json_object<char*, js::str_type>(o, "address") , &free_string );
@ -377,7 +380,7 @@ js::Value to_json<message const*>(message const* const& msg)
{
if(msg == nullptr)
{
return js::Value("NULL-MESSAGE");
return js::Value();
}
js::Object o;
@ -421,16 +424,20 @@ js::Value to_json<message*>(message* const& msg)
template<>
stringlist_t* from_json<stringlist_t*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Array& a = v.get_array();
auto sl = pEp::utility::make_c_ptr(new_stringlist( nullptr ), &free_stringlist );
for(const js::Value& v : a )
{
const std::string& s = v.get_str();
stringlist_add(sl.get(), s.c_str() );
}
return sl.release();
}
@ -444,16 +451,20 @@ const stringlist_t* from_json<const stringlist_t*>(const js::Value& v)
template<>
identity_list* from_json<identity_list*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Array& a = v.get_array();
auto il = pEp::utility::make_c_ptr(new_identity_list( nullptr ), &free_identity_list);
for(const js::Value& v : a )
{
pEp_identity* id = from_json<pEp_identity*>(v);
identity_list_add(il.get(), id );
}
return il.release();
}
@ -461,6 +472,11 @@ identity_list* from_json<identity_list*>(const js::Value& v)
template<>
_bloblist_t* from_json<_bloblist_t*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Array& a = v.get_array();
if(a.empty())
return nullptr;
@ -531,6 +547,11 @@ js::Value to_json<_bloblist_t*>(_bloblist_t* const& bl)
template<>
stringpair_t* from_json<stringpair_t*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Object& o = v.get_obj();
char* key = from_json_object<char*, js::str_type>(o, "key");
char* val = from_json_object<char*, js::str_type>(o, "value");
@ -545,6 +566,11 @@ stringpair_t* from_json<stringpair_t*>(const js::Value& v)
template<>
stringpair_list_t* from_json<stringpair_list_t*>(const js::Value& v)
{
if(v.is_null())
{
return nullptr;
}
const js::Array& a = v.get_array();
if(a.empty())
return nullptr;
@ -628,7 +654,7 @@ js::Value to_json<const pEp_identity*>(const pEp_identity* const& id)
{
if(id == nullptr)
{
return js::Value("NULL-IDENTITY");
return js::Value();
}
js::Object o;


+ 10
- 2
server/server_version.cc View File

@ -53,7 +53,8 @@ static const std::string VersionName =
// "(34) Erndtebrück"; // remove apiVersion(), change version() to return a semver-compatible version number in a JSON object.
// "(35) Bad Berleburg"; // fix the fork() problem on MacOS. daemonize() now got a function parameter. \o/
// "(36) Hatzfeld"; // JSON-81: add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
"(37) Battenberg"; // JSON-75: change debonize() behavior, especially on MS Windows
// "(37) Battenberg"; // JSON-75: change debonize() behavior, especially on MS Windows
"(38) Frankenberg"; // JSON-92: API CHANGE: decrypt_message() has InOut src message, MIME_decrypt_message() returns changed src msg, too.
//const ServerVersion sv{0, 10, 0, version_name}; // first version defined.
//const ServerVersion sv{0, 11, 0, version_name}; // add set_own_key()
@ -62,7 +63,8 @@ static const std::string VersionName =
//const ServerVersion sv{0, 12, 2, version_name}; // fix the fork() problem on MacOS. daemonize() now got a function parameter.
//const ServerVersion sv(0,13,0); // add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
//const ServerVersion sv(0,13,1); // JSON-91: add MIME_encrypt_message_for_self() and encrypt_message_for_self()
const ServerVersion sv(0,14,0); // JSON-75: incompatible behavior of daemonize() especially in MS Windows
//const ServerVersion sv(0,14,0); // JSON-75: incompatible behavior of daemonize() especially in MS Windows
const ServerVersion sv(0,15,0); // JSON-92: API CHANGE.
} // end of anonymous namespace
////////////////////////////////////////////////////////////////////////////
@ -118,5 +120,11 @@ js::Value to_json<ServerVersion>(const ServerVersion& sv)
return o;
}
template<>
Out<ServerVersion>::~Out()
{
}
template<>
js::Value Type2String<ServerVersion>::get() { return "ServerVersion"; }

+ 109
- 0
server/unittest_rpc.cc View File

@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include "json_rpc.hh"
#include "function_map.hh"
#include "c_string.hh"
#include "json_spirit/json_spirit_reader.h"
#include <pEp/pEp_string.h> // for new_string()
#include <pEp/stringlist.h>
#include <vector>
namespace js = json_spirit;
namespace json_spirit
{
std::ostream& operator<<(std::ostream& os, const Value& value)
{
js::write(value, os, js::pretty_print | js::raw_utf8 );
return os;
}
} // end of namespace json_spirit
namespace {
int add_mul_simple(int x, int y, int z)
{
return (x+y) * z;
}
// test function for InOut parameters etc.
char* add_mul_inout(int x, const char* y_str, int* z_result, char** result)
{
const int y = y_str ? strtol(y_str, nullptr, 0) : -1;
const int z = z_result ? *z_result : -1;
const int r = (x+y) * z;
if(z_result)
*z_result = r;
const std::string rs = std::to_string(r);
char* rcs = new_string( rs.c_str(), 0 ); // == strdup() but allocated on Engine's heap
*result = rcs;
return new_string( ("x" + rs + "x").c_str(), 0);
}
const FunctionMap test_functions = {
FP( "add_mul_simple", new Func<int, In<int>, In<int>, In<int>>( &add_mul_simple )),
FP( "add_mul_inout", new Func<char*, In<int>, In<c_string>, InOutP<int>, Out<char*>>( &add_mul_inout )),
FP( "stringlist_add", new Func<stringlist_t*, In<stringlist_t*>, In<c_string>>( &stringlist_add )),
};
struct TestEntry
{
std::string input;
std::string result;
};
std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
{
return o << "input=«" << tt.input << "», result=«" << tt.result << "». ";
}
const std::vector<TestEntry> testValues =
{
{ "{\"jsonrpc\":\"2.0\", \"id\":21, \"method\":\"add_mul_simple\", \"params\":[10,11,12]}",
"{\"jsonrpc\":\"2.0\", \"id\":21, \"result\":{ \"outParams\":[], \"return\":252}}"
},
{ "{\"jsonrpc\":\"2.0\", \"id\":22, \"method\":\"add_mul_simple\", \"params\":[10,-11,-12]}",
"{\"jsonrpc\":\"2.0\", \"id\":22, \"result\":{ \"outParams\":[], \"return\":12}}"
},
{ "{\"jsonrpc\":\"2.0\", \"id\":23, \"method\":\"add_mul_inout\", \"params\":[100,\"111\",123, \"dummy\"]}",
"{\"jsonrpc\":\"2.0\", \"id\":23, \"result\":{ \"outParams\":[\"25953\",25953], \"return\":\"x25953x\"}}"
},
/* does not work, yet. JSON-93 will fix that:
{ "{\"jsonrpc\":\"2.0\", \"id\":24, \"method\":\"stringlist_add\", \"params\":[[\"abc\",\"def\"], \"ADD\"]}",
"{\"jsonrpc\":\"2.0\", \"id\":24, \"result\":{ \"outParams\":[], \"return\":[\"abc\", \"def\", \"ADD\"]}}"
},
*/
};
} // end of anonymous namespace
class RpcTest : public ::testing::TestWithParam<TestEntry>
{
// intentionally left blank for now.
};
INSTANTIATE_TEST_CASE_P(RpcTestInstance, RpcTest, testing::ValuesIn(testValues) );
TEST_P( RpcTest, Meh )
{
const auto v = GetParam();
js::Value request;
js::read_or_throw(v.input, request);
js::Value expected_result;
js::read_or_throw(v.result, expected_result);
const js::Value actual_result = call( test_functions, request.get_obj(), nullptr, false ); // don't check for security token in this unittest
EXPECT_EQ( expected_result, actual_result );
}

Loading…
Cancel
Save