mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
added resource_api and rs-nogui-webui (requires libmicrohttpd, Html files are not included)
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8047 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
97831d0667
commit
ad1bc7f3b8
@ -8,5 +8,6 @@ SUBDIRS += \
|
||||
libbitdht/src/libbitdht.pro \
|
||||
libretroshare/src/libretroshare.pro \
|
||||
retroshare-gui/src/retroshare-gui.pro \
|
||||
libresapi/src/libresapi.pro \
|
||||
retroshare-nogui/src/retroshare-nogui.pro \
|
||||
plugins/plugins.pro
|
||||
|
351
libresapi/src/api/ApiServer.cpp
Normal file
351
libresapi/src/api/ApiServer.cpp
Normal file
@ -0,0 +1,351 @@
|
||||
#include "ApiServer.h"
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <retroshare/rsmsgs.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include "json.h"
|
||||
|
||||
#include <retroshare/rsservicecontrol.h>
|
||||
#ifdef FIXME
|
||||
#include "rswall.h"
|
||||
#endif
|
||||
#include "JsonStream.h"
|
||||
#include "StateTokenServer.h" // for the state token serialisers
|
||||
|
||||
/*
|
||||
data types in json http://json.org/
|
||||
string (utf-8 unicode)
|
||||
number (int and float)
|
||||
object (key value pairs, key must be a string)
|
||||
true
|
||||
false
|
||||
null
|
||||
|
||||
data types in lua http://www.lua.org/pil/2.html
|
||||
nil
|
||||
boolean
|
||||
number (double)
|
||||
string (8-bit)
|
||||
table (key value pairs, keys can be anything except nil)
|
||||
|
||||
data types in QML http://qt-project.org/doc/qt-5/qtqml-typesystem-basictypes.html
|
||||
bool
|
||||
string
|
||||
real/double
|
||||
int
|
||||
list
|
||||
object types?
|
||||
|
||||
QML has many more types with special meaning like date
|
||||
|
||||
|
||||
C++ delivers
|
||||
std::string
|
||||
bool
|
||||
int
|
||||
(double? i don't know)
|
||||
enum
|
||||
bitflags
|
||||
raw binary data
|
||||
|
||||
objects
|
||||
std::vector
|
||||
std::list
|
||||
|
||||
different types of ids/hashes
|
||||
-> convert to/from string with a generic operator
|
||||
-> the operator signals ok/fail to the stream
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
data types to handle:
|
||||
- bool
|
||||
- string
|
||||
- bitflags
|
||||
- enums
|
||||
|
||||
containers:
|
||||
- arrays: collection of objects or values without name, usually of the same type
|
||||
- objects: objects and values with names
|
||||
|
||||
careful: the json lib has many asserts, so retroshare will stop if the smalles thing goes wrong
|
||||
-> check type of json before usage
|
||||
|
||||
there are two possible implementations:
|
||||
- with a virtual base class for the serialisation targets
|
||||
- better documentation of the interface
|
||||
- with templates
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
the general idea is:
|
||||
want output in many different formats, while the retrival of the source data is always the same
|
||||
|
||||
get, put
|
||||
|
||||
ressource adress like
|
||||
.org.retroshare.api.peers
|
||||
|
||||
generic router from adress to the ressource handler object
|
||||
|
||||
data formats for input and output:
|
||||
- json
|
||||
- lua
|
||||
- QObject
|
||||
- maybe just a typed c++ object
|
||||
|
||||
rest inspired / resource based interface
|
||||
- have resources with adresses
|
||||
- requests:
|
||||
- put
|
||||
- get
|
||||
- request has parameters
|
||||
- response:
|
||||
- returncode:
|
||||
- ok
|
||||
- not modified
|
||||
- error
|
||||
- data = object or list of objects
|
||||
|
||||
want to have a typesafe interface at the top?
|
||||
probably not, a system with generic return values is enough
|
||||
this interface is for scripting languages which don't have types
|
||||
|
||||
the interface at the top should look like this:
|
||||
template <class RequestDataFormatT, class ResponseDataFormatT>
|
||||
processRequest(const RequestMeta& req, const RequestDataFormatT& reqData,
|
||||
ResponseMeta& respMeta, ResponseDataFormatT& respData);
|
||||
|
||||
idea: pass all the interfaces to the retroshare core to this function,
|
||||
or have this function as part of an object
|
||||
|
||||
the processor then applies all members of the request and response data to the data format like this:
|
||||
reqData << "member1" << member1
|
||||
<< "member2" << member2 ... ;
|
||||
|
||||
these operators have to be implemented for common things like boolean, int, std::string, std::vector, std::list ...
|
||||
request data gets only deserialised
|
||||
response data gets only serialised
|
||||
|
||||
response and request meta contains things like resource address, method and additional parameters
|
||||
|
||||
want generic resource caching mechanism
|
||||
- on first request a request handler is created
|
||||
- request handler is stored with its input data
|
||||
- if a request handler for a given resource adress and parameters exists
|
||||
then the request handler is asked if the result is still valid
|
||||
if yes the result from the existing handler is used
|
||||
- request handler gets deleted after timeout
|
||||
- can have two types of resource handlers: static handlers and dynamic handlers
|
||||
- static handlers don't get deleted, because they don't contain result data
|
||||
- dynamic handlers contain result data, and thus get deleted after a while
|
||||
|
||||
it is even possible to implement a resource-changed check at the highest level
|
||||
this allows to compute everything on the server side and only send chanes to the client
|
||||
the different resource providers don't have to implement a resource changed check then
|
||||
a top level change detector will poll them
|
||||
of course this does not wokr with a deep resource tree with millions of nodes
|
||||
|
||||
for this we have the dynamic handlers,
|
||||
they are created on demand and know how to listen for changes which affect them
|
||||
|
||||
*/
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
// old code, only to copy and paste from
|
||||
// to be removed
|
||||
/*
|
||||
class ChatlobbiesHandler
|
||||
{
|
||||
public:
|
||||
ChatlobbiesHandler(RsMsgs* msgs): mMsgs(msgs) {}
|
||||
|
||||
template <class InputT, class OutputT>
|
||||
void handleRequest(Request& req, InputT& reqData, Response& resp, OutputT& respData)
|
||||
{
|
||||
if(req.mMethod == "GET")
|
||||
{
|
||||
typename OutputT::Array result;
|
||||
// subscribed lobbies
|
||||
std::list<ChatLobbyInfo> slobbies;
|
||||
mMsgs->getChatLobbyList(slobbies);
|
||||
for(std::list<ChatLobbyInfo>::iterator lit = slobbies.begin(); lit != slobbies.end(); lit++)
|
||||
{
|
||||
typename OutputT::Object lobby;
|
||||
ChatLobbyInfo& lobbyRecord = *lit;
|
||||
lobby["name"] = lobbyRecord.lobby_name;
|
||||
RsPeerId pid;
|
||||
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
|
||||
lobby["id"] = pid.toStdString();
|
||||
lobby["subscribed"] = true;
|
||||
result.push_back(lobby);
|
||||
}
|
||||
// unsubscirbed lobbies
|
||||
std::vector<VisibleChatLobbyRecord> ulobbies;
|
||||
mMsgs->getListOfNearbyChatLobbies(ulobbies);
|
||||
for(std::vector<VisibleChatLobbyRecord>::iterator vit = ulobbies.begin(); vit != ulobbies.end(); vit++)
|
||||
{
|
||||
typename OutputT::Object lobby;
|
||||
VisibleChatLobbyRecord& lobbyRecord = *vit;
|
||||
lobby["name"] = lobbyRecord.lobby_name;
|
||||
RsPeerId pid;
|
||||
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
|
||||
lobby["id"] = pid.toStdString();
|
||||
lobby["subscribed"] = false;
|
||||
result.push_back(lobby);
|
||||
}
|
||||
respData = result;
|
||||
}
|
||||
else if(req.mMethod == "PUT")
|
||||
{
|
||||
RsPeerId id = RsPeerId(req.mAdress.substr(1));
|
||||
|
||||
if(!id.isNull() && reqData.HasKey("msg"))
|
||||
{
|
||||
// for now can send only id as message
|
||||
mMsgs->sendPrivateChat(id, reqData["msg"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RsMsgs* mMsgs;
|
||||
};
|
||||
*/
|
||||
|
||||
class ApiServerMainModules
|
||||
{
|
||||
public:
|
||||
ApiServerMainModules(ResourceRouter& router, StateTokenServer* sts, const RsPlugInInterfaces &ifaces):
|
||||
mPeersHandler(sts, ifaces.mNotify, ifaces.mPeers, ifaces.mMsgs),
|
||||
mIdentityHandler(ifaces.mIdentity),
|
||||
mServiceControlHandler(rsServiceControl), // TODO: don't use global variable here
|
||||
mFileSearchHandler(sts, ifaces.mNotify, ifaces.mTurtle, ifaces.mFiles),
|
||||
mTransfersHandler(sts, ifaces.mFiles)
|
||||
{
|
||||
// the dynamic cast is to not confuse the addResourceHandler template like this:
|
||||
// addResourceHandler(derived class, parent class)
|
||||
// the template would then be instantiated using derived class as parameter
|
||||
// and then parent class would not match the type
|
||||
router.addResourceHandler("peers",dynamic_cast<ResourceRouter*>(&mPeersHandler),
|
||||
&PeersHandler::handleRequest);
|
||||
router.addResourceHandler("identity", dynamic_cast<ResourceRouter*>(&mIdentityHandler),
|
||||
&IdentityHandler::handleRequest);
|
||||
router.addResourceHandler("servicecontrol", dynamic_cast<ResourceRouter*>(&mServiceControlHandler),
|
||||
&ServiceControlHandler::handleRequest);
|
||||
router.addResourceHandler("filesearch", dynamic_cast<ResourceRouter*>(&mFileSearchHandler),
|
||||
&FileSearchHandler::handleRequest);
|
||||
router.addResourceHandler("transfers", dynamic_cast<ResourceRouter*>(&mTransfersHandler),
|
||||
&TransfersHandler::handleRequest);
|
||||
}
|
||||
|
||||
PeersHandler mPeersHandler;
|
||||
IdentityHandler mIdentityHandler;
|
||||
ServiceControlHandler mServiceControlHandler;
|
||||
FileSearchHandler mFileSearchHandler;
|
||||
TransfersHandler mTransfersHandler;
|
||||
};
|
||||
#ifdef FIXME
|
||||
class ApiServerWallModule
|
||||
{
|
||||
public:
|
||||
ApiServerWallModule(ResourceRouter& router, const RsPlugInInterfaces& ifaces, RsWall::RsWall* wall):
|
||||
mWallHandler(wall, ifaces.mIdentity)
|
||||
{
|
||||
router.addResourceHandler("wall", dynamic_cast<ResourceRouter*>(&mWallHandler),
|
||||
&WallHandler::handleRequest);
|
||||
}
|
||||
|
||||
WallHandler mWallHandler;
|
||||
};
|
||||
#endif
|
||||
ApiServer::ApiServer():
|
||||
mStateTokenServer(),
|
||||
mMainModules(0),
|
||||
mWallModule(0)
|
||||
{
|
||||
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),
|
||||
&StateTokenServer::handleRequest);
|
||||
}
|
||||
|
||||
ApiServer::~ApiServer()
|
||||
{
|
||||
if(mMainModules)
|
||||
delete mMainModules;
|
||||
if(mWallModule)
|
||||
delete mWallModule;
|
||||
}
|
||||
|
||||
void ApiServer::loadMainModules(const RsPlugInInterfaces &ifaces)
|
||||
{
|
||||
if(mMainModules == 0)
|
||||
mMainModules = new ApiServerMainModules(mRouter, &mStateTokenServer, ifaces);
|
||||
}
|
||||
#ifdef FIXME
|
||||
void ApiServer::loadWallModule(const RsPlugInInterfaces &ifaces, RsWall::RsWall* wall)
|
||||
{
|
||||
if(mWallModule == 0)
|
||||
mWallModule = new ApiServerWallModule(mRouter, ifaces, wall);
|
||||
}
|
||||
#endif
|
||||
std::string ApiServer::handleRequest(Request &request)
|
||||
{
|
||||
resource_api::JsonStream outstream;
|
||||
std::stringstream debugString;
|
||||
|
||||
StreamBase& data = outstream.getStreamToMember("data");
|
||||
resource_api::Response resp(data, debugString);
|
||||
|
||||
ResponseTask* task = mRouter.handleRequest(request, resp);
|
||||
|
||||
time_t start = time(NULL);
|
||||
while(task && task->doWork(request, resp))
|
||||
{
|
||||
usleep(10*1000);
|
||||
/*if(time(NULL) > (start+5))
|
||||
{
|
||||
std::cerr << "ApiServer::handleRequest() Error: task timed out" << std::endl;
|
||||
resp.mDebug << "Error: task timed out." << std::endl;
|
||||
resp.mReturnCode = resource_api::Response::FAIL;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if(task)
|
||||
delete task;
|
||||
|
||||
std::string returncode;
|
||||
switch(resp.mReturnCode){
|
||||
case resource_api::Response::NOT_SET:
|
||||
returncode = "not_set";
|
||||
break;
|
||||
case resource_api::Response::OK:
|
||||
returncode = "ok";
|
||||
break;
|
||||
case resource_api::Response::WARNING:
|
||||
returncode = "warning";
|
||||
break;
|
||||
case resource_api::Response::FAIL:
|
||||
returncode = "fail";
|
||||
break;
|
||||
}
|
||||
|
||||
// evil HACK, remove this
|
||||
if(data.isRawData())
|
||||
return data.getRawData();
|
||||
|
||||
outstream << resource_api::makeKeyValue("debug_msg", debugString.str());
|
||||
outstream << resource_api::makeKeyValueReference("returncode", returncode);
|
||||
if(!resp.mStateToken.isNull())
|
||||
outstream << resource_api::makeKeyValueReference("statetoken", resp.mStateToken);
|
||||
return outstream.getJsonString();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
86
libresapi/src/api/ApiServer.h
Normal file
86
libresapi/src/api/ApiServer.h
Normal file
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <retroshare/rsplugin.h>
|
||||
|
||||
#include "ApiTypes.h"
|
||||
#include "PeersHandler.h"
|
||||
#include "IdentityHandler.h"
|
||||
#ifdef FIXME
|
||||
#include "WallHandler.h"
|
||||
#endif
|
||||
#include "ServiceControlHandler.h"
|
||||
#include "StateTokenServer.h"
|
||||
#include "FileSearchHandler.h"
|
||||
#include "TransfersHandler.h"
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
class ApiServerMainModules;
|
||||
class ApiServerWallModule;
|
||||
|
||||
// main entry point for all resource api calls
|
||||
|
||||
// call chain is like this:
|
||||
// Wt -> ApiServerWt -> ApiServer -> different handlers
|
||||
// later i want to replace the parts with Wt with something else
|
||||
// (Wt is a too large framework, a simple http server would be enough)
|
||||
// maybe use libmicrohttpd
|
||||
// the other use case for this api is a qt webkit view
|
||||
// this works without html, the webkitview calls directly into our c++ code
|
||||
|
||||
// general part of the api server
|
||||
// should work with any http library or a different transport protocol
|
||||
class ApiServer
|
||||
{
|
||||
public:
|
||||
ApiServer();
|
||||
~ApiServer();
|
||||
|
||||
// it is currently hard to separate into http and non http stuff
|
||||
// mainly because the http path is used in the api
|
||||
// this has to change later
|
||||
// for now let the http part make the request object
|
||||
// and the general apiserver part makes the response
|
||||
std::string handleRequest(Request& request);
|
||||
|
||||
// load the main api modules
|
||||
void loadMainModules(const RsPlugInInterfaces& ifaces);
|
||||
|
||||
// only after rswall was started!
|
||||
#ifdef FIXME
|
||||
void loadWallModule(const RsPlugInInterfaces& ifaces, RsWall::RsWall* wall);
|
||||
#endif
|
||||
|
||||
// allows to add more handlers
|
||||
// make sure the livetime of the handlers is longer than the api server
|
||||
template <class T>
|
||||
void addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp));
|
||||
template <class T>
|
||||
void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp));
|
||||
|
||||
StateTokenServer* getStateTokenServer(){ return &mStateTokenServer; }
|
||||
|
||||
private:
|
||||
StateTokenServer mStateTokenServer; // goes first, as others may depend on it
|
||||
// is always loaded, because it has no dependencies
|
||||
|
||||
// only pointers here, to load/unload modules at runtime
|
||||
ApiServerMainModules* mMainModules; // loaded when RS is started
|
||||
ApiServerWallModule* mWallModule; // only loaded in rssocialnet plugin
|
||||
|
||||
ResourceRouter mRouter;
|
||||
};
|
||||
|
||||
// implementations
|
||||
template <class T>
|
||||
void ApiServer::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
mRouter.addResourceHandler(name, instance, callback);
|
||||
}
|
||||
template <class T>
|
||||
void ApiServer::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
mRouter.addResourceHandler(name, instance, callback);
|
||||
}
|
||||
|
||||
}
|
353
libresapi/src/api/ApiServerMHD.cpp
Normal file
353
libresapi/src/api/ApiServerMHD.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
#include "ApiServerMHD.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
// for filestreamer
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
||||
#include "api/JsonStream.h"
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
const char* API_ENTRY_PATH = "/api/v2";
|
||||
const char* FILESTREAMER_ENTRY_PATH = "/fstream/";
|
||||
|
||||
// interface for request handler classes
|
||||
class MHDHandlerBase
|
||||
{
|
||||
public:
|
||||
virtual ~MHDHandlerBase(){}
|
||||
// return MHD_NO to terminate connection
|
||||
// return MHD_YES otherwise
|
||||
// this function will get called by MHD until a response was queued
|
||||
virtual int handleRequest( struct MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size) = 0;
|
||||
};
|
||||
|
||||
// handles calls to the resource_api
|
||||
class MHDApiHandler: public MHDHandlerBase
|
||||
{
|
||||
public:
|
||||
MHDApiHandler(ApiServer* s): mState(BEGIN), mApiServer(s){}
|
||||
virtual ~MHDApiHandler(){}
|
||||
// return MHD_NO or MHD_YES
|
||||
virtual int handleRequest( struct MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size)
|
||||
{
|
||||
// new request
|
||||
if(mState == BEGIN)
|
||||
{
|
||||
if(strcmp(method, "POST") == 0)
|
||||
{
|
||||
mState = WAITING_DATA;
|
||||
// first time there is no data, do nohtin and return
|
||||
return MHD_YES;
|
||||
}
|
||||
}
|
||||
if(mState == WAITING_DATA)
|
||||
{
|
||||
if(upload_data && *upload_data_size)
|
||||
{
|
||||
mRequesString += std::string(upload_data, *upload_data_size);
|
||||
*upload_data_size = 0;
|
||||
return MHD_YES;
|
||||
}
|
||||
}
|
||||
|
||||
if(strstr(url, API_ENTRY_PATH) != url)
|
||||
{
|
||||
std::cerr << "FATAL ERROR in MHDApiHandler::handleRequest(): url does not start with api entry path, which is \"" << API_ENTRY_PATH << "\"" << std::endl;
|
||||
return MHD_NO;
|
||||
}
|
||||
std::string path2 = (url + strlen(API_ENTRY_PATH));
|
||||
|
||||
resource_api::JsonStream instream;
|
||||
instream.setJsonString(mRequesString);
|
||||
resource_api::Request req(instream);
|
||||
|
||||
if(strcmp(method, "GET") == 0)
|
||||
{
|
||||
req.mMethod = resource_api::Request::GET;
|
||||
}
|
||||
else if(strcmp(method, "POST") == 0)
|
||||
{
|
||||
req.mMethod = resource_api::Request::PUT;
|
||||
}
|
||||
else if(strcmp(method, "DELETE") == 0)
|
||||
{
|
||||
req.mMethod = resource_api::Request::DELETE_AA;
|
||||
}
|
||||
|
||||
std::stack<std::string> stack;
|
||||
std::string str;
|
||||
for(std::string::reverse_iterator sit = path2.rbegin(); sit != path2.rend(); sit++)
|
||||
{
|
||||
if((*sit) != '/')
|
||||
{
|
||||
// add to front because we are traveling in reverse order
|
||||
str = *sit + str;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(str != "")
|
||||
{
|
||||
stack.push(str);
|
||||
str.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(str != "")
|
||||
{
|
||||
stack.push(str);
|
||||
}
|
||||
req.mPath = stack;
|
||||
req.mFullPath = path2;
|
||||
|
||||
std::string result = mApiServer->handleRequest(req);
|
||||
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(result.size(), (void*)result.data(), 0, 1);
|
||||
|
||||
// EVIL HACK remove
|
||||
if(result[0] != '{')
|
||||
MHD_add_response_header(resp, "Content-Type", "image/png");
|
||||
else
|
||||
MHD_add_response_header(resp, "Content-Type", "text/plain");
|
||||
|
||||
MHD_queue_response(connection, MHD_HTTP_OK, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
enum State {BEGIN, WAITING_DATA};
|
||||
State mState;
|
||||
std::string mRequesString;
|
||||
ApiServer* mApiServer;
|
||||
};
|
||||
|
||||
class MHDFilestreamerHandler: public MHDHandlerBase
|
||||
{
|
||||
public:
|
||||
MHDFilestreamerHandler(): mSize(0){}
|
||||
virtual ~MHDFilestreamerHandler(){}
|
||||
|
||||
RsFileHash mHash;
|
||||
uint64_t mSize;
|
||||
|
||||
// return MHD_NO or MHD_YES
|
||||
virtual int handleRequest( struct MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size)
|
||||
{
|
||||
if(rsFiles == 0)
|
||||
{
|
||||
ApiServerMHD::sendMessage(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Error: rsFiles is null. Retroshare is probably not yet started.");
|
||||
return MHD_YES;
|
||||
}
|
||||
if(url[0] == 0 || (mHash=RsFileHash(url+strlen(FILESTREAMER_ENTRY_PATH))).isNull())
|
||||
{
|
||||
ApiServerMHD::sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: URL is not a valid file hash");
|
||||
return MHD_YES;
|
||||
}
|
||||
FileInfo info;
|
||||
std::list<RsFileHash> dls;
|
||||
rsFiles->FileDownloads(dls);
|
||||
if(!(rsFiles->alreadyHaveFile(mHash, info) || std::find(dls.begin(), dls.end(), mHash) != dls.end()))
|
||||
{
|
||||
ApiServerMHD::sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: file not existing on local peer and not downloading. Start the download before streaming it.");
|
||||
return MHD_YES;
|
||||
}
|
||||
mSize = info.size;
|
||||
|
||||
struct MHD_Response* resp = MHD_create_response_from_callback(
|
||||
mSize, 1024*1024, &contentReadercallback, this, NULL);
|
||||
MHD_queue_response(connection, MHD_HTTP_OK, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
static ssize_t contentReadercallback(void *cls, uint64_t pos, char *buf, size_t max)
|
||||
{
|
||||
MHDFilestreamerHandler* handler = (MHDFilestreamerHandler*)cls;
|
||||
if(pos >= handler->mSize)
|
||||
return MHD_CONTENT_READER_END_OF_STREAM;
|
||||
uint32_t size_to_send = max;
|
||||
if(!rsFiles->getFileData(handler->mHash, pos, size_to_send, (uint8_t*)buf))
|
||||
return 0;
|
||||
return size_to_send;
|
||||
}
|
||||
};
|
||||
|
||||
ApiServerMHD::ApiServerMHD(std::string root_dir, uint16_t port):
|
||||
mRootDir(root_dir), mPort(port),
|
||||
mDaemon(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ApiServerMHD::~ApiServerMHD()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool ApiServerMHD::start()
|
||||
{
|
||||
if(mDaemon)
|
||||
{
|
||||
std::cerr << "ApiServerMHD::start() ERROR: server already started. You have to call stop() first." << std::endl;
|
||||
return false;
|
||||
}
|
||||
mDaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, mPort,
|
||||
&static_acceptPolicyCallback, this,
|
||||
&static_accessHandlerCallback, this,
|
||||
MHD_OPTION_NOTIFY_COMPLETED, &static_requestCompletedCallback, this,
|
||||
MHD_OPTION_END);
|
||||
if(mDaemon)
|
||||
{
|
||||
std::cerr << "ApiServerMHD::start() SUCCESS. Started server on port " << mPort << ". mRootDir=\"" << mRootDir << "\"" << std::endl;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ApiServerMHD::start() ERROR: starting the server failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ApiServerMHD::stop()
|
||||
{
|
||||
if(mDaemon == 0)
|
||||
return;
|
||||
MHD_stop_daemon(mDaemon);
|
||||
mDaemon = 0;
|
||||
}
|
||||
|
||||
/*static*/ void ApiServerMHD::sendMessage(MHD_Connection *connection, unsigned int status, std::string message)
|
||||
{
|
||||
std::string page = "<html><body><p>"+message+"</p></body></html>";
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(page.size(), (void*)page.data(), 0, 1);
|
||||
MHD_add_response_header(resp, "Content-Type", "text/html");
|
||||
MHD_queue_response(connection, status, resp);
|
||||
MHD_destroy_response(resp);
|
||||
}
|
||||
|
||||
int ApiServerMHD::static_acceptPolicyCallback(void *cls, const sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
return ((ApiServerMHD*)cls)->acceptPolicyCallback(addr, addrlen);
|
||||
}
|
||||
|
||||
int ApiServerMHD::static_accessHandlerCallback(void* cls, struct MHD_Connection * connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size,
|
||||
void **con_cls)
|
||||
{
|
||||
return ((ApiServerMHD*)cls)->accessHandlerCallback(connection, url, method, version,
|
||||
upload_data, upload_data_size, con_cls);
|
||||
}
|
||||
|
||||
void ApiServerMHD::static_requestCompletedCallback(void *cls, MHD_Connection* connection,
|
||||
void **con_cls, MHD_RequestTerminationCode toe)
|
||||
{
|
||||
((ApiServerMHD*)cls)->requestCompletedCallback(connection, con_cls, toe);
|
||||
}
|
||||
|
||||
|
||||
int ApiServerMHD::acceptPolicyCallback(const sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
// limit to localhost
|
||||
// denies from localhost, TODO
|
||||
/*
|
||||
if(addr->sa_family == AF_INET && ((sockaddr_in*)addr)->sin_addr.s_addr == INADDR_LOOPBACK)
|
||||
return MHD_YES;
|
||||
else
|
||||
return MHD_NO;
|
||||
*/
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size,
|
||||
void **con_cls)
|
||||
{
|
||||
// is this call a continuation for an existing request?
|
||||
if(*con_cls)
|
||||
{
|
||||
return ((MHDHandlerBase*)(*con_cls))->handleRequest(connection, url, method, version, upload_data, upload_data_size);
|
||||
}
|
||||
|
||||
// these characters are not allowe in the url, raise an error if they occour
|
||||
// reason: don't want to serve files outside the current document root
|
||||
const char *double_dots = "..";
|
||||
if(strstr(url, double_dots))
|
||||
{
|
||||
const char *error = "<html><body><p>Fatal error: found double dots (\"..\") in the url. This is not allowed</p></body></html>";
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
|
||||
MHD_add_response_header(resp, "Content-Type", "text/html");
|
||||
MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
// is it a call to the resource api?
|
||||
if(strstr(url, API_ENTRY_PATH) == url)
|
||||
{
|
||||
// create a new handler and store it in con_cls
|
||||
MHDHandlerBase* handler = new MHDApiHandler(&mApiServer);
|
||||
*con_cls = (void*) handler;
|
||||
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
|
||||
}
|
||||
// is it a call to the filestreamer?
|
||||
if(strstr(url, FILESTREAMER_ENTRY_PATH) == url)
|
||||
{
|
||||
// create a new handler and store it in con_cls
|
||||
MHDHandlerBase* handler = new MHDFilestreamerHandler();
|
||||
*con_cls = (void*) handler;
|
||||
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
|
||||
}
|
||||
|
||||
// else server static files
|
||||
std::string filename = std::string(".") + url;
|
||||
// important: binary open mode,
|
||||
// else libmicrohttpd will replace crlf with lf and add garbage at the end of the file
|
||||
FILE* fd = fopen(filename.c_str(), "rb");
|
||||
if(fd == 0)
|
||||
{
|
||||
const char *error = "<html><body><p>Error: can't open the requested file.</p></body></html>";
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
|
||||
MHD_add_response_header(resp, "Content-Type", "text/html");
|
||||
MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
if(fstat(fileno(fd), &s) == -1)
|
||||
{
|
||||
const char *error = "<html><body><p>Error: file was opened but stat failed.</p></body></html>";
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
|
||||
MHD_add_response_header(resp, "Content-Type", "text/html");
|
||||
MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
struct MHD_Response* resp = MHD_create_response_from_fd(s.st_size, fileno(fd));
|
||||
MHD_queue_response(connection, MHD_HTTP_OK, resp);
|
||||
MHD_destroy_response(resp);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
void ApiServerMHD::requestCompletedCallback(struct MHD_Connection *connection,
|
||||
void **con_cls, MHD_RequestTerminationCode toe)
|
||||
{
|
||||
if(*con_cls)
|
||||
{
|
||||
delete (MHDHandlerBase*)(*con_cls);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
37
libresapi/src/api/ApiServerMHD.h
Normal file
37
libresapi/src/api/ApiServerMHD.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <microhttpd.h>
|
||||
#include <string>
|
||||
|
||||
#include "api/ApiServer.h"
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
class ApiServerMHD
|
||||
{
|
||||
public:
|
||||
ApiServerMHD(std::string root_dir, uint16_t port);
|
||||
~ApiServerMHD();
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
ApiServer& getApiServer(){ return mApiServer; }
|
||||
|
||||
|
||||
// internal helper
|
||||
static void sendMessage(struct MHD_Connection* connection, unsigned int status, std::string message);
|
||||
private:
|
||||
// static callbacks for libmicrohttpd, they call the members below
|
||||
static int static_acceptPolicyCallback(void* cls, const struct sockaddr * addr, socklen_t addrlen);
|
||||
static int static_accessHandlerCallback(void* cls, struct MHD_Connection * connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls);
|
||||
static void static_requestCompletedCallback(void *cls, struct MHD_Connection* connection, void **con_cls, enum MHD_RequestTerminationCode toe);
|
||||
int acceptPolicyCallback(const struct sockaddr * addr, socklen_t addrlen);
|
||||
int accessHandlerCallback(struct MHD_Connection * connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls);
|
||||
void requestCompletedCallback(struct MHD_Connection *connection, void **con_cls, MHD_RequestTerminationCode toe);
|
||||
std::string mRootDir;
|
||||
uint16_t mPort;
|
||||
MHD_Daemon* mDaemon;
|
||||
ApiServer mApiServer;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
288
libresapi/src/api/ApiTypes.h
Normal file
288
libresapi/src/api/ApiTypes.h
Normal file
@ -0,0 +1,288 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <stdint.h>
|
||||
#include <ostream>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// things to clean up:
|
||||
// - temporary variables when serialising rs-ids
|
||||
// - always ensure proper return values
|
||||
// - add help functions
|
||||
// - remove unused functions or implement them
|
||||
// - add operators or functions for std::set, std::list, std::vector, std::map
|
||||
|
||||
|
||||
// idea:
|
||||
// make a second parameter like
|
||||
// ValueReference(this->member, OPTIONAL); // optional signals that it is not an error if this member is missing
|
||||
// make a third parameter with a type hint: time, length, kilobytes
|
||||
|
||||
// to make arrays
|
||||
template<class T>
|
||||
class ValueReference
|
||||
{
|
||||
public:
|
||||
ValueReference(T& value): value(value){}
|
||||
T& value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
ValueReference<T> makeValueReference(T& value);
|
||||
|
||||
template<class T>
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
Value(T value): value(value){}
|
||||
operator ValueReference<T>(){ return ValueReference<T>(value);}
|
||||
T value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
Value<T> makeValue(T value);
|
||||
|
||||
// to make objects
|
||||
template<class T>
|
||||
class KeyValueReference
|
||||
{
|
||||
public:
|
||||
KeyValueReference(std::string key, T& value): key(key), value(value){}
|
||||
//KeyValueReference(const char* key, T& value): key(key), value(value){}
|
||||
std::string key;
|
||||
T& value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
KeyValueReference<T> makeKeyValueReference(std::string key, T& value);
|
||||
|
||||
// for serialisation
|
||||
// copies the supplied value
|
||||
// automatically converts itself to a KeyValueReference
|
||||
template<class T>
|
||||
class KeyValue
|
||||
{
|
||||
public:
|
||||
KeyValue(std::string key, T value): key(key), value(value){}
|
||||
|
||||
operator KeyValueReference<T>(){ return KeyValueReference<T>(key, value);}
|
||||
|
||||
std::string key;
|
||||
T value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
KeyValue<T> makeKeyValue(std::string key, T value);
|
||||
|
||||
// interface for streams
|
||||
class StreamBase
|
||||
{
|
||||
public:
|
||||
// the stream provides operators for basic data types
|
||||
// everything else should be broken down by others
|
||||
// the same stream can either become an object or an array stream, or a binary data object
|
||||
// a binary data object is just raw binary data without any decoration
|
||||
// binary data is good to pass things like images and small files
|
||||
// this depends on how this stream is used
|
||||
// but once the stream is used as array, then only array operations are allowed
|
||||
// same with an stream used as object
|
||||
|
||||
// idea: can have filter streams which forward the calls to another stream
|
||||
// to make debug protocols of other steam implementations
|
||||
// idea: make a stream shich creates a hash from the input to detect changes in the data
|
||||
|
||||
// make an array
|
||||
virtual StreamBase& operator<<(ValueReference<bool> value) = 0;
|
||||
virtual StreamBase& operator<<(ValueReference<int> value) = 0;
|
||||
virtual StreamBase& operator<<(ValueReference<double> value) = 0;
|
||||
virtual StreamBase& operator<<(ValueReference<std::string> value) = 0;
|
||||
// usefull if the new array member should be an array or object
|
||||
// the reference should be at least valid until another method of this class gets called
|
||||
virtual StreamBase& getStreamToMember() = 0;
|
||||
|
||||
// make an object
|
||||
virtual StreamBase& operator<<(KeyValueReference<bool> keyValue) = 0;
|
||||
virtual StreamBase& operator<<(KeyValueReference<int> keyValue) = 0;
|
||||
virtual StreamBase& operator<<(KeyValueReference<double> keyValue) = 0;
|
||||
virtual StreamBase& operator<<(KeyValueReference<std::string> keyValue) = 0;
|
||||
// usefull if the new object member should be an array or object
|
||||
// the reference should be at least valid until another method of this class gets called
|
||||
virtual StreamBase& getStreamToMember(std::string name) = 0;
|
||||
|
||||
// make a binay data object (not a real object, just binary data)
|
||||
// idea: can use vector.swap() to allow passing larger data items without copying
|
||||
virtual StreamBase& operator<<(std::vector<uint8_t>& data) = 0;
|
||||
|
||||
// return true if there are more members in this object/array
|
||||
// useful for array reading
|
||||
virtual bool hasMore() = 0;
|
||||
|
||||
virtual bool serialise() = 0; // let external operators find out they should serialise or deserialise
|
||||
// return true if no serialisation/deserialisation error occoured
|
||||
virtual bool isOK() = 0;
|
||||
virtual void setError() = 0; // let external operators set the failed bit
|
||||
//virtual void addLogMsg(std::string msg) = 0;
|
||||
virtual void addErrorMsg(std::string msg) = 0;
|
||||
virtual std::string getLog() = 0;
|
||||
virtual std::string getErrorLog() = 0;
|
||||
|
||||
virtual bool isRawData() = 0;
|
||||
virtual std::string getRawData() = 0;// HACK, remove this
|
||||
};
|
||||
|
||||
// todo:
|
||||
// define clear rules how a response to a request should look like
|
||||
// the clients should be able to know when something failed
|
||||
// then it is desired to have a minimum of debug output to track the errors down
|
||||
// currently no check for successful serialisation/deserialisation is performed
|
||||
//
|
||||
// response metadata:
|
||||
// - etag, when will this result expire
|
||||
// - maybe a hint how often this etag should be checked for updates
|
||||
//
|
||||
// outcome of a request:
|
||||
// - full ok
|
||||
// - partial ok
|
||||
// - resource not found, invalid address or resource not available
|
||||
// - not ok, internal error
|
||||
// - wrong usage, parameters or POST data is wrong, like deserialisation error
|
||||
// is is hard to find the cause of the error
|
||||
// maybe include a comment with additional info
|
||||
//
|
||||
// want to include a mime type of the resulting data?
|
||||
// because some data is json, othe plain text, other unknown binary stuff
|
||||
|
||||
// live-stream resources
|
||||
// some resources like typing notifications are only valid for a short time
|
||||
|
||||
// resource types:
|
||||
// - list, either with objects or adresses of objects.
|
||||
// lists need a navigation mechanism like get objects before and get objects after
|
||||
// - object
|
||||
// - stream
|
||||
// - binary data, for example files
|
||||
|
||||
// TODO: record a timestamp for each token, to allow garbage collection of very old tokens
|
||||
class StateToken{
|
||||
public:
|
||||
StateToken(): value(0){}
|
||||
StateToken(uint32_t value): value(value){}
|
||||
std::string toString();
|
||||
|
||||
uint32_t getValue() const {return value;}
|
||||
bool isNull() const {return value == 0;}
|
||||
private:
|
||||
uint32_t value; // 0 is reserved for invalid token
|
||||
};
|
||||
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
Request(StreamBase& stream): mStream(stream){}
|
||||
|
||||
bool isGet(){ return mMethod == GET;}
|
||||
bool isPut(){ return mMethod == PUT;}
|
||||
bool isDelete(){ return mMethod == DELETE_AA;}
|
||||
bool isExec(){ return mMethod == EXEC;}
|
||||
|
||||
// path is the adress to the resource
|
||||
// if the path has multiple parts which get handled by different handlers,
|
||||
// then each handler should pop the top element
|
||||
std::stack<std::string> mPath;
|
||||
std::string mFullPath;
|
||||
|
||||
// parameters should be used to influence the result
|
||||
// for example include or exclude some information
|
||||
// question: when to use parameters, and when to use the data field?
|
||||
// it would be easier to have only one thing...
|
||||
// UNUSED: was never implemented
|
||||
//std::vector<std::pair<std::string, std::string> > mParameters;
|
||||
|
||||
// contains data for new resources
|
||||
StreamBase& mStream;
|
||||
|
||||
// use the is*() methods to query the method type
|
||||
//private:
|
||||
enum Method { GET, PUT, DELETE_AA, EXEC};// something is wrong with DELETE, it won't compile with it
|
||||
Method mMethod;
|
||||
};
|
||||
|
||||
// new notes on responses
|
||||
// later we want to send multiple requests over the same link
|
||||
// and we want to be able to send the responses in a different order than the requests
|
||||
// for this we need a unique token in every request which gets returned in the response
|
||||
|
||||
// response:
|
||||
// message token
|
||||
// status (ok, warning, fail)
|
||||
// data (different for different resources)
|
||||
// debugstring (a human readable error message in case something went wrong)
|
||||
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
Response(StreamBase& stream, std::ostream& debug): mReturnCode(NOT_SET), mDataStream(stream), mDebug(debug){}
|
||||
|
||||
// WARNING means: a valid result is available, but an error occoured
|
||||
// FAIL means: the result is not valid
|
||||
enum ReturnCode{ NOT_SET, OK, WARNING, FAIL};
|
||||
ReturnCode mReturnCode;
|
||||
|
||||
StateToken mStateToken;
|
||||
|
||||
// the result
|
||||
StreamBase& mDataStream;
|
||||
|
||||
// humand readable string for debug messages/logging
|
||||
std::ostream& mDebug;
|
||||
|
||||
inline void setOk(){mReturnCode = OK;}
|
||||
inline void setWarning(){ mReturnCode = WARNING;}
|
||||
inline void setFail(std::string msg = ""){
|
||||
mReturnCode = FAIL;
|
||||
if(msg != "")
|
||||
mDebug << msg << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// if a response can not be handled immediately,
|
||||
// then the handler should return a ResponseTask object
|
||||
// the api server will then call the doWork() method periodically
|
||||
class ResponseTask
|
||||
{
|
||||
public:
|
||||
// return true if function should get called again
|
||||
// return false when finished
|
||||
virtual bool doWork(Request& req, Response& resp) = 0;
|
||||
};
|
||||
|
||||
// implementations
|
||||
|
||||
template<class T>
|
||||
ValueReference<T> makeValueReference(T& value)
|
||||
{
|
||||
return ValueReference<T>(value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Value<T> makeValue(T value)
|
||||
{
|
||||
return Value<T>(value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
KeyValueReference<T> makeKeyValueReference(std::string key, T& value)
|
||||
{
|
||||
return KeyValueReference<T>(key, value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
KeyValue<T> makeKeyValue(std::string key, T value)
|
||||
{
|
||||
return KeyValue<T>(key, value);
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
240
libresapi/src/api/FileSearchHandler.cpp
Normal file
240
libresapi/src/api/FileSearchHandler.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "FileSearchHandler.h"
|
||||
|
||||
#include <retroshare/rsexpr.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "Operators.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
FileSearchHandler::FileSearchHandler(StateTokenServer *sts, RsNotify *notify, RsTurtle *turtle, RsFiles *files):
|
||||
mStateTokenServer(sts), mNotify(notify), mTurtle(turtle), mFiles(files),
|
||||
mMtx("FileSearchHandler")
|
||||
{
|
||||
mNotify->registerNotifyClient(this);
|
||||
addResourceHandler("*", this, &FileSearchHandler::handleWildcard);
|
||||
addResourceHandler("create_search", this, &FileSearchHandler::handleCreateSearch);
|
||||
|
||||
mSearchesStateToken = mStateTokenServer->getNewToken();
|
||||
}
|
||||
|
||||
FileSearchHandler::~FileSearchHandler()
|
||||
{
|
||||
mNotify->unregisterNotifyClient(this);
|
||||
mStateTokenServer->discardToken(mSearchesStateToken);
|
||||
}
|
||||
|
||||
void FileSearchHandler::notifyTurtleSearchResult(uint32_t search_id, const std::list<TurtleFileInfo>& files)
|
||||
{
|
||||
RsStackMutex stackMtx(mMtx); // ********** STACK LOCKED MTX **********
|
||||
std::map<uint32_t, Search>::iterator mit = mSearches.find(search_id);
|
||||
if(mit == mSearches.end())
|
||||
return;
|
||||
|
||||
Search& search = mit->second;
|
||||
// set to a limit of 100 for now, can have more when we have pagination
|
||||
std::list<TurtleFileInfo>::const_iterator lit = files.begin();
|
||||
bool changed = false;
|
||||
while(search.mResults.size() < 100 && lit != files.end())
|
||||
{
|
||||
if(search.mHashes.find(lit->hash) == search.mHashes.end())
|
||||
{
|
||||
changed = true;
|
||||
FileDetail det ;
|
||||
det.rank = 0 ;
|
||||
det.age = 0 ;
|
||||
det.name = (*lit).name;
|
||||
det.hash = (*lit).hash;
|
||||
det.size = (*lit).size;
|
||||
det.id.clear();
|
||||
search.mResults.push_back(det);
|
||||
search.mHashes.insert(lit->hash);
|
||||
}
|
||||
lit++;
|
||||
}
|
||||
if(changed)
|
||||
{
|
||||
mStateTokenServer->discardToken(search.mStateToken);
|
||||
search.mStateToken = mStateTokenServer->getNewToken();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delete searches
|
||||
void FileSearchHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
if(!req.mPath.empty())
|
||||
{
|
||||
std::string str = req.mPath.top();
|
||||
req.mPath.pop();
|
||||
|
||||
if(str.size() != 8)
|
||||
{
|
||||
resp.setFail("Error: id has wrong size, should be 8 characters");
|
||||
return;
|
||||
}
|
||||
uint32_t id = 0;
|
||||
// TODO fix this
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
id += (uint32_t(str[i]-'A')) << (i*4);
|
||||
}
|
||||
|
||||
{
|
||||
RsStackMutex stackMtx(mMtx); // ********** STACK LOCKED MTX **********
|
||||
std::map<uint32_t, Search>::iterator mit = mSearches.find(id);
|
||||
if(mit == mSearches.end())
|
||||
{
|
||||
resp.setFail("Error: search id invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
Search& search = mit->second;
|
||||
resp.mStateToken = search.mStateToken;
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::list<FileDetail>::iterator lit = search.mResults.begin(); lit != search.mResults.end(); ++lit)
|
||||
{
|
||||
FileDetail& fd = *lit;
|
||||
double size = fd.size;
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< makeKeyValueReference("id", fd.hash)
|
||||
<< makeKeyValueReference("name", fd.name)
|
||||
<< makeKeyValueReference("hash", fd.hash)
|
||||
<< makeKeyValueReference("size", size)
|
||||
<< makeKeyValueReference("rank", fd.rank);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// list searches
|
||||
RsStackMutex stackMtx(mMtx); // ********** STACK LOCKED MTX **********
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::map<uint32_t, Search>::iterator mit = mSearches.begin(); mit != mSearches.end(); ++mit)
|
||||
{
|
||||
uint32_t id = mit->first;
|
||||
std::string idstr;
|
||||
// how many times do i have to write int to string conversation?
|
||||
// a library should do this
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
char c = ((id>>(i*4))&0xF)+'A';
|
||||
idstr += c;
|
||||
}
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< makeKeyValueReference("id", idstr)
|
||||
<< makeKeyValueReference("search_string", mit->second.mSearchString);
|
||||
}
|
||||
resp.mStateToken = mSearchesStateToken;
|
||||
resp.setOk();
|
||||
}
|
||||
}
|
||||
|
||||
static bool dirDetailToFileDetail(const DirDetails& dir, FileDetail& fd)
|
||||
{
|
||||
if (dir.type == DIR_TYPE_FILE)
|
||||
{
|
||||
fd.id = dir.id;
|
||||
fd.name = dir.name;
|
||||
fd.hash = dir.hash;
|
||||
fd.path = dir.path;
|
||||
fd.size = dir.count;
|
||||
fd.age = dir.age;
|
||||
fd.rank = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// see retroshare-gui/src/gui/Searchdialog.cpp
|
||||
void FileSearchHandler::handleCreateSearch(Request &req, Response &resp)
|
||||
{
|
||||
bool distant = false;// distant involves sending data, so have it off by default for privacy
|
||||
bool local = true;
|
||||
bool remote = true;
|
||||
std::string search_string;
|
||||
req.mStream << makeKeyValueReference("distant", distant)
|
||||
<< makeKeyValueReference("local", local)
|
||||
<< makeKeyValueReference("remote", remote)
|
||||
<< makeKeyValueReference("search_string", search_string);
|
||||
|
||||
std::istringstream iss(search_string);
|
||||
std::list<std::string> words;
|
||||
std::string s;
|
||||
while (std::getline(iss, s, ' ')) {
|
||||
std::cout << s << std::endl;
|
||||
words.push_back(s);
|
||||
}
|
||||
|
||||
if(words.empty())
|
||||
{
|
||||
resp.setFail("Error: no search string given");
|
||||
return;
|
||||
}
|
||||
|
||||
NameExpression exprs(ContainsAllStrings,words,true) ;
|
||||
LinearizedExpression lin_exp ;
|
||||
exprs.linearize(lin_exp) ;
|
||||
|
||||
uint32_t search_id = RSRandom::random_u32();
|
||||
if(distant)
|
||||
{
|
||||
// i have no idea what the reasons for two different search modes are
|
||||
// rs-gui does it, so do we
|
||||
if(words.size() == 1)
|
||||
search_id = mTurtle->turtleSearch(words.front());
|
||||
else
|
||||
search_id = mTurtle->turtleSearch(lin_exp);
|
||||
}
|
||||
|
||||
std::list<FileDetail> results;
|
||||
if(local)
|
||||
{
|
||||
std::list<DirDetails> local_results;
|
||||
rsFiles->SearchBoolExp(&exprs, local_results, RS_FILE_HINTS_LOCAL);
|
||||
|
||||
for(std::list<DirDetails>::iterator lit = local_results.begin(); lit != local_results.end(); ++lit)
|
||||
{
|
||||
FileDetail fd;
|
||||
if(dirDetailToFileDetail(*lit, fd))
|
||||
results.push_back(fd);
|
||||
}
|
||||
}
|
||||
if(remote)
|
||||
{
|
||||
std::list<DirDetails> remote_results;
|
||||
rsFiles->SearchBoolExp(&exprs, remote_results, RS_FILE_HINTS_REMOTE);
|
||||
for(std::list<DirDetails>::iterator lit = remote_results.begin(); lit != remote_results.end(); ++lit)
|
||||
{
|
||||
FileDetail fd;
|
||||
if(dirDetailToFileDetail(*lit, fd))
|
||||
results.push_back(fd);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
RsStackMutex stackMtx(mMtx); // ********** STACK LOCKED MTX **********
|
||||
|
||||
Search& search = mSearches[search_id];
|
||||
search.mStateToken = mStateTokenServer->getNewToken();
|
||||
search.mSearchString = search_string;
|
||||
search.mResults.swap(results);
|
||||
|
||||
mStateTokenServer->discardToken(mSearchesStateToken);
|
||||
mSearchesStateToken = mStateTokenServer->getNewToken();
|
||||
}
|
||||
|
||||
std::string idstr;
|
||||
// how many times do i have to write int to string conversation?
|
||||
// a library should do this
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
char c = ((search_id>>(i*4))&0xF)+'A';
|
||||
idstr += c;
|
||||
}
|
||||
resp.mDataStream << makeKeyValueReference("search_id", idstr);
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
43
libresapi/src/api/FileSearchHandler.h
Normal file
43
libresapi/src/api/FileSearchHandler.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "ResourceRouter.h"
|
||||
#include "StateTokenServer.h"
|
||||
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include <retroshare/rsturtle.h>
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class FileSearchHandler: public ResourceRouter, NotifyClient
|
||||
{
|
||||
public:
|
||||
FileSearchHandler(StateTokenServer* sts, RsNotify* notify, RsTurtle* turtle, RsFiles* files);
|
||||
virtual ~FileSearchHandler();
|
||||
|
||||
// from NotifyClient
|
||||
virtual void notifyTurtleSearchResult(uint32_t search_id, const std::list<TurtleFileInfo>& files);
|
||||
private:
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
void handleCreateSearch(Request& req, Response& resp);
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsNotify* mNotify;
|
||||
RsTurtle* mTurtle;
|
||||
RsFiles* mFiles;
|
||||
|
||||
class Search{
|
||||
public:
|
||||
StateToken mStateToken;
|
||||
std::string mSearchString; // extra service: store the search string
|
||||
std::list<FileDetail> mResults;
|
||||
// a set for fast deduplication lookup
|
||||
std::set<RsFileHash> mHashes;
|
||||
};
|
||||
|
||||
RsMutex mMtx;
|
||||
StateToken mSearchesStateToken;
|
||||
std::map<uint32_t, Search> mSearches; // mutex protected
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
48
libresapi/src/api/GetPluginInterfaces.cpp
Normal file
48
libresapi/src/api/GetPluginInterfaces.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "GetPluginInterfaces.h"
|
||||
|
||||
#include <retroshare/rsplugin.h>
|
||||
|
||||
#include <retroshare/rsmsgs.h>
|
||||
#include <retroshare/rsturtle.h>
|
||||
#include <retroshare/rsdisc.h>
|
||||
#include <retroshare/rsdht.h>
|
||||
#include <retroshare/rsnotify.h>
|
||||
|
||||
#include <retroshare/rsidentity.h>
|
||||
#include <retroshare/rsgxscircles.h>
|
||||
#include <retroshare/rsgxsforums.h>
|
||||
#include <retroshare/rsgxschannels.h>
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
bool getPluginInterfaces(RsPlugInInterfaces& interfaces)
|
||||
{
|
||||
// when rsPlugins is null, then rs was not started
|
||||
if(rsPlugins == 0)
|
||||
return false;
|
||||
|
||||
interfaces.mFiles = rsFiles;
|
||||
interfaces.mPeers = rsPeers;
|
||||
interfaces.mMsgs = rsMsgs;
|
||||
interfaces.mTurtle = rsTurtle;
|
||||
interfaces.mDisc = rsDisc;
|
||||
interfaces.mDht = rsDht;
|
||||
interfaces.mNotify = rsNotify;
|
||||
|
||||
// gxs
|
||||
interfaces.mGxsDir = "";
|
||||
interfaces.mIdentity = rsIdentity;
|
||||
// not exposed with global variable, can't get it
|
||||
interfaces.mRsNxsNetMgr = 0;
|
||||
// same as identity service, but different interface
|
||||
interfaces.mGxsIdService = 0;
|
||||
//
|
||||
interfaces.mGxsCirlces = 0;
|
||||
// not exposed with global variable
|
||||
interfaces.mPgpAuxUtils = 0;
|
||||
interfaces.mGxsForums = rsGxsForums;
|
||||
interfaces.mGxsChannels = rsGxsChannels;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
9
libresapi/src/api/GetPluginInterfaces.h
Normal file
9
libresapi/src/api/GetPluginInterfaces.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class RsPlugInInterfaces;
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
bool getPluginInterfaces(RsPlugInInterfaces& interfaces);
|
||||
|
||||
} // namespace resource_api
|
6
libresapi/src/api/GxsMetaOperators.h
Normal file
6
libresapi/src/api/GxsMetaOperators.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <retroshare/rsgxsifacetypes.h>
|
||||
|
||||
// operators for rsgxsgrpmeta and rsgxsmsgmeta
|
||||
//
|
115
libresapi/src/api/GxsResponseTask.cpp
Normal file
115
libresapi/src/api/GxsResponseTask.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "GxsResponseTask.h"
|
||||
|
||||
#include "Operators.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
GxsResponseTask::GxsResponseTask(RsIdentity *id_service, RsTokenService *token_service):
|
||||
mIdService(id_service), mTokenService(token_service),
|
||||
mDone(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool GxsResponseTask::doWork(Request &req, Response &resp)
|
||||
{
|
||||
bool ready = true;
|
||||
// check if gxs requests are ready
|
||||
if(mTokenService && !mWaitingTokens.empty())
|
||||
{
|
||||
for(std::vector<uint32_t>::iterator vit = mWaitingTokens.begin(); vit != mWaitingTokens.end(); ++vit)
|
||||
{
|
||||
uint8_t status = mTokenService->requestStatus(*vit);
|
||||
if(status != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
||||
{
|
||||
ready = false;
|
||||
}
|
||||
if(status == RsTokenService::GXS_REQUEST_V2_STATUS_FAILED)
|
||||
{
|
||||
std::cerr << "GxsResponseTask::doWork() Error: token failed. aborting." << std::endl;
|
||||
resp.setFail("GxsResponseTask::doWork() Error: token failed.");
|
||||
return false; // don't continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mIdService == 0)
|
||||
{
|
||||
std::cerr << "GxsResponseTask::doWork() ERROR: constucted with idservice = 0. Fix your code or report this bug." << std::endl;
|
||||
resp.setFail("GxsResponseTask::doWork() ERROR: constucted with idservice = 0. Fix your code or report this bug.");
|
||||
return false; // don't continue
|
||||
}
|
||||
|
||||
// check if we have identities to fetch
|
||||
bool more = true;
|
||||
while(!mIdentitiesToFetch.empty() && more)
|
||||
{
|
||||
// there are two methods to fetch identity data:
|
||||
// - request gxs group, get token, get group
|
||||
// - the direct way where we may have to wait, but identities will cache the result
|
||||
// if we need to get many identuties, then we may flush the cache
|
||||
// but if we reaquest the groups, no caching is done on the rs side (OS will cache the identities file)
|
||||
// it has to be measured what is better
|
||||
RsGxsId id = mIdentitiesToFetch.back();
|
||||
RsIdentityDetails details;
|
||||
if(mIdService->getIdDetails(id, details))
|
||||
{
|
||||
mIdentitiesToFetch.pop_back();
|
||||
mIdentityDetails.push_back(details);
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false; // pause when an id failed, to give the service time tim fetch the data
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
if(!ready)
|
||||
return true; // want to continue later
|
||||
|
||||
mWaitingTokens.clear();
|
||||
mIdentitiesToFetch.clear();
|
||||
gxsDoWork(req, resp);
|
||||
|
||||
if(mDone) return false;
|
||||
else return true;
|
||||
}
|
||||
|
||||
void GxsResponseTask::addWaitingToken(uint32_t token)
|
||||
{
|
||||
if(mTokenService)
|
||||
mWaitingTokens.push_back(token);
|
||||
else
|
||||
std::cerr << "GxsResponseTask::addWaitingToken() ERROR: constructed with tokenservice=0. Unable to handle token processing. Fix your code or report this bug." << std::endl;
|
||||
}
|
||||
|
||||
void GxsResponseTask::done()
|
||||
{
|
||||
mDone = true;
|
||||
}
|
||||
|
||||
void GxsResponseTask::requestGxsId(RsGxsId id)
|
||||
{
|
||||
mIdentitiesToFetch.push_back(id);
|
||||
}
|
||||
|
||||
void GxsResponseTask::streamGxsId(RsGxsId id, StreamBase &stream)
|
||||
{
|
||||
// will see if this works or if we have to use an index
|
||||
for(std::vector<RsIdentityDetails>::iterator vit = mIdentityDetails.begin();
|
||||
vit != mIdentityDetails.end(); ++vit)
|
||||
{
|
||||
if(vit->mId == id)
|
||||
{
|
||||
stream << makeKeyValueReference("id", id)
|
||||
<< makeKeyValueReference("gxs_id", id)
|
||||
<< makeKeyValueReference("is_own", vit->mIsOwnId)
|
||||
<< makeKeyValueReference("name", vit->mNickname)
|
||||
<< makeKeyValueReference("pgp_linked", vit->mPgpLinked)
|
||||
<< makeKeyValueReference("pgp_known", vit->mPgpKnown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
50
libresapi/src/api/GxsResponseTask.h
Normal file
50
libresapi/src/api/GxsResponseTask.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "ApiTypes.h"
|
||||
#include <retroshare/rsidentity.h>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// parent class for all responses that use the gxs backend
|
||||
// this class implements the polling for gxs-tokens
|
||||
// child classes pass gxs tokens to this class
|
||||
|
||||
// question: should this class be able to handle tokens from different services?
|
||||
// then we would have to store a pointer to the token service for ever token
|
||||
class GxsResponseTask: public ResponseTask
|
||||
{
|
||||
public:
|
||||
// token service is allowed to be null if no token functions are wanted
|
||||
GxsResponseTask(RsIdentity* id_service, RsTokenService* token_service);
|
||||
virtual bool doWork(Request &req, Response& resp);
|
||||
|
||||
protected:
|
||||
// this method gets called when all the pending tokens have either status ok or fail
|
||||
// (= when the requests for these tokens are processed)
|
||||
// how will the child class find out if a request failed?
|
||||
// idea: don't call gxsDoWork() when a request failed, instead set the api response to fail
|
||||
// con: then the child class has no way to tell the outside world which request failed
|
||||
// pro: child class gets simpler, because no special error handling is required
|
||||
// implement this in a child class
|
||||
virtual void gxsDoWork(Request& req, Response& resp) = 0;
|
||||
|
||||
// call this to wait for tokens before the next call to gxsDoWork()
|
||||
void addWaitingToken(uint32_t token);
|
||||
// call this to end the task
|
||||
void done();
|
||||
// request name for gxs id
|
||||
void requestGxsId(RsGxsId id);
|
||||
// call stream function in the next cycle, then the names are available
|
||||
void streamGxsId(RsGxsId id, StreamBase& stream);
|
||||
private:
|
||||
RsIdentity* mIdService;
|
||||
RsTokenService* mTokenService;
|
||||
|
||||
std::vector<uint32_t> mWaitingTokens;
|
||||
bool mDone;
|
||||
|
||||
std::vector<RsGxsId> mIdentitiesToFetch;
|
||||
std::vector<RsIdentityDetails> mIdentityDetails;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
133
libresapi/src/api/IdentityHandler.cpp
Normal file
133
libresapi/src/api/IdentityHandler.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "IdentityHandler.h"
|
||||
|
||||
#include <retroshare/rsidentity.h>
|
||||
|
||||
#include "Operators.h"
|
||||
#include "ApiTypes.h"
|
||||
#include "GxsResponseTask.h"
|
||||
#ifndef WINDOWS_SYS
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class SendIdentitiesListTask: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
SendIdentitiesListTask(RsIdentity* idservice, std::list<RsGxsId> ids):
|
||||
GxsResponseTask(idservice, 0)
|
||||
{
|
||||
for(std::list<RsGxsId>::iterator vit = ids.begin(); vit != ids.end(); ++vit)
|
||||
{
|
||||
requestGxsId(*vit);
|
||||
mIds.push_back(*vit);// convert fro list to vector
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<RsGxsId> mIds;
|
||||
protected:
|
||||
virtual void gxsDoWork(Request &req, Response &resp)
|
||||
{
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::vector<RsGxsId>::iterator vit = mIds.begin(); vit != mIds.end(); ++vit)
|
||||
{
|
||||
streamGxsId(*vit, resp.mDataStream.getStreamToMember());
|
||||
}
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IdentityHandler::IdentityHandler(RsIdentity *identity):
|
||||
mRsIdentity(identity)
|
||||
{
|
||||
addResourceHandler("*", this, &IdentityHandler::handleWildcard);
|
||||
addResourceHandler("own", this, &IdentityHandler::handleOwn);
|
||||
}
|
||||
|
||||
void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
if(req.isPut())
|
||||
{
|
||||
RsIdentityParameters params;
|
||||
req.mStream << makeKeyValueReference("name", params.nickname);
|
||||
if(req.mStream.isOK())
|
||||
{
|
||||
uint32_t token;
|
||||
mRsIdentity->createIdentity(token, params);
|
||||
// not sure if should acknowledge the token
|
||||
// for now go the easier way
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RsTokReqOptions opts;
|
||||
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
|
||||
uint32_t token;
|
||||
mRsIdentity->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
|
||||
|
||||
time_t start = time(NULL);
|
||||
while((mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
||||
&&(mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_FAILED)
|
||||
&&((time(NULL) < (start+10)))
|
||||
)
|
||||
{
|
||||
#ifdef WINDOWS_SYS
|
||||
Sleep(500);
|
||||
#else
|
||||
usleep(500*1000) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(mRsIdentity->getTokenService()->requestStatus(token) == RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
||||
{
|
||||
std::vector<RsGxsIdGroup> grps;
|
||||
ok &= mRsIdentity->getGroupData(token, grps);
|
||||
for(std::vector<RsGxsIdGroup>::iterator vit = grps.begin(); vit != grps.end(); vit++)
|
||||
{
|
||||
RsGxsIdGroup& grp = *vit;
|
||||
KeyValueReference<RsGxsGroupId> id("id", grp.mMeta.mGroupId);
|
||||
KeyValueReference<RsPgpId> pgp_id("pgp_id",grp.mPgpId );
|
||||
// not very happy about this, i think the flags should stay hidden in rsidentities
|
||||
bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
|
||||
bool pgp_linked = (grp.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID);
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< id
|
||||
<< pgp_id
|
||||
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
|
||||
<< makeKeyValueReference("own", own)
|
||||
<< makeKeyValueReference("pgp_linked", pgp_linked);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok)
|
||||
{
|
||||
resp.setOk();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setFail();
|
||||
}
|
||||
}
|
||||
|
||||
ResponseTask* IdentityHandler::handleOwn(Request &req, Response &resp)
|
||||
{
|
||||
std::list<RsGxsId> ids;
|
||||
mRsIdentity->getOwnIds(ids);
|
||||
return new SendIdentitiesListTask(mRsIdentity, ids);
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
19
libresapi/src/api/IdentityHandler.h
Normal file
19
libresapi/src/api/IdentityHandler.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceRouter.h"
|
||||
|
||||
class RsIdentity;
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class IdentityHandler: public ResourceRouter
|
||||
{
|
||||
public:
|
||||
IdentityHandler(RsIdentity* identity);
|
||||
private:
|
||||
RsIdentity* mRsIdentity;
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
ResponseTask *handleOwn(Request& req, Response& resp);
|
||||
};
|
||||
} // namespace resource_api
|
516
libresapi/src/api/JsonStream.cpp
Normal file
516
libresapi/src/api/JsonStream.cpp
Normal file
@ -0,0 +1,516 @@
|
||||
#include "JsonStream.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
JsonStream::JsonStream():
|
||||
mSerialise(true), mDataType(TYPE_UNDEFINED), mArrayNextRead(0), mIsOk(true), mChild(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JsonStream::~JsonStream()
|
||||
{
|
||||
deleteCurrentChild();
|
||||
}
|
||||
|
||||
void JsonStream::setJsonString(std::string jsonStr)
|
||||
{
|
||||
mRawString = jsonStr;
|
||||
// have to delay the deserialisation, because this stream can also be a raw data stream without json
|
||||
// can find this out when others atucally call the operators
|
||||
mSerialise = false;
|
||||
}
|
||||
|
||||
std::string JsonStream::getJsonString()
|
||||
{
|
||||
deleteCurrentChild();
|
||||
if(mIsOk)
|
||||
{
|
||||
switch(mDataType)
|
||||
{
|
||||
case TYPE_UNDEFINED:
|
||||
return "";
|
||||
case TYPE_ARRAY:
|
||||
return json::Serialize(mArray);
|
||||
case TYPE_OBJECT:
|
||||
return json::Serialize(mObject);
|
||||
case TYPE_RAW:
|
||||
return mRawString;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
std::cerr << "JsonStream::getJsonString() Warning: stream not ok, will return empty string." << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//----------Stream Interface ---------------
|
||||
|
||||
//----------Array---------------
|
||||
StreamBase& JsonStream::operator<<(ValueReference<bool> value)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_ARRAY);
|
||||
mArray.push_back(value.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
valueToBool(mArray[mArrayNextRead], value.value);
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(ValueReference<int> value)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_ARRAY);
|
||||
mArray.push_back(value.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
valueToInt(mArray[mArrayNextRead], value.value);
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(ValueReference<double> value)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_ARRAY);
|
||||
mArray.push_back(value.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
valueToDouble(mArray[mArrayNextRead], value.value);
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(ValueReference<std::string> value)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_ARRAY);
|
||||
mArray.push_back(value.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
valueToString(mArray[mArrayNextRead], value.value);
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::getStreamToMember()
|
||||
{
|
||||
setType(TYPE_ARRAY);
|
||||
deleteCurrentChild();
|
||||
mChild = new JsonStream();
|
||||
if(!serialise())
|
||||
{
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
mChild->mValue = mArray[mArrayNextRead];
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
//----------Object---------------
|
||||
StreamBase& JsonStream::operator<<(KeyValueReference<bool> keyValue)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_OBJECT);
|
||||
mObject[keyValue.key] = keyValue.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && checkObjectMember(keyValue.key))
|
||||
{
|
||||
valueToBool(mObject[keyValue.key], keyValue.value);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(KeyValueReference<int> keyValue)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_OBJECT);
|
||||
mObject[keyValue.key] = keyValue.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && checkObjectMember(keyValue.key))
|
||||
{
|
||||
valueToInt(mObject[keyValue.key], keyValue.value);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(KeyValueReference<double> keyValue)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_OBJECT);
|
||||
mObject[keyValue.key] = keyValue.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && checkObjectMember(keyValue.key))
|
||||
{
|
||||
valueToDouble(mObject[keyValue.key], keyValue.value);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StreamBase& JsonStream::operator<<(KeyValueReference<std::string> keyValue)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
setType(TYPE_OBJECT);
|
||||
mObject[keyValue.key] = keyValue.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(checkDeserialisation() && checkObjectMember(keyValue.key))
|
||||
{
|
||||
valueToString(mObject[keyValue.key], keyValue.value);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// usefull if the new object member should be an array or object
|
||||
// the reference should be at least valid until another method of this class gets called
|
||||
StreamBase& JsonStream::getStreamToMember(std::string name)
|
||||
{
|
||||
setType(TYPE_OBJECT);
|
||||
deleteCurrentChild();
|
||||
mChildKey = name;
|
||||
mChild = new JsonStream();
|
||||
if(!serialise())
|
||||
{
|
||||
if(checkDeserialisation() && checkObjectMember(name))
|
||||
{
|
||||
mChild->mValue = mObject[name];
|
||||
}
|
||||
}
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
// make a binay data object (not a real object, just binary data)
|
||||
StreamBase& JsonStream::operator<<(std::vector<uint8_t>& data)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
if((mDataType == TYPE_UNDEFINED)||(mDataType == TYPE_RAW))
|
||||
{
|
||||
mDataType = TYPE_RAW;
|
||||
mRawString = std::string(data.begin(), data.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
mErrorLog += "Error: trying to set raw data while the type of this object is already another type\n";
|
||||
mIsOk = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((mDataType == TYPE_UNDEFINED)||(mDataType == TYPE_RAW))
|
||||
{
|
||||
mDataType = TYPE_RAW;
|
||||
data = std::vector<uint8_t>(mRawString.begin(), mRawString.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
mErrorLog += "Error: trying to read raw data while the type of this object is already another type\n";
|
||||
mIsOk = false;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// return true if there are more members in this object/array
|
||||
// useful for array reading
|
||||
bool JsonStream::hasMore()
|
||||
{
|
||||
return arrayBoundsOk();
|
||||
}
|
||||
|
||||
bool JsonStream::serialise()
|
||||
{
|
||||
return mSerialise;
|
||||
}
|
||||
|
||||
bool JsonStream::isOK()
|
||||
{
|
||||
return mIsOk;
|
||||
}
|
||||
|
||||
void JsonStream::setError()
|
||||
{
|
||||
mIsOk = false;
|
||||
}
|
||||
|
||||
/*
|
||||
void JsonStream::addLogMsg(std::string msg)
|
||||
{}
|
||||
*/
|
||||
|
||||
void JsonStream::addErrorMsg(std::string msg)
|
||||
{
|
||||
mErrorLog += msg;
|
||||
}
|
||||
|
||||
std::string JsonStream::getLog()
|
||||
{
|
||||
return "not implemented yet";
|
||||
}
|
||||
|
||||
std::string JsonStream::getErrorLog()
|
||||
{
|
||||
return mErrorLog;
|
||||
}
|
||||
|
||||
bool JsonStream::isRawData()
|
||||
{
|
||||
return mDataType == TYPE_RAW;
|
||||
}
|
||||
|
||||
std::string JsonStream::getRawData()
|
||||
{
|
||||
return mRawString;
|
||||
}
|
||||
|
||||
void JsonStream::setType(DataType type)
|
||||
{
|
||||
if((mDataType == TYPE_UNDEFINED)||(mDataType == type))
|
||||
{
|
||||
mDataType = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::setType() Error: type alread set to another type\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonStream::checkObjectMember(std::string key)
|
||||
{
|
||||
if(mDataType == TYPE_OBJECT)
|
||||
{
|
||||
if(mObject.HasKey(key))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mErrorLog += "JsonStream::checkObjectMember() Warning: missing key \""+key+"\"\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::checkObjectMember() Error: type is not TYPE_OBJECT\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonStream::arrayBoundsOk()
|
||||
{
|
||||
if(checkDeserialisation())
|
||||
{
|
||||
if(mDataType == TYPE_ARRAY)
|
||||
{
|
||||
if(mArrayNextRead < mArray.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::arrayBoundsOk() Error: type is not TYPE_ARRAY\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool JsonStream::checkDeserialisation()
|
||||
{
|
||||
if(mDataType != TYPE_RAW)
|
||||
{
|
||||
if(mDataType == TYPE_UNDEFINED)
|
||||
{
|
||||
if((mValue.GetType() == json::NULLVal) && mRawString != "")
|
||||
{
|
||||
mValue = json::Deserialize(mRawString);
|
||||
}
|
||||
if(mValue.GetType() == json::ObjectVal)
|
||||
{
|
||||
mDataType = TYPE_OBJECT;
|
||||
mObject = mValue;
|
||||
return true;
|
||||
}
|
||||
else if(mValue.GetType() == json::ArrayVal)
|
||||
{
|
||||
mDataType = TYPE_ARRAY;
|
||||
mArray = mValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::checkDeserialisation() Error: deserialisation did not end with an object or array\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// already deserialised
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::checkDeserialisation() Error: type is TYPE_RAW\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStream::valueToBool(json::Value &value, bool &boolean)
|
||||
{
|
||||
if(value.GetType() == json::BoolVal)
|
||||
{
|
||||
boolean = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::valueToBool() Error: wrong type\n";
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStream::valueToInt(json::Value &value, int &integer)
|
||||
{
|
||||
if(value.GetType() == json::IntVal)
|
||||
{
|
||||
integer = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::valueToInt() Error: wrong type\n";
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStream::valueToDouble(json::Value &value, double &doubleVal)
|
||||
{
|
||||
if(value.IsNumeric())
|
||||
{
|
||||
doubleVal = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::valueToDouble() Error: wrong type\n";
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStream::valueToString(json::Value &value, std::string& str)
|
||||
{
|
||||
if(value.GetType() == json::StringVal)
|
||||
{
|
||||
str = value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsOk = false;
|
||||
mErrorLog += "JsonStream::valueToString() Error: wrong type\n";
|
||||
}
|
||||
}
|
||||
|
||||
void JsonStream::deleteCurrentChild()
|
||||
{
|
||||
if(mChild)
|
||||
{
|
||||
if(serialise())
|
||||
{
|
||||
if(mDataType == TYPE_ARRAY)
|
||||
{
|
||||
// don't add empty value
|
||||
if(mChild->getJsonValue().GetType() != json::NULLVal)
|
||||
mArray.push_back(mChild->getJsonValue());
|
||||
}
|
||||
else if(mDataType == TYPE_OBJECT)
|
||||
{
|
||||
mObject[mChildKey] = mChild->getJsonValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
mErrorLog += "JsonStream::deleteCurrentChild() Error: cannot add child because own type is wrong\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't have to do anything for deserialisation
|
||||
}
|
||||
delete mChild;
|
||||
mChild = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
json::Value JsonStream::getJsonValue()
|
||||
{
|
||||
// remove the child and add it to own data
|
||||
deleteCurrentChild();
|
||||
switch(mDataType)
|
||||
{
|
||||
case TYPE_UNDEFINED:
|
||||
return json::Value();
|
||||
case TYPE_ARRAY:
|
||||
return mArray;
|
||||
case TYPE_OBJECT:
|
||||
return mObject;
|
||||
case TYPE_RAW:
|
||||
return mRawString;
|
||||
default:
|
||||
return json::Value();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
101
libresapi/src/api/JsonStream.h
Normal file
101
libresapi/src/api/JsonStream.h
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "ApiTypes.h"
|
||||
#include "json.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class JsonStream: public StreamBase
|
||||
{
|
||||
public:
|
||||
JsonStream();
|
||||
virtual ~JsonStream();
|
||||
|
||||
void setJsonString(std::string jsonStr);
|
||||
std::string getJsonString();
|
||||
|
||||
|
||||
//----------Stream Interface ---------------
|
||||
|
||||
// make an array
|
||||
virtual StreamBase& operator<<(ValueReference<bool> value);
|
||||
virtual StreamBase& operator<<(ValueReference<int> value);
|
||||
virtual StreamBase& operator<<(ValueReference<double> value);
|
||||
virtual StreamBase& operator<<(ValueReference<std::string> value);
|
||||
// usefull if the new array member should be an array or object
|
||||
// the reference should be at least valid until another method of this class gets called
|
||||
virtual StreamBase& getStreamToMember();
|
||||
|
||||
// make an object
|
||||
virtual StreamBase& operator<<(KeyValueReference<bool> keyValue);
|
||||
virtual StreamBase& operator<<(KeyValueReference<int> keyValue);
|
||||
virtual StreamBase& operator<<(KeyValueReference<double> keyValue);
|
||||
virtual StreamBase& operator<<(KeyValueReference<std::string> keyValue);
|
||||
// usefull if the new object member should be an array or object
|
||||
// the reference should be at least valid until another method of this class gets called
|
||||
virtual StreamBase& getStreamToMember(std::string name);
|
||||
|
||||
// make a binay data object (not a real object, just binary data)
|
||||
// idea: can use vector.swap() to allow passing larger data items without copying
|
||||
virtual StreamBase& operator<<(std::vector<uint8_t>& data);
|
||||
|
||||
// return true if there are more members in this object/array
|
||||
// useful for array reading
|
||||
virtual bool hasMore();
|
||||
|
||||
virtual bool serialise(); // let external operators find out they should serialise or deserialise
|
||||
// return true if no serialisation/deserialisation error occoured
|
||||
virtual bool isOK();
|
||||
virtual void setError(); // let external operators set the failed bit
|
||||
//virtual void addLogMsg(std::string msg); // mayb remove? (put log messages to error log einstead)
|
||||
virtual void addErrorMsg(std::string msg);
|
||||
virtual std::string getLog();
|
||||
virtual std::string getErrorLog();
|
||||
|
||||
virtual bool isRawData();
|
||||
virtual std::string getRawData();
|
||||
private:
|
||||
bool mSerialise;
|
||||
enum DataType{ TYPE_UNDEFINED, TYPE_ARRAY, TYPE_OBJECT, TYPE_RAW };
|
||||
// check if the current type is undefined
|
||||
// if not check if the new type matches the old type
|
||||
// if not set the error bit
|
||||
void setType(DataType type);
|
||||
DataType mDataType;
|
||||
|
||||
json::Value mValue;
|
||||
|
||||
json::Object mObject;
|
||||
// check if we are and object
|
||||
// check if this key exists
|
||||
bool checkObjectMember(std::string key);
|
||||
json::Array mArray;
|
||||
size_t mArrayNextRead;
|
||||
// check if we are an array
|
||||
// check if next read is valid
|
||||
// if not set error bit
|
||||
bool arrayBoundsOk();
|
||||
std::string mRawString;
|
||||
|
||||
bool mIsOk;
|
||||
std::string mErrorLog;
|
||||
|
||||
// try serialisation and set error bit on error
|
||||
bool checkDeserialisation();
|
||||
|
||||
// check if value has correct type
|
||||
// if yes return the extracted value
|
||||
// if not then set the error bit
|
||||
void valueToBool(json::Value& value, bool& boolean);
|
||||
void valueToInt(json::Value& value, int& integer);
|
||||
void valueToDouble(json::Value& value, double& doubleVal);
|
||||
void valueToString(json::Value& value, std::string& str);
|
||||
|
||||
void deleteCurrentChild();
|
||||
json::Value getJsonValue();
|
||||
JsonStream* mChild;
|
||||
std::string mChildKey;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
133
libresapi/src/api/Operators.cpp
Normal file
133
libresapi/src/api/Operators.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "Operators.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<uint32_t> ref)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
uint32_t num = ref.value;
|
||||
uint8_t digit;
|
||||
std::string str;
|
||||
while(num >= 10)
|
||||
{
|
||||
digit = num % 10;
|
||||
num = num / 10;
|
||||
str += (char)(digit + '0');
|
||||
}
|
||||
str += (char)(num + '0');
|
||||
left << makeKeyValueReference(ref.key, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
left << makeKeyValueReference(ref.key, str);
|
||||
uint32_t num = 0;
|
||||
for(std::string::iterator sit = str.begin(); sit != str.end(); sit++)
|
||||
{
|
||||
uint32_t numbefore = num;
|
||||
num = num * 10;
|
||||
if(num < numbefore)
|
||||
{
|
||||
left.addErrorMsg("operator for uint32_t to std::string: oveflow");
|
||||
left.setError();
|
||||
}
|
||||
else if((*sit)<'0' || (*sit)>'9')
|
||||
{
|
||||
left.addErrorMsg("operator for uint32_t to std::string: invalid characters");
|
||||
left.setError();
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything ok, can add value
|
||||
num += (*sit) - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
|
||||
StreamBase& operator <<(StreamBase& left, t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID>& id)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
left << id.toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
left << str;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
*/
|
||||
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID>& ref)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
left << makeValueReference(ref.value.toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
left << makeValueReference(str);
|
||||
T_ID id(str);
|
||||
if(id.isNull)
|
||||
{
|
||||
left.setError();
|
||||
left.addErrorMsg("operator for retroshare id value: id is null\n");
|
||||
}
|
||||
ref.value = id;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
// idea: have external operators which do the translation form different containers to basic operations
|
||||
// better idea: take input iterators as arguments, will then work with everything which has an iterator
|
||||
// but what about deserilisation?
|
||||
template<template <class> class ContainerT, class ValueT>
|
||||
StreamBase& operator<<(StreamBase& left, ContainerT<ValueT>& right)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
typename ContainerT<ValueT>::iterator vit;
|
||||
for(vit = right.begin(); vit != right.end(); vit++)
|
||||
{
|
||||
left << ValueReference<ValueT>(*vit);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(left.hasMore())
|
||||
{
|
||||
ValueReference<ValueT> ref;
|
||||
left << ref;
|
||||
right.push_back(ref.value);
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
// maybe like this:
|
||||
template<class ItertatorT>
|
||||
class Array
|
||||
{
|
||||
public:
|
||||
Array(ItertatorT begin, ItertatorT end): begin(begin), end(end) {}
|
||||
ItertatorT begin;
|
||||
ItertatorT end;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace resource_api
|
93
libresapi/src/api/Operators.h
Normal file
93
libresapi/src/api/Operators.h
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <retroshare/rstypes.h>
|
||||
#include "ApiTypes.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// note: pass the KeyValueReference and ValueReference objects by value to enable such things:
|
||||
// stream << somefunc(); // can't get a reference to the return value of somefunc
|
||||
|
||||
// uint32_t to std::string with decimal numbers
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<uint32_t> ref);
|
||||
|
||||
|
||||
// convert retroshare ids to strings and back
|
||||
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
|
||||
//StreamBase& operator <<(StreamBase& left, t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID>& id);
|
||||
|
||||
// operators for retroshare ids
|
||||
/*
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID>& ref);
|
||||
*/
|
||||
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<T_ID> ref);
|
||||
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID> ref);
|
||||
|
||||
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
|
||||
//StreamBase& operator <<(StreamBase& left, KeyValueReference<t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> >& ref);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// implementations
|
||||
|
||||
// idea: each rs generic id type has a number
|
||||
// put this number in front of the id data to make the ids type safe, even across languages
|
||||
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<T_ID> ref)
|
||||
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
|
||||
//StreamBase& operator <<(StreamBase& left, KeyValueReference<t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> >& ref)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
std::string idStr = ref.value.toStdString();
|
||||
left << makeKeyValueReference(ref.key, idStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
left << makeKeyValueReference(ref.key, str);
|
||||
//t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> id(str);
|
||||
T_ID id(str);
|
||||
if(id.isNull())
|
||||
{
|
||||
left.setError();
|
||||
left.addErrorMsg("operator for retroshare id keyValue: id is null\n");
|
||||
}
|
||||
ref.value = id;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
template<class T_ID>
|
||||
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID> ref)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
std::string idStr = ref.value.toStdString();
|
||||
left << makeValueReference(idStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
left << makeValueReference(str);
|
||||
T_ID id(str);
|
||||
if(id.isNull())
|
||||
{
|
||||
left.setError();
|
||||
left.addErrorMsg("operator for retroshare id Value: id is null\n");
|
||||
}
|
||||
ref.value = id;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
79
libresapi/src/api/Pagination.h
Normal file
79
libresapi/src/api/Pagination.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "ApiTypes.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
// C must be a type with STL like iterator, a begin() and an end() method
|
||||
// additionally a function id() which gives a unique value for every container element
|
||||
// the type of the id should be string
|
||||
// the type returned by dereferencing the iterator should have a stream operator for StreamBase
|
||||
// the stream operator must not add an element "id", this is done by the pagination handler
|
||||
template<class C>
|
||||
void handlePaginationRequest(Request& req, Response& resp, const C& data)
|
||||
{
|
||||
if(!req.isGet()){
|
||||
resp.mDebug << "unsupported method. only GET is allowed." << std::endl;
|
||||
resp.setFail();
|
||||
return;
|
||||
}
|
||||
if(data.begin() == data.end()){
|
||||
// set result type to list
|
||||
resp.mDataStream.getStreamToMember();
|
||||
resp.mDebug << "note: list is empty" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string first;
|
||||
std::string last;
|
||||
req.mStream << makeKeyValueReference("first", first) << makeKeyValueReference("last", last);
|
||||
|
||||
C::iterator it_first = data.begin();
|
||||
if(first != "begin")
|
||||
{
|
||||
while(it_first != data.end() && id(*it_first) != first)
|
||||
it_first++;
|
||||
if(it_first == data.end())
|
||||
{
|
||||
resp.setFail("Error: first id did not match any element");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
C::iterator it_last = data.begin();
|
||||
if(last == "end")
|
||||
{
|
||||
it_last = data.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
while(it_last != data.end() && id(*it_last) != last)
|
||||
it_last++;
|
||||
if(it_last == data.end())
|
||||
{
|
||||
resp.setFail("Error: last id did not match any element");
|
||||
return;
|
||||
}
|
||||
++it_last; // increment to get iterator to element after the last wanted element
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for(C::iterator it = it_first; it != it_last; ++it)
|
||||
{
|
||||
StreamBase& stream = resp.mDataStream.getStreamToMember();
|
||||
stream << *it;
|
||||
stream << makeKeyValue("id", id(*it));
|
||||
|
||||
// todo: also handle the case when the last element is specified and the first element is begin
|
||||
// then want to return the elements near the specified element
|
||||
count++;
|
||||
if(count > 20){
|
||||
resp.mDebug << "limited the number of returned items to 20" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
223
libresapi/src/api/PeersHandler.cpp
Normal file
223
libresapi/src/api/PeersHandler.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include "PeersHandler.h"
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <retroshare/rsmsgs.h>
|
||||
#include <util/radix64.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Operators.h"
|
||||
#include "ApiTypes.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
// todo: groups, add friend, remove friend, permissions
|
||||
|
||||
void peerDetailsToStream(StreamBase& stream, RsPeerDetails& details)
|
||||
{
|
||||
stream
|
||||
<< makeKeyValueReference("peer_id", details.id)
|
||||
<< makeKeyValueReference("name", details.name)
|
||||
<< makeKeyValueReference("location", details.location)
|
||||
<< makeKeyValueReference("pgp_id", details.gpg_id)
|
||||
;
|
||||
}
|
||||
|
||||
bool peerInfoToStream(StreamBase& stream, RsPeerDetails& details, RsPeers* peers, std::list<RsGroupInfo>& grpInfo)
|
||||
{
|
||||
bool ok = true;
|
||||
peerDetailsToStream(stream, details);
|
||||
stream << makeKeyValue("is_online", peers->isOnline(details.id));
|
||||
|
||||
std::string avatar_address = "/"+details.id.toStdString()+"/avatar_image";
|
||||
stream << makeKeyValue("avatar_address", avatar_address);
|
||||
|
||||
StreamBase& grpStream = stream.getStreamToMember("groups");
|
||||
|
||||
for(std::list<RsGroupInfo>::iterator lit = grpInfo.begin(); lit != grpInfo.end(); lit++)
|
||||
{
|
||||
RsGroupInfo& grp = *lit;
|
||||
if(std::find(grp.peerIds.begin(), grp.peerIds.end(), details.gpg_id) != grp.peerIds.end())
|
||||
{
|
||||
grpStream.getStreamToMember()
|
||||
<< makeKeyValueReference("group_name", grp.name)
|
||||
<< makeKeyValueReference("group_id", grp.id);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
PeersHandler::PeersHandler(StateTokenServer* sts, RsNotify* notify, RsPeers *peers, RsMsgs* msgs):
|
||||
mStateTokenServer(sts),
|
||||
mNotify(notify),
|
||||
mRsPeers(peers), mRsMsgs(msgs),
|
||||
mMtx("PeersHandler Mutex")
|
||||
{
|
||||
mNotify->registerNotifyClient(this);
|
||||
mStateTokenServer->registerTickClient(this);
|
||||
addResourceHandler("*", this, &PeersHandler::handleWildcard);
|
||||
addResourceHandler("examine_cert", this, &PeersHandler::handleExamineCert);
|
||||
}
|
||||
|
||||
PeersHandler::~PeersHandler()
|
||||
{
|
||||
mNotify->unregisterNotifyClient(this);
|
||||
mStateTokenServer->unregisterTickClient(this);
|
||||
}
|
||||
|
||||
void PeersHandler::notifyListChange(int list, int type)
|
||||
{
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
if(list == NOTIFY_LIST_FRIENDS)
|
||||
{
|
||||
mStateTokenServer->discardToken(mStateToken);
|
||||
mStateToken = mStateTokenServer->getNewToken();
|
||||
}
|
||||
}
|
||||
|
||||
void PeersHandler::tick()
|
||||
{
|
||||
std::list<RsPeerId> online;
|
||||
mRsPeers->getOnlineList(online);
|
||||
if(!std::equal(online.begin(), online.end(), mOnlinePeers.begin()))
|
||||
{
|
||||
mOnlinePeers = online;
|
||||
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
mStateTokenServer->discardToken(mStateToken);
|
||||
mStateToken = mStateTokenServer->getNewToken();
|
||||
}
|
||||
}
|
||||
|
||||
void PeersHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
bool ok = false;
|
||||
if(!req.mPath.empty())
|
||||
{
|
||||
std::string str = req.mPath.top();
|
||||
req.mPath.pop();
|
||||
if(str != "")
|
||||
{
|
||||
// assume the path element is a peer id
|
||||
// sometimes it is a peer id for location info
|
||||
// another time it is a pgp id
|
||||
// this will confuse the client developer
|
||||
if(!req.mPath.empty() && req.mPath.top() == "avatar_image")
|
||||
{
|
||||
// the avatar image
|
||||
// better have this extra, else have to load all avatar images
|
||||
// only to see who is online
|
||||
unsigned char *data = NULL ;
|
||||
int size = 0 ;
|
||||
mRsMsgs->getAvatarData(RsPeerId(str),data,size) ;
|
||||
std::vector<uint8_t> avatar(data, data+size);
|
||||
delete[] data;
|
||||
resp.mDataStream << avatar;
|
||||
}
|
||||
else if(!req.mPath.empty() && req.mPath.top() == "delete")
|
||||
{
|
||||
mRsPeers->removeFriend(RsPgpId(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::list<RsGroupInfo> grpInfo;
|
||||
mRsPeers->getGroupInfoList(grpInfo);
|
||||
RsPeerDetails details;
|
||||
ok &= mRsPeers->getPeerDetails(RsPeerId(str), details);
|
||||
ok = peerInfoToStream(resp.mDataStream, details, mRsPeers, grpInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more path element
|
||||
if(req.isGet())
|
||||
{
|
||||
// list all peers
|
||||
ok = true;
|
||||
std::list<RsPgpId> identities;
|
||||
ok &= mRsPeers->getGPGAcceptedList(identities);
|
||||
std::list<RsPeerId> peers;
|
||||
ok &= mRsPeers->getFriendList(peers);
|
||||
std::list<RsGroupInfo> grpInfo;
|
||||
mRsPeers->getGroupInfoList(grpInfo);
|
||||
std::vector<RsPeerDetails> detailsVec;
|
||||
for(std::list<RsPeerId>::iterator lit = peers.begin(); lit != peers.end(); ++lit)
|
||||
{
|
||||
RsPeerDetails details;
|
||||
ok &= mRsPeers->getPeerDetails(*lit, details);
|
||||
detailsVec.push_back(details);
|
||||
}
|
||||
for(std::list<RsPgpId>::iterator lit = identities.begin(); lit != identities.end(); ++lit)
|
||||
{
|
||||
StreamBase& itemStream = resp.mDataStream.getStreamToMember();
|
||||
itemStream << makeKeyValueReference("pgp_id", *lit);
|
||||
itemStream << makeKeyValue("name", mRsPeers->getGPGName(*lit));
|
||||
StreamBase& locationStream = itemStream.getStreamToMember("locations");
|
||||
for(std::vector<RsPeerDetails>::iterator vit = detailsVec.begin(); vit != detailsVec.end(); ++vit)
|
||||
{
|
||||
if(vit->gpg_id == *lit)
|
||||
peerInfoToStream(locationStream.getStreamToMember(),*vit, mRsPeers, grpInfo);
|
||||
}
|
||||
}
|
||||
resp.mStateToken = getCurrentStateToken();
|
||||
}
|
||||
else if(req.isPut())
|
||||
{
|
||||
std::string cert_string;
|
||||
req.mStream << makeKeyValueReference("cert_string", cert_string);
|
||||
RsPeerId peer_id;
|
||||
RsPgpId pgp_id;
|
||||
std::string error_string;
|
||||
if(mRsPeers->loadCertificateFromString(cert_string, peer_id, pgp_id, error_string)
|
||||
&& mRsPeers->addFriend(peer_id, pgp_id))
|
||||
{
|
||||
ok = true;
|
||||
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id);
|
||||
resp.mDataStream << makeKeyValueReference("peer_id", peer_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.mDebug << "Error: failed to add peer" << std::endl;
|
||||
resp.mDebug << error_string << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ok)
|
||||
{
|
||||
resp.setOk();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setFail();
|
||||
}
|
||||
}
|
||||
|
||||
void PeersHandler::handleExamineCert(Request &req, Response &resp)
|
||||
{
|
||||
std::string cert_string;
|
||||
req.mStream << makeKeyValueReference("cert_string", cert_string);
|
||||
RsPeerDetails details;
|
||||
uint32_t error_code;
|
||||
if(mRsPeers->loadDetailsFromStringCert(cert_string, details, error_code))
|
||||
{
|
||||
peerDetailsToStream(resp.mDataStream, details);
|
||||
resp.setOk();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setFail("failed to load certificate");
|
||||
}
|
||||
}
|
||||
|
||||
StateToken PeersHandler::getCurrentStateToken()
|
||||
{
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
if(mStateToken.isNull())
|
||||
mStateToken = mStateTokenServer->getNewToken();
|
||||
return mStateToken;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
43
libresapi/src/api/PeersHandler.h
Normal file
43
libresapi/src/api/PeersHandler.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceRouter.h"
|
||||
#include "StateTokenServer.h"
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include <util/rsthreads.h>
|
||||
|
||||
class RsPeers;
|
||||
class RsMsgs;
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class PeersHandler: public ResourceRouter, NotifyClient, Tickable
|
||||
{
|
||||
public:
|
||||
PeersHandler(StateTokenServer* sts, RsNotify* notify, RsPeers* peers, RsMsgs* msgs);
|
||||
virtual ~PeersHandler();
|
||||
|
||||
// from NotifyClient
|
||||
// note: this may get called from foreign threads
|
||||
virtual void notifyListChange(int list, int type); // friends list change
|
||||
|
||||
// from Tickable
|
||||
virtual void tick();
|
||||
private:
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
void handleExamineCert(Request& req, Response& resp);
|
||||
|
||||
// a helper which ensures proper mutex locking
|
||||
StateToken getCurrentStateToken();
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsNotify* mNotify;
|
||||
RsPeers* mRsPeers;
|
||||
RsMsgs* mRsMsgs; // required for avatar data
|
||||
|
||||
std::list<RsPeerId> mOnlinePeers;
|
||||
|
||||
RsMutex mMtx;
|
||||
StateToken mStateToken; // mutex protected
|
||||
};
|
||||
} // namespace resource_api
|
67
libresapi/src/api/README.md
Normal file
67
libresapi/src/api/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
New programming interface for Retroshare
|
||||
========================================
|
||||
|
||||
* access to libretroshare for webinterfaces, QML and scripting
|
||||
* client - server architecture.
|
||||
* network friendly: transport messages over high latency and low bandwidth networks
|
||||
* multiple clients: can use scripting and webinterface at the same time
|
||||
* simple packet format: no special serialiser required
|
||||
* simple protocol: one request creates one response. A requets does not depend on a previous request.
|
||||
* automatic state change propagation: if a resource on the server changes, the clients will get notified
|
||||
* no shared state: Client and server don't have to track what they send each other.
|
||||
* works with all programming languages
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
- Client sends a request: adress of a resource and optional parameters encoded as JSON
|
||||
{
|
||||
"method": "get",
|
||||
"resource": ["peers"],
|
||||
}
|
||||
- Server sends a Response:
|
||||
{
|
||||
"returncode": "ok",
|
||||
"statetoken": "ASDF",
|
||||
"data": [...]
|
||||
}
|
||||
|
||||
- Client asks if data is still valid
|
||||
{
|
||||
"method": "exec",
|
||||
"resource": "statetokenservice"
|
||||
"data": ["ASDF", "GHJK"]
|
||||
}
|
||||
- Server answers Client that statetoken "ASDF" expired
|
||||
{
|
||||
"returncode": "ok",
|
||||
"data": ["ASDF"]
|
||||
}
|
||||
|
||||
Transport
|
||||
---------
|
||||
|
||||
A transport protocol transports requests and responses between client and server.
|
||||
|
||||
* tracks message boundaries, so messages don't get merged
|
||||
* may be able to handle concurrent requests with out of order delivery of responses
|
||||
* knows to which request a response belongs to
|
||||
|
||||
Transport could do encryption and authentication with a standardized protocol like SSH or TLS.
|
||||
|
||||
Ideas:
|
||||
|
||||
* request id + length + request data -> SSH -> TCP -> ...
|
||||
* Websockets
|
||||
* Retroshare Items
|
||||
* Unix domain sockets
|
||||
|
||||
Currently only unencrypted http is implemented. libmicrohttpd (short MHD) is used as http server.
|
||||
Can use a proxy to add TLS encryption.
|
||||
|
||||
Message encoding
|
||||
----------------
|
||||
|
||||
Currently JSON, because it is already available in JavaScript and QML.
|
||||
Other key-value encodings could be used as well.
|
||||
Read more about basic data types of different languages (JavaScript, QML, Lua, C++) in ./ApiServer.cpp.
|
57
libresapi/src/api/ResourceRouter.cpp
Normal file
57
libresapi/src/api/ResourceRouter.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "ResourceRouter.h"
|
||||
|
||||
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class TestResource: public ResourceRouter
|
||||
{
|
||||
public:
|
||||
TestResource()
|
||||
{
|
||||
addResourceHandler("eins", this, &TestResource::eins);
|
||||
}
|
||||
ResponseTask* eins(Request& req, Response& resp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
ResourceRouter::~ResourceRouter()
|
||||
{
|
||||
std::vector<std::pair<std::string, HandlerBase*> >::iterator vit;
|
||||
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
|
||||
{
|
||||
delete vit->second;
|
||||
}
|
||||
}
|
||||
|
||||
ResponseTask* ResourceRouter::handleRequest(Request& req, Response& resp)
|
||||
{
|
||||
std::vector<std::pair<std::string, HandlerBase*> >::iterator vit;
|
||||
if(!req.mPath.empty())
|
||||
{
|
||||
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
|
||||
{
|
||||
if(vit->first == req.mPath.top())
|
||||
{
|
||||
req.mPath.pop();
|
||||
return vit->second->handleRequest(req, resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
// not found, search for wildcard handler
|
||||
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
|
||||
{
|
||||
if(vit->first == "*")
|
||||
{
|
||||
// don't pop the path component, because it may contain usefull info for the wildcard handler
|
||||
//req.mPath.pop();
|
||||
return vit->second->handleRequest(req, resp);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
76
libresapi/src/api/ResourceRouter.h
Normal file
76
libresapi/src/api/ResourceRouter.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "ApiTypes.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// a base class for routing requests to handler methods
|
||||
class ResourceRouter
|
||||
{
|
||||
public:
|
||||
virtual ~ResourceRouter();
|
||||
|
||||
// can return NULL, if the request was processed
|
||||
// if the Response can not be created immediately,
|
||||
// then return a object which implements the ResponseTask interface
|
||||
ResponseTask* handleRequest(Request& req, Response& resp);
|
||||
|
||||
template <class T>
|
||||
void addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp));
|
||||
template <class T>
|
||||
void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp));
|
||||
private:
|
||||
class HandlerBase
|
||||
{
|
||||
public:
|
||||
virtual ResponseTask* handleRequest(Request& req, Response& resp) = 0;
|
||||
};
|
||||
template <class T>
|
||||
class Handler: public HandlerBase
|
||||
{
|
||||
public:
|
||||
virtual ResponseTask* handleRequest(Request &req, Response &resp)
|
||||
{
|
||||
return (instance->*method)(req, resp);
|
||||
}
|
||||
T* instance;
|
||||
ResponseTask* (T::*method)(Request& req, Response& resp);
|
||||
};
|
||||
template <class T>
|
||||
class InstantResponseHandler: public HandlerBase
|
||||
{
|
||||
public:
|
||||
virtual ResponseTask* handleRequest(Request &req, Response &resp)
|
||||
{
|
||||
(instance->*method)(req, resp);
|
||||
return 0;
|
||||
}
|
||||
T* instance;
|
||||
void (T::*method)(Request& req, Response& resp);
|
||||
};
|
||||
|
||||
std::vector<std::pair<std::string, HandlerBase*> > mHandlers;
|
||||
};
|
||||
|
||||
// the advantage of this approach is:
|
||||
// the method name is arbitrary, one class can have many different handler methods
|
||||
// with raw objects the name of the handler method would be fixed, and we would need one class for every handler
|
||||
// the downside is complicated template magic
|
||||
template <class T>
|
||||
void ResourceRouter::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
Handler<T>* handler = new Handler<T>();
|
||||
handler->instance = instance;
|
||||
handler->method = callback;
|
||||
mHandlers.push_back(std::make_pair(name, handler));
|
||||
}
|
||||
template <class T>
|
||||
void ResourceRouter::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
InstantResponseHandler<T>* handler = new InstantResponseHandler<T>();
|
||||
handler->instance = instance;
|
||||
handler->method = callback;
|
||||
mHandlers.push_back(std::make_pair(name, handler));
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
325
libresapi/src/api/RsControlModule.cpp
Normal file
325
libresapi/src/api/RsControlModule.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
#include "RsControlModule.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <retroshare/rsinit.h>
|
||||
#include <retroshare/rsiface.h>
|
||||
|
||||
#include "api/ApiServer.h"
|
||||
#include "api/Operators.h"
|
||||
#include "api/StateTokenServer.h"
|
||||
|
||||
#include "GetPluginInterfaces.h"
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
RsControlModule::RsControlModule(int argc, char **argv, StateTokenServer* sts, ApiServer *apiserver):
|
||||
mStateTokenServer(sts),
|
||||
mApiServer(apiserver),
|
||||
mExitFlagMtx("RsControlModule::mExitFlagMtx"),
|
||||
mProcessShouldExit(false),
|
||||
mDataMtx("RsControlModule::mDataMtx"),
|
||||
mRunState(WAITING_INIT),
|
||||
mAutoLoginNextTime(false),
|
||||
mWantPassword(false)
|
||||
{
|
||||
mStateToken = sts->getNewToken();
|
||||
this->argc = argc;
|
||||
this->argv = argv;
|
||||
// start worker thread
|
||||
start();
|
||||
|
||||
addResourceHandler("runstate", this, &RsControlModule::handleRunState);
|
||||
addResourceHandler("identities", this, &RsControlModule::handleIdentities);
|
||||
addResourceHandler("locations", this, &RsControlModule::handleLocations);
|
||||
addResourceHandler("password", this, &RsControlModule::handlePassword);
|
||||
addResourceHandler("login", this, &RsControlModule::handleLogin);
|
||||
addResourceHandler("shutdown", this, &RsControlModule::handleShutdown);
|
||||
}
|
||||
|
||||
RsControlModule::~RsControlModule()
|
||||
{
|
||||
join();
|
||||
}
|
||||
|
||||
bool RsControlModule::processShouldExit()
|
||||
{
|
||||
RsStackMutex stack(mExitFlagMtx);
|
||||
return mProcessShouldExit;
|
||||
}
|
||||
|
||||
bool RsControlModule::askForPassword(const std::string &key_details, bool prev_is_bad, std::string &password)
|
||||
{
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
mWantPassword = true;
|
||||
mKeyName = key_details;
|
||||
mPassword = "";
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
}
|
||||
|
||||
bool wait = true;
|
||||
while(wait)
|
||||
{
|
||||
usleep(5*1000);
|
||||
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
wait = mWantPassword;
|
||||
if(!wait && mPassword != "")
|
||||
{
|
||||
password = mPassword;
|
||||
mPassword = "";
|
||||
mWantPassword = false;
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RsControlModule::run()
|
||||
{
|
||||
std::cerr << "RsControlModule: initialising libretroshare..." << std::endl;
|
||||
|
||||
RsInit::InitRsConfig();
|
||||
int initResult = RsInit::InitRetroShare(argc, argv, true);
|
||||
|
||||
if (initResult < 0) {
|
||||
std::cerr << "RsControlModule: FATAL ERROR, initialising libretroshare FAILED." << std::endl;
|
||||
/* Error occured */
|
||||
std::stringstream ss;
|
||||
switch (initResult) {
|
||||
case RS_INIT_AUTH_FAILED:
|
||||
ss << "RsInit::InitRetroShare AuthGPG::InitAuth failed" << std::endl;
|
||||
break;
|
||||
default:
|
||||
/* Unexpected return code */
|
||||
ss << "RsInit::InitRetroShare unexpected return code " << initResult << std::endl;
|
||||
break;
|
||||
}
|
||||
// FATAL ERROR, we can't recover from this. Just send the message to the user.
|
||||
setRunState(FATAL_ERROR, ss.str());
|
||||
return;
|
||||
}
|
||||
|
||||
// This is needed to allocate rsNotify, so that it can be used to ask for PGP passphrase
|
||||
RsControl::earlyInitNotificationSystem();
|
||||
rsNotify->registerNotifyClient(this);
|
||||
|
||||
bool login_ok = false;
|
||||
while(!login_ok)
|
||||
{
|
||||
// skip account selection if autologin is available
|
||||
if(initResult != RS_INIT_HAVE_ACCOUNT)
|
||||
setRunState(WAITING_ACCOUNT_SELECT);
|
||||
|
||||
// wait for login request
|
||||
bool auto_login = false;
|
||||
bool wait_for_account_select = (initResult != RS_INIT_HAVE_ACCOUNT);
|
||||
while(wait_for_account_select && !processShouldExit())
|
||||
{
|
||||
usleep(5*1000);
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
wait_for_account_select = mLoadPeerId.isNull();
|
||||
auto_login = mAutoLoginNextTime;
|
||||
if(!wait_for_account_select)
|
||||
{
|
||||
wait_for_account_select = !RsAccounts::SelectAccount(mLoadPeerId);
|
||||
if(wait_for_account_select)
|
||||
setRunState(WAITING_ACCOUNT_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
if(processShouldExit())
|
||||
return;
|
||||
|
||||
bool autoLogin = (initResult == RS_INIT_HAVE_ACCOUNT) | auto_login;
|
||||
std::string lockFile;
|
||||
int retVal = RsInit::LockAndLoadCertificates(autoLogin, lockFile);
|
||||
|
||||
std::string error_string;
|
||||
switch (retVal) {
|
||||
case 0:
|
||||
login_ok = true;
|
||||
break;
|
||||
case 1:
|
||||
error_string = "Another RetroShare using the same profile is "
|
||||
"already running on your system. Please close "
|
||||
"that instance first\n Lock file:\n" + lockFile;
|
||||
break;
|
||||
case 2:
|
||||
error_string = "An unexpected error occurred when Retroshare "
|
||||
"tried to acquire the single instance lock\n Lock file:\n"
|
||||
+ lockFile;
|
||||
break;
|
||||
case 3:
|
||||
error_string = "Login Failure: Maybe password is wrong";
|
||||
break;
|
||||
default:
|
||||
std::cerr << "RsControlModule::run() LockAndLoadCertificates failed. Unexpected switch value: " << retVal << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setRunState(WAITING_STARTUP);
|
||||
|
||||
std::cerr << "RsControlModule: login ok, starting Retroshare worker threads..." << std::endl;
|
||||
RsControl::instance() -> StartupRetroShare();
|
||||
|
||||
std::cerr << "RsControlModule: loading main resource api modules..." << std::endl;
|
||||
RsPlugInInterfaces ifaces;
|
||||
getPluginInterfaces(ifaces);
|
||||
mApiServer->loadMainModules(ifaces);
|
||||
|
||||
std::cerr << "RsControlModule: Retroshare is up and running. Enjoy!" << std::endl;
|
||||
setRunState(RUNNING_OK);
|
||||
|
||||
while(!processShouldExit())
|
||||
{
|
||||
usleep(5*1000);
|
||||
}
|
||||
|
||||
std::cerr << "RsControlModule: stopping Retroshare..." << std::endl;
|
||||
RsControl::instance() -> rsGlobalShutDown();
|
||||
std::cerr << "RsControlModule: Retroshare stopped. Bye!" << std::endl;
|
||||
}
|
||||
|
||||
void RsControlModule::handleRunState(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
std::string state;
|
||||
switch(mRunState)
|
||||
{
|
||||
case WAITING_INIT:
|
||||
state = "waiting_init";
|
||||
break;
|
||||
case FATAL_ERROR:
|
||||
state = "fatal_error";
|
||||
break;
|
||||
case WAITING_ACCOUNT_SELECT:
|
||||
state = "waiting_account_select";
|
||||
break;
|
||||
case WAITING_STARTUP:
|
||||
state = "waiting_startup";
|
||||
break;
|
||||
case RUNNING_OK:
|
||||
state = "running_ok";
|
||||
break;
|
||||
default:
|
||||
state = "error_should_not_happen_this_is_a_bug";
|
||||
}
|
||||
resp.mDataStream << makeKeyValueReference("runstate", state);
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::handleIdentities(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
|
||||
{
|
||||
resp.setFail("Retroshare is not initialised. Operation not possible.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::list<RsPgpId> pgp_ids;
|
||||
RsAccounts::GetPGPLogins(pgp_ids);
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::list<RsPgpId>::iterator lit = pgp_ids.begin(); lit != pgp_ids.end(); ++lit)
|
||||
{
|
||||
std::string name;
|
||||
std::string email;
|
||||
if(RsAccounts::GetPGPLoginDetails(*lit, name, email))
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< makeKeyValueReference("id", *lit)
|
||||
<< makeKeyValueReference("pgp_id", *lit)
|
||||
<< makeKeyValueReference("name", name);
|
||||
}
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::handleLocations(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
|
||||
{
|
||||
resp.setFail("Retroshare is not initialised. Operation not possible.");
|
||||
return;
|
||||
}
|
||||
|
||||
RsPeerId preferedId;
|
||||
RsAccounts::GetPreferredAccountId(preferedId);
|
||||
|
||||
std::list<RsPeerId> peer_ids;
|
||||
RsAccounts::GetAccountIds(peer_ids);
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::list<RsPeerId>::iterator lit = peer_ids.begin(); lit != peer_ids.end(); ++lit)
|
||||
{
|
||||
bool preferred = preferedId==*lit;
|
||||
RsPgpId pgp_id;
|
||||
std::string pgp_name, pgp_mail, location_name;
|
||||
if(RsAccounts::GetAccountDetails(*lit, pgp_id, pgp_name, pgp_mail, location_name))
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< makeKeyValueReference("id", *lit)
|
||||
<< makeKeyValueReference("pgp_id", pgp_id)
|
||||
<< makeKeyValueReference("peer_id", *lit)
|
||||
<< makeKeyValueReference("name", pgp_name)
|
||||
<< makeKeyValueReference("location", location_name)
|
||||
<< makeKeyValueReference("preferred", preferred);
|
||||
}
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::handlePassword(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
std::string passwd;
|
||||
req.mStream << makeKeyValueReference("password", passwd);
|
||||
if(passwd != "" && mWantPassword)
|
||||
{
|
||||
// client sends password
|
||||
mPassword = passwd;
|
||||
mWantPassword = false;
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
}
|
||||
|
||||
resp.mDataStream
|
||||
<< makeKeyValueReference("want_password", mWantPassword)
|
||||
<< makeKeyValueReference("key_name", mKeyName);
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::handleLogin(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
if(mRunState != WAITING_ACCOUNT_SELECT)
|
||||
{
|
||||
resp.setFail("Operation not allowed in this runstate. Login is only allowed rigth after initialisation.");
|
||||
return;
|
||||
}
|
||||
req.mStream << makeKeyValueReference("id", mLoadPeerId)
|
||||
<< makeKeyValueReference("autologin", mAutoLoginNextTime);
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::handleShutdown(Request &req, Response &resp)
|
||||
{
|
||||
RsStackMutex stack(mExitFlagMtx); // ********** LOCKED **********
|
||||
mProcessShouldExit = true;
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void RsControlModule::setRunState(RunState s, std::string errstr)
|
||||
{
|
||||
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
|
||||
mRunState = s;
|
||||
mLastErrorString = errstr;
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
}
|
||||
|
||||
|
||||
} // namespace resource_api
|
80
libresapi/src/api/RsControlModule.h
Normal file
80
libresapi/src/api/RsControlModule.h
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <util/rsthreads.h>
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include "api/ResourceRouter.h"
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
class StateTokenServer;
|
||||
class ApiServer;
|
||||
|
||||
// resource api module to control accounts, startup and shutdown of retroshare
|
||||
// - this module handles everything, not things are required from outside
|
||||
// - exception: users of this module have to create an api server and register this module
|
||||
// tasks:
|
||||
// - show, import, export and create private pgp keys
|
||||
// - show existing and create new locations
|
||||
// - load certificate, startup retroshare
|
||||
// - handle password callback
|
||||
// - confirm plugin loading
|
||||
// - shutdown retroshare
|
||||
class RsControlModule: public ResourceRouter, NotifyClient,
|
||||
private RsThread
|
||||
{
|
||||
public:
|
||||
// ApiServer will be called once RS is started, to load additional api modules
|
||||
RsControlModule(int argc, char **argv, StateTokenServer* sts, ApiServer* apiserver);
|
||||
~RsControlModule();
|
||||
|
||||
// returns true if the process should terminate
|
||||
bool processShouldExit();
|
||||
|
||||
// from NotifyClient
|
||||
virtual bool askForPassword(const std::string& key_details, bool prev_is_bad , std::string& password);
|
||||
|
||||
protected:
|
||||
// from RsThread
|
||||
// wee need a thread to call into things which block like askForPassword()
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
enum RunState { WAITING_INIT, FATAL_ERROR, WAITING_ACCOUNT_SELECT, WAITING_STARTUP, RUNNING_OK};
|
||||
void handleRunState(Request& req, Response& resp);
|
||||
void handleIdentities(Request& req, Response& resp);
|
||||
void handleLocations(Request& req, Response& resp);
|
||||
void handlePassword(Request& req, Response& resp);
|
||||
void handleLogin(Request& req, Response& resp);
|
||||
void handleShutdown(Request& req, Response& resp);
|
||||
|
||||
void setRunState(RunState s, std::string errstr = "");
|
||||
// for startup
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
StateTokenServer* const mStateTokenServer;
|
||||
ApiServer* const mApiServer;
|
||||
|
||||
RsMutex mExitFlagMtx;
|
||||
bool mProcessShouldExit;
|
||||
|
||||
RsMutex mDataMtx;
|
||||
|
||||
StateToken mStateToken; // one state token for everything, to make life easier
|
||||
|
||||
RunState mRunState;
|
||||
std::string mLastErrorString;
|
||||
|
||||
// id of the account to load
|
||||
// null when no account was selected
|
||||
RsPeerId mLoadPeerId;
|
||||
bool mAutoLoginNextTime;
|
||||
|
||||
// to notify that a password callback is waiting
|
||||
// to answer the request, clear the flag and set the password
|
||||
bool mWantPassword;
|
||||
std::string mKeyName;
|
||||
std::string mPassword;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
89
libresapi/src/api/ServiceControlHandler.cpp
Normal file
89
libresapi/src/api/ServiceControlHandler.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "ServiceControlHandler.h"
|
||||
|
||||
#include "retroshare/rsservicecontrol.h"
|
||||
|
||||
#include "Operators.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// maybe move to another place later
|
||||
// need more generic operators for list, vector, map
|
||||
template<class T>
|
||||
void setToStream(StreamBase& stream, std::set<T>& set)
|
||||
{
|
||||
if(stream.serialise())
|
||||
{
|
||||
for(typename std::set<T>::iterator sit = set.begin(); sit != set.end(); sit++)
|
||||
{
|
||||
T item = *sit;
|
||||
stream << makeValueReference(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(stream.hasMore())
|
||||
{
|
||||
T item;
|
||||
stream << makeValueReference(item);
|
||||
set.insert(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void servicePermissionToStream(StreamBase& stream, RsServicePermissions& perm)
|
||||
{
|
||||
stream << makeKeyValueReference("service_id", perm.mServiceId)
|
||||
<< makeKeyValueReference("service_name", perm.mServiceName)
|
||||
<< makeKeyValueReference("default_allowed", perm.mDefaultAllowed)
|
||||
;
|
||||
setToStream(stream.getStreamToMember("peers_allowed"), perm.mPeersAllowed);
|
||||
setToStream(stream.getStreamToMember("peers_denied"), perm.mPeersDenied);
|
||||
}
|
||||
|
||||
ServiceControlHandler::ServiceControlHandler(RsServiceControl* control):
|
||||
mRsServiceControl(control)
|
||||
{
|
||||
addResourceHandler("*", this, &ServiceControlHandler::handleWildcard);
|
||||
}
|
||||
|
||||
void ServiceControlHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
bool ok = false;
|
||||
if(!req.mPath.empty())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more path element
|
||||
if(req.isGet())
|
||||
{
|
||||
// list all peers
|
||||
ok = true;
|
||||
RsPeerServiceInfo psi;
|
||||
ok &= mRsServiceControl->getOwnServices(psi);
|
||||
for(std::map<uint32_t, RsServiceInfo>::iterator mit = psi.mServiceList.begin(); mit != psi.mServiceList.end(); mit++)
|
||||
{
|
||||
RsServicePermissions perms;
|
||||
ok &= mRsServiceControl->getServicePermissions(mit->first, perms);
|
||||
if(ok)
|
||||
{
|
||||
servicePermissionToStream(resp.mDataStream.getStreamToMember(), perms);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(req.isPut())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
if(ok)
|
||||
{
|
||||
resp.setOk();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setFail();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
20
libresapi/src/api/ServiceControlHandler.h
Normal file
20
libresapi/src/api/ServiceControlHandler.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "ApiTypes.h"
|
||||
#include "ResourceRouter.h"
|
||||
|
||||
class RsServiceControl;
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class ServiceControlHandler: public ResourceRouter
|
||||
{
|
||||
public:
|
||||
ServiceControlHandler(RsServiceControl* control);
|
||||
|
||||
private:
|
||||
RsServiceControl* mRsServiceControl;
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
};
|
||||
} // namespace resource_api
|
150
libresapi/src/api/StateTokenServer.cpp
Normal file
150
libresapi/src/api/StateTokenServer.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
#include "StateTokenServer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
// maybe it would be good to make this part of state token or friend, to be able to directly access the value
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<StateToken> kv)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
// have to make a variable, to be able to pass it by reference
|
||||
// (cant pass return value of a function by refernce to another function)
|
||||
int value = kv.value.getValue();
|
||||
left << makeKeyValueReference(kv.key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
int value;
|
||||
left << makeKeyValueReference(kv.key, value);
|
||||
kv.value = StateToken(value);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
StreamBase& operator<<(StreamBase& left, StateToken& token)
|
||||
{
|
||||
if(left.serialise())
|
||||
{
|
||||
// have to make a variable, to be able to pass it by reference
|
||||
// (cant pass return value of a function by refernce to another function)
|
||||
int value = token.getValue();
|
||||
left << value;
|
||||
}
|
||||
else
|
||||
{
|
||||
int value;
|
||||
left << value;
|
||||
token = StateToken(value);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
bool operator==(const StateToken& left, const StateToken& right)
|
||||
{
|
||||
if(left.getValue() == right.getValue())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
StateTokenServer::StateTokenServer():
|
||||
mMtx("StateTokenServer mMtx"),
|
||||
mNextToken(1),
|
||||
mClientsMtx("StateTokenServer mClientsMtx")
|
||||
{
|
||||
addResourceHandler("*", this, &StateTokenServer::handleWildcard);
|
||||
}
|
||||
|
||||
StateToken StateTokenServer::getNewToken()
|
||||
{
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
return locked_getNewToken();
|
||||
}
|
||||
|
||||
void StateTokenServer::discardToken(StateToken token)
|
||||
{
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
locked_discardToken(token);
|
||||
}
|
||||
|
||||
void StateTokenServer::replaceToken(StateToken &token)
|
||||
{
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
locked_discardToken(token);
|
||||
token = locked_getNewToken();
|
||||
}
|
||||
|
||||
void StateTokenServer::registerTickClient(Tickable *c)
|
||||
{
|
||||
// extra service: tick it to let it init its ticking stuff
|
||||
c->tick();
|
||||
|
||||
// avoid double registration
|
||||
unregisterTickClient(c);
|
||||
|
||||
RsStackMutex stack(mClientsMtx); /********** STACK LOCKED MTX ***********/
|
||||
mTickClients.push_back(c);
|
||||
}
|
||||
|
||||
void StateTokenServer::unregisterTickClient(Tickable *c)
|
||||
{
|
||||
RsStackMutex stack(mClientsMtx); /********** STACK LOCKED MTX ***********/
|
||||
std::vector<Tickable*>::iterator vit = std::find(mTickClients.begin(), mTickClients.end(), c);
|
||||
if(vit != mTickClients.end())
|
||||
mTickClients.erase(vit);
|
||||
}
|
||||
|
||||
void StateTokenServer::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
{
|
||||
RsStackMutex stack(mClientsMtx); /********** STACK LOCKED MTX ***********/
|
||||
for(std::vector<Tickable*>::iterator vit = mTickClients.begin(); vit != mTickClients.end(); ++vit)
|
||||
{
|
||||
(*vit)->tick();
|
||||
}
|
||||
}
|
||||
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
// want to lookpup many tokens at once, return a list of invalid tokens
|
||||
// TODO: make generic list serialiser/deserialiser
|
||||
while(req.mStream.hasMore())
|
||||
{
|
||||
StateToken token;
|
||||
req.mStream << token;
|
||||
// lookup if token is valid
|
||||
if(std::find(mValidTokens.begin(), mValidTokens.end(), token) == mValidTokens.end())
|
||||
{
|
||||
// if invalid, add to response list
|
||||
resp.mDataStream << token;
|
||||
}
|
||||
}
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
StateToken StateTokenServer::locked_getNewToken()
|
||||
{
|
||||
StateToken token(mNextToken);
|
||||
mValidTokens.push_back(token);
|
||||
mNextToken++;
|
||||
if(mNextToken == 0) // 0 is a reserved value, don't ever use it
|
||||
mNextToken = 1;
|
||||
return token;
|
||||
}
|
||||
|
||||
void StateTokenServer::locked_discardToken(StateToken token)
|
||||
{
|
||||
std::vector<StateToken>::iterator toDelete = std::find(mValidTokens.begin(), mValidTokens.end(), token);
|
||||
if(toDelete != mValidTokens.end())
|
||||
{
|
||||
mValidTokens.erase(toDelete);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
69
libresapi/src/api/StateTokenServer.h
Normal file
69
libresapi/src/api/StateTokenServer.h
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <util/rsthreads.h>
|
||||
#include "ResourceRouter.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
//class StreamBase;
|
||||
//class StateToken;
|
||||
|
||||
// serialiser/deserialiser (depends on stream type)
|
||||
// for single value
|
||||
StreamBase& operator <<(StreamBase& left, KeyValueReference<StateToken> kv);
|
||||
// for lists
|
||||
StreamBase& operator <<(StreamBase& left, StateToken& token);
|
||||
bool operator ==(const StateToken& left, const StateToken& right);
|
||||
|
||||
class Tickable{
|
||||
public:
|
||||
virtual void tick() = 0;
|
||||
};
|
||||
|
||||
|
||||
class StateTokenServer: public ResourceRouter
|
||||
{
|
||||
public:
|
||||
StateTokenServer();
|
||||
|
||||
// thread safe
|
||||
// this allows tokens to be created and destroyed from arbitrary threads
|
||||
StateToken getNewToken();
|
||||
void discardToken(StateToken token);
|
||||
// discard the token and fill in a new one
|
||||
void replaceToken(StateToken& token);
|
||||
|
||||
void registerTickClient(Tickable* c);
|
||||
void unregisterTickClient(Tickable* c);
|
||||
|
||||
private:
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
|
||||
StateToken locked_getNewToken();
|
||||
void locked_discardToken(StateToken token);
|
||||
|
||||
RsMutex mMtx;
|
||||
|
||||
uint32_t mNextToken;
|
||||
// not sure what the most efficient data structure for simple token storage is
|
||||
// have to:
|
||||
// - add elements
|
||||
// - remove elements by value
|
||||
// - store many values
|
||||
// vector: expensive token erase, could make this better with a invalidate flag
|
||||
// and periodic cleanup
|
||||
// list: lots of overhead for pointers
|
||||
// a set would offer cheap lookup
|
||||
// we have to lookup often, so maybe this would be an option
|
||||
// have to see where the bottleneck is in practice
|
||||
// idea: invalidate all tokens after x minutes/hours, to limit the range of the token values
|
||||
// then store the token states in a circular bitbuffer
|
||||
std::vector<StateToken> mValidTokens;
|
||||
|
||||
// classes which want to be ticked
|
||||
RsMutex mClientsMtx; // needs extra mutex, because clients may call back to modify get/delete tokens
|
||||
std::vector<Tickable*> mTickClients;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
177
libresapi/src/api/TransfersHandler.cpp
Normal file
177
libresapi/src/api/TransfersHandler.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
#include "TransfersHandler.h"
|
||||
#include "Operators.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
TransfersHandler::TransfersHandler(StateTokenServer *sts, RsFiles *files):
|
||||
mStateTokenServer(sts), mFiles(files), mLastUpdateTS(0)
|
||||
{
|
||||
addResourceHandler("*", this, &TransfersHandler::handleWildcard);
|
||||
addResourceHandler("downloads", this, &TransfersHandler::handleDownloads);
|
||||
addResourceHandler("control_download", this, &TransfersHandler::handleControlDownload);
|
||||
mStateToken = mStateTokenServer->getNewToken();
|
||||
mStateTokenServer->registerTickClient(this);
|
||||
}
|
||||
|
||||
TransfersHandler::~TransfersHandler()
|
||||
{
|
||||
mStateTokenServer->unregisterTickClient(this);
|
||||
}
|
||||
|
||||
const int UPDATE_PERIOD_SECONDS = 5;
|
||||
|
||||
void TransfersHandler::tick()
|
||||
{
|
||||
if(time(0) > (mLastUpdateTS + UPDATE_PERIOD_SECONDS))
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
|
||||
// extra check: was the list of files changed?
|
||||
// if yes, replace state token immediately
|
||||
std::list<RsFileHash> dls;
|
||||
mFiles->FileDownloads(dls);
|
||||
// there is no guarantee of the order
|
||||
// so have to sort before comparing the lists
|
||||
dls.sort();
|
||||
if(!std::equal(dls.begin(), dls.end(), mDownloadsAtLastCheck.begin()))
|
||||
{
|
||||
mDownloadsAtLastCheck.swap(dls);
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
}
|
||||
}
|
||||
|
||||
void TransfersHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TransfersHandler::handleControlDownload(Request &req, Response &resp)
|
||||
{
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
RsFileHash hash;
|
||||
std::string action;
|
||||
req.mStream << makeKeyValueReference("action", action);
|
||||
if(action == "begin")
|
||||
{
|
||||
std::string fname;
|
||||
double size;
|
||||
req.mStream << makeKeyValueReference("name", fname);
|
||||
req.mStream << makeKeyValueReference("size", size);
|
||||
req.mStream << makeKeyValueReference("hash", hash);
|
||||
std::list<RsPeerId> scrIds;
|
||||
bool ok = req.mStream.isOK();
|
||||
if(ok)
|
||||
ok = mFiles->FileRequest(fname, hash, size, "", RS_FILE_REQ_ANONYMOUS_ROUTING, scrIds);
|
||||
if(ok)
|
||||
resp.setOk();
|
||||
else
|
||||
resp.setFail("something went wrong. are all fields filled in? is the file already downloaded?");
|
||||
return;
|
||||
}
|
||||
|
||||
req.mStream << makeKeyValueReference("id", hash);
|
||||
if(!req.mStream.isOK())
|
||||
{
|
||||
resp.setFail("error: could not deserialise the request");
|
||||
return;
|
||||
}
|
||||
bool ok = false;
|
||||
bool handled = false;
|
||||
if(action == "pause")
|
||||
{
|
||||
handled = true;
|
||||
ok = mFiles->FileControl(hash, RS_FILE_CTRL_PAUSE);
|
||||
}
|
||||
if(action == "start")
|
||||
{
|
||||
handled = true;
|
||||
ok = mFiles->FileControl(hash, RS_FILE_CTRL_START);
|
||||
}
|
||||
if(action == "check")
|
||||
{
|
||||
handled = true;
|
||||
ok = mFiles->FileControl(hash, RS_FILE_CTRL_FORCE_CHECK);
|
||||
}
|
||||
if(action == "cancel")
|
||||
{
|
||||
handled = true;
|
||||
ok = mFiles->FileCancel(hash);
|
||||
}
|
||||
if(ok)
|
||||
resp.setOk();
|
||||
else
|
||||
resp.setFail("something went wrong. not sure what or why.");
|
||||
if(handled)
|
||||
return;
|
||||
resp.setFail("error: action not handled");
|
||||
}
|
||||
|
||||
void TransfersHandler::handleDownloads(Request &req, Response &resp)
|
||||
{
|
||||
tick();
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::list<RsFileHash>::iterator lit = mDownloadsAtLastCheck.begin();
|
||||
lit != mDownloadsAtLastCheck.end(); ++lit)
|
||||
{
|
||||
FileInfo fi;
|
||||
if(mFiles->FileDetails(*lit, RS_FILE_HINTS_DOWNLOAD, fi))
|
||||
{
|
||||
StreamBase& stream = resp.mDataStream.getStreamToMember();
|
||||
stream << makeKeyValueReference("id", fi.hash)
|
||||
<< makeKeyValueReference("hash", fi.hash)
|
||||
<< makeKeyValueReference("name", fi.fname);
|
||||
double size = fi.size;
|
||||
double transfered = fi.transfered;
|
||||
stream << makeKeyValueReference("size", size)
|
||||
<< makeKeyValueReference("transfered", transfered);
|
||||
|
||||
std::string dl_status;
|
||||
/*
|
||||
const uint32_t FT_STATE_FAILED = 0x0000 ;
|
||||
const uint32_t FT_STATE_OKAY = 0x0001 ;
|
||||
const uint32_t FT_STATE_WAITING = 0x0002 ;
|
||||
const uint32_t FT_STATE_DOWNLOADING = 0x0003 ;
|
||||
const uint32_t FT_STATE_COMPLETE = 0x0004 ;
|
||||
const uint32_t FT_STATE_QUEUED = 0x0005 ;
|
||||
const uint32_t FT_STATE_PAUSED = 0x0006 ;
|
||||
const uint32_t FT_STATE_CHECKING_HASH = 0x0007 ;
|
||||
*/
|
||||
switch(fi.downloadStatus)
|
||||
{
|
||||
case FT_STATE_FAILED:
|
||||
dl_status = "failed";
|
||||
break;
|
||||
case FT_STATE_OKAY:
|
||||
dl_status = "okay";
|
||||
break;
|
||||
case FT_STATE_WAITING:
|
||||
dl_status = "waiting";
|
||||
break;
|
||||
case FT_STATE_DOWNLOADING:
|
||||
dl_status = "downloading";
|
||||
break;
|
||||
case FT_STATE_COMPLETE:
|
||||
dl_status = "complete";
|
||||
break;
|
||||
case FT_STATE_QUEUED:
|
||||
dl_status = "queued";
|
||||
break;
|
||||
case FT_STATE_PAUSED:
|
||||
dl_status = "paused";
|
||||
break;
|
||||
case FT_STATE_CHECKING_HASH:
|
||||
dl_status = "checking";
|
||||
break;
|
||||
default:
|
||||
dl_status = "error_unknown";
|
||||
}
|
||||
|
||||
stream << makeKeyValueReference("download_status", dl_status);
|
||||
}
|
||||
}
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
34
libresapi/src/api/TransfersHandler.h
Normal file
34
libresapi/src/api/TransfersHandler.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
#include "ResourceRouter.h"
|
||||
#include "StateTokenServer.h"
|
||||
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class TransfersHandler: public ResourceRouter, Tickable
|
||||
{
|
||||
public:
|
||||
TransfersHandler(StateTokenServer* sts, RsFiles* files);
|
||||
virtual ~TransfersHandler();
|
||||
|
||||
// from Tickable
|
||||
virtual void tick();
|
||||
private:
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
void handleControlDownload(Request& req, Response& resp);
|
||||
void handleDownloads(Request& req, Response& resp);
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsFiles* mFiles;
|
||||
|
||||
StateToken mStateToken;
|
||||
time_t mLastUpdateTS;
|
||||
|
||||
std::list<RsFileHash> mDownloadsAtLastCheck;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
813
libresapi/src/api/json.cpp
Normal file
813
libresapi/src/api/json.cpp
Normal file
@ -0,0 +1,813 @@
|
||||
#include "json.h"
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <climits>
|
||||
#include <string.h>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <stack>
|
||||
|
||||
#ifndef WIN32
|
||||
#define _stricmp strcasecmp
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf sprintf_s
|
||||
#endif
|
||||
|
||||
using namespace json;
|
||||
|
||||
namespace json
|
||||
{
|
||||
enum StackDepthType
|
||||
{
|
||||
InObject,
|
||||
InArray
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static std::string Trim(const std::string& str)
|
||||
{
|
||||
std::string s = str;
|
||||
|
||||
// remove white space in front
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
|
||||
|
||||
// remove trailing white space
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// Finds the position of the first " character that is NOT preceeded immediately by a \ character.
|
||||
// In JSON, \" is valid and has a different meaning than the escaped " character.
|
||||
static size_t GetQuotePos(const std::string& str, size_t start_pos = 0)
|
||||
{
|
||||
bool found_slash = false;
|
||||
for (size_t i = start_pos; i < str.length(); i++)
|
||||
{
|
||||
char c = str[i];
|
||||
if ((c == '\\') && !found_slash)
|
||||
{
|
||||
found_slash = true;
|
||||
continue;
|
||||
}
|
||||
else if ((c == '\"') && !found_slash)
|
||||
return i;
|
||||
|
||||
found_slash = false;
|
||||
}
|
||||
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Value::Value(const Value& v) : mValueType(v.mValueType)
|
||||
{
|
||||
switch (mValueType)
|
||||
{
|
||||
case StringVal : mStringVal = v.mStringVal; break;
|
||||
case IntVal : mIntVal = v.mIntVal; mFloatVal = (float)v.mIntVal; mDoubleVal = (double)v.mIntVal; break;
|
||||
case FloatVal : mFloatVal = v.mFloatVal; mIntVal = (int)v.mFloatVal; mDoubleVal = (double)v.mDoubleVal; break;
|
||||
case DoubleVal : mDoubleVal = v.mDoubleVal; mIntVal = (int)v.mDoubleVal; mFloatVal = (float)v.mDoubleVal; break;
|
||||
case BoolVal : mBoolVal = v.mBoolVal; break;
|
||||
case ObjectVal : mObjectVal = v.mObjectVal; break;
|
||||
case ArrayVal : mArrayVal = v.mArrayVal; break;
|
||||
default : break;
|
||||
}
|
||||
}
|
||||
|
||||
Value& Value::operator =(const Value& v)
|
||||
{
|
||||
if (&v == this)
|
||||
return *this;
|
||||
|
||||
mValueType = v.mValueType;
|
||||
|
||||
switch (mValueType)
|
||||
{
|
||||
case StringVal : mStringVal = v.mStringVal; break;
|
||||
case IntVal : mIntVal = v.mIntVal; mFloatVal = (float)v.mIntVal; mDoubleVal = (double)v.mIntVal; break;
|
||||
case FloatVal : mFloatVal = v.mFloatVal; mIntVal = (int)v.mFloatVal; mDoubleVal = (double)v.mDoubleVal; break;
|
||||
case DoubleVal : mDoubleVal = v.mDoubleVal; mIntVal = (int)v.mDoubleVal; mFloatVal = (float)v.mDoubleVal; break;
|
||||
case BoolVal : mBoolVal = v.mBoolVal; break;
|
||||
case ObjectVal : mObjectVal = v.mObjectVal; break;
|
||||
case ArrayVal : mArrayVal = v.mArrayVal; break;
|
||||
default : break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator [](size_t idx)
|
||||
{
|
||||
assert(mValueType == ArrayVal);
|
||||
return mArrayVal[idx];
|
||||
}
|
||||
|
||||
const Value& Value::operator [](size_t idx) const
|
||||
{
|
||||
assert(mValueType == ArrayVal);
|
||||
return mArrayVal[idx];
|
||||
}
|
||||
|
||||
Value& Value::operator [](const std::string& key)
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal[key];
|
||||
}
|
||||
|
||||
Value& Value::operator [](const char* key)
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal[key];
|
||||
}
|
||||
|
||||
const Value& Value::operator [](const char* key) const
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal[key];
|
||||
}
|
||||
|
||||
const Value& Value::operator [](const std::string& key) const
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal[key];
|
||||
}
|
||||
|
||||
void Value::Clear()
|
||||
{
|
||||
mValueType = NULLVal;
|
||||
}
|
||||
|
||||
size_t Value::size() const
|
||||
{
|
||||
if ((mValueType != ObjectVal) && (mValueType != ArrayVal))
|
||||
return 1;
|
||||
|
||||
return mValueType == ObjectVal ? mObjectVal.size() : mArrayVal.size();
|
||||
}
|
||||
|
||||
bool Value::HasKey(const std::string &key) const
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal.HasKey(key);
|
||||
}
|
||||
|
||||
int Value::HasKeys(const std::vector<std::string> &keys) const
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal.HasKeys(keys);
|
||||
}
|
||||
|
||||
int Value::HasKeys(const char **keys, int key_count) const
|
||||
{
|
||||
assert(mValueType == ObjectVal);
|
||||
return mObjectVal.HasKeys(keys, key_count);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Array::Array()
|
||||
{
|
||||
}
|
||||
|
||||
Array::Array(const Array& a) : mValues(a.mValues)
|
||||
{
|
||||
}
|
||||
|
||||
Array& Array::operator =(const Array& a)
|
||||
{
|
||||
if (&a == this)
|
||||
return *this;
|
||||
|
||||
Clear();
|
||||
mValues = a.mValues;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Array::operator [](size_t i)
|
||||
{
|
||||
return mValues[i];
|
||||
}
|
||||
|
||||
const Value& Array::operator [](size_t i) const
|
||||
{
|
||||
return mValues[i];
|
||||
}
|
||||
|
||||
|
||||
Array::ValueVector::const_iterator Array::begin() const
|
||||
{
|
||||
return mValues.begin();
|
||||
}
|
||||
|
||||
Array::ValueVector::const_iterator Array::end() const
|
||||
{
|
||||
return mValues.end();
|
||||
}
|
||||
|
||||
Array::ValueVector::iterator Array::begin()
|
||||
{
|
||||
return mValues.begin();
|
||||
}
|
||||
|
||||
Array::ValueVector::iterator Array::end()
|
||||
{
|
||||
return mValues.end();
|
||||
}
|
||||
|
||||
void Array::push_back(const Value& v)
|
||||
{
|
||||
mValues.push_back(v);
|
||||
}
|
||||
|
||||
void Array::insert(size_t index, const Value& v)
|
||||
{
|
||||
mValues.insert(mValues.begin() + index, v);
|
||||
}
|
||||
|
||||
size_t Array::size() const
|
||||
{
|
||||
return mValues.size();
|
||||
}
|
||||
|
||||
void Array::Clear()
|
||||
{
|
||||
mValues.clear();
|
||||
}
|
||||
|
||||
Array::ValueVector::iterator Array::find(const Value& v)
|
||||
{
|
||||
return std::find(mValues.begin(), mValues.end(), v);
|
||||
}
|
||||
|
||||
Array::ValueVector::const_iterator Array::find(const Value& v) const
|
||||
{
|
||||
return std::find(mValues.begin(), mValues.end(), v);
|
||||
}
|
||||
|
||||
bool Array::HasValue(const Value& v) const
|
||||
{
|
||||
return find(v) != end();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Object::Object()
|
||||
{
|
||||
}
|
||||
|
||||
Object::Object(const Object& obj) : mValues(obj.mValues)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Object& Object::operator =(const Object& obj)
|
||||
{
|
||||
if (&obj == this)
|
||||
return *this;
|
||||
|
||||
Clear();
|
||||
mValues = obj.mValues;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Object::operator [](const std::string& key)
|
||||
{
|
||||
return mValues[key];
|
||||
}
|
||||
|
||||
const Value& Object::operator [](const std::string& key) const
|
||||
{
|
||||
ValueMap::const_iterator it = mValues.find(key);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Value& Object::operator [](const char* key)
|
||||
{
|
||||
return mValues[key];
|
||||
}
|
||||
|
||||
const Value& Object::operator [](const char* key) const
|
||||
{
|
||||
ValueMap::const_iterator it = mValues.find(key);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Object::ValueMap::const_iterator Object::begin() const
|
||||
{
|
||||
return mValues.begin();
|
||||
}
|
||||
|
||||
Object::ValueMap::const_iterator Object::end() const
|
||||
{
|
||||
return mValues.end();
|
||||
}
|
||||
|
||||
Object::ValueMap::iterator Object::begin()
|
||||
{
|
||||
return mValues.begin();
|
||||
}
|
||||
|
||||
Object::ValueMap::iterator Object::end()
|
||||
{
|
||||
return mValues.end();
|
||||
}
|
||||
|
||||
Object::ValueMap::iterator Object::find(const std::string& key)
|
||||
{
|
||||
return mValues.find(key);
|
||||
}
|
||||
|
||||
Object::ValueMap::const_iterator Object::find(const std::string& key) const
|
||||
{
|
||||
return mValues.find(key);
|
||||
}
|
||||
|
||||
bool Object::HasKey(const std::string& key) const
|
||||
{
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
int Object::HasKeys(const std::vector<std::string>& keys) const
|
||||
{
|
||||
for (size_t i = 0; i < keys.size(); i++)
|
||||
{
|
||||
if (!HasKey(keys[i]))
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Object::HasKeys(const char** keys, int key_count) const
|
||||
{
|
||||
for (int i = 0; i < key_count; i++)
|
||||
if (!HasKey(keys[i]))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Object::Clear()
|
||||
{
|
||||
mValues.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
std::string SerializeArray(const Array& a);
|
||||
|
||||
std::string SerializeValue(const Value& v)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
static const int BUFF_SZ = 500;
|
||||
char buff[BUFF_SZ];
|
||||
switch (v.GetType())
|
||||
{
|
||||
case IntVal : snprintf(buff, BUFF_SZ, "%d", (int)v); str = buff; break;
|
||||
case FloatVal : snprintf(buff, BUFF_SZ, "%f", (float)v); str = buff; break;
|
||||
case DoubleVal : snprintf(buff, BUFF_SZ, "%f", (double)v); str = buff; break;
|
||||
case BoolVal : str = v ? "true" : "false"; break;
|
||||
case NULLVal : str = "null"; break;
|
||||
case ObjectVal : str = Serialize(v); break;
|
||||
case ArrayVal : str = SerializeArray(v); break;
|
||||
case StringVal : str = std::string("\"") + (std::string)v + std::string("\""); break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string SerializeArray(const Array& a)
|
||||
{
|
||||
std::string str = "[";
|
||||
|
||||
bool first = true;
|
||||
for (size_t i = 0; i < a.size(); i++)
|
||||
{
|
||||
const Value& v = a[i];
|
||||
if (!first)
|
||||
str += std::string(",");
|
||||
|
||||
str += SerializeValue(v);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
str += "]";
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string json::Serialize(const Value& v)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
bool first = true;
|
||||
|
||||
if (v.GetType() == ObjectVal)
|
||||
{
|
||||
str = "{";
|
||||
Object obj = v.ToObject();
|
||||
for (Object::ValueMap::const_iterator it = obj.begin(); it != obj.end(); it++)
|
||||
{
|
||||
if (!first)
|
||||
str += std::string(",");
|
||||
|
||||
str += std::string("\"") + it->first + std::string("\":") + SerializeValue(it->second);
|
||||
first = false;
|
||||
}
|
||||
|
||||
str += "}";
|
||||
}
|
||||
else if (v.GetType() == ArrayVal)
|
||||
{
|
||||
str = "[";
|
||||
Array a = v.ToArray();
|
||||
for (Array::ValueVector::const_iterator it = a.begin(); it != a.end(); it++)
|
||||
{
|
||||
if (!first)
|
||||
str += std::string(",");
|
||||
|
||||
str += SerializeValue(*it);
|
||||
first = false;
|
||||
}
|
||||
|
||||
str += "]";
|
||||
|
||||
}
|
||||
//else error
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static Value DeserializeArray(std::string& str, std::stack<StackDepthType>& depth_stack);
|
||||
static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>& depth_stack);
|
||||
|
||||
static Value DeserializeInternal(const std::string& _str, std::stack<StackDepthType>& depth_stack)
|
||||
{
|
||||
Value v;
|
||||
|
||||
std::string str = Trim(_str);
|
||||
if (str[0] == '{')
|
||||
{
|
||||
// Error: Began with a { but doesn't end with one
|
||||
if (str[str.length() - 1] != '}')
|
||||
return Value();
|
||||
|
||||
depth_stack.push(InObject);
|
||||
v = DeserializeObj(str, depth_stack);
|
||||
if ((v.GetType() == NULLVal) || (depth_stack.top() != InObject))
|
||||
return v;
|
||||
|
||||
depth_stack.pop();
|
||||
}
|
||||
else if (str[0] == '[')
|
||||
{
|
||||
// Error: Began with a [ but doesn't end with one
|
||||
if (str[str.length() - 1] != ']')
|
||||
return Value();
|
||||
|
||||
depth_stack.push(InArray);
|
||||
v = DeserializeArray(str, depth_stack);
|
||||
if ((v.GetType() == NULLVal) || (depth_stack.top() != InArray))
|
||||
return v;
|
||||
|
||||
depth_stack.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Will never get here unless _str is not valid JSON
|
||||
return Value();
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static size_t GetEndOfArrayOrObj(const std::string& str, std::stack<StackDepthType>& depth_stack)
|
||||
{
|
||||
size_t i = 1;
|
||||
bool in_quote = false;
|
||||
size_t original_count = depth_stack.size();
|
||||
|
||||
for (; i < str.length(); i++)
|
||||
{
|
||||
if (str[i] == '\"')
|
||||
{
|
||||
if (str[i - 1] != '\\')
|
||||
in_quote = !in_quote;
|
||||
}
|
||||
else if (!in_quote)
|
||||
{
|
||||
if (str[i] == '[')
|
||||
depth_stack.push(InArray);
|
||||
else if (str[i] == '{')
|
||||
depth_stack.push(InObject);
|
||||
else if (str[i] == ']')
|
||||
{
|
||||
StackDepthType t = depth_stack.top();
|
||||
if (t != InArray)
|
||||
{
|
||||
// expected to be closing an array but instead we're inside an object block.
|
||||
// Example problem: {]}
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
size_t count = depth_stack.size();
|
||||
depth_stack.pop();
|
||||
if (count == original_count)
|
||||
break;
|
||||
}
|
||||
else if (str[i] == '}')
|
||||
{
|
||||
StackDepthType t = depth_stack.top();
|
||||
if (t != InObject)
|
||||
{
|
||||
// expected to be closing an object but instead we're inside an array.
|
||||
// Example problem: [}]
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
size_t count = depth_stack.size();
|
||||
depth_stack.pop();
|
||||
if (count == original_count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static std::string UnescapeJSONString(const std::string& str)
|
||||
{
|
||||
std::string s = "";
|
||||
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
char c = str[i];
|
||||
if ((c == '\\') && (i + 1 < str.length()))
|
||||
{
|
||||
int skip_ahead = 1;
|
||||
unsigned int hex;
|
||||
std::string hex_str;
|
||||
|
||||
switch (str[i+1])
|
||||
{
|
||||
case '"' : s.push_back('\"'); break;
|
||||
case '\\': s.push_back('\\'); break;
|
||||
case '/' : s.push_back('/'); break;
|
||||
case 't' : s.push_back('\t'); break;
|
||||
case 'n' : s.push_back('\n'); break;
|
||||
case 'r' : s.push_back('\r'); break;
|
||||
case 'b' : s.push_back('\b'); break;
|
||||
case 'f' : s.push_back('\f'); break;
|
||||
case 'u' : skip_ahead = 5;
|
||||
hex_str = str.substr(i + 4, 2);
|
||||
hex = (unsigned int)std::strtoul(hex_str.c_str(), NULL, 16);
|
||||
s.push_back((char)hex);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
i += skip_ahead;
|
||||
}
|
||||
else
|
||||
s.push_back(c);
|
||||
}
|
||||
|
||||
return Trim(s);
|
||||
}
|
||||
|
||||
static Value DeserializeValue(std::string& str, bool* had_error, std::stack<StackDepthType>& depth_stack)
|
||||
{
|
||||
Value v;
|
||||
|
||||
*had_error = false;
|
||||
str = Trim(str);
|
||||
|
||||
if (str.length() == 0)
|
||||
return v;
|
||||
|
||||
if (str[0] == '[')
|
||||
{
|
||||
depth_stack.push(InArray);
|
||||
size_t i = GetEndOfArrayOrObj(str, depth_stack);
|
||||
if (i == std::string::npos)
|
||||
{
|
||||
*had_error = true;
|
||||
return Value();
|
||||
}
|
||||
|
||||
std::string array_str = str.substr(0, i + 1);
|
||||
v = Value(DeserializeArray(array_str, depth_stack));
|
||||
str = str.substr(i + 1, str.length());
|
||||
}
|
||||
else if (str[0] == '{')
|
||||
{
|
||||
depth_stack.push(InObject);
|
||||
size_t i = GetEndOfArrayOrObj(str, depth_stack);
|
||||
|
||||
if (i == std::string::npos)
|
||||
{
|
||||
*had_error = true;
|
||||
return Value();
|
||||
}
|
||||
|
||||
std::string obj_str = str.substr(0, i + 1);
|
||||
v = Value(DeserializeInternal(obj_str, depth_stack));
|
||||
str = str.substr(i + 1, str.length());
|
||||
}
|
||||
else if (str[0] == '\"')
|
||||
{
|
||||
size_t end_quote = GetQuotePos(str, 1);
|
||||
if (end_quote == std::string::npos)
|
||||
{
|
||||
*had_error = true;
|
||||
return Value();
|
||||
}
|
||||
|
||||
v = Value(UnescapeJSONString(str.substr(1, end_quote - 1)));
|
||||
str = str.substr(end_quote + 1, str.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
bool has_dot = false;
|
||||
bool has_e = false;
|
||||
std::string temp_val;
|
||||
size_t i = 0;
|
||||
for (; i < str.length(); i++)
|
||||
{
|
||||
if (str[i] == '.')
|
||||
has_dot = true;
|
||||
else if (str[i] == 'e')
|
||||
has_e = true;
|
||||
else if (str[i] == ']')
|
||||
{
|
||||
if (depth_stack.top() != InArray)
|
||||
{
|
||||
*had_error = true;
|
||||
return Value();
|
||||
}
|
||||
|
||||
depth_stack.pop();
|
||||
}
|
||||
else if (str[i] == '}')
|
||||
{
|
||||
if (depth_stack.top() != InObject)
|
||||
{
|
||||
*had_error = true;
|
||||
return Value();
|
||||
}
|
||||
|
||||
depth_stack.pop();
|
||||
}
|
||||
else if (str[i] == ',')
|
||||
break;
|
||||
|
||||
if (!std::isspace(str[i]))
|
||||
temp_val += str[i];
|
||||
}
|
||||
|
||||
// store all floating point as doubles. This will also set the float and int values as well.
|
||||
if (_stricmp(temp_val.c_str(), "true") == 0)
|
||||
v = Value(true);
|
||||
else if (_stricmp(temp_val.c_str(), "false") == 0)
|
||||
v = Value(false);
|
||||
else if (has_e || has_dot)
|
||||
v = Value(atof(temp_val.c_str()));
|
||||
else if (_stricmp(temp_val.c_str(), "null") == 0)
|
||||
v = Value();
|
||||
else
|
||||
{
|
||||
// Check if the value is beyond the size of an int and if so, store it as a double
|
||||
double tmp_val = atof(temp_val.c_str());
|
||||
if ((tmp_val >= (double)INT_MIN) && (tmp_val <= (double)INT_MAX))
|
||||
v = Value(atoi(temp_val.c_str()));
|
||||
else
|
||||
v = Value(tmp_val);
|
||||
}
|
||||
|
||||
str = str.substr(i, str.length());
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value DeserializeArray(std::string& str, std::stack<StackDepthType>& depth_stack)
|
||||
{
|
||||
Array a;
|
||||
bool had_error = false;
|
||||
|
||||
str = Trim(str);
|
||||
|
||||
if ((str[0] == '[') && (str[str.length() - 1] == ']'))
|
||||
str = str.substr(1, str.length() - 2);
|
||||
else
|
||||
return Value();
|
||||
|
||||
while (str.length() > 0)
|
||||
{
|
||||
std::string tmp;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < str.length(); i++)
|
||||
{
|
||||
// If we get to an object or array, parse it:
|
||||
if ((str[i] == '{') || (str[i] == '['))
|
||||
{
|
||||
Value v = DeserializeValue(str, &had_error, depth_stack);
|
||||
if (had_error)
|
||||
return Value();
|
||||
|
||||
if (v.GetType() != NULLVal)
|
||||
a.push_back(v);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bool terminate_parsing = false;
|
||||
|
||||
if ((str[i] == ',') || (str[i] == ']'))
|
||||
terminate_parsing = true; // hit the end of a value, parse it in the next block
|
||||
else
|
||||
{
|
||||
// keep grabbing chars to build up the value
|
||||
tmp += str[i];
|
||||
if (i == str.length() - 1)
|
||||
terminate_parsing = true; // end of string, finish parsing
|
||||
}
|
||||
|
||||
if (terminate_parsing)
|
||||
{
|
||||
Value v = DeserializeValue(tmp, &had_error, depth_stack);
|
||||
if (had_error)
|
||||
return Value();
|
||||
|
||||
if (v.GetType() != NULLVal)
|
||||
a.push_back(v);
|
||||
|
||||
str = str.substr(i + 1, str.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static Value DeserializeObj(const std::string& _str, std::stack<StackDepthType>& depth_stack)
|
||||
{
|
||||
Object obj;
|
||||
|
||||
std::string str = Trim(_str);
|
||||
|
||||
if ((str[0] != '{') && (str[str.length() - 1] != '}'))
|
||||
return Value();
|
||||
else
|
||||
str = str.substr(1, str.length() - 2);
|
||||
|
||||
while (str.length() > 0)
|
||||
{
|
||||
// Get the key name
|
||||
size_t start_quote_idx = GetQuotePos(str);
|
||||
size_t end_quote_idx = GetQuotePos(str, start_quote_idx + 1);
|
||||
size_t colon_idx = str.find(':', end_quote_idx);
|
||||
|
||||
if ((start_quote_idx == std::string::npos) || (end_quote_idx == std::string::npos) || (colon_idx == std::string::npos))
|
||||
return Value(); // can't find key name
|
||||
|
||||
std::string key = str.substr(start_quote_idx + 1, end_quote_idx - start_quote_idx - 1);
|
||||
if (key.length() == 0)
|
||||
return Value();
|
||||
|
||||
bool had_error = false;
|
||||
str = str.substr(colon_idx + 1, str.length());
|
||||
obj[key] = DeserializeValue(str, &had_error, depth_stack);
|
||||
if (had_error)
|
||||
return Value();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Value json::Deserialize(const std::string &str)
|
||||
{
|
||||
std::stack<StackDepthType> depth_stack;
|
||||
return DeserializeInternal(str, depth_stack);
|
||||
}
|
||||
|
528
libresapi/src/api/json.h
Normal file
528
libresapi/src/api/json.h
Normal file
@ -0,0 +1,528 @@
|
||||
/*
|
||||
SuperEasyJSON
|
||||
http://www.sourceforge.net/p/supereasyjson
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Jeff Weinstein (jeff.weinstein at gmail)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
CHANGELOG:
|
||||
==========
|
||||
2/8/2014:
|
||||
---------
|
||||
MAJOR BUG FIXES, all courtesy of Per Rovegård, Ph.D.
|
||||
* Feature request: HasKey and HasKeys added to Value for convenience and
|
||||
to avoid having to make a temporary object.
|
||||
* Strings should now be properly unescaped. Previously, as an example, the
|
||||
string "\/Date(1390431949211+0100)\/\" would be parsed as
|
||||
\/Date(1390431949211+0100)\/. The string is now properly parsed as
|
||||
/Date(1390431949211+0100)/.
|
||||
As per http://www.json.org the other escape characters including
|
||||
\u+4 hex digits will now be properly unescaped. So for example,
|
||||
\u0061 now becomes "A".
|
||||
* Serialize now supports serializing a toplevel array (which is valid JSON).
|
||||
The parameter it takes is now a Value, but existing code doesn't
|
||||
need to be changed.
|
||||
* Fixed bug with checking for proper opening/closing sequence for braces/brackets.
|
||||
Previously, this code:
|
||||
const char *json = "{\"arr\":[{}}]}";
|
||||
auto val = json::Deserialize(json);
|
||||
worked fine with no errors. That's a bug. I did a major overhaul so that
|
||||
now improperly formatted pairs will now correctly result in an error.
|
||||
* Made internal deserialize methods static
|
||||
|
||||
1/30/2014:
|
||||
----------
|
||||
* Changed #pragma once to the standard #ifndef header guard style for
|
||||
better compatibility.
|
||||
* Added a [] operator for Value that takes a const char* as an argument
|
||||
to avoid having to explicitly (and annoyingly) cast to std::string.
|
||||
Thus, my_value["asdf"] = "a string" should now work fine.
|
||||
The same has been added to the Object class.
|
||||
* Added non-operator methods of casting a Value to int/string/bool/etc.
|
||||
Implicitly casting a Value to a std::string doesn't work as per C++
|
||||
rules. As such, previously to assign a Value to a std::string you
|
||||
had to do:
|
||||
my_std_string = (std::string)my_value;
|
||||
You can now instead do:
|
||||
my_std_string = my_value.ToString();
|
||||
If you want more information on why this can't be done, please read
|
||||
this topic for more details:
|
||||
http://stackoverflow.com/questions/3518145/c-overloading-conversion-operator-for-custom-type-to-stdstring
|
||||
|
||||
1/27/2014
|
||||
----------
|
||||
* Deserialize will now return a NULLType Value instance if there was an
|
||||
error instead of asserting. This way you can handle however you want to
|
||||
invalid JSON being passed in. As a top level object must be either an
|
||||
array or an object, a NULL value return indicates an invalid result.
|
||||
|
||||
1/11/2014
|
||||
---------
|
||||
* Major bug fix: Strings containing []{} characters could cause
|
||||
parsing errors under certain conditions. I've just tested
|
||||
the class parsing a 300KB JSON file with all manner of bizarre
|
||||
characters and permutations and it worked, so hopefully this should
|
||||
be the end of "major bug" fixes.
|
||||
|
||||
1/10/2014
|
||||
---------
|
||||
Bug fixes courtesy of Gerry Beauregard:
|
||||
* Pretty big bug: was using wrong string paramter in ::Deserialize
|
||||
and furthermore it wasn't being trimmed.
|
||||
* Object::HasKeys now casts the return value to avoid compiler warnings.
|
||||
* Slight optimization to the Trim function
|
||||
* Made asserts in ::Deserialize easier to read
|
||||
|
||||
1/9/2014
|
||||
--------
|
||||
* Major bug fix: for JSON strings containing \" (as in, two characters,
|
||||
not the escaped " character), the lib would mess up and not parse
|
||||
correctly.
|
||||
* Major bug fix: I erroneously was assuming that all root JSON types
|
||||
had to be an object. This was an oversight, as a root JSON
|
||||
object can be an array. I have therefore changed the Deserialize
|
||||
method to return a json::Value rather than a json::Object. This
|
||||
will NOT impact any existing code you have, as a json::Value will
|
||||
cast to a json::Object (if it is indeed an object). But for
|
||||
correctness, you should be using json::Value = Deserialize...
|
||||
The Value type can be checked if it's an array (or any other type),
|
||||
and furthermore can even be accessed with the [] operator for
|
||||
convenience.
|
||||
* I've made the NULL value type set numeric fields to 0 and bool to false.
|
||||
This is for convenience for using the NULL type as a default return
|
||||
value in your code.
|
||||
* asserts added to casting (Gerry Beauregard)
|
||||
* Added method HasKeys to json::Object which will check if all the keys
|
||||
specified are in the object, returning the index of the first key
|
||||
not found or -1 if all found (hoppe).
|
||||
|
||||
1/4/2014
|
||||
--------
|
||||
* Fixed bug where booleans were being parsed as doubles (Gerry Beauregard).
|
||||
|
||||
1/2/2014 v3
|
||||
------------
|
||||
* More missing headers added for VisualStudio 2012
|
||||
* Switched to snprintf instead of sprintf (or sprintf_s in MSVC)
|
||||
|
||||
1/2/2014 v2
|
||||
-----------
|
||||
* Added yet more missing headers for compiling on GNU and Linux systems
|
||||
* Made Deserialize copy the passed in string so it won't mangle it
|
||||
|
||||
1/2/2014
|
||||
--------
|
||||
* Fixed previous changelog years. Got ahead of myself and marked them
|
||||
as 2014 when they were in fact done in 2013.
|
||||
* Added const version of [] to Array/Object/Value
|
||||
* Removed C++11 requirements, should work with older compilers
|
||||
(thanks to Meng Wang for pointing that out)
|
||||
* Made ValueMap and ValueVector typedefs in Object/Value public
|
||||
so you can actually iterate over the class
|
||||
* Added HasKey and HasValue to Object/Array for convenience
|
||||
(note this could have been done comparing .find to .end)
|
||||
|
||||
12/29/2013 v2
|
||||
-------------
|
||||
* Added .size() field to Value. Returns 1 for non Array/Object types,
|
||||
otherwise the number of elements contained.
|
||||
* Added .find() to Object to search for a key. Returns Object::end()
|
||||
if not found, otherwise the Value.
|
||||
Example: bool found = my_obj.find("some key") != my_obj.end();
|
||||
* Added .find() to Array to search for a value. Just a convenience
|
||||
wrapper for std::find(Array::begin(), Array::end(), Value)
|
||||
* Added ==, !=, <, >, <=, >= operators to Object/Array/Value.
|
||||
For Objects/Arrays, the operators function just like they do for a
|
||||
std::map and std::vector, respectively.
|
||||
* Added IsNumeric to Value to indicate if it's an int/float/double type.
|
||||
|
||||
12/29/2013
|
||||
----------
|
||||
* Added the DoubleVal type which stores, you guessed it, double values.
|
||||
* Bug fix for floats with an exact integer value. Now, setting any numerical
|
||||
field will also set the fields for the other numerical types. So if you
|
||||
have obj["value"] = 12, then the int/float/double cast methods will
|
||||
return 12/12.0f/12.0. Previously, in the example above, only the int
|
||||
value was set, making a cast to float return 0.
|
||||
* Bug fix for deserializing JSON strings that contained large integer values.
|
||||
Now if the numerical value of a key in a JSON string contains a number
|
||||
less than INT_MIN or greater than INT_MAX it will be stored as a double.
|
||||
Note that as mentioned above, all numerical fields are set.
|
||||
* Should work fine with scientific notation values now.
|
||||
|
||||
12/28/2013
|
||||
----------
|
||||
|
||||
* Fixed a bug where if there were spaces around values or key names in a JSON
|
||||
string passed in to Deserialize, invalid results or asserts would occur.
|
||||
(Fix courtesy of Gerry Beauregard)
|
||||
|
||||
* Added method named "Clear()" to Object/Array/Value to reset state
|
||||
|
||||
* Added license to header file for easyness (totally valid word).
|
||||
*/
|
||||
|
||||
#ifndef __SUPER_EASY_JSON_H__
|
||||
#define __SUPER_EASY_JSON_H__
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
namespace json
|
||||
{
|
||||
enum ValueType
|
||||
{
|
||||
NULLVal,
|
||||
StringVal,
|
||||
IntVal,
|
||||
FloatVal,
|
||||
DoubleVal,
|
||||
ObjectVal,
|
||||
ArrayVal,
|
||||
BoolVal
|
||||
};
|
||||
|
||||
class Value;
|
||||
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::map<std::string, Value> ValueMap;
|
||||
|
||||
protected:
|
||||
|
||||
ValueMap mValues;
|
||||
|
||||
public:
|
||||
|
||||
Object();
|
||||
Object(const Object& obj);
|
||||
|
||||
Object& operator =(const Object& obj);
|
||||
|
||||
friend bool operator ==(const Object& lhs, const Object& rhs);
|
||||
inline friend bool operator !=(const Object& lhs, const Object& rhs) {return !(lhs == rhs);}
|
||||
friend bool operator <(const Object& lhs, const Object& rhs);
|
||||
inline friend bool operator >(const Object& lhs, const Object& rhs) {return operator<(rhs, lhs);}
|
||||
inline friend bool operator <=(const Object& lhs, const Object& rhs) {return !operator>(lhs, rhs);}
|
||||
inline friend bool operator >=(const Object& lhs, const Object& rhs) {return !operator<(lhs, rhs);}
|
||||
|
||||
Value& operator [](const std::string& key);
|
||||
const Value& operator [](const std::string& key) const;
|
||||
Value& operator [](const char* key);
|
||||
const Value& operator [](const char* key) const;
|
||||
|
||||
ValueMap::const_iterator begin() const;
|
||||
ValueMap::const_iterator end() const;
|
||||
ValueMap::iterator begin();
|
||||
ValueMap::iterator end();
|
||||
|
||||
// Find will return end() if the key can't be found, just like std::map does.
|
||||
ValueMap::iterator find(const std::string& key);
|
||||
ValueMap::const_iterator find(const std::string& key) const;
|
||||
|
||||
// Convenience wrapper to find to search for a key
|
||||
bool HasKey(const std::string& key) const;
|
||||
|
||||
// Checks if the object contains all the keys in the array. If it does, returns -1.
|
||||
// If it doesn't, returns the index of the first key it couldn't find.
|
||||
int HasKeys(const std::vector<std::string>& keys) const;
|
||||
int HasKeys(const char* keys[], int key_count) const;
|
||||
|
||||
// Removes all values and resets the state back to default
|
||||
void Clear();
|
||||
|
||||
size_t size() const {return mValues.size();}
|
||||
|
||||
};
|
||||
|
||||
class Array
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<Value> ValueVector;
|
||||
|
||||
protected:
|
||||
|
||||
ValueVector mValues;
|
||||
|
||||
public:
|
||||
|
||||
Array();
|
||||
Array(const Array& a);
|
||||
|
||||
Array& operator =(const Array& a);
|
||||
|
||||
friend bool operator ==(const Array& lhs, const Array& rhs);
|
||||
inline friend bool operator !=(const Array& lhs, const Array& rhs) {return !(lhs == rhs);}
|
||||
friend bool operator <(const Array& lhs, const Array& rhs);
|
||||
inline friend bool operator >(const Array& lhs, const Array& rhs) {return operator<(rhs, lhs);}
|
||||
inline friend bool operator <=(const Array& lhs, const Array& rhs) {return !operator>(lhs, rhs);}
|
||||
inline friend bool operator >=(const Array& lhs, const Array& rhs) {return !operator<(lhs, rhs);}
|
||||
|
||||
Value& operator[] (size_t i);
|
||||
const Value& operator[] (size_t i) const;
|
||||
|
||||
ValueVector::const_iterator begin() const;
|
||||
ValueVector::const_iterator end() const;
|
||||
ValueVector::iterator begin();
|
||||
ValueVector::iterator end();
|
||||
|
||||
// Just a convenience wrapper for doing a std::find(Array::begin(), Array::end(), Value)
|
||||
ValueVector::iterator find(const Value& v);
|
||||
ValueVector::const_iterator find(const Value& v) const;
|
||||
|
||||
// Convenience wrapper to check if a value is in the array
|
||||
bool HasValue(const Value& v) const;
|
||||
|
||||
// Removes all values and resets the state back to default
|
||||
void Clear();
|
||||
|
||||
void push_back(const Value& v);
|
||||
void insert(size_t index, const Value& v);
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
class Value
|
||||
{
|
||||
protected:
|
||||
|
||||
ValueType mValueType;
|
||||
int mIntVal;
|
||||
float mFloatVal;
|
||||
double mDoubleVal;
|
||||
std::string mStringVal;
|
||||
Object mObjectVal;
|
||||
Array mArrayVal;
|
||||
bool mBoolVal;
|
||||
|
||||
public:
|
||||
|
||||
Value() : mValueType(NULLVal), mIntVal(0), mFloatVal(0), mDoubleVal(0), mBoolVal(false) {}
|
||||
Value(int v) : mValueType(IntVal), mIntVal(v), mFloatVal((float)v), mDoubleVal((double)v) {}
|
||||
Value(float v) : mValueType(FloatVal), mFloatVal(v), mIntVal((int)v), mDoubleVal((double)v) {}
|
||||
Value(double v) : mValueType(DoubleVal), mDoubleVal(v), mIntVal((int)v), mFloatVal((float)v) {}
|
||||
Value(const std::string& v) : mValueType(StringVal), mStringVal(v) {}
|
||||
Value(const char* v) : mValueType(StringVal), mStringVal(v) {}
|
||||
Value(const Object& v) : mValueType(ObjectVal), mObjectVal(v) {}
|
||||
Value(const Array& v) : mValueType(ArrayVal), mArrayVal(v) {}
|
||||
Value(const bool v) : mValueType(BoolVal), mBoolVal(v) {}
|
||||
Value(const Value& v);
|
||||
|
||||
ValueType GetType() const {return mValueType;}
|
||||
|
||||
Value& operator =(const Value& v);
|
||||
|
||||
friend bool operator ==(const Value& lhs, const Value& rhs);
|
||||
inline friend bool operator !=(const Value& lhs, const Value& rhs) {return !(lhs == rhs);}
|
||||
friend bool operator <(const Value& lhs, const Value& rhs);
|
||||
inline friend bool operator >(const Value& lhs, const Value& rhs) {return operator<(rhs, lhs);}
|
||||
inline friend bool operator <=(const Value& lhs, const Value& rhs) {return !operator>(lhs, rhs);}
|
||||
inline friend bool operator >=(const Value& lhs, const Value& rhs) {return !operator<(lhs, rhs);}
|
||||
|
||||
|
||||
// For use with Array/ObjectVal types, respectively
|
||||
Value& operator [](size_t idx);
|
||||
const Value& operator [](size_t idx) const;
|
||||
Value& operator [](const std::string& key);
|
||||
const Value& operator [](const std::string& key) const;
|
||||
Value& operator [](const char* key);
|
||||
const Value& operator [](const char* key) const;
|
||||
|
||||
bool HasKey(const std::string& key) const;
|
||||
int HasKeys(const std::vector<std::string>& keys) const;
|
||||
int HasKeys(const char* keys[], int key_count) const;
|
||||
|
||||
|
||||
// non-operator versions
|
||||
int ToInt() const {assert(IsNumeric()); return mIntVal;}
|
||||
float ToFloat() const {assert(IsNumeric()); return mFloatVal;}
|
||||
double ToDouble() const {assert(IsNumeric()); return mDoubleVal;}
|
||||
bool ToBool() const {assert(mValueType == BoolVal); return mBoolVal;}
|
||||
std::string ToString() const {assert(mValueType == StringVal); return mStringVal;}
|
||||
Object ToObject() const {assert(mValueType == ObjectVal); return mObjectVal;}
|
||||
Array ToArray() const {assert(mValueType == ArrayVal); return mArrayVal;}
|
||||
|
||||
// Please note that as per C++ rules, implicitly casting a Value to a std::string won't work.
|
||||
// This is because it could use the int/float/double/bool operators as well. So to assign a
|
||||
// Value to a std::string you can either do:
|
||||
// my_string = (std::string)my_value
|
||||
// Or you can now do:
|
||||
// my_string = my_value.ToString();
|
||||
//
|
||||
operator int() const {assert(IsNumeric()); return mIntVal;}
|
||||
operator float() const {assert(IsNumeric()); return mFloatVal;}
|
||||
operator double() const {assert(IsNumeric()); return mDoubleVal;}
|
||||
operator bool() const {assert(mValueType == BoolVal); return mBoolVal;}
|
||||
operator std::string() const {assert(mValueType == StringVal); return mStringVal;}
|
||||
operator Object() const {assert(mValueType == ObjectVal); return mObjectVal;}
|
||||
operator Array() const {assert(mValueType == ArrayVal); return mArrayVal;}
|
||||
|
||||
bool IsNumeric() const {return (mValueType == IntVal) || (mValueType == DoubleVal) || (mValueType == FloatVal);}
|
||||
|
||||
// Returns 1 for anything not an Array/ObjectVal
|
||||
size_t size() const;
|
||||
|
||||
// Resets the state back to default, aka NULLVal
|
||||
void Clear();
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Converts a JSON Object or Array instance into a JSON string representing it.
|
||||
std::string Serialize(const Value& obj);
|
||||
|
||||
// If there is an error, Value will be NULLType
|
||||
Value Deserialize(const std::string& str);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
inline bool operator ==(const Object& lhs, const Object& rhs)
|
||||
{
|
||||
return lhs.mValues == rhs.mValues;
|
||||
}
|
||||
|
||||
inline bool operator <(const Object& lhs, const Object& rhs)
|
||||
{
|
||||
return lhs.mValues < rhs.mValues;
|
||||
}
|
||||
|
||||
inline bool operator ==(const Array& lhs, const Array& rhs)
|
||||
{
|
||||
return lhs.mValues == rhs.mValues;
|
||||
}
|
||||
|
||||
inline bool operator <(const Array& lhs, const Array& rhs)
|
||||
{
|
||||
return lhs.mValues < rhs.mValues;
|
||||
}
|
||||
|
||||
/* When comparing different numeric types, this method works the same as if you compared different numeric types
|
||||
on your own. Thus it performs the same as if you, for example, did this:
|
||||
|
||||
int a = 1;
|
||||
float b = 1.1f;
|
||||
bool equivalent = a == b;
|
||||
|
||||
The same logic applies to the other comparison operators.
|
||||
*/
|
||||
inline bool operator ==(const Value& lhs, const Value& rhs)
|
||||
{
|
||||
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric())
|
||||
return false;
|
||||
|
||||
switch (lhs.mValueType)
|
||||
{
|
||||
case StringVal : return lhs.mStringVal == rhs.mStringVal;
|
||||
|
||||
case IntVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mIntVal == rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mIntVal == rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mIntVal == rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
case FloatVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mFloatVal == rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mFloatVal == rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mFloatVal == rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
case DoubleVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mDoubleVal == rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mDoubleVal == rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mDoubleVal == rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
case BoolVal : return lhs.mBoolVal == rhs.mBoolVal;
|
||||
|
||||
case ObjectVal : return lhs.mObjectVal == rhs.mObjectVal;
|
||||
|
||||
case ArrayVal : return lhs.mArrayVal == rhs.mArrayVal;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator <(const Value& lhs, const Value& rhs)
|
||||
{
|
||||
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric())
|
||||
return false;
|
||||
|
||||
switch (lhs.mValueType)
|
||||
{
|
||||
case StringVal : return lhs.mStringVal < rhs.mStringVal;
|
||||
|
||||
case IntVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mIntVal < rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mIntVal < rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mIntVal < rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
case FloatVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mFloatVal < rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mFloatVal < rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mFloatVal < rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
case DoubleVal : if (rhs.GetType() == FloatVal)
|
||||
return lhs.mDoubleVal < rhs.mFloatVal;
|
||||
else if (rhs.GetType() == DoubleVal)
|
||||
return lhs.mDoubleVal < rhs.mDoubleVal;
|
||||
else if (rhs.GetType() == IntVal)
|
||||
return lhs.mDoubleVal < rhs.mIntVal;
|
||||
else
|
||||
return false;
|
||||
|
||||
case BoolVal : return lhs.mBoolVal < rhs.mBoolVal;
|
||||
|
||||
case ObjectVal : return lhs.mObjectVal < rhs.mObjectVal;
|
||||
|
||||
case ArrayVal : return lhs.mArrayVal < rhs.mArrayVal;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__SUPER_EASY_JSON_H__
|
57
libresapi/src/libresapi.pro
Normal file
57
libresapi/src/libresapi.pro
Normal file
@ -0,0 +1,57 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib
|
||||
CONFIG -= qt
|
||||
TARGET = resapi
|
||||
DESTDIR = lib
|
||||
|
||||
CONFIG += libmicrohttpd
|
||||
|
||||
INCLUDEPATH += ../../libretroshare/src
|
||||
|
||||
win32{
|
||||
DEFINES *= WINDOWS_SYS
|
||||
INCLUDEPATH += $$PWD/../../../libs/include
|
||||
}
|
||||
|
||||
libmicrohttpd{
|
||||
SOURCES += \
|
||||
api/ApiServerMHD.cpp
|
||||
|
||||
HEADERS += \
|
||||
api/ApiServerMHD.h
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
api/ApiServer.cpp \
|
||||
api/json.cpp \
|
||||
api/JsonStream.cpp \
|
||||
api/ResourceRouter.cpp \
|
||||
api/PeersHandler.cpp \
|
||||
api/Operators.cpp \
|
||||
api/IdentityHandler.cpp \
|
||||
api/ServiceControlHandler.cpp \
|
||||
api/StateTokenServer.cpp \
|
||||
api/GxsResponseTask.cpp \
|
||||
api/FileSearchHandler.cpp \
|
||||
api/TransfersHandler.cpp \
|
||||
api/RsControlModule.cpp \
|
||||
api/GetPluginInterfaces.cpp
|
||||
|
||||
HEADERS += \
|
||||
api/ApiServer.h \
|
||||
api/json.h \
|
||||
api/JsonStream.h \
|
||||
api/ApiTypes.h \
|
||||
api/ResourceRouter.h \
|
||||
api/PeersHandler.h \
|
||||
api/Operators.h \
|
||||
api/IdentityHandler.h \
|
||||
api/ServiceControlHandler.h \
|
||||
api/GxsMetaOperators.h \
|
||||
api/StateTokenServer.h \
|
||||
api/GxsResponseTask.h \
|
||||
api/Pagination.h \
|
||||
api/FileSearchHandler.h \
|
||||
api/TransfersHandler.h \
|
||||
api/RsControlModule.h \
|
||||
api/GetPluginInterfaces.h
|
@ -3,6 +3,8 @@ TARGET = retroshare-nogui
|
||||
CONFIG += bitdht
|
||||
#CONFIG += introserver
|
||||
#CONFIG += sshserver
|
||||
# webinterface, requires libmicrohttpd
|
||||
CONFIG += webui
|
||||
CONFIG -= qt xml gui
|
||||
|
||||
# if you are linking against the libretroshare with gxs.
|
||||
@ -197,6 +199,11 @@ introserver {
|
||||
DEFINES *= RS_INTRO_SERVER
|
||||
}
|
||||
|
||||
webui {
|
||||
DEFINES *= ENABLE_WEBUI
|
||||
LIBS += ../../libresapi/src/lib/libresapi.a -lmicrohttpd
|
||||
INCLUDEPATH += ../../libresapi/src
|
||||
}
|
||||
|
||||
sshserver {
|
||||
|
||||
|
@ -51,10 +51,12 @@
|
||||
// NASTY GLOBAL VARIABLE HACK - NEED TO THINK OF A BETTER SYSTEM.
|
||||
#include "rpc/proto/rpcprotosystem.h"
|
||||
|
||||
void generatePasswordHash() ;
|
||||
#endif
|
||||
|
||||
#ifdef RS_SSH_SERVER
|
||||
void generatePasswordHash() ;
|
||||
#ifdef ENABLE_WEBUI
|
||||
#include "api/ApiServerMHD.h"
|
||||
#include "api/RsControlModule.h"
|
||||
#endif
|
||||
|
||||
/* Basic instructions for running libretroshare as background thread.
|
||||
@ -69,6 +71,33 @@ void generatePasswordHash() ;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef ENABLE_WEBUI
|
||||
|
||||
std::string docroot;
|
||||
bool is_portable = false;
|
||||
#ifdef WINDOWS_SYS
|
||||
// test for portable version
|
||||
if (GetFileAttributes(L"portable") != (DWORD) -1) {
|
||||
is_portable = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
resource_api::ApiServerMHD httpd(docroot, 9090);
|
||||
|
||||
resource_api::RsControlModule ctrl_mod(argc, argv, httpd.getApiServer().getStateTokenServer(), &httpd.getApiServer());
|
||||
httpd.getApiServer().addResourceHandler("control", dynamic_cast<resource_api::ResourceRouter*>(&ctrl_mod), &resource_api::RsControlModule::handleRequest);
|
||||
|
||||
httpd.start();
|
||||
|
||||
while(ctrl_mod.processShouldExit() == false)
|
||||
{
|
||||
usleep(20*1000);
|
||||
}
|
||||
httpd.stop();
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* Retroshare startup is configured using an RsInit object.
|
||||
* This is an opaque class, which the user cannot directly tweak
|
||||
* If you want to peek at whats happening underneath look in
|
||||
|
Loading…
Reference in New Issue
Block a user