Skip to content
Permalink
Browse files

ShareAPI: add support for temp shares

  • Loading branch information...
maksis committed Mar 8, 2019
1 parent 87baae7 commit e1649cb2ce1ddb2b6bdf0cb542a1d9bc3e560397
@@ -21,4 +21,5 @@
*.opendb
*.exe
*- Copy*
*.tmp
/enc_temp_folder
@@ -20,13 +20,19 @@

#include <api/ShareApi.h>

#include <web-server/Session.h>
#include <web-server/WebServerManager.h>

#include <api/common/Deserializer.h>
#include <api/common/FileSearchParser.h>
#include <api/common/Serializer.h>

#include <web-server/JsonUtil.h>

#include <airdcpp/ClientManager.h>
#include <airdcpp/HashManager.h>
#include <airdcpp/HubEntry.h>
#include <airdcpp/Magnet.h>
#include <airdcpp/SearchResult.h>
#include <airdcpp/ShareManager.h>
#include <airdcpp/SharePathValidator.h>
@@ -41,7 +47,10 @@ namespace webserver {
"share_refresh_completed",

"share_exclude_added",
"share_exclude_removed"
"share_exclude_removed",

"share_temp_item_added",
"share_temp_item_removed",
},
Access::SETTINGS_EDIT
)
@@ -60,6 +69,10 @@ namespace webserver {
METHOD_HANDLER(Access::SETTINGS_EDIT, METHOD_POST, (EXACT_PARAM("excludes"), EXACT_PARAM("add")), ShareApi::handleAddExclude);
METHOD_HANDLER(Access::SETTINGS_EDIT, METHOD_POST, (EXACT_PARAM("excludes"), EXACT_PARAM("remove")), ShareApi::handleRemoveExclude);

METHOD_HANDLER(Access::SETTINGS_VIEW, METHOD_GET, (EXACT_PARAM("temp_shares")), ShareApi::handleGetTempShares);
METHOD_HANDLER(Access::SETTINGS_EDIT, METHOD_POST, (EXACT_PARAM("temp_shares")), ShareApi::handleAddTempShare);
METHOD_HANDLER(Access::SETTINGS_EDIT, METHOD_DELETE, (EXACT_PARAM("temp_shares"), TOKEN_PARAM), ShareApi::handleRemoveTempShare);

createHook("share_file_validation_hook", [this](const string& aId, const string& aName) {
return ShareManager::getInstance()->getValidator().fileValidationHook.addSubscriber(aId, aName, HOOK_HANDLER(ShareApi::fileValidationHook));
}, [this](const string& aId) {
@@ -147,6 +160,99 @@ namespace webserver {
return websocketpp::http::status_code::ok;
}

api_return ShareApi::handleAddTempShare(ApiRequest& aRequest) {
//auto content = JsonUtil::getField<string>("content", aRequest.getRequestBody(), false);
const auto fileId = JsonUtil::getField<string>("file_id", aRequest.getRequestBody(), false);
const auto name = JsonUtil::getField<string>("name", aRequest.getRequestBody(), false);
const auto user = Deserializer::deserializeUser(aRequest.getRequestBody(), false, true);

ProfileToken shareProfileToken;
{
const auto hubUrl = JsonUtil::getField<string>("hub_url", aRequest.getRequestBody(), false);
auto client = ClientManager::getInstance()->getClient(hubUrl);
if (client) {
shareProfileToken = client->get(HubSettings::ShareProfile);
} else {
aRequest.setResponseErrorStr("Hub " + hubUrl + " was not found");
return websocketpp::http::status_code::bad_request;
}
}

//const auto filePath = Util::getTempPath() + Util::toString(Util::rand()) + "_" + name;
const auto filePath = aRequest.getSession()->getServer()->getFileServer().getTempFilePath(fileId);
if (filePath.empty() || !Util::fileExists(filePath)) {
aRequest.setResponseErrorStr("File with an ID " + fileId + " was not found");
return websocketpp::http::status_code::bad_request;
}

/*int64_t size = 0;
// Save on disk
try {
File file(filePath, File::WRITE, File::TRUNCATE | File::CREATE, File::BUFFER_SEQUENTIAL);
file.write(Util::base64_decode(content));
size = file.getSize();
} catch (const FileException& e) {
aRequest.setResponseErrorStr("Failed to write the file: " + e.getError());
return websocketpp::http::status_code::internal_server_error;
}*/

const auto size = File::getSize(filePath);
TTHValue tth;

{
int64_t sizeLeft = 0;
bool cancelHashing = false;

// Calculate TTH
try {
HashManager::getInstance()->getFileTTH(filePath, size, true, tth, sizeLeft, cancelHashing);
} catch (const Exception& e) {
aRequest.setResponseErrorStr("Failed to calculate file TTH: " + e.getError());
return websocketpp::http::status_code::internal_server_error;
}
}

auto key = user ? user->getCID().toBase32() : Util::emptyString;
auto item = ShareManager::getInstance()->addTempShare(key, tth, name, filePath, size, shareProfileToken);

aRequest.setResponseBody({
{ "magnet", Magnet::makeMagnet(tth, name, size) },
{ "item", !item ? json() : serializeTempShare(*item) }
});

return websocketpp::http::status_code::ok;
}

api_return ShareApi::handleRemoveTempShare(ApiRequest& aRequest) {
auto token = aRequest.getTokenParam();
if (!ShareManager::getInstance()->removeTempShare(token)) {
aRequest.setResponseErrorStr("Temp share was not found");
return websocketpp::http::status_code::bad_request;
}

return websocketpp::http::status_code::no_content;
}

json ShareApi::serializeTempShare(const TempShareInfo& aInfo) noexcept {
return {
{ "id", aInfo.id },
{ "name", aInfo.name },
{ "path", aInfo.path },
{ "size", aInfo.size },
{ "tth", aInfo.tth.toBase32() },
{ "time_added", aInfo.timeAdded },
{ "type", Serializer::serializeFileType(aInfo.path) }
};
}

api_return ShareApi::handleGetTempShares(ApiRequest& aRequest) {
const auto tempShares = ShareManager::getInstance()->getTempShares();

aRequest.setResponseBody(Serializer::serializeList(tempShares, serializeTempShare));
return websocketpp::http::status_code::ok;
}

api_return ShareApi::handleGetExcludes(ApiRequest& aRequest) {
aRequest.setResponseBody(ShareManager::getInstance()->getExcludedPaths());
return websocketpp::http::status_code::ok;
@@ -187,6 +293,19 @@ namespace webserver {
});
}


void ShareApi::on(ShareManagerListener::TempFileAdded, const TempShareInfo& aFile) noexcept {
maybeSend("share_temp_item_added", [&] {
return serializeTempShare(aFile);
});
}

void ShareApi::on(ShareManagerListener::TempFileRemoved, const TempShareInfo& aFile) noexcept {
maybeSend("share_temp_item_removed", [&] {
return serializeTempShare(aFile);
});
}

api_return ShareApi::handleRefreshShare(ApiRequest& aRequest) {
auto incoming = JsonUtil::getOptionalFieldDefault<bool>("incoming", aRequest.getRequestBody(), false);
ShareManager::getInstance()->refresh(incoming);
@@ -41,6 +41,11 @@ namespace webserver {
api_return handleRemoveExclude(ApiRequest& aRequest);
api_return handleGetExcludes(ApiRequest& aRequest);

static json serializeTempShare(const TempShareInfo& aInfo) noexcept;
api_return handleAddTempShare(ApiRequest& aRequest);
api_return handleRemoveTempShare(ApiRequest& aRequest);
api_return handleGetTempShares(ApiRequest& aRequest);

api_return handleGetStats(ApiRequest& aRequest);
api_return handleSearch(ApiRequest& aRequest);

@@ -54,6 +59,9 @@ namespace webserver {
void on(ShareManagerListener::ExcludeAdded, const string& aPath) noexcept override;
void on(ShareManagerListener::ExcludeRemoved, const string& aPath) noexcept override;

void on(ShareManagerListener::TempFileAdded, const TempShareInfo& aFile) noexcept override;
void on(ShareManagerListener::TempFileRemoved, const TempShareInfo& aFile) noexcept override;

void onShareRefreshed(const RefreshPathList& aRealPaths, uint8_t aTaskType, const string& aSubscription) noexcept;

static string refreshTypeToString(uint8_t aTaskType) noexcept;
@@ -60,18 +60,27 @@ namespace webserver {
return TTHValue(aTTH);
}

UserPtr Deserializer::parseUser(const json& aJson, bool aAllowMe) {
return getUser(JsonUtil::getField<string>("cid", aJson, false), aAllowMe);
UserPtr Deserializer::deserializeUser(const json& aJson, bool aAllowMe, bool aOptional) {
const auto cid = JsonUtil::getOptionalField<string>("cid", aJson, !aOptional);
if (!cid) {
return nullptr;
}

return getUser(*cid, aAllowMe);
}

UserPtr Deserializer::deserializeUser(const json& aJson, bool aAllowMe, const string& aFieldName) {
auto userJson = JsonUtil::getRawField(aFieldName, aJson);
/*UserPtr Deserializer::deserializeUser(const json& aJson, bool aAllowMe, const string& aFieldName, bool aOptional) {
auto userJson = JsonUtil::getRawField(aFieldName, aJson, !aOptional);
if (userJson.is_null()) {
return nullptr;
}
return parseUser(userJson, aAllowMe);
}
}*/

HintedUser Deserializer::deserializeHintedUser(const json& aJson, bool aAllowMe, const string& aFieldName) {
auto userJson = JsonUtil::getRawField(aFieldName, aJson);
auto user = parseUser(userJson, aAllowMe);
auto user = deserializeUser(userJson, aAllowMe, false);
return HintedUser(user, JsonUtil::getField<string>("hub_url", userJson, aAllowMe && user == ClientManager::getInstance()->getMe()));
}

@@ -38,7 +38,8 @@ namespace webserver {

static TTHValue parseTTH(const string& aTTH);

static UserPtr deserializeUser(const json& aJson, bool aAllowMe = false, const string& aFieldName = "user");
//static UserPtr deserializeUser(const json& aJson, bool aAllowMe = false, const string& aFieldName = "user", bool aOptional = false);
static UserPtr deserializeUser(const json& aJson, bool aAllowMe, bool aOptional = false);
static HintedUser deserializeHintedUser(const json& aJson, bool aAllowMe = false, const string& aFieldName = "user");
static OnlineUserPtr deserializeOnlineUser(const json& aJson, bool aAllowMe = false, const string& aFieldName = "user");
static TTHValue deserializeTTH(const json& aJson);
@@ -58,7 +59,6 @@ namespace webserver {
static OptionalProfileToken deserializeOptionalShareProfile(const json& aJson);
private:
static LogMessage::Severity parseSeverity(const string& aText);
static UserPtr parseUser(const json& aJson, bool aAllowMe);
};
}

@@ -39,7 +39,10 @@ namespace webserver {
}

FileServer::~FileServer() {

RLock l(cs);
for (const auto& f: tempFiles) {
File::deleteFile(f.second);
}
}

const string& FileServer::getResourcePath() const noexcept {
@@ -280,18 +283,74 @@ namespace webserver {
return true;
}

websocketpp::http::status_code::value FileServer::handleRequest(const string& aResource, const websocketpp::http::parser::request& aRequest,

websocketpp::http::status_code::value FileServer::handlePostRequest(const websocketpp::http::parser::request& aRequest,
std::string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept {

const auto& requestPath = aRequest.get_uri();
if (requestPath == "/temp") {
if (!aSession || !aSession->getUser()->hasPermission(Access::FILESYSTEM_EDIT)) {
//throw RequestException(websocketpp::http::status_code::unauthorized, "Not authorized");
output_ = "Not authorized";
return websocketpp::http::status_code::unauthorized;
}

const auto fileName = Util::toString(Util::rand());
const auto filePath = Util::getTempPath() + fileName;

try {
File file(filePath, File::WRITE, File::TRUNCATE | File::CREATE, File::BUFFER_SEQUENTIAL);
file.write(aRequest.get_body());
} catch (const FileException& e) {
output_ = "Failed to write the file: " + e.getError();
return websocketpp::http::status_code::internal_server_error;
}

{
WLock l(cs);
tempFiles.emplace(fileName, filePath);
}

headers_.emplace_back("Location", fileName);
return websocketpp::http::status_code::created;
}

output_ = "Requested resource was not found";
return websocketpp::http::status_code::not_found;
}

string FileServer::getTempFilePath(const string& fileId) const noexcept {
RLock l(cs);
auto i = tempFiles.find(fileId);
return i != tempFiles.end() ? i->second : Util::emptyString;
}

websocketpp::http::status_code::value FileServer::handleRequest(const websocketpp::http::parser::request& aRequest,
string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept {

if (aRequest.get_method() == "GET") {
return handleGetRequest(aRequest, output_, headers_, aSession);
} else if (aRequest.get_method() == "POST") {
return handlePostRequest(aRequest, output_, headers_, aSession);
}

output_ = "Requested resource was not found";
return websocketpp::http::status_code::not_found;
}

websocketpp::http::status_code::value FileServer::handleGetRequest(const websocketpp::http::parser::request& aRequest,
string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept {

dcdebug("Requesting file %s\n", aResource.c_str());
const auto& requestUrl = aRequest.get_uri();
dcdebug("Requesting file %s\n", requestUrl.c_str());

// Get the disk path
string filePath;
try {
if (aResource.length() >= 6 && aResource.compare(0, 6, "/view/") == 0) {
filePath = parseViewFilePath(aResource.substr(6), headers_, aSession);
if (requestUrl.length() >= 6 && requestUrl.compare(0, 6, "/view/") == 0) {
filePath = parseViewFilePath(requestUrl.substr(6), headers_, aSession);
} else {
filePath = parseResourcePath(aResource, aRequest, headers_);
filePath = parseResourcePath(requestUrl, aRequest, headers_);
}
} catch (const RequestException& e) {
output_ = e.what();
@@ -337,7 +396,7 @@ namespace webserver {
{
// Get the mime type (but get it from the original request with gzipped content)
auto usingEncoding = find_if(headers_.begin(), headers_.end(), CompareFirst<string, string>("Content-Encoding")) != headers_.end();
auto type = getMimeType(usingEncoding ? aResource : filePath);
auto type = getMimeType(usingEncoding ? requestUrl : filePath);
if (type) {
headers_.emplace_back("Content-Type", type);
}
@@ -22,6 +22,8 @@
#include "stdinc.h"

#include <airdcpp/typedefs.h>
#include <airdcpp/CriticalSection.h>


namespace webserver {
class FileServer {
@@ -32,11 +34,19 @@ namespace webserver {
void setResourcePath(const string& aPath) noexcept;
const string& getResourcePath() const noexcept;

websocketpp::http::status_code::value handleRequest(const string& aResource, const websocketpp::http::parser::request& aRequest,
websocketpp::http::status_code::value handleRequest(const websocketpp::http::parser::request& aRequest,
std::string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept;

static const char* getMimeType(const string& aFileName) noexcept;

string getTempFilePath(const string& fileId) const noexcept;
private:
websocketpp::http::status_code::value handleGetRequest(const websocketpp::http::parser::request& aRequest,
std::string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept;

websocketpp::http::status_code::value handlePostRequest(const websocketpp::http::parser::request& aRequest,
std::string& output_, StringPairList& headers_, const SessionPtr& aSession) noexcept;

string resourcePath;

string parseResourcePath(const string& aResource, const websocketpp::http::parser::request& aRequest, StringPairList& headers_) const;
@@ -52,6 +62,9 @@ namespace webserver {
static string formatPartialRange(int64_t aStart, int64_t aEnd, int64_t aFileSize) noexcept;

static void addCacheControlHeader(StringPairList& headers_, int aDaysValid) noexcept;

mutable SharedMutex cs;
StringMap tempFiles;
};
}

0 comments on commit e1649cb

Please sign in to comment.
You can’t perform that action at this time.