Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "third_party/tomlplusplus"]
path = third_party/tomlplusplus
url = https://github.com/marzer/tomlplusplus.git
[submodule "third_party/nlohmann_json"]
path = third_party/nlohmann_json
url = https://github.com/nlohmann/json.git
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ find_package(MAVSDK REQUIRED)

include_directories(third_party/cpp-httplib/)
include_directories(third_party/tomlplusplus/)
include_directories(third_party/nlohmann_json/single_include)
include_directories(${SQLite3_INCLUDE_DIRS})

add_executable(${PROJECT_NAME}
src/main.cpp
src/ServerInterface.cpp
src/MealaServerInterface.cpp
src/LogLoader.cpp)

target_link_libraries(${PROJECT_NAME}
Expand Down
2 changes: 2 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
connection_url = "udp://:14551"
local_server = "http://127.0.0.1:5006"
upload_service = 0
remote_server = "https://review.px4.io"
credentials_file = ""
email = ""
upload_enabled = false
public_logs = false
17 changes: 16 additions & 1 deletion src/LogLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <future>
#include <regex>
#include <fstream>
#include "ServerInterface.hpp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "ServerInterface.hpp"

already in the hpp


namespace fs = std::filesystem;

Expand All @@ -27,6 +28,9 @@ LogLoader::LogLoader(const LogLoader::Settings& settings)
.db_path = _settings.application_directory + "local_server.db",
.upload_enabled = true, // Always upload to local server
.public_logs = true, // Public required true for searching using Web UI
.upload_service = UploadService::FlightReview, // Local server is always FlightReview,
.credentials_file = "" // FlightReview does not need credentials

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

};

// Setup remote server interface
Expand All @@ -37,10 +41,21 @@ LogLoader::LogLoader(const LogLoader::Settings& settings)
.db_path = _settings.application_directory + "remote_server.db",
.upload_enabled = settings.upload_enabled,
.public_logs = settings.public_logs,
.upload_service = settings.upload_service,
.credentials_file = settings.credentials_file
};

_local_server = std::make_shared<ServerInterface>(local_server_settings);
_remote_server = std::make_shared<ServerInterface>(remote_server_settings);

// _remote_server = std::make_shared<ServerInterface>(remote_server_settings);
if (_settings.upload_service == UploadService::Meala) {
MealaCredentials creds;
creds.credentials_file = _settings.credentials_file;
_remote_server = std::make_shared<MealaServerInterface>(remote_server_settings, creds);

} else {
_remote_server = std::make_shared<ServerInterface>(remote_server_settings);
}

std::cout << std::fixed << std::setprecision(8);

