29#include <condition_variable>
38#include <wx/tokenzr.h>
39#include <wx/fileconf.h>
43#include "REST_server.h"
45#include "config_vars.h"
47#include "REST_server_gui.h"
52#include "nav_object_database.h"
54extern bool g_bportable;
55extern std::vector<Track*> g_TrackList;
57Route *GPXLoadRoute1(pugi::xml_node &wpt_node,
bool b_fullviz,
58 bool b_layer,
bool b_layerviz,
int layer_id,
60Track *GPXLoadTrack1(pugi::xml_node &trk_node,
bool b_fullviz,
61 bool b_layer,
bool b_layerviz,
int layer_id);
62RoutePoint *GPXLoadWaypoint1(pugi::xml_node &wpt_node,
63 wxString def_symbol_name, wxString GUID,
64 bool b_fullviz,
bool b_layer,
65 bool b_layerviz,
int layer_id);
68bool InsertTrack(
Track *pTentTrack,
bool bApplyChanges =
false);
69bool InsertWpt(
RoutePoint *pWp,
bool overwrite);
76std::condition_variable return_status_condition;
101 wxEventType commandType = wxEVT_RESTFUL_SERVER,
int id = 0)
102 : wxEvent(
id, commandType){};
106 void SetPayload(std::shared_ptr<std::string> data) {
109 void SetSource(std::string source){
110 m_source_peer = source;
112 void SetAPIKey(std::string key){
115 std::shared_ptr<std::string> GetPayload() {
return m_payload; }
118 wxEvent* Clone()
const {
121 newevent->m_payload = this->m_payload;
122 newevent->m_source_peer = this->m_source_peer;
123 newevent->m_api_key = this->m_api_key;
127 std::shared_ptr<std::string> m_payload;
128 std::string m_source_peer;
129 std::string m_api_key;
141RESTServer::RESTServer()
142 : m_Thread_run_flag(-1)
145 m_PINCreateDialog = NULL;
148 Bind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage,
153RESTServer::~RESTServer() { }
155bool RESTServer::StartServer(std::string certificate_location) {
157 m_certificate_directory = certificate_location;
158 m_cert_file = m_certificate_directory + std::string(
"cert.pem");
159 m_key_file = m_certificate_directory + std::string(
"key.pem");
168 GetSecondaryThread()->Run();
173void RESTServer::StopServer() {
175 wxString::Format(_T(
"Stopping REST service")));
177 Unbind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage,
181 if (m_pSecondary_Thread) {
182 m_pSecondary_Thread->Delete();
184 if (m_bsec_thread_active)
186 wxLogMessage(_T(
"Stopping Secondary Thread"));
188 m_Thread_run_flag = 0;
191 while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
194 if (m_Thread_run_flag < 0)
195 msg.Printf(_T(
"Stopped in %d sec."), 10 - tsec);
197 msg.Printf(_T(
"Not Stopped after 10 sec."));
201 m_pSecondary_Thread = NULL;
202 m_bsec_thread_active =
false;
206bool RESTServer::LoadConfig(
void )
208 if( TheBaseConfig() ) {
209 TheBaseConfig()->SetPath(
"/Settings/RESTServer");
213 TheBaseConfig()->Read(
"ServerKeys", &key_string );
214 wxStringTokenizer st(key_string, _T(
";"));
215 while (st.HasMoreTokens()) {
216 wxString s1 = st.GetNextToken();
217 wxString client_name = s1.BeforeFirst(
':');
218 wxString client_key = s1.AfterFirst(
':');
220 m_key_map[client_name.ToStdString()] = client_key.ToStdString();
222 TheBaseConfig()->Read(
"ServerOverwriteDuplicates", &m_b_overwrite, 0 );
228bool RESTServer::SaveConfig(
void )
230 if( TheBaseConfig() ) {
231 TheBaseConfig()->SetPath( _T (
"/Settings/RESTServer" ) );
234 for (
auto it : m_key_map){
235 wxString item = it.first.c_str() + wxString(
":") + it.second.c_str() + wxString(
";");
239 TheBaseConfig()->Write(
"ServerKeys", key_string );
241 TheBaseConfig()->Write(
"ServerOverwriteDuplicates", m_b_overwrite );
247unsigned long long PINtoRandomKey(
int dpin) {
248 std::linear_congruential_engine<unsigned long long, 48271, 0, 0xFFFFFFFFFFFFFFFF> engine;
250 unsigned long long r = engine();
255std::string PINtoRandomKeyString(
int dpin) {
256 unsigned long long pin = PINtoRandomKey(dpin);
258 snprintf(buffer,
sizeof(buffer)-1,
"%0llX", pin);
259 return std::string(buffer);
265 if(m_PINCreateDialog){
266 m_PINCreateDialog->Close();
267 m_PINCreateDialog->Destroy();
268 m_PINCreateDialog = NULL;
271 auto p =
event.GetPayload();
272 std::string *payload = p.get();
277 int return_stat = RESTServerResult::RESULT_GENERIC_ERROR;
282 std::string api_found;
283 for (
auto it : m_key_map){
284 if (it.first == event.m_source_peer && it.second == event.m_api_key){
285 api_found = it.second;
290 if (!api_found.size()){
292 m_dPIN = wxMin(rand() % 10000 + 1, 9999);
293 m_sPIN.Printf(
"%04d", m_dPIN);
295 std::string new_api_key = PINtoRandomKeyString(m_dPIN);
298 m_key_map[
event.m_source_peer] = new_api_key;
304 m_PINCreateDialog =
new PINCreateDialog((wxWindow *)gFrame, wxID_ANY, _(
"OpenCPN Server Message"),
305 "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE );
307 wxString hmsg(event.m_source_peer.c_str());
309 hmsg +=
"wants to sent you a new route.\nPlease enter the following PIN number on ";
310 hmsg += wxString(event.m_source_peer.c_str());
311 hmsg +=
" to pair with this device.\n";
313 m_PINCreateDialog->SetMessage(hmsg);
314 m_PINCreateDialog->SetText1Message(m_sPIN);
316 m_PINCreateDialog->Show();
317 return_status = RESTServerResult::RESULT_NEW_PIN_REQUESTED;
319 std::lock_guard<std::mutex> lock{mx};
320 return_status_condition.notify_one();
333 "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE );
335 wxString hmsg(event.m_source_peer.c_str());
336 hmsg +=
" has sent you a new route.\nAccept?";
338 dialog1.SetMessage(hmsg);
339 dialog1.SetCheck1Message(_(
"Always accept objects from this source?"));
340 b_cont = dialog1.ShowModal() == ID_STG_OK;
347 pugi::xml_document doc;
348 pugi::xml_parse_result result = doc.load_buffer(payload->c_str(), payload->size());
349 if (result.status == pugi::status_ok){
350 pugi::xml_node objects = doc.child(
"gpx");
351 for (pugi::xml_node
object = objects.first_child();
object;
352 object =
object.next_sibling()) {
353 if (!strcmp(
object.name(),
"rte")) {
354 Route *pRoute = NULL;
355 pRoute = GPXLoadRoute1(
object,
true,
false,
false, 0,
true);
359 bool b_overwrite_one =
false;
360 Route *duplicate = g_pRouteMan->FindRouteByGUID(pRoute->GetGUID());
364 "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE );
366 dialog2.SetMessage(
"The received route already exists on this system.\nReplace?");
367 dialog2.SetCheck1Message(_(
"Always replace objects from this source?"));
369 int result = dialog2.ShowModal();
370 bool b_always = dialog2.GetCheck1Value();
372 if (result != ID_STG_OK){
374 return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED;
377 m_b_overwrite = b_always;
378 b_overwrite_one =
true;
383 if (m_b_overwrite || b_overwrite_one){
386 NavObjectChanges::getInstance());
396 if (InsertRouteA(pRoute, &pSet))
397 return_stat = RESTServerResult::RESULT_NO_ERROR;
399 return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR;
400 ((wxWindow *)gFrame)->Refresh();
403 }
else if (!strcmp(
object.name(),
"trk")) {
404 Track *pRoute = NULL;
405 pRoute = GPXLoadTrack1(
object,
true,
false,
false, 0);
409 bool b_overwrite_one =
false;
411 Track *duplicate = g_pRouteMan->FindTrackByGUID(pRoute->m_GUID);
415 "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE );
417 dialog2.SetMessage(
"The received track already exists on this system.\nReplace?");
418 dialog2.SetCheck1Message(_(
"Always replace objects from this source?"));
420 int result = dialog2.ShowModal();
421 bool b_always = dialog2.GetCheck1Value();
423 if (result != ID_STG_OK){
425 return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED;
428 m_b_overwrite = b_always;
429 b_overwrite_one =
true;
434 if (m_b_overwrite || b_overwrite_one){
435 auto it = std::find(g_TrackList.begin(), g_TrackList.end(), duplicate);
436 if (it != g_TrackList.end()) {
437 g_TrackList.erase(it);
449 if (InsertTrack(pRoute,
false))
450 return_stat = RESTServerResult::RESULT_NO_ERROR;
452 return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR;
453 ((wxWindow *)gFrame)->Refresh();
456 }
else if (!strcmp(
object.name(),
"wpt")) {
458 pWp = GPXLoadWaypoint1(
object,
"circle",
"",
false,
false,
false, 0);
462 bool b_overwrite_one =
false;
464 RoutePoint *duplicate = WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
468 "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE );
470 dialog2.SetMessage(
"The received waypoint already exists on this system.\nReplace?");
471 dialog2.SetCheck1Message(_(
"Always replace objects from this source?"));
473 int result = dialog2.ShowModal();
474 bool b_always = dialog2.GetCheck1Value();
476 if (result != ID_STG_OK){
478 return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED;
481 m_b_overwrite = b_always;
482 b_overwrite_one =
true;
490 if (InsertWpt(pWp, m_b_overwrite || b_overwrite_one))
491 return_stat = RESTServerResult::RESULT_NO_ERROR;
493 return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR;
494 ((wxWindow *)gFrame)->Refresh();
502 return_stat = RESTServerResult::RESULT_OBJECT_REJECTED;
508 return_stat = RESTServerResult::RESULT_GENERIC_ERROR;
511 return_status = return_stat;
513 std::lock_guard<std::mutex> lock{mx};
514 return_status_condition.notify_one();
518 static const char* s_http_addr =
"http://0.0.0.0:8000";
519 static const char* s_https_addr =
"https://0.0.0.0:8443";
521 static const char* s_http_addr_portable =
"http://0.0.0.0:8001";
522 static const char* s_https_addr_portable =
"https://0.0.0.0:8444";
528static void fn(
struct mg_connection *c,
int ev,
void *ev_data,
void *fn_data) {
531 if (ev == MG_EV_ACCEPT ) {
532 struct mg_tls_opts opts;
533 memset(&opts, 0,
sizeof(mg_tls_opts));
536 opts.cert = parent->m_cert_file.c_str();
537 opts.certkey = parent->m_key_file.c_str();
539 mg_tls_init(c, &opts);
540 }
else if (ev == MG_EV_HTTP_MSG) {
541 struct mg_http_message *hm = (
struct mg_http_message *) ev_data;
542 if (mg_http_match_uri(hm,
"/api/rx_object")) {
545 struct mg_str api_key_parm = mg_http_var(hm->query, mg_str(
"apikey"));
546 if(api_key_parm.len && api_key_parm.ptr){
547 api_key = std::string(api_key_parm.ptr, api_key_parm.len);
551 struct mg_str source = mg_http_var(hm->query, mg_str(
"source"));
553 if(source.len && hm->body.len )
555 std::string xml_content(hm->body.ptr, hm->body.len);
556 std::string source_peer(source.ptr, source.len);
566 auto buffer = std::make_shared<std::string>(xml_content);
567 Nevent.SetPayload(buffer);
568 Nevent.SetSource(source_peer);
569 Nevent.SetAPIKey(api_key);
570 parent->AddPendingEvent(Nevent);
573 std::unique_lock<std::mutex> lock{mx};
574 while (return_status < 0) {
575 std::this_thread::sleep_for (std::chrono::milliseconds(100));
576 return_status_condition.wait(lock);
581 mg_http_reply(c, 200,
"",
"{\"result\": %d}\n", return_status);
587std::string server_ip;
589RESTServerThread::RESTServerThread(
RESTServer* Launcher) {
590 m_pParent = Launcher;
592 server_ip = s_https_addr;
595 server_ip = s_https_addr_portable;
596 wxString sip(server_ip);
597 wxLogMessage(
"Portable REST server IP: Port " + sip);
603RESTServerThread::~RESTServerThread(
void) {}
605void RESTServerThread::OnExit(
void) {}
607void* RESTServerThread::Entry() {
608 bool not_done =
true;
609 m_pParent->SetSecThreadActive();
612 mg_log_set(MG_LL_DEBUG);
615 mg_http_listen(&mgr, server_ip.c_str(), fn, m_pParent);
618 for (;;) mg_mgr_poll(&mgr, 1000);
621 m_pParent->SetSecThreadInActive();
622 m_pParent->m_Thread_run_flag = -1;
"Accept Object" Dialog Definition
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)