27#include <unordered_map> 
   32#include <wx/fileconf.h> 
   33#include <wx/json_defs.h> 
   34#include <wx/jsonreader.h> 
   39#include "model/nav_object_database.h" 
   50    memory = (
char*)malloc(1);
 
 
   56using PeerDlgPair = std::pair<PeerDlgResult, std::string>;
 
   62      run_status_dlg([](PeerDlg, int) { 
return PeerDlgResult::Cancel; }),
 
   63      run_pincode_dlg([] { 
return PeerDlgPair(PeerDlgResult::Cancel, 
""); }) {}
 
   65static size_t WriteMemoryCallback(
void* contents, 
size_t size, 
size_t nmemb,
 
   67  size_t realsize = size * nmemb;
 
   70  char* ptr = (
char*)realloc(mem->memory, mem->size + realsize + 1);
 
   73    std::cerr << 
"not enough memory (realloc returned NULL)\n";
 
   78  memcpy(&(mem->memory[mem->size]), contents, realsize);
 
   79  mem->size += realsize;
 
   80  mem->memory[mem->size] = 0;
 
   85static int xfer_callback(
void* clientp, [[maybe_unused]] curl_off_t dltotal,
 
   86                         [[maybe_unused]] curl_off_t dlnow, curl_off_t ultotal,
 
   88  auto peer_data = 
static_cast<PeerData*
>(clientp);
 
   92    peer_data->progress.Notify(100 * ulnow / ultotal, 
"");
 
   96#ifdef CURL_PROGRESSFUNC_CONTINUE 
   97  return CURL_PROGRESSFUNC_CONTINUE;
 
  107static long ApiPost(
const std::string& url, 
const std::string& body,
 
  109  long response_code = -1;
 
  112  CURL* c = curl_easy_init();
 
  114  curl_easy_setopt(c, CURLOPT_ENCODING, 
"identity");  
 
  115  curl_easy_setopt(c, CURLOPT_URL, url.c_str());
 
  116  curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 0L);
 
  117  curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 0L);
 
  119  curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, body.size());
 
  120  curl_easy_setopt(c, CURLOPT_COPYPOSTFIELDS, body.c_str());
 
  121  curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
 
  122  curl_easy_setopt(c, CURLOPT_WRITEDATA, (
void*)response);
 
  123  curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
 
  124  curl_easy_setopt(c, CURLOPT_XFERINFODATA, &peer_data);
 
  125  curl_easy_setopt(c, CURLOPT_XFERINFOFUNCTION, xfer_callback);
 
  126  curl_easy_setopt(c, CURLOPT_TIMEOUT, 20);
 
  128  curl_easy_setopt(c, CURLOPT_VERBOSE,
 
  129                   wxLog::GetLogLevel() >= wxLOG_Debug ? 1 : 0);
 
  131  CURLcode result = curl_easy_perform(c);
 
  133  if (result == CURLE_OK)
 
  134    curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
 
  136  curl_easy_cleanup(c);
 
  137  return response_code == -1 ? -
static_cast<long>(result) : response_code;
 
  144static int ApiGet(
const std::string& url, 
const MemoryStruct* chunk,
 
  146  long response_code = -1;
 
  148  CURL* c = curl_easy_init();
 
  149  curl_easy_setopt(c, CURLOPT_ENCODING, 
"identity");  
 
  150  curl_easy_setopt(c, CURLOPT_URL, url.c_str());
 
  151  curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 0L);
 
  152  curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 0L);
 
  153  curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
 
  154  curl_easy_setopt(c, CURLOPT_WRITEDATA, (
void*)chunk);
 
  155  curl_easy_setopt(c, CURLOPT_NOPROGRESS, 1);
 
  156  if (timeout != 0) curl_easy_setopt(c, CURLOPT_TIMEOUT, timeout);
 
  157  CURLcode result = curl_easy_perform(c);
 
  158  if (result == CURLE_OK)
 
  159    curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
 
  160  curl_easy_cleanup(c);
 
  161  return response_code == -1 ? -
