OpenCPN Partial API docs
Loading...
Searching...
No Matches
peer_client.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Peer-peer data sharing.
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 by David Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26
27#include <iostream>
28#include <sstream>
29
30#include <curl/curl.h>
31
32#include "peer_client.h"
33
34#include <wx/fileconf.h>
35#include <wx/json_defs.h>
36#include <wx/jsonreader.h>
37#include <wx/tokenzr.h>
38
39#include "config_vars.h"
40#include "FontMgr.h"
41#include "gui_lib.h"
42#include "nav_object_database.h"
43#include "REST_server.h"
44
45extern std::string PINtoRandomKeyString(int dpin);
46
47extern MyFrame *gFrame;
48
49wxString GetErrorText(int result){
50 switch (result) {
51 case RESTServerResult::RESULT_GENERIC_ERROR:
52 return _("Server Generic Error");
53 case RESTServerResult::RESULT_OBJECT_REJECTED:
54 return _("Peer rejected object");
55 case RESTServerResult::RESULT_DUPLICATE_REJECTED:
56 return _("Peer rejected duplicate object");
57 case RESTServerResult::RESULT_ROUTE_INSERT_ERROR:
58 return _("Peer internal error (insert)");
59 default:
60 return _("Server Unknown Error");
61 }
62}
63
64size_t wxcurl_string_write_UTF8(void* ptr, size_t size, size_t nmemb, void* pcharbuf)
65{
66 size_t iRealSize = size * nmemb;
67 wxCharBuffer* pStr = (wxCharBuffer*) pcharbuf;
68
69 if(pStr)
70 {
71#ifdef __WXMSW__
72 wxString str1a = wxString(*pStr);
73 wxString str2 = wxString((const char*)ptr, wxConvUTF8, iRealSize);
74 *pStr = (str1a + str2).mb_str();
75#else
76 wxString str = wxString(*pStr, wxConvUTF8) + wxString((const char*)ptr, wxConvUTF8, iRealSize);
77 *pStr = str.mb_str(wxConvUTF8);
78#endif
79 }
80
81 return iRealSize;
82}
83
84
86 char *memory;
87 size_t size;
88};
89
90static size_t
91WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
92{
93 size_t realsize = size * nmemb;
94 struct MemoryStruct *mem = (struct MemoryStruct *)userp;
95
96 char *ptr = (char *)realloc(mem->memory, mem->size + realsize + 1);
97 if(!ptr) {
98 /* out of memory! */
99 printf("not enough memory (realloc returned NULL)\n");
100 return 0;
101 }
102
103 mem->memory = ptr;
104 memcpy(&(mem->memory[mem->size]), contents, realsize);
105 mem->size += realsize;
106 mem->memory[mem->size] = 0;
107
108 return realsize;
109}
110
111long PostSendObjectMessage( std::string url, std::ostringstream &body,
112 MemoryStruct *response){
113
114 long response_code = -1;
115
116#ifdef ANDROID
117//FIXME (dave)
118
119#else
120 CURL* c = curl_easy_init();
121 curl_easy_setopt(c, CURLOPT_ENCODING, "identity"); // No encoding, plain ASCII
122 curl_easy_setopt(c, CURLOPT_URL, url.c_str());
123 curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 0L);
124 curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 0L);
125
126 int iSize = strlen(body.str().c_str());
127 curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, iSize);
128 curl_easy_setopt(c, CURLOPT_COPYPOSTFIELDS, body.str().c_str());
129
130 curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
131 curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)response);
132
133 CURLcode result = curl_easy_perform(c);
134
135 if(result == CURLE_OK)
136 curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
137
138 curl_easy_cleanup(c);
139
140#endif
141 return response_code;
142}
143
144std::string GetClientKey( std::string &server_name )
145{
146 if (TheBaseConfig()) {
147 TheBaseConfig()->SetPath("/Settings/RESTClient");
148
149 wxString key_string;
150
151 TheBaseConfig()->Read("ServerKeys", &key_string );
152 wxStringTokenizer st(key_string, _T(";"));
153 while (st.HasMoreTokens()) {
154 wxString s1 = st.GetNextToken();
155 wxString server_name_persisted = s1.BeforeFirst(':');
156 wxString server_key = s1.AfterFirst(':');
157
158 if (!server_name_persisted.ToStdString().compare(server_name))
159 return server_key.ToStdString();
160 }
161 }
162 return "1";
163}
164
165void SaveClientKey( std::string &server_name, std::string key )
166{
167 if (TheBaseConfig()) {
168 TheBaseConfig()->SetPath("/Settings/RESTClient");
169
170 wxArrayString array;
171 wxString key_string;
172 TheBaseConfig()->Read("ServerKeys", &key_string );
173 wxStringTokenizer st(key_string, _T(";"));
174 while (st.HasMoreTokens()) {
175 wxString s1 = st.GetNextToken();
176 array.Add(s1);
177 }
178
179 bool b_updated = false;
180 for (unsigned int i=0; i<array.GetCount(); i++){
181 wxString s1 = array[i];
182 wxString server_name_persisted = s1.BeforeFirst(':');
183 wxString server_key = s1.AfterFirst(':');
184 if (server_name_persisted.IsSameAs(server_name.c_str())){
185 array[i] = server_name_persisted + ":" + key.c_str();
186 b_updated = true;
187 break;
188 }
189 }
190
191 if (!b_updated){
192 wxString new_entry = server_name.c_str() + wxString(":") + key.c_str();
193 array.Add(new_entry);
194 }
195
196 wxString key_string_updated;
197 for (unsigned int i=0; i<array.GetCount(); i++){
198 wxString s1 = array[i];
199 key_string_updated += s1;
200 key_string_updated += ";";
201 }
202
203 TheBaseConfig()->Write("ServerKeys", key_string_updated );
204
205 }
206 return ;
207}
208
209
210
211int SendRoute(std::string dest_ip_address, std::string server_name, std::vector<Route*> route, std::vector<RoutePoint*> routepoint, std::vector<Track*> track, bool overwrite)
212{
213 if(route.empty() && routepoint.empty() && track.empty())
214 return -1;
215
216 // Get XML representation of object.
218 for (auto r : route) {
219 pgpx->AddGPXRoute(r);
220 }
221 for (auto r : routepoint) {
222 pgpx->AddGPXWaypoint(r);
223 }
224 for (auto r : track) {
225 pgpx->AddGPXTrack(r);
226 }
227 std::ostringstream stream;
228 pgpx->save(stream, PUGIXML_TEXT(" "));
229
230 bool apikey_ok = false;
231 bool b_cancel = false;
232
233 while (!apikey_ok && b_cancel == false){
234 std::string api_key = GetClientKey(server_name);
235
236 std::string url(dest_ip_address);
237 url += "/api/rx_object";
238 url += std::string("?source=") + g_hostname;
239 url += std::string("&apikey=") + api_key;
240
241 struct MemoryStruct chunk;
242 chunk.memory = (char *)malloc(1);
243 chunk.size = 0;
244
245 long response_code = PostSendObjectMessage( url, stream, &chunk);
246
247 if(response_code == 200){
248 wxString body(chunk.memory);
249 wxJSONValue root;
250 wxJSONReader reader;
251
252 int numErrors = reader.Parse( body, &root );
253 // Capture the result
254 int result = root["result"].AsInt();
255 if (result > 0){
256 if (result == RESULT_NEW_PIN_REQUESTED){
257
258 // Show the dialog asking for PIN
259 PINConfirmDialog dlg((wxWindow *)gFrame, wxID_ANY, _("OpenCPN Server Message"),
260 "", wxDefaultPosition, wxDefaultSize, SYMBOL_PCD_STYLE );
261
262 wxString hmsg("The server ");
263 hmsg += "needs a PIN.\nPlease enter the PIN number from ";
264 hmsg += wxString("the server ");
265 hmsg += " to pair with this device.\n";
266
267 dlg.SetMessage(hmsg);
268 dlg.SetText1Message("");
269
270 if (dlg.ShowModal() == ID_PCD_OK) {
271 wxString PIN_tentative = dlg.GetText1Value().Trim().Trim(false);
272 unsigned int dPIN = atoi(PIN_tentative.ToStdString().c_str());
273 std::string new_api_key = PINtoRandomKeyString(dPIN);;
274
275 SaveClientKey(server_name, new_api_key);
276 }
277 else
278 b_cancel = true;
279 }
280 else{
281 wxString error_text = GetErrorText(result);
282 OCPNMessageDialog mdlg(NULL, error_text, wxString(_("OpenCPN Info")),
283 wxICON_ERROR | wxOK);
284 mdlg.ShowModal();
285 b_cancel = true;
286 }
287 }
288 else
289 apikey_ok = true;
290 }
291 else{
292 wxString err_msg;
293 err_msg.Printf("Server HTTP response is: %ld", response_code);
294 OCPNMessageDialog mdlg(NULL, err_msg, wxString(_("OpenCPN Info")),
295 wxICON_ERROR | wxOK);
296 mdlg.ShowModal();
297
298 b_cancel = true;
299 }
300 }
301 return true;
302}
303
304IMPLEMENT_DYNAMIC_CLASS(PINConfirmDialog, wxDialog)
305
306BEGIN_EVENT_TABLE(PINConfirmDialog, wxDialog)
307 EVT_BUTTON(ID_PCD_CANCEL, PINConfirmDialog::OnCancelClick)
308 EVT_BUTTON(ID_PCD_OK, PINConfirmDialog::OnOKClick)
309END_EVENT_TABLE()
310
312 m_OKButton = NULL;
313 m_CancelButton = NULL;
314 premtext = NULL;
315}
316
317PINConfirmDialog::PINConfirmDialog(wxWindow* parent, wxWindowID id,
318 const wxString& caption, const wxString& hint,
319 const wxPoint& pos, const wxSize& size, long style) {
320 wxFont* pif = FontMgr::Get().GetFont(_T("Dialog"));
321 SetFont( *pif );
322 Create(parent, id, caption, hint, pos, size, style);
323}
324
325PINConfirmDialog::~PINConfirmDialog() {
326 delete m_OKButton;
327 delete m_CancelButton;
328}
329
330bool PINConfirmDialog::Create(wxWindow* parent, wxWindowID id,
331 const wxString& caption, const wxString& hint,
332 const wxPoint& pos, const wxSize& size, long style) {
333 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
334 wxDialog::Create(parent, id, caption, pos, size, style);
335
336 CreateControls(hint);
337 GetSizer()->Fit(this);
338 GetSizer()->SetSizeHints(this);
339 Centre();
340
341 return TRUE;
342}
343
344void PINConfirmDialog::CreateControls(const wxString& hint) {
345 PINConfirmDialog* itemDialog1 = this;
346
347 wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
348 SetSizer(itemBoxSizer2);
349
350
351 // Add a reminder text box
352 itemBoxSizer2->AddSpacer(20);
353
354 premtext = new wxStaticText( this, -1, "A loooooooooooooooooooooooooooooooooooooooooooooong line\n");
355 itemBoxSizer2->Add(premtext, 0, wxEXPAND | wxALL, 10);
356
357 m_pText1 = new wxTextCtrl(this, wxID_ANY, " ",
358 wxDefaultPosition, wxDefaultSize, wxTE_CENTRE);
359 itemBoxSizer2->Add(m_pText1, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10);
360
361
362 // OK/Cancel/etc.
363 wxBoxSizer* itemBoxSizer16 = new wxBoxSizer(wxHORIZONTAL);
364 itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5);
365
366 m_CancelButton = new wxButton(itemDialog1, ID_PCD_CANCEL, _("Cancel"),
367 wxDefaultPosition, wxDefaultSize, 0);
368 itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
369
370 m_OKButton = new wxButton(itemDialog1, ID_PCD_OK, "OK",
371 wxDefaultPosition, wxDefaultSize, 0);
372 itemBoxSizer16->Add(m_OKButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
373 m_OKButton->SetDefault();
374}
375
376void PINConfirmDialog::SetMessage(const wxString &msg) {
377 if (premtext) {
378 premtext->SetLabel(msg);
379 premtext->Refresh(true);
380 }
381}
382
383void PINConfirmDialog::SetText1Message(const wxString &msg) {
384 m_pText1->ChangeValue(msg);
385 m_pText1->Show();
386 GetSizer()->Fit(this);
387}
388
389void PINConfirmDialog::OnOKClick(wxCommandEvent& event) {
390 EndModal(ID_PCD_OK);
391}
392
393void PINConfirmDialog::OnCancelClick(wxCommandEvent& event) {
394 EndModal(ID_PCD_CANCEL);
395}