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