- added profile import/creation
- fixed leaking file descriptors
- added upload handler for small files
- fixed terminal thread
- removed some unused parameter warnings

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8485 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
electron128 2015-06-16 12:35:07 +00:00
parent 3ed1be74a6
commit 8fc3917c4b
21 changed files with 1343 additions and 121 deletions

View file

@ -256,6 +256,7 @@ ApiServer::ApiServer():
mMtx("ApiServer mMtx"),
mStateTokenServer(),
mLivereloadhandler(&mStateTokenServer),
mTmpBlobStore(&mStateTokenServer),
mMainModules(0)
{
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),

View file

@ -10,6 +10,7 @@
#include "FileSearchHandler.h"
#include "TransfersHandler.h"
#include "LivereloadHandler.h"
#include "TmpBlobStore.h"
namespace resource_api{
@ -73,12 +74,14 @@ public:
void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp));
StateTokenServer* getStateTokenServer(){ return &mStateTokenServer; }
TmpBlobStore* getTmpBlobStore(){ return &mTmpBlobStore; }
private:
RsMutex mMtx;
StateTokenServer mStateTokenServer; // goes first, as others may depend on it
// is always loaded, because it has no dependencies
LivereloadHandler mLivereloadhandler;
TmpBlobStore mTmpBlobStore;
// only pointers here, to load/unload modules at runtime
ApiServerMainModules* mMainModules; // loaded when RS is started

View file