Expand Down
3 changes: 3 additions & 0 deletions src/LogLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
class LogLoader
{
public:

struct Settings {
std::string email;
std::string local_server;
Expand All @@ -19,6 +20,8 @@ class LogLoader
std::string application_directory;
bool upload_enabled;
bool public_logs;
UploadService upload_service;
std::string credentials_file;
};

LogLoader(const Settings& settings);
Expand Down
138 changes: 138 additions & 0 deletions src/MealaServerInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include "ServerInterface.hpp"
#include <fstream>
#include <filesystem>
#include <vector>
#include <sstream>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>
#include <nlohmann/json.hpp>
#include "Log.hpp"

namespace fs = std::filesystem;
using json = nlohmann::json;

MealaServerInterface::MealaServerInterface(const Settings& settings, const MealaCredentials& creds)
: ServerInterface(settings), _creds(creds)
{}

bool MealaServerInterface::login()
{
std::string username, password, token;

if (!_creds.credentials_file.empty() && fs::exists(_creds.credentials_file)) {
std::ifstream f(_creds.credentials_file);
json creds_json;
f >> creds_json;
username = creds_json.value("username", "");
token = creds_json.value("token", "");

} else {
username = _creds.username;
password = _creds.password;
}

httplib::Params params;
params.emplace("username", username);

if (!token.empty()) {
params.emplace("token", token);

} else {
params.emplace("password", password);
}

std::string url_path = "/login";
httplib::Result res;

if (_protocol == Protocol::Https) {
httplib::SSLClient cli(_settings.server_url);
res = cli.Post(url_path.c_str(), params);

} else {
httplib::Client cli(_settings.server_url);
res = cli.Post(url_path.c_str(), params);
}

if (res && res->status == 200) {
auto it = res->headers.find("Set-Cookie");

if (it != res->headers.end()) {
_session_cookie = it->second;
_logged_in = true;
return true;
}
}

_logged_in = false;
return false;
}

ServerInterface::UploadResult MealaServerInterface::upload(const std::string& filepath)
{
if (!_logged_in && !login()) {
return {false, 401, "Login to Meala failed"};
}

constexpr size_t chunk_size = 5 * 1024 * 1024; // 5 MB

if (!fs::exists(filepath)) {
return {false, 404, "Log file does not exist: " + filepath};
}

size_t file_size = fs::file_size(filepath);
size_t total_chunks = (file_size / chunk_size) + (file_size % chunk_size ? 1 : 0);
std::string filename = fs::path(filepath).filename().string();

std::ifstream file(filepath, std::ios::binary);

if (!file) {
return {false, 0, "Failed to open file"};
}

for (size_t chunk_index = 0; chunk_index < total_chunks; ++chunk_index) {
size_t offset = chunk_index * chunk_size;
size_t this_chunk_size = std::min(chunk_size, file_size - offset);
std::vector<char> buffer(this_chunk_size);
file.read(buffer.data(), this_chunk_size);

httplib::MultipartFormDataItems items = {
{"comments", "Log uploaded from C++ API", "", ""},
{"battery", "", "", ""},
{"pic", "", "", ""},
{"gso", "", "", ""},
{"vehicle_id", "", "", ""},
{"dzchunkbyteoffset", std::to_string(offset), "", ""},
{"dzchunkindex", std::to_string(chunk_index), "", ""},
{"dztotalchunkcount", std::to_string(total_chunks), "", ""},
{"files", std::string(buffer.begin(), buffer.end()), filename, "application/octet-stream"}
};

std::string url_path = "/upload/api";
httplib::Headers headers;

if (!_session_cookie.empty()) {
headers.emplace("Cookie", _session_cookie);
}

LOG("Uploading " << filename << " to " << _settings.server_url);

httplib::Result res;

if (_protocol == Protocol::Https) {
httplib::SSLClient cli(_settings.server_url);
res = cli.Post(url_path.c_str(), headers, items);

} else {
httplib::Client cli(_settings.server_url);
res = cli.Post(url_path.c_str(), headers, items);
}

if (!res || res->status != 200) {
LOG("Upload failed on chunk " << (chunk_index + 1) << ": "
<< (res ? std::to_string(res->status) : "No response"));
return {false, res ? res->status : 0, "No response from server"};
}
}

return {true, 200, "Upload completed successfully."};
}
3 changes: 2 additions & 1 deletion src/ServerInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <functional>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>
#include <vector>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary?


namespace fs = std::filesystem;

Expand Down Expand Up @@ -597,4 +598,4 @@ ServerInterface::DatabaseEntry ServerInterface::row_to_db_entry(sqlite3_stmt* st
entry.downloaded = sqlite3_column_int(stmt, 4) != 0;

return entry;
}
}
38 changes: 34 additions & 4 deletions src/ServerInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#include <vector>
#include <sqlite3.h>
#include <mavsdk/plugins/log_files/log_files.h>
#include <optional>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary?


enum class UploadService {
FlightReview = 0,
Meala = 1
};

class ServerInterface
{
Expand All @@ -15,6 +21,8 @@ class ServerInterface
std::string db_path; // Path to this server's database
bool upload_enabled {};
bool public_logs {};
UploadService upload_service;
std::string credentials_file; // For Meala credentials
};

struct UploadResult {
Expand Down Expand Up @@ -59,23 +67,45 @@ class ServerInterface
void start();
void stop();

private:
protected:
enum class Protocol {
Http,
Https
};
virtual UploadResult upload(const std::string& filepath);
Protocol _protocol {Protocol::Https};
Settings _settings;

private:
void sanitize_url_and_determine_protocol();
UploadResult upload(const std::string& filepath);
bool server_reachable();

// Database operations
bool execute_query(const std::string& query);
bool add_to_blacklist(const std::string& uuid, const std::string& reason);
DatabaseEntry row_to_db_entry(sqlite3_stmt* stmt);

Settings _settings;
Protocol _protocol {Protocol::Https};
bool _should_exit = false;
sqlite3* _db = nullptr;
};

struct MealaCredentials {
std::string username;
std::string password;
std::string token;
std::string credentials_file;
};

class MealaServerInterface : public ServerInterface
{
public:
MealaServerInterface(const Settings& settings, const MealaCredentials& creds);

bool login();
UploadResult upload(const std::string& filepath) override;

private:
MealaCredentials _creds;
std::string _session_cookie;
bool _logged_in = false;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put this class declaration and MealaCredentials struct in a separate header

4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ int main()
.mavsdk_connection_url = config["connection_url"].value_or("0.0.0"),
.application_directory = std::string(getenv("HOME")) + "/.local/share/logloader/",
.upload_enabled = config["upload_enabled"].value_or(false),
.public_logs = config["public_logs"].value_or(false)
.public_logs = config["public_logs"].value_or(false),
.upload_service = static_cast<UploadService>(config["upload_service"].value_or(UploadService::FlightReview)), // 0 for FlightReview, 1 for Meala
.credentials_file = config["credentials_file"].value_or(std::string(getenv("HOME")) + "/.local/share/logloader/meala_creds.cert")
};

_log_loader = std::make_shared<LogLoader>(settings);
Expand Down
1 change: 1 addition & 0 deletions third_party/nlohmann_json
Submodule nlohmann_json added at 52d3f6