1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 // David Freese, W1HKJ
4 //
5 // This file is part of fldigi
6 //
7 // fldigi is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // fldigi is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20
21 #include <iostream>
22 #include <cmath>
23 #include <cstring>
24 #include <sstream>
25 #include <stdlib.h>
26
27 #include <FL/Fl.H>
28 #include <FL/filename.H>
29 #include <FL/fl_ask.H>
30
31 #include "xmlrpcpp/XmlRpc.h"
32
33 #include "config.h"
34 #include "lgbook.h"
35 #include "icons.h"
36 #include "gettext.h"
37 #include "debug.h"
38 #include "util.h"
39 #include "date.h"
40 #include "logbook.h"
41 #include "logger.h"
42 #include "locator.h"
43 #include "counties.h"
44 #include "confdialog.h"
45 #include "fl_digi.h"
46 #include "adif_io.h"
47 #include "modem.h"
48 #include "trx.h"
49 #include "status.h"
50
51 #include "configuration.h"
52
53 using namespace XmlRpc;
54
55 XmlRpcClient *log_client = (XmlRpcClient *)0;
56
test_connection(bool info=false)57 bool test_connection(bool info = false)
58 {
59 if (!log_client) {
60 create_logbook_dialogs();
61 return false;
62 }
63 XmlRpcValue query, result;
64 if (log_client->execute("system.listMethods", query, result)) {
65 if (info) {
66 string res;
67 int asize = result.size();
68 XmlRpcValue oneArg, help;
69 res = "Xml-log methods:";
70 for (int i = 0; i < asize; i++) {
71 oneArg[0] = result[i];
72 try {
73 if (std::string(result[i]).find("system") == string::npos) {
74 log_client->execute("system.methodHelp", oneArg, help);
75 res.append("\n\t").append(help);
76 }
77 } catch ( XmlRpcException err) {
78 res.append("\n").append(oneArg[0]).append(": ").append(err.getMessage());
79 }
80 }
81 LOG_INFO("%s", res.c_str());
82 }
83 return true;
84 }
85 return false;
86 }
87
activate_log_menus(bool val)88 void activate_log_menus(bool val)
89 {
90 set_server_label(!val);
91 activate_menu_item(_("View"), val);
92 activate_menu_item(_("New"), val);
93 activate_menu_item(_("Open..."), val);
94 activate_menu_item(_("Save"), val);
95 activate_menu_item(_("ADIF"), val);
96 activate_menu_item(_("Reports"), val);
97 }
98
get_field(string & adifline,int field)99 string get_field(string &adifline, int field)
100 {
101 string fld;
102 fld.append("<").append(fields[field].name).append(":");
103 size_t pos1 = adifline.find(fld);
104 if (pos1 == std::string::npos)
105 return "";
106
107 pos1 = adifline.find(">", pos1) + 1;
108 size_t pos2 = adifline.find("<", pos1);
109 fld = adifline.substr(pos1, pos2 - pos1);
110 return fld;
111 }
112
search_fllog(const char * callsign)113 cQsoRec* search_fllog(const char *callsign)
114 {
115 cQsoRec *rec = new cQsoRec;
116
117 XmlRpcValue oneArg, result;
118 if (!test_connection()) {
119 LOG_INFO("%s","Logbook server down!");
120 progdefaults.xml_logbook = false;
121 activate_log_menus(true);
122 start_logbook();
123 return (cQsoRec *)0;
124 }
125 oneArg[0] = callsign;
126 if (log_client->execute("log.get_record", oneArg, result)) {
127 string adifline = std::string(result);
128
129 rec->putField(NAME, get_field(adifline, NAME).c_str());
130 rec->putField(QTH, get_field(adifline, QTH).c_str());
131 rec->putField(QSO_DATE, get_field(adifline, QSO_DATE).c_str());
132 rec->putField(BAND, get_field(adifline, BAND).c_str());
133 rec->putField(ADIF_MODE, get_field(adifline, ADIF_MODE).c_str());
134
135 return rec;
136 }
137 return (cQsoRec *)0;
138 }
139
xml_get_record(const char * callsign)140 bool xml_get_record(const char *callsign)
141 {
142 XmlRpcValue oneArg, result;
143 if (!test_connection()) {
144 LOG_INFO("%s","Logbook server down!");
145 progdefaults.xml_logbook = false;
146 activate_log_menus(true);
147 start_logbook();
148 return false;
149 }
150 oneArg[0] = callsign;
151 if (log_client->execute("log.get_record", oneArg, result)) {
152 string adifline = std::string(result);
153 //std::cout << adifline << std::endl;
154
155 inpName->value(get_field(adifline, NAME).c_str());
156 inpQth->value(get_field(adifline, QTH).c_str());
157 inpState->value(get_field(adifline, STATE).c_str());
158 inpVEprov->value(get_field(adifline, VE_PROV).c_str());
159 cboCountry->value(get_field(adifline, COUNTRY).c_str());
160 inpCounty->value(get_field(adifline, CNTY).c_str());
161 inpLoc->value(get_field(adifline, GRIDSQUARE).c_str());
162
163 inp_SS_SerialNoR->value(get_field(adifline, SS_SERNO).c_str());
164 inp_SS_Precedence->value(get_field(adifline, SS_PREC).c_str());
165 inp_SS_Check->value(get_field(adifline, SS_CHK).c_str());
166 inp_SS_Section->value(get_field(adifline, SS_SEC).c_str());
167
168 inp_KD_age->value(get_field(adifline, AGE).c_str());
169 inp_ARR_check->value(get_field(adifline, CHECK).c_str());
170 inp_1010_nr->value(get_field(adifline, TEN_TEN).c_str());
171
172 inp_JOTA_troop->value(get_field(adifline, TROOPR).c_str());
173 inp_JOTA_scout->value(get_field(adifline, SCOUTR).c_str());
174
175 inpNotes->value(get_field(adifline, NOTES).c_str());
176 } else {
177 inpName->value("");
178 inpQth->value("");
179 inpState->value("");
180 inpVEprov->value("");
181 cboCountry->value("");
182 inpCounty->value("");
183 inpLoc->value("");
184 inp_SS_SerialNoR->value("");
185 inp_SS_Precedence->value("");
186 inp_SS_Check->value("");
187 inp_SS_Section->value("");
188 inp_KD_age->value("");
189 inp_ARR_check->value("");
190 inp_1010_nr->value("");
191 inp_JOTA_troop->value("");
192 inp_JOTA_scout->value("");
193 inpNotes->value("");
194 }
195 if (inpLoc->value()[0]) {
196 double lon1, lat1, lon2, lat2;
197 double azimuth, distance;
198 char szAZ[4];
199 if ( QRB::locator2longlat(&lon1, &lat1, progdefaults.myLocator.c_str()) == QRB::QRB_OK &&
200 QRB::locator2longlat(&lon2, &lat2, inpLoc->value()) == QRB::QRB_OK &&
201 QRB::qrb(lon1, lat1, lon2, lat2, &distance, &azimuth) == QRB::QRB_OK ) {
202 snprintf(szAZ,sizeof(szAZ),"%0.f", azimuth);
203 inpAZ->value(szAZ);
204 } else
205 inpAZ->value("");
206 } else
207 inpAZ->value("");
208 return true;
209 }
210
211 static std::string adif;
212 static std::string notes;
213
214 #define adif_str(a, b) { \
215 std::ostringstream os; \
216 os << "<" << fields[(a)].name << ":" << strlen((b)) << ">" << (b); \
217 adif.append(os.str()); }
218
xml_add_record()219 void xml_add_record()
220 {
221 if (!test_connection()) {
222 LOG_INFO("%s","Logbook server down!");
223 progdefaults.xml_logbook = false;
224 activate_log_menus(true);
225 start_logbook();
226 AddRecord();
227 return;
228 }
229
230 // create the ADIF record
231 char Mhz[30];
232 adif.erase();
233
234 adif_str(QSO_DATE, sDate_on.c_str());
235 adif_str(QSO_DATE_OFF, sDate_off.c_str());
236 adif_str(TIME_ON, sTime_on.c_str());
237 adif_str(TIME_OFF, sTime_off.c_str());
238 adif_str(CALL, inpCall->value());
239 {
240 snprintf(Mhz, sizeof(Mhz), "%-f", atof(inpFreq->value()) / 1000.0);
241 inpFreq_log->value(Mhz);
242 adif_str(FREQ, Mhz);
243 }
244 adif_str(ADIF_MODE, mode_info[active_modem->get_mode()].adif_name);
245 adif_str(RST_SENT, inpRstOut->value());
246 adif_str(RST_RCVD, inpRstIn->value());
247 adif_str(TX_PWR, progdefaults.mytxpower.c_str());
248 adif_str(NAME, inpName->value());
249
250 adif_str(QTH, inpQth->value());
251 adif_str(STATE, inpState->value());
252 adif_str(VE_PROV, inpVEprov->value());
253 adif_str(COUNTRY, cboCountry->value());
254 adif_str(CNTY, inpCounty->value());
255 adif_str(GRIDSQUARE, inpLoc->value());
256 adif_str(STX, outSerNo->value());
257 adif_str(SRX, inpSerNo->value());
258 adif_str(XCHG1, inpXchgIn->value());
259 adif_str(MYXCHG, progdefaults.myXchg.c_str());
260 adif_str(NOTES, inpNotes->value());
261 adif_str(CLASS, inpClass->value());
262 adif_str(ARRL_SECT, inpSection->value());
263 adif_str(CQZ, inp_CQzone->value());
264 // these fields will always be blank unless they are added to the main
265 // QSO log area.
266 // need to add the remaining fields
267 adif_str(IOTA, "");
268 adif_str(DXCC, "");
269 adif_str(QSL_VIA, "");
270 adif_str(QSLRDATE, "");
271 adif_str(QSLSDATE, "");
272
273 // new contest fields
274
275 adif_str(SS_SEC, inp_SS_Section->value());
276 adif_str(SS_SERNO, inp_SS_SerialNoR->value());
277 adif_str(SS_PREC, inp_SS_Precedence->value());
278 adif_str(SS_CHK, inp_SS_Check->value());
279
280 adif_str(AGE, inp_KD_age->value());
281
282 adif_str(TEN_TEN, inp_1010_nr->value());
283 adif_str(CHECK, inp_ARR_check->value());
284
285 adif_str(TROOPS, progdefaults.my_JOTA_troop.c_str());
286 adif_str(TROOPR, inp_JOTA_troop->value());
287 adif_str(SCOUTS, progdefaults.my_JOTA_scout.c_str());
288 adif_str(SCOUTR, inp_JOTA_scout->value());
289
290 adif_str(OP_CALL, progdefaults.operCall.c_str());
291 adif_str(STA_CALL, progdefaults.myCall.c_str());
292 adif_str(MY_CITY,
293 std::string(progdefaults.myQth).
294 append(", ").
295 append(inp_QP_state_short->value()).c_str());
296 adif_str(MY_GRID, progdefaults.myLocator.c_str());
297
298 adif.append("<eor>");
299
300 // send it to the server
301 XmlRpcValue oneArg, result;
302 oneArg[0] = adif.c_str();
303 log_client->execute("log.add_record", oneArg, result);
304
305 // submit it foreign log programs
306 cQsoRec rec;
307 rec.putField(CALL, inpCall->value());
308 rec.putField(NAME, inpName->value());
309 rec.putField(QSO_DATE, sDate_on.c_str());
310 rec.putField(QSO_DATE_OFF, sDate_off.c_str());
311 rec.putField(TIME_ON, inpTimeOn->value());
312 rec.putField(TIME_OFF, ztime());
313 rec.putField(FREQ, Mhz);
314 rec.putField(ADIF_MODE, mode_info[active_modem->get_mode()].adif_name);
315 rec.putField(QTH, inpQth->value());
316 rec.putField(STATE, inpState->value());
317 rec.putField(VE_PROV, inpVEprov->value());
318 rec.putField(COUNTRY, cboCountry->value());
319 rec.putField(CNTY, inpCounty->value());
320 rec.putField(GRIDSQUARE, inpLoc->value());
321 rec.putField(NOTES, inpNotes->value());
322 rec.putField(QSLRDATE, "");
323 rec.putField(QSLSDATE, "");
324 rec.putField(RST_RCVD, inpRstIn->value ());
325 rec.putField(RST_SENT, inpRstOut->value ());
326 rec.putField(SRX, inpSerNo->value());
327 rec.putField(STX, outSerNo->value());
328 rec.putField(XCHG1, inpXchgIn->value());
329 rec.putField(MYXCHG, progdefaults.myXchg.c_str());
330 rec.putField(CLASS, inpClass->value());
331 rec.putField(ARRL_SECT, inpSection->value());
332 rec.putField(CNTY, "");
333 rec.putField(IOTA, "");
334 rec.putField(DXCC, "");
335 rec.putField(CONT, "");
336 rec.putField(CQZ, "");
337 rec.putField(ITUZ, "");
338 rec.putField(TX_PWR, "");
339
340 // new contest fields
341
342 rec.putField(SS_SEC, inp_SS_Section->value());
343 rec.putField(SS_SERNO, inp_SS_SerialNoR->value());
344 rec.putField(SS_PREC, inp_SS_Precedence->value());
345 rec.putField(SS_CHK, inp_SS_Check->value());
346
347 rec.putField(AGE, inp_KD_age->value());
348
349 rec.putField(TEN_TEN, inp_1010_nr->value());
350 rec.putField(CHECK, inp_ARR_check->value());
351
352 rec.putField(TROOPS, progdefaults.my_JOTA_troop.c_str());
353 rec.putField(TROOPR, inp_JOTA_troop->value());
354 rec.putField(SCOUTS, progdefaults.my_JOTA_scout.c_str());
355 rec.putField(SCOUTR, inp_JOTA_scout->value());
356
357 rec.putField(OP_CALL, progdefaults.operCall.c_str());
358 rec.putField(STA_CALL, progdefaults.myCall.c_str());
359
360 submit_record(rec);
361
362 }
363
xml_check_dup()364 int xml_check_dup()
365 {
366 int dup_test = 0;
367 if (!test_connection()) {
368 LOG_INFO("%s","Logbook server down!");
369 progdefaults.xml_logbook = false;
370 progdefaults.changed = true;
371 activate_log_menus(true);
372 if (!dlgLogbook) create_logbook_dialogs();
373 start_logbook();
374 return dup_test;
375 }
376 XmlRpcValue six_args, result;
377 six_args[0] = inpCall->value();
378 six_args[1] = progdefaults.dupmode ? mode_info[active_modem->get_mode()].adif_name : "0";
379 char tspn[10];
380 snprintf(tspn, sizeof(tspn), "%d", progdefaults.timespan);
381 six_args[2] = progdefaults.duptimespan ? tspn : "0";
382 six_args[3] = progdefaults.dupband ? inpFreq->value() : "0";
383 six_args[4] = (progdefaults.dupstate && inpState->value()[0]) ? inpState->value() : "0";
384 six_args[5] = (progdefaults.dupxchg1 && inpXchgIn->value()[0]) ? inpXchgIn->value() : "0";
385 if (log_client->execute("log.check_dup", six_args, result)) {
386 string res = std::string(result);
387 if (res == "true")
388 dup_test = 1;
389 else if (res == "possible")
390 dup_test = 2;
391 }
392 return dup_test;
393 }
394
xml_update_eqsl()395 void xml_update_eqsl()
396 {
397 adif.erase();
398 adif_str(EQSLSDATE, sDate_on.c_str());
399 adif.append("<EOR>");
400
401 XmlRpcValue oneArg, result;
402 oneArg[0] = adif.c_str();
403 LOG_INFO("%s", "xmlrpc log: update eqsl date");
404 log_client->execute("log.update_record", oneArg, result);
405 }
406
xml_update_lotw()407 void xml_update_lotw()
408 {
409 adif.erase();
410 adif_str(LOTWSDATE, sDate_on.c_str());
411 adif.append("<EOR>");
412
413 XmlRpcValue oneArg, result;
414 oneArg[0] = adif.c_str();
415 LOG_INFO("%s", "xmlrpc log: update LoTW date");
416 log_client->execute("log.update_record", oneArg, result);
417 }
418
connect_to_log_server(void *)419 void connect_to_log_server(void *)
420 {
421 if (log_client) {
422 delete log_client;
423 log_client = 0;
424 }
425 LOG_INFO("%s","Create XMLRPC client");
426 log_client = new XmlRpcClient(
427 progdefaults.xmllog_address.c_str(),
428 atoi(progdefaults.xmllog_port.c_str()));
429
430 LOG_INFO("%s","Created");
431 if (progdefaults.xml_logbook) {
432 if (test_connection(true)) {
433 LOG_INFO("%s","Close local logbook");
434 close_logbook();
435 if (dlgLogbook) dlgLogbook->hide();
436 activate_log_menus(false);
437 } else {
438 LOG_INFO("%s","Remote server not responding");
439 progdefaults.xml_logbook = false;
440 activate_log_menus(true);
441 start_logbook();
442 LOG_INFO("%s","Use local logbook");
443 }
444 } else {
445 LOG_INFO("%s","Enable local logbook");
446 activate_log_menus(true);
447 start_logbook();
448 }
449 }
450
451