@ -2,6 +2,7 @@
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <algorithm>
@ -44,9 +45,15 @@ struct MHD_Response * MHD_create_response_from_fd(size_t size, int fd)
{
unsigned char *buf = (unsigned char *)malloc(size) ;
if(buf == 0 || read(fd,buf,size) != size)
if(buf == 0)
{
std::cerr << "replacement MHD_create_response_from_fd: malloc failed, size was " << size << std::endl;
close(fd);
return NULL ;
}
if(read(fd,buf,size) != size)
{
std::cerr << "malloc failed or READ error in file descriptor " << fd << " requested read size was " << size << std::endl;
std::cerr << "replacement MHD_create_response_from_fd: READ error in file descriptor " << fd << " requested read size was " << size << std::endl;
close(fd);
free(buf) ;
return NULL ;
@ -69,6 +76,7 @@ std::string getDefaultDocroot()
const char* API_ENTRY_PATH = "/api/v2";
const char* FILESTREAMER_ENTRY_PATH = "/fstream/";
const char* STATIC_FILES_ENTRY_PATH = "/static/";
const char* UPLOAD_ENTRY_PATH = "/upload/";
static void secure_queue_response(MHD_Connection *connection, unsigned int status_code, struct MHD_Response* response);
static void sendMessage(MHD_Connection *connection, unsigned int status, std::string message);
@ -86,6 +94,65 @@ public:
const char *upload_data, size_t *upload_data_size) = 0;
};
// handles calls to the resource_api
class MHDUploadHandler: public MHDHandlerBase
{
public:
MHDUploadHandler(ApiServer* s): mState(BEGIN), mApiServer(s){}
virtual ~MHDUploadHandler(){}
// 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 nothing 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;
}
}
std::vector<uint8_t> bytes(mRequesString.begin(), mRequesString.end());
int id = mApiServer->getTmpBlobStore()->storeBlob(bytes);
resource_api::JsonStream responseStream;
if(id)
responseStream << makeKeyValue("ok", true);
else
responseStream << makeKeyValue("ok", false);
responseStream << makeKeyValueReference("id", id);
std::string result = responseStream.getJsonString();
struct MHD_Response* resp = MHD_create_response_from_data(result.size(), (void*)result.data(), 0, 1);
MHD_add_response_header(resp, "Content-Type", "application/json");
secure_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;
};
// handles calls to the resource_api
class MHDApiHandler: public MHDHandlerBase
{
@ -94,7 +161,7 @@ public:
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 *url, const char *method, const char */*version*/,
const char *upload_data, size_t *upload_data_size)
{
// new request
@ -198,8 +265,8 @@ public:
// 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)
const char *url, const char */*method*/, const char */*version*/,
const char */*upload_data*/, size_t */*upload_data_size*/)
{
if(rsFiles == 0)
{
@ -346,7 +413,7 @@ ApiServerMHD::~ApiServerMHD()
stop();
}
bool ApiServerMHD::configure(std::string docroot, uint16_t port, std::string bind_address, bool allow_from_all)
bool ApiServerMHD::configure(std::string docroot, uint16_t port, std::string /*bind_address*/, bool allow_from_all)
{
mRootDir = docroot;
// make sure the docroot dir ends with a slash
@ -512,10 +579,14 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
url = url + strlen(STATIC_FILES_ENTRY_PATH);
// else server static files
std::string filename = mRootDir + url;
// important: binary open mode,
// important: binary open mode (windows)
// 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)
#ifdef O_BINARY
int fd = open(filename.c_str(), O_RDONLY | O_BINARY);
#else
int fd = open(filename.c_str(), O_RDONLY);
#endif
if(fd == -1)
{
#warning sending untrusted string to the browser
std::string msg = "<html><body><p>Error: can't open the requested file. Path is &quot;"+filename+"&quot;</p></body></html>";
@ -524,9 +595,9 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
}
struct stat s;
if(fstat(fileno(fd), &s) == -1)
if(fstat(fd, &s) == -1)
{
fclose(fd);
close(fd);
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");
@ -561,14 +632,21 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
else
type = "application/octet-stream";
struct MHD_Response* resp = MHD_create_response_from_fd(s.st_size, fileno(fd));
struct MHD_Response* resp = MHD_create_response_from_fd(s.st_size, fd);
MHD_add_response_header(resp, "Content-Type", type);
secure_queue_response(connection, MHD_HTTP_OK, resp);
MHD_destroy_response(resp);
fclose(fd);
return MHD_YES;
}
if(strstr(url, UPLOAD_ENTRY_PATH) == url)
{
// create a new handler and store it in con_cls
MHDHandlerBase* handler = new MHDUploadHandler(mApiServer);
*con_cls = (void*) handler;
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
}
// if url is not a valid path, then serve a help page
sendMessage(connection, MHD_HTTP_NOT_FOUND,
"This address is invalid. Try one of the adresses below:<br/>"
@ -582,8 +660,8 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
return MHD_YES;
}
void ApiServerMHD::requestCompletedCallback(struct MHD_Connection *connection,
void **con_cls, MHD_RequestTerminationCode toe)
void ApiServerMHD::requestCompletedCallback(struct MHD_Connection */*connection*/,
void **con_cls, MHD_RequestTerminationCode /*toe*/)
{
if(*con_cls)
{

View file

@ -39,6 +39,8 @@ RsControlModule::RsControlModule(int argc, char **argv, StateTokenServer* sts, A
addResourceHandler("password", this, &RsControlModule::handlePassword);
addResourceHandler("login", this, &RsControlModule::handleLogin);
addResourceHandler("shutdown", this, &RsControlModule::handleShutdown);
addResourceHandler("import_pgp", this, &RsControlModule::handleImportPgp);
addResourceHandler("create_location", this, &RsControlModule::handleCreateLocation);
}
RsControlModule::~RsControlModule()
@ -57,6 +59,12 @@ bool RsControlModule::askForPassword(const std::string &key_details, bool prev_i
cancelled = false ;
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
if(mFixedPassword != "")
{
password = mFixedPassword;
return true;
}
mWantPassword = true;
mKeyName = key_details;
mPassword = "";
@ -189,7 +197,7 @@ void RsControlModule::run()
std::cerr << "RsControlModule: Retroshare stopped. Bye!" << std::endl;
}
void RsControlModule::handleRunState(Request &req, Response &resp)
void RsControlModule::handleRunState(Request &, Response &resp)
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
std::string state;
@ -221,7 +229,7 @@ void RsControlModule::handleRunState(Request &req, Response &resp)
resp.setOk();
}
void RsControlModule::handleIdentities(Request &req, Response &resp)
void RsControlModule::handleIdentities(Request &, Response &resp)
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
@ -247,7 +255,7 @@ void RsControlModule::handleIdentities(Request &req, Response &resp)
resp.setOk();
}
void RsControlModule::handleLocations(Request &req, Response &resp)
void RsControlModule::handleLocations(Request &, Response &resp)
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
@ -313,13 +321,139 @@ void RsControlModule::handleLogin(Request &req, Response &resp)
resp.setOk();
}
void RsControlModule::handleShutdown(Request &req, Response &resp)
void RsControlModule::handleShutdown(Request &, Response &resp)
{
RsStackMutex stack(mExitFlagMtx); // ********** LOCKED **********
mProcessShouldExit = true;
resp.setOk();
}
void RsControlModule::handleImportPgp(Request &req, Response &resp)
{
std::string key_string;
req.mStream << makeKeyValueReference("key_string", key_string);
if(key_string.empty())
{
resp.setFail("required field key_string is empty");
return;
}
RsPgpId pgp_id;
std::string error_string;
if(RsAccounts::ImportIdentityFromString(key_string, pgp_id, error_string))
{
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id);
resp.setOk();
return;
}
resp.setFail("Failed to import key: " + error_string);
}
void RsControlModule::handleCreateLocation(Request &req, Response &resp)
{
std::string hidden_address;
std::string hidden_port_str;
req.mStream << makeKeyValueReference("hidden_adress", hidden_address)
<< makeKeyValueReference("hidden_port", hidden_port_str);
uint16_t hidden_port = 0;
if(hidden_address.empty() != hidden_port_str.empty())
{
resp.setFail("you must both specify string hidden_adress and string hidden_port to create a hidden node.");
return;
}
if(!hidden_address.empty())
{
int p;
if(sscanf(hidden_port_str.c_str(), "%i", &p) != 1)
{
resp.setFail("failed to parse hidden_port, not a number. Must be a dec or hex number.");
return;
}
if(p < 0 || p > 0xFFFF)
{
resp.setFail("hidden_port out of range. It must fit into uint16!");
return;
}
hidden_port = p;
}
RsPgpId pgp_id;
std::string pgp_name;
std::string pgp_password;
std::string ssl_name;
req.mStream << makeKeyValueReference("pgp_id", pgp_id)
<< makeKeyValueReference("pgp_name", pgp_name)
<< makeKeyValueReference("pgp_password", pgp_password)
<< makeKeyValueReference("ssl_name", ssl_name);
if(pgp_password.empty())
{
resp.setFail("Error: pgp_password is empty.");
return;
}
// pgp_id is set: use existing pgp key
// pgp_name is set: attempt to create a new key
if(pgp_id.isNull() && (pgp_name.empty()||pgp_password.empty()))
{
resp.setFail("You have to set pgp_id to use an existing key, or pgp_name and pgp_password to create a new pgp key.");
return;
}
if(pgp_id.isNull())
{
std::string err_string;
if(!RsAccounts::GeneratePGPCertificate(pgp_name, "", pgp_password, pgp_id, 2048, err_string))
{
resp.setFail("could not cerate pgp key: "+err_string);
return;
}
}
if(hidden_port)
RsInit::SetHiddenLocation(hidden_address, hidden_port);
std::string ssl_password = RSRandom::random_alphaNumericString(RsInit::getSslPwdLen()) ;
/* GenerateSSLCertificate - selects the PGP Account */
//RsInit::SelectGPGAccount(PGPId);
RsPeerId ssl_id;
std::string err_string;
// give the password to the password callback
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
mFixedPassword = pgp_password;
}
bool ssl_ok = RsAccounts::GenerateSSLCertificate(pgp_id, "", ssl_name, "", hidden_port!=0, ssl_password, ssl_id, err_string);
// clear fixed password to restore normal password operation
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
mFixedPassword = "";
}
if (ssl_ok)
{
// load ssl password and load account
RsInit::LoadPassword(ssl_password);
// trigger login in init thread
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********
mLoadPeerId = ssl_id;
}
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id)
<< makeKeyValueReference("pgp_name", pgp_name)
<< makeKeyValueReference("ssl_name", ssl_name)
<< makeKeyValueReference("ssl_id", ssl_id);
resp.setOk();
return;
}
resp.setFail("could not create a new location. Error: "+err_string);
}
void RsControlModule::setRunState(RunState s, std::string errstr)
{
RsStackMutex stack(mDataMtx); // ********** LOCKED **********

View file

@ -10,7 +10,7 @@ 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
// - this module handles everything, no 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
@ -47,6 +47,8 @@ private:
void handlePassword(Request& req, Response& resp);
void handleLogin(Request& req, Response& resp);
void handleShutdown(Request& req, Response& resp);
void handleImportPgp(Request& req, Response& resp);
void handleCreateLocation(Request& req, Response& resp);
void setRunState(RunState s, std::string errstr = "");
// for startup
@ -76,6 +78,11 @@ private:
bool mWantPassword;
std::string mKeyName;
std::string mPassword;
// for ssl cert generation:
// we know the password already, so we want to avoid to rpompt the user
// we store the password in this variable, it has higher priority than the normal password variable
// it is also to avoid a lock, when we make a synchronous call into librs, like in ssl cert generation
std::string mFixedPassword;
};
} // namespace resource_api