static_cast<long>(result) : response_code;
 
  164static std::string GetClientKey(std::string& server_name) {
 
  167  auto key_string = server_keys.Get(
"");
 
  168  auto entries = 
ocpn::split(key_string.c_str(), 
";");
 
  169  for (
const auto& entry : entries) {
 
  171    if (server_key.size() != 2) 
continue;
 
  172    if (server_key[0] == server_name) 
return server_key[1];
 
  177static void SaveClientKey(std::string& server_name, std::string key) {
 
  180  auto config_server_keys = server_keys.Get(
"");
 
  182  auto server_keys_list = 
ocpn::split(config_server_keys.c_str(), 
";");
 
  183  std::unordered_map<std::string, std::string> key_by_server;
 
  184  for (
const auto& item : server_keys_list) {
 
  185    auto server_and_key = 
ocpn::split(item.c_str(), 
":");
 
  186    if (server_and_key.size() != 2) 
continue;
 
  187    key_by_server[server_and_key[0]] = server_and_key[1];
 
  189  key_by_server[server_name] = key;
 
  191  config_server_keys = 
"";
 
  192  for (
const auto& it : key_by_server) {
 
  193    config_server_keys += it.first + 
":" + it.second + 
";";
 
  195  server_keys.Set(config_server_keys);
 
  196  wxLog::FlushActive();
 
  200  wxString body(reply.memory);
 
  203  int num_errors = reader.
Parse(body, &root);
 
  204  if (num_errors != 0) {
 
  205    for (
const auto& error : reader.GetErrors()) {
 
  206      wxLogMessage(
"Json server reply parse error: %s",
 
  207                   error.ToStdString().c_str());
 
  211    return RestServerResult::Void;
 
  214    auto s = root[
"version"].
AsString().ToStdString();
 
  220    return RestServerResult::Void;
 
  224bool CheckKey(
const std::string& key, 
PeerData peer_data) {
 
  225  std::stringstream url;
 
  226  url << 
"https://" << peer_data.dest_ip_address << 
"/api/ping" 
  227      << 
"?source=" << g_hostname << 
"&apikey=" << key;
 
  229  long status = ApiGet(url.str(), &reply, 5);
 
  234  auto result = ParseServerJson(reply, peer_data);
 
  235  return result != RestServerResult::NewPinRequested;
 
  238void GetApiVersion(
PeerData& peer_data) {
 
  240  std::stringstream url;
 
  241  url << 
"https://" << peer_data.dest_ip_address << 
"/api/get-version";
 
  245  long response_code = ApiGet(url.str(), &chunk, 2);
 
  247  if (response_code == 200) {
 
  248    ParseServerJson(chunk, peer_data);
 
  256static bool GetApiKey(
PeerData& peer_data, std::string& key) {
 
  261    api_key = GetClientKey(peer_data.server_name);
 
  263      api_key = 
"0123456789abc";  
 
  264    std::stringstream url;
 
  265    url << 
"https://" << peer_data.dest_ip_address << 
"/api/ping" 
  266        << 
"?source=" << g_hostname << 
"&apikey=" << api_key;
 
  268    int status = ApiGet(url.str(), &chunk, 3);
 
  270      auto r = peer_data.
run_status_dlg(PeerDlg::InvalidHttpResponse, status);
 
  271      if (r == PeerDlgResult::Ok) 
continue;
 
  274    auto result = ParseServerJson(chunk, peer_data);
 
  276      case RestServerResult::NewPinRequested: {
 
  278        if (pin_result.first == PeerDlgResult::HasPincode) {
 
  279          std::string tentative_pin = 
ocpn::trim(pin_result.second);
 
  280          unsigned int_pin = atoi(tentative_pin.c_str());
 
  282          api_key = pincode.Hash();
 
  283          GetApiVersion(peer_data);
 
  285            api_key = pincode.CompatHash();
 
  287          if (!CheckKey(api_key, peer_data)) {
 
  289            if (r == PeerDlgResult::Ok) 
continue;
 
  292          SaveClientKey(peer_data.server_name, api_key);
 
  293        } 
else if (pin_result.first == PeerDlgResult::Cancel) {
 
  297                                            static_cast<int>(result));
 
  298          if (r == PeerDlgResult::Ok) 
continue;
 
  302      case RestServerResult::GenericError:
 
  305      case RestServerResult::NoError:
 
  309                                          static_cast<int>(result));
 
  310        if (r == PeerDlgResult::Ok) 
continue;
 
  320static std::string PeerDataToXml(
PeerData& peer_data) {
 
  322  std::ostringstream stream;
 
  323  int total = peer_data.routes.size() + peer_data.tracks.size() +
 
  324              peer_data.routepoints.size();
 
  326  for (
auto r : peer_data.routes) {
 
  332  for (
auto r : peer_data.routepoints) {
 
  334    gpx.AddGPXWaypoint(r);
 
  338  for (
auto r : peer_data.tracks) {
 
  344  gpx.save(stream, PUGIXML_TEXT(
" "));
 
  349static void SendObjects(std::string& body, 
const std::string& api_key,
 
  353    std::stringstream url;
 
  354    url << 
"https://" << peer_data.dest_ip_address << 
"/api/rx_object" 
  355        << 
"?source=" << g_hostname << 
"&apikey=" << api_key;
 
  356    if (peer_data.
overwrite) url << 
"&force=1";
 
  357    if (peer_data.
activate) url << 
"&activate=1";
 
  360    long response_code = ApiPost(url.str(), body, peer_data, &chunk);
 
  361    if (response_code == 200) {
 
  362      wxString json(chunk.memory);
 
  366      int num_errors = reader.
Parse(json, &root);
 
  368        wxLogDebug(
"SendObjects, parse errors: %d", num_errors);
 
  370      int result = root[
"result"].
AsInt();
 
  378      peer_data.
run_status_dlg(PeerDlg::InvalidHttpResponse, response_code);
 
  385static int CheckChunk(
struct MemoryStruct& chunk, 
const std::string& guid) {
 
  386  wxString body(chunk.memory);
 
  389  int num_errors = reader.
Parse(body, &root);
 
  391    wxLogDebug(
"CheckChunk: parsing errors found: %d", num_errors);
 
  392  int result = root[
"result"].
AsInt();
 
  394    wxLogDebug(
"Server rejected guid %s, status: %d", guid.c_str(), result);
 
  401static bool CheckObjects(
const std::string& api_key, 
PeerData& peer_data) {
 
  402  std::stringstream url;
 
  403  url << 
"https://" << peer_data.dest_ip_address << 
"/api/writable" 
  404      << 
"?source=" << g_hostname << 
"&apikey=" << api_key << 
"&guid=";
 
  405  for (
const auto& r : peer_data.routes) {
 
  406    std::string guid = r->GetGUID().ToStdString();
 
  407    std::string full_url = url.str() + guid;
 
  409    if (ApiGet(full_url, &chunk) != 200) {
 
  410      wxLogMessage(
"Cannot check /api/writable for route %s", guid.c_str());
 
  413    int result = CheckChunk(chunk, guid);
 
  414    if (result != 0) 
return false;
 
  416  for (
const auto& t : peer_data.tracks) {
 
  417    std::string guid = t->m_GUID.ToStdString();
 
  418    std::string full_url = url.str() + guid;
 
  420    if (ApiGet(full_url, &chunk) != 200) {
 
  421      wxLogMessage(
"Cannot check /api/writable for track %s", guid.c_str());
 
  424    int result = CheckChunk(chunk, guid);
 
  425    if (result != 0) 
return false;
 
  427  for (
const auto& rp : peer_data.routepoints) {
 
  428    std::string guid = rp->m_GUID.ToStdString();
 
  429    std::string full_url = url.str() + guid;
 
  431    if (ApiGet(full_url, &chunk) != 200) {
 
  432      wxLogMessage(
"Cannot check /api/writable for waypoint %s", guid.c_str());
 
  435    int result = CheckChunk(chunk, guid);
 
  436    if (result != 0) 
return false;
 
  442  if (peer_data.routes.empty() && peer_data.routepoints.empty() &&
 
  443      peer_data.tracks.empty()) {
 
  447  bool apikey_ok = GetApiKey(peer_data, api_key);
 
  448  if (!apikey_ok) 
return false;
 
  453  std::string body = PeerDataToXml(peer_data);
 
  454  SendObjects(body, api_key, peer_data);
 
 
  459  if (peer_data.routes.empty() && peer_data.routepoints.empty() &&
 
  460      peer_data.tracks.empty()) {
 
  464  bool apikey_ok = GetApiKey(peer_data, apikey);
 
  465  if (!apikey_ok) 
return false;
 
  466  return CheckObjects(apikey, peer_data);
 
 
Wrapper for configuration variables which lives in a wxBaseConfig object.
Generic event handling between MVC Model and Controller based on a shared EventVar variable.
void Notify() override
Notify all listeners, no data supplied.
A random generated int value with accessors for string and hashcode.
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
wxString AsString() const
Return the stored value as a wxWidget's string.
int AsInt() const
Return the stored value as an integer.
Global variables stored in configuration file.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
std::string trim(std::string s)
Strip possibly trailing and/or leading space characters in s.
Notify()/Listen() configuration variable wrapper.
Miscellaneous utilities, many of which string related.
bool SendNavobjects(PeerData &peer_data)
Send data to server peer.
bool CheckNavObjects(PeerData &peer_data)
Check if server peer deems that writing these objects can be accepted i.
Peer client non-gui abstraction.
RestServerResult
Return codes from HandleServerMessage and eventually in the http response.
Semantic version encode/decode object.
bool activate
API parameter, activate route after transfer.
EventVar & progress
Notified with transfer percent progress (0-100).
std::function< std::pair< PeerDlgResult, std::string >()> run_pincode_dlg
Pin confirm dialog, returns new {0, user_pin} or {error_code, error msg)
SemanticVersion api_version
server API version
std::function< PeerDlgResult(PeerDlg, int)> run_status_dlg
Dialog displaying status (good, bad, ...)
bool overwrite
API parameter, force overwrite w/o server dialogs.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.