1 /***************************************************************************
2 tqsl.cpp
3 -------------------
4 begin : Mon Nov 4 2002
5 copyright : (C) 2002-2018 by ARRL and the TrustedQSL Developers
6 author : Jon Bloom
7 email : jbloom@arrl.org
8 ***************************************************************************/
9
10 #include <curl/curl.h> // has to be before something else in this list
11 #include <stdlib.h>
12 #include <errno.h>
13 #ifndef _WIN32
14 #include <unistd.h>
15 #include <fcntl.h>
16 #endif
17 #include <expat.h>
18 #include <sys/stat.h>
19
20 #ifndef _WIN32
21 #include <unistd.h>
22 #include <dirent.h>
23 #else
24 #include <direct.h>
25 #include "windirent.h"
26 #endif
27
28 #include <wx/wxprec.h>
29 #include <wx/object.h>
30 #include <wx/wxchar.h>
31 #include <wx/config.h>
32 #include <wx/regex.h>
33 #include <wx/tokenzr.h>
34 #include <wx/hashmap.h>
35 #include <wx/hyperlink.h>
36 #include <wx/cmdline.h>
37 #include <wx/notebook.h>
38 #include <wx/statline.h>
39 #include <wx/app.h>
40 #include <wx/stdpaths.h>
41 #include <wx/intl.h>
42 #include <wx/cshelp.h>
43
44 #ifdef __BORLANDC__
45 #pragma hdrstop
46 #endif
47
48 #ifndef WX_PRECOMP
49 #include <wx/wx.h>
50 #endif
51
52 #include <wx/wxhtml.h>
53 #include <wx/wfstream.h>
54
55 #ifdef _MSC_VER //could probably do a more generic check here...
56 // stdint exists on vs2012 and (I think) 2010, but not 2008 or its platform
57 #define uint8_t unsigned char
58 #else
59 #include <stdint.h> //for uint8_t; should be cstdint but this is C++11 and not universally supported
60 #endif
61
62 #ifdef _WIN32
63 #include <io.h>
64 #endif
65 #include <zlib.h>
66 #include <openssl/opensslv.h> // only for version info!
67 #ifdef USE_LMDB
68 #include <lmdb.h> //only for version info!
69 #else
70 #include <db.h> //only for version info!
71 #endif
72
73 #include <iostream>
74 #include <fstream>
75 #include <memory>
76 #include <string>
77 #include <vector>
78 #include <map>
79
80 #ifdef HAVE_CONFIG_H
81 #include "sysconfig.h"
82 #endif
83
84 #include "tqslhelp.h"
85
86 #include "crqwiz.h"
87 #include "getpassword.h"
88 #include "loadcertwiz.h"
89 #include "tqsllib.h"
90 #include "util.h"
91 #include "wxutil.h"
92
93 #include "tqslwiz.h"
94 #include "qsodatadialog.h"
95 #include "tqslerrno.h"
96 #include "tqslexcept.h"
97 #include "tqslpaths.h"
98 #include "stationdial.h"
99 #include "tqslconvert.h"
100 #include "dxcc.h"
101 #include "tqsl_prefs.h"
102 #include "tqslbuild.h"
103 #include "tqslapp.h"
104 #include "tqsltrace.h"
105
106 #include "winstrdefs.h"
107
108 #include "jsonval.h"
109 #include "jsonreader.h"
110
111 using std::ifstream;
112 using std::ios;
113 using std::ofstream;
114 using std::cerr;
115 using std::endl;
116 using std::vector;
117 using std::string;
118
119 #ifdef _WIN32
120 #include "key.xpm"
121 #else
122 #include "key-new.xpm"
123 #endif
124 #include "save.xpm"
125 #include "upload.xpm"
126 #include "upload_dis.xpm"
127 #include "file_edit.xpm"
128 #include "file_edit_dis.xpm"
129 #include "loc_add.xpm"
130 #include "loc_add_dis.xpm"
131 #include "delete.xpm"
132 #include "delete_dis.xpm"
133 #include "edit.xpm"
134 #include "edit_dis.xpm"
135 #include "download.xpm"
136 #include "download_dis.xpm"
137 #include "properties.xpm"
138 #include "properties_dis.xpm"
139 #include "import.xpm"
140 #include "lotw.xpm"
141
142 /// GEOMETRY
143
144 #define LABEL_HEIGHT 20
145
146 #define CERTLIST_FLAGS TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_SUPERCEDED | TQSL_SELECT_CERT_EXPIRED
147
148 static wxMenu *stn_menu;
149
150 static wxString flattenCallSign(const wxString& call);
151
152 static wxString ErrorTitle(_("TQSL Error"));
153
154 static bool verify_cert(tQSL_Location loc, bool editing);
155
156 static CURL* tqsl_curl_init(const char *logTitle, const char *url, FILE **curlLogFile, bool newFile);
157
158 static wxString origCommandLine = wxT("");
159 static MyFrame *frame = 0;
160
161 static char unipwd[64];
162 static bool quiet = false;
163 static bool verifyCA = true;
164
165 static int lock_db(bool wait);
166 static void unlock_db(void);
167 int get_address_field(const char *callsign, const char *field, string& result);
168
exitNow(int status,bool quiet)169 static void exitNow(int status, bool quiet) {
170 const char *errors[] = { __("Success"),
171 __("User Cancelled"),
172 __("Upload Rejected"),
173 __("Unexpected LoTW Response"),
174 __("TQSL Error"),
175 __("TQSLLib Error"),
176 __("Error opening input file"),
177 __("Error opening output file"),
178 __("No QSOs written"),
179 __("Some QSOs suppressed"),
180 __("Command Syntax Error"),
181 __("LoTW Connection Failed"),
182 __("Unknown"),
183 __("The duplicates database is locked")
184 };
185 int stat = status;
186 if (stat > TQSL_EXIT_UNKNOWN || stat < 0) stat = TQSL_EXIT_UNKNOWN;
187 wxString msg = wxGetTranslation(wxString::FromUTF8(errors[stat]));
188 char emsg[512];
189 strncpy(emsg, msg.ToUTF8(), sizeof emsg);
190 if (quiet)
191 wxLogMessage(_("Final Status: %hs (%d)"), emsg, status);
192 else
193 cerr << "Final Status: " << emsg << "(" << status << ")" << endl;
194 exit(status);
195 }
196
197 /////////// Application //////////////
198
199 class QSLApp : public wxApp {
200 public:
201 QSLApp();
202 virtual ~QSLApp();
203 class MyFrame *GUIinit(bool checkUpdates, bool quiet = false);
204 virtual bool OnInit();
205 virtual int OnRun();
GetLang()206 wxLanguage GetLang() {return lang; }
207 // virtual wxLog *CreateLogTarget();
208 protected:
209 wxLanguage lang; // Language specified by user
210 wxLocale* locale; // Locale we're using
211 };
212
~QSLApp()213 QSLApp::~QSLApp() {
214 wxConfigBase *c = wxConfigBase::Set(0);
215 if (c)
216 delete c;
217 tqsl_closeDiagFile();
218 }
219
IMPLEMENT_APP(QSLApp)220 IMPLEMENT_APP(QSLApp)
221
222 static int
223 getCertPassword(char *buf, int bufsiz, tQSL_Cert cert) {
224 tqslTrace("getCertPassword", "buf = %lx, bufsiz=%d, cert=%lx", buf, bufsiz, cert);
225 char call[TQSL_CALLSIGN_MAX+1] = "";
226 int dxcc = 0;
227 tqsl_getCertificateCallSign(cert, call, sizeof call);
228 tqsl_getCertificateDXCCEntity(cert, &dxcc);
229 DXCC dx;
230 dx.getByEntity(dxcc);
231
232 // TRANSLATORS: this is followed by the callsign and entity name
233 wxString fmt = _("Enter the password to unlock the callsign certificate for %hs -- %hs\n"
234 "(This is the password you made up when you installed the callsign certificate.)");
235 wxString message = wxString::Format(fmt, call, dx.name());
236
237 wxWindow* top = wxGetApp().GetTopWindow();
238 if (frame->IsQuiet()) {
239 frame->Show(true);
240 }
241 top->SetFocus();
242 top->Raise();
243
244 wxString pwd;
245 int ret = getPasswordFromUser(pwd, message, _("Enter password"), wxT(""), top);
246 if (ret != wxID_OK)
247 return 1;
248 strncpy(buf, pwd.ToUTF8(), bufsiz);
249 utf8_to_ucs2(buf, unipwd, sizeof unipwd);
250 return 0;
251 }
252
253 class ConvertingDialog : public wxDialog {
254 public:
255 explicit ConvertingDialog(wxWindow *parent, const char *filename = "");
256 void OnCancel(wxCommandEvent&);
257 bool running;
258 wxStaticText *msg;
259 private:
260 wxButton *canbut;
261
262 DECLARE_EVENT_TABLE()
263 };
264
BEGIN_EVENT_TABLE(ConvertingDialog,wxDialog)265 BEGIN_EVENT_TABLE(ConvertingDialog, wxDialog)
266 EVT_BUTTON(TQSL_CD_CANBUT, ConvertingDialog::OnCancel)
267 END_EVENT_TABLE()
268
269 void
270 ConvertingDialog::OnCancel(wxCommandEvent&) {
271 running = false;
272 canbut->Enable(FALSE);
273 }
274
ConvertingDialog(wxWindow * parent,const char * filename)275 ConvertingDialog::ConvertingDialog(wxWindow *parent, const char *filename)
276 : wxDialog(parent, -1, wxString(_("Signing QSO Data"))),
277 running(true) {
278 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
279 wxString label = wxString(_("Converting ")) + wxString::FromUTF8(filename) + wxT(" ") + _("to TQSL format");
280 sizer->Add(new wxStaticText(this, -1, label), 0, wxALL|wxALIGN_CENTER, 10);
281 msg = new wxStaticText(this, TQSL_CD_MSG, wxT(" "));
282 sizer->Add(msg, 0, wxALL|wxALIGN_LEFT, 10);
283 canbut = new wxButton(this, TQSL_CD_CANBUT, _("Cancel"));
284 sizer->Add(canbut, 0, wxALL|wxEXPAND, 10);
285 SetAutoLayout(TRUE);
286 SetSizer(sizer);
287 sizer->Fit(this);
288 sizer->SetSizeHints(this);
289 CenterOnParent();
290 }
291
292 #define TQSL_PROG_CANBUT TQSL_ID_LOW+30
293
294 DECLARE_EVENT_TYPE(wxEVT_LOGUPLOAD_DONE, -1)
295 DEFINE_EVENT_TYPE(wxEVT_LOGUPLOAD_DONE)
296
297 class UploadDialog : public wxDialog {
298 public:
299 explicit UploadDialog(wxWindow *parent, wxString title = wxString(_("Uploading Signed Data")), wxString label = wxString(_("Uploading signed log data...")));
300 void OnCancel(wxCommandEvent&);
301 void OnDone(wxCommandEvent&);
302 int doUpdateProgress(double dltotal, double dlnow, double ultotal, double ulnow);
UpdateProgress(void * clientp,double dltotal,double dlnow,double ultotal,double ulnow)303 static int UpdateProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
304 return (reinterpret_cast<UploadDialog*>(clientp))->doUpdateProgress(dltotal, dlnow, ultotal, ulnow);
305 }
306 private:
307 wxButton *canbut;
308 wxGauge* progress;
309 bool cancelled;
310 DECLARE_EVENT_TABLE()
311 };
312
BEGIN_EVENT_TABLE(UploadDialog,wxDialog)313 BEGIN_EVENT_TABLE(UploadDialog, wxDialog)
314 EVT_BUTTON(TQSL_PROG_CANBUT, UploadDialog::OnCancel)
315 EVT_COMMAND(wxID_ANY, wxEVT_LOGUPLOAD_DONE, UploadDialog::OnDone)
316 END_EVENT_TABLE()
317
318 void
319 UploadDialog::OnCancel(wxCommandEvent&) {
320 cancelled = true;
321 canbut->Enable(false);
322 }
323
UploadDialog(wxWindow * parent,wxString title,wxString label)324 UploadDialog::UploadDialog(wxWindow *parent, wxString title, wxString label)
325 : wxDialog(parent, -1, title), cancelled(false) {
326 tqslTrace("UploadDialog::UploadDialog", "parent = %lx", parent);
327 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
328 sizer->Add(new wxStaticText(this, -1, label), 0, wxALL|wxALIGN_CENTER, 10);
329
330 progress = new wxGauge(this, -1, 100);
331 progress->SetValue(0);
332 sizer->Add(progress, 0, wxALL|wxEXPAND);
333
334 canbut = new wxButton(this, TQSL_PROG_CANBUT, _("Cancel"));
335 sizer->Add(canbut, 0, wxALL|wxEXPAND, 10);
336 SetAutoLayout(TRUE);
337 SetSizer(sizer);
338 sizer->Fit(this);
339 sizer->SetSizeHints(this);
340 CenterOnParent();
341 }
342
OnDone(wxCommandEvent &)343 void UploadDialog::OnDone(wxCommandEvent&) {
344 tqslTrace("UploadDialog::OnDone", "Upload complete");
345 EndModal(1);
346 }
347
doUpdateProgress(double dltotal,double dlnow,double ultotal,double ulnow)348 int UploadDialog::doUpdateProgress(double dltotal, double dlnow, double ultotal, double ulnow) {
349 static double lastDlnow = 0.0;
350 if (dlnow != lastDlnow) {
351 tqslTrace("UploadDialog::doUpdateProgresss", "dltotal=%f, dlnow=%f, ultotal=%f, ulnow=%f", dltotal, dlnow, ultotal, ulnow);
352 lastDlnow = dlnow;
353 }
354 if (cancelled) return 1;
355 if (ultotal > 0.0000001) progress->SetValue(static_cast<int>((100*(ulnow/ultotal))));
356 return 0;
357 }
358
359 #define TQSL_DR_START TQSL_ID_LOW+10
360 #define TQSL_DR_END TQSL_ID_LOW+11
361 #define TQSL_DR_OK TQSL_ID_LOW+12
362 #define TQSL_DR_CAN TQSL_ID_LOW+13
363 #define TQSL_DR_MSG TQSL_ID_LOW+14
364
365 class DateRangeDialog : public wxDialog {
366 public:
367 explicit DateRangeDialog(wxWindow *parent = 0);
368 tQSL_Date start, end;
369 private:
370 void OnOk(wxCommandEvent&);
371 void OnCancel(wxCommandEvent&);
372 virtual bool TransferDataFromWindow();
373 wxTextCtrl *start_tc, *end_tc;
374 wxStaticText *msg;
375 DECLARE_EVENT_TABLE()
376 };
377
BEGIN_EVENT_TABLE(DateRangeDialog,wxDialog)378 BEGIN_EVENT_TABLE(DateRangeDialog, wxDialog)
379 EVT_BUTTON(TQSL_DR_OK, DateRangeDialog::OnOk)
380 EVT_BUTTON(TQSL_DR_CAN, DateRangeDialog::OnCancel)
381 END_EVENT_TABLE()
382
383 DateRangeDialog::DateRangeDialog(wxWindow *parent) : wxDialog(parent, -1, wxString(_("QSO Date Range"))) {
384 tqslTrace("DateRangeDialog::DateRangeDialog", NULL);
385 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
386 wxString msgstr = _("You may set the starting and/or ending QSO dates "
387 "in order to select QSOs from the input file.");
388 msgstr += wxT("\n\n");
389 msgstr += _("QSOs prior to the starting date or after the ending "
390 "date will not be signed or included in the output file.");
391 msgstr += wxT("\n\n");
392 msgstr += _("You may leave either date (or both dates) blank.");
393 wxSize sz = getTextSize(this);
394 int em_w = sz.GetWidth();
395 wxStaticText *st = new wxStaticText(this, -1, msgstr);
396 st->Wrap(em_w * 30);
397 sizer->Add(st, 0, wxALL|wxALIGN_CENTER, 10);
398
399 wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
400 hsizer->Add(new wxStaticText(this, -1, _("Start Date (YYYY-MM-DD)")), 0, wxRIGHT, 5);
401 start_tc = new wxTextCtrl(this, TQSL_DR_START);
402 hsizer->Add(start_tc, 0, 0, 0);
403 sizer->Add(hsizer, 0, wxALL|wxALIGN_CENTER, 10);
404 hsizer = new wxBoxSizer(wxHORIZONTAL);
405 hsizer->Add(new wxStaticText(this, -1, _("End Date (YYYY-MM-DD)")), 0, wxRIGHT, 5);
406 end_tc = new wxTextCtrl(this, TQSL_DR_END);
407 hsizer->Add(end_tc, 0, 0, 0);
408 sizer->Add(hsizer, 0, wxALL|wxALIGN_CENTER, 10);
409 msg = new wxStaticText(this, TQSL_DR_MSG, wxT(""));
410 sizer->Add(msg, 0, wxALL, 5);
411 hsizer = new wxBoxSizer(wxHORIZONTAL);
412 hsizer->Add(new wxButton(this, TQSL_DR_OK, _("OK")), 0, wxRIGHT, 5);
413 hsizer->Add(new wxButton(this, TQSL_DR_CAN, _("Cancel")), 0, wxLEFT, 10);
414 sizer->Add(hsizer, 0, wxALIGN_CENTER|wxALL, 10);
415 SetAutoLayout(TRUE);
416 SetSizer(sizer);
417 sizer->Fit(this);
418 sizer->SetSizeHints(this);
419 CenterOnParent();
420 }
421
422 bool
TransferDataFromWindow()423 DateRangeDialog::TransferDataFromWindow() {
424 tqslTrace("DateRangeDialog::TransferDataFromWindow", NULL);
425 wxString text = start_tc->GetValue();
426 tqslTrace("DateRangeDialog::TransferDataFromWindow", "start=%s", S(text));
427 if (text.Trim() == wxT("")) {
428 start.year = start.month = start.day = 0;
429 } else if (tqsl_initDate(&start, text.ToUTF8()) || !tqsl_isDateValid(&start)) {
430 msg->SetLabel(_("Start date is invalid"));
431 return false;
432 }
433 text = end_tc->GetValue();
434 tqslTrace("DateRangeDialog::TransferDataFromWindow", "end=%s", S(text));
435 if (text.Trim() == wxT("")) {
436 end.year = end.month = end.day = 0;
437 } else if (tqsl_initDate(&end, text.ToUTF8()) || !tqsl_isDateValid(&end)) {
438 msg->SetLabel(_("End date is invalid"));
439 return false;
440 }
441 return true;
442 }
443
444 void
OnOk(wxCommandEvent &)445 DateRangeDialog::OnOk(wxCommandEvent&) {
446 tqslTrace("DateRangeDialog::OnOk", NULL);
447 if (TransferDataFromWindow())
448 EndModal(wxOK);
449 }
450
451 void
OnCancel(wxCommandEvent &)452 DateRangeDialog::OnCancel(wxCommandEvent&) {
453 tqslTrace("DateRangeDialog::OnCancel", NULL);
454 EndModal(wxCANCEL);
455 }
456
457 #define TQSL_DP_OK TQSL_ID_LOW+20
458 #define TQSL_DP_CAN TQSL_ID_LOW+21
459 #define TQSL_DP_ALLOW TQSL_ID_LOW+22
460
461 class DupesDialog : public wxDialog {
462 public:
463 explicit DupesDialog(wxWindow *parent = 0, int qso_count = 0, int dupes = 0, int action = TQSL_ACTION_ASK);
464 private:
465 void OnOk(wxCommandEvent&);
466 void OnCancel(wxCommandEvent&);
467 void OnAllow(wxCommandEvent&);
468 DECLARE_EVENT_TABLE()
469 };
470
BEGIN_EVENT_TABLE(DupesDialog,wxDialog)471 BEGIN_EVENT_TABLE(DupesDialog, wxDialog)
472 EVT_BUTTON(TQSL_DP_OK, DupesDialog::OnOk)
473 EVT_BUTTON(TQSL_DP_CAN, DupesDialog::OnCancel)
474 EVT_BUTTON(TQSL_DP_ALLOW, DupesDialog::OnAllow)
475 END_EVENT_TABLE()
476
477 DupesDialog::DupesDialog(wxWindow *parent, int qso_count, int dupes, int action)
478 : wxDialog(parent, -1, wxString(_("Duplicate QSOs Detected")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) {
479 tqslTrace("DupesDialog::DupesDialog", "qso_count = %d, dupes =%d, action= =%d", qso_count, dupes, action);
480 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
481 wxString message;
482
483 if (qso_count == dupes) {
484 wxString fmt = _("This log contains %d QSO(s) which appear "
485 "to have already been signed for upload to LoTW, and no new QSOs.");
486 fmt += wxT("\n\n");
487 fmt += _("Click 'Cancel' to abandon processing this log file (Recommended).");
488 fmt += wxT("\n");
489 fmt += _("Click 'Allow Duplicates' to re-process this "
490 "log while allowing duplicate QSOs.");
491 message = wxString::Format(fmt, qso_count);
492 } else {
493 int newq = qso_count - dupes;
494 wxString fmt = _("This log contains %d QSO(s) which appear "
495 "to have already been signed for upload to LoTW, and "
496 "%d QSOs which are new.");
497 fmt += wxT("\n\n");
498 fmt += _("Click 'Exclude duplicates' to sign normally, without the duplicate QSOs (Recommended).");
499 fmt += wxT("\n");
500 fmt += _("Click 'Cancel' to abandon processing this log file.");
501 fmt += wxT("\n");
502 fmt += _("Click 'Allow duplicates' to re-process this log "
503 "while allowing duplicate QSOs.");
504 wxString fmt1 = _("This log contains %d QSO(s) which appear "
505 "to have already been signed for upload to LoTW, and "
506 "one QSO which is new.");
507 fmt1 += wxT("\n\n");
508 fmt1 += _("Click 'Exclude duplicates' to sign normally, without the duplicate QSOs (Recommended).");
509 fmt1 += wxT("\n");
510 fmt1 += _("Click 'Cancel' to abandon processing this log file.");
511 fmt1 += wxT("\n");
512 fmt1 += _("Click 'Allow duplicates' to re-process this log "
513 "while allowing duplicate QSOs.");
514 if (newq == 1) {
515 message = wxString::Format(fmt1, dupes);
516 } else {
517 message = wxString::Format(fmt, dupes, newq);
518 }
519 }
520
521 if (action == TQSL_ACTION_UNSPEC) {
522 if (qso_count == dupes) {
523 message+= wxT("\n\n");
524 message += _("The log file you are uploading using your QSO Logging system consists entirely of previously uploaded "
525 "QSOs (duplicates) that create unnecessary work for LoTW. There may be a more recent version of your QSO "
526 "Logging system that would prevent this. Please check with your QSO Logging system's vendor for an updated version.");
527 message += wxT("\n");
528 message += _("In the meantime, please note that some loggers may exhibit strange behavior if an option other than 'Allow duplicates' "
529 "is clicked. Choosing 'Cancel' is usually safe, but a defective logger not checking the status messages reported by TrustedQSL may produce "
530 "strange (but harmless) behavior such as attempting to upload an empty file or marking all chosen QSOs as 'sent'");
531 } else {
532 message+= wxT("\n\n");
533 message += _("The log file you are uploading using your QSO Logging system includes some previously uploaded "
534 "QSOs (duplicates) that create unnecessary work for LoTW. There may be a more recent version of your "
535 "QSO Logging system that would prevent this. Please check with your QSO Logging system's vendor for an updated version.");
536 message += wxT("\n");
537 message += _("In the meantime, please note that some loggers may exhibit strange behavior if an option other than 'Allow duplicates' "
538 "is clicked. 'Exclude duplicates' is recommended, but a logger that does its own duplicate tracking may incorrectly "
539 "set the status in this case. A logger that doesn't track duplicates should be unaffected by choosing 'Exclude duplicates' "
540 "and if it tracks 'QSO sent' status, will correctly mark all selected QSOs as sent - they are in your account even though "
541 "they would not be in this specific batch");
542 message += wxT("\n");
543 message += _("Choosing 'Cancel' is usually safe, but a defective logger not checking the status messages reported by TrustedQSL may produce "
544 "strange (but harmless) behavior such as attempting to upload an empty file or marking all chosen QSOs as 'sent'");
545 }
546 }
547 wxStaticText* mtext = new wxStaticText(this, -1, message);
548 sizer->Add(mtext, 0, wxALL|wxALIGN_CENTER, 10);
549
550 wxSize sz = getTextSize(this);
551 int em_w = sz.GetWidth();
552 mtext->Wrap(em_w * 50);
553 wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
554 if (qso_count != dupes)
555 hsizer->Add(new wxButton(this, TQSL_DP_OK, _("Exclude duplicates")), 0, wxRIGHT, 5);
556 hsizer->Add(new wxButton(this, TQSL_DP_CAN, _("Cancel")), 0, wxLEFT, 10);
557 hsizer->Add(new wxButton(this, TQSL_DP_ALLOW, _("Allow duplicates")), 0, wxLEFT, 20);
558 sizer->Add(hsizer, 0, wxALIGN_CENTER|wxALL, 10);
559 SetAutoLayout(TRUE);
560 SetSizer(sizer);
561 sizer->Fit(this);
562 sizer->SetSizeHints(this);
563 CenterOnParent();
564 }
565
566 void
OnOk(wxCommandEvent &)567 DupesDialog::OnOk(wxCommandEvent&) {
568 tqslTrace("DupesDialog::OnOk", NULL);
569 EndModal(TQSL_DP_OK);
570 }
571
572 void
OnCancel(wxCommandEvent &)573 DupesDialog::OnCancel(wxCommandEvent&) {
574 tqslTrace("DupesDialog::OnCancel", NULL);
575 EndModal(TQSL_DP_CAN);
576 }
577
578 void
OnAllow(wxCommandEvent &)579 DupesDialog::OnAllow(wxCommandEvent&) {
580 tqslTrace("DupesDialog::OnAllow", NULL);
581
582 wxString msg = _("The only reason to re-sign duplicate QSOs is if a previous upload "
583 "was not processed by LoTW, either because it was never uploaded, or there was a server failure");
584 msg += wxT("\n\n");
585 msg += _("Are you sure you want to proceed? Click 'No' to review the choices");
586 if (wxMessageBox(msg, _("Are you sure?"), wxYES_NO|wxICON_EXCLAMATION, this) == wxYES) {
587 EndModal(TQSL_DP_ALLOW);
588 }
589 }
590
591 #define TQSL_AE_OK TQSL_ID_LOW+40
592 #define TQSL_AE_CAN TQSL_ID_LOW+41
593
594 class ErrorsDialog : public wxDialog {
595 public:
596 explicit ErrorsDialog(wxWindow *parent = 0, wxString msg = wxT(""));
597 private:
598 void OnOk(wxCommandEvent&);
599 void OnCancel(wxCommandEvent&);
600 DECLARE_EVENT_TABLE()
601 };
602
BEGIN_EVENT_TABLE(ErrorsDialog,wxDialog)603 BEGIN_EVENT_TABLE(ErrorsDialog, wxDialog)
604 EVT_BUTTON(TQSL_AE_OK, ErrorsDialog::OnOk)
605 EVT_BUTTON(TQSL_AE_CAN, ErrorsDialog::OnCancel)
606 END_EVENT_TABLE()
607
608 ErrorsDialog::ErrorsDialog(wxWindow *parent, wxString msg)
609 : wxDialog(parent, -1, wxString(_("Errors Detected")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) {
610 tqslTrace("ErrorsDialog::ErrorsDialog", "msg=%s", S(msg));
611 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
612
613 sizer->Add(new wxStaticText(this, -1, msg), 0, wxALL|wxALIGN_CENTER, 10);
614
615 wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
616 hsizer->Add(new wxButton(this, TQSL_AE_OK, _("Ignore")), 0, wxRIGHT, 5);
617 hsizer->Add(new wxButton(this, TQSL_AE_CAN, _("Cancel")), 0, wxLEFT, 10);
618 sizer->Add(hsizer, 0, wxALIGN_CENTER|wxALL, 10);
619 SetAutoLayout(TRUE);
620 SetSizer(sizer);
621 sizer->Fit(this);
622 sizer->SetSizeHints(this);
623 CenterOnParent();
624 }
625
626 void
OnOk(wxCommandEvent &)627 ErrorsDialog::OnOk(wxCommandEvent&) {
628 tqslTrace("ErrorsDialog::OnOk", NULL);
629 EndModal(TQSL_AE_OK);
630 }
631
632 void
OnCancel(wxCommandEvent &)633 ErrorsDialog::OnCancel(wxCommandEvent&) {
634 tqslTrace("ErrorsDialog::OnCancel", NULL);
635 EndModal(TQSL_AE_CAN);
636 }
637
638 static void
init_modes()639 init_modes() {
640 tqslTrace("init_modes", NULL);
641 tqsl_clearADIFModes();
642 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
643 long cookie;
644 wxString key, value;
645 vector<wxString> badModes;
646 config->SetPath(wxT("/modeMap"));
647 bool stat = config->GetFirstEntry(key, cookie);
648 while (stat) {
649 value = config->Read(key, wxT(""));
650 bool newMode = true;
651 int numModes;
652 if (tqsl_getNumMode(&numModes) == 0) {
653 for (int i = 0; i < numModes; i++) {
654 const char *modestr;
655 if (tqsl_getMode(i, &modestr, NULL) == 0) {
656 if (strcasecmp(key.ToUTF8(), modestr) == 0) {
657 wxLogWarning(_("Your custom mode map %s conflicts with the standard mode definition for %hs and was deleted."), key.c_str(), modestr);
658 newMode = false;
659 badModes.push_back(key);
660 break;
661 }
662 }
663 }
664 }
665 if (newMode)
666 tqsl_setADIFMode(key.ToUTF8(), value.ToUTF8());
667 stat = config->GetNextEntry(key, cookie);
668 }
669 // Delete the conflicting entries
670 for (int i = 0; i < static_cast<int>(badModes.size()); i++) {
671 config->DeleteEntry(badModes[i]);
672 }
673 config->SetPath(wxT("/"));
674 }
675
676 static void
init_contests()677 init_contests() {
678 tqslTrace("init_contests", NULL);
679 tqsl_clearCabrilloMap();
680 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
681 long cookie;
682 wxString key, value;
683 config->SetPath(wxT("/cabrilloMap"));
684 bool stat = config->GetFirstEntry(key, cookie);
685 while (stat) {
686 value = config->Read(key, wxT(""));
687 int contest_type = strtol(value.ToUTF8(), NULL, 10);
688 int callsign_field = strtol(value.AfterFirst(';').ToUTF8(), NULL, 10);
689 tqsl_setCabrilloMapEntry(key.ToUTF8(), callsign_field, contest_type);
690 stat = config->GetNextEntry(key, cookie);
691 }
692 config->SetPath(wxT("/"));
693 }
694
695 static void
check_tqsl_error(int rval)696 check_tqsl_error(int rval) {
697 if (rval == 0)
698 return;
699 tqslTrace("check_tqsl_error", "rval=%d", rval);
700 wxString msg = getLocalizedErrorString();
701 tqslTrace("check_tqsl_error", "msg=%s", S(msg));
702 throw TQSLException(S(msg));
703 }
704
705 static tQSL_Cert *certlist = 0;
706 static int ncerts;
707
708 static void
free_certlist()709 free_certlist() {
710 tqslTrace("free_certlist", NULL);
711 if (certlist) {
712 tqsl_freeCertificateList(certlist, ncerts);
713 certlist = 0;
714 }
715 ncerts = 0;
716 }
717
718 static void
get_certlist(string callsign,int dxcc,bool expired,bool superceded,bool withkeys)719 get_certlist(string callsign, int dxcc, bool expired, bool superceded, bool withkeys) {
720 tqslTrace("get_certlist", "callsign=%s, dxcc=%d, expired=%d, superceded=%d withkeys=%d", callsign.c_str(), dxcc, expired, superceded, withkeys);
721 free_certlist();
722 int select = 0;
723 if (expired) select |= TQSL_SELECT_CERT_EXPIRED;
724 if (superceded) select |= TQSL_SELECT_CERT_SUPERCEDED;
725 if (withkeys) select |= TQSL_SELECT_CERT_WITHKEYS;
726 tqsl_selectCertificates(&certlist, &ncerts,
727 (callsign == "") ? 0 : callsign.c_str(), dxcc, 0, 0, select);
728 }
729
730 class LogList : public wxLog {
731 public:
LogList(MyFrame * frame)732 explicit LogList(MyFrame *frame) : wxLog(), _frame(frame) {}
733 virtual void DoLogString(const wxChar *szString, time_t t);
734 private:
735 MyFrame *_frame;
736 };
737
DoLogString(const wxChar * szString,time_t)738 void LogList::DoLogString(const wxChar *szString, time_t) {
739 wxTextCtrl *_logwin = 0;
740 const char *msg = wxString(szString).ToUTF8();
741 tqslTrace(NULL, "%s", msg);
742 if (wxString(szString).StartsWith(wxT("Debug:")))
743 return;
744 if (wxString(szString).StartsWith(wxT("Error: Unable to open requested HTML document:")))
745 return;
746 if (_frame != 0)
747 _logwin = _frame->logwin;
748 if (_logwin == 0) {
749 #ifdef _WIN32
750 cerr << szString << endl;
751 #else
752 fprintf(stderr, "%ls\n", szString);
753 #endif
754 return;
755 }
756 _logwin->AppendText(szString);
757 _logwin->AppendText(wxT("\n"));
758 // Select the log tab when there's some new message
759 if (_frame->notebook->GetPageCount() > TQSL_LOG_TAB)
760 _frame->notebook->SetSelection(TQSL_LOG_TAB);
761 }
762
763 class LogStderr : public wxLog {
764 public:
LogStderr(void)765 LogStderr(void) : wxLog() {}
766 virtual void DoLogString(const wxChar *szString, time_t t);
767 };
768
DoLogString(const wxChar * szString,time_t)769 void LogStderr::DoLogString(const wxChar *szString, time_t) {
770 const char *msg = wxString(szString).ToUTF8();
771 tqslTrace(NULL, "%s", msg);
772 if (wxString(szString).StartsWith(wxT("Debug:")))
773 return;
774 #ifdef _WIN32
775 cerr << szString << endl;
776 #else
777 fprintf(stderr, "%ls\n", szString);
778 #endif
779 return;
780 }
781
BEGIN_EVENT_TABLE(MyFrame,wxFrame)782 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
783 EVT_MENU(tm_s_add, MyFrame::AddStationLocation)
784 EVT_BUTTON(tl_AddLoc, MyFrame::AddStationLocation)
785 EVT_MENU(tm_s_edit, MyFrame::EditStationLocation)
786 EVT_BUTTON(tl_EditLoc, MyFrame::EditStationLocation)
787 EVT_MENU(tm_f_new, MyFrame::EnterQSOData)
788 EVT_BUTTON(tl_Edit, MyFrame::EnterQSOData)
789 EVT_MENU(tm_f_edit, MyFrame::EditQSOData)
790 EVT_MENU(tm_f_import_compress, MyFrame::ImportQSODataFile)
791 EVT_BUTTON(tl_Save, MyFrame::ImportQSODataFile)
792 EVT_MENU(tm_f_upload, MyFrame::UploadQSODataFile)
793 EVT_BUTTON(tl_Upload, MyFrame::UploadQSODataFile)
794 EVT_MENU(tm_f_exit, MyFrame::DoExit)
795 EVT_MENU(tm_f_preferences, MyFrame::OnPreferences)
796 EVT_MENU(tm_f_loadconfig, MyFrame::OnLoadConfig)
797 EVT_MENU(tm_f_saveconfig, MyFrame::OnSaveConfig)
798 EVT_MENU(tm_h_contents, MyFrame::OnHelpContents)
799 EVT_MENU(tm_h_about, MyFrame::OnHelpAbout)
800 EVT_MENU(tm_f_diag, MyFrame::OnHelpDiagnose)
801 EVT_MENU(tm_f_lang, MyFrame::OnChooseLanguage)
802
803 EVT_MENU(tm_h_update, MyFrame::CheckForUpdates)
804
805 EVT_CLOSE(MyFrame::OnExit)
806
807 EVT_MENU(tc_CRQWizard, MyFrame::CRQWizard)
808 EVT_MENU(tc_c_New, MyFrame::CRQWizard)
809 EVT_MENU(tc_c_Load, MyFrame::OnLoadCertificateFile)
810 EVT_BUTTON(tc_Load, MyFrame::OnLoadCertificateFile)
811 EVT_MENU(tc_c_Properties, MyFrame::OnCertProperties)
812 EVT_BUTTON(tc_CertProp, MyFrame::OnCertProperties)
813 EVT_MENU(tc_c_Export, MyFrame::OnCertExport)
814 EVT_BUTTON(tc_CertSave, MyFrame::OnCertExport)
815 EVT_MENU(tc_c_Delete, MyFrame::OnCertDelete)
816 EVT_MENU(tc_c_Undelete, MyFrame::OnCertUndelete)
817 // EVT_MENU(tc_c_Import, MyFrame::OnCertImport)
818 // EVT_MENU(tc_c_Sign, MyFrame::OnSign)
819 EVT_MENU(tc_c_Renew, MyFrame::CRQWizardRenew)
820 EVT_BUTTON(tc_CertRenew, MyFrame::CRQWizardRenew)
821 EVT_MENU(tc_h_Contents, MyFrame::OnHelpContents)
822 EVT_MENU(tc_h_About, MyFrame::OnHelpAbout)
823 EVT_MENU(tl_c_Properties, MyFrame::OnLocProperties)
824 EVT_MENU(tm_s_Properties, MyFrame::OnLocProperties)
825 EVT_MENU(tm_s_undelete, MyFrame::OnLocUndelete)
826 EVT_BUTTON(tl_PropLoc, MyFrame::OnLocProperties)
827 EVT_MENU(tl_c_Delete, MyFrame::OnLocDelete)
828 EVT_BUTTON(tl_DeleteLoc, MyFrame::OnLocDelete)
829 EVT_MENU(tl_c_Edit, MyFrame::OnLocEdit)
830 EVT_BUTTON(tl_Login, MyFrame::OnLoginToLogbook)
831 EVT_TREE_SEL_CHANGED(tc_CertTree, MyFrame::OnCertTreeSel)
832 EVT_TREE_SEL_CHANGED(tc_LocTree, MyFrame::OnLocTreeSel)
833
834 EVT_MENU(bg_updateCheck, MyFrame::OnUpdateCheckDone)
835 EVT_MENU(bg_expiring, MyFrame::OnExpiredCertFound)
836
837 END_EVENT_TABLE()
838
839 void
840 MyFrame::SaveOldBackups(const wxString& directory, const wxString& filename, const wxString& ext) {
841 #ifdef _WIN32
842 wxString bfile = directory + wxT("\\") + filename + wxT(".") + ext;
843 struct _stat32 s;
844 wchar_t* lfn = utf8_to_wchar(bfile.ToUTF8());
845 int ret = _wstat32(lfn, &s);
846 free_wchar(lfn);
847 if (ret != 0) { // Does it exist?
848 #else
849 wxString bfile = directory + wxT("/") + filename + wxT(".") + ext;
850 struct stat s;
851 if (lstat(bfile.ToUTF8(), &s) != 0) { // Does it exist?
852 #endif
853 return; // No. No need to back it up.
854 }
855
856 // There's a file with that name already.
857 // Rename it for backup purposes
858
859 struct tm *t;
860
861 #ifdef _WIN32
862 t = _gmtime32(&s.st_mtime);
863 wxString newName = directory + wxT("\\") + filename +
864 #else
865 t = gmtime(&s.st_mtime);
866 wxString newName = directory + wxT("/") + filename +
867 #endif
868 wxString::Format(wxT("-%4.4d-%2.2d-%2.2d-%2.2d-%2.2d."),
869 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
870 t->tm_hour, t->tm_min) + ext;
871 #ifdef _WIN32
872 lfn = utf8_to_wchar(bfile.ToUTF8());
873 wchar_t* newlfn = utf8_to_wchar(newName.ToUTF8());
874 ret = _wrename(lfn, newlfn);
875 free_wchar(lfn);
876 free_wchar(newlfn);
877 #else
878 int ret = rename(bfile.ToUTF8(), newName.ToUTF8());
879 #endif
880 if (ret) {
881 tQSL_Error = TQSL_SYSTEM_ERROR;
882 tQSL_Errno = errno;
883 tqslTrace("MyFrame::SaveOldBackups", "Error renaming: %s", strerror(errno));
884 wxLogError(_("Error renaming backup file %s: %hs"), bfile.c_str(), strerror(errno));
885 return;
886 }
887
888 // Find any backups and delete older ones
889
890 wxArrayString bfiles;
891
892 #ifdef _WIN32
893 wchar_t* wpath = utf8_to_wchar(directory);
894 _WDIR *dir = _wopendir(wpath);
895 free_wchar(wpath);
896 #else
897 DIR *dir = opendir(directory.ToUTF8());
898 #endif
899
900 if (dir != NULL) {
901 #ifdef _WIN32
902 struct _wdirent *ent = NULL;
903 while ((ent = _wreaddir(dir)) != NULL) {
904 if (wcsstr(ent->d_name, L"tqslconfig-") == ent->d_name && wcsstr(ent->d_name, L".tbk")) {
905 bfiles.Add(wxString(ent->d_name));
906 }
907 }
908 #else
909 struct dirent *ent = NULL;
910 while ((ent = readdir(dir)) != NULL) {
911 if (strstr(ent->d_name, "tqslconfig-") == ent->d_name && strstr(ent->d_name, ".tbk")) {
912 bfiles.Add(wxString::FromUTF8(ent->d_name));
913 }
914 }
915 #endif
916 }
917 bfiles.Sort();
918 long vlimit = DEFAULT_BACKUP_VERSIONS;
919 wxConfig::Get()->Read(wxT("BackupVersions"), &vlimit, DEFAULT_BACKUP_VERSIONS);
920
921 if (vlimit <= 0)
922 vlimit = DEFAULT_BACKUP_VERSIONS;
923 int toRemove = bfiles.GetCount() - vlimit;
924 if (toRemove <= 0)
925 return; // Nothing to remove
926
927 // Remove, starting from the oldest
928 for (int i = 0; i < toRemove; i++) {
929 #ifdef _WIN32
930 wxString removeIt = directory + wxT("\\") + bfiles[i];
931 wchar_t* wfname = utf8_to_wchar(removeIt.ToUTF8());
932 _wunlink(wfname);
933 free_wchar(wfname);
934 #else
935 wxString removeIt = directory + wxT("/") + bfiles[i];
936 unlink(removeIt.ToUTF8());
937 #endif
938 }
939 }
940
941 void
942 MyFrame::SaveWindowLayout() {
943 int x, y, w, h;
944 // Don't save window size/position if minimized or too small
945 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
946 if (!IsIconized()) {
947 GetPosition(&x, &y);
948 GetSize(&w, &h);
949 if (w >= MAIN_WINDOW_MIN_WIDTH && h >= MAIN_WINDOW_MIN_HEIGHT) {
950 config->Write(wxT("MainWindowX"), x);
951 config->Write(wxT("MainWindowY"), y);
952 config->Write(wxT("MainWindowWidth"), w);
953 config->Write(wxT("MainWindowHeight"), h);
954 config->Write(wxT("MainWindowMaximized"), IsMaximized());
955 config->Flush(false);
956 }
957 }
958 }
959 void
960 MyFrame::OnExit(TQ_WXCLOSEEVENT& WXUNUSED(event)) {
961 tqslTrace("MyFrame::OnExit", "exiting");
962 if (logConv) {
963 tqsl_converterRollBack(logConv);
964 tqsl_endConverter(&logConv);
965 }
966 SaveWindowLayout();
967 unlock_db();
968 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
969 bool ab;
970 config->Read(wxT("AutoBackup"), &ab, DEFAULT_AUTO_BACKUP);
971 if (ab) {
972 wxString bdir = config->Read(wxT("BackupFolder"), wxString::FromUTF8(tQSL_BaseDir));
973 if (bdir.Trim(true).Trim(false) == wxT(""))
974 bdir = wxString::FromUTF8(tQSL_BaseDir);
975 SaveOldBackups(bdir, wxT("tqslconfig"), wxT("tbk"));
976 #ifdef _WIN32
977 bdir += wxT("\\tqslconfig.tbk");
978 #else
979 bdir += wxT("/tqslconfig.tbk");
980 #endif
981 tqslTrace("MyFrame::OnExit", "Auto Backup %s", S(bdir));
982 BackupConfig(bdir, true);
983 }
984 tqslTrace("MyFrame::OnExit", "GUI Destroy");
985 Destroy(); // close the window
986 tqslTrace("MyFrame::OnExit", "Done");
987 }
988
989 void
990 MyFrame::DoExit(wxCommandEvent& WXUNUSED(event)) {
991 tqslTrace("MyFrame::DoExit", "About to close");
992 Close();
993 tqslTrace("MyFrame::DoExit", "About to destroy GUI");
994 Destroy();
995 tqslTrace("MyFrame::DoExit", "Done");
996 }
997
998 static wxSemaphore *updateLocker;
999
1000 class UpdateThread : public wxThread {
1001 public:
1002 UpdateThread(MyFrame *frame, bool silent, bool noGUI) : wxThread(wxTHREAD_DETACHED) {
1003 _frame = frame;
1004 _silent = silent;
1005 _noGUI = noGUI;
1006 }
1007 virtual void *Entry() {
1008 _frame->DoCheckForUpdates(_silent, _noGUI);
1009 updateLocker->Post();
1010 return NULL;
1011 }
1012 private:
1013 MyFrame *_frame;
1014 bool _silent;
1015 bool _noGUI;
1016 };
1017
1018 void
1019 MyFrame::DoUpdateCheck(bool silent, bool noGUI) {
1020 tqslTrace("MyFrame::DoUpdateCheck", "silent=%d noGUI=%d", silent, noGUI);
1021 //check for updates
1022 if (!noGUI) {
1023 wxBeginBusyCursor();
1024 wxTheApp->Yield(true);
1025 wxLogMessage(_("Checking for TQSL updates..."));
1026 wxTheApp->Yield(true);
1027 }
1028 updateLocker = new wxSemaphore(0, 1); // One waiter
1029 UpdateThread* thread = new UpdateThread(this, silent, noGUI);
1030 thread->Create();
1031 wxTheApp->Yield(true);
1032 thread->Run();
1033 wxTheApp->Yield(true);
1034 while (updateLocker->TryWait() == wxSEMA_BUSY) {
1035 wxTheApp->Yield(true);
1036 #if (wxMAJOR_VERSION == 2 && wxMINOR_VERSION == 9)
1037 wxGetApp().DoIdle();
1038 #endif
1039 wxMilliSleep(300);
1040 }
1041 delete updateLocker;
1042 if (!noGUI) {
1043 wxString val = logwin->GetValue();
1044 wxString chk(_("Checking for TQSL updates..."));
1045 val.Replace(chk + wxT("\n"), wxT(""));
1046 val.Replace(chk, wxT(""));
1047 logwin->SetValue(val); // Clear the checking message
1048 notebook->SetSelection(0);
1049 // Refresh the cert tree in case any new info on expires/supercedes
1050 cert_tree->Build(CERTLIST_FLAGS);
1051 CertTreeReset();
1052 wxEndBusyCursor();
1053 }
1054 }
1055
1056 MyFrame::MyFrame(const wxString& title, int x, int y, int w, int h, bool checkUpdates, bool quiet, wxLocale* loca)
1057 : wxFrame(0, -1, title, wxPoint(x, y), wxSize(w, h)), locale(loca) {
1058 _quiet = quiet;
1059
1060 #ifdef __WXMAC__
1061 DocPaths docpaths(wxT("tqsl.app"));
1062 #else
1063 DocPaths docpaths(wxT("tqslapp"));
1064 #endif
1065 wxBitmap savebm(save_xpm);
1066 wxBitmap uploadbm(upload_xpm);
1067 wxBitmap upload_disbm(upload_dis_xpm);
1068 wxBitmap file_editbm(file_edit_xpm);
1069 wxBitmap file_edit_disbm(file_edit_dis_xpm);
1070 wxBitmap locaddbm(loc_add_xpm);
1071 wxBitmap locadd_disbm(loc_add_dis_xpm);
1072 wxBitmap editbm(edit_xpm);
1073 wxBitmap edit_disbm(edit_dis_xpm);
1074 wxBitmap deletebm(delete_xpm);
1075 wxBitmap delete_disbm(delete_dis_xpm);
1076 wxBitmap downloadbm(download_xpm);
1077 wxBitmap download_disbm(download_dis_xpm);
1078 wxBitmap propertiesbm(properties_xpm);
1079 wxBitmap properties_disbm(properties_dis_xpm);
1080 wxBitmap importbm(import_xpm);
1081 wxBitmap lotwbm(lotw_xpm);
1082 loc_edit_button = NULL;
1083 cert_save_label = NULL;
1084 req = NULL;
1085 curlReq = NULL;
1086 curlLogFile = NULL;
1087 logConv = NULL;
1088
1089 // File menu
1090 file_menu = new wxMenu;
1091 file_menu->Append(tm_f_upload, _("Sign and &upload ADIF or Cabrillo File..."));
1092 file_menu->Append(tm_f_import_compress, _("&Sign and save ADIF or Cabrillo file..."));
1093 file_menu->AppendSeparator();
1094 file_menu->Append(tm_f_saveconfig, _("&Backup Station Locations, Certificates, and Preferences..."));
1095 file_menu->Append(tm_f_loadconfig, _("&Restore Station Locations, Certificates, and Preferences..."));
1096 file_menu->AppendSeparator();
1097 file_menu->Append(tm_f_new, _("Create &New ADIF file..."));
1098 file_menu->Append(tm_f_edit, _("&Edit existing ADIF file..."));
1099 file_menu->AppendSeparator();
1100 #ifdef __WXMAC__ // On Mac, Preferences not on File menu
1101 file_menu->Append(tm_f_preferences, _("&Preferences..."));
1102 #else
1103 file_menu->Append(tm_f_preferences, _("Display or Modify &Preferences..."));
1104 #endif
1105 file_menu->AppendSeparator();
1106 file_menu->Append(tm_f_lang, _("Language"));
1107 file_menu->AppendSeparator();
1108 file_menu->AppendCheckItem(tm_f_diag, _("Dia&gnostic Mode"));
1109 file_menu->Check(tm_f_diag, false);
1110 #ifndef __WXMAC__ // On Mac, Exit not on File menu
1111 file_menu->AppendSeparator();
1112 #endif
1113 file_menu->Append(tm_f_exit, _("E&xit TQSL\tAlt-X"));
1114
1115 cert_menu = makeCertificateMenu(false);
1116 // Station menu
1117 stn_menu = new wxMenu;
1118 stn_menu->Append(tm_s_Properties, _("&Display Station Location Properties"));
1119 stn_menu->Enable(tm_s_Properties, false);
1120 stn_menu->Append(tm_s_edit, _("&Edit Station Location"));
1121 stn_menu->Append(tm_s_add, _("&Add Station Location"));
1122 stn_menu->AppendSeparator();
1123
1124 int nloc = 0;
1125 char **locp = NULL;
1126 tqsl_getDeletedStationLocations(&locp, &nloc);
1127 stn_menu->Append(tm_s_undelete, _("&Restore a Deleted Station Location"));
1128 stn_menu->Enable(tm_s_undelete, nloc > 0);
1129 if (nloc > 0)
1130 tqsl_freeDeletedLocationList(locp, nloc);
1131
1132 // Help menu
1133 help = new wxHtmlHelpController(wxHF_DEFAULT_STYLE | wxHF_OPEN_FILES);
1134 help_menu = new wxMenu;
1135 help->UseConfig(wxConfig::Get());
1136 wxString hhp = docpaths.FindAbsoluteValidPath(wxT("tqslapp.hhp"));
1137 if (wxFileNameFromPath(hhp) != wxT("")) {
1138 if (help->AddBook(hhp))
1139 #ifdef __WXMAC__
1140 help_menu->Append(tm_h_contents, _("&Contents"));
1141 #else
1142 help_menu->Append(tm_h_contents, _("Display &Documentation"));
1143 help_menu->AppendSeparator();
1144 #endif
1145 }
1146
1147 help_menu->Append(tm_h_update, _("Check for &Updates..."));
1148
1149 help_menu->Append(tm_h_about, _("&About"));
1150 // Main menu
1151 wxMenuBar *menu_bar = new wxMenuBar;
1152 menu_bar->Append(file_menu, _("&File"));
1153 menu_bar->Append(stn_menu, _("&Station Location"));
1154 menu_bar->Append(cert_menu, _("Callsign &Certificate"));
1155 menu_bar->Append(help_menu, _("&Help"));
1156
1157 SetMenuBar(menu_bar);
1158
1159 wxPanel* topPanel = new wxPanel(this);
1160 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
1161 topPanel->SetSizer(topSizer);
1162
1163 bool logTab = DEFAULT_LOG_TAB;
1164 wxConfig::Get()->Read(wxT("LogTab"), &logTab, DEFAULT_LOG_TAB);
1165
1166 // Log operations
1167
1168 topSizer->AddSpacer(2);
1169
1170 notebook = new wxNotebook(topPanel, -1, wxDefaultPosition, wxSize(400, 300), wxNB_TOP /* | wxNB_FIXEDWIDTH*/, _("Log Operations"));
1171
1172 notebook->SetBackgroundColour(wxColour(255, 255, 255));
1173 topSizer->Add(notebook, 1, wxEXPAND | wxALL, 1);
1174
1175 if (!logTab) {
1176 topSizer->Add(new wxStaticText(topPanel, -1, _("Status Log")), 0, wxEXPAND | wxALL, 1);
1177
1178 logwin = new wxTextCtrl(topPanel, -1, wxT(""), wxDefaultPosition, wxSize(400, 300),
1179 wxTE_MULTILINE|wxTE_READONLY);
1180 topSizer->Add(logwin, 1, wxEXPAND | wxALL, 1);
1181 }
1182
1183 wxPanel* buttons = new wxPanel(notebook, -1);
1184 buttons->SetBackgroundColour(wxColour(255, 255, 255));
1185
1186 wxBoxSizer* bsizer = new wxBoxSizer(wxVERTICAL);
1187 buttons->SetSizer(bsizer);
1188
1189 wxPanel* b1Panel = new wxPanel(buttons);
1190 b1Panel->SetBackgroundColour(wxColour(255, 255, 255));
1191 wxBoxSizer* b1sizer = new wxBoxSizer(wxHORIZONTAL);
1192 b1Panel->SetSizer(b1sizer);
1193
1194 wxBitmapButton *up = new wxBitmapButton(b1Panel, tl_Upload, uploadbm);
1195 // Use a really tiny label font on the buttons, as the labels are there
1196 // for accessibility only.
1197 wxFont f(1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1198 up->SetLabel(_("Sign a log and upload it automatically to LoTW"));
1199 up->SetBitmapDisabled(upload_disbm);
1200 up->SetFont(f);
1201 b1sizer->Add(up, 0, wxALL, 1);
1202 wxString b1lbl = wxT("\n");
1203 b1lbl += _("Sign a log and upload it automatically to LoTW");
1204 b1sizer->Add(new wxStaticText(b1Panel, -1, b1lbl), 1, wxALL, 1);
1205 bsizer->Add(b1Panel, 0, wxALL, 1);
1206
1207 wxPanel* b2Panel = new wxPanel(buttons);
1208 wxBoxSizer* b2sizer = new wxBoxSizer(wxHORIZONTAL);
1209 b2Panel->SetBackgroundColour(wxColour(255, 255, 255));
1210 b2Panel->SetSizer(b2sizer);
1211 wxBitmapButton *signsave = new wxBitmapButton(b2Panel, tl_Save, savebm);
1212 signsave->SetLabel(_("Sign a log and save it for uploading later"));
1213 signsave->SetFont(f);
1214 b2sizer->Add(signsave, 0, wxALL, 1);
1215 wxString b2lbl = wxT("\n");
1216 b2lbl += _("Sign a log and save it for uploading later");
1217 b2sizer->Add(new wxStaticText(b2Panel, -1, b2lbl), 1, wxALL, 1);
1218 bsizer->Add(b2Panel, 0, wxALL, 1);
1219
1220 wxPanel* b3Panel = new wxPanel(buttons);
1221 wxBoxSizer* b3sizer = new wxBoxSizer(wxHORIZONTAL);
1222 b3Panel->SetBackgroundColour(wxColour(255, 255, 255));
1223 b3Panel->SetSizer(b3sizer);
1224 wxBitmapButton *fed = new wxBitmapButton(b3Panel, tl_Edit, file_editbm);
1225 fed->SetBitmapDisabled(file_edit_disbm);
1226 fed->SetLabel(_("Create an ADIF file for signing and uploading"));
1227 fed->SetFont(f);
1228 b3sizer->Add(fed, 0, wxALL, 1);
1229 wxString b3lbl = wxT("\n");
1230 b3lbl += _("Create an ADIF file for signing and uploading");
1231 b3sizer->Add(new wxStaticText(b3Panel, -1, b3lbl), 1, wxALL, 1);
1232 bsizer->Add(b3Panel, 0, wxALL, 1);
1233
1234 wxPanel* b4Panel = new wxPanel(buttons);
1235 wxBoxSizer* b4sizer = new wxBoxSizer(wxHORIZONTAL);
1236 b4Panel->SetBackgroundColour(wxColour(255, 255, 255));
1237 b4Panel->SetSizer(b4sizer);
1238 wxBitmapButton *lotw = new wxBitmapButton(b4Panel, tl_Login, lotwbm);
1239 lotw->SetLabel(_("Log in to the Logbook of the World Site"));
1240 lotw->SetFont(f);
1241 b4sizer->Add(lotw, 0, wxALL, 1);
1242 wxString b4lbl = wxT("\n");
1243 b4lbl += _("Log in to the Logbook of the World Site");
1244 b4sizer->Add(new wxStaticText(b4Panel, -1, b4lbl), 1, wxALL, 1);
1245 bsizer->Add(b4Panel, 0, wxALL, 1);
1246
1247 notebook->AddPage(buttons, _("Log Operations"));
1248
1249 // notebook->InvalidateBestSize();
1250 // logwin->FitInside();
1251
1252 // Location tab
1253
1254 wxPanel* loctab = new wxPanel(notebook, -1);
1255
1256 wxBoxSizer* locsizer = new wxBoxSizer(wxHORIZONTAL);
1257 loctab->SetSizer(locsizer);
1258
1259 wxPanel* locgrid = new wxPanel(loctab, -1);
1260 locgrid->SetBackgroundColour(wxColour(255, 255, 255));
1261 wxBoxSizer* lgsizer = new wxBoxSizer(wxVERTICAL);
1262 locgrid->SetSizer(lgsizer);
1263
1264 loc_tree = new LocTree(locgrid, tc_LocTree, wxDefaultPosition,
1265 wxDefaultSize, wxTR_DEFAULT_STYLE | wxBORDER_NONE);
1266
1267 loc_tree->SetBackgroundColour(wxColour(255, 255, 255));
1268 loc_tree->Build();
1269 LocTreeReset();
1270 lgsizer->Add(loc_tree, 1, wxEXPAND);
1271
1272 wxString lsl = wxT("\n");
1273 lsl += _("Select a Station Location to process ");
1274 loc_select_label = new wxStaticText(locgrid, -1, lsl);
1275 lgsizer->Add(loc_select_label, 0, wxALL, 1);
1276
1277 locsizer->Add(locgrid, 50, wxEXPAND);
1278
1279 wxStaticLine *locsep = new wxStaticLine(loctab, -1, wxDefaultPosition, wxSize(2, -1), wxLI_VERTICAL);
1280 locsizer->Add(locsep, 0, wxEXPAND);
1281
1282 wxPanel* lbuttons = new wxPanel(loctab, -1);
1283 lbuttons->SetBackgroundColour(wxColour(255, 255, 255));
1284 locsizer->Add(lbuttons, 50, wxEXPAND);
1285 wxBoxSizer* lbsizer = new wxBoxSizer(wxVERTICAL);
1286 lbuttons->SetSizer(lbsizer);
1287
1288 wxPanel* lb1Panel = new wxPanel(lbuttons);
1289 lb1Panel->SetBackgroundColour(wxColour(255, 255, 255));
1290 wxBoxSizer* lb1sizer = new wxBoxSizer(wxHORIZONTAL);
1291 lb1Panel->SetSizer(lb1sizer);
1292
1293 loc_add_button = new wxBitmapButton(lb1Panel, tl_AddLoc, locaddbm);
1294 loc_add_button->SetFont(f);
1295 loc_add_button->SetBitmapDisabled(locadd_disbm);
1296 lb1sizer->Add(loc_add_button, 0, wxALL, 1);
1297 // Note - the doubling below is to size the label to allow the control to stretch later
1298 loc_add_label = new wxStaticText(lb1Panel, -1, wxT("\nCreate a new Station LocationCreate a new Station\n"));
1299 lb1sizer->Add(loc_add_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1300 lbsizer->Add(lb1Panel, 0, wxALL, 1);
1301 int tw, th;
1302 loc_add_label->GetSize(&tw, &th);
1303 wxString lal = wxT("\n");
1304 lal += _("Create a new Station Location");
1305 loc_add_label->SetLabel(lal);
1306 loc_add_button->SetLabel(lal);
1307
1308 wxPanel* lb2Panel = new wxPanel(lbuttons);
1309 lb2Panel->SetBackgroundColour(wxColour(255, 255, 255));
1310 wxBoxSizer* lb2sizer = new wxBoxSizer(wxHORIZONTAL);
1311 lb2Panel->SetSizer(lb2sizer);
1312
1313 loc_edit_button = new wxBitmapButton(lb2Panel, tl_EditLoc, editbm);
1314 loc_edit_button->SetFont(f);
1315 loc_edit_button->SetBitmapDisabled(edit_disbm);
1316 loc_edit_button->Enable(false);
1317 lb2sizer->Add(loc_edit_button, 0, wxALL, 1);
1318 wxString lel = wxT("\n");
1319 lel += _("Edit a Station Location");
1320 loc_edit_label = new wxStaticText(lb2Panel, -1, lel, wxDefaultPosition, wxSize(tw, th));
1321 lb2sizer->Add(loc_edit_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1322 lbsizer->Add(lb2Panel, 0, wxALL, 1);
1323
1324 wxPanel* lb3Panel = new wxPanel(lbuttons);
1325 lb3Panel->SetBackgroundColour(wxColour(255, 255, 255));
1326 wxBoxSizer* lb3sizer = new wxBoxSizer(wxHORIZONTAL);
1327 lb3Panel->SetSizer(lb3sizer);
1328
1329 loc_delete_button = new wxBitmapButton(lb3Panel, tl_DeleteLoc, deletebm);
1330 loc_delete_button->SetFont(f);
1331 loc_delete_button->SetBitmapDisabled(delete_disbm);
1332 loc_delete_button->Enable(false);
1333 lb3sizer->Add(loc_delete_button, 0, wxALL, 1);
1334 wxString ldl = wxT("\n");
1335 ldl += _("Delete a Station Location");
1336 loc_delete_label = new wxStaticText(lb3Panel, -1, ldl, wxDefaultPosition, wxSize(tw, th));
1337 lb3sizer->Add(loc_delete_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1338 lbsizer->Add(lb3Panel, 0, wxALL, 1);
1339
1340 wxPanel* lb4Panel = new wxPanel(lbuttons);
1341 lb4Panel->SetBackgroundColour(wxColour(255, 255, 255));
1342 wxBoxSizer* lb4sizer = new wxBoxSizer(wxHORIZONTAL);
1343 lb4Panel->SetSizer(lb4sizer);
1344
1345 loc_prop_button = new wxBitmapButton(lb4Panel, tl_PropLoc, propertiesbm);
1346 loc_prop_button->SetFont(f);
1347 loc_prop_button->SetBitmapDisabled(properties_disbm);
1348 loc_prop_button->Enable(false);
1349 lb4sizer->Add(loc_prop_button, 0, wxALL, 1);
1350 wxString lpl = wxT("\n");
1351 lpl += _("Display Station Location Properties");
1352 loc_prop_label = new wxStaticText(lb4Panel, -1, lpl, wxDefaultPosition, wxSize(tw, th));
1353 lb4sizer->Add(loc_prop_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1354 lbsizer->Add(lb4Panel, 0, wxALL, 1);
1355
1356 notebook->AddPage(loctab, _("Station Locations"));
1357
1358 // Certificates tab
1359
1360 wxPanel* certtab = new wxPanel(notebook, -1);
1361
1362 wxBoxSizer* certsizer = new wxBoxSizer(wxHORIZONTAL);
1363 certtab->SetSizer(certsizer);
1364
1365 wxPanel* certgrid = new wxPanel(certtab, -1);
1366 certgrid->SetBackgroundColour(wxColour(255, 255, 255));
1367 wxBoxSizer* cgsizer = new wxBoxSizer(wxVERTICAL);
1368 certgrid->SetSizer(cgsizer);
1369
1370 cert_tree = new CertTree(certgrid, tc_CertTree, wxDefaultPosition,
1371 wxDefaultSize, wxTR_DEFAULT_STYLE | wxBORDER_NONE); //wxTR_HAS_BUTTONS | wxSUNKEN_BORDER);
1372
1373 cert_tree->SetBackgroundColour(wxColour(255, 255, 255));
1374 cgsizer->Add(cert_tree, 1, wxEXPAND);
1375
1376 wxString csq = wxT("\n");
1377 csq += _("Select a Callsign Certificate to process");
1378 cert_select_label = new wxStaticText(certgrid, -1, csq);
1379 cgsizer->Add(cert_select_label, 0, wxALL, 1);
1380
1381 certsizer->Add(certgrid, 50, wxEXPAND);
1382
1383 wxStaticLine *certsep = new wxStaticLine(certtab, -1, wxDefaultPosition, wxSize(2, -1), wxLI_VERTICAL);
1384 certsizer->Add(certsep, 0, wxEXPAND);
1385
1386 wxPanel* cbuttons = new wxPanel(certtab, -1);
1387 cbuttons->SetBackgroundColour(wxColour(255, 255, 255));
1388 certsizer->Add(cbuttons, 50, wxEXPAND, 0);
1389
1390 wxBoxSizer* cbsizer = new wxBoxSizer(wxVERTICAL);
1391 cbuttons->SetSizer(cbsizer);
1392
1393 wxPanel* cb1Panel = new wxPanel(cbuttons);
1394 cb1Panel->SetBackgroundColour(wxColour(255, 255, 255));
1395 wxBoxSizer* cb1sizer = new wxBoxSizer(wxHORIZONTAL);
1396 cb1Panel->SetSizer(cb1sizer);
1397
1398 cert_load_button = new wxBitmapButton(cb1Panel, tc_Load, importbm);
1399 cert_load_button->SetFont(f);
1400 cert_load_button->SetBitmapDisabled(delete_disbm);
1401 cb1sizer->Add(cert_load_button, 0, wxALL, 1);
1402 wxString lcl = wxT("\n");
1403 lcl += _("Load a Callsign Certificate");
1404 cert_load_label = new wxStaticText(cb1Panel, -1, lcl, wxDefaultPosition, wxSize(tw, th));
1405 cert_load_button->SetLabel(lcl);
1406 cb1sizer->Add(cert_load_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1407 cbsizer->Add(cb1Panel, 0, wxALL, 1);
1408
1409 wxPanel* cb2Panel = new wxPanel(cbuttons);
1410 cb2Panel->SetBackgroundColour(wxColour(255, 255, 255));
1411 wxBoxSizer* cb2sizer = new wxBoxSizer(wxHORIZONTAL);
1412 cb2Panel->SetSizer(cb2sizer);
1413
1414 cert_save_button = new wxBitmapButton(cb2Panel, tc_CertSave, downloadbm);
1415 cert_save_button->SetFont(f);
1416 cert_save_button->SetBitmapDisabled(download_disbm);
1417 cert_save_button->Enable(false);
1418 cb2sizer->Add(cert_save_button, 0, wxALL, 1);
1419 wxString csl = wxT("\n");
1420 csl += _("Save a Callsign Certificate");
1421 cert_save_label = new wxStaticText(cb2Panel, -1, csl, wxDefaultPosition, wxSize(tw, th));
1422 cb2sizer->Add(cert_save_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1423 cbsizer->Add(cb2Panel, 0, wxALL, 1);
1424
1425 wxPanel* cb3Panel = new wxPanel(cbuttons);
1426 cb3Panel->SetBackgroundColour(wxColour(255, 255, 255));
1427 wxBoxSizer* cb3sizer = new wxBoxSizer(wxHORIZONTAL);
1428 cb3Panel->SetSizer(cb3sizer);
1429
1430 cert_renew_button = new wxBitmapButton(cb3Panel, tc_CertRenew, uploadbm);
1431 cert_renew_button->SetFont(f);
1432 cert_renew_button->SetBitmapDisabled(upload_disbm);
1433 cert_renew_button->Enable(false);
1434 cb3sizer->Add(cert_renew_button, 0, wxALL, 1);
1435 wxString crl = wxT("\n");
1436 crl += _("Renew a Callsign Certificate");
1437 cert_renew_label = new wxStaticText(cb3Panel, -1, crl, wxDefaultPosition, wxSize(tw, th));
1438 cb3sizer->Add(cert_renew_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1439 cbsizer->Add(cb3Panel, 0, wxALL, 1);
1440
1441 wxPanel* cb4Panel = new wxPanel(cbuttons);
1442 cb4Panel->SetBackgroundColour(wxColour(255, 255, 255));
1443 wxBoxSizer* cb4sizer = new wxBoxSizer(wxHORIZONTAL);
1444 cb4Panel->SetSizer(cb4sizer);
1445
1446 cert_prop_button = new wxBitmapButton(cb4Panel, tc_CertProp, propertiesbm);
1447 cert_prop_button->SetFont(f);
1448 cert_prop_button->SetBitmapDisabled(properties_disbm);
1449 cert_prop_button->Enable(false);
1450 cb4sizer->Add(cert_prop_button, 0, wxALL, 1);
1451 wxString dcl = wxT("\n");
1452 dcl += _("Display a Callsign Certificate's Properties");
1453 cert_prop_label = new wxStaticText(cb4Panel, -1, dcl, wxDefaultPosition, wxSize(tw, th));
1454 cb4sizer->Add(cert_prop_label, 1, wxFIXED_MINSIZE | wxALL, 1);
1455 cbsizer->Add(cb4Panel, 0, wxALL, 1);
1456
1457 notebook->AddPage(certtab, _("Callsign Certificates"));
1458
1459 // Status Log tab (if enabled)
1460 if (logTab) {
1461 wxPanel* logtab = new wxPanel(notebook, -1);
1462 wxBoxSizer* ltsizer = new wxBoxSizer(wxHORIZONTAL);
1463 logtab->SetSizer(ltsizer);
1464 logwin = new wxTextCtrl(logtab, -1, wxT(""), wxDefaultPosition, wxSize(800, 600),
1465 wxTE_MULTILINE|wxTE_READONLY);
1466 ltsizer->Add(logwin, 1, wxEXPAND);
1467 notebook->AddPage(logtab, _("Status Log"));
1468 }
1469
1470 //app icon
1471 SetIcon(wxIcon(key_xpm));
1472
1473 if (checkUpdates) {
1474 LogList *log = new LogList(this);
1475 wxLog::SetActiveTarget(log);
1476 }
1477 }
1478
1479 static wxString
1480 run_station_wizard(wxWindow *parent, tQSL_Location loc, wxHtmlHelpController *help = 0,
1481 bool expired = false, wxString title = _("Add Station Location"), wxString dataname = wxT(""), wxString callsign = wxT("")) {
1482 tqslTrace("run_station_wizard", "loc=%lx, expired=%d, title=%s, dataname=%s, callsign=%s", loc, expired, S(title), S(dataname), S(callsign));
1483 wxString rval(wxT(""));
1484 get_certlist("", 0, expired, false, false);
1485 if (ncerts == 0)
1486 throw TQSLException("No certificates available");
1487 TQSLWizard *wiz = new TQSLWizard(loc, parent, help, title, expired);
1488 wiz->SetDefaultCallsign(callsign);
1489 wiz->GetPage(true);
1490 TQSLWizCertPage *cpage = reinterpret_cast<TQSLWizCertPage*>(wiz->GetPage());
1491 if (cpage && !callsign.IsEmpty())
1492 cpage->UpdateFields(0);
1493 TQSLWizPage *page = wiz->GetPage();
1494 if (page == 0)
1495 throw TQSLException("Error getting first wizard page");
1496 wiz->AdjustSize();
1497 // Note: If dynamically created pages are larger than the pages already
1498 // created (the initial page and the final page), the wizard will need to
1499 // be resized, but we don't presently have that happening. (The final
1500 // page is larger than all expected dynamic pages.)
1501 bool okay = wiz->RunWizard(page);
1502 rval = wiz->GetLocationName();
1503 wiz->Destroy();
1504 if (!okay)
1505 return rval;
1506 wxCharBuffer locname = rval.ToUTF8();
1507 check_tqsl_error(tqsl_setStationLocationCaptureName(loc, locname.data()));
1508 check_tqsl_error(tqsl_saveStationLocationCapture(loc, 1));
1509 return rval;
1510 }
1511
1512 void
1513 MyFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event)) {
1514 help->Display(wxT("main.htm"));
1515 }
1516
1517 // Return the "About" string
1518 //
1519 static wxString getAbout() {
1520 wxString msg = wxT("TQSL V") wxT(VERSION) wxT(" build ") wxT(BUILD) wxT("\n(c) 2001-2016 American Radio Relay League\n\n");
1521 int major, minor;
1522 if (tqsl_getVersion(&major, &minor))
1523 wxLogError(getLocalizedErrorString());
1524 else
1525 msg += wxString::Format(wxT("TrustedQSL library V%d.%d\n"), major, minor);
1526 if (tqsl_getConfigVersion(&major, &minor))
1527 wxLogError(getLocalizedErrorString());
1528 else
1529 msg += wxString::Format(wxT("\nConfiguration data V%d.%d\n\n"), major, minor);
1530 msg += wxVERSION_STRING;
1531 #ifdef wxUSE_UNICODE
1532 if (wxUSE_UNICODE)
1533 msg += wxT(" (Unicode)");
1534 #endif
1535 msg+=wxString::Format(wxT("\nlibcurl V%hs\n"), LIBCURL_VERSION);
1536 msg+=wxString::Format(wxT("%hs\n"), OPENSSL_VERSION_TEXT);
1537 msg+=wxString::Format(wxT("zlib V%hs\n"), ZLIB_VERSION);
1538 #ifdef USE_LMDB
1539 msg+=wxString::Format(wxT("%hs"), MDB_VERSION_STRING);
1540 #else
1541 msg+=wxString::Format(wxT("%hs"), DB_VERSION_STRING);
1542 #endif
1543 msg+=wxT("\n\n\nTranslators:\n"
1544 "German: Andreas Rehberg, DF4WC\n"
1545 "Spanish: Jordi Quintero, EA3GCV\n"
1546 "Italian: Salvatore Besso, I4FYV\n"
1547 "Japanese: Akihiro KODA, JL3OXR\n"
1548 "Finnish: Juhani Tapaninen, OH8MXL\n"
1549 "Portuguese: Nuno Lopes, CT2IRY\n"
1550 "Russian: Vic Goncharsky, US5WE\n"
1551 "Chinese: Caros, BH4TXN\n"
1552 "Hindi: Manmohan Bhagat, VU3YBH\n");
1553 return msg;
1554 }
1555
1556 void
1557 MyFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event)) {
1558 wxMessageBox(getAbout(), _("About"), wxOK | wxCENTRE | wxICON_INFORMATION, this);
1559 }
1560
1561 void
1562 MyFrame::OnHelpDiagnose(wxCommandEvent& event) {
1563 wxString s_fname;
1564
1565 if (tqsl_diagFileOpen()) {
1566 file_menu->Check(tm_f_diag, false);
1567 tqsl_closeDiagFile();
1568 wxMessageBox(wxT("Diagnostic log closed"), wxT("Diagnostics"), wxOK | wxCENTRE| wxICON_INFORMATION, this);
1569 return;
1570 }
1571 s_fname = wxFileSelector(_("Log File"), wxT(""), wxT("tqsldiag.log"), wxT("log"),
1572 _("Log files (*.log)|*.log|All files (*.*)|*.*"),
1573 wxFD_SAVE|wxFD_OVERWRITE_PROMPT, this);
1574 if (s_fname == wxT("")) {
1575 file_menu->Check(tm_f_diag, false); //would be better to not check at all, but no, apparently that's a crazy thing to want
1576 return;
1577 }
1578 if (tqsl_openDiagFile(s_fname.ToUTF8())) {
1579 wxString errmsg = wxString::Format(_("Error opening diagnostic log %s: %hs"), s_fname.c_str(), strerror(errno));
1580 wxMessageBox(errmsg, _("Log File Error"), wxOK | wxICON_EXCLAMATION);
1581 return;
1582 }
1583 file_menu->Check(tm_f_diag, true);
1584 wxString about = getAbout();
1585 #ifdef _WIN32
1586 about.Replace(wxT("\\n"), wxT("\\r\\n"));
1587 #endif
1588 tqslTrace(NULL, "TQSL Diagnostics\r\n%s\r\n\r\n", (const char *)about.ToUTF8());
1589 tqslTrace(NULL, "Command Line: %s\r\n", (const char *)origCommandLine.ToUTF8());
1590 tqslTrace(NULL, "Working Directory:%s\r\n", tQSL_BaseDir);
1591 }
1592
1593 class FileUploadHandler {
1594 public:
1595 string s;
1596 FileUploadHandler(): s() { s.reserve(2000); }
1597
1598 size_t internal_recv(char *ptr, size_t size, size_t nmemb) {
1599 s.append(ptr, size*nmemb);
1600 return size*nmemb;
1601 }
1602
1603 static size_t recv(char *ptr, size_t size, size_t nmemb, void *userdata) {
1604 return (reinterpret_cast<FileUploadHandler*>(userdata))->internal_recv(ptr, size, nmemb);
1605 }
1606 };
1607
1608 static void
1609 AddEditStationLocation(tQSL_Location loc, bool expired = false, const wxString& title = _("Add Station Location"), const wxString& callsign = wxT("")) {
1610 tqslTrace("AddEditStationLocation", "loc=%lx, expired=%lx, title=%s, callsign=%s", loc, expired, S(title), S(callsign));
1611 try {
1612 run_station_wizard(frame, loc, frame->help, expired, title, wxT(""), callsign);
1613 frame->loc_tree->Build();
1614 }
1615 catch(TQSLException& x) {
1616 wxLogError(wxT("%hs"), x.what());
1617 }
1618 }
1619
1620 void
1621 MyFrame::AddStationLocation(wxCommandEvent& WXUNUSED(event)) {
1622 tqslTrace("MyFrame::AddStationLocation", NULL);
1623 wxTreeItemId root = loc_tree->GetRootItem();
1624 wxTreeItemId id = loc_tree->GetSelection();
1625 wxString call;
1626 if (id != root && id.IsOk()) { // If something selected
1627 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(id));
1628 if (data) {
1629 call = data->getCallSign(); // Station location
1630 } else {
1631 call = loc_tree->GetItemText(id); // Callsign folder selected
1632 }
1633 tqslTrace("MyFrame::AddStationLocation", "Call selected is %s", S(call));
1634 }
1635 tQSL_Location loc;
1636 if (tqsl_initStationLocationCapture(&loc)) {
1637 wxLogError(getLocalizedErrorString());
1638 }
1639 AddEditStationLocation(loc, false, _("Add Station Location"), call);
1640 if (tqsl_endStationLocationCapture(&loc)) {
1641 wxLogError(getLocalizedErrorString());
1642 }
1643 loc_tree->Build();
1644 LocTreeReset();
1645 }
1646
1647 void
1648 MyFrame::EditStationLocation(wxCommandEvent& event) {
1649 tqslTrace("MyFrame::EditStationLocation", NULL);
1650 if (event.GetId() == tl_EditLoc) {
1651 try {
1652 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(loc_tree->GetSelection()));
1653 tQSL_Location loc;
1654 wxString selname;
1655 char errbuf[512];
1656
1657 if (data == NULL) return;
1658
1659 check_tqsl_error(tqsl_getStationLocation(&loc, data->getLocname().ToUTF8()));
1660 if (!verify_cert(loc, true)) // Check if there is a certificate before editing
1661 return;
1662 check_tqsl_error(tqsl_getStationLocationErrors(loc, errbuf, sizeof(errbuf)));
1663 if (strlen(errbuf) > 0) {
1664 wxString fmt = wxT("%hs\n");
1665 // TRANSLATORS: uncommon error - error in a station location, followed by the ignore message that follows.
1666 fmt += _("The invalid data was ignored.");
1667 wxMessageBox(wxString::Format(fmt, errbuf), _("Location data error"), wxOK | wxICON_EXCLAMATION, this);
1668 }
1669 char loccall[512];
1670 check_tqsl_error(tqsl_getLocationCallSign(loc, loccall, sizeof loccall));
1671 selname = run_station_wizard(this, loc, help, true, wxString::Format(_("Edit Station Location : %hs - %s"), loccall, data->getLocname().c_str()), data->getLocname());
1672 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
1673 loc_tree->Build();
1674 LocTreeReset();
1675 return;
1676 }
1677 catch(TQSLException& x) {
1678 wxLogError(wxT("%hs"), x.what());
1679 return;
1680 }
1681 }
1682 // How many locations are there?
1683 try {
1684 int n;
1685 tQSL_Location loc;
1686 check_tqsl_error(tqsl_initStationLocationCapture(&loc));
1687 check_tqsl_error(tqsl_getNumStationLocations(loc, &n));
1688 if (n == 1) {
1689 // There's only one station location. Use that and don't prompt.
1690 char deflocn[512];
1691 check_tqsl_error(tqsl_getStationLocationName(loc, 0, deflocn, sizeof deflocn));
1692 wxString locname = wxString::FromUTF8(deflocn);
1693 tqsl_endStationLocationCapture(&loc);
1694 check_tqsl_error(tqsl_getStationLocation(&loc, deflocn));
1695 char loccall[512];
1696 check_tqsl_error(tqsl_getLocationCallSign(loc, loccall, sizeof loccall));
1697 run_station_wizard(this, loc, help, true, wxString::Format(_("Edit Station Location : %hs - %s"), loccall, locname.c_str()), locname);
1698 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
1699 loc_tree->Build();
1700 LocTreeReset();
1701 return;
1702 }
1703 // More than one location or not selected in the tree. Prompt for the location.
1704 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
1705 SelectStationLocation(_("Edit Station Location"), _("Close"), true);
1706 loc_tree->Build();
1707 LocTreeReset();
1708 }
1709 catch(TQSLException& x) {
1710 wxLogError(wxT("%hs"), x.what());
1711 return;
1712 }
1713 }
1714
1715 static tqsl_adifFieldDefinitions fielddefs[] = {
1716 { "CALL", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_CALLSIGN_MAX, 0, 0, 0, 0 },
1717 { "BAND", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_BAND_MAX, 0, 0, 0, 0 },
1718 { "BAND_RX", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_BAND_MAX, 0, 0, 0, 0 },
1719 { "MODE", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_MODE_MAX, 0, 0, 0, 0 },
1720 { "FREQ", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_FREQ_MAX, 0, 0, 0, 0 },
1721 { "FREQ_RX", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_FREQ_MAX, 0, 0, 0, 0 },
1722 { "QSO_DATE", "", TQSL_ADIF_RANGE_TYPE_NONE, 8, 0, 0, 0, 0 },
1723 { "TIME_ON", "", TQSL_ADIF_RANGE_TYPE_NONE, 6, 0, 0, 0, 0 },
1724 { "SAT_NAME", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_SATNAME_MAX, 0, 0, 0, 0 },
1725 { "PROP_MODE", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_PROPMODE_MAX, 0, 0, 0, 0 },
1726 { "EOR", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, 0, 0 },
1727 { "", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, 0, 0 },
1728 };
1729
1730 static const char *defined_types[] = { "T", "D", "M", "C", "N", "6" };
1731
1732 static unsigned char *
1733 adif_alloc(size_t n) {
1734 return new unsigned char[n];
1735 }
1736
1737 static void
1738 loadQSOfile(wxString& file, QSORecordList& recs) {
1739 init_modes();
1740 tqsl_adifFieldResults field;
1741 TQSL_ADIF_GET_FIELD_ERROR stat;
1742 tQSL_ADIF adif;
1743 if (tqsl_beginADIF(&adif, file.ToUTF8())) {
1744 wxLogError(getLocalizedErrorString());
1745 }
1746 QSORecord rec;
1747 do {
1748 if (tqsl_getADIFField(adif, &field, &stat, fielddefs, defined_types, adif_alloc)) {
1749 wxLogError(getLocalizedErrorString());
1750 }
1751 if (stat == TQSL_ADIF_GET_FIELD_SUCCESS) {
1752 if (!strcasecmp(field.name, "CALL")) {
1753 rec._call = wxString::FromUTF8((const char *)field.data);
1754 } else if (!strcasecmp(field.name, "BAND")) {
1755 rec._band = wxString::FromUTF8((const char *)field.data);
1756 } else if (!strcasecmp(field.name, "BAND_RX")) {
1757 rec._rxband = wxString::FromUTF8((const char *)field.data);
1758 } else if (!strcasecmp(field.name, "MODE")) {
1759 rec._mode = wxString::FromUTF8((const char *)field.data);
1760 char amode[40];
1761 if (tqsl_getADIFMode(rec._mode.ToUTF8(), amode, sizeof amode) == 0 && amode[0] != '\0')
1762 rec._mode = wxString::FromUTF8(amode);
1763 } else if (!strcasecmp(field.name, "FREQ")) {
1764 rec._freq = wxString::FromUTF8((const char *)field.data);
1765 } else if (!strcasecmp(field.name, "FREQ_RX")) {
1766 rec._rxfreq = wxString::FromUTF8((const char *)field.data);
1767 } else if (!strcasecmp(field.name, "PROP_MODE")) {
1768 rec._propmode = wxString::FromUTF8((const char *)field.data);
1769 } else if (!strcasecmp(field.name, "SAT_NAME")) {
1770 rec._satellite = wxString::FromUTF8((const char *)field.data);
1771 } else if (!strcasecmp(field.name, "QSO_DATE")) {
1772 char *cp = reinterpret_cast<char *>(field.data);
1773 if (strlen(cp) == 8) {
1774 rec._date.day = strtol(cp+6, NULL, 10);
1775 *(cp+6) = '\0';
1776 rec._date.month = strtol(cp+4, NULL, 10);
1777 *(cp+4) = '\0';
1778 rec._date.year = strtol(cp, NULL, 10);
1779 }
1780 } else if (!strcasecmp(field.name, "TIME_ON")) {
1781 char *cp = reinterpret_cast<char *>(field.data);
1782 if (strlen(cp) >= 4) {
1783 rec._time.second = (strlen(cp) > 4) ? strtol(cp+4, NULL, 10) : 0;
1784 *(cp+4) = 0;
1785 rec._time.minute = strtol(cp+2, NULL, 10);
1786 *(cp+2) = '\0';
1787 rec._time.hour = strtol(cp, NULL, 10);
1788 }
1789 } else if (!strcasecmp(field.name, "EOR")) {
1790 recs.push_back(rec);
1791 rec = QSORecord();
1792 }
1793 delete[] field.data;
1794 }
1795 } while (stat == TQSL_ADIF_GET_FIELD_SUCCESS || stat == TQSL_ADIF_GET_FIELD_NO_NAME_MATCH);
1796 tqsl_endADIF(&adif);
1797 return;
1798 }
1799
1800 void
1801 MyFrame::EditQSOData(wxCommandEvent& WXUNUSED(event)) {
1802 tqslTrace("MyFrame::EditQSOData", NULL);
1803 QSORecordList recs;
1804 wxString file = wxFileSelector(_("Open File"), wxConfig::Get()->Read(wxT("QSODataPath"), wxT("")), wxT(""), wxT("adi"),
1805 #if !defined(__APPLE__) && !defined(_WIN32)
1806 _("ADIF files (*.adi;*.adif;*.ADI;*.ADIF)|*.adi;*.adif;*.ADI;*.ADIF|All files (*.*)|*.*"),
1807 #else
1808 _("ADIF files (*.adi;*.adif)|*.adi;*.adif|All files (*.*)|*.*"),
1809 #endif
1810 wxFD_OPEN|wxFD_FILE_MUST_EXIST, this);
1811 if (file == wxT(""))
1812 return;
1813 loadQSOfile(file, recs);
1814 wxMessageBox(_("Warning: The TQSL ADIF editor only processes a limited number of ADIF fields.\n\nUsing the editor on an ADIF file can cause QSO details to be lost!"), _("Warning"), wxOK | wxICON_EXCLAMATION, frame);
1815 try {
1816 QSODataDialog dial(this, file, help, &recs);
1817 dial.ShowModal();
1818 } catch(TQSLException& x) {
1819 wxLogError(wxT("%hs"), x.what());
1820 }
1821 }
1822
1823 void
1824 MyFrame::EnterQSOData(wxCommandEvent& WXUNUSED(event)) {
1825 tqslTrace("MyFrame::EnterQSOData", NULL);
1826 QSORecordList recs;
1827 wxString file = wxT("tqsl.adi");
1828 try {
1829 QSODataDialog dial(this, file, help, &recs);
1830 dial.ShowModal();
1831 } catch(TQSLException& x) {
1832 wxLogError(wxT("%hs"), x.what());
1833 }
1834 }
1835
1836 int MyFrame::ConvertLogToString(tQSL_Location loc, const wxString& infile, wxString& output, int& n, bool suppressdate, tQSL_Date* startdate, tQSL_Date* enddate, int action, const char* password, const char* defcall) {
1837 tqslTrace("MyFrame::ConvertLogToString", "loc = %lx, infile=%s, suppressdate=%d, startdate=0x%lx, enddate=0x%lx, action=%d, defcall=%s", reinterpret_cast<void *>(loc), S(infile), suppressdate, reinterpret_cast<void *>(startdate), reinterpret_cast<void *>(enddate), action, defcall ? defcall : "");
1838 static const char *iam = "TQSL V" VERSION;
1839 const char *cp;
1840 char callsign[40];
1841 int dxcc = 0;
1842 wxString name, ext;
1843 bool allow_dupes = false;
1844 bool restarting = false;
1845 bool ignore_err = false;
1846 bool show_dupes = false;
1847
1848 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
1849 config->Read(wxT("DispDupes"), &show_dupes, DEFAULT_DISP_DUPES);
1850
1851 try {
1852 if (defcall) {
1853 strncpy(callsign, defcall, sizeof callsign);
1854 check_tqsl_error(tqsl_setLocationCallSign(loc, callsign));
1855 } else {
1856 check_tqsl_error(tqsl_getLocationCallSign(loc, callsign, sizeof callsign));
1857 }
1858 } catch(TQSLException &x) {
1859 wxLogError(wxT("%hs"), x.what());
1860 return TQSL_EXIT_LIB_ERROR;
1861 }
1862 tqsl_getLocationDXCCEntity(loc, &dxcc);
1863 DXCC dx;
1864 dx.getByEntity(dxcc);
1865
1866 get_certlist(callsign, dxcc, false, false, false);
1867 if (ncerts == 0) {
1868 // No certificates for this callsign/dxcc pair.
1869 // If the callsign for this location is set to "[None]", go look
1870 // for a suitable callsign certificate
1871 if (strcmp(callsign, "[None]") == 0) {
1872 // Get all of the certificates for this location's DXCC entity
1873 get_certlist("", dxcc, false, false, false);
1874 // If only one callsign matches the dxcc entity, just use that
1875 if (ncerts == 1) {
1876 tqsl_getCertificateCallSign(certlist[0], callsign, sizeof callsign);
1877 tqsl_setLocationCallSign(loc, callsign);
1878 } else if (ncerts > 1) {
1879 wxString *choices = new wxString[ncerts];
1880 // Get today's date
1881 time_t t = time(0);
1882 struct tm *tm = gmtime(&t);
1883 tQSL_Date d;
1884 d.year = tm->tm_year + 1900;
1885 d.month = tm->tm_mon + 1;
1886 d.day = tm->tm_mday;
1887 tQSL_Date exp;
1888 int def_index = 0;
1889 int oldest = -1;
1890 for (int i = 0; i < ncerts; i++) {
1891 tqsl_getCertificateCallSign(certlist[i], callsign, sizeof callsign);
1892 choices[i] = wxString::FromUTF8(callsign);
1893 if (0 == tqsl_getCertificateNotAfterDate(certlist[i], &exp)) {
1894 int days_left;
1895 tqsl_subtractDates(&d, &exp, &days_left);
1896 if (days_left > oldest) {
1897 def_index = i;
1898 oldest = days_left;
1899 }
1900 }
1901 }
1902 wxSingleChoiceDialog dialog(this, _("Please choose a callsign for this Station Location"),
1903 _("Select Callsign"), ncerts, choices);
1904 dialog.SetSelection(def_index);
1905 int idx;
1906 if (dialog.ShowModal() == wxID_OK)
1907 idx = dialog.GetSelection();
1908 else
1909 return TQSL_EXIT_CANCEL;
1910 tqsl_getCertificateCallSign(certlist[idx], callsign, sizeof callsign);
1911 get_certlist(callsign, dxcc, false, false, false);
1912 tqsl_setLocationCallSign(loc, callsign);
1913 }
1914 // TRANSLATORS: filename being signed, station location and callsign added later.
1915 wxString fmt = _("The file (%s) will be signed using:");
1916 fmt += wxT("\n");
1917 fmt += _("Station Location:");
1918 fmt += wxT(" %hs\n");
1919 fmt += _("Call sign:");
1920 fmt += wxT(" %hs\n");
1921 fmt += _("DXCC:");
1922 fmt += wxT(" %hs\n");
1923 fmt += _("Is this correct?");
1924 char loc_name[128];
1925 if (tqsl_getStationLocationCaptureName(loc, loc_name, sizeof loc_name)) {
1926 strncpy(loc_name, "Unknown", sizeof loc_name);
1927 }
1928 if (wxMessageBox(wxString::Format(fmt, infile.c_str(), loc_name,
1929 callsign, dx.name()), _("TQSL - Confirm signing"), wxYES_NO | wxICON_QUESTION, this) != wxYES) {
1930 return TQSL_EXIT_CANCEL;
1931 }
1932 }
1933 } else {
1934 // The defcall or callsign from the location has matching certificates
1935 // for this DXCC location. If we used the default call, set that
1936 // for the location
1937 if (defcall) {
1938 tqsl_setLocationCallSign(loc, defcall);
1939 }
1940 }
1941 if (ncerts == 0) {
1942 wxString msg;
1943 wxString fmt = _("There are no valid callsign certificates for callsign");
1944 if (dxcc != 0) {
1945 fmt += wxT(" %hs ");
1946 fmt += _("in entity");
1947 fmt += wxT(" %hs.\n");
1948 fmt += _("Signing aborted.");
1949 fmt + wxT("\n");
1950 msg = wxString::Format(fmt, callsign, dx.name());
1951 } else {
1952 fmt += wxT(" %hs.\n");
1953 fmt += _("Signing aborted.");
1954 fmt + wxT("\n");
1955 msg = wxString::Format(fmt, callsign);
1956 }
1957 wxLogError(msg);
1958 return TQSL_EXIT_TQSL_ERROR;
1959 }
1960
1961 wxString msg = _("Signing using Callsign %hs, DXCC Entity %hs");
1962 msg += wxT("\n");
1963 wxLogMessage(msg, callsign, dx.name());
1964
1965 init_modes();
1966 init_contests();
1967
1968 if (lock_db(false) < 0) {
1969 if (quiet) { // If the database is locked, don't stall if in batch mode.
1970 return TQSL_EXIT_BUSY;
1971 }
1972 wxSafeYield();
1973 wxLogMessage(_("TQSL must wait for other running copies of TQSL to exit before signing..."));
1974 wxSafeYield();
1975 lock_db(true);
1976 }
1977
1978 restart:
1979
1980 ConvertingDialog *conv_dial = new ConvertingDialog(this, infile.ToUTF8());
1981 n = 0;
1982 bool cancelled = false;
1983 bool aborted = false;
1984 int lineno = 0;
1985 int out_of_range = 0;
1986 int duplicates = 0;
1987 int processed = 0;
1988 int errors = 0;
1989 try {
1990 if (tqsl_beginCabrilloConverter(&logConv, infile.ToUTF8(), certlist, ncerts, loc)) {
1991 if (tQSL_Error != TQSL_CABRILLO_ERROR || tQSL_Cabrillo_Error != TQSL_CABRILLO_NO_START_RECORD)
1992 check_tqsl_error(1); // A bad error
1993 lineno = 0;
1994 check_tqsl_error(tqsl_beginADIFConverter(&logConv, infile.ToUTF8(), certlist, ncerts, loc));
1995 }
1996 bool range = true;
1997 config->Read(wxT("DateRange"), &range);
1998 if (range && !suppressdate && !restarting) {
1999 DateRangeDialog dial(this);
2000 if (dial.ShowModal() != wxOK) {
2001 wxLogMessage(_("Cancelled"));
2002 unlock_db();
2003 return TQSL_EXIT_CANCEL;
2004 }
2005 tqsl_setADIFConverterDateFilter(logConv, &dial.start, &dial.end);
2006 if (this->IsQuiet()) {
2007 this->Show(false);
2008 wxSafeYield(this);
2009 }
2010 }
2011 if (startdate || enddate) {
2012 tqslTrace("MyFrame::ConvertLogToString", "startdate %d/%d/%d, enddate %d/%d/%d",
2013 startdate ? startdate->year : 0,
2014 startdate ? startdate->month : 0,
2015 startdate ? startdate->day : 0,
2016 enddate ? enddate->year : 0,
2017 enddate ? enddate->month : 0,
2018 enddate ? enddate->day : 0);
2019 tqsl_setADIFConverterDateFilter(logConv, startdate, enddate);
2020 }
2021 bool allow = false;
2022 config->Read(wxT("BadCalls"), &allow);
2023 tqsl_setConverterAllowBadCall(logConv, allow);
2024 tqsl_setConverterAllowDuplicates(logConv, allow_dupes);
2025 tqsl_setConverterAppName(logConv, iam);
2026
2027 wxFileName::SplitPath(infile, 0, &name, &ext);
2028 if (ext != wxT(""))
2029 name += wxT(".") + ext;
2030 // Only display windows if notin batch mode -- KD6PAG
2031 if (!this->IsQuiet()) {
2032 conv_dial->Show(TRUE);
2033 }
2034 this->Enable(FALSE);
2035
2036 output = wxT("");
2037
2038 do {
2039 while ((cp = tqsl_getConverterGABBI(logConv)) != 0) {
2040 if (!this->IsQuiet())
2041 wxSafeYield(conv_dial);
2042 if (!conv_dial->running)
2043 break;
2044 // Only count QSO records
2045 if (strstr(cp, "tCONTACT")) {
2046 ++n;
2047 ++processed;
2048 }
2049 if ((processed % 10) == 0) {
2050 wxString progress = wxString::Format(_("QSOs: %d"), processed);
2051 if (duplicates > 0)
2052 progress += wxT(" ") + wxString::Format(_("Duplicates: %d"), duplicates);
2053 if (errors > 0 || out_of_range > 0)
2054 progress += wxT(" ") + wxString::Format(_("Errors: %d"), errors + out_of_range);
2055 conv_dial->msg->SetLabel(progress);
2056 }
2057 output << (wxString::FromUTF8(cp) + wxT("\n"));
2058 }
2059 if ((processed % 10) == 0) {
2060 wxString progress = wxString::Format(_("QSOs: %d"), processed);
2061 if (duplicates > 0)
2062 progress += wxT(" ") + wxString::Format(_("Duplicates: %d"), duplicates);
2063 if (errors > 0 || out_of_range > 0)
2064 progress += wxT(" ") + wxString::Format(_("Errors: %d"), errors + out_of_range);
2065 conv_dial->msg->SetLabel(progress);
2066 }
2067 if (cp == 0) {
2068 if (!this->IsQuiet())
2069 wxSafeYield(conv_dial);
2070 if (!conv_dial->running)
2071 break;
2072 }
2073 if (tQSL_Error == TQSL_SIGNINIT_ERROR) {
2074 tQSL_Cert cert;
2075 int rval;
2076 check_tqsl_error(tqsl_getConverterCert(logConv, &cert));
2077 do {
2078 if ((rval = tqsl_beginSigning(cert, const_cast<char *>(password), getCertPassword, cert)) == 0)
2079 break;
2080 if (tQSL_Error == TQSL_PASSWORD_ERROR) {
2081 if ((rval = tqsl_beginSigning(cert, const_cast<char *>(unipwd), NULL, cert)) == 0)
2082 break;
2083 wxLogMessage(_("Password error"));
2084 if (password)
2085 free(reinterpret_cast<void *>(const_cast<char *>(password)));
2086 password = NULL;
2087 }
2088 } while (tQSL_Error == TQSL_PASSWORD_ERROR);
2089 if (tQSL_Error == TQSL_CUSTOM_ERROR && (tQSL_Errno == ENOENT || tQSL_Errno == EPERM)) {
2090 snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
2091 "Can't open the private key file for %s: %s", callsign, strerror(tQSL_Errno));
2092 }
2093 check_tqsl_error(rval);
2094 if (this->IsQuiet()) {
2095 this->Show(false);
2096 wxSafeYield(this);
2097 }
2098 continue;
2099 }
2100 if (tQSL_Error == TQSL_DATE_OUT_OF_RANGE) {
2101 processed++;
2102 out_of_range++;
2103 continue;
2104 }
2105 char dupeErrString[256];
2106 dupeErrString[0] = '\0';
2107 bool dupe_error = (tQSL_Error == TQSL_DUPLICATE_QSO);
2108 if (dupe_error) {
2109 strncpy(dupeErrString, tQSL_CustomError, sizeof dupeErrString);
2110 duplicates++;
2111 if (!show_dupes) {
2112 processed++;
2113 continue;
2114 }
2115 }
2116 bool has_error = (tQSL_Error != TQSL_NO_ERROR);
2117 if (has_error) {
2118 processed++;
2119 if (!dupe_error) {
2120 errors++;
2121 }
2122 try {
2123 check_tqsl_error(1);
2124 } catch(TQSLException& x) {
2125 tqsl_getConverterLine(logConv, &lineno);
2126 wxString msg = wxGetTranslation(wxString::FromUTF8(x.what()));
2127 if (lineno)
2128 msg += wxT(" ") + wxString::Format(_("on line %d"), lineno);
2129 const char *bad_text = tqsl_getConverterRecordText(logConv);
2130 if (bad_text)
2131 msg += wxString(wxT("\n")) + wxString::FromUTF8(bad_text);
2132
2133 if (dupeErrString[0] != '\0') {
2134 wxStringTokenizer dupes(wxString::FromUTF8(dupeErrString), wxT("|"));
2135 wxString olddupe = dupes.GetNextToken();
2136 wxString newdupe = dupes.GetNextToken();
2137 if (olddupe != newdupe) {
2138 msg += wxT("\n") + wxString::Format(_("Your QTH Details changed for this QSO.\n\nOriginally these were: %s\nNow they are:%s\n\nPlease verify that you intended to change this QSO!\n"), olddupe.c_str(), newdupe.c_str());
2139 }
2140 }
2141 wxLogError(wxT("%s"), msg.c_str());
2142 if (frame->IsQuiet()) {
2143 switch (action) {
2144 case TQSL_ACTION_ABORT:
2145 aborted = true;
2146 ignore_err = true;
2147 goto abortSigning;
2148 case TQSL_ACTION_NEW: // For ALL or NEW, let the signing proceed
2149 case TQSL_ACTION_ALL:
2150 ignore_err = true;
2151 break;
2152 case TQSL_ACTION_ASK:
2153 case TQSL_ACTION_UNSPEC:
2154 break; // The error will show as a popup
2155 }
2156 }
2157 if (!ignore_err) {
2158 tqslTrace("MyFrame::ConvertLogToString", "Error: %s/asking for action", S(msg));
2159 wxString errmsg = msg + wxT("\n");
2160 errmsg += _("Click 'Ignore' to continue signing this log while ignoring errors.");
2161 errmsg += wxT("\n");
2162 errmsg += _("Click 'Cancel' to abandon signing this log.");
2163 ErrorsDialog dial(this, errmsg);
2164 int choice = dial.ShowModal();
2165 if (choice == TQSL_AE_CAN) {
2166 cancelled = true;
2167 goto abortSigning;
2168 }
2169 if (choice == TQSL_AE_OK) {
2170 ignore_err = true;
2171 if (this->IsQuiet()) {
2172 this->Show(false);
2173 wxSafeYield(this);
2174 }
2175 }
2176 }
2177 }
2178 }
2179 tqsl_getErrorString(); // Clear error
2180 if (has_error && ignore_err)
2181 continue;
2182 break;
2183 } while (1);
2184 cancelled = !conv_dial->running;
2185
2186 abortSigning:
2187 this->Enable(TRUE);
2188
2189 if (cancelled) {
2190 wxLogWarning(_("Signing cancelled"));
2191 n = 0;
2192 } else if (aborted) {
2193 wxLogWarning(_("Signing aborted"));
2194 n = 0;
2195 } else if (tQSL_Error != TQSL_NO_ERROR) {
2196 check_tqsl_error(1);
2197 }
2198 delete conv_dial;
2199 } catch(TQSLException& x) {
2200 this->Enable(TRUE);
2201 delete conv_dial;
2202 string msg = x.what();
2203 tqsl_getConverterLine(logConv, &lineno);
2204 tqsl_converterRollBack(logConv);
2205 tqsl_endConverter(&logConv);
2206 unlock_db();
2207 if (lineno) {
2208 msg += " ";
2209 msg += wxString::Format(_("on line %d"), lineno).ToUTF8();
2210 }
2211
2212 wxLogError(_("Signing aborted due to errors"));
2213 throw TQSLException(msg.c_str());
2214 }
2215 if (!cancelled && out_of_range > 0)
2216 wxLogMessage(_("%s: %d QSO records were outside the selected date range"),
2217 infile.c_str(), out_of_range);
2218 if (duplicates > 0) {
2219 if (cancelled) {
2220 tqsl_converterRollBack(logConv);
2221 tqsl_endConverter(&logConv);
2222 unlock_db();
2223 return TQSL_EXIT_CANCEL;
2224 }
2225 if (action == TQSL_ACTION_ASK || action == TQSL_ACTION_UNSPEC) { // want to ask the user
2226 DupesDialog dial(this, processed - errors - out_of_range, duplicates, action);
2227 int choice = dial.ShowModal();
2228 if (choice == TQSL_DP_CAN) {
2229 wxLogMessage(_("Cancelled"));
2230 tqsl_converterRollBack(logConv);
2231 tqsl_endConverter(&logConv);
2232 unlock_db();
2233 return TQSL_EXIT_CANCEL;
2234 }
2235 if (choice == TQSL_DP_ALLOW) {
2236 allow_dupes = true;
2237 tqsl_converterRollBack(logConv);
2238 tqsl_endConverter(&logConv);
2239 restarting = true;
2240 if (this->IsQuiet()) {
2241 this->Show(false);
2242 wxSafeYield(this);
2243 }
2244 goto restart;
2245 }
2246 } else if (action == TQSL_ACTION_ABORT) {
2247 if (processed == duplicates) {
2248 wxLogMessage(_("All QSOs are duplicates; aborted"));
2249 tqsl_converterRollBack(logConv);
2250 tqsl_endConverter(&logConv);
2251 unlock_db();
2252 n = 0;
2253 return TQSL_EXIT_NO_QSOS;
2254 } else {
2255 wxLogMessage(_("%d of %d QSOs are duplicates; aborted"), duplicates, processed);
2256 tqsl_converterRollBack(logConv);
2257 tqsl_endConverter(&logConv);
2258 unlock_db();
2259 n = 0;
2260 return TQSL_EXIT_NO_QSOS;
2261 }
2262 } else if (action == TQSL_ACTION_ALL) {
2263 allow_dupes = true;
2264 tqsl_converterRollBack(logConv);
2265 tqsl_endConverter(&logConv);
2266 restarting = true;
2267 goto restart;
2268 }
2269 // Otherwise it must be TQSL_ACTION_NEW, so fall through
2270 // and output the new records.
2271 wxLogMessage(_("%s: %d QSO records were duplicates"),
2272 infile.c_str(), duplicates);
2273 }
2274 //if (!cancelled) tqsl_converterCommit(logConv);
2275 if (cancelled || processed == 0) {
2276 tqsl_converterRollBack(logConv);
2277 tqsl_endConverter(&logConv);
2278 }
2279 unlock_db();
2280 if (cancelled)
2281 return TQSL_EXIT_CANCEL;
2282 if (processed == 0)
2283 return TQSL_EXIT_NO_QSOS;
2284 if (aborted || duplicates > 0 || out_of_range > 0 || errors > 0)
2285 return TQSL_EXIT_QSOS_SUPPRESSED;
2286 return TQSL_EXIT_SUCCESS;
2287 }
2288
2289 // Errors from tqsllib that indicate bad QSO data.
2290 // Add here so they're picked up for translation.
2291 #ifdef tqsltranslate
2292 static const char* qsoerrs[] = {
2293 __("Invalid contact - QSO does not specify a Callsign"),
2294 __("Invalid contact - QSO does not specify a band or frequency"),
2295 __("Invalid contact - QSO does not specify a mode"),
2296 __("Invalid contact - QSO does not specify a date"),
2297 __("Invalid contact - QSO does not specify a time"),
2298 // Piggy-back this error as well
2299 __("This callsign certificate is already active and cannot be restored.")
2300 }
2301 #endif
2302
2303 int
2304 MyFrame::ConvertLogFile(tQSL_Location loc, const wxString& infile, const wxString& outfile,
2305 bool compressed, bool suppressdate, tQSL_Date* startdate, tQSL_Date* enddate, int action, const char *password, const char *defcall) {
2306 tqslTrace("MyFrame::ConvertLogFile", "loc=%lx, infile=%s, outfile=%s, compressed=%d, suppressdate=%d, startdate=0x%lx enddate=0x%lx action=%d", reinterpret_cast<void *>(loc), S(infile), S(outfile), compressed, suppressdate, reinterpret_cast<void *>(startdate), reinterpret_cast<void*>(enddate), action);
2307 gzFile gout = 0;
2308 #ifdef _WIN32
2309 int fd = -1;
2310 #endif
2311 ofstream out;
2312
2313 if (compressed) {
2314 #ifdef _WIN32
2315 wchar_t* lfn = utf8_to_wchar(outfile.ToUTF8());
2316 fd = _wopen(lfn, _O_WRONLY |_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
2317 free_wchar(lfn);
2318 if (fd != -1)
2319 gout = gzdopen(fd, "wb9");
2320 #else
2321 gout = gzopen(outfile.ToUTF8(), "wb9");
2322 #endif
2323 } else {
2324 #ifdef _WIN32
2325 wchar_t* lfn = utf8_to_wchar(outfile.ToUTF8());
2326 out.open(lfn, ios::out|ios::trunc|ios::binary);
2327 free_wchar(lfn);
2328 #else
2329 out.open(outfile.ToUTF8(), ios::out|ios::trunc|ios::binary);
2330 #endif
2331 }
2332
2333 if ((compressed && !gout) || (!compressed && !out)) {
2334 wxLogError(_("Unable to open %s for output"), outfile.c_str());
2335 return TQSL_EXIT_ERR_OPEN_OUTPUT;
2336 }
2337
2338 wxString output;
2339 int numrecs = 0;
2340 int status = this->ConvertLogToString(loc, infile, output, numrecs, suppressdate, startdate, enddate, action, password, defcall);
2341
2342 if (numrecs == 0) {
2343 wxLogMessage(_("No records output"));
2344 if (compressed) {
2345 gzclose(gout);
2346 } else {
2347 out.close();
2348 }
2349 #ifdef _WIN32
2350 wchar_t* lfn = utf8_to_wchar(outfile.ToUTF8());
2351 _wunlink(lfn);
2352 free_wchar(lfn);
2353 #else
2354 unlink(outfile.ToUTF8());
2355 #endif
2356 if (status == TQSL_EXIT_CANCEL || TQSL_EXIT_QSOS_SUPPRESSED)
2357 return status;
2358 else
2359 return TQSL_EXIT_NO_QSOS;
2360 } else {
2361 if(compressed) {
2362 if (gzwrite(gout, output.ToUTF8(), output.size()) <= 0) {
2363 tqsl_converterRollBack(logConv);
2364 tqsl_endConverter(&logConv);
2365 gzclose(gout);
2366 return TQSL_EXIT_LIB_ERROR;
2367 }
2368 if (gzflush(gout, Z_FINISH) != Z_OK) {
2369 tqsl_converterRollBack(logConv);
2370 tqsl_endConverter(&logConv);
2371 gzclose(gout);
2372 return TQSL_EXIT_LIB_ERROR;
2373 }
2374 if (gzclose(gout) != Z_OK) {
2375 tqsl_converterRollBack(logConv);
2376 tqsl_endConverter(&logConv);
2377 return TQSL_EXIT_LIB_ERROR;
2378 }
2379 } else {
2380 out << output;
2381 if (out.fail()) {
2382 tqsl_converterRollBack(logConv);
2383 tqsl_endConverter(&logConv);
2384 out.close();
2385 return TQSL_EXIT_LIB_ERROR;
2386 }
2387 out.close();
2388 if (out.fail()) {
2389 tqsl_converterRollBack(logConv);
2390 tqsl_endConverter(&logConv);
2391 return TQSL_EXIT_LIB_ERROR;
2392 }
2393 }
2394
2395 tqsl_converterCommit(logConv);
2396 tqsl_endConverter(&logConv);
2397
2398 wxLogMessage(_("%s: wrote %d records to %s"), infile.c_str(), numrecs,
2399 outfile.c_str());
2400 wxLogMessage(_("%s is ready to be emailed or uploaded."), outfile.c_str());
2401 wxLogMessage(_("Note: TQSL assumes that this file will be uploaded to LoTW."));
2402 wxLogMessage(_("Resubmitting these QSOs will cause them to be reported as duplicates."));
2403 }
2404
2405 return status;
2406 }
2407
2408 long compressToBuf(string& buf, const char* input) {
2409 tqslTrace("compressToBuf", NULL);
2410 const size_t TBUFSIZ = 128*1024;
2411 uint8_t* tbuf = new uint8_t[TBUFSIZ];
2412
2413 //vector<uint8_t> buf;
2414 z_stream stream;
2415 stream.zalloc = 0;
2416 stream.zfree = 0;
2417 stream.next_in = reinterpret_cast<Bytef*>(const_cast<char *>(input));
2418 stream.avail_in = strlen(input);
2419 stream.next_out = tbuf;
2420 stream.avail_out = TBUFSIZ;
2421
2422 //deflateInit(&stream, Z_BEST_COMPRESSION);
2423 deflateInit2(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, 16+15, 9, Z_DEFAULT_STRATEGY); //use gzip header
2424
2425 while (stream.avail_in) { //while still some left
2426 int res = deflate(&stream, Z_NO_FLUSH);
2427 assert(res == Z_OK);
2428 if (!stream.avail_out) {
2429 buf.insert(buf.end(), tbuf, tbuf+TBUFSIZ);
2430 stream.next_out = tbuf;
2431 stream.avail_out = TBUFSIZ;
2432 }
2433 }
2434
2435 do {
2436 if (stream.avail_out == 0) {
2437 buf.insert(buf.end(), tbuf, tbuf+TBUFSIZ);
2438 stream.next_out = tbuf;
2439 stream.avail_out = TBUFSIZ;
2440 }
2441 } while (deflate(&stream, Z_FINISH) == Z_OK);
2442
2443 buf.insert(buf.end(), tbuf, tbuf+TBUFSIZ-stream.avail_out);
2444 deflateEnd(&stream);
2445
2446 delete tbuf;
2447
2448 return buf.length();
2449 }
2450
2451
2452 class UploadThread: public wxThread {
2453 public:
2454 UploadThread(CURL* handle_, wxDialog* dial_): wxThread(wxTHREAD_JOINABLE),
2455 handle(handle_), dial(dial_) {
2456 wxThread::Create();
2457 }
2458 protected:
2459 CURL* handle;
2460 wxDialog* dial;
2461 virtual wxThread::ExitCode Entry() {
2462 int ret = curl_easy_perform(handle);
2463 wxCommandEvent evt(wxEVT_LOGUPLOAD_DONE, wxID_ANY);
2464 dial->GetEventHandler()->AddPendingEvent(evt);
2465 return (wxThread::ExitCode)((intptr_t)ret);
2466 }
2467 };
2468
2469 int MyFrame::UploadLogFile(tQSL_Location loc, const wxString& infile, bool compressed, bool suppressdate, tQSL_Date* startdate, tQSL_Date* enddate, int action, const char* password, const char *defcall) {
2470 tqslTrace("MyFrame::UploadLogFile", "infile=%s, compressed=%d, suppressdate=%d, action=%d", S(infile), compressed, suppressdate, action);
2471 int numrecs = 0;
2472 wxString signedOutput;
2473
2474 tqslTrace("MyFrame::UploadLogFile", "About to convert log to string");
2475 int status = this->ConvertLogToString(loc, infile, signedOutput, numrecs, suppressdate, startdate, enddate, action, password, defcall);
2476 tqslTrace("MyFrame::UploadLogFile", "Log converted, status = %d, numrecs=%d", status, numrecs);
2477
2478 if (numrecs == 0) {
2479 wxLogMessage(_("No records to upload"));
2480 if (status == TQSL_EXIT_CANCEL || TQSL_EXIT_QSOS_SUPPRESSED)
2481 return status;
2482 else
2483 return TQSL_EXIT_NO_QSOS;
2484 } else {
2485 //compress the upload
2486 tqslTrace("MyFrame::UploadLogFile", "Compressing");
2487 string compressed;
2488 size_t compressedSize = compressToBuf(compressed, (const char*)signedOutput.ToUTF8());
2489 tqslTrace("MyFrame::UploadLogFile", "Compressed to %d bytes", compressedSize);
2490 //ofstream f; f.open("testzip.tq8", ios::binary); f<<compressed; f.close(); //test of compression routine
2491 if (compressedSize == 0) {
2492 wxLogMessage(_("Error compressing before upload"));
2493 return TQSL_EXIT_TQSL_ERROR;
2494 }
2495
2496 tqslTrace("MyFrame::UploadLogFile", "Creating filename");
2497 wxDateTime now = wxDateTime::Now().ToUTC();
2498
2499 wxString name, ext;
2500 wxFileName::SplitPath(infile, 0, &name, &ext);
2501 name += wxT(".tq8");
2502 tqslTrace("MyFrame::UploadLogFile", "file=%s", S(name));
2503 //unicode mess. can't just use mb_str directly because it's a temp ptr
2504 // and the curl form expects it to still be there during perform() so
2505 // we have to do all this copying around to please the unicode gods
2506
2507 char filename[1024];
2508 strncpy(filename, wxString::Format(wxT("<TQSLUpl %s-%s> %s"),
2509 now.Format(wxT("%Y%m%d")).c_str(),
2510 now.Format(wxT("%H%M")).c_str(),
2511 name.c_str()).ToUTF8(), sizeof filename);
2512 filename[sizeof filename - 1] = '\0';
2513 tqslTrace("MyFrame::UploadLogFile", "Upload Name=%s", filename);
2514
2515 wxString fileType(wxT("Log"));
2516 tqslTrace("MyFrame::UploadLogFile", "About to call UploadFile");
2517 int retval = UploadFile(infile, filename, numrecs, reinterpret_cast<void *>(const_cast<char *>(compressed.c_str())),
2518 compressedSize, fileType);
2519
2520 tqslTrace("MyFrame::UploadLogFile", "UploadFile returns %d", retval);
2521 if (retval == 0) {
2522 tqsl_converterCommit(logConv);
2523 tqslTrace("MyFrame::UploadLogFile", "Committing");
2524 } else {
2525 tqslTrace("MyFrame::UploadLogFile", "Rollback");
2526 tqsl_converterRollBack(logConv);
2527 }
2528
2529 tqsl_endConverter(&logConv);
2530 tqslTrace("MyFrame::UploadLogFile", "Upload done");
2531 return retval;
2532 }
2533 }
2534
2535 static CURL*
2536 tqsl_curl_init(const char *logTitle, const char *url, FILE **curlLogFile, bool newFile) {
2537 tqslTrace("tqsl_curl_init", "title=%s url=%s newFile=%d", logTitle, url, newFile);
2538 CURL* curlReq = curl_easy_init();
2539 if (!curlReq)
2540 return NULL;
2541
2542 wxString filename;
2543 #ifdef _WIN32
2544 filename.Printf(wxT("%hs\\curl.log"), wxString::FromUTF8(tQSL_BaseDir));
2545 #else
2546 filename.Printf(wxT("%hs/curl.log"), tQSL_BaseDir);
2547 #endif
2548 #ifdef _WIN32
2549 wchar_t*lfn = utf8_to_wchar(filename.ToUTF8());
2550 *curlLogFile = _wfopen(lfn, newFile ? L"wb" : L"ab");
2551 free_wchar(lfn);
2552 #else
2553 *curlLogFile = fopen(filename.ToUTF8(), newFile ? "wb" : "ab");
2554 #endif
2555 if (*curlLogFile) {
2556 curl_easy_setopt(curlReq, CURLOPT_VERBOSE, 1);
2557 curl_easy_setopt(curlReq, CURLOPT_STDERR, *curlLogFile);
2558 fprintf(*curlLogFile, "%s:\n", logTitle);
2559 }
2560 //set up options
2561 curl_easy_setopt(curlReq, CURLOPT_URL, url);
2562
2563 #ifdef __WXMAC__
2564 DocPaths docpaths(wxT("tqsl.app"));
2565 #else
2566 DocPaths docpaths(wxT("tqslapp"));
2567 #endif
2568 docpaths.Add(wxString::FromUTF8(tQSL_BaseDir));
2569 #ifdef CONFDIR
2570 docpaths.Add(wxT(CONFDIR));
2571 #endif
2572 #ifdef _WIN32
2573 wxStandardPaths sp;
2574 wxString exePath;
2575 wxFileName::SplitPath(sp.GetExecutablePath(), &exePath, 0, 0);
2576 docpaths.Add(exePath);
2577 #endif
2578 wxString caBundlePath = docpaths.FindAbsoluteValidPath(wxT("ca-bundle.crt"));
2579 if (!caBundlePath.IsEmpty()) {
2580 char caBundle[256];
2581 strncpy(caBundle, caBundlePath.ToUTF8(), sizeof caBundle);
2582 curl_easy_setopt(curlReq, CURLOPT_CAINFO, caBundle);
2583 } else {
2584 tqslTrace("tqsl_curl_init", "Can't find ca-bundle.crt in the docpaths!");
2585 }
2586 // Get the proxy configuration and pass it to cURL
2587 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
2588 config->SetPath(wxT("/Proxy"));
2589
2590 bool enabled = false;
2591 config->Read(wxT("ProxyEnabled"), &enabled, false);
2592 wxString pHost = config->Read(wxT("proxyHost"), wxT(""));
2593 wxString pPort = config->Read(wxT("proxyPort"), wxT(""));
2594 wxString pType = config->Read(wxT("proxyType"), wxT(""));
2595 config->SetPath(wxT("/"));
2596
2597 if (!enabled) return curlReq; // No proxy defined
2598
2599 long port = strtol(pPort.ToUTF8(), NULL, 10);
2600 if (port == 0 || pHost.IsEmpty())
2601 return curlReq; // Invalid proxy. Ignore it.
2602
2603 curl_easy_setopt(curlReq, CURLOPT_PROXY, (const char *)pHost.ToUTF8());
2604 curl_easy_setopt(curlReq, CURLOPT_PROXYPORT, port);
2605 if (pType == wxT("HTTP")) {
2606 curl_easy_setopt(curlReq, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); // Default is HTTP
2607 } else if (pType == wxT("Socks4")) {
2608 curl_easy_setopt(curlReq, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
2609 } else if (pType == wxT("Socks5")) {
2610 curl_easy_setopt(curlReq, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
2611 }
2612 curl_easy_setopt(curlReq, CURLOPT_SSL_VERIFYPEER, verifyCA);
2613 return curlReq;
2614 }
2615
2616 int MyFrame::UploadFile(const wxString& infile, const char* filename, int numrecs, void *content, size_t clen, const wxString& fileType) {
2617 tqslTrace("MyFrame::UploadFile", "infile=%s, filename=%s, numrecs=%d, content=0x%lx, clen=%d fileType=%s", S(infile), filename, numrecs, reinterpret_cast<void *>(content), clen, S(fileType));
2618
2619 //upload the file
2620
2621 //get the url from the config, can be overridden by an installer
2622 //defaults are valid for LoTW as of 1/31/2013
2623
2624 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
2625 config->SetPath(wxT("/LogUpload"));
2626 wxString uploadURL = config->Read(wxT("UploadURL"), DEFAULT_UPL_URL);
2627 wxString uploadField = config->Read(wxT("PostField"), DEFAULT_UPL_FIELD);
2628 wxString uplStatus = config->Read(wxT("StatusRegex"), DEFAULT_UPL_STATUSRE);
2629 wxString uplStatusSuccess = config->Read(wxT("StatusSuccess"), DEFAULT_UPL_STATUSOK).Lower();
2630 wxString uplMessage = config->Read(wxT("MessageRegex"), DEFAULT_UPL_MESSAGERE);
2631 bool uplVerifyCA;
2632 config->Read(wxT("VerifyCA"), &uplVerifyCA, DEFAULT_UPL_VERIFYCA);
2633 config->SetPath(wxT("/"));
2634
2635 // Copy the strings so they remain around
2636 char *urlstr = strdup(uploadURL.ToUTF8());
2637 char *cpUF = strdup(uploadField.ToUTF8());
2638
2639 retry_upload:
2640
2641 curlReq = tqsl_curl_init("Upload Log", urlstr, &curlLogFile, true);
2642
2643 if (!curlReq) {
2644 wxLogMessage(_("Error: Could not upload file (CURL Init error)"));
2645 free(urlstr);
2646 free(cpUF);
2647 return TQSL_EXIT_TQSL_ERROR;
2648 }
2649
2650 //the following allow us to write our log and read the result
2651
2652 FileUploadHandler handler;
2653
2654 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &FileUploadHandler::recv);
2655 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, &handler);
2656 curl_easy_setopt(curlReq, CURLOPT_SSL_VERIFYPEER, uplVerifyCA);
2657
2658 char errorbuf[CURL_ERROR_SIZE];
2659 errorbuf[0] = '\0';
2660 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
2661
2662 struct curl_httppost* post = NULL, *lastitem = NULL;
2663
2664 curl_formadd(&post, &lastitem,
2665 CURLFORM_PTRNAME, cpUF,
2666 CURLFORM_BUFFER, filename,
2667 CURLFORM_BUFFERPTR, content,
2668 CURLFORM_BUFFERLENGTH, clen,
2669 CURLFORM_END);
2670
2671 curl_easy_setopt(curlReq, CURLOPT_HTTPPOST, post);
2672
2673 intptr_t retval;
2674
2675 UploadDialog* upload = NULL;
2676
2677 if (numrecs > 0) {
2678 if (numrecs == 1) {
2679 wxLogMessage(_("Attempting to upload one QSO"));
2680 } else {
2681 wxLogMessage(_("Attempting to upload %d QSOs"), numrecs);
2682 }
2683 } else {
2684 wxLogMessage(_("Attempting to upload %s"), fileType.c_str());
2685 }
2686
2687 if(this && !quiet) {
2688 if (fileType == wxT("Log")) {
2689 upload = new UploadDialog(this);
2690 } else {
2691 upload = new UploadDialog(this, wxString(_("Uploading Callsign Certificate")), wxString(_("Uploading Callsign Certificate Request...")));
2692 }
2693
2694 curl_easy_setopt(curlReq, CURLOPT_PROGRESSFUNCTION, &UploadDialog::UpdateProgress);
2695 curl_easy_setopt(curlReq, CURLOPT_PROGRESSDATA, upload);
2696 curl_easy_setopt(curlReq, CURLOPT_NOPROGRESS, 0);
2697
2698 UploadThread thread(curlReq, upload);
2699 if (thread.Run() != wxTHREAD_NO_ERROR) {
2700 wxLogError(_("Could not spawn upload thread!"));
2701 upload->Destroy();
2702 free(urlstr);
2703 free(cpUF);
2704 if (curlLogFile) {
2705 fclose(curlLogFile);
2706 curlLogFile = NULL;
2707 }
2708 return TQSL_EXIT_TQSL_ERROR;
2709 }
2710
2711 upload->ShowModal();
2712 retval = ((intptr_t)thread.Wait());
2713 } else { retval = curl_easy_perform(curlReq); }
2714
2715 if (retval == 0) { //success
2716 //check the result
2717
2718 wxString uplresult = wxString::FromAscii(handler.s.c_str());
2719
2720 wxRegEx uplStatusRE(uplStatus);
2721 wxRegEx uplMessageRE(uplMessage);
2722 wxRegEx stripSpacesRE(wxT("\\n +"), wxRE_ADVANCED);
2723 wxRegEx certUploadRE(wxT("(Started processing.*For QSOs not after: [0-9\\-: <none>]*)\\n(.*Your certificate request contains error.*)"), wxRE_ADVANCED);
2724
2725 if (uplStatusRE.Matches(uplresult)) { //we can make sense of the error
2726 //sometimes has leading/trailing spaces
2727 if (uplStatusRE.GetMatch(uplresult, 1).Lower().Trim(true).Trim(false) == uplStatusSuccess) { //success
2728 if (uplMessageRE.Matches(uplresult)) { //and a message
2729 wxString lotwmessage = uplMessageRE.GetMatch(uplresult, 1).Trim(true).Trim(false);
2730 stripSpacesRE.ReplaceAll(&lotwmessage, wxString(wxT("\n")));
2731 if (fileType == wxT("Log")) {
2732 wxLogMessage(_("%s: Log uploaded successfully with result:\n\n%s"),
2733 infile.c_str(), lotwmessage.c_str());
2734 wxLogMessage(_("After reading this message, you may close this program."));
2735
2736 retval = TQSL_EXIT_SUCCESS;
2737 } else {
2738 // Split the log message when there is an error detected
2739 if (certUploadRE.Matches(lotwmessage)) {
2740 wxString log = certUploadRE.GetMatch(lotwmessage, 1).Trim(true).Trim(false);
2741 wxString err = certUploadRE.GetMatch(lotwmessage, 2).Trim(true).Trim(false);
2742 wxLogMessage(_("%s uploaded with result:\n\n%s"),
2743 fileType.c_str(), log.c_str());
2744 wxMessageBox(wxString::Format(_("%s Uploaded with result:\n\n%s"), fileType.c_str(), err.c_str()), _("Error"), wxOK | wxICON_EXCLAMATION);
2745 retval = TQSL_EXIT_REJECTED;
2746 } else {
2747 wxLogMessage(_("%s uploaded with result:\n\n%s"),
2748 fileType.c_str(), lotwmessage.c_str());
2749 retval = TQSL_EXIT_SUCCESS;
2750 }
2751 }
2752 } else { // no message we could find
2753 if (fileType == wxT("Log")) {
2754 wxLogMessage(_("%s: Log uploaded successfully"), infile.c_str());
2755 wxLogMessage(_("After reading this message, you may close this program."));
2756 } else {
2757 wxLogMessage(_("%s uploaded successfully"), fileType.c_str());
2758 }
2759 retval = TQSL_EXIT_SUCCESS;
2760 }
2761
2762 } else { // failure, but site is working
2763 if (uplMessageRE.Matches(uplresult)) { //and a message
2764 wxLogMessage(_("%s: %s upload was rejected with result \"%s\""),
2765 infile.c_str(), fileType.c_str(), uplMessageRE.GetMatch(uplresult, 1).c_str());
2766
2767 } else { // no message we could find
2768 wxLogMessage(_("%s: %s upload was rejected"), infile.c_str(), fileType.c_str());
2769 }
2770
2771 retval = TQSL_EXIT_REJECTED;
2772 }
2773 } else { //site isn't working
2774 wxLogMessage(_("%s: Got an unexpected response on %s upload! Maybe the site is down?"), infile.c_str(), fileType.c_str());
2775 retval = TQSL_EXIT_UNEXP_RESP;
2776 }
2777
2778 } else {
2779 tqslTrace("MyFrame::UploadFile", "cURL Error: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
2780 if (retval == CURLE_SSL_CACERT && verifyCA) {
2781 tqslTrace("MyFrame::UploadFile", "cURL SSL Certificate error - disabling verify and retry");
2782 verifyCA = false;
2783 goto retry_upload;
2784 }
2785 if (retval == CURLE_COULDNT_RESOLVE_HOST || retval == CURLE_COULDNT_CONNECT) {
2786 wxLogMessage(_("%s: Unable to upload - either your Internet connection is down or LoTW is unreachable."), infile.c_str());
2787 wxLogMessage(_("Please try uploading the %s later."), fileType.c_str());
2788 retval = TQSL_EXIT_CONNECTION_FAILED;
2789 } else if (retval == CURLE_WRITE_ERROR || retval == CURLE_SEND_ERROR || retval == CURLE_RECV_ERROR) {
2790 wxLogMessage(_("%s: Unable to upload. The nework is down or the LoTW site is too busy."), infile.c_str());
2791 wxLogMessage(_("Please try uploading the %s later."), fileType.c_str());
2792 retval = TQSL_EXIT_CONNECTION_FAILED;
2793 } else if (retval == CURLE_SSL_CONNECT_ERROR) {
2794 wxLogMessage(_("%s: Unable to connect to the upload site."), infile.c_str());
2795 wxLogMessage(_("Please try uploading the %s later."), fileType.c_str());
2796 retval = TQSL_EXIT_CONNECTION_FAILED;
2797 } else if (retval == CURLE_ABORTED_BY_CALLBACK) { //cancelled.
2798 wxLogMessage(_("%s: Upload cancelled"), infile.c_str());
2799 retval = TQSL_EXIT_CANCEL;
2800 } else { //error
2801 //don't know why the conversion from char* -> wxString -> char* is necessary but it
2802 // was turned into garbage otherwise
2803 wxLogMessage(_("%s: Couldn't upload the file: CURL returned \"%hs\" (%hs)"), infile.c_str(), curl_easy_strerror((CURLcode)retval), errorbuf);
2804 retval = TQSL_EXIT_TQSL_ERROR;
2805 }
2806 }
2807 if (this && upload) upload->Destroy();
2808
2809 curl_formfree(post);
2810 curl_easy_cleanup(curlReq);
2811 curlReq = NULL;
2812
2813 // If there's a GUI and we didn't successfully upload and weren't cancelled,
2814 // ask the user if we should retry the upload.
2815 if (this && retval != TQSL_EXIT_CANCEL && retval != TQSL_EXIT_SUCCESS) {
2816 if (wxMessageBox(_("Your upload appears to have failed. Should TQSL try again?"), _("Retry?"), wxYES_NO | wxICON_QUESTION, this) == wxYES)
2817 goto retry_upload;
2818 }
2819
2820 if (urlstr) free(urlstr);
2821 if (cpUF) free (cpUF);
2822 if (curlLogFile) {
2823 fclose(curlLogFile);
2824 curlLogFile = NULL;
2825 }
2826 return retval;
2827 }
2828
2829 // Verify that a certificate exists for this station location
2830 // before allowing the location to be edited
2831 static bool verify_cert(tQSL_Location loc, bool editing) {
2832 tqslTrace("verify_cert", "loc=%lx, editing=%d", reinterpret_cast<void *>(loc), editing);
2833 char call[128];
2834 tQSL_Cert *certlist;
2835 int ncerts;
2836 // Get the callsign from the location
2837 check_tqsl_error(tqsl_getLocationCallSign(loc, call, sizeof(call)));
2838 // See if there is a certificate for that call
2839 int flags = 0;
2840 if (editing)
2841 flags = TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_EXPIRED;
2842 tqsl_selectCertificates(&certlist, &ncerts, call, 0, 0, 0, flags);
2843 if (ncerts == 0 && strcmp(call, "NONE") && strcmp(call, "[None]")) {
2844 if (editing) {
2845 wxMessageBox(wxString::Format(_("There are no callsign certificates for callsign %hs. This station location cannot be edited."), call), _("No Certificate"), wxOK | wxICON_EXCLAMATION);
2846 } else {
2847 wxMessageBox(wxString::Format(_("There are no current callsign certificates for callsign %hs. This station location cannot be used to sign a log file."), call), _("No Certificate"), wxOK | wxICON_EXCLAMATION);
2848 }
2849 return false;
2850 }
2851 tqsl_freeCertificateList(certlist, ncerts);
2852 return true;
2853 }
2854
2855 tQSL_Location
2856 MyFrame::SelectStationLocation(const wxString& title, const wxString& okLabel, bool editonly) {
2857 tqslTrace("MyFrame::SelectStationLocation", "title=%s, okLabel=%s, editonly=%d", S(title), S(okLabel), editonly);
2858 int rval;
2859 tQSL_Location loc;
2860 wxString selname;
2861 char errbuf[512];
2862 do {
2863 TQSLGetStationNameDialog station_dial(this, help, wxDefaultPosition, false, title, okLabel, editonly);
2864 if (selname != wxT(""))
2865 station_dial.SelectName(selname);
2866 rval = station_dial.ShowModal();
2867 switch (rval) {
2868 case wxID_CANCEL: // User hit Close
2869 return 0;
2870 case wxID_APPLY: // User hit New
2871 try {
2872 check_tqsl_error(tqsl_initStationLocationCapture(&loc));
2873 selname = run_station_wizard(this, loc, help, false);
2874 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
2875 frame->loc_tree->Build();
2876 break;
2877 }
2878 catch(TQSLException& x) {
2879 wxLogError(wxT("%s"), x.what());
2880 return 0;
2881 }
2882 case wxID_MORE: // User hit Edit
2883 try {
2884 check_tqsl_error(tqsl_getStationLocation(&loc, station_dial.Selected().ToUTF8()));
2885 if (verify_cert(loc, true)) { // Check if there is a certificate before editing
2886 check_tqsl_error(tqsl_getStationLocationErrors(loc, errbuf, sizeof(errbuf)));
2887 if (strlen(errbuf) > 0) {
2888 wxString fmt = wxT("%hs\n");
2889 fmt += _("The invalid data was ignored.");
2890 wxMessageBox(wxString::Format(fmt, errbuf), _("Station Location data error"), wxOK | wxICON_EXCLAMATION, this);
2891 }
2892 char loccall[512];
2893 check_tqsl_error(tqsl_getLocationCallSign(loc, loccall, sizeof loccall));
2894 selname = run_station_wizard(this, loc, help, true, wxString::Format(_("Edit Station Location : %hs - %s"), loccall, station_dial.Selected().c_str()), station_dial.Selected());
2895 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
2896 }
2897 break;
2898 }
2899 catch(TQSLException& x) {
2900 wxLogError(wxT("%hs"), x.what());
2901 return 0;
2902 }
2903 case wxID_OK: // User hit OK
2904 try {
2905 check_tqsl_error(tqsl_getStationLocation(&loc, station_dial.Selected().ToUTF8()));
2906 check_tqsl_error(tqsl_getStationLocationErrors(loc, errbuf, sizeof(errbuf)));
2907 if (strlen(errbuf) > 0) {
2908 wxString fmt = wxT("%hs\n");
2909 fmt += _("This should be corrected before signing a log file.");
2910 wxMessageBox(wxString::Format(fmt, errbuf), _("Station Location data error"), wxOK | wxICON_EXCLAMATION, this);
2911 }
2912 break;
2913 }
2914 catch(TQSLException& x) {
2915 wxLogError(wxT("%hs"), x.what());
2916 return 0;
2917 }
2918 }
2919 } while (rval != wxID_OK);
2920 return loc;
2921 }
2922
2923 void MyFrame::CheckForUpdates(wxCommandEvent&) {
2924 tqslTrace("MyFrame::CheckForUpdates", NULL);
2925 DoUpdateCheck(false, false);
2926 }
2927
2928 wxString GetUpdatePlatformString() {
2929 tqslTrace("GetUpdatePlatformString", NULL);
2930 wxString ret;
2931 #if defined(_WIN32)
2932 #if defined(_WIN64)
2933 //this is 64 bit code already; if we are running we support 64
2934 ret = wxT("win64 win32");
2935
2936 #else // this is not 64 bit code, but we are on windows
2937 // are we 64-bit compatible? if so prefer it
2938 BOOL val = false;
2939 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
2940 LPFN_ISWOW64PROCESS fnIsWow64Process =
2941 (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
2942 if (fnIsWow64Process != NULL) {
2943 fnIsWow64Process(GetCurrentProcess(), &val);
2944 }
2945 if(val) //32 bit running on 64 bit
2946 ret = wxT("win64 win32");
2947 else //just 32-bit only
2948 ret = wxT("win32");
2949 #endif
2950
2951 #elif defined(__APPLE__) && defined(__MACH__) //osx has universal binaries
2952 ret = wxT("osx");
2953
2954 #elif defined(__gnu_linux__)
2955 #if defined(__amd64__)
2956 ret = wxT("linux64 linux32 source");
2957 #elif defined(__i386__)
2958 ret = wxT("linux32 source");
2959 #else
2960 ret = wxT("source"); //source distribution is kosher on linux
2961 #endif
2962 #else
2963 ret = wxT(""); //give them a homepage
2964 #endif
2965 return ret;
2966 }
2967
2968 // Class for encapsulating version information
2969 class revLevel {
2970 public:
2971 explicit revLevel(long _major = 0, long _minor = 0, long _patch = 0) {
2972 major = _major;
2973 minor = _minor;
2974 patch = _patch;
2975 }
2976 explicit revLevel(const wxString& _value) {
2977 wxString str = _value;
2978 str.Trim(true);
2979 str.Trim(false);
2980 wxStringTokenizer vers(str, wxT("."));
2981 wxString majorVer = vers.GetNextToken();
2982 wxString minorVer = vers.GetNextToken();
2983 wxString patchVer = vers.GetNextToken();
2984 if (majorVer.IsNumber()) {
2985 majorVer.ToLong(&major);
2986 } else {
2987 major = -1;
2988 }
2989 if (minorVer.IsNumber()) {
2990 minorVer.ToLong(&minor);
2991 } else {
2992 minor = -1;
2993 }
2994 if (patchVer != wxT("") && patchVer.IsNumber()) {
2995 patchVer.ToLong(&patch);
2996 } else {
2997 patch = 0;
2998 }
2999 }
3000 wxString Value(void) {
3001 if (patch > 0)
3002 return wxString::Format(wxT("%ld.%ld.%ld"), major, minor, patch);
3003 else
3004 return wxString::Format(wxT("%ld.%ld"), major, minor);
3005 }
3006 long major;
3007 long minor;
3008 long patch;
3009 bool operator >(const revLevel& other) {
3010 if (major > other.major) return true;
3011 if (major == other.major) {
3012 if (minor > other.minor) return true;
3013 if (minor == other.minor) {
3014 if (patch > other.patch) return true;
3015 }
3016 }
3017 return false;
3018 }
3019 bool operator >=(const revLevel& other) {
3020 if (major > other.major) return true;
3021 if (major == other.major) {
3022 if (minor > other.minor) return true;
3023 if (minor == other.minor) {
3024 if (patch >= other.patch) return true;
3025 }
3026 }
3027 return false;
3028 }
3029 };
3030
3031 class revInfo {
3032 public:
3033 explicit revInfo(bool _noGUI = false, bool _silent = false) {
3034 noGUI = _noGUI;
3035 silent = _silent;
3036 error = false;
3037 message = false;
3038 programRev = newProgramRev = NULL;
3039 configRev = newConfigRev = NULL;
3040 mutex = new wxMutex;
3041 condition = new wxCondition(*mutex);
3042 mutex->Lock();
3043 }
3044 ~revInfo() {
3045 if (programRev)
3046 delete programRev;
3047 if (newProgramRev)
3048 delete newProgramRev;
3049 if (configRev)
3050 delete configRev;
3051 if (newConfigRev)
3052 delete newConfigRev;
3053 if (condition)
3054 delete condition;
3055 if (mutex)
3056 delete mutex;
3057 }
3058 bool error;
3059 bool message;
3060 bool noGUI;
3061 bool silent;
3062 revLevel* programRev;
3063 revLevel* newProgramRev;
3064 revLevel* configRev;
3065 revLevel* newConfigRev;
3066 bool newProgram;
3067 bool newConfig;
3068 wxString errorText;
3069 wxString homepage;
3070 wxString url;
3071 wxMutex* mutex;
3072 wxCondition* condition;
3073 };
3074
3075
3076 class UpdateDialogMsgBox: public wxDialog {
3077 public:
3078 UpdateDialogMsgBox(wxWindow* parent, bool newProg, bool newConfig, revLevel* currentProgRev, revLevel* newProgRev,
3079 revLevel* currentConfigRev, revLevel* newConfigRev, wxString platformURL, wxString homepage)
3080 : wxDialog(parent, (wxWindowID)wxID_ANY, _("TQSL Update Available"), wxDefaultPosition, wxDefaultSize) {
3081 tqslTrace("UpdateDialogMsgBox::UpdateDialogMsgBox", "parent=%lx, newProg=%d, newConfig=%d, currentProgRev %s, newProgRev %s, currentConfigRev %s, newConfigRev=%s, platformURL=%s, homepage=%s", reinterpret_cast<void *>(parent), newProg, newConfig, S(currentProgRev->Value()), S(newProgRev->Value()), S(currentConfigRev->Value()), S(newConfigRev->Value()), S(platformURL), S(homepage));
3082 wxSizer* overall = new wxBoxSizer(wxVERTICAL);
3083 long flags = wxOK;
3084 #ifndef _WIN32
3085 if (newConfig)
3086 #endif
3087 flags |= wxCANCEL;
3088
3089 wxSizer* buttons = CreateButtonSizer(flags);
3090 wxString notice;
3091 if (newProg)
3092 notice = wxString::Format(_("A new TQSL release (V%s) is available!"), newProgRev->Value().c_str());
3093 else if (newConfig)
3094 notice = wxString::Format(_("An updated TrustedQSL configuration file (V%s) is available!\nThe configuration file installs definitions for entities, modes, etc."), newConfigRev->Value().c_str());
3095
3096 overall->Add(new wxStaticText(this, wxID_ANY, notice), 0, wxALIGN_CENTER_HORIZONTAL);
3097
3098 if (newProg) {
3099 #ifndef _WIN32
3100 if (!platformURL.IsEmpty()) {
3101 wxSizer* thisline = new wxBoxSizer(wxHORIZONTAL);
3102 thisline->Add(new wxStaticText(this, wxID_ANY, _("Download from:")));
3103 thisline->Add(new wxHyperlinkCtrl(this, wxID_ANY, platformURL, platformURL));
3104
3105 overall->AddSpacer(10);
3106 overall->Add(thisline);
3107 }
3108
3109 if (!homepage.IsEmpty()) {
3110 wxSizer* thisline = new wxBoxSizer(wxHORIZONTAL);
3111 thisline->Add(new wxStaticText(this, wxID_ANY, _("More details at:")));
3112 thisline->Add(new wxHyperlinkCtrl(this, wxID_ANY, homepage, homepage));
3113
3114 overall->AddSpacer(10);
3115 overall->Add(thisline);
3116 }
3117 #else
3118 overall->AddSpacer(10);
3119 overall->Add(new wxStaticText(this, wxID_ANY, _("Click 'OK' to install the new version of TQSL, or Cancel to ignore it.")));
3120 #endif
3121 }
3122 if (newConfig) {
3123 overall->AddSpacer(10);
3124 overall->Add(new wxStaticText(this, wxID_ANY, _("Click 'OK' to install the new configuration file, or Cancel to ignore it.")));
3125 }
3126 if (buttons) { //should always be here but documentation says to check
3127 overall->AddSpacer(10);
3128 overall->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL);
3129 }
3130
3131 #if wxMAJOR_VERSION > 2 || (wxMAJOR_VERSION == 2 && wxMINOR_VERSION == 9)
3132 SetSizerAndFit(overall);
3133 #else
3134 wxSizer* padding = new wxBoxSizer(wxVERTICAL);
3135 padding->Add(overall, 0, wxALL, 10);
3136 SetSizer(padding);
3137 Fit();
3138 #endif
3139 }
3140
3141 private:
3142 };
3143
3144 static size_t file_recv(void *ptr, size_t size, size_t nmemb, void *stream) {
3145 size_t left = nmemb * size;
3146 size_t written;
3147
3148 while (left > 0) {
3149 written = fwrite(ptr, size, nmemb, reinterpret_cast <FILE *>(stream));
3150 if (written == 0)
3151 return 0;
3152 left -= (written * size);
3153 }
3154 return nmemb * size;
3155 }
3156
3157 void MyFrame::UpdateConfigFile() {
3158 tqslTrace("MyFrame::UpdateConfigFile", NULL);
3159 wxConfig* config = reinterpret_cast<wxConfig *>(wxConfig::Get());
3160 wxString newConfigURL = config->Read(wxT("NewConfigURL"), DEFAULT_CONFIG_FILE_URL);
3161 retry:
3162 curlReq = tqsl_curl_init("Config File Download Log", (const char *)newConfigURL.ToUTF8(), &curlLogFile, false);
3163
3164 wxString filename;
3165 #ifdef _WIN32
3166 filename.Printf(wxT("%hs\\config.tq6"), wxString::FromUTF8(tQSL_BaseDir));
3167 wchar_t* lfn = utf8_to_wchar(filename.ToUTF8());
3168 FILE *configFile = _wfopen(lfn, L"wb");
3169 free_wchar(lfn);
3170 #else
3171 filename.Printf(wxT("%hs/config.tq6"), tQSL_BaseDir);
3172 FILE *configFile = fopen(filename.ToUTF8(), "wb");
3173 #endif
3174 if (!configFile) {
3175 tqslTrace("UpdateConfigFile", "Can't open new file %s: %s", static_cast<const char *>(filename.ToUTF8()), strerror(errno));
3176 wxMessageBox(wxString::Format(_("Can't open new configuration file %s: %hs"), filename.c_str(), strerror(errno)), _("Error"), wxOK | wxICON_ERROR, this);
3177 return;
3178 }
3179
3180 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &file_recv);
3181 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, configFile);
3182
3183 curl_easy_setopt(curlReq, CURLOPT_FAILONERROR, 1); //let us find out about a server issue
3184
3185 char errorbuf[CURL_ERROR_SIZE];
3186 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
3187 int retval = curl_easy_perform(curlReq);
3188 if (retval == CURLE_OK) {
3189 if (fclose(configFile)) {
3190 tqslTrace("UpdateConfigFile", "Error writing new file %s: %s", static_cast<const char *>(filename.ToUTF8()), strerror(errno));
3191 wxMessageBox(wxString::Format(_("Error writing new configuration file %s: %hs"), filename.c_str(), strerror(errno)), _("Error"), wxOK | wxICON_ERROR, this);
3192 return;
3193 }
3194 notifyData nd;
3195 if (tqsl_importTQSLFile(filename.ToUTF8(), notifyImport, &nd)) {
3196 wxLogError(getLocalizedErrorString());
3197 } else {
3198 tqslTrace("UpdateConfigFile", "Config update success");
3199 wxMessageBox(_("Configuration file successfully updated"), _("Update Completed"), wxOK | wxICON_INFORMATION, this);
3200 }
3201 } else {
3202 tqslTrace("MyFrame::UpdateConfigFile", "cURL Error during config file download: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3203 if (retval == CURLE_SSL_CACERT && verifyCA) {
3204 tqslTrace("MyFrame::UpdateConfigFile", "cURL SSL Certificate error - disabling verify and retry");
3205 verifyCA = false;
3206 goto retry;
3207 }
3208 if (curlLogFile) {
3209 fprintf(curlLogFile, "cURL Error during config file download: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3210 }
3211 if (retval == CURLE_COULDNT_RESOLVE_HOST || retval == CURLE_COULDNT_CONNECT) {
3212 wxLogMessage(_("Unable to update - either your Internet connection is down or LoTW is unreachable."));
3213 wxLogMessage(_("Please try again later."));
3214 } else if (retval == CURLE_WRITE_ERROR || retval == CURLE_SEND_ERROR || retval == CURLE_RECV_ERROR) {
3215 wxLogMessage(_("Unable to update. The nework is down or the LoTW site is too busy."));
3216 wxLogMessage(_("Please try again later."));
3217 } else if (retval == CURLE_SSL_CONNECT_ERROR) {
3218 wxLogMessage(_("Unable to connect to the update site."));
3219 wxLogMessage(_("Please try again later."));
3220 } else { // some other error
3221 wxString fmt = _("Error downloading new configuration file:");
3222 fmt += wxT("\n%hs");
3223 wxMessageBox(wxString::Format(fmt, errorbuf), _("Update"), wxOK | wxICON_EXCLAMATION, this);
3224 }
3225 }
3226 }
3227
3228 #ifdef _WIN32
3229 void MyFrame::UpdateTQSL(wxString& url) {
3230 tqslTrace("MyFrame::UpdateTQSL", "url=%s", S(url));
3231 retry:
3232 bool needToCleanUp = false;
3233 if (curlReq) {
3234 curl_easy_setopt(curlReq, CURLOPT_URL, (const char *)url.ToUTF8());
3235 } else {
3236 curlReq = tqsl_curl_init("TQSL Update Download Log", (const char *)url.ToUTF8(), &curlLogFile, false);
3237 needToCleanUp = true;
3238 }
3239
3240 wxString filename;
3241 #ifdef _WIN32
3242 filename.Printf(wxT("%hs\\tqslupdate.msi"), wxString::FromUTF8(tQSL_BaseDir));
3243 wchar_t* lfn = utf8_to_wchar(filename.ToUTF8());
3244 FILE *updateFile = _wfopen(lfn, L"wb");
3245 free_wchar(lfn);
3246 #else
3247 filename.Printf(wxT("%hs/tqslupdate.msi"), tQSL_BaseDir);
3248 FILE *updateFile = fopen(filename.ToUTF8(), "wb");
3249 #endif
3250 if (!updateFile) {
3251 tqslTrace("UpdateTQSL", "Can't open new file %s: %s", static_cast<const char *>(filename.ToUTF8()), strerror(errno));
3252 wxMessageBox(wxString::Format(_("Can't open TQSL update file %s: %hs"), filename.c_str(), strerror(errno)), _("Error"), wxOK | wxICON_ERROR, this);
3253 return;
3254 }
3255
3256 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &file_recv);
3257 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, updateFile);
3258
3259 curl_easy_setopt(curlReq, CURLOPT_FAILONERROR, 1); //let us find out about a server issue
3260
3261 char errorbuf[CURL_ERROR_SIZE];
3262 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
3263 int retval = curl_easy_perform(curlReq);
3264 if (retval == CURLE_OK) {
3265 if (fclose(updateFile)) {
3266 tqslTrace("UpdateTQSL", "Error writing new file %s: %s", static_cast<const char *>(filename.ToUTF8()), strerror(errno));
3267 wxMessageBox(wxString::Format(_("Error writing new configuration file %s: %hs"), filename.c_str(), strerror(errno)), _("Error"), wxOK | wxICON_ERROR, this);
3268 return;
3269 }
3270 wxExecute(wxT("msiexec /i ") + filename, wxEXEC_ASYNC);
3271 wxExit();
3272 } else {
3273 tqslTrace("MyFrame::UpdateTQSL", "cURL Error during file download: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3274 if (retval == CURLE_SSL_CACERT && verifyCA) {
3275 tqslTrace("MyFrame::UpdateTQSL", "cURL SSL Certificate error - disabling verify and retry");
3276 verifyCA = false;
3277 goto retry;
3278 }
3279 if (curlLogFile) {
3280 fprintf(curlLogFile, "cURL Error during file download: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3281 }
3282 if (retval == CURLE_COULDNT_RESOLVE_HOST || retval == CURLE_COULDNT_CONNECT) {
3283 wxLogMessage(_("Unable to update - either your Internet connection is down or LoTW is unreachable."));
3284 wxLogMessage(_("Please try again later."));
3285 } else if (retval == CURLE_WRITE_ERROR || retval == CURLE_SEND_ERROR || retval == CURLE_RECV_ERROR) {
3286 wxLogMessage(_("Unable to update. The nework is down or the LoTW site is too busy."));
3287 wxLogMessage(_("Please try again later."));
3288 } else if (retval == CURLE_SSL_CONNECT_ERROR) {
3289 wxLogMessage(_("Unable to connect to the update site."));
3290 wxLogMessage(_("Please try again later."));
3291 } else { // some other error
3292 wxString fmt = _("Error downloading new file:");
3293 fmt += wxT("\n%hs");
3294 wxMessageBox(wxString::Format(fmt, errorbuf), _("Update"), wxOK | wxICON_EXCLAMATION, this);
3295 }
3296 }
3297 if (needToCleanUp) {
3298 curl_easy_cleanup(curlReq);
3299 curlReq = NULL;
3300 }
3301 }
3302 #endif /* _WIN32 */
3303
3304 // Check if a certificate is still valid and current at LoTW
3305 bool MyFrame::CheckCertStatus(long serial, wxString& result) {
3306 tqslTrace("MyFrame::CheckCertStatus()", "Serial=%ld", serial);
3307 wxConfig* config = reinterpret_cast<wxConfig *>(wxConfig::Get());
3308
3309 wxString certCheckURL = config->Read(wxT("CertCheckURL"), DEFAULT_CERT_CHECK_URL);
3310 wxString certCheckRE = config->Read(wxT("StatusRegex"), DEFAULT_CERT_CHECK_RE);
3311 certCheckURL = certCheckURL + wxString::Format(wxT("%ld"), serial);
3312 bool needToCleanUp = false;
3313
3314 if (curlReq == NULL) {
3315 needToCleanUp = true;
3316 curlReq = tqsl_curl_init("checkCert", certCheckURL.ToUTF8(), &curlLogFile, false);
3317 } else {
3318 curl_easy_setopt(curlReq, CURLOPT_URL, (const char *)certCheckURL.ToUTF8());
3319 }
3320
3321 FileUploadHandler handler;
3322
3323 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &FileUploadHandler::recv);
3324 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, &handler);
3325
3326 curl_easy_setopt(curlReq, CURLOPT_FAILONERROR, 1); //let us find out about a server issue
3327
3328 char errorbuf[CURL_ERROR_SIZE];
3329 errorbuf[0] = '\0';
3330 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
3331 int retval = curl_easy_perform(curlReq);
3332 result = wxString(wxT("Unknown"));
3333 bool ret = false;
3334 if (retval == CURLE_OK) {
3335 wxString checkresult = wxString::FromAscii(handler.s.c_str());
3336
3337 wxRegEx checkStatusRE(certCheckRE);
3338
3339 if (checkStatusRE.Matches(checkresult)) { // valid response
3340 result = checkStatusRE.GetMatch(checkresult, 1).Trim(true).Trim(false);
3341 ret = true;
3342 }
3343 } else {
3344 tqslTrace("MyFrame::CheckCertStatus", "cURL Error during cert status check: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3345 if (curlLogFile) {
3346 fprintf(curlLogFile, "cURL Error during cert status check: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3347 }
3348 if (retval == CURLE_SSL_CACERT && verifyCA) {
3349 verifyCA = false;
3350 }
3351 }
3352 if (needToCleanUp) {
3353 curl_easy_cleanup(curlReq);
3354 curlReq = NULL;
3355 }
3356 return ret;
3357 }
3358
3359 class expInfo {
3360 public:
3361 explicit expInfo(bool _noGUI = false) {
3362 noGUI = _noGUI;
3363 days = 0;
3364 callsign = NULL;
3365 error = false;
3366 mutex = new wxMutex;
3367 condition = new wxCondition(*mutex);
3368 mutex->Lock();
3369 }
3370 bool noGUI;
3371 int days;
3372 tQSL_Cert cert;
3373 char* callsign;
3374 bool error;
3375 wxString errorText;
3376 wxMutex* mutex;
3377 wxCondition* condition;
3378 ~expInfo() {
3379 if (callsign) free(callsign);
3380 if (condition) delete condition;
3381 if (mutex) delete mutex;
3382 }
3383 };
3384
3385 // Report an error back to the main thread
3386 static void
3387 report_error(expInfo **eip) {
3388 expInfo *ei = *eip;
3389 ei->error = true;
3390 ei->errorText = getLocalizedErrorString();
3391 // Send the result back to the main thread
3392 wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, bg_expiring);
3393 event->SetClientData(ei);
3394 wxPostEvent(frame, *event);
3395 ei->condition->Wait(); // stalls here until the main thread resumes the thread
3396 ei->mutex->Unlock();
3397 delete ei;
3398 delete event;
3399 *eip = new expInfo;
3400 }
3401
3402 // Check for certificates expiring in the next nn (default 60) days
3403 void
3404 MyFrame::DoCheckExpiringCerts(bool noGUI) {
3405 tQSL_Cert *clist;
3406 int nc;
3407
3408 tqsl_selectCertificates(&clist, &nc, 0, 0, 0, 0, TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_EXPIRED);
3409 if (nc == 0) return;
3410
3411 expInfo *ei = new expInfo;
3412 ei->noGUI = noGUI;
3413
3414 bool needToCleanUp = false;
3415
3416 if (curlReq == NULL) {
3417 needToCleanUp = true;
3418 curlReq = tqsl_curl_init("Certificate Check Log", "https://lotw.arrl.org", &curlLogFile, false);
3419 }
3420
3421 long expireDays = DEFAULT_CERT_WARNING;
3422 wxConfig::Get()->Read(wxT("CertWarnDays"), &expireDays);
3423 // Get today's date
3424 time_t t = time(0);
3425 struct tm *tm = gmtime(&t);
3426 tQSL_Date d;
3427 d.year = tm->tm_year + 1900;
3428 d.month = tm->tm_mon + 1;
3429 d.day = tm->tm_mday;
3430 tQSL_Date exp;
3431
3432 for (int i = 0; i < nc; i ++) {
3433 ei->cert = clist[i];
3434 char callsign[64];
3435 if (tqsl_getCertificateCallSign(clist[i], callsign, sizeof callsign)) {
3436 report_error(&ei);
3437 continue;
3438 }
3439 wxString county;
3440 wxString grid;
3441
3442 // Get the user detail info for this callsign from the ARRL server
3443 SaveAddressInfo(callsign);
3444
3445 int expired = 0;
3446 tqsl_isCertificateExpired(clist[i], &expired);
3447 if (expired) continue; // Don't check expired already
3448
3449 int keyonly, pending;
3450 keyonly = pending = 0;
3451 if (tqsl_getCertificateKeyOnly(clist[i], &keyonly)) {
3452 report_error(&ei);
3453 continue;
3454 }
3455 long serial = 0;
3456 wxString status = wxString(wxT("KeyOnly"));
3457 if (!keyonly) {
3458 if (tqsl_getCertificateSerial(clist[i], &serial)) {
3459 report_error(&ei);
3460 continue;
3461 }
3462 CheckCertStatus(serial, status);
3463 if (tqsl_setCertificateStatus(serial, (const char *)status.ToUTF8())) {
3464 report_error(&ei);
3465 continue;
3466 }
3467 }
3468 wxString reqPending = wxConfig::Get()->Read(wxT("RequestPending"));
3469 wxStringTokenizer tkz(reqPending, wxT(","));
3470 while (tkz.HasMoreTokens()) {
3471 wxString pend = tkz.GetNextToken();
3472 if (pend == wxString::FromUTF8(callsign)) {
3473 pending = true;
3474 break;
3475 }
3476 }
3477
3478 if (keyonly || pending)
3479 continue;
3480
3481 if (0 == tqsl_getCertificateNotAfterDate(clist[i], &exp)) {
3482 int days_left;
3483 tqsl_subtractDates(&d, &exp, &days_left);
3484 if (days_left < expireDays) {
3485 ei->days = days_left;
3486 ei->callsign = strdup(callsign);
3487 ei->cert = clist[i];
3488 // Send the result back to the main thread
3489 wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, bg_expiring);
3490 event->SetClientData(ei);
3491 wxPostEvent(frame, *event);
3492 ei->condition->Wait();
3493 ei->mutex->Unlock();
3494 delete ei;
3495 delete event;
3496 ei = new expInfo;
3497 ei->noGUI = noGUI;
3498 }
3499 }
3500 }
3501 ei->mutex->Unlock();
3502 delete ei;
3503 if (clist) {
3504 tqsl_freeCertificateList(clist, nc);
3505 }
3506 if (needToCleanUp) {
3507 curl_easy_cleanup(curlReq);
3508 curlReq = NULL;
3509 if (curlLogFile) {
3510 fclose(curlLogFile);
3511 curlLogFile = NULL;
3512 }
3513 }
3514 return;
3515 }
3516
3517 void
3518 MyFrame::OnExpiredCertFound(wxCommandEvent& event) {
3519 expInfo *ei = reinterpret_cast<expInfo *>(event.GetClientData());
3520 if (ei->error) {
3521 if (ei->noGUI) {
3522 wxLogError(ei->errorText);
3523 } else {
3524 wxMessageBox(wxString(_("Error checking for expired callsign certificates:")) + ei->errorText,
3525 _("Check Error"), wxOK | wxICON_EXCLAMATION, this);
3526 }
3527 } else if (ei->noGUI) {
3528 wxLogMessage(_("The certificate for %hs expires in %d days."),
3529 ei->callsign, ei->days);
3530 } else {
3531 wxString fmt = _("The certificate for %hs expires in %d days");
3532 fmt += wxT("\n");
3533 fmt += _("Do you want to renew it now?");
3534 if (wxMessageBox(wxString::Format(fmt, ei->callsign, ei->days),
3535 _("Certificate Expiring"), wxYES_NO|wxICON_QUESTION, this) == wxYES) {
3536 // Select the certificate in the tree
3537 cert_tree->SelectCert(ei->cert);
3538 // Then start the renewal
3539 wxCommandEvent dummy;
3540 CRQWizardRenew(dummy);
3541 }
3542 }
3543 // Tell the background thread that it's OK to continue
3544 wxMutexLocker lock(*ei->mutex);
3545 ei->condition->Signal();
3546 }
3547
3548 void
3549 MyFrame::OnUpdateCheckDone(wxCommandEvent& event) {
3550 revInfo *ri = reinterpret_cast<revInfo *>(event.GetClientData());
3551 if (!ri) return;
3552 if (ri->error) {
3553 if (!ri->silent && !ri->noGUI)
3554 wxLogMessage(ri->errorText);
3555 wxMutexLocker lock(*ri->mutex);
3556 ri->condition->Signal();
3557 return;
3558 }
3559 if (ri->message) {
3560 if (!ri->silent && !ri->noGUI)
3561 wxMessageBox(ri->errorText, _("Update"), wxOK | wxICON_EXCLAMATION, this);
3562 wxMutexLocker lock(*ri->mutex);
3563 ri->condition->Signal();
3564 return;
3565 }
3566 if (ri->newProgram) {
3567 if (ri->noGUI) {
3568 wxLogMessage(_("A new TQSL release (V%s) is available."), ri->newProgramRev->Value().c_str());
3569 } else {
3570 //will create ("homepage"->"") if none there, which is what we'd be checking for anyway
3571 UpdateDialogMsgBox msg(this, true, false, ri->programRev, ri->newProgramRev,
3572 ri->configRev, ri->newConfigRev, ri->url, ri->homepage);
3573
3574 #ifdef _WIN32
3575 if (msg.ShowModal() == wxID_OK) {
3576 UpdateTQSL(ri->url);
3577 }
3578 #else
3579 msg.ShowModal();
3580 #endif
3581 }
3582 }
3583 if (ri->newConfig) {
3584 if (ri->noGUI) {
3585 wxLogMessage(_("A new TrustedQSL configuration file (V%s) is available."), ri->newConfigRev->Value().c_str());
3586 } else {
3587 UpdateDialogMsgBox msg(this, false, true, ri->programRev, ri->newProgramRev,
3588 ri->configRev, ri->newConfigRev, wxT(""), wxT(""));
3589
3590 if (msg.ShowModal() == wxID_OK) {
3591 UpdateConfigFile();
3592 }
3593 }
3594 }
3595
3596 if (!ri->newProgram && !ri->newConfig) {
3597 if (!ri->silent && !ri->noGUI) {
3598 wxString fmt = _("Your system is up to date");
3599 fmt += wxT("\n");
3600 fmt += _("TQSL Version %hs and Configuration Data Version %s");
3601 fmt += wxT("\n");
3602 fmt += _("are the newest available");
3603 wxMessageBox(wxString::Format(fmt, VERSION, ri->configRev->Value().c_str()), _("No Updates"), wxOK | wxICON_INFORMATION, this);
3604 }
3605 }
3606 // Tell the background thread that it's OK to continue
3607 wxMutexLocker lock(*ri->mutex);
3608 ri->condition->Signal();
3609 }
3610
3611 // The macro for declaring a hash map defines a couple of typedefs
3612 // that it never uses. Current GCC warns about those. The pragma
3613 // below suppresses those warnings for those.
3614 #if !defined(__APPLE__) && !defined(_WIN32) && !defined(__clang__)
3615 #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
3616 #endif
3617 void
3618 MyFrame::DoCheckForUpdates(bool silent, bool noGUI) {
3619 tqslTrace("MyFrame::DoCheckForUpdates", "silent=%d noGUI=%d", silent, noGUI);
3620 wxConfig* config = reinterpret_cast<wxConfig *>(wxConfig::Get());
3621
3622 wxString lastUpdateTime = config->Read(wxT("UpdateCheckTime"));
3623 int numdays = config->Read(wxT("UpdateCheckInterval"), 1); // in days
3624
3625 bool check = true;
3626 bool networkError = false;
3627 if (!lastUpdateTime.IsEmpty()) {
3628 wxDateTime lastcheck; lastcheck.ParseFormat(lastUpdateTime, wxT("%Y-%m-%d"));
3629 lastcheck+=wxDateSpan::Days(numdays); // x days from when we checked
3630 if (lastcheck > wxDateTime::Today()) //if we checked less than x days ago
3631 check = false; // don't check again
3632 } // else no stored value, means check
3633
3634 if (!silent) check = true; //unless the user explicitly asked
3635
3636 if (!check) return; //if we really weren't supposed to check, get out of here
3637
3638 revInfo* ri = new revInfo;
3639 ri->noGUI = noGUI;
3640 ri->silent = silent;
3641 ri->programRev = new revLevel(wxT(VERSION));
3642
3643 int currentConfigMajor, currentConfigMinor;
3644 tqsl_getConfigVersion(¤tConfigMajor, ¤tConfigMinor);
3645 ri->configRev = new revLevel(currentConfigMajor, currentConfigMinor, 0); // config files don't have patch levels
3646
3647 wxString updateURL = config->Read(wxT("UpdateURL"), DEFAULT_UPD_URL);
3648
3649 bool needToCleanUp = false;
3650 retry:
3651 if (curlReq) {
3652 curl_easy_setopt(curlReq, CURLOPT_URL, (const char *)updateURL.ToUTF8());
3653 } else {
3654 needToCleanUp = true;
3655 curlReq = tqsl_curl_init("Version Check Log", (const char*)updateURL.ToUTF8(), &curlLogFile, false);
3656 }
3657
3658 //the following allow us to analyze our file
3659
3660 FileUploadHandler handler;
3661
3662 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &FileUploadHandler::recv);
3663 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, &handler);
3664
3665 if (silent) { // if there's a problem, we don't want the program to hang while we're starting it
3666 curl_easy_setopt(curlReq, CURLOPT_CONNECTTIMEOUT, 120);
3667 }
3668
3669 curl_easy_setopt(curlReq, CURLOPT_FAILONERROR, 1); //let us find out about a server issue
3670
3671 char errorbuf[CURL_ERROR_SIZE];
3672 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
3673
3674 tqslTrace("MyFrame::DoCheckForUpdates", "calling curl_easy_perform");
3675 int retval = curl_easy_perform(curlReq);
3676 tqslTrace("MyFrame::DoCheckForUpdates", "Program rev check returns %d", retval);
3677 if (retval == CURLE_OK) {
3678 tqslTrace("MyFrame::DoCheckForUpdates", "Program rev returns %d chars, %s", handler.s.size(), handler.s.c_str());
3679 // Add the config.xml text to the result
3680 wxString configURL = config->Read(wxT("ConfigFileVerURL"), DEFAULT_UPD_CONFIG_URL);
3681 curl_easy_setopt(curlReq, CURLOPT_URL, (const char*)configURL.ToUTF8());
3682
3683 retval = curl_easy_perform(curlReq);
3684 if (retval == CURLE_OK) {
3685 tqslTrace("MyFrame::DoCheckForUpdates", "Prog + Config rev returns %d chars, %s", handler.s.size(), handler.s.c_str());
3686 wxString result = wxString::FromAscii(handler.s.c_str());
3687 wxString url;
3688 WX_DECLARE_STRING_HASH_MAP(wxString, URLHashMap);
3689 URLHashMap map;
3690 ri->newProgramRev = NULL;
3691 ri->newConfigRev = NULL;
3692
3693 wxStringTokenizer urls(result, wxT("\n"));
3694 wxString onlinever;
3695 while(urls.HasMoreTokens()) {
3696 wxString header = urls.GetNextToken().Trim();
3697 if (header.StartsWith(wxT("TQSLVERSION;"), &onlinever)) {
3698 ri->newProgramRev = new revLevel(onlinever);
3699 } else if (header.IsEmpty()) {
3700 continue; //blank line
3701 } else if (header[0] == '#') {
3702 continue; //comments
3703 } else if (header.StartsWith(wxT("config.xml"), &onlinever)) {
3704 onlinever.Replace(wxT(":"), wxT(""));
3705 onlinever.Replace(wxT("Version"), wxT(""));
3706 onlinever.Trim(true);
3707 onlinever.Trim(false);
3708 ri->newConfigRev = new revLevel(onlinever);
3709 } else {
3710 int sep = header.Find(';'); //; is invalid in URLs
3711 if (sep == wxNOT_FOUND) continue; //malformed string
3712 wxString plat = header.Left(sep);
3713 wxString url = header.Right(header.size()-sep-1);
3714 map[plat] = url;
3715 }
3716 }
3717 #ifdef TQSL_TEST_BUILD
3718 ri->newProgram = ri->newProgramRev ? (*ri->newProgramRev >= *ri->programRev) : false;
3719 #else
3720 ri->newProgram = ri->newProgramRev ? (*ri->newProgramRev > *ri->programRev) : false;
3721 #endif
3722 ri->newConfig = ri->newConfigRev ? (*ri->newConfigRev > *ri->configRev) : false;
3723 if (ri->newProgram) {
3724 wxString ourPlatURL; //empty by default (we check against this later)
3725
3726 wxStringTokenizer plats(GetUpdatePlatformString(), wxT(" "));
3727 while(plats.HasMoreTokens()) {
3728 wxString tok = plats.GetNextToken();
3729 //see if this token is here
3730 if (map.count(tok)) { ourPlatURL=map[tok]; break; }
3731 }
3732 ri->homepage = map[wxT("homepage")];
3733 ri->url = ourPlatURL;
3734 }
3735 } else {
3736 tqslTrace("MyFrame::DoCheckForUpdates", "cURL Error during config file version check: %d : %s (%s)\n", retval, curl_easy_strerror((CURLcode)retval), errorbuf);
3737 if (curlLogFile) {
3738 fprintf(curlLogFile, "cURL Error during config file version check: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3739 }
3740 if (retval == CURLE_COULDNT_RESOLVE_HOST || retval == CURLE_COULDNT_CONNECT) {
3741 networkError = true;
3742 ri->error = true;
3743 ri->errorText = wxString(_("Unable to check for updates - either your Internet connection is down or LoTW is unreachable."));
3744 ri->errorText += wxT("\n");
3745 ri->errorText += _("Please try again later.");
3746 } else if (retval == CURLE_WRITE_ERROR || retval == CURLE_SEND_ERROR || retval == CURLE_RECV_ERROR) {
3747 networkError = true;
3748 ri->error = true;
3749 ri->errorText = wxString(_("Unable to check for updates. The nework is down or the LoTW site is too busy."));
3750 ri->errorText += wxT("\n");
3751 ri->errorText += _("Please try again later.");
3752 } else if (retval == CURLE_SSL_CONNECT_ERROR) {
3753 networkError = true;
3754 ri->error = true;
3755 ri->errorText = wxString(_("Unable to connect to the update site."));
3756 ri->errorText += wxT("\n");
3757 ri->errorText += _("Please try again later.");
3758 } else { // some other error
3759 ri->message = true;
3760 wxString fmt = _("Error downloading new version information:");
3761 fmt += wxT("\n%hs");
3762 ri->errorText = wxString::Format(fmt, errorbuf);
3763 }
3764 }
3765 } else {
3766 tqslTrace("MyFrame::DoCheckForUpdates", "cURL Error during program revision check: %d: %s (%s)\n", retval, curl_easy_strerror((CURLcode)retval), errorbuf);
3767 if (retval == CURLE_SSL_CACERT && verifyCA) {
3768 tqslTrace("MyFrame::DoCheckForUpdates", "cURL SSL Certificate error - disabling verify and retry");
3769 verifyCA = false;
3770 goto retry;
3771 }
3772 if (curlLogFile) {
3773 fprintf(curlLogFile, "cURL Error during program revision check: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3774 }
3775 if (retval == CURLE_COULDNT_RESOLVE_HOST || retval == CURLE_COULDNT_CONNECT) {
3776 networkError = true;
3777 ri->error = true;
3778 ri->errorText = wxString(_("Unable to check for updates - either your Internet connection is down or LoTW is unreachable."));
3779 ri->errorText += wxT("\n");
3780 ri->errorText += _("Please try again later.");
3781 } else if (retval == CURLE_WRITE_ERROR || retval == CURLE_SEND_ERROR || retval == CURLE_RECV_ERROR) {
3782 networkError = true;
3783 ri->error = true;
3784 ri->errorText = wxString(_("Unable to check for updates. The nework is down or the LoTW site is too busy."));
3785 ri->errorText += wxT("\n");
3786 ri->errorText += _("Please try again later.");
3787 } else if (retval == CURLE_SSL_CONNECT_ERROR) {
3788 networkError = true;
3789 ri->error = true;
3790 ri->errorText = wxString(_("Unable to connect to the update site."));
3791 ri->errorText += wxT("\n");
3792 ri->errorText += _("Please try again later.");
3793 } else { // some other error
3794 ri->message = true;
3795 wxString fmt = _("Error downloading update version information:");
3796 fmt += wxT("\n%hs");
3797 ri->errorText = wxString::Format(fmt, errorbuf);
3798 }
3799 }
3800
3801 // Send the result back to the main thread
3802 wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, bg_updateCheck);
3803 event->SetClientData(ri);
3804 wxPostEvent(frame, *event);
3805 ri->condition->Wait();
3806 ri->mutex->Unlock();
3807 delete ri;
3808 delete event;
3809 if (needToCleanUp) {
3810 if (curlReq) curl_easy_cleanup(curlReq);
3811 if (curlLogFile) fclose(curlLogFile);
3812 curlReq = NULL;
3813 curlLogFile = NULL;
3814 }
3815
3816 // we checked today, and whatever the result, no need to (automatically) check again until the next interval
3817
3818 config->Write(wxT("UpdateCheckTime"), wxDateTime::Today().FormatISODate());
3819
3820 // After update check, validate user certificates
3821 if (!networkError)
3822 DoCheckExpiringCerts(noGUI);
3823 return;
3824 }
3825 #if !defined(__APPLE__) && !defined(_WIN32) && !defined(__clang__)
3826 #pragma GCC diagnostic warning "-Wunused-local-typedefs"
3827 #endif
3828
3829 static void
3830 wx_tokens(const wxString& str, vector<wxString> &toks) {
3831 size_t idx = 0;
3832 size_t newidx;
3833 wxString tok;
3834 do {
3835 newidx = str.find(wxT(" "), idx);
3836 if (newidx != wxString::npos) {
3837 toks.push_back(str.Mid(idx, newidx - idx));
3838 idx = newidx + 1;
3839 }
3840 } while (newidx != wxString::npos);
3841 if (str.Mid(idx) != wxT(""))
3842 toks.push_back(str.Mid(idx));
3843 }
3844
3845 int
3846 MyFrame::SaveAddressInfo(const char *callsign) {
3847 if (callsign == NULL) {
3848 tQSL_Error = TQSL_ARGUMENT_ERROR;
3849 return 1;
3850 }
3851
3852 bool needToCleanUp = false;
3853 char url[512];
3854 strncpy(url, (wxString::Format(wxT("https://lotw.arrl.org/tqsl-setup.php?callsign=%hs"), callsign)).ToUTF8(), sizeof url);
3855 tqslTrace("MyFrame::SaveAddressInfo", "Call = %s, url = %s", callsign, url);
3856 if (curlReq) {
3857 curl_easy_setopt(curlReq, CURLOPT_URL, url);
3858 } else {
3859 curlReq = tqsl_curl_init("checkLoc", url, &curlLogFile, false);
3860 needToCleanUp = true;
3861 }
3862 FileUploadHandler handler;
3863
3864 curl_easy_setopt(curlReq, CURLOPT_WRITEFUNCTION, &FileUploadHandler::recv);
3865 curl_easy_setopt(curlReq, CURLOPT_WRITEDATA, &handler);
3866
3867 curl_easy_setopt(curlReq, CURLOPT_FAILONERROR, 1); //let us find out about a server issue
3868
3869 char errorbuf[CURL_ERROR_SIZE];
3870 errorbuf[0] = '\0';
3871 curl_easy_setopt(curlReq, CURLOPT_ERRORBUFFER, errorbuf);
3872 int retval = curl_easy_perform(curlReq);
3873 wxString checkresult = wxT("");
3874
3875 if (needToCleanUp) {
3876 if (curlLogFile)
3877 fclose(curlLogFile);
3878 curl_easy_cleanup(curlReq);
3879 curlReq = NULL;
3880 }
3881
3882 if (retval == CURLE_OK) {
3883 if (handler.s != "null") {
3884 tqslTrace("MyFrame::SaveAddressInfo", "callsign=%s, result = %s", callsign, handler.s.c_str());
3885 tqsl_saveCallsignLocationInfo(callsign, handler.s.c_str());
3886 }
3887 } else {
3888 tqslTrace("save_address_info", "cURL Error during cert status check: %s (%s)\n", curl_easy_strerror((CURLcode)retval), errorbuf);
3889 return 1;
3890 }
3891
3892 return 0;
3893 }
3894
3895 int
3896 get_address_field(const char *callsign, const char *field, string& result) {
3897 typedef map<string, string>LocMap;
3898 static LocMap locInfo;
3899
3900 if (callsign == NULL || field == NULL) {
3901 tQSL_Error = TQSL_ARGUMENT_ERROR;
3902 return 1;
3903 }
3904
3905 result = "";
3906
3907 if (strcmp(callsign, "[None]") == 0) {
3908 return 1;
3909 }
3910
3911 if (locInfo["call"] == callsign) { // Already got data for this call
3912 LocMap::iterator it;
3913
3914 it = locInfo.find(field);
3915 if (it == locInfo.end()) {
3916 return 1;
3917 }
3918 string temp = it->second;
3919 if (temp.empty() || temp == "null") {
3920 return 1;
3921 }
3922 result = temp;
3923 return 0;
3924 }
3925
3926 locInfo.clear();
3927
3928 char *buf;
3929 if (tqsl_getCallsignLocationInfo(callsign, &buf)) {
3930 return 1;
3931 }
3932 wxString checkresult = wxString::FromUTF8(buf);
3933
3934 wxJSONReader reader;
3935 wxJSONValue root;
3936
3937 int errors = reader.Parse(checkresult, &root);
3938 if (errors > 0)
3939 return 1;
3940
3941 locInfo["call"] = callsign;
3942
3943 locInfo["status"] = root[wxT("Status")].AsString().ToUTF8();
3944 if (locInfo["status"] != "OK") {
3945 return 1;
3946 }
3947 locInfo["dxcc"] = root[wxT("DXCC_Entity")].AsString().ToUTF8();
3948 locInfo["entity"] = root[wxT("DXCC_Entity_Name")].AsString().ToUTF8();
3949 locInfo["country"] = root[wxT("Country")].AsString().ToUTF8();
3950 locInfo["state"] = root[wxT("STATE")].AsString().ToUTF8();
3951 wxStringTokenizer toker(root[wxT("CNTY")].AsString(), wxT(","));
3952 wxString state = toker.GetNextToken();
3953 wxString cty = toker.GetNextToken();
3954 locInfo["county"] = cty.ToUTF8();
3955 if (locInfo["state"] == "") {
3956 locInfo["state"] = state.ToUTF8();
3957 }
3958 locInfo["grid"] = root[wxT("GRID")].AsString().ToUTF8();
3959 locInfo["pas"] = root[wxT("Primary_Admnistrative_Subdivision")].AsString().ToUTF8();
3960 locInfo["sas"] = root[wxT("Secondary_Admnistrative_Subdivision")].AsString().ToUTF8();
3961 locInfo["address"] = root[wxT("Address_In")].AsString().ToUTF8();
3962 string grids;
3963 wxJSONValue gridlist = root[wxT("VUCC_Grids")];
3964 for (int x = 0; x < gridlist.Size(); x++) {
3965 if (!grids.empty()) {
3966 grids = grids + "|";
3967 }
3968 grids = grids + ((string)gridlist[x].AsString().ToUTF8());
3969 }
3970 locInfo["grids"] = grids;
3971 wxString first = root[wxT("Source_address_components")][wxT("first_name")].AsString();
3972 wxString middle = root[wxT("Source_address_components")][wxT("middle_name")].AsString();
3973 wxString last = root[wxT("Source_address_components")][wxT("last_name")].AsString();
3974
3975 wxString name;
3976 if (middle == wxT(""))
3977 name = first + wxT(" ") + last;
3978 else
3979 name = first + wxT(" ") + middle + wxT(" ") + last;
3980 if (!name.IsEmpty())
3981 locInfo["name"] = ((string)name.ToUTF8());
3982
3983 locInfo["addr1"] = root[wxT("Source_address_components")][wxT("addr1")].AsString().ToUTF8();
3984 locInfo["addr2"] = root[wxT("Source_address_components")][wxT("addr2")].AsString().ToUTF8();
3985 locInfo["addr3"] = root[wxT("Source_address_components")][wxT("addr3")].AsString().ToUTF8();
3986 locInfo["city"] = root[wxT("Source_address_components")][wxT("city")].AsString().ToUTF8();
3987 locInfo["addrState"] = root[wxT("Source_address_components")][wxT("state")].AsString().ToUTF8();
3988 locInfo["mailCode"] = root[wxT("Source_address_components")][wxT("mail_code")].AsString().ToUTF8();
3989 locInfo["aCountry"] = root[wxT("Source_address_components")][wxT("country")].AsString().ToUTF8();
3990
3991 LocMap::iterator it;
3992
3993 it = locInfo.find(field);
3994 if (it == locInfo.end()) {
3995 return 1;
3996 }
3997 string temp = it->second;
3998 if (temp.empty() || temp == "null") {
3999 return 1;
4000 }
4001 result = temp;
4002 return 0;
4003 }
4004
4005 // Common method for sign and (save, upload) a log
4006 void
4007 MyFrame::ProcessQSODataFile(bool upload, bool compressed) {
4008 tqslTrace("MyFrame::ProcessQSODataFile", "upload=%d, compressed=%d", upload, compressed);
4009 wxString infile;
4010 wxString outfile;
4011
4012 // Does the user have any certificates?
4013 get_certlist("", 0, false, false, true);
4014 if (ncerts == 0) {
4015 wxString msg = _("You have no callsign certificates to use to sign a log file.");
4016 msg += wxT("\n");
4017 msg += _("Please install a callsign certificate then try again.");
4018 wxMessageBox(msg, _("No Callsign Certificates"),
4019 wxOK | wxICON_EXCLAMATION, this);
4020 free_certlist();
4021 return;
4022 }
4023 free_certlist();
4024 try {
4025 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
4026 // Get input file
4027 wxString path = config->Read(wxT("ImportPath"), wxString(wxT("")));
4028 wxString defext = config->Read(wxT("ImportExtension"), wxString(wxT("adi"))).Lower();
4029 bool defFound = false;
4030
4031 // Construct filter string for file-open dialog
4032 wxString filter = wxT("All files (*.*)|*.*");
4033 vector<wxString> exts;
4034 wxString file_exts = config->Read(wxT("ADIFFiles"), wxString(DEFAULT_ADIF_FILES));
4035 wx_tokens(file_exts, exts);
4036 wxString extList;
4037 for (int i = 0; i < static_cast<int>(exts.size()); i++) {
4038 extList += wxT("*.") + exts[i] + wxT(";");
4039 if (exts[i] == defext)
4040 defFound = true;
4041 }
4042 extList.RemoveLast(); // Remove the trailing semicolon
4043 filter += _("|ADIF files (") + extList + wxT(")|") + extList;
4044
4045 exts.clear();
4046 extList.Clear();
4047 file_exts = config->Read(wxT("CabrilloFiles"), wxString(DEFAULT_CABRILLO_FILES));
4048 wx_tokens(file_exts, exts);
4049 for (int i = 0; i < static_cast<int>(exts.size()); i++) {
4050 extList += wxT("*.") + exts[i] + wxT(";");
4051 if (exts[i] == defext)
4052 defFound = true;
4053 }
4054 extList.RemoveLast();
4055 filter += _("|Cabrillo files (") + extList + wxT(")|") + extList;
4056 if (defext.IsEmpty() || !defFound)
4057 defext = wxString(wxT("adi"));
4058 infile = wxFileSelector(_("Select file to Sign"), path, wxT(""), defext, filter,
4059 wxFD_OPEN|wxFD_FILE_MUST_EXIST, this);
4060 if (infile == wxT(""))
4061 return;
4062 wxString inPath;
4063 wxString inExt;
4064 wxFileName::SplitPath(infile.c_str(), &inPath, NULL, &inExt);
4065 inExt.Lower();
4066 config->Write(wxT("ImportPath"), inPath);
4067 config->Write(wxT("ImportExtension"), inExt);
4068 if (!upload) {
4069 // Get output file
4070 wxString basename;
4071 wxFileName::SplitPath(infile.c_str(), 0, &basename, 0);
4072 path = wxConfig::Get()->Read(wxT("ExportPath"), wxString(wxT("")));
4073 wxString deftype = compressed ? wxT("tq8") : wxT("tq7");
4074 filter = compressed ? _("TQSL compressed data files (*.tq8)|*.tq8")
4075 : _("TQSL data files (*.tq7)|*.tq7");
4076 basename += wxT(".") + deftype;
4077 outfile = wxFileSelector(_("Select file to write to"),
4078 path, basename, deftype, filter + _("|All files (*.*)|*.*"),
4079 wxFD_SAVE|wxFD_OVERWRITE_PROMPT, this);
4080 if (outfile == wxT(""))
4081 return;
4082 config->Write(wxT("ExportPath"), wxPathOnly(outfile));
4083 }
4084
4085 // Get Station Location
4086 int n;
4087 tQSL_Location loc;
4088 check_tqsl_error(tqsl_initStationLocationCapture(&loc));
4089 check_tqsl_error(tqsl_getNumStationLocations(loc, &n));
4090 if (n != 1) {
4091 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
4092 loc = SelectStationLocation(_("Select Station Location for Signing"));
4093 } else {
4094 // There's only one station location. Use that and don't prompt.
4095 char deflocn[512];
4096 check_tqsl_error(tqsl_getStationLocationName(loc, 0, deflocn, sizeof deflocn));
4097 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
4098 check_tqsl_error(tqsl_getStationLocation(&loc, deflocn));
4099 }
4100 if (loc == 0)
4101 return;
4102
4103 if (!verify_cert(loc, false))
4104 return;
4105 char callsign[40];
4106 char loc_name[256];
4107 int dxccnum;
4108 check_tqsl_error(tqsl_getLocationCallSign(loc, callsign, sizeof callsign));
4109 check_tqsl_error(tqsl_getLocationDXCCEntity(loc, &dxccnum));
4110 check_tqsl_error(tqsl_getStationLocationCaptureName(loc, loc_name, sizeof loc_name));
4111 DXCC dxcc;
4112 dxcc.getByEntity(dxccnum);
4113 tqslTrace("MyFrame::ProcessQSODataFile", "file=%s location %hs, call %hs dxcc %hs",
4114 S(infile), loc_name, callsign, dxcc.name());
4115 if (strcmp(callsign, "[None]")) {
4116 wxString fmt = _("The file (%s) will be signed using:");
4117 fmt += wxT("\n");
4118 fmt += _("Station Location:");
4119 fmt += wxT(" %hs\n");
4120 fmt += _("Call sign:");
4121 fmt += wxT(" %hs\n");
4122 fmt += _("DXCC:");
4123 fmt += wxT(" %hs\n");
4124 fmt += _("Is this correct?");
4125 if (wxMessageBox(wxString::Format(fmt, infile.c_str(), loc_name,
4126 callsign, dxcc.name()), _("TQSL - Confirm signing"), wxYES_NO | wxICON_QUESTION, this) == wxYES) {
4127 if (upload) {
4128 UploadLogFile(loc, infile);
4129 } else {
4130 ConvertLogFile(loc, infile, outfile, compressed);
4131 }
4132 } else {
4133 wxLogMessage(_("Signing abandoned"));
4134 }
4135 } else {
4136 if (upload) {
4137 UploadLogFile(loc, infile);
4138 } else {
4139 ConvertLogFile(loc, infile, outfile, compressed);
4140 }
4141 }
4142 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
4143 }
4144 catch(TQSLException& x) {
4145 wxString s;
4146 wxString err = wxString::FromUTF8(x.what());
4147 if (err.Find(infile) == wxNOT_FOUND) {
4148 if (infile != wxT(""))
4149 s = infile + wxT(": ");
4150 }
4151 s += err;
4152 wxLogError(wxT("%s"), (const char *)s.c_str());
4153 }
4154 free_certlist();
4155 return;
4156 }
4157
4158 void
4159 MyFrame::ImportQSODataFile(wxCommandEvent& event) {
4160 tqslTrace("MyFrame::ImportQSODataFile", NULL);
4161
4162 bool compressed = (event.GetId() == tm_f_import_compress || event.GetId() == tl_Save);
4163 ProcessQSODataFile(false, compressed);
4164 return;
4165 }
4166
4167 void
4168 MyFrame::UploadQSODataFile(wxCommandEvent& event) {
4169 tqslTrace("MyFrame::UploadQSODataFile", NULL);
4170 ProcessQSODataFile(true, true);
4171 return;
4172 }
4173
4174 void MyFrame::OnPreferences(wxCommandEvent& WXUNUSED(event)) {
4175 tqslTrace("MyFrame::OnPreferences", NULL);
4176 Preferences* dial = new Preferences(this, help);
4177 dial->Show(true);
4178 file_menu->Enable(tm_f_preferences, false);
4179 }
4180
4181 class TQSLConfig {
4182 public:
4183 TQSLConfig() {
4184 callSign = "";
4185 serial = 0;
4186 dxcc = 0;
4187 dupes = 0;
4188 elementBody = wxT("");
4189 locstring = wxT("");
4190 config = NULL;
4191 outstr = NULL;
4192 conv = NULL;
4193 }
4194 void SaveSettings(gzFile* out, wxString appname);
4195 void RestoreCert(void);
4196 void RestoreConfig(const gzFile& in);
4197 void ParseLocations(gzFile* out, const tQSL_StationDataEnc loc);
4198 wxConfig *config;
4199 long serial;
4200 int dxcc;
4201 int dupes;
4202 string callSign;
4203 wxString signedCert;
4204 wxString privateKey;
4205 wxString elementBody;
4206 wxString locstring;
4207 gzFile* outstr;
4208 tQSL_Converter conv;
4209
4210 private:
4211 static void xml_restore_start(void *data, const XML_Char *name, const XML_Char **atts);
4212 static void xml_restore_end(void *data, const XML_Char *name);
4213 static void xml_text(void *data, const XML_Char *text, int len);
4214 static void xml_location_start(void *data, const XML_Char *name, const XML_Char **atts);
4215 static void xml_location_end(void *data, const XML_Char *name);
4216 };
4217
4218 // Save the user's configuration settings - appname is the
4219 // application name (tqslapp)
4220
4221 void TQSLConfig::SaveSettings(gzFile* out, wxString appname) {
4222 tqslTrace("TQSLConfig::SaveSettings", "appname=%s", S(appname));
4223 config = new wxConfig(appname);
4224 wxString name, gname;
4225 long context;
4226 wxString svalue;
4227 long lvalue;
4228 bool bvalue;
4229 double dvalue;
4230 wxArrayString groupNames;
4231
4232 tqslTrace("TQSLConfig::SaveSettings", "... init groups");
4233 groupNames.Add(wxT("/"));
4234 bool more = config->GetFirstGroup(gname, context);
4235 while (more) {
4236 tqslTrace("TQSLConfig::SaveSettings", "... add group %s", S(name));
4237 groupNames.Add(wxT("/") + gname);
4238 more = config->GetNextGroup(gname, context);
4239 }
4240 tqslTrace("TQSLConfig::SaveSettings", "... groups done.");
4241
4242 for (unsigned i = 0; i < groupNames.GetCount(); i++) {
4243 tqslTrace("TQSLConfig::SaveSettings", "Group %d setting path %s", i, S(groupNames[i]));
4244 int err;
4245 config->SetPath(groupNames[i]);
4246 more = config->GetFirstEntry(name, context);
4247 while (more) {
4248 tqslTrace("TQSLConfig::SaveSettings", "name=%s", S(name));
4249 if (name.IsEmpty()) {
4250 more = config->GetNextEntry(name, context);
4251 continue;
4252 }
4253 if (gzprintf(*out, "<Setting name=\"%s\" group=\"%s\" ",
4254 (const char *)name.ToUTF8(), (const char *)groupNames[i].ToUTF8()) < 0) {
4255 throw TQSLException(gzerror(*out, &err));
4256 }
4257 wxConfigBase::EntryType etype = config->GetEntryType(name);
4258 switch (etype) {
4259 case wxConfigBase::Type_Unknown:
4260 case wxConfigBase::Type_String:
4261 config->Read(name, &svalue);
4262 long testlong;
4263 if (svalue.ToLong(&testlong)) {
4264 if (gzprintf(*out, "Type=\"Int\" Value=\"%d\"/>\n", testlong) < 0) {
4265 throw TQSLException(gzerror(*out, &err));
4266 }
4267 } else {
4268 urlEncode(svalue);
4269 if (gzprintf(*out, "Type=\"String\" Value=\"%s\"/>\n",
4270 (const char *)svalue.ToUTF8()) < 0) {
4271 throw TQSLException(gzerror(*out, &err));
4272 }
4273 }
4274 break;
4275 case wxConfigBase::Type_Boolean:
4276 config->Read(name, &bvalue);
4277 if (bvalue) {
4278 if (gzprintf(*out, "Type=\"Bool\" Value=\"true\"/>\n") < 0) {
4279 throw TQSLException(gzerror(*out, &err));
4280 }
4281 } else {
4282 if (gzprintf(*out, "Type=\"Bool\" Value=\"false\"/>\n") < 0) {
4283 throw TQSLException(gzerror(*out, &err));
4284 }
4285 } break;
4286 case wxConfigBase::Type_Integer:
4287 config->Read(name, &lvalue);
4288 if (gzprintf(*out, "Type=\"Int\" Value=\"%d\"/>\n", lvalue) < 0)
4289 throw TQSLException(gzerror(*out, &err));
4290 break;
4291 case wxConfigBase::Type_Float:
4292 config->Read(name, &dvalue);
4293 if (gzprintf(*out, "Type=\"Float\" Value=\"%f\"/>\n", dvalue) < 0)
4294 throw TQSLException(gzerror(*out, &err));
4295 break;
4296 }
4297 more = config->GetNextEntry(name, context);
4298 }
4299 }
4300 tqslTrace("TQSLConfig::SaveSettings", "Done.");
4301 config->SetPath(wxT("/"));
4302
4303 return;
4304 }
4305
4306 void
4307 MyFrame::BackupConfig(const wxString& filename, bool quiet) {
4308 tqslTrace("MyFrame::BackupConfig", "filename=%s, quiet=%d", S(filename), quiet);
4309 int i;
4310 gzFile out = 0;
4311 int err;
4312 wxBusyCursor wait;
4313 #ifdef _WIN32
4314 int fd = -1;
4315 #endif
4316 if (lock_db(false) < 0) {
4317 if (quiet) // If there's an active signing thread,
4318 return; // then exit without taking a backup.
4319 wxSafeYield();
4320 wxLogMessage(_("TQSL must wait for other running copies of TQSL to exit before backing up..."));
4321 wxSafeYield();
4322 lock_db(true);
4323 }
4324 try {
4325 #ifdef _WIN32
4326 wchar_t* lfn = utf8_to_wchar(filename.ToUTF8());
4327 fd = _wopen(lfn, _O_WRONLY |_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
4328 free_wchar(lfn);
4329 if (fd != -1)
4330 out = gzdopen(fd, "wb9");
4331 #else
4332 out = gzopen(filename.ToUTF8(), "wb9");
4333 #endif
4334 if (!out) {
4335 wxLogError(_("Error opening save file %s: %hs"), filename.c_str(), strerror(errno));
4336 return;
4337 }
4338 TQSLConfig* conf = new TQSLConfig();
4339
4340 if (gzprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TQSL_Configuration>\n") < 0)
4341 throw TQSLException(gzerror(out, &err));
4342 if (gzprintf(out, "<!-- Warning! If you directly edit this file, you are responsible for its content.\n") < 0)
4343 throw TQSLException(gzerror(out, &err));
4344 if (gzprintf(out, "The ARRL's LoTW Help Desk will be unable to assist you. -->\n") < 0)
4345 throw TQSLException(gzerror(out, &err));
4346 if (gzprintf(out, "<Certificates>\n") < 0)
4347 throw TQSLException(gzerror(out, &err));
4348
4349 if (!quiet) {
4350 wxLogMessage(_("Saving callsign certificates"));
4351 } else {
4352 tqslTrace("MyFrame::BackupConfig", "Saving callsign certificates");
4353 }
4354 wxSafeYield(frame);
4355 int ncerts;
4356 char buf[8192];
4357 // Save root certificates
4358 check_tqsl_error(tqsl_selectCACertificates(&certlist, &ncerts, "root"));
4359 for (i = 0; i < ncerts; i++) {
4360 if (gzprintf(out, "<RootCert>\n") < 0)
4361 throw TQSLException(gzerror(out, &err));
4362 check_tqsl_error(tqsl_getCertificateEncoded(certlist[i], buf, sizeof buf));
4363 if (gzwrite(out, buf, strlen(buf)) < 0)
4364 throw TQSLException(gzerror(out, &err));
4365 if (gzprintf(out, "</RootCert>\n") < 0)
4366 throw TQSLException(gzerror(out, &err));
4367 }
4368 tqsl_freeCertificateList(certlist, ncerts);
4369 // Save CA certificates
4370 check_tqsl_error(tqsl_selectCACertificates(&certlist, &ncerts, "authorities"));
4371 for (i = 0; i < ncerts; i++) {
4372 if (gzprintf(out, "<CACert>\n") < 0)
4373 throw TQSLException(gzerror(out, &err));
4374 check_tqsl_error(tqsl_getCertificateEncoded(certlist[i], buf, sizeof buf));
4375 if (gzwrite(out, buf, strlen(buf)) < 0)
4376 throw TQSLException(gzerror(out, &err));
4377 if (gzprintf(out, "</CACert>\n") < 0)
4378 throw TQSLException(gzerror(out, &err));
4379 }
4380 tqsl_freeCertificateList(certlist, ncerts);
4381 tqsl_selectCertificates(&certlist, &ncerts, 0, 0, 0, 0, TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_EXPIRED | TQSL_SELECT_CERT_SUPERCEDED);
4382 for (i = 0; i < ncerts; i++) {
4383 char callsign[64];
4384 long serial = 0;
4385 int dxcc = 0;
4386 int keyonly;
4387 check_tqsl_error(tqsl_getCertificateKeyOnly(certlist[i], &keyonly));
4388 check_tqsl_error(tqsl_getCertificateCallSign(certlist[i], callsign, sizeof callsign));
4389 if (!keyonly) {
4390 check_tqsl_error(tqsl_getCertificateSerial(certlist[i], &serial));
4391 }
4392 check_tqsl_error(tqsl_getCertificateDXCCEntity(certlist[i], &dxcc));
4393 if (!quiet) {
4394 wxLogMessage(wxString(wxT("\t")) + _("Saving callsign certificate for %hs"), callsign);
4395 }
4396 if (gzprintf(out, "<UserCert CallSign=\"%s\" dxcc=\"%d\" serial=\"%d\">\n", callsign, dxcc, serial) < 0)
4397 throw TQSLException(gzerror(out, &err));
4398 if (!keyonly) {
4399 if (gzprintf(out, "<SignedCert>\n") < 0)
4400 throw TQSLException(gzerror(out, &err));
4401 check_tqsl_error(tqsl_getCertificateEncoded(certlist[i], buf, sizeof buf));
4402 if (gzwrite(out, buf, strlen(buf)) < 0)
4403 throw TQSLException(gzerror(out, &err));
4404 if (gzprintf(out, "</SignedCert>\n") < 0)
4405 throw TQSLException(gzerror(out, &err));
4406 }
4407 // Handle case where there's no private key
4408 if (tqsl_getKeyEncoded(certlist[i], buf, sizeof buf) == 0) {
4409 if (gzprintf(out, "<PrivateKey>\n") < 0)
4410 throw TQSLException(gzerror(out, &err));
4411 if (gzwrite(out, buf, strlen(buf)) < 0)
4412 throw TQSLException(gzerror(out, &err));
4413 if (gzprintf(out, "</PrivateKey>\n</UserCert>\n") < 0)
4414 throw TQSLException(gzerror(out, &err));
4415 } else {
4416 // No private key.
4417 if (gzprintf(out, "</UserCert>\n") < 0)
4418 throw TQSLException(gzerror(out, &err));
4419 }
4420 }
4421 free_certlist();
4422 if (gzprintf(out, "</Certificates>\n") < 0)
4423 throw TQSLException(gzerror(out, &err));
4424 if (gzprintf(out, "<Locations>\n") < 0)
4425 throw TQSLException(gzerror(out, &err));
4426 if (!quiet) {
4427 wxLogMessage(_("Saving Station Locations"));
4428 } else {
4429 tqslTrace("MyFrame::BackupConfig", "Saving Station Locations");
4430 }
4431 wxSafeYield(frame);
4432 tQSL_StationDataEnc sdbuf = NULL;
4433 check_tqsl_error(tqsl_getStationDataEnc(&sdbuf));
4434 TQSLConfig* parser = new TQSLConfig();
4435 if (sdbuf)
4436 parser->ParseLocations(&out, sdbuf);
4437 check_tqsl_error(tqsl_freeStationDataEnc(sdbuf));
4438 if (gzprintf(out, "</Locations>\n") < 0)
4439 throw TQSLException(gzerror(out, &err));
4440
4441 if (!quiet) {
4442 wxLogMessage(_("Saving TQSL Preferences"));
4443 } else {
4444 tqslTrace("MyFrame::BackupConfig", "Saving TQSL Preferences - out=0x%lx", reinterpret_cast<void *>(out));
4445 }
4446 wxSafeYield(frame);
4447 if (gzprintf(out, "<TQSLSettings>\n") < 0)
4448 throw TQSLException(gzerror(out, &err));
4449 conf->SaveSettings(&out, wxT("tqslapp"));
4450 tqslTrace("MyFrame::BackupConfig", "Done with settings. out=0x%lx", reinterpret_cast<void *>(out));
4451 if (gzprintf(out, "</TQSLSettings>\n") < 0)
4452 throw TQSLException(gzerror(out, &err));
4453
4454 if (!quiet) {
4455 wxLogMessage(_("Saving QSOs"));
4456 } else {
4457 tqslTrace("MyFrame::BackupConfig", "Saving QSOs");
4458 }
4459
4460 wxSafeYield(frame);
4461 tQSL_Converter conv = NULL;
4462 check_tqsl_error(tqsl_beginConverter(&conv));
4463 tqslTrace("MyFrame::BackupConfig", "beginConverter call success");
4464 if (gzprintf(out, "<DupeDb>\n") < 0)
4465 throw TQSLException(gzerror(out, &err));
4466
4467 char dupekey[256];
4468 char dupedata[256];
4469 int count = 0;
4470 while (true) {
4471 int status = tqsl_getDuplicateRecordsV2(conv, dupekey, dupedata, sizeof(dupekey));
4472 if (status == -1) // End of file
4473 break;
4474 check_tqsl_error(status);
4475 wxString dk = wxString::FromUTF8(dupekey);
4476 wxString dd = wxString::FromUTF8(dupedata);
4477 dk = urlEncode(dk);
4478 dd = urlEncode(dd);
4479 strncpy(dupekey, dk.ToUTF8(), sizeof dupekey);
4480 strncpy(dupedata, dd.ToUTF8(), sizeof dupedata);
4481 if (gzprintf(out, "<Dupe key=\"%s\" data=\"%s\" />\n", dupekey, dupedata) < 0)
4482 throw TQSLException(gzerror(out, &err));
4483 if ((count++ % 100000) == 0) {
4484 wxSafeYield(frame);
4485 }
4486 }
4487 if (gzprintf(out, "</DupeDb>\n") < 0)
4488 throw TQSLException(gzerror(out, &err));
4489 tqsl_converterCommit(conv);
4490 tqsl_endConverter(&conv);
4491 unlock_db();
4492 tqslTrace("MyFrame::BackupConfig", "Dupes db saved OK");
4493
4494 if (gzprintf(out, "</TQSL_Configuration>\n") < 0)
4495 throw TQSLException(gzerror(out, &err));
4496 if (gzclose(out) != Z_OK)
4497 throw TQSLException(gzerror(out, &err));
4498 if (!quiet) {
4499 wxLogMessage(_("Save operation complete."));
4500 } else {
4501 tqslTrace("MyFrame::BackupConfig", "Save operation complete.");
4502 }
4503 }
4504 catch(TQSLException& x) {
4505 if (out) gzclose(out);
4506 if (quiet) {
4507 wxString errmsg = wxString::Format(_("Error performing automatic backup: %hs"), x.what());
4508 wxMessageBox(errmsg, _("Backup Error"), wxOK | wxICON_EXCLAMATION, this);
4509 } else {
4510 wxLogError(_("Backup operation failed: %hs"), x.what());
4511 }
4512 }
4513 }
4514
4515 void
4516 MyFrame::OnSaveConfig(wxCommandEvent& WXUNUSED(event)) {
4517 tqslTrace("MyFrame::OnSaveConfig", NULL);
4518 try {
4519 wxString file_default = wxT("tqslconfig.tbk");
4520 wxString filename = wxFileSelector(_("Enter file to save to"), wxT(""),
4521 file_default, wxT(".tbk"), _("Configuration files (*.tbk)|*.tbk|All files (*.*)|*.*"),
4522 wxFD_SAVE|wxFD_OVERWRITE_PROMPT, this);
4523 if (filename == wxT(""))
4524 return;
4525
4526 BackupConfig(filename, false);
4527 }
4528 catch(TQSLException& x) {
4529 wxLogError(_("Backup operation failed: %hs"), x.what());
4530 }
4531 }
4532
4533
4534 void
4535 restore_user_cert(TQSLConfig* loader) {
4536 tqslTrace("restore_user_cert", "Restoring certificate for callsign %s", loader->callSign.c_str());
4537 get_certlist(loader->callSign.c_str(), loader->dxcc, true, true, true);
4538 for (int i = 0; i < ncerts; i++) {
4539 long serial;
4540 int dxcc;
4541 int keyonly;
4542 check_tqsl_error(tqsl_getCertificateKeyOnly(certlist[i], &keyonly));
4543 check_tqsl_error(tqsl_getCertificateDXCCEntity(certlist[i], &dxcc));
4544 if (!keyonly) {
4545 check_tqsl_error(tqsl_getCertificateSerial(certlist[i], &serial));
4546 if (serial == loader->serial && dxcc == loader->dxcc) {
4547 return; // This certificate is already installed.
4548 }
4549 } else {
4550 // See if the keyonly cert is the one we're trying to load
4551 char buf[8192];
4552 check_tqsl_error(tqsl_getKeyEncoded(certlist[i], buf, sizeof buf));
4553 if (!strcmp(buf, loader->privateKey.ToUTF8())) {
4554 return; // Already installed
4555 }
4556 }
4557 }
4558
4559 // There is no certificate matching this callsign/entity/serial.
4560 wxLogMessage(wxString(wxT("\t")) + _("Restoring callsign certificate for %hs"), loader->callSign.c_str());
4561 wxSafeYield(frame);
4562 check_tqsl_error(tqsl_importKeyPairEncoded(loader->callSign.c_str(), "user", loader->privateKey.ToUTF8(), loader->signedCert.ToUTF8()));
4563 }
4564
4565 void
4566 restore_root_cert(TQSLConfig* loader) {
4567 int rval = tqsl_importKeyPairEncoded(NULL, "root", NULL, loader->signedCert.ToUTF8());
4568 if (rval && tQSL_Error != TQSL_CERT_ERROR)
4569 check_tqsl_error(rval);
4570 }
4571
4572 void
4573 restore_ca_cert(TQSLConfig* loader) {
4574 int rval = tqsl_importKeyPairEncoded(NULL, "authorities", NULL, loader->signedCert.ToUTF8());
4575 if (rval && tQSL_Error != TQSL_CERT_ERROR)
4576 check_tqsl_error(rval);
4577 }
4578
4579 void
4580 TQSLConfig::xml_restore_start(void *data, const XML_Char *name, const XML_Char **atts) {
4581 TQSLConfig* loader = reinterpret_cast<TQSLConfig *> (data);
4582 int i;
4583
4584 loader->elementBody = wxT("");
4585 if (strcmp(name, "UserCert") == 0) {
4586 for (int i = 0; atts[i]; i+=2) {
4587 if (strcmp(atts[i], "CallSign") == 0) {
4588 loader->callSign = atts[i + 1];
4589 } else if (strcmp(atts[i], "serial") == 0) {
4590 if (strlen(atts[i+1]) == 0) {
4591 loader->serial = 0;
4592 } else {
4593 loader->serial = strtol(atts[i+1], NULL, 10);
4594 }
4595 } else if (strcmp(atts[i], "dxcc") == 0) {
4596 if (strlen(atts[i+1]) == 0) {
4597 loader->dxcc = 0;
4598 } else {
4599 loader->dxcc = strtol(atts[i+1], NULL, 10);
4600 }
4601 }
4602 }
4603 loader->privateKey = wxT("");
4604 loader->signedCert = wxT("");
4605 } else if (strcmp(name, "TQSLSettings") == 0) {
4606 wxLogMessage(_("Restoring Preferences"));
4607 wxSafeYield(frame);
4608 loader->config = new wxConfig(wxT("tqslapp"));
4609 } else if (strcmp(name, "Setting") == 0) {
4610 wxString sname;
4611 wxString sgroup;
4612 wxString stype;
4613 wxString svalue;
4614 for (i = 0; atts[i]; i+=2) {
4615 if (strcmp(atts[i], "name") == 0) {
4616 sname = wxString::FromUTF8(atts[i+1]);
4617 } else if (strcmp(atts[i], "group") == 0) {
4618 sgroup = wxString::FromUTF8(atts[i+1]);
4619 } else if (strcmp(atts[i], "Type") == 0) {
4620 stype = wxString::FromUTF8(atts[i+1]);
4621 } else if (strcmp(atts[i], "Value") == 0) {
4622 svalue = wxString::FromUTF8(atts[i+1]);
4623 }
4624 }
4625 // Don't restore wxHtmlWindow as these settings are OS-specific.
4626 if (sgroup != wxT("wxHtmlWindow")) {
4627 loader->config->SetPath(sgroup);
4628 if (stype == wxT("String")) {
4629 svalue.Replace(wxT("<"), wxT("<"), true);
4630 svalue.Replace(wxT(">"), wxT(">"), true);
4631 svalue.Replace(wxT("&"), wxT("&"), true);
4632 if (sname == wxT("BackupFolder") && !svalue.IsEmpty()) {
4633 // If it's the backup directory, don't restore it if the
4634 // referenced directory doesn't exist.
4635 #ifdef _WIN32
4636 struct _stat32 s;
4637 wchar_t* lfn = utf8_to_wchar(svalue.ToUTF8());
4638 int ret = _wstat32(lfn, &s);
4639 free_wchar(lfn);
4640 if (ret == 0) {
4641 #else
4642 struct stat s;
4643 if (lstat(svalue.ToUTF8(), &s) == 0) { // Does it exist?
4644 #endif
4645 if (S_ISDIR(s.st_mode)) { // And is it a directory?
4646 loader->config->Write(sname, svalue); // OK to use it.
4647 }
4648 }
4649 } else {
4650 loader->config->Write(sname, svalue);
4651 }
4652 } else if (stype == wxT("Bool")) {
4653 bool bsw = (svalue == wxT("true"));
4654 loader->config->Write(sname, bsw);
4655 } else if (stype == wxT("Int")) {
4656 long lval = strtol(svalue.ToUTF8(), NULL, 10);
4657 loader->config->Write(sname, lval);
4658 } else if (stype == wxT("Float")) {
4659 double dval = strtod(svalue.ToUTF8(), NULL);
4660 loader->config->Write(sname, dval);
4661 }
4662 loader->config->SetPath(wxT("/"));
4663 }
4664 } else if (strcmp(name, "Locations") == 0) {
4665 wxLogMessage(_("Restoring Station Locations"));
4666 wxSafeYield(frame);
4667 loader->locstring = wxT("<StationDataFile>\n");
4668 } else if (strcmp(name, "Location") == 0) {
4669 for (i = 0; atts[i]; i+=2) {
4670 wxString attval = wxString::FromUTF8(atts[i+1]);
4671 if (strcmp(atts[i], "name") == 0) {
4672 tqslTrace("TQSLConfig::xml_restore_start", "Restoring location %s", atts[i+1]);
4673 loader->locstring += wxT("<StationData name=\"") + urlEncode(attval) + wxT("\">\n");
4674 break;
4675 }
4676 }
4677 for (i = 0; atts[i]; i+=2) {
4678 wxString attname = wxString::FromUTF8(atts[i]);
4679 wxString attval = wxString::FromUTF8(atts[i+1]);
4680 if (strcmp(atts[i], "name") != 0) {
4681 loader->locstring += wxT("<") + attname + wxT(">") +
4682 urlEncode(attval) + wxT("</") + attname + wxT(">\n");
4683 }
4684 }
4685 } else if (strcmp(name, "DupeDb") == 0) {
4686 wxLogMessage(_("Restoring QSO records"));
4687 wxSafeYield(frame);
4688 check_tqsl_error(tqsl_beginConverter(&loader->conv));
4689 } else if (strcmp(name, "Dupe") == 0) {
4690 const char *dupekey = NULL;
4691 const char *dupedata = NULL;
4692 for (i = 0; atts[i]; i+=2) {
4693 if (strcmp(atts[i], "key") == 0) {
4694 dupekey = atts[i+1];
4695 }
4696 if (strcmp(atts[i], "data") == 0) {
4697 dupedata = atts[i+1];
4698 }
4699 }
4700 if (dupedata == NULL) {
4701 dupedata = "D"; // Old school dupe record
4702 }
4703 int status = tqsl_putDuplicateRecord(loader->conv, dupekey, dupedata, dupekey ? strlen(dupekey) : 0);
4704 if (status > 0) { // Error writing that record
4705 check_tqsl_error(status);
4706 }
4707 if ((loader->dupes++ % 100000) == 0) {
4708 wxSafeYield(frame);
4709 }
4710 }
4711 }
4712
4713 void
4714 TQSLConfig::xml_restore_end(void *data, const XML_Char *name) {
4715 TQSLConfig* loader = reinterpret_cast<TQSLConfig *> (data);
4716 if (strcmp(name, "SignedCert") == 0) {
4717 loader->signedCert = loader->elementBody.Trim(false);
4718 } else if (strcmp(name, "PrivateKey") == 0) {
4719 loader->privateKey = loader->elementBody.Trim(false);
4720 } else if (strcmp(name, "RootCert") == 0) {
4721 loader->signedCert = loader->elementBody.Trim(false);
4722 restore_root_cert(loader);
4723 } else if (strcmp(name, "CACert") == 0) {
4724 loader->signedCert = loader->elementBody.Trim(false);
4725 restore_ca_cert(loader);
4726 } else if (strcmp(name, "UserCert") == 0) {
4727 restore_user_cert(loader);
4728 } else if (strcmp(name, "Location") == 0) {
4729 loader->locstring += wxT("</StationData>\n");
4730 } else if (strcmp(name, "Locations") == 0) {
4731 loader->locstring += wxT("</StationDataFile>\n");
4732 tqslTrace("TQSLConfig::xml_restore_end", "Merging station locations");
4733 if (tqsl_mergeStationLocations(loader->locstring.ToUTF8()) != 0) {
4734 char buf[500];
4735 strncpy(buf, getLocalizedErrorString().ToUTF8(), sizeof buf);
4736 wxLogError(wxString::Format(wxString(wxT("\t")) + _("Error importing station locations: %hs"), buf));
4737 }
4738 tqslTrace("TQSLConfig::xml_restore_end", "Completed merging station locations");
4739 } else if (strcmp(name, "TQSLSettings") == 0) {
4740 loader->config->Flush(false);
4741 } else if (strcmp(name, "DupeDb") == 0) {
4742 check_tqsl_error(tqsl_converterCommit(loader->conv));
4743 check_tqsl_error(tqsl_endConverter(&loader->conv));
4744 }
4745 loader->elementBody = wxT("");
4746 }
4747
4748 void
4749 TQSLConfig::xml_location_start(void *data, const XML_Char *name, const XML_Char **atts) {
4750 TQSLConfig* parser = reinterpret_cast<TQSLConfig *> (data);
4751 int err;
4752
4753 if (strcmp(name, "StationDataFile") == 0)
4754 return;
4755 if (strcmp(name, "StationData") == 0) {
4756 wxString locname = wxString::FromUTF8(atts[1]);
4757 urlEncode(locname);
4758 if (gzprintf(*parser->outstr, "<Location name=\"%s\"", (const char *)locname.ToUTF8()) < 0)
4759 throw TQSLException(gzerror(*parser->outstr, &err));
4760 }
4761 }
4762 void
4763 TQSLConfig::xml_location_end(void *data, const XML_Char *name) {
4764 TQSLConfig* parser = reinterpret_cast<TQSLConfig *> (data);
4765 int err;
4766 if (strcmp(name, "StationDataFile") == 0)
4767 return;
4768 if (strcmp(name, "StationData") == 0) {
4769 if (gzprintf(*parser->outstr , " />\n") < 0)
4770 throw TQSLException(gzerror(*parser->outstr, &err));
4771 return;
4772 }
4773 // Anything else is a station attribute. Add it to the definition.
4774 parser->elementBody.Trim(false);
4775 parser->elementBody.Trim(true);
4776 urlEncode(parser->elementBody);
4777 if (gzprintf(*parser->outstr, " %s=\"%s\"", name, (const char *)parser->elementBody.ToUTF8()) < 0)
4778 throw TQSLException(gzerror(*parser->outstr, &err));
4779 parser->elementBody = wxT("");
4780 }
4781
4782 void
4783 TQSLConfig::xml_text(void *data, const XML_Char *text, int len) {
4784 TQSLConfig* loader = reinterpret_cast<TQSLConfig *>(data);
4785 char buf[512];
4786 memcpy(buf, text, len);
4787 buf[len] = '\0';
4788 loader->elementBody += wxString::FromUTF8(buf);
4789 }
4790
4791 void
4792 TQSLConfig::RestoreConfig(const gzFile& in) {
4793 tqslTrace("TQSLConfig::RestoreConfig", NULL);
4794 XML_Parser xp = XML_ParserCreate(0);
4795 XML_SetUserData(xp, reinterpret_cast<void *>(this));
4796 XML_SetStartElementHandler(xp, &TQSLConfig::xml_restore_start);
4797 XML_SetEndElementHandler(xp, &TQSLConfig::xml_restore_end);
4798 XML_SetCharacterDataHandler(xp, &TQSLConfig::xml_text);
4799
4800 char buf[4096];
4801 wxBusyCursor wait;
4802 wxLogMessage(_("Restoring Callsign Certificates"));
4803 wxSafeYield(frame);
4804 int rcount = 0;
4805 do {
4806 rcount = gzread(in, buf, sizeof(buf));
4807 if (rcount > 0) {
4808 if (XML_Parse(xp, buf, rcount, 0) == 0) {
4809 wxLogError(_("Error parsing saved configuration file: %hs"), XML_ErrorString(XML_GetErrorCode(xp)));
4810 XML_ParserFree(xp);
4811 return;
4812 }
4813 }
4814 } while (rcount > 0);
4815 if (!gzeof(in)) {
4816 int gerr;
4817 wxLogError(_("Error parsing saved configuration file: %hs"), gzerror(in, &gerr));
4818 XML_ParserFree(xp);
4819 return;
4820 }
4821 if (XML_Parse(xp, "", 0, 1) == 0) {
4822 wxLogError(_("Error parsing saved configuration file: %hs"), XML_ErrorString(XML_GetErrorCode(xp)));
4823 XML_ParserFree(xp);
4824 return;
4825 }
4826 wxLogMessage(_("Restore Complete."));
4827 }
4828
4829 void
4830 TQSLConfig::ParseLocations(gzFile* out, const tQSL_StationDataEnc loc) {
4831 tqslTrace("TQSL::ParseLocations", "loc=%s", loc);
4832 XML_Parser xp = XML_ParserCreate(0);
4833 XML_SetUserData(xp, reinterpret_cast<void *>(this));
4834 XML_SetStartElementHandler(xp, &TQSLConfig::xml_location_start);
4835 XML_SetEndElementHandler(xp, &TQSLConfig::xml_location_end);
4836 XML_SetCharacterDataHandler(xp, &TQSLConfig::xml_text);
4837 outstr = out;
4838
4839 if (XML_Parse(xp, loc, strlen(loc), 1) == 0) {
4840 wxLogError(_("Error parsing station location file: %hs"), XML_ErrorString(XML_GetErrorCode(xp)));
4841 XML_ParserFree(xp);
4842 return;
4843 }
4844 }
4845
4846 void
4847 MyFrame::OnLoadConfig(wxCommandEvent& WXUNUSED(event)) {
4848 #ifdef _WIN32
4849 int fd = -1;
4850 #endif
4851 tqslTrace("MyFrame::OnLoadConfig", NULL);
4852 wxString filename = wxFileSelector(_("Select saved configuration file"), wxT(""),
4853 wxT("tqslconfig.tbk"), wxT("tbk"), _("Saved configuration files (*.tbk)|*.tbk"),
4854 wxFD_OPEN|wxFD_FILE_MUST_EXIST);
4855 if (filename == wxT(""))
4856 return;
4857
4858 gzFile in = 0;
4859 try {
4860 #ifdef _WIN32
4861 wchar_t* lfn = utf8_to_wchar(filename.ToUTF8());
4862 fd = _wopen(lfn, _O_RDONLY|_O_BINARY);
4863 free_wchar(lfn);
4864 if (fd != -1)
4865 in = gzdopen(fd, "rb");
4866 #else
4867 in = gzopen(filename.ToUTF8(), "rb");
4868 #endif
4869 if (!in) {
4870 wxLogError(_("Error opening save file %s: %hs"), filename.c_str(), strerror(errno));
4871 return;
4872 }
4873
4874 TQSLConfig loader;
4875 loader.RestoreConfig(in);
4876 cert_tree->Build(CERTLIST_FLAGS);
4877 loc_tree->Build();
4878 LocTreeReset();
4879 CertTreeReset();
4880 gzclose(in);
4881 }
4882 catch(TQSLException& x) {
4883 wxLogError(_("Restore operation failed: %hs"), x.what());
4884 gzclose(in);
4885 }
4886 }
4887
4888 QSLApp::QSLApp() : wxApp() {
4889 lang = wxLANGUAGE_UNKNOWN;
4890 locale = NULL;
4891 #ifdef __WXMAC__ // Tell wx to put these items on the proper menu
4892 wxApp::s_macAboutMenuItemId = long(tm_h_about);
4893 wxApp::s_macPreferencesMenuItemId = long(tm_f_preferences);
4894 wxApp::s_macExitMenuItemId = long(tm_f_exit);
4895 #endif
4896
4897 wxConfigBase::Set(new wxConfig(wxT("tqslapp")));
4898 }
4899
4900 /*
4901 wxLog *
4902 QSLApp::CreateLogTarget() {
4903 cerr << "called" << endl;
4904 MyFrame *mf = (MyFrame *)GetTopWindow();
4905 if (mf)
4906 return new LogList(mf);
4907 return 0;
4908 }
4909 */
4910
4911 MyFrame *
4912 QSLApp::GUIinit(bool checkUpdates, bool quiet) {
4913 tqslTrace("QSLApp::GUIinit", "checkUpdates=%d", checkUpdates);
4914 int x, y, w, h;
4915 bool maximized;
4916 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
4917 config->Read(wxT("MainWindowX"), &x, 50);
4918 config->Read(wxT("MainWindowY"), &y, 50);
4919 config->Read(wxT("MainWindowWidth"), &w, 800);
4920 config->Read(wxT("MainWindowHeight"), &h, 600);
4921 config->Read(wxT("MainWindowMaximized"), &maximized, false);
4922 if (w < MAIN_WINDOW_MIN_WIDTH) w = MAIN_WINDOW_MIN_WIDTH;
4923 if (h < MAIN_WINDOW_MIN_HEIGHT) w = MAIN_WINDOW_MIN_HEIGHT;
4924
4925 frame = new MyFrame(wxT("TQSL"), x, y, w, h, checkUpdates, quiet, locale);
4926 frame->SetMinSize(wxSize(MAIN_WINDOW_MIN_WIDTH, MAIN_WINDOW_MIN_HEIGHT));
4927 if (maximized)
4928 frame->Maximize();
4929 if (checkUpdates)
4930 frame->FirstTime();
4931 frame->Show(!quiet);
4932 if (checkUpdates && !quiet)
4933 frame->SetFocus();
4934 SetTopWindow(frame);
4935
4936 return frame;
4937 }
4938
4939 // Override OnRun so we can have a last-chance exception handler
4940 // in case something doesn't handle an error.
4941 int
4942 QSLApp::OnRun() {
4943 tqslTrace("QSLApp::OnRun", NULL);
4944 try {
4945 if (m_exitOnFrameDelete == Later)
4946 m_exitOnFrameDelete = Yes;
4947 return MainLoop();
4948 }
4949 catch(TQSLException& x) {
4950 string msg = x.what();
4951 tqslTrace("QSLApp::OnRun", "Last chance handler, string=%s", (const char *)msg.c_str());
4952 cerr << "An exception has occurred! " << msg << endl;
4953 wxLogError(wxT("%hs"), x.what());
4954 exitNow(TQSL_EXIT_TQSL_ERROR, false);
4955 }
4956 return 0;
4957 }
4958
4959 bool
4960 QSLApp::OnInit() {
4961 frame = 0;
4962 long lng = -1;
4963
4964 int major, minor;
4965 if (tqsl_getConfigVersion(&major, &minor)) {
4966 wxMessageBox(getLocalizedErrorString(), _("Error"), wxOK | wxICON_ERROR, frame);
4967 exitNow(TQSL_EXIT_TQSL_ERROR, quiet);
4968 }
4969
4970 wxConfig::Get()->Read(wxT("Language"), &lng, wxLANGUAGE_UNKNOWN);
4971 lang = (wxLanguage) lng;
4972
4973 if (lang == wxLANGUAGE_UNKNOWN) {
4974 lang = wxLANGUAGE_DEFAULT;
4975 }
4976
4977 for (lng = 0; (unsigned) lng < sizeof (langNames); lng++) {
4978 if (lang == langIds[lng])
4979 break;
4980 }
4981
4982 #ifdef __WXGTK__
4983 // Add locale search path for where we install language files
4984 locale->AddCatalogLookupPathPrefix(wxT("/usr/local/share/locale"));
4985 #endif
4986 if (wxLocale::IsAvailable(lang)) {
4987 if (locale) delete locale;
4988 locale = new wxLocale(lang);
4989 if (!locale)
4990 locale = new wxLocale(wxLANGUAGE_DEFAULT);
4991 } else {
4992 wxLogError(wxT("This language is not supported by the system."));
4993 locale = new wxLocale(wxLANGUAGE_DEFAULT);
4994 }
4995
4996 // Add a subdirectory for language files
4997 locale->AddCatalogLookupPathPrefix(wxT("lang"));
4998
4999 // Initialize the catalogs we'll be using
5000 locale->AddCatalog(wxT("tqslapp"));
5001 locale->AddCatalog(wxT("wxstd"));
5002
5003 // this catalog is installed in standard location on Linux systems and
5004 // shows that you may make use of the standard message catalogs as well
5005 //
5006 // If it's not installed on your system, it is just silently ignored
5007 #ifdef __LINUX__
5008 {
5009 wxLogNull nolog;
5010 locale->AddCatalog(wxT("fileutils"));
5011 }
5012 #endif
5013
5014 wxFileSystem::AddHandler(new tqslInternetFSHandler());
5015 // Allow JAWS for windows to speak the context-sensitive help.
5016 wxHelpProvider::Set(new wxSimpleHelpProvider());
5017
5018 //short circuit if no arguments
5019
5020 if (argc <= 1) {
5021 GUIinit(true, quiet);
5022 return true;
5023 }
5024
5025 tQSL_Location loc = 0;
5026 wxString locname;
5027 bool suppressdate = false;
5028 int action = TQSL_ACTION_UNSPEC;
5029 bool upload = false;
5030 char *password = NULL;
5031 char *defcall = NULL;
5032 wxString infile(wxT(""));
5033 wxString outfile(wxT(""));
5034 wxString importfile(wxT(""));
5035 wxString diagfile(wxT(""));
5036
5037 wxCmdLineParser parser;
5038
5039 #if wxMAJOR_VERSION > 2 || (wxMAJOR_VERSION == 2 && wxMINOR_VERSION == 9)
5040 #define arg(x) (x)
5041 #define i18narg(x) (x)
5042 #else
5043 #define arg(x) wxT(x)
5044 #define i18narg(x) _(x)
5045 #endif
5046 static const wxCmdLineEntryDesc cmdLineDesc[] = {
5047 { wxCMD_LINE_OPTION, arg("a"), arg("action"), i18narg("Specify dialog action - abort, all, compliant or ask") },
5048 { wxCMD_LINE_OPTION, arg("b"), arg("begindate"), i18narg("Specify start date for QSOs to sign") },
5049 { wxCMD_LINE_OPTION, arg("c"), arg("callsign"), i18narg("Specify default callsign for log signing") },
5050 { wxCMD_LINE_OPTION, arg("e"), arg("enddate"), i18narg("Specify end date for QSOs to sign") },
5051 { wxCMD_LINE_SWITCH, arg("d"), arg("nodate"), i18narg("Suppress date range dialog") },
5052 { wxCMD_LINE_OPTION, arg("i"), arg("import"), i18narg("Import a certificate file (.p12 or .tq6)") },
5053 { wxCMD_LINE_OPTION, arg("l"), arg("location"), i18narg("Selects Station Location") },
5054 { wxCMD_LINE_SWITCH, arg("s"), arg("editlocation"), i18narg("Edit (if used with -l) or create Station Location") },
5055 { wxCMD_LINE_OPTION, arg("o"), arg("output"), i18narg("Output file name (defaults to input name minus extension plus .tq8") },
5056 { wxCMD_LINE_SWITCH, arg("u"), arg("upload"), i18narg("Upload after signing instead of saving") },
5057 { wxCMD_LINE_SWITCH, arg("x"), arg("batch"), i18narg("Exit after processing log (otherwise start normally)") },
5058 { wxCMD_LINE_OPTION, arg("p"), arg("password"), i18narg("Password for the signing key") },
5059 { wxCMD_LINE_SWITCH, arg("q"), arg("quiet"), i18narg("Quiet Mode - same behavior as -x") },
5060 { wxCMD_LINE_OPTION, arg("t"), arg("diagnose"), i18narg("File name for diagnostic tracking log") },
5061 { wxCMD_LINE_SWITCH, arg("v"), arg("version"), i18narg("Display the version information and exit") },
5062 { wxCMD_LINE_SWITCH, arg("n"), arg("updates"), i18narg("Check for updates to tqsl and the configuration file") },
5063 { wxCMD_LINE_SWITCH, arg("h"), arg("help"), i18narg("Display command line help"), wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
5064 { wxCMD_LINE_PARAM, NULL, NULL, i18narg("Input ADIF or Cabrillo log file to sign"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
5065 { wxCMD_LINE_NONE }
5066 };
5067
5068 // Lowercase command options
5069 origCommandLine = argv[0];
5070 for (int i = 1; i < argc; i++) {
5071 origCommandLine += wxT(" ");
5072 origCommandLine += argv[i];
5073 #ifdef _WIN32
5074 if ((const wxChar *)argv[i])
5075 if (argv[i][0] == wxT('-') || argv[i][0] == wxT('/'))
5076 if (wxIsalpha(argv[i][1]) && wxIsupper(argv[i][1]))
5077 argv[i][1] = wxTolower(argv[i][1]);
5078 #endif
5079 }
5080
5081 parser.SetCmdLine(argc, argv);
5082 parser.SetDesc(cmdLineDesc);
5083 // only allow "-" for options, otherwise "/path/something.adif"
5084 // is parsed as "-path"
5085 //parser.SetSwitchChars(wxT("-")); //by default, this is '-' on Unix, or '-' or '/' on Windows. We should respect the Win32 conventions, but allow the cross-platform Unix one for cross-plat loggers
5086 int parseStatus = parser.Parse(true);
5087 if (parseStatus == -1) { // said "-h"
5088 return false;
5089 }
5090 // Always display TQSL version
5091 if ((!parser.Found(wxT("n"))) || parser.Found(wxT("v"))) {
5092 cerr << "TQSL Version " VERSION " " BUILD "\n";
5093 }
5094 if (parseStatus != 0) {
5095 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5096 }
5097
5098 // version already displayed - just exit
5099 if (parser.Found(wxT("v"))) {
5100 return false;
5101 }
5102
5103 if (parser.Found(wxT("x")) || parser.Found(wxT("q"))) {
5104 quiet = true;
5105 wxLog::SetActiveTarget(new LogStderr());
5106 }
5107
5108 if (parser.Found(wxT("t"), &diagfile)) {
5109 if (tqsl_openDiagFile(diagfile.ToUTF8())) {
5110 cerr << "Error opening diagnostic log " << diagfile.ToUTF8() << ": " << strerror(errno) << endl;
5111 } else {
5112 wxString about = getAbout();
5113 tqslTrace(NULL, "TQSL Diagnostics\r\n%s\n\n", (const char *)about.ToUTF8());
5114 tqslTrace(NULL, "Command Line: %s\r\n", (const char *)origCommandLine.ToUTF8());
5115 tqsl_init();
5116 tqslTrace(NULL, "Working Directory: %s\r\n", tQSL_BaseDir);
5117 }
5118 }
5119
5120 tqsl_init(); // Init tqsllib
5121 // check for logical command switches
5122 if (parser.Found(wxT("o")) && parser.Found(wxT("u"))) {
5123 cerr << "Option -o cannot be combined with -u" << endl;
5124 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5125 }
5126 if ((parser.Found(wxT("o")) || parser.Found(wxT("u"))) && parser.Found(wxT("s"))) {
5127 cerr << "Option -s cannot be combined with -u or -o" << endl;
5128 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5129 }
5130 if (parser.Found(wxT("s")) && parser.GetParamCount() > 0) {
5131 cerr << "Option -s cannot be combined with an input file" << endl;
5132 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5133 }
5134
5135 // Request to check for new versions of tqsl/config/certs
5136 if (parser.Found(wxT("n"))) {
5137 if (parser.Found(wxT("i")) || parser.Found(wxT("o")) ||
5138 parser.Found(wxT("s")) || parser.Found(wxT("u"))) {
5139 cerr << "Option -n cannot be combined with any other options" << endl;
5140 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5141 }
5142 frame = GUIinit(false, true);
5143 frame->Show(false);
5144 // Check for updates then bail out.
5145 wxLog::SetActiveTarget(new LogStderr());
5146 frame->DoUpdateCheck(false, true);
5147 return(false);
5148 }
5149
5150 frame = GUIinit(!quiet, quiet);
5151 if (quiet) {
5152 wxLog::SetActiveTarget(new LogStderr());
5153 frame->Show(false);
5154 }
5155
5156 if (parser.Found(wxT("l"), &locname)) {
5157 locname.Trim(true); // clean up whitespace
5158 locname.Trim(false);
5159 tqsl_endStationLocationCapture(&loc);
5160 if (tqsl_getStationLocation(&loc, locname.ToUTF8())) {
5161 if (quiet) {
5162 wxLogError(getLocalizedErrorString());
5163 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5164 } else {
5165 wxMessageBox(getLocalizedErrorString(), ErrorTitle, wxOK | wxICON_ERROR | wxCENTRE, frame);
5166 return false;
5167 }
5168 }
5169 }
5170
5171 wxString call;
5172 if (parser.Found(wxT("c"), &call)) {
5173 call.Trim(true);
5174 call.Trim(false);
5175 defcall = strdup(call.MakeUpper().ToUTF8());
5176 }
5177
5178 wxString pwd;
5179 if (parser.Found(wxT("p"), &pwd)) {
5180 password = strdup(pwd.ToUTF8());
5181 utf8_to_ucs2(password, unipwd, sizeof unipwd);
5182 }
5183 if (parser.Found(wxT("o"), &outfile)) {
5184 }
5185
5186 if (parser.Found(wxT("d"))) {
5187 suppressdate = true;
5188 }
5189 wxString start = wxT("");
5190 wxString end = wxT("");
5191 tQSL_Date* startdate = NULL;
5192 tQSL_Date* enddate = NULL;
5193 tQSL_Date s, e;
5194 if (parser.Found(wxT("b"), &start)) {
5195 if (start.Trim() == wxT("")) {
5196 startdate = NULL;
5197 } else if (tqsl_initDate(&s, start.ToUTF8()) || !tqsl_isDateValid(&s)) {
5198 if (quiet) {
5199 wxLogError(_("Start date of %s is invalid"), start.c_str());
5200 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5201 } else {
5202 wxMessageBox(wxString::Format(_("Start date of %s is invalid"), start.c_str()), ErrorTitle, wxOK | wxICON_ERROR | wxCENTRE, frame);
5203 return false;
5204 }
5205 }
5206 startdate = &s;
5207 }
5208 if (parser.Found(wxT("e"), &end)) {
5209 if (end.Trim() == wxT("")) {
5210 enddate = NULL;
5211 } else if (tqsl_initDate(&e, end.ToUTF8()) || !tqsl_isDateValid(&e)) {
5212 if (quiet) {
5213 wxLogError(_("End date of %s is invalid"), end.c_str());
5214 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5215 } else {
5216 wxMessageBox(wxString::Format(_("End date of %s is invalid"), end.c_str()), ErrorTitle, wxOK | wxICON_ERROR | wxCENTRE, frame);
5217 return false;
5218 }
5219 }
5220 enddate = &e;
5221 }
5222
5223 wxString act;
5224 if (parser.Found(wxT("a"), &act)) {
5225 if (!act.CmpNoCase(wxT("abort"))) {
5226 action = TQSL_ACTION_ABORT;
5227 } else if (!act.CmpNoCase(wxT("compliant"))) {
5228 action = TQSL_ACTION_NEW;
5229 } else if (!act.CmpNoCase(wxT("all"))) {
5230 action = TQSL_ACTION_ALL;
5231 } else if (!act.CmpNoCase(wxT("ask"))) {
5232 action = TQSL_ACTION_ASK;
5233 } else {
5234 char tmp[100];
5235 strncpy(tmp, (const char *)act.ToUTF8(), sizeof tmp);
5236 tmp[sizeof tmp -1] = '\0';
5237 if (quiet)
5238 wxLogMessage(_("The -a parameter %hs is not recognized"), tmp);
5239 else
5240 cerr << "The action parameter " << tmp << " is not recognized" << endl;
5241 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5242 }
5243 }
5244 if (parser.Found(wxT("u"))) {
5245 upload = true;
5246 }
5247 if (parser.Found(wxT("s"))) {
5248 // Add/Edit station location
5249 if (loc == 0) {
5250 if (tqsl_initStationLocationCapture(&loc)) {
5251 wxLogError(getLocalizedErrorString());
5252 }
5253 AddEditStationLocation(loc, true);
5254 } else {
5255 AddEditStationLocation(loc, false, _("Edit Station Location"));
5256 }
5257 tqsl_endStationLocationCapture(&loc);
5258 return false;
5259 }
5260 if (parser.GetParamCount() > 0) {
5261 infile = parser.GetParam(0);
5262 }
5263
5264 wxString path, name, ext;
5265 wxFileName::SplitPath(infile, &path, &name, &ext);
5266
5267 // Handle "-i" (import cert), or bare cert file on command line
5268
5269 bool tq6File = false;
5270 if (!wxIsEmpty(infile)) {
5271 if (ext.CmpNoCase(wxT("tq6")) == 0) {
5272 tq6File = true;
5273 }
5274 }
5275 if (parser.Found(wxT("i"), &infile) && (!wxIsEmpty(infile))) {
5276 tq6File = true;
5277 }
5278
5279 if (tq6File) {
5280 infile.Trim(true).Trim(false);
5281 notifyData nd;
5282 if (tqsl_importTQSLFile(infile.ToUTF8(), notifyImport, &nd)) {
5283 if (tQSL_Error != TQSL_CERT_ERROR) {
5284 wxLogError(getLocalizedErrorString());
5285 }
5286 } else {
5287 wxLogMessage(nd.Message());
5288 if (tQSL_ImportSerial != 0) {
5289 wxString status;
5290 frame->CheckCertStatus(tQSL_ImportSerial, status); // Update from LoTW's "CRL"
5291 tqsl_setCertificateStatus(tQSL_ImportSerial, (const char *)status.ToUTF8());
5292 }
5293 if (tQSL_ImportCall[0] != '\0' && tQSL_ImportSerial != 0 && tqsl_getCertificateStatus(tQSL_ImportSerial) == TQSL_CERT_STATUS_OK) {
5294 get_certlist(tQSL_ImportCall, 0, true, true, true); // Get any expired/superceded ones for this call
5295 for (int i = 0; i < ncerts; i++) {
5296 long serial = 0;
5297 int keyonly = false;
5298 tqsl_getCertificateKeyOnly(certlist[i], &keyonly);
5299 if (keyonly) {
5300 if (tQSL_ImportSerial != 0) { // A full cert for this was imported
5301 tqsl_deleteCertificate(certlist[i]);
5302 }
5303 continue;
5304 }
5305 if (tqsl_getCertificateSerial(certlist[i], &serial)) {
5306 continue;
5307 }
5308 if (serial == tQSL_ImportSerial) {
5309 continue;
5310 }
5311 // This is not the one we just imported
5312 int sup, exp;
5313 if (tqsl_isCertificateSuperceded(certlist[i], &sup) == 0 && sup) {
5314 tqsl_deleteCertificate(certlist[i]);
5315 } else if (tqsl_isCertificateExpired(certlist[i], &exp) == 0 && exp) {
5316 tqsl_deleteCertificate(certlist[i]);
5317 }
5318 }
5319 }
5320 frame->cert_tree->Build(CERTLIST_FLAGS);
5321 wxString call = wxString::FromUTF8(tQSL_ImportCall);
5322 wxString pending = wxConfig::Get()->Read(wxT("RequestPending"));
5323 pending.Replace(call, wxT(""), true);
5324 wxString rest;
5325 while (pending.StartsWith(wxT(","), &rest))
5326 pending = rest;
5327 while (pending.EndsWith(wxT(","), &rest))
5328 pending = rest;
5329 wxConfig::Get()->Write(wxT("RequestPending"), pending);
5330 }
5331 return(true);
5332 }
5333
5334 // We need a logfile, else there's nothing to do.
5335 if (wxIsEmpty(infile)) { // Nothing to sign
5336 if (quiet) {
5337 wxLogError(_("No logfile to sign!"));
5338 exitNow(TQSL_EXIT_COMMAND_ERROR, quiet);
5339 return false;
5340 }
5341 return true;
5342 }
5343
5344 bool editAdif = DEFAULT_ADIF_EDIT;
5345 wxConfig::Get()->Read(wxT("AdifEdit"), &editAdif, DEFAULT_ADIF_EDIT);
5346
5347 // If it's an ADIF file, invoke the editor if that's the only argument
5348 // unless we're running in batch mode
5349 if (editAdif && !quiet && !wxIsEmpty(infile) && (ext.CmpNoCase(wxT("adi")) || ext.CmpNoCase(wxT("adif")))) {
5350 QSORecordList recs;
5351 loadQSOfile(infile, recs);
5352 wxMessageBox(_("Warning: The TQSL ADIF editor only processes a limited number of ADIF fields.\n\nUsing the editor on an ADIF file can cause QSO details to be lost!"), _("Warning"), wxOK | wxICON_EXCLAMATION, frame);
5353 try {
5354 QSODataDialog dial(frame, infile, frame->help, &recs);
5355 dial.ShowModal();
5356 } catch(TQSLException& x) {
5357 wxLogError(wxT("%hs"), x.what());
5358 }
5359 exitNow(TQSL_EXIT_SUCCESS, quiet);
5360 }
5361
5362 // Assume that it's a log to sign
5363 if (loc == 0) {
5364 try {
5365 int n;
5366 check_tqsl_error(tqsl_initStationLocationCapture(&loc));
5367 check_tqsl_error(tqsl_getNumStationLocations(loc, &n));
5368 if (n != 1) {
5369 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
5370 loc = frame->SelectStationLocation(_("Select Station Location for Signing"));
5371 } else {
5372 // There's only one station location. Use that and don't prompt.
5373 char deflocn[512];
5374 check_tqsl_error(tqsl_getStationLocationName(loc, 0, deflocn, sizeof deflocn));
5375 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
5376 check_tqsl_error(tqsl_getStationLocation(&loc, deflocn));
5377 }
5378 }
5379 catch(TQSLException& x) {
5380 wxLogError(wxT("%hs"), x.what());
5381 if (quiet)
5382 exitNow(TQSL_EXIT_CANCEL, quiet);
5383 }
5384 }
5385 // If no location specified and not chosen, can't sign. Exit.
5386 if (loc == 0) {
5387 if (quiet)
5388 exitNow(TQSL_EXIT_CANCEL, quiet);
5389 return false;
5390 }
5391 if (!wxIsEmpty(outfile)) {
5392 path = outfile;
5393 } else {
5394 if (!wxIsEmpty(path))
5395 path += wxT("/");
5396 path += name + wxT(".tq8");
5397 }
5398 if (upload) {
5399 try {
5400 int val = frame->UploadLogFile(loc, infile, true, suppressdate, startdate, enddate, action, password, defcall);
5401 if (quiet)
5402 exitNow(val, quiet);
5403 else
5404 return true; // Run the GUI
5405 } catch(TQSLException& x) {
5406 wxString s;
5407 wxString err = wxString::FromUTF8(x.what());
5408 if (err.Find(infile) == wxNOT_FOUND) {
5409 if (!infile.empty())
5410 s = infile + wxT(": ");
5411 }
5412 s += err;
5413 wxLogError(wxT("%s"), (const char *)s.c_str());
5414 if (quiet)
5415 exitNow(TQSL_EXIT_LIB_ERROR, quiet);
5416 else
5417 return true;
5418 }
5419 } else {
5420 try {
5421 int val = frame->ConvertLogFile(loc, infile, path, true, suppressdate, startdate, enddate, action, password, defcall);
5422 if (quiet)
5423 exitNow(val, quiet);
5424 else
5425 return true;
5426 } catch(TQSLException& x) {
5427 wxString s;
5428 wxString err = wxString::FromUTF8(x.what());
5429 if (err.Find(infile) == wxNOT_FOUND) {
5430 if (infile != wxT(""))
5431 s = infile + wxT(": ");
5432 }
5433 s += err;
5434 wxLogError(wxT("%s"), (const char *)s.c_str());
5435 if (quiet)
5436 exitNow(TQSL_EXIT_LIB_ERROR, quiet);
5437 else
5438 return true;
5439 }
5440 }
5441 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
5442 if (quiet)
5443 exitNow(TQSL_EXIT_SUCCESS, quiet);
5444 return true;
5445 }
5446
5447 void MyFrame::FirstTime(void) {
5448 tqslTrace("MyFrame::FirstTime", NULL);
5449 if (wxConfig::Get()->Read(wxT("HasRun")) == wxT("")) {
5450 wxConfig::Get()->Write(wxT("HasRun"), wxT("yes"));
5451 DisplayHelp();
5452 wxMessageBox(_("Please review the introductory documentation before using this program."),
5453 _("Notice"), wxOK | wxICON_INFORMATION, this);
5454 }
5455 int ncerts = cert_tree->Build(CERTLIST_FLAGS);
5456 CertTreeReset();
5457 if (ncerts == 0) {
5458 wxString msg = _("You have no callsign certificate installed on this computer with which to sign log submissions.");
5459 msg += wxT("\n");
5460 msg += _("Would you like to request a callsign certificate now?");
5461 if (wxMessageBox(msg, _("Alert"), wxYES_NO | wxICON_QUESTION, this) == wxYES) {
5462 wxCommandEvent e;
5463 CRQWizard(e);
5464 }
5465 }
5466 wxString pending = wxConfig::Get()->Read(wxT("RequestPending"));
5467 wxStringTokenizer tkz(pending, wxT(","));
5468 while (tkz.HasMoreTokens()) {
5469 wxString pend = tkz.GetNextToken();
5470 bool found = false;
5471 tQSL_Cert *certs;
5472 int ncerts = 0;
5473 if (!tqsl_selectCertificates(&certs, &ncerts, pend.ToUTF8(), 0, 0, 0, TQSL_SELECT_CERT_WITHKEYS)) {
5474 for (int i = 0; i < ncerts; i++) {
5475 int keyonly;
5476 if (!tqsl_getCertificateKeyOnly(certs[i], &keyonly)) {
5477 if (!found && keyonly)
5478 found = true;
5479 }
5480 }
5481 tqsl_freeCertificateList(certs, ncerts);
5482 }
5483
5484 if (!found) {
5485 // Remove this call from the list of pending certificate requests
5486 wxString p = wxConfig::Get()->Read(wxT("RequestPending"));
5487 p.Replace(pend, wxT(""), true);
5488 wxString rest;
5489 while (p.StartsWith(wxT(","), &rest))
5490 p = rest;
5491 while (p.EndsWith(wxT(","), &rest))
5492 p = rest;
5493 wxConfig::Get()->Write(wxT("RequestPending"), p);
5494 }
5495 }
5496
5497 if (ncerts > 0) {
5498 TQ_WXCOOKIE cookie;
5499 wxTreeItemId it = cert_tree->GetFirstChild(cert_tree->GetRootItem(), cookie);
5500 while (it.IsOk()) {
5501 if (cert_tree->GetItemText(it) == wxT("Test Certificate Authority")) {
5502 wxMessageBox(wxT("You must delete your beta-test certificates (the ones\n")
5503 wxT("listed under \"Test Certificate Authority\") to ensure proprer\n")
5504 wxT("operation of the TrustedQSL software."), wxT("Warning"), wxOK, this);
5505 break;
5506 }
5507 it = cert_tree->GetNextChild(cert_tree->GetRootItem(), cookie);
5508 }
5509 }
5510 // Copy tqslcert preferences to tqsl unless already done.
5511 if (wxConfig::Get()->Read(wxT("PrefsMigrated")) == wxT("")) {
5512 wxConfig::Get()->Write(wxT("PrefsMigrated"), wxT("yes"));
5513 tqslTrace("MyFrame::FirstTime", "Migrating preferences from tqslcert");
5514 wxConfig* certconfig = new wxConfig(wxT("tqslcert"));
5515
5516 wxString name, gname;
5517 long context;
5518 wxString svalue;
5519 long lvalue;
5520 bool bvalue;
5521 double dvalue;
5522 wxArrayString groupNames;
5523
5524 groupNames.Add(wxT("/"));
5525 bool more = certconfig->GetFirstGroup(gname, context);
5526 while (more) {
5527 groupNames.Add(wxT("/") + gname);
5528 more = certconfig->GetNextGroup(gname, context);
5529 }
5530
5531 for (unsigned i = 0; i < groupNames.GetCount(); i++) {
5532 certconfig->SetPath(groupNames[i]);
5533 wxConfig::Get()->SetPath(groupNames[i]);
5534 more = certconfig->GetFirstEntry(name, context);
5535 while (more) {
5536 wxConfigBase::EntryType etype = certconfig->GetEntryType(name);
5537 switch (etype) {
5538 case wxConfigBase::Type_Unknown:
5539 case wxConfigBase::Type_String:
5540 certconfig->Read(name, &svalue);
5541 wxConfig::Get()->Write(name, svalue);
5542 break;
5543 case wxConfigBase::Type_Boolean:
5544 certconfig->Read(name, &bvalue);
5545 wxConfig::Get()->Write(name, bvalue);
5546 break;
5547 case wxConfigBase::Type_Integer:
5548 certconfig->Read(name, &lvalue);
5549 wxConfig::Get()->Write(name, lvalue);
5550 break;
5551 case wxConfigBase::Type_Float:
5552 certconfig->Read(name, &dvalue);
5553 wxConfig::Get()->Write(name, dvalue);
5554 break;
5555 }
5556 more = certconfig->GetNextEntry(name, context);
5557 }
5558 }
5559 delete certconfig;
5560 wxConfig::Get()->SetPath(wxT("/"));
5561 wxConfig::Get()->Flush();
5562 }
5563 // Find and report conflicting mode maps
5564 init_modes();
5565 // Run automatic check for updates - except for wx 2.9, which breaks threading.
5566 wxConfig *config = reinterpret_cast<wxConfig *>(wxConfig::Get());
5567 if (config->Read(wxT("AutoUpdateCheck"), true)) {
5568 DoUpdateCheck(true, false);
5569 }
5570 return;
5571 }
5572
5573 wxMenu *
5574 makeCertificateMenu(bool enable, bool keyonly, const char *callsign) {
5575 tqslTrace("makeCertificateMenu", "enable=%d, keyonly=%d", enable, keyonly);
5576 wxMenu *c_menu = new wxMenu;
5577 c_menu->Append(tc_c_Properties, _("Display Callsign Certificate &Properties"));
5578 c_menu->Enable(tc_c_Properties, enable);
5579 c_menu->AppendSeparator();
5580 c_menu->Append(tc_c_Load, _("&Load Callsign Certificate from File"));
5581 c_menu->Append(tc_c_Export, _("&Save Callsign Certificate to File..."));
5582 c_menu->Enable(tc_c_Export, enable);
5583 if (!keyonly) {
5584 c_menu->AppendSeparator();
5585 c_menu->Append(tc_c_New, _("Request &New Callsign Certificate..."));
5586 c_menu->Append(tc_c_Renew, _("&Renew Callsign Certificate"));
5587 c_menu->Enable(tc_c_Renew, enable);
5588 } else {
5589 c_menu->AppendSeparator();
5590 }
5591 c_menu->Append(tc_c_Delete, _("&Delete Callsign Certificate"));
5592 c_menu->Enable(tc_c_Delete, enable);
5593 c_menu->AppendSeparator();
5594
5595 int ncalls = 0;
5596 tqsl_getDeletedCallsignCertificates(NULL, &ncalls, callsign);
5597 c_menu->Append(tc_c_Undelete, _("Restore Deleted Callsign Certificate"));
5598 c_menu->Enable(tc_c_Undelete, ncalls > 0);
5599
5600 return c_menu;
5601 }
5602
5603 wxMenu *
5604 makeLocationMenu(bool enable) {
5605 tqslTrace("makeLocationMenu", "enable=%d", enable);
5606 wxMenu *loc_menu = new wxMenu;
5607 loc_menu->Append(tl_c_Properties, _("&Properties"));
5608 loc_menu->Enable(tl_c_Properties, enable);
5609 stn_menu->Enable(tm_s_Properties, enable);
5610 loc_menu->Append(tl_c_Edit, _("&Edit"));
5611 loc_menu->Enable(tl_c_Edit, enable);
5612 loc_menu->Append(tl_c_Delete, _("&Delete"));
5613 loc_menu->Enable(tl_c_Delete, enable);
5614 return loc_menu;
5615 }
5616
5617 /////////// Frame /////////////
5618
5619 void MyFrame::OnLoadCertificateFile(wxCommandEvent& WXUNUSED(event)) {
5620 tqslTrace("MyFrame::OnLoadCertificateFile", NULL);
5621 LoadCertWiz lcw(this, help, _("Load Certificate File"));
5622 lcw.RunWizard();
5623 if (tQSL_ImportCall[0] != '\0') { // If a user cert was imported
5624 if (tQSL_ImportSerial != 0) {
5625 wxString status;
5626 CheckCertStatus(tQSL_ImportSerial, status); // Update from LoTW's "CRL"
5627 tqsl_setCertificateStatus(tQSL_ImportSerial, (const char *)status.ToUTF8());
5628 }
5629 if (tQSL_ImportCall[0] != '\0' && tQSL_ImportSerial != 0 && tqsl_getCertificateStatus(tQSL_ImportSerial) == TQSL_CERT_STATUS_OK) {
5630 get_certlist(tQSL_ImportCall, 0, true, true, true); // Get any superceded ones for this call
5631 for (int i = 0; i < ncerts; i++) {
5632 long serial = 0;
5633 int keyonly = false;
5634 tqsl_getCertificateKeyOnly(certlist[i], &keyonly);
5635 if (keyonly) {
5636 tqsl_deleteCertificate(certlist[i]);
5637 }
5638 if (tqsl_getCertificateSerial(certlist[i], &serial)) {
5639 continue;
5640 }
5641 if (serial == tQSL_ImportSerial) { // Don't delete the one we just imported
5642 continue;
5643 }
5644 // This is not the one we just imported
5645 int sup, exp;
5646 if (tqsl_isCertificateSuperceded(certlist[i], &sup) == 0 && sup) {
5647 tqsl_deleteCertificate(certlist[i]);
5648 } else if (tqsl_isCertificateExpired(certlist[i], &exp) == 0 && exp) {
5649 tqsl_deleteCertificate(certlist[i]);
5650 }
5651 }
5652 }
5653 }
5654 cert_tree->Build(CERTLIST_FLAGS);
5655 CertTreeReset();
5656 }
5657
5658 void MyFrame::CRQWizardRenew(wxCommandEvent& event) {
5659 tqslTrace("MyFrame::CRQWizardRenew", NULL);
5660 CertTreeItemData *data = reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(cert_tree->GetSelection()));
5661 req = 0;
5662 tQSL_Cert cert;
5663 wxString callSign, name, address1, address2, city, state, postalCode,
5664 country, emailAddress;
5665 if (data != NULL && (cert = data->getCert()) != 0) { // Should be true
5666 char buf[256];
5667 req = new TQSL_CERT_REQ;
5668 if (!tqsl_getCertificateIssuerOrganization(cert, buf, sizeof buf))
5669 strncpy(req->providerName, buf, sizeof req->providerName);
5670 if (!tqsl_getCertificateIssuerOrganizationalUnit(cert, buf, sizeof buf))
5671 strncpy(req->providerUnit, buf, sizeof req->providerUnit);
5672 if (!tqsl_getCertificateCallSign(cert, buf, sizeof buf)) {
5673 callSign = wxString::FromUTF8(buf);
5674 strncpy(req->callSign, callSign.ToUTF8(), sizeof req->callSign);
5675 }
5676 if (!tqsl_getCertificateAROName(cert, buf, sizeof buf)) {
5677 name = wxString::FromUTF8(buf);
5678 strncpy(req->name, name.ToUTF8(), sizeof req->name);
5679 }
5680 tqsl_getCertificateDXCCEntity(cert, &(req->dxccEntity));
5681 tqsl_getCertificateQSONotBeforeDate(cert, &(req->qsoNotBefore));
5682 tqsl_getCertificateQSONotAfterDate(cert, &(req->qsoNotAfter));
5683 if (!tqsl_getCertificateEmailAddress(cert, buf, sizeof buf)) {
5684 emailAddress = wxString::FromUTF8(buf);
5685 strncpy(req->emailAddress, emailAddress.ToUTF8(), sizeof req->emailAddress);
5686 }
5687 if (!tqsl_getCertificateRequestAddress1(cert, buf, sizeof buf)) {
5688 address1 = wxString::FromUTF8(buf);
5689 strncpy(req->address1, address1.ToUTF8(), sizeof req->address1);
5690 }
5691 if (!tqsl_getCertificateRequestAddress2(cert, buf, sizeof buf)) {
5692 address2 = wxString::FromUTF8(buf);
5693 strncpy(req->address2, address2.ToUTF8(), sizeof req->address2);
5694 }
5695 if (!tqsl_getCertificateRequestCity(cert, buf, sizeof buf)) {
5696 city = wxString::FromUTF8(buf);
5697 strncpy(req->city, city.ToUTF8(), sizeof req->city);
5698 }
5699 if (!tqsl_getCertificateRequestState(cert, buf, sizeof buf)) {
5700 state = wxString::FromUTF8(buf);
5701 strncpy(req->state, state.ToUTF8(), sizeof req->state);
5702 }
5703 if (!tqsl_getCertificateRequestPostalCode(cert, buf, sizeof buf)) {
5704 postalCode = wxString::FromUTF8(buf);
5705 strncpy(req->postalCode, postalCode.ToUTF8(), sizeof req->postalCode);
5706 }
5707 if (!tqsl_getCertificateRequestCountry(cert, buf, sizeof buf)) {
5708 country = wxString::FromUTF8(buf);
5709 strncpy(req->country, country.ToUTF8(), sizeof req->country);
5710 }
5711 }
5712 CRQWizard(event);
5713 if (req)
5714 delete req;
5715 req = 0;
5716 }
5717
5718 // Delete an abandoned/failed cert request
5719 static void deleteRequest(const char *callsign, int dxccEntity) {
5720 int savedError = tQSL_Error;
5721 free_certlist();
5722 tqsl_selectCertificates(&certlist, &ncerts, callsign, dxccEntity, 0, 0, TQSL_SELECT_CERT_WITHKEYS);
5723 int ko;
5724 for (int i = 0; i < ncerts; i ++) {
5725 if (!tqsl_getCertificateKeyOnly(certlist[i], &ko) && ko) {
5726 if (tqsl_deleteCertificate(certlist[i])) {
5727 wxLogError(getLocalizedErrorString());
5728 }
5729 certlist = NULL; // Invalidated in deleteCertificate flow
5730 ncerts = 0;
5731 tQSL_Error = savedError;
5732 return;
5733 }
5734 }
5735 tQSL_Error = savedError;
5736 return;
5737 }
5738
5739 void MyFrame::CRQWizard(wxCommandEvent& event) {
5740 tqslTrace("MyFrame::CRQWizard", NULL);
5741 char renew = (req != 0) ? 1 : 0;
5742 tQSL_Cert cert = (renew ? (reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(cert_tree->GetSelection()))->getCert()) : 0);
5743 CRQWiz wiz(req, cert, this, help, renew ? _("Renew a Callsign Certificate") : _("Request a new Callsign Certificate"));
5744 int retval = 0;
5745 /*
5746 CRQ_ProviderPage *prov = new CRQ_ProviderPage(wiz, req);
5747 CRQ_IntroPage *intro = new CRQ_IntroPage(wiz, req);
5748 CRQ_NamePage *name = new CRQ_NamePage(wiz, req);
5749 CRQ_EmailPage *email = new CRQ_EmailPage(wiz, req);
5750 wxSize size = prov->GetSize();
5751 if (intro->GetSize().GetWidth() > size.GetWidth())
5752 size = intro->GetSize();
5753 if (name->GetSize().GetWidth() > size.GetWidth())
5754 size = name->GetSize();
5755 if (email->GetSize().GetWidth() > size.GetWidth())
5756 size = email->GetSize();
5757 CRQ_PasswordPage *pw = new CRQ_PasswordPage(wiz);
5758 CRQ_SignPage *sign = new CRQ_SignPage(wiz, size, &(prov->provider));
5759 wxWizardPageSimple::Chain(prov, intro);
5760 wxWizardPageSimple::Chain(intro, name);
5761 wxWizardPageSimple::Chain(name, email);
5762 wxWizardPageSimple::Chain(email, pw);
5763 if (renew)
5764 sign->cert = ;
5765 else
5766 wxWizardPageSimple::Chain(pw, sign);
5767
5768 wiz.SetPageSize(size);
5769
5770 */
5771
5772 if (wiz.RunWizard()) {
5773 // Save or upload?
5774 wxString file = flattenCallSign(wiz.callsign) + wxT(".") + wxT(TQSL_CRQ_FILE_EXT);
5775 bool upload = false;
5776 wxString msg = _("Do you want to upload this certificate request to LoTW now?");
5777 if (!renew) {
5778 msg += wxT("\n");
5779 msg += _("You do not need an account on LoTW to do this.");
5780 }
5781 if (wxMessageBox(msg, _("Upload"), wxYES_NO|wxICON_QUESTION, this) == wxYES) {
5782 upload = true;
5783 // Save it in the working directory
5784 #ifdef _WIN32
5785 file = wxString::FromUTF8(tQSL_BaseDir) + wxT("\\") + file;
5786 #else
5787 file = wxString::FromUTF8(tQSL_BaseDir) + wxT("/") + file;
5788 #endif
5789 } else {
5790 // Where to put it?
5791 wxString wildcard = _("tQSL Cert Request files (*.");
5792 wildcard += wxString::FromUTF8(TQSL_CRQ_FILE_EXT ")|*." TQSL_CRQ_FILE_EXT);
5793 wildcard += _("|All files (") + wxString::FromUTF8(ALLFILESWILD ")|" ALLFILESWILD);
5794 file = wxFileSelector(_("Save request"), wxT(""), file, wxT(TQSL_CRQ_FILE_EXT), wildcard,
5795 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
5796 if (file.IsEmpty()) {
5797 wxLogMessage(_("Request cancelled"));
5798 deleteRequest(wiz.callsign.ToUTF8(), wiz.dxcc);
5799 return;
5800 }
5801 }
5802 TQSL_CERT_REQ req;
5803 strncpy(req.providerName, wiz.provider.organizationName, sizeof req.providerName);
5804 strncpy(req.providerUnit, wiz.provider.organizationalUnitName, sizeof req.providerUnit);
5805 strncpy(req.callSign, wiz.callsign.ToUTF8(), sizeof req.callSign);
5806 strncpy(req.name, wiz.name.ToUTF8(), sizeof req.name);
5807 strncpy(req.address1, wiz.addr1.ToUTF8(), sizeof req.address1);
5808 strncpy(req.address2, wiz.addr2.ToUTF8(), sizeof req.address2);
5809 strncpy(req.city, wiz.city.ToUTF8(), sizeof req.city);
5810 strncpy(req.state, wiz.state.ToUTF8(), sizeof req.state);
5811 strncpy(req.postalCode, wiz.zip.ToUTF8(), sizeof req.postalCode);
5812 if (wiz.country.IsEmpty())
5813 strncpy(req.country, "USA", sizeof req.country);
5814 else
5815 strncpy(req.country, wiz.country.ToUTF8(), sizeof req.country);
5816 strncpy(req.emailAddress, wiz.email.ToUTF8(), sizeof req.emailAddress);
5817 strncpy(req.password, wiz.password.ToUTF8(), sizeof req.password);
5818 req.dxccEntity = wiz.dxcc;
5819 req.qsoNotBefore = wiz.qsonotbefore;
5820 req.qsoNotAfter = wiz.qsonotafter;
5821 req.signer = wiz.cert;
5822 if (req.signer) {
5823 char buf[40];
5824 void *call = 0;
5825 if (!tqsl_getCertificateCallSign(req.signer, buf, sizeof(buf)))
5826 call = &buf;
5827 while (tqsl_beginSigning(req.signer, 0, getPassword, call)) {
5828 if (tQSL_Error != TQSL_PASSWORD_ERROR) {
5829 if (tQSL_Error == TQSL_CUSTOM_ERROR && (tQSL_Errno == ENOENT || tQSL_Errno == EPERM)) {
5830 snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
5831 "Can't open the private key file for %s: %s", static_cast<char *>(call), strerror(tQSL_Errno));
5832 }
5833 wxLogError(getLocalizedErrorString());
5834 deleteRequest(req.callSign, req.dxccEntity);
5835 return;
5836 }
5837 // Try signing with the unicode version of the password
5838 if (tqsl_beginSigning(req.signer, unipwd, NULL, call) == 0) {
5839 // If OK, signing is ready to go.
5840 break;
5841 }
5842 if (tQSL_Error != TQSL_PASSWORD_ERROR) {
5843 deleteRequest(req.callSign, req.dxccEntity);
5844 return;
5845 } else {
5846 wxLogError(getLocalizedErrorString());
5847 }
5848 }
5849 }
5850 req.renew = renew ? 1 : 0;
5851 if (tqsl_createCertRequest(file.ToUTF8(), &req, 0, 0)) {
5852 wxString msg = getLocalizedErrorString();
5853 if (req.signer)
5854 tqsl_endSigning(req.signer);
5855 wxLogError(msg);
5856 char m[500];
5857 strncpy(m, msg.ToUTF8(), sizeof m);
5858 wxMessageBox(wxString::Format(_("Error creating callsign certificate request: %hs"), m), _("Error creating Callsign Certificate Request"), wxOK | wxICON_EXCLAMATION, this);
5859 deleteRequest(req.callSign, req.dxccEntity);
5860 return;
5861 }
5862 if (upload) {
5863 #ifdef _WIN32
5864 wchar_t* wfile = utf8_to_wchar(file.ToUTF8());
5865 ifstream in(wfile, ios::in | ios::binary);
5866 free_wchar(wfile);
5867 #else
5868 ifstream in(file.ToUTF8(), ios::in | ios::binary);
5869 #endif
5870 if (!in) {
5871 wxLogError(_("Error opening certificate request file %s: %hs"), file.c_str(), strerror(errno));
5872 deleteRequest(req.callSign, req.dxccEntity);
5873 } else {
5874 string contents;
5875 in.seekg(0, ios::end);
5876 contents.resize(in.tellg());
5877 in.seekg(0, ios::beg);
5878 in.read(&contents[0], contents.size());
5879 in.close();
5880
5881 wxString fileType(_("Certificate Request"));
5882 retval = UploadFile(file, file.ToUTF8(), 0, reinterpret_cast<void *>(const_cast<char *>(contents.c_str())),
5883 contents.size(), fileType);
5884 if (retval != 0) {
5885 wxLogError(_("Your certificate request did not upload properly"));
5886 wxLogError(_("Please try again."));
5887 deleteRequest(req.callSign, req.dxccEntity);
5888 }
5889 }
5890 } else {
5891 wxString msg = _("You may now send your new certificate request (");
5892 msg += file;
5893 msg += wxT(")");
5894 if (wiz.provider.emailAddress[0] != 0) {
5895 msg += wxT("\n");
5896 msg += _("to:");
5897 msg += wxT("\n ");
5898 msg += wxString::FromUTF8(wiz.provider.emailAddress);
5899 }
5900 if (wiz.provider.url[0] != 0) {
5901 msg += wxT("\n");
5902 if (wiz.provider.emailAddress[0] != 0)
5903 msg += _("or ");
5904 msg += wxString(_("see:"));
5905 msg += wxT("\n ");
5906 msg += wxString::FromUTF8(wiz.provider.url);
5907 }
5908 wxMessageBox(msg, wxT("TQSL"), wxOK | wxICON_ERROR, this);
5909 }
5910 if (retval == 0) {
5911 wxString pending = wxConfig::Get()->Read(wxT("RequestPending"));
5912 if (pending.IsEmpty())
5913 pending = wiz.callsign;
5914 else
5915 pending += wxT(",") + wiz.callsign;
5916 wxConfig::Get()->Write(wxT("RequestPending"), pending);
5917 }
5918 if (req.signer)
5919 tqsl_endSigning(req.signer);
5920 cert_tree->Build(CERTLIST_FLAGS);
5921 CertTreeReset();
5922 }
5923 }
5924
5925 void
5926 MyFrame::CertTreeReset() {
5927 if (!cert_save_label) return;
5928 wxString nl = wxT("\n");
5929 cert_save_label->SetLabel(nl + _("Save a Callsign Certificate"));
5930 cert_save_button->SetLabel(nl + _("Save a Callsign Certificate"));
5931 cert_renew_label->SetLabel(nl + _("Renew a Callsign Certificate"));
5932 cert_renew_button->SetLabel(nl + _("Renew a Callsign Certificate"));
5933 cert_prop_label->SetLabel(nl + _("Display a Callsign Certificate"));
5934 cert_prop_button->SetLabel(nl + _("Display a Callsign Certificate"));
5935 cert_menu->Enable(tc_c_Renew, false);
5936 cert_renew_button->Enable(false);
5937 cert_select_label->SetLabel(nl + _("Select a Callsign Certificate to process"));
5938 cert_save_button->Enable(false);
5939 cert_prop_button->Enable(false);
5940 int ncalls = 0;
5941 tqsl_getDeletedCallsignCertificates(NULL, &ncalls, NULL);
5942 cert_menu->Enable(tc_c_Undelete, ncalls > 0);
5943 }
5944
5945 void MyFrame::OnCertTreeSel(wxTreeEvent& event) {
5946 tqslTrace("MyFrame::OnCertTreeSel", NULL);
5947 wxTreeItemId id = event.GetItem();
5948 CertTreeItemData *data = reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(id));
5949 if (data) {
5950 int keyonly = 0;
5951 int expired = 0;
5952 int superseded = 0;
5953 int deleted = 0;
5954 char call[40];
5955 tqsl_getCertificateCallSign(data->getCert(), call, sizeof call);
5956 wxString callSign = wxString::FromUTF8(call);
5957 tqsl_getCertificateKeyOnly(data->getCert(), &keyonly);
5958 tqsl_isCertificateExpired(data->getCert(), &expired);
5959 tqsl_isCertificateSuperceded(data->getCert(), &superseded);
5960 tqsl_getDeletedCallsignCertificates(NULL, &deleted, call);
5961 tqslTrace("MyFrame::OnCertTreeSel", "call=%s", call);
5962
5963 cert_select_label->SetLabel(wxT(""));
5964 cert_menu->Enable(tc_c_Properties, true);
5965 cert_menu->Enable(tc_c_Export, true);
5966 cert_menu->Enable(tc_c_Delete, true);
5967 cert_menu->Enable(tc_c_Renew, true);
5968 cert_menu->Enable(tc_c_Undelete, deleted != 0);
5969 cert_save_button->Enable(true);
5970 cert_load_button->Enable(true);
5971 cert_prop_button->Enable(true);
5972
5973 int w, h;
5974 loc_add_label->GetSize(&w, &h);
5975 wxString nl = wxT("\n");
5976 cert_save_label->SetLabel(nl + _("Save the Callsign Certificate for") + wxT(" ") + callSign);
5977 cert_save_label->Wrap(w - 10);
5978 cert_save_button->SetLabel(nl + _("Save the Callsign Certificate for") + wxT(" ") + callSign);
5979 cert_prop_label->SetLabel(nl + _("Display the Callsign Certificate properties for") + wxT(" ") + callSign);
5980 cert_prop_label->Wrap(w - 10);
5981 cert_prop_button->SetLabel(nl + _("Display the Callsign Certificate properties for") + wxT(" ") + callSign);
5982 if (!(keyonly || expired || superseded)) {
5983 cert_renew_label->SetLabel(nl + _("Renew the Callsign Certificate for") +wxT(" ") + callSign);
5984 cert_renew_label->Wrap(w - 10);
5985 cert_renew_button->SetLabel(nl + _("Renew the Callsign Certificate for") +wxT(" ") + callSign);
5986 } else {
5987 cert_renew_label->SetLabel(nl + _("Renew a Callsign Certificate"));
5988 cert_renew_button->SetLabel(nl + _("Renew a Callsign Certificate"));
5989 }
5990 cert_menu->Enable(tc_c_Renew, !(keyonly || expired || superseded));
5991 cert_menu->Enable(tc_c_Undelete, deleted != 0);
5992 cert_renew_button->Enable(!(keyonly || expired || superseded));
5993 } else {
5994 CertTreeReset();
5995 }
5996 }
5997
5998 void MyFrame::OnCertProperties(wxCommandEvent& WXUNUSED(event)) {
5999 tqslTrace("MyFrame::OnCertProperties", NULL);
6000 CertTreeItemData *data = reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(cert_tree->GetSelection()));
6001 if (data != NULL)
6002 displayCertProperties(data, this);
6003 }
6004
6005 void MyFrame::OnCertExport(wxCommandEvent& WXUNUSED(event)) {
6006 tqslTrace("MyFrame::OnCertExport", NULL);
6007 CertTreeItemData *data = reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(cert_tree->GetSelection()));
6008 if (data == NULL) // "Never happens"
6009 return;
6010
6011 char call[40];
6012 if (tqsl_getCertificateCallSign(data->getCert(), call, sizeof call)) {
6013 wxLogError(getLocalizedErrorString());
6014 return;
6015 }
6016 tqslTrace("MyFrame::OnCertExport", "call=%s", call);
6017 wxString file_default = flattenCallSign(wxString::FromUTF8(call));
6018 int ko = 0;
6019 tqsl_getCertificateKeyOnly(data->getCert(), &ko);
6020 if (ko)
6021 file_default += wxT("-key-only");
6022 file_default += wxT(".p12");
6023 wxString path = wxConfig::Get()->Read(wxT("CertFilePath"), wxT(""));
6024 wxString filename = wxFileSelector(_("Enter the name for the new Certificate Container file"), path,
6025 file_default, wxT(".p12"), _("Certificate Container files (*.p12)|*.p12|All files (*.*)|*.*"),
6026 wxFD_SAVE|wxFD_OVERWRITE_PROMPT, this);
6027 if (filename == wxT(""))
6028 return;
6029 wxConfig::Get()->Write(wxT("CertFilePath"), wxPathOnly(filename));
6030 wxString msg = _("Enter the password for the certificate container file.");
6031 msg += wxT("\n\n");
6032 msg += _("If you are using a computer system that is shared "
6033 "with others, you should specify a password to "
6034 "protect this certificate. However, if you are using "
6035 "a computer in a private residence, no password need be specified.");
6036 msg += wxT("\n\n");
6037 msg += _("You will have to enter the password any time you "
6038 "load the file into TrustedQSL.");
6039 msg += wxT("\n\n");
6040 msg += _("Leave the password blank and click 'OK' unless you want to "
6041 "use a password.");
6042 msg += wxT("\n\n");
6043 GetNewPasswordDialog dial(this, _("Certificate Container Password"), msg, true, help, wxT("save.htm"));
6044 if (dial.ShowModal() != wxID_OK)
6045 return; // Cancelled
6046 int terr;
6047 do {
6048 terr = tqsl_beginSigning(data->getCert(), 0, getPassword, reinterpret_cast<void *>(&call));
6049 if (terr) {
6050 if (tQSL_Error == TQSL_PASSWORD_ERROR) {
6051 terr = tqsl_beginSigning(data->getCert(), unipwd, NULL, reinterpret_cast<void *>(&call));
6052 if (terr) {
6053 if (tQSL_Error == TQSL_PASSWORD_ERROR)
6054 continue;
6055 wxLogError(getLocalizedErrorString());
6056 }
6057 continue;
6058 }
6059 if (tQSL_Error == TQSL_OPERATOR_ABORT)
6060 return;
6061 // Unable to open the private key
6062 if (tQSL_Error == TQSL_CUSTOM_ERROR && (tQSL_Errno == ENOENT || tQSL_Errno == EPERM)) {
6063 snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
6064 "Can't open the private key file for %s: %s", call, strerror(tQSL_Errno));
6065 }
6066 wxLogError(getLocalizedErrorString());
6067 return;
6068 }
6069 } while (terr);
6070 // When setting the password, always use UTF8.
6071 if (tqsl_exportPKCS12File(data->getCert(), filename.ToUTF8(), dial.Password().ToUTF8())) {
6072 char buf[500];
6073 strncpy(buf, getLocalizedErrorString().ToUTF8(), sizeof buf);
6074 wxLogError(wxString::Format(_("Export to %s failed: %hs"), filename.c_str(), buf));
6075 } else {
6076 wxLogMessage(_("Certificate saved in file %s"), filename.c_str());
6077 }
6078 tqsl_endSigning(data->getCert());
6079 }
6080
6081 void MyFrame::OnCertDelete(wxCommandEvent& WXUNUSED(event)) {
6082 tqslTrace("MyFrame::OnCertDelete", NULL);
6083 CertTreeItemData *data = reinterpret_cast<CertTreeItemData *>(cert_tree->GetItemData(cert_tree->GetSelection()));
6084 if (data == NULL) // "Never happens"
6085 return;
6086
6087 wxString warn = _("This will remove the selected callsign certificate from your system.");
6088 warn += wxT("\n");
6089 warn += _("You will NOT be able to recover it by loading a .TQ6 file.");
6090 warn += wxT("\n");
6091 warn += _("You WILL be able to recover it from a container (.p12) file,");
6092 warn += wxT("\n");
6093 warn += _("if you have created one via the Callsign Certificate menu's");
6094 warn += wxT("\n");
6095 warn += _("'Save Callsign Certificate' command.");
6096 warn += wxT("\n\n");
6097 warn += _("Are you sure you want to delete the certificate?");
6098 if (wxMessageBox(warn, _("Warning"), wxYES_NO|wxICON_QUESTION, this) == wxYES) {
6099 char buf[128];
6100 if (!tqsl_getCertificateCallSign(data->getCert(), buf, sizeof buf)) {
6101 wxString call = wxString::FromUTF8(buf);
6102 wxString pending = wxConfig::Get()->Read(wxT("RequestPending"));
6103 pending.Replace(call, wxT(""), true);
6104 wxString rest;
6105 while (pending.StartsWith(wxT(","), &rest))
6106 pending = rest;
6107 while (pending.EndsWith(wxT(","), &rest))
6108 pending = rest;
6109 wxConfig::Get()->Write(wxT("RequestPending"), pending);
6110 }
6111 int keyonly, sup, exp;
6112 long serial;
6113 tqsl_getCertificateKeyOnly(data->getCert(), &keyonly);
6114 tqsl_getCertificateSerial(data->getCert(), &serial);
6115 tqsl_isCertificateExpired(data->getCert(), &exp);
6116 tqsl_isCertificateSuperceded(data->getCert(), &sup);
6117 tqslTrace("MyFrame::OnCertDelete", "About to delete cert for callsign %s, serial %ld, keyonly %d, superceded %d, expired %d", buf, serial, keyonly, sup, exp);
6118 if (tqsl_deleteCertificate(data->getCert()))
6119 wxLogError(getLocalizedErrorString());
6120 cert_tree->Build(CERTLIST_FLAGS);
6121 CertTreeReset();
6122 }
6123 }
6124
6125 void MyFrame::OnCertUndelete(wxCommandEvent& WXUNUSED(event)) {
6126 tqslTrace("MyFrame::OnCertUndelete", NULL);
6127
6128 int ncalls;
6129 char **calls = NULL;
6130
6131 try {
6132 check_tqsl_error(tqsl_getDeletedCallsignCertificates(&calls, &ncalls, NULL));
6133
6134 if (ncalls <= 0) {
6135 wxMessageBox(_("There are no deleted Callsign Certificates to restore"), _("Undelete Error"), wxOK | wxICON_EXCLAMATION, this);
6136 return;
6137 }
6138
6139 wxArrayString choices;
6140 choices.clear();
6141 for (int i = 0; i < ncalls; i++) {
6142 choices.Add(wxString::FromUTF8(calls[i]));
6143 }
6144 choices.Sort();
6145
6146 wxString selected = wxGetSingleChoice(_("Choose a Callsign Certificate to restore"),
6147 _("Callsign Certificates"),
6148 choices);
6149 if (selected.IsEmpty())
6150 return; // Cancelled
6151
6152 check_tqsl_error(tqsl_restoreCallsignCertificate(selected.ToUTF8()));
6153 tqsl_freeDeletedCertificateList(calls, ncalls);
6154 cert_tree->Build(CERTLIST_FLAGS);
6155 CertTreeReset();
6156 } catch(TQSLException& x) {
6157 wxLogError(wxT("%hs"), x.what());
6158 }
6159 }
6160
6161 void
6162 MyFrame::LocTreeReset() {
6163 if (!loc_edit_button) return;
6164 loc_edit_button->Disable();
6165 loc_delete_button->Disable();
6166 loc_prop_button->Disable();
6167 stn_menu->Enable(tm_s_Properties, false);
6168 wxString nl = wxT("\n");
6169 loc_edit_label->SetLabel(nl + _("Edit a Station Location"));
6170 loc_edit_button->SetLabel(nl + _("Edit a Station Location"));
6171 loc_delete_label->SetLabel(nl + _("Delete a Station Location"));
6172 loc_delete_button->SetLabel(nl + _("Delete a Station Location"));
6173 loc_prop_label->SetLabel(nl + _("Display Station Location Properties"));
6174 loc_prop_button->SetLabel(nl + _("Display Station Location Properties"));
6175 loc_select_label->SetLabel(nl + _("Select a Station Location to process"));
6176 }
6177
6178 void MyFrame::OnLocTreeSel(wxTreeEvent& event) {
6179 tqslTrace("MyFrame::OnLocTreeSel", NULL);
6180 wxTreeItemId id = event.GetItem();
6181 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(id));
6182 if (data) {
6183 int w, h;
6184 wxString lname = data->getLocname();
6185 wxString call = data->getCallSign();
6186 tqslTrace("MyFrame::OnLocTreeSel", "lname=%s, call=%s", S(lname), S(call));
6187
6188 loc_add_label->GetSize(&w, &h);
6189
6190 loc_edit_button->Enable();
6191 loc_delete_button->Enable();
6192 loc_prop_button->Enable();
6193 stn_menu->Enable(tm_s_Properties, true);
6194 loc_edit_label->SetLabel(_("Edit Station Location ") + call + wxT(": ") + lname);
6195 loc_edit_label->Wrap(w - 10);
6196 loc_edit_button->SetLabel(_("Edit Station Location ") + call + wxT(": ") + lname);
6197 loc_delete_label->SetLabel(_("Delete Station Location ") + call + wxT(": ") + lname);
6198 loc_delete_label->Wrap(w - 10);
6199 loc_delete_button->SetLabel(_("Delete Station Location ") + call + wxT(": ") + lname);
6200 loc_prop_label->SetLabel(_("Display Station Location Properties for ") + call + wxT(": ") + lname);
6201 loc_prop_label->Wrap(w - 10);
6202 loc_prop_button->SetLabel(_("Display Station Location Properties for ") + call + wxT(": ") + lname);
6203 loc_select_label->SetLabel(wxT(""));
6204 } else {
6205 LocTreeReset();
6206 }
6207 }
6208
6209 void MyFrame::OnLocProperties(wxCommandEvent& WXUNUSED(event)) {
6210 tqslTrace("MyFrame::OnLocProperties", NULL);
6211 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(loc_tree->GetSelection()));
6212 if (data != NULL)
6213 displayLocProperties(data, this);
6214 }
6215
6216 void MyFrame::OnLocDelete(wxCommandEvent& WXUNUSED(event)) {
6217 tqslTrace("MyFrame::OnLocDelete", NULL);
6218 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(loc_tree->GetSelection()));
6219 if (data == NULL) // "Never happens"
6220 return;
6221
6222 wxString warn = _("This will remove this station location from your system.");
6223 warn += wxT("\n");
6224 warn += _("Are you sure you want to delete this station location?");
6225 if (wxMessageBox(warn, _("Warning"), wxYES_NO|wxICON_QUESTION, this) == wxYES) {
6226 if (tqsl_deleteStationLocation(data->getLocname().ToUTF8()))
6227 wxLogError(getLocalizedErrorString());
6228 loc_tree->Build();
6229 LocTreeReset();
6230 }
6231 }
6232
6233 void MyFrame::OnLocUndelete(wxCommandEvent& WXUNUSED(event)) {
6234 tqslTrace("MyFrame::OnLocUndelete", NULL);
6235
6236 int nloc;
6237 char **locp = NULL;
6238
6239 try {
6240 check_tqsl_error(tqsl_getDeletedStationLocations(&locp, &nloc));
6241
6242 if (nloc <= 0) {
6243 wxMessageBox(_("There are no deleted Station Locations to restore"), _("Undelete Error"), wxOK | wxICON_EXCLAMATION, this);
6244 return;
6245 }
6246
6247 wxArrayString choices;
6248 choices.clear();
6249 for (int i = 0; i < nloc; i++) {
6250 choices.Add(wxString::FromUTF8(locp[i]));
6251 }
6252 choices.Sort();
6253
6254 wxString selected = wxGetSingleChoice(_("Choose a Station Location to restore"),
6255 _("Station Locations"),
6256 choices);
6257 if (selected.IsEmpty()) {
6258 tqsl_freeDeletedLocationList(locp, nloc);
6259 return; // Cancelled
6260 }
6261
6262 check_tqsl_error(tqsl_restoreStationLocation(selected.ToUTF8()));
6263 tqsl_freeDeletedLocationList(locp, nloc);
6264 } catch(TQSLException& x) {
6265 wxLogError(wxT("%hs"), x.what());
6266 }
6267 loc_tree->Build();
6268 LocTreeReset();
6269 }
6270
6271 void MyFrame::OnLocEdit(wxCommandEvent& WXUNUSED(event)) {
6272 tqslTrace("MyFrame::OnLocEdit", NULL);
6273 LocTreeItemData *data = reinterpret_cast<LocTreeItemData *>(loc_tree->GetItemData(loc_tree->GetSelection()));
6274 if (data == NULL) // "Never happens"
6275 return;
6276
6277 tQSL_Location loc;
6278 wxString selname;
6279 char errbuf[512];
6280
6281 try {
6282 check_tqsl_error(tqsl_getStationLocation(&loc, data->getLocname().ToUTF8()));
6283 if (verify_cert(loc, true)) { // Check if there is a certificate before editing
6284 check_tqsl_error(tqsl_getStationLocationErrors(loc, errbuf, sizeof(errbuf)));
6285 if (strlen(errbuf) > 0) {
6286 wxString fmt = wxT("%hs\n");
6287 fmt += _("The invalid data was ignored.");
6288 wxMessageBox(wxString::Format(fmt, errbuf), _("Station Location data error"), wxOK | wxICON_EXCLAMATION, this);
6289 }
6290 char loccall[512];
6291 check_tqsl_error(tqsl_getLocationCallSign(loc, loccall, sizeof loccall));
6292 selname = run_station_wizard(this, loc, help, true, wxString::Format(_("Edit Station Location : %hs - %s"), loccall, data->getLocname().c_str()));
6293 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
6294 }
6295 }
6296 catch(TQSLException& x) {
6297 wxLogError(wxT("%hs"), x.what());
6298 }
6299 loc_tree->Build();
6300 LocTreeReset();
6301 }
6302
6303 void MyFrame::OnLoginToLogbook(wxCommandEvent& WXUNUSED(event)) {
6304 tqslTrace("MyFrame::OnLoginToLogbook", NULL);
6305 wxString url = wxConfig::Get()->Read(wxT("LogbookURL"), DEFAULT_LOTW_LOGIN_URL);
6306 if (!url.IsEmpty())
6307 wxLaunchDefaultBrowser(url);
6308 return;
6309 }
6310
6311 void MyFrame::OnChooseLanguage(wxCommandEvent& WXUNUSED(event)) {
6312 tqslTrace("MyFrame::OnChooseLanguage", "Language choice dialog");
6313
6314 wxLanguage lang = wxGetApp().GetLang();
6315 long lng = wxGetSingleChoiceIndex(_("Please choose language:"),
6316 _("Language"),
6317 WXSIZEOF(langNames), langNames, this);
6318 tqslTrace("MyFrame::OnChooseLanguage", "Language chosen: %d", lng);
6319 if (lng == -1 || langIds[lng] == lang) // Cancel or No change
6320 return;
6321
6322 wxConfig::Get()->Write(wxT("Language"), static_cast<int>(langIds[lng]));
6323 wxConfig::Get()->Flush();
6324
6325 if (wxLocale::IsAvailable(langIds[lng])) {
6326 if (locale) delete locale;
6327 locale = new wxLocale(langIds[lng]);
6328 if (!locale) locale = new wxLocale(wxLANGUAGE_DEFAULT);
6329 } else {
6330 wxLogError(wxT("This language is not supported by the system."));
6331 locale = new wxLocale(wxLANGUAGE_DEFAULT);
6332 }
6333 // Add a subdirectory for language files
6334 locale->AddCatalogLookupPathPrefix(wxT("lang"));
6335
6336 // Initialize the catalogs we'll be using
6337 locale->AddCatalog(wxT("tqslapp"));
6338 locale->AddCatalog(wxT("wxstd"));
6339
6340 // this catalog is installed in standard location on Linux systems and
6341 // shows that you may make use of the standard message catalogs as well
6342 //
6343 // If it's not installed on your system, it is just silently ignored
6344 #ifdef __LINUX__
6345 {
6346 wxLogNull nolog;
6347 locale->AddCatalog(wxT("fileutils"));
6348 }
6349 #endif
6350 SaveWindowLayout();
6351 tqslTrace("MyFrame::OnChooseLanguage", "Destroying GUI");
6352 Destroy();
6353 tqslTrace("MyFrame::OnChooseLanguage", "Recreating GUI");
6354 (reinterpret_cast<QSLApp*>(wxTheApp))->OnInit();
6355 }
6356
6357 class CertPropDial : public wxDialog {
6358 public:
6359 explicit CertPropDial(tQSL_Cert cert, wxWindow *parent = 0);
6360 void closeMe(wxCommandEvent&) { EndModal(wxID_OK); }
6361 DECLARE_EVENT_TABLE()
6362 };
6363
6364 BEGIN_EVENT_TABLE(CertPropDial, wxDialog)
6365 EVT_BUTTON(wxID_OK, CertPropDial::closeMe)
6366 END_EVENT_TABLE()
6367
6368 CertPropDial::CertPropDial(tQSL_Cert cert, wxWindow *parent)
6369 : wxDialog(parent, -1, _("Certificate Properties"), wxDefaultPosition, wxSize(400, 15 * LABEL_HEIGHT)) {
6370 tqslTrace("CertPropDial::CertPropDial", "cert=%lx", static_cast<void *>(cert));
6371 const char *labels[] = {
6372 __("Begins: "),
6373 __("Expires: "),
6374 __("Organization: "),
6375 "",
6376 __("Serial: "),
6377 __("Operator: "),
6378 __("Call sign: "),
6379 __("DXCC Entity: "),
6380 __("QSO Start Date: "),
6381 __("QSO End Date: "),
6382 __("Password: ")
6383 };
6384
6385 wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
6386
6387 int label_width = 0;
6388
6389 wxStaticText* mst = new wxStaticText(this, -1, wxT("M"));
6390 int char_width = mst->GetSize().GetWidth();
6391 // Measure the widest label
6392 for (int i = 0; i < static_cast<int>(sizeof labels / sizeof labels[0]); i++) {
6393 int em_w;
6394 wxString lab = wxGetTranslation(wxString::FromUTF8(labels[i]));
6395 mst->SetLabel(lab);
6396 em_w = mst->GetSize().GetWidth();
6397 if (em_w > label_width) label_width = em_w;
6398 }
6399
6400 int keyonly;
6401 char callsign[40];
6402
6403 tqsl_getCertificateKeyOnly(cert, &keyonly);
6404 wxString blob = wxT("");
6405 for (int i = 0; i < static_cast<int>(sizeof labels / sizeof labels[0]); i++) {
6406 wxString lbl = wxGetTranslation(wxString::FromUTF8(labels[i]));
6407 while (1) {
6408 mst->SetLabel(lbl);
6409 int cur_size = mst->GetSize().GetWidth();
6410 int delta = label_width - cur_size;
6411 if (delta < char_width) break;
6412 lbl += wxT(" ");
6413 }
6414
6415 char buf[128] = "";
6416 tQSL_Date date;
6417 DXCC DXCC;
6418 int dxcc;
6419 long serial;
6420 switch (i) {
6421 case 0:
6422 if (keyonly)
6423 strncpy(buf, "N/A", sizeof buf);
6424 else if (!tqsl_getCertificateNotBeforeDate(cert, &date))
6425 tqsl_convertDateToText(&date, buf, sizeof buf);
6426 break;
6427 case 1:
6428 if (keyonly)
6429 strncpy(buf, "N/A", sizeof buf);
6430 else if (!tqsl_getCertificateNotAfterDate(cert, &date))
6431 tqsl_convertDateToText(&date, buf, sizeof buf);
6432 break;
6433 case 2:
6434 tqsl_getCertificateIssuerOrganization(cert, buf, sizeof buf);
6435 break;
6436 case 3:
6437 tqsl_getCertificateIssuerOrganizationalUnit(cert, buf, sizeof buf);
6438 break;
6439 case 4:
6440 if (keyonly) {
6441 strncpy(buf, "N/A", sizeof buf);
6442 } else {
6443 tqsl_getCertificateSerial(cert, &serial);
6444 snprintf(buf, sizeof buf, "%ld", serial);
6445 }
6446 break;
6447 case 5:
6448 if (keyonly)
6449 strncpy(buf, "N/A", sizeof buf);
6450 else
6451 tqsl_getCertificateAROName(cert, buf, sizeof buf);
6452 break;
6453 case 6:
6454 tqsl_getCertificateCallSign(cert, buf, sizeof buf);
6455 strncpy(callsign, buf, sizeof callsign);
6456 break;
6457 case 7:
6458 tqsl_getCertificateDXCCEntity(cert, &dxcc);
6459 DXCC.getByEntity(dxcc);
6460 strncpy(buf, DXCC.name(), sizeof buf);
6461 break;
6462 case 8:
6463 if (!tqsl_getCertificateQSONotBeforeDate(cert, &date))
6464 tqsl_convertDateToText(&date, buf, sizeof buf);
6465 break;
6466 case 9:
6467 if (!tqsl_getCertificateQSONotAfterDate(cert, &date))
6468 tqsl_convertDateToText(&date, buf, sizeof buf);
6469 break;
6470 case 10:
6471 switch (tqsl_getCertificatePrivateKeyType(cert)) {
6472 case TQSL_PK_TYPE_ERR:
6473 if (tQSL_Error == TQSL_CUSTOM_ERROR && (tQSL_Errno == ENOENT || tQSL_Errno == EPERM)) {
6474 snprintf(tQSL_CustomError, sizeof tQSL_CustomError,
6475 "Can't open the private key file for %s: %s", callsign, strerror(tQSL_Errno));
6476 }
6477 wxMessageBox(getLocalizedErrorString(), _("Error"), wxOK | wxICON_WARNING, this);
6478 strncpy(buf, __("<ERROR>"), sizeof buf);
6479 break;
6480 case TQSL_PK_TYPE_NONE:
6481 strncpy(buf, __("None"), sizeof buf);
6482 break;
6483 case TQSL_PK_TYPE_UNENC:
6484 strncpy(buf, __("None"), sizeof buf);
6485 break;
6486 case TQSL_PK_TYPE_ENC:
6487 strncpy(buf, __("Password protected"), sizeof buf);
6488 break;
6489 }
6490 break;
6491 }
6492 if (keyonly && i == 0) {
6493 blob += _("Certificate Request:");
6494 blob += wxT("\t");
6495 blob += _("Awaiting response from ARRL");
6496 blob += wxT("\n");
6497 }
6498 if (!keyonly || i > 1) {
6499 blob += lbl;
6500 blob += wxT("\t");
6501 blob += wxGetTranslation(wxString::FromUTF8(buf));
6502 blob += wxT("\n");
6503 }
6504 }
6505 delete mst;
6506
6507 int sup, exp;
6508 if (tqsl_isCertificateSuperceded(cert, &sup) == 0 && sup) {
6509 blob += wxT("\t\t\t\t");
6510 blob += _("Replaced");
6511 blob += wxT("\n");
6512 }
6513 if (tqsl_isCertificateExpired(cert, &exp) == 0 && exp) {
6514 blob += wxT("\t\t\t\t");
6515 blob += _("Expired");
6516 blob += wxT("\n");
6517 }
6518
6519 topsizer->Add(new wxStaticText(this, -1, blob));
6520 topsizer->Add(
6521 new wxButton(this, wxID_OK, _("Close")),
6522 0, wxALIGN_CENTER | wxALL, 10
6523 );
6524 SetAutoLayout(TRUE);
6525 SetSizer(topsizer);
6526 topsizer->Fit(this);
6527 topsizer->SetSizeHints(this);
6528 CenterOnParent();
6529 }
6530
6531 void
6532 displayCertProperties(CertTreeItemData *item, wxWindow *parent) {
6533 tqslTrace("displayCertProperties", "item=%lx", static_cast<void *>(item));
6534 if (item != NULL) {
6535 CertPropDial dial(item->getCert(), parent);
6536 dial.ShowModal();
6537 }
6538 }
6539
6540
6541 class LocPropDial : public wxDialog {
6542 public:
6543 explicit LocPropDial(wxString locname, wxWindow *parent = 0);
6544 void closeMe(wxCommandEvent&) { EndModal(wxID_OK); }
6545 DECLARE_EVENT_TABLE()
6546 };
6547
6548 BEGIN_EVENT_TABLE(LocPropDial, wxDialog)
6549 EVT_BUTTON(wxID_OK, LocPropDial::closeMe)
6550 END_EVENT_TABLE()
6551
6552 LocPropDial::LocPropDial(wxString locname, wxWindow *parent)
6553 : wxDialog(parent, -1, _("Station Location Properties"), wxDefaultPosition, wxSize(1000, 15 * LABEL_HEIGHT)) {
6554 tqslTrace("LocPropDial", "locname=%s", S(locname));
6555
6556 const char *fields[] = { "CALL", __("Call sign: "),
6557 "DXCC", __("DXCC Entity: "),
6558 "GRIDSQUARE", __("Grid Square: "),
6559 "ITUZ", __("ITU Zone: "),
6560 "CQZ", __("CQ Zone: "),
6561 "IOTA", __("IOTA Locator: "),
6562 "US_STATE", __("State: "),
6563 "US_COUNTY", __("County: "),
6564 "US_PARK", __("Park: "),
6565 "CA_PROVINCE", __("Province: "),
6566 "CA_US_PARK", __("Park: "),
6567 "RU_OBLAST", __("Oblast: "),
6568 "CN_PROVINCE", __("Province: "),
6569 "AU_STATE", __("State: "),
6570 "DX_US_PARK", __("Park: ") };
6571
6572 tQSL_Location loc;
6573 try {
6574 check_tqsl_error(tqsl_getStationLocation(&loc, locname.ToUTF8()));
6575 }
6576 catch(TQSLException& x) {
6577 wxLogError(wxT("%hs"), x.what());
6578 return;
6579 }
6580
6581 wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
6582
6583 int label_width = 0;
6584
6585 wxStaticText* mst = new wxStaticText(this, -1, wxT("M"));
6586 int char_width = mst->GetSize().GetWidth();
6587 // Measure the widest label
6588 for (int i = 0; i < static_cast<int>(sizeof fields / sizeof fields[0]); i++) {
6589 int em_w;
6590 mst->SetLabel(wxGetTranslation(wxString::FromUTF8(fields[i])));
6591 em_w = mst->GetSize().GetWidth();
6592 if (em_w > label_width) label_width = em_w;
6593 }
6594
6595 wxString blob = wxT("");
6596 char fieldbuf[512];
6597 for (int i = 0; i < static_cast<int>(sizeof fields / sizeof fields[0]); i+=2) {
6598 if (tqsl_getStationLocationField(loc, fields[i], fieldbuf, sizeof fieldbuf) == 0) {
6599 if (strlen(fieldbuf) > 0 && strcmp(fieldbuf, "[None]") != 0) {
6600 wxString lbl = wxGetTranslation(wxString::FromUTF8(fields[i+1]));
6601 while(1) {
6602 mst->SetLabel(lbl);
6603 int cur_size = mst->GetSize().GetWidth();
6604 int delta = label_width - cur_size;
6605 if (delta < char_width) break;
6606 lbl += wxT(" ");
6607 }
6608 blob += lbl;
6609 blob += wxT("\t");
6610 if (!strcmp(fields[i], "DXCC")) {
6611 int dxcc = strtol(fieldbuf, NULL, 10);
6612 const char *dxccname = NULL;
6613 if (tqsl_getDXCCEntityName(dxcc, &dxccname))
6614 strncpy(fieldbuf, "Unknown", sizeof fieldbuf);
6615 else
6616 strncpy(fieldbuf, dxccname, sizeof fieldbuf);
6617 }
6618 blob += wxString::FromUTF8(fieldbuf);
6619 blob += wxT("\n");
6620 }
6621 }
6622 }
6623 delete mst;
6624
6625 topsizer->Add(new wxStaticText(this, -1, blob));
6626 topsizer->Add(
6627 new wxButton(this, wxID_OK, _("Close")),
6628 0, wxALIGN_CENTER | wxALL, 10
6629 );
6630 SetAutoLayout(TRUE);
6631 SetSizer(topsizer);
6632 topsizer->Fit(this);
6633 topsizer->SetSizeHints(this);
6634 CenterOnParent();
6635 }
6636
6637 void
6638 displayLocProperties(LocTreeItemData *item, wxWindow *parent) {
6639 tqslTrace("displayLocProperties", "item=%lx", item);
6640 if (item != NULL) {
6641 LocPropDial dial(item->getLocname(), parent);
6642 dial.ShowModal();
6643 }
6644 }
6645
6646 int
6647 getPassword(char *buf, int bufsiz, void *callsign) {
6648 tqslTrace("getPassword", "buf=%lx, bufsiz=%d, callsign=%s", buf, bufsiz, callsign ? callsign : "NULL");
6649 wxString prompt(_("Enter the password to unlock the callsign certificate"));
6650
6651 if (callsign)
6652 prompt = wxString::Format(_T("Enter the password for your active %hs Callsign Certificate"), reinterpret_cast<char *>(callsign));
6653
6654 tqslTrace("getPassword", "Probing for top window");
6655 wxWindow* top = wxGetApp().GetTopWindow();
6656 tqslTrace("getPassword", "Top window = 0x%lx", reinterpret_cast<void *>(top));
6657 top->SetFocus();
6658 tqslTrace("getPassword", "Focus grabbed. About to pop up password dialog");
6659 GetPasswordDialog dial(top, _("Enter password"), prompt);
6660 if (dial.ShowModal() != wxID_OK) {
6661 tqslTrace("getPassword", "Password entry cancelled");
6662 return 1;
6663 }
6664 tqslTrace("getPassword", "Password entered OK");
6665 strncpy(buf, dial.Password().ToUTF8(), bufsiz);
6666 utf8_to_ucs2(buf, unipwd, sizeof unipwd);
6667 buf[bufsiz-1] = 0;
6668 return 0;
6669 }
6670
6671 void
6672 displayTQSLError(const char *pre) {
6673 tqslTrace("displayTQSLError", "pre=%s", pre);
6674 wxString s = wxGetTranslation(wxString::FromUTF8(pre));
6675 s += wxT(":\n");
6676 s += getLocalizedErrorString();
6677 wxMessageBox(s, _("Error"), wxOK | wxICON_WARNING, frame);
6678 }
6679
6680
6681 static wxString
6682 flattenCallSign(const wxString& call) {
6683 tqslTrace("flattenCallSign", "call=%s", S(call));
6684 wxString flat = call;
6685 size_t idx;
6686 while ((idx = flat.find_first_not_of(wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_"))) != wxString::npos)
6687 flat[idx] = '_';
6688 return flat;
6689 }
6690
6691 static int lockfileFD = -1;
6692
6693 #ifndef _WIN32
6694 static int
6695 lock_db(bool wait) {
6696 struct flock fl;
6697 fl.l_type = F_WRLCK;
6698 fl.l_whence = SEEK_SET;
6699 fl.l_start = 0;
6700 fl.l_len = 1;
6701
6702 wxString lfname = wxString::FromUTF8(tQSL_BaseDir) + wxT("/dblock");
6703
6704 if (lockfileFD < 0) {
6705 lockfileFD = open(lfname.ToUTF8(), O_RDWR| O_CREAT, 0644);
6706 if (lockfileFD < 0)
6707 return 1;
6708 }
6709 if (wait) {
6710 fcntl(lockfileFD, F_SETLKW, &fl);
6711 return 0;
6712 }
6713 int ret = fcntl(lockfileFD, F_SETLK, &fl);
6714 if (ret < 0 && (errno == EACCES || errno == EAGAIN)) {
6715 return -1;
6716 }
6717 return 0;
6718 }
6719
6720 static void
6721 unlock_db(void) {
6722 if (lockfileFD < 0) return;
6723 close(lockfileFD);
6724 lockfileFD = -1;
6725 return;
6726 }
6727 #else /* _WIN32 */
6728
6729 static OVERLAPPED ov;
6730 static HANDLE hFile = 0;
6731
6732 static int
6733 lock_db(bool wait) {
6734 BOOL ret = FALSE;
6735 DWORD locktype = LOCKFILE_EXCLUSIVE_LOCK;
6736
6737 wxString lfname = wxString::FromUTF8(tQSL_BaseDir) + wxT("\\dblock");
6738
6739 if (lockfileFD < 0) {
6740 wchar_t* wlfname = utf8_to_wchar(lfname.ToUTF8());
6741 lockfileFD = _wopen(wlfname, O_RDWR| O_CREAT, 0644);
6742 free_wchar(wlfname);
6743 if (lockfileFD < 0)
6744 return 1;
6745 ZeroMemory(&ov, sizeof(ov));
6746 ov.hEvent = NULL;
6747 ov.Offset = 0;
6748 ov.OffsetHigh = 0x80000000;
6749 }
6750
6751 hFile = (HANDLE) _get_osfhandle(lockfileFD);
6752
6753 if (!wait) {
6754 locktype |= LOCKFILE_FAIL_IMMEDIATELY;
6755 }
6756 ret = LockFileEx(hFile, locktype, 0, 0, 0x80000000, &ov);
6757 if (!ret) {
6758 switch (GetLastError()) {
6759 case ERROR_SHARING_VIOLATION:
6760 case ERROR_LOCK_VIOLATION:
6761 case ERROR_IO_PENDING:
6762 return -1;
6763 default:
6764 return 0;
6765 }
6766 }
6767 return 0;
6768 }
6769
6770 static void
6771 unlock_db(void) {
6772 if (hFile)
6773 UnlockFileEx(hFile, 0, 0, 0x80000000, &ov);
6774 if (lockfileFD != -1)
6775 _close(lockfileFD);
6776 lockfileFD = -1;
6777 hFile = 0;
6778 }
6779 #endif /* _WIN32 */
6780