View file

@ -0,0 +1,120 @@
#include "TmpBlobStore.h"
#include <util/rsrandom.h>
#include <time.h>
namespace resource_api{
TmpBlobStore::TmpBlobStore(StateTokenServer* sts):
mStateTokenServer(sts), mMtx("TmpBlobStore::mMtx"), mBlobs(0)
{
mStateTokenServer->registerTickClient(this);
}
TmpBlobStore::~TmpBlobStore()
{
mStateTokenServer->unregisterTickClient(this);
RS_STACK_MUTEX(mMtx);
Blob* blob = mBlobs;
while(blob)
{
Blob* next = blob->next;
delete blob;
blob = next;
}
}
void TmpBlobStore::tick()
{
RS_STACK_MUTEX(mMtx);
Blob* prev = 0;
Blob* current = mBlobs;
time_t now = time(NULL);
for(; current != 0; current = current->next)
{
if(current->timestamp + BLOB_STORE_TIME_SECS < now)
{
if(prev)
prev->next = current->next;
else
mBlobs = current->next;
delete current;
return;
}
prev = current;
}
}
uint32_t TmpBlobStore::storeBlob(std::vector<uint8_t>& bytes)
{
if(bytes.size() > MAX_BLOBSIZE)
return 0;
RS_STACK_MUTEX(mMtx);
Blob* blob = new Blob();
blob->bytes.swap(bytes);
blob->timestamp = time(NULL);
blob->id = locked_make_id();
blob->next = 0;
if(!mBlobs)
{
mBlobs = blob;
return blob->id;
}
Blob* blob2 = mBlobs;
while(blob2->next)
blob2 = blob2->next;
blob2->next = blob;
return blob->id;
}
bool TmpBlobStore::fetchBlob(uint32_t blobid, std::vector<uint8_t>& bytes)
{
RS_STACK_MUTEX(mMtx);
Blob* prev = 0;
Blob* current = mBlobs;
for(; current != 0; current = current->next)
{
if(current->id == blobid)
{
bytes.swap(current->bytes);
if(prev)
prev->next = current->next;
else
mBlobs = current->next;
delete current;
return true;
}
prev = current;
}
return false;
}
TmpBlobStore::Blob* TmpBlobStore::locked_findblob(uint32_t id)
{
Blob* blob;
for(blob = mBlobs; blob != 0; blob = blob->next)
{
if(blob->id == id)
return blob;
}
return 0;
}
uint32_t TmpBlobStore::locked_make_id()
{
uint32_t id = RSRandom::random_u32();
// make sure the id is not in use already
while(locked_findblob(id))
id = RSRandom::random_u32();
return id;
}
} // namespace resource_api

