mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-07-03 10:36:57 -04:00
webui:
- 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:
parent
3ed1be74a6
commit
8fc3917c4b
21 changed files with 1343 additions and 121 deletions
|
@ -256,6 +256,7 @@ ApiServer::ApiServer():
|
|||
mMtx("ApiServer mMtx"),
|
||||
mStateTokenServer(),
|
||||
mLivereloadhandler(&mStateTokenServer),
|
||||
mTmpBlobStore(&mStateTokenServer),
|
||||
mMainModules(0)
|
||||
{
|
||||
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""+filename+""</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)
|
||||
{
|
||||
|
|
|
@ -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 **********
|
||||
|
|
|
@ -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
|
||||
|
|
120
libresapi/src/api/TmpBlobStore.cpp
Normal file
120
libresapi/src/api/TmpBlobStore.cpp
Normal 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
|
54
libresapi/src/api/TmpBlobStore.h
Normal file
54
libresapi/src/api/TmpBlobStore.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue