mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-26 07:16:11 -05:00
- added support for multiple client threads to ApiServer
- added api client which reads the password from stdin. This allows to login from the webinterface and from the terminal at the same time. git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8103 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
18e29c1e65
commit
73a6ca8af6
@ -6,12 +6,10 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include "json.h"
|
||||
|
||||
#include <retroshare/rsservicecontrol.h>
|
||||
#ifdef FIXME
|
||||
#include "rswall.h"
|
||||
#endif
|
||||
#include "JsonStream.h"
|
||||
#include "StateTokenServer.h" // for the state token serialisers
|
||||
|
||||
@ -253,24 +251,11 @@ public:
|
||||
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():
|
||||
mMtx("ApiServer mMtx"),
|
||||
mStateTokenServer(),
|
||||
mMainModules(0),
|
||||
mWallModule(0)
|
||||
mMainModules(0)
|
||||
{
|
||||
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),
|
||||
&StateTokenServer::handleRequest);
|
||||
@ -278,24 +263,22 @@ ApiServer::ApiServer():
|
||||
|
||||
ApiServer::~ApiServer()
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
for(std::vector<RequestId>::iterator vit = mRequests.begin(); vit != mRequests.end(); ++vit)
|
||||
delete vit->task;
|
||||
mRequests.clear();
|
||||
|
||||
if(mMainModules)
|
||||
delete mMainModules;
|
||||
if(mWallModule)
|
||||
delete mWallModule;
|
||||
}
|
||||
|
||||
void ApiServer::loadMainModules(const RsPlugInInterfaces &ifaces)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
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;
|
||||
@ -304,11 +287,21 @@ std::string ApiServer::handleRequest(Request &request)
|
||||
StreamBase& data = outstream.getStreamToMember("data");
|
||||
resource_api::Response resp(data, debugString);
|
||||
|
||||
ResponseTask* task = mRouter.handleRequest(request, resp);
|
||||
ResponseTask* task = 0;
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
task = mRouter.handleRequest(request, resp);
|
||||
}
|
||||
|
||||
time_t start = time(NULL);
|
||||
while(task && task->doWork(request, resp))
|
||||
bool morework = true;
|
||||
while(task && morework)
|
||||
{
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
morework = task->doWork(request, resp);
|
||||
}
|
||||
if(morework)
|
||||
usleep(10*1000);
|
||||
/*if(time(NULL) > (start+5))
|
||||
{
|
||||
@ -348,4 +341,53 @@ std::string ApiServer::handleRequest(Request &request)
|
||||
return outstream.getJsonString();
|
||||
}
|
||||
|
||||
ApiServer::RequestId ApiServer::handleRequest(Request &request, Response &response)
|
||||
{
|
||||
RequestId id;
|
||||
ResponseTask* task = 0;
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
task = mRouter.handleRequest(request, response);
|
||||
}
|
||||
if(task == 0)
|
||||
{
|
||||
id.done = true;
|
||||
return id;
|
||||
}
|
||||
id.done = false,
|
||||
id.task = task;
|
||||
id.request = &request;
|
||||
id.response = &response;
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
mRequests.push_back(id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
bool ApiServer::isRequestDone(RequestId id)
|
||||
{
|
||||
if(id.done)
|
||||
return true;
|
||||
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
std::vector<RequestId>::iterator vit = std::find(mRequests.begin(), mRequests.end(), id);
|
||||
// Request id not found, maybe the id is old and was removed from the list
|
||||
if(vit == mRequests.end())
|
||||
return true;
|
||||
|
||||
if(id.task->doWork(*id.request, *id.response))
|
||||
return false;
|
||||
|
||||
// if we reach this point, the request is in the list and done
|
||||
// remove the id from the list of valid ids
|
||||
// delete the ResponseTask object
|
||||
|
||||
*vit = mRequests.back();
|
||||
mRequests.pop_back();
|
||||
|
||||
delete id.task;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
||||
|
@ -5,9 +5,6 @@
|
||||
#include "ApiTypes.h"
|
||||
#include "PeersHandler.h"
|
||||
#include "IdentityHandler.h"
|
||||
#ifdef FIXME
|
||||
#include "WallHandler.h"
|
||||
#endif
|
||||
#include "ServiceControlHandler.h"
|
||||
#include "StateTokenServer.h"
|
||||
#include "FileSearchHandler.h"
|
||||
@ -16,41 +13,57 @@
|
||||
namespace resource_api{
|
||||
|
||||
class ApiServerMainModules;
|
||||
class ApiServerWallModule;
|
||||
|
||||
// main entry point for all resource api calls
|
||||
// main entry point for all resource_api calls
|
||||
// general part of the api server
|
||||
// should work with any http library or a different transport protocol (e.g. SSH)
|
||||
|
||||
// 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
|
||||
// HTTP server -> ApiServer -> different handlers
|
||||
// or
|
||||
// GUI -> ApiServer -> different handlers
|
||||
// multiple clients can use the same ApiServer instance at the same time
|
||||
|
||||
// general part of the api server
|
||||
// should work with any http library or a different transport protocol
|
||||
// ALL public methods in this class are thread safe
|
||||
// this allows differen threads to send requests
|
||||
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
|
||||
class RequestId{
|
||||
public:
|
||||
RequestId(): done(false), task(0), request(0), response(0){}
|
||||
bool operator ==(const RequestId& r){
|
||||
const RequestId& l = *this;
|
||||
return (l.done==r.done)&&(l.task==r.task)&&(l.request==r.request)&&(l.response&&r.response);
|
||||
}
|
||||
private:
|
||||
friend class ApiServer;
|
||||
bool done; // this flag will be set to true, to signal the task id is valid and the task is done
|
||||
// (in case there was no ResponseTask and task was zero)
|
||||
ResponseTask* task; // null when the task id is invalid or when there was no task
|
||||
Request* request;
|
||||
Response* response;
|
||||
};
|
||||
|
||||
// process the requestgiven by request and return the response as json string
|
||||
// blocks until the request was processed
|
||||
std::string handleRequest(Request& request);
|
||||
|
||||
// request and response must stay valid until isRequestDone returns true
|
||||
// this method may do some work but it does not block
|
||||
RequestId handleRequest(Request& request, Response& response);
|
||||
|
||||
// ticks the request
|
||||
// returns true if the request is done or the id is invalid
|
||||
// this method may do some work but it does not block
|
||||
bool isRequestDone(RequestId id);
|
||||
|
||||
// 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>
|
||||
@ -61,25 +74,29 @@ public:
|
||||
StateTokenServer* getStateTokenServer(){ return &mStateTokenServer; }
|
||||
|
||||
private:
|
||||
RsMutex mMtx;
|
||||
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;
|
||||
|
||||
std::vector<RequestId> mRequests;
|
||||
};
|
||||
|
||||
// implementations
|
||||
template <class T>
|
||||
void ApiServer::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
mRouter.addResourceHandler(name, instance, callback);
|
||||
}
|
||||
template <class T>
|
||||
void ApiServer::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp))
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
mRouter.addResourceHandler(name, instance, callback);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,8 @@
|
||||
// for filestreamer
|
||||
#include <retroshare/rsfiles.h>
|
||||
|
||||
#include "api/JsonStream.h"
|
||||
#include "JsonStream.h"
|
||||
#include "ApiServer.h"
|
||||
|
||||
#if MHD_VERSION < 0x00090000
|
||||
// very old version, probably v0.4.x on old debian/ubuntu
|
||||
@ -327,8 +328,8 @@ static void sendMessage(MHD_Connection *connection, unsigned int status, std::st
|
||||
MHD_destroy_response(resp);
|
||||
}
|
||||
|
||||
ApiServerMHD::ApiServerMHD():
|
||||
mConfigOk(false), mDaemon(0)
|
||||
ApiServerMHD::ApiServerMHD(ApiServer *server):
|
||||
mConfigOk(false), mDaemon(0), mApiServer(server)
|
||||
{
|
||||
}
|
||||
|
||||
@ -394,7 +395,7 @@ bool ApiServerMHD::start()
|
||||
MHD_OPTION_END);
|
||||
if(mDaemon)
|
||||
{
|
||||
std::cerr << "ApiServerMHD::start() SUCCESS. Started server on port " << ntohs(mListenAddr.sin_port) << ". Serving files from \"" << mRootDir << "\" at /" << std::endl;
|
||||
std::cerr << "ApiServerMHD::start() SUCCESS. Started server on port " << ntohs(mListenAddr.sin_port) << ". Serving files from \"" << mRootDir << "\" at " << STATIC_FILES_ENTRY_PATH << std::endl;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -480,7 +481,7 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
|
||||
if(strstr(url, API_ENTRY_PATH) == url)
|
||||
{
|
||||
// create a new handler and store it in con_cls
|
||||
MHDHandlerBase* handler = new MHDApiHandler(&mApiServer);
|
||||
MHDHandlerBase* handler = new MHDApiHandler(mApiServer);
|
||||
*con_cls = (void*) handler;
|
||||
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
|
||||
}
|
||||
|
@ -11,14 +11,13 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "api/ApiServer.h"
|
||||
|
||||
namespace resource_api{
|
||||
class ApiServer;
|
||||
|
||||
class ApiServerMHD
|
||||
{
|
||||
public:
|
||||
ApiServerMHD();
|
||||
ApiServerMHD(ApiServer* server);
|
||||
~ApiServerMHD();
|
||||
/**
|
||||
* @brief configure the http server
|
||||
@ -32,8 +31,6 @@ public:
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
ApiServer& getApiServer(){ return mApiServer; }
|
||||
|
||||
private:
|
||||
// static callbacks for libmicrohttpd, they call the members below
|
||||
static int static_acceptPolicyCallback(void* cls, const struct sockaddr * addr, socklen_t addrlen);
|
||||
@ -46,7 +43,7 @@ private:
|
||||
std::string mRootDir;
|
||||
struct sockaddr_in mListenAddr;
|
||||
MHD_Daemon* mDaemon;
|
||||
ApiServer mApiServer;
|
||||
ApiServer* mApiServer;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
||||
|
@ -254,6 +254,7 @@ public:
|
||||
class ResponseTask
|
||||
{
|
||||
public:
|
||||
virtual ~ResponseTask(){}
|
||||
// return true if function should get called again
|
||||
// return false when finished
|
||||
virtual bool doWork(Request& req, Response& resp) = 0;
|
||||
|
@ -46,6 +46,12 @@ std::string JsonStream::getJsonString()
|
||||
return "";
|
||||
}
|
||||
|
||||
void JsonStream::switchToDeserialisation()
|
||||
{
|
||||
deleteCurrentChild();
|
||||
mSerialise = false;
|
||||
}
|
||||
|
||||
|
||||
//----------Stream Interface ---------------
|
||||
|
||||
@ -132,6 +138,7 @@ StreamBase& JsonStream::getStreamToMember()
|
||||
if(checkDeserialisation() && arrayBoundsOk())
|
||||
{
|
||||
mChild->mValue = mArray[mArrayNextRead];
|
||||
mChild->mSerialise = false;
|
||||
mArrayNextRead++;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,11 @@ public:
|
||||
void setJsonString(std::string jsonStr);
|
||||
std::string getJsonString();
|
||||
|
||||
// it is possible to use this class as buffer
|
||||
// first use as serialiser and fill with values
|
||||
// then call this method to deserialise the values
|
||||
void switchToDeserialisation();
|
||||
|
||||
|
||||
//----------Stream Interface ---------------
|
||||
|
||||
|
@ -51,6 +51,7 @@ ResponseTask* ResourceRouter::handleRequest(Request& req, Response& resp)
|
||||
return vit->second->handleRequest(req, resp);
|
||||
}
|
||||
}
|
||||
resp.setFail("ResourceRouter::handleRequest() Error: no handler for this path.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
namespace resource_api
|
||||
{
|
||||
// a base class for routing requests to handler methods
|
||||
// nothing is thread safe here
|
||||
class ResourceRouter
|
||||
{
|
||||
public:
|
||||
@ -23,6 +24,7 @@ private:
|
||||
class HandlerBase
|
||||
{
|
||||
public:
|
||||
virtual ~HandlerBase(){}
|
||||
virtual ResponseTask* handleRequest(Request& req, Response& resp) = 0;
|
||||
};
|
||||
template <class T>
|
||||
|
310
retroshare-nogui/src/TerminalApiClient.cpp
Normal file
310
retroshare-nogui/src/TerminalApiClient.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
#include "TerminalApiClient.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <api/JsonStream.h>
|
||||
|
||||
// need two functions for non blocking read from stdin:
|
||||
// int _kbhit() (returns a non zero value if a key was pressed)
|
||||
// int _getch() (return the pressed key)
|
||||
// these function are available on windows in conio.h
|
||||
// they are not available on linux
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#else // LINUX
|
||||
/**
|
||||
Linux (POSIX) implementation of _kbhit().
|
||||
Morgan McGuire, morgan@cs.brown.edu
|
||||
(modified to disable echo)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <stropts.h>
|
||||
|
||||
int _kbhit() {
|
||||
static const int STDIN = 0;
|
||||
static bool initialized = false;
|
||||
|
||||
if (! initialized) {
|
||||
// Use termios to turn off line buffering
|
||||
termios term;
|
||||
tcgetattr(STDIN, &term);
|
||||
term.c_lflag &= ~ICANON;
|
||||
term.c_lflag &= ~ECHO; // disable echo
|
||||
tcsetattr(STDIN, TCSANOW, &term);
|
||||
setbuf(stdin, NULL);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
int bytesWaiting;
|
||||
ioctl(STDIN, FIONREAD, &bytesWaiting);
|
||||
return bytesWaiting;
|
||||
}
|
||||
/*
|
||||
Q: Is there a getch() (from conio) equivalent on Linux/UNIX?
|
||||
|
||||
A: No. But it's easy to emulate:
|
||||
|
||||
This code sets the terminal into non-canonical mode, thus disabling line buffering, reads a character from stdin and then restores the old terminal status. For more info on what else you can do with termios, see ``man termios''.
|
||||
There's also a ``getch()'' function in the curses library, but it is /not/ equivalent to the DOS ``getch()'' and may only be used within real curses applications (ie: it only works in curses ``WINDOW''s).
|
||||
|
||||
http://cboard.cprogramming.com/faq-board/27714-faq-there-getch-conio-equivalent-linux-unix.html
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int _getch( ) {
|
||||
struct termios oldt,
|
||||
newt;
|
||||
int ch;
|
||||
tcgetattr( STDIN_FILENO, &oldt );
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~( ICANON | ECHO );
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
|
||||
ch = getchar();
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
|
||||
return ch;
|
||||
}
|
||||
#endif // LINUX
|
||||
|
||||
|
||||
namespace resource_api {
|
||||
|
||||
TerminalApiClient::TerminalApiClient(ApiServer *api):
|
||||
mApiServer(api)
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
TerminalApiClient::~TerminalApiClient()
|
||||
{
|
||||
join();
|
||||
}
|
||||
|
||||
void TerminalApiClient::run()
|
||||
{
|
||||
// values in milliseconds
|
||||
const int MIN_WAIT_TIME = 20; // sleep time must be smaller or equal than the smallest period
|
||||
const int IO_POLL_PERIOD = 20;
|
||||
const int API_EVENT_POLL_PERIOD = 1000;
|
||||
|
||||
int last_io_poll = 0;
|
||||
int last_event_api_poll = 0;
|
||||
|
||||
int last_char = 0;
|
||||
std::string inbuf;
|
||||
bool enter_was_pressed = false;
|
||||
|
||||
StateToken runstate_state_token;
|
||||
std::string runstate;
|
||||
|
||||
std::vector<std::string> accounts;
|
||||
|
||||
StateToken password_state_token;
|
||||
bool ask_for_password = false;
|
||||
std::string key_name;
|
||||
|
||||
while(isRunning())
|
||||
{
|
||||
// assuming sleep_time >> work_time
|
||||
// so we don't have to check the absolute time, just sleep every cycle
|
||||
usleep(MIN_WAIT_TIME * 1000);
|
||||
last_io_poll += MIN_WAIT_TIME;
|
||||
last_event_api_poll += MIN_WAIT_TIME;
|
||||
|
||||
if(last_io_poll >= IO_POLL_PERIOD)
|
||||
{
|
||||
last_io_poll = 0;
|
||||
last_char = 0;
|
||||
if(_kbhit())
|
||||
{
|
||||
enter_was_pressed = false;
|
||||
last_char = _getch();
|
||||
if(last_char > 127)
|
||||
std::cout << "Warning: non ASCII characters probably won't work." << std::endl;
|
||||
if(last_char >= ' ')// space is the first printable ascii character
|
||||
inbuf += (char) last_char;
|
||||
if(last_char == '\r' || last_char == '\n')
|
||||
enter_was_pressed = true;
|
||||
else
|
||||
enter_was_pressed = false;
|
||||
// send echo
|
||||
if(ask_for_password)
|
||||
std::cout << "*";
|
||||
else
|
||||
std::cout << (char) last_char;
|
||||
|
||||
//std::cout << "you pressed key " << (char) last_char << " as integer: " << last_char << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if(last_event_api_poll >= API_EVENT_POLL_PERIOD)
|
||||
{
|
||||
last_event_api_poll = 0;
|
||||
if(!runstate_state_token.isNull() && !isTokenValid(runstate_state_token))
|
||||
runstate_state_token = StateToken(); // must get new state with new token
|
||||
|
||||
if(!password_state_token.isNull() && !isTokenValid(password_state_token))
|
||||
password_state_token = StateToken();
|
||||
}
|
||||
|
||||
bool edge = false;
|
||||
if(runstate_state_token.isNull())
|
||||
{
|
||||
edge = true;
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("runstate");
|
||||
req.mPath.push("control");
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
resps.switchToDeserialisation();
|
||||
resps << makeKeyValueReference("runstate", runstate);
|
||||
runstate_state_token = resp.mStateToken;
|
||||
}
|
||||
if(password_state_token.isNull())
|
||||
{
|
||||
edge = true;
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("password");
|
||||
req.mPath.push("control");
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
resps.switchToDeserialisation();
|
||||
resps << makeKeyValueReference("want_password", ask_for_password);
|
||||
resps << makeKeyValueReference("key_name", key_name);
|
||||
password_state_token = resp.mStateToken;
|
||||
}
|
||||
|
||||
if(!ask_for_password && edge && runstate == "waiting_account_select")
|
||||
{
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("locations");
|
||||
req.mPath.push("control");
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
resps.switchToDeserialisation();
|
||||
std::cout << "Type a number to select an account" << std::endl;
|
||||
if(!resps.hasMore())
|
||||
std::cout << "Error: No Accounts. Use the Qt-GUI or the webinterface to create an account." << std::endl;
|
||||
int i = 0;
|
||||
accounts.clear();
|
||||
while(resps.hasMore())
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string location;
|
||||
resps.getStreamToMember()
|
||||
<< makeKeyValueReference("id", id)
|
||||
<< makeKeyValueReference("name", name)
|
||||
<< makeKeyValueReference("location", location);
|
||||
std::cout << "[" << i << "] " << name << "(" << location << ")" << std::endl;
|
||||
accounts.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
if(!ask_for_password && runstate == "waiting_account_select"
|
||||
&& last_char >= '0' && last_char <= '9'
|
||||
&& (last_char-'0') < accounts.size())
|
||||
{
|
||||
std::string acc = accounts[last_char-'0'];
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("login");
|
||||
req.mPath.push("control");
|
||||
reqs << makeKeyValueReference("id", acc);
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
inbuf.clear();
|
||||
}
|
||||
|
||||
if(edge && ask_for_password)
|
||||
{
|
||||
std::cout << "Enter the password for key " << key_name << std::endl;
|
||||
}
|
||||
|
||||
if(ask_for_password && enter_was_pressed && !inbuf.empty())
|
||||
{
|
||||
std::cout << "TerminalApiClient: got a password" << std::endl;
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("password");
|
||||
req.mPath.push("control");
|
||||
reqs << makeKeyValueReference("password", inbuf);
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
inbuf.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalApiClient::waitForResponse(ApiServer::RequestId id)
|
||||
{
|
||||
while(!mApiServer->isRequestDone(id))
|
||||
usleep(20*1000);
|
||||
}
|
||||
|
||||
bool TerminalApiClient::isTokenValid(StateToken runstate_state_token)
|
||||
{
|
||||
JsonStream reqs;
|
||||
JsonStream resps;
|
||||
Request req(reqs);
|
||||
std::stringstream ss;
|
||||
Response resp(resps, ss);
|
||||
|
||||
req.mPath.push("statetokenservice");
|
||||
req.mStream << runstate_state_token;
|
||||
reqs.switchToDeserialisation();
|
||||
|
||||
ApiServer::RequestId id = mApiServer->handleRequest(req, resp);
|
||||
waitForResponse(id);
|
||||
|
||||
resps.switchToDeserialisation();
|
||||
if(resps.hasMore())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
27
retroshare-nogui/src/TerminalApiClient.h
Normal file
27
retroshare-nogui/src/TerminalApiClient.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <api/ApiServer.h>
|
||||
#include <util/rsthreads.h>
|
||||
|
||||
namespace resource_api {
|
||||
|
||||
// allows basic control from stdin/stdout
|
||||
// - account selection
|
||||
// - login
|
||||
// - shutdown
|
||||
class TerminalApiClient: private RsThread{
|
||||
public:
|
||||
// zero setup: create an instance of this class and destroy it when not needed anymore
|
||||
// no need to call start or stop or something
|
||||
// parameter api must not be null
|
||||
TerminalApiClient(ApiServer* api);
|
||||
~TerminalApiClient();
|
||||
protected:
|
||||
// from RsThread
|
||||
virtual void run(); /* called once the thread is started. Should be overloaded by subclasses. */
|
||||
private:
|
||||
void waitForResponse(ApiServer::RequestId id);
|
||||
bool isTokenValid(StateToken st);
|
||||
ApiServer* mApiServer;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
@ -201,8 +201,14 @@ introserver {
|
||||
|
||||
webui {
|
||||
DEFINES *= ENABLE_WEBUI
|
||||
PRE_TARGETDEPS *= ../../libresapi/src/lib/libresapi.a
|
||||
LIBS += ../../libresapi/src/lib/libresapi.a -lmicrohttpd
|
||||
DEPENDPATH += ../../libresapi/src
|
||||
INCLUDEPATH += ../../libresapi/src
|
||||
HEADERS += \
|
||||
TerminalApiClient.h
|
||||
SOURCES += \
|
||||
TerminalApiClient.cpp
|
||||
}
|
||||
|
||||
sshserver {
|
||||
|
@ -58,6 +58,7 @@ void generatePasswordHash() ;
|
||||
#include <stdarg.h>
|
||||
#include "api/ApiServerMHD.h"
|
||||
#include "api/RsControlModule.h"
|
||||
#include "TerminalApiClient.h"
|
||||
#endif
|
||||
|
||||
/* Basic instructions for running libretroshare as background thread.
|
||||
@ -92,29 +93,35 @@ int main(int argc, char **argv)
|
||||
std::cerr << args.usage() << std::endl;
|
||||
}
|
||||
|
||||
resource_api::ApiServer api;
|
||||
resource_api::RsControlModule ctrl_mod(argc, argv, api.getStateTokenServer(), &api, true);
|
||||
api.addResourceHandler("control", dynamic_cast<resource_api::ResourceRouter*>(&ctrl_mod), &resource_api::RsControlModule::handleRequest);
|
||||
|
||||
resource_api::ApiServerMHD* httpd = 0;
|
||||
if(httpPort != 0)
|
||||
{
|
||||
resource_api::ApiServerMHD httpd;
|
||||
|
||||
if(!httpd.configure(docroot, httpPort, listenAddress, allowAllIps))
|
||||
httpd = new resource_api::ApiServerMHD(&api);
|
||||
if(!httpd->configure(docroot, httpPort, listenAddress, allowAllIps))
|
||||
{
|
||||
std::cerr << "Failed to configure the http server. Check your parameters." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
httpd->start();
|
||||
}
|
||||
|
||||
resource_api::RsControlModule ctrl_mod(argc, argv, httpd.getApiServer().getStateTokenServer(), &httpd.getApiServer(), true);
|
||||
httpd.getApiServer().addResourceHandler("control", dynamic_cast<resource_api::ResourceRouter*>(&ctrl_mod), &resource_api::RsControlModule::handleRequest);
|
||||
|
||||
httpd.start();
|
||||
|
||||
resource_api::TerminalApiClient tac(&api);
|
||||
while(ctrl_mod.processShouldExit() == false)
|
||||
{
|
||||
usleep(20*1000);
|
||||
}
|
||||
httpd.stop();
|
||||
|
||||
if(httpd)
|
||||
{
|
||||
httpd->stop();
|
||||
delete httpd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Retroshare startup is configured using an RsInit object.
|
||||
|
Loading…
x
Reference in New Issue
Block a user