View file

@ -0,0 +1,54 @@
#pragma once
#include "StateTokenServer.h"
namespace resource_api{
// store for temporary binary data
// use cases: upload of avatar image, upload of certificate file
// those files are first store in this class, then and a handler will fetch the datas later
// blobs are removed if they are not used after xx hours
class TmpBlobStore: private Tickable
{
public:
TmpBlobStore(StateTokenServer* sts);
virtual ~TmpBlobStore();
// from Tickable
virtual void tick();
// 30MB should be enough for avatars pictures, pdfs and mp3s
// can remove this limit, once we can store data on disk
static const int MAX_BLOBSIZE = 30*1000*1000;
static const int BLOB_STORE_TIME_SECS = 12*60*60;
// steals the bytes
// returns a blob number as identifier
// returns null on failure
uint32_t storeBlob(std::vector<uint8_t>& bytes);
// fetch blob with given id
// the blob is removed from the store
// return true on success
bool fetchBlob(uint32_t blobid, std::vector<uint8_t>& bytes);
private:
class Blob{
public:
std::vector<uint8_t> bytes;
time_t timestamp;
uint32_t id;
Blob* next;
};
Blob* locked_findblob(uint32_t id);
uint32_t locked_make_id();
StateTokenServer* const mStateTokenServer;
RsMutex mMtx;
uint32_t mLastBlobId;
Blob* mBlobs;
};
} // namespace resource_api