1 // ----------------------------------------------------------------------------
2 // logger.cxx Remote Log Interface for fldigi
3 //
4 // Copyright 2006-2009 W1HKJ, Dave Freese
5 //
6 // This file is part of fldigi.
7 //
8 // Fldigi is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
12 //
13 // Fldigi is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
20 // ----------------------------------------------------------------------------
21 
22 #include <config.h>
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #if !defined(__WOE32__) && !defined(__APPLE__)
27 #  include <sys/ipc.h>
28 #  include <sys/msg.h>
29 #endif
30 #include <errno.h>
31 #include <string>
32 #include <fstream>
33 #include <vector>
34 
35 #include "icons.h"
36 #include "logger.h"
37 #include "lgbook.h"
38 #include "main.h"
39 #include "modem.h"
40 #include "waterfall.h"
41 #include "fl_digi.h"
42 #include "trx.h"
43 #include "debug.h"
44 #include "macros.h"
45 #include "status.h"
46 #include "spot.h"
47 #include "adif_io.h"
48 #include "date.h"
49 #include "configuration.h"
50 #include "flmisc.h"
51 #include "strutil.h"
52 #include "qrunner.h"
53 
54 #include "logsupport.h"
55 #include "lookupcall.h"
56 #include "fd_logger.h"
57 
58 #include "n3fjp_logger.h"
59 
60 #include "dx_cluster.h"
61 
62 #include "network.h"
63 
64 #include <FL/fl_ask.H>
65 
66 using namespace std;
67 
68 //---------------------------------------------------------------------
69 const char *logmode;
70 
71 static string log_msg;
72 static string errmsg;
73 static string notes;
74 
75 //======================================================================
76 // LoTW
77 static string lotw_fname;
78 static string lotw_logfname;
79 static string lotw_send_fname;
80 static string lotw_log_fname;
81 static int tracefile_timeout = 0;
82 
83 string str_lotw;
84 //======================================================================
85 // eQSL
86 void submit_eQSL(cQsoRec &rec, string msg);
87 //======================================================================
88 
89 static string adif;
90 
writeADIF()91 void writeADIF () {
92 // open the adif file
93 	FILE *adiFile;
94 	string fname;
95 
96     fname = TempDir;
97     fname.append("log.adif");
98 	adiFile = fl_fopen (fname.c_str(), "a");
99 	if (adiFile) {
100 // write the current record to the file
101 		fprintf(adiFile,"%s<EOR>\n", adif.c_str());
102 		fclose (adiFile);
103 	}
104 }
105 
putadif(int num,const char * s,string & str=adif)106 void putadif(int num, const char *s, string &str = adif)
107 {
108 	char tempstr[100];
109 	int slen = strlen(s);
110 	int n = snprintf(tempstr, sizeof(tempstr), "<%s:%d>", fields[num].name, slen);
111 	if (n == -1) {
112 		LOG_PERROR("snprintf");
113 		return;
114 	}
115 	str.append(tempstr).append(s);
116 }
117 
118 vector<int> lotw_recs_sent;
119 
clear_lotw_recs_sent()120 void clear_lotw_recs_sent()
121 {
122 	lotw_recs_sent.clear();
123 }
124 
restore_lotwsdates()125 void restore_lotwsdates()
126 {
127 extern int editNbr;
128 	if (lotw_recs_sent.empty()) return;
129 
130 	int recnbr;
131 	cQsoRec *rec;
132 	for (size_t n = 0; n < lotw_recs_sent.size(); n++) {
133 		recnbr = lotw_recs_sent[n];
134 		rec = qsodb.getRec(recnbr);
135 		rec->putField(LOTWSDATE, "");
136 		if (recnbr == editNbr) {
137 			inpLOTWsentdate_log->value("");
138 			inpLOTWsentdate_log->redraw();
139 		}
140 	}
141 	clear_lotw_recs_sent();
142 }
143 
144 static notify_dialog *lotw_alert_window = 0;
145 
check_lotw_log(void *)146 void check_lotw_log(void *)
147 {
148 	FILE * logfile = fl_fopen(lotw_log_fname.c_str(), "rb");
149 
150 	if (!logfile) {
151 		tracefile_timeout--;
152 		if (!tracefile_timeout) {
153 			LOG_ERROR(_("NO tqsl log file in %d seconds!"), progdefaults.tracefile_timeout);
154 			restore_lotwsdates();
155 			clear_lotw_recs_sent();
156 			return;
157 		}
158 		Fl::repeat_timeout(1.0, check_lotw_log);
159 		return;
160 	}
161 
162 	string trace_text;
163 	fseek(logfile, 0, SEEK_END);
164 	size_t logfile_size = ftell(logfile);
165 	rewind(logfile);
166 
167 	if (logfile_size == 0) {
168 		tracefile_timeout--;
169 		if (!tracefile_timeout) {
170 			LOG_ERROR(_("Tqsl log file empty! waited %d seconds!"), progdefaults.tracefile_timeout);
171 			restore_lotwsdates();
172 			clear_lotw_recs_sent();
173 			return;
174 		}
175 		Fl::repeat_timeout(1.0, check_lotw_log);
176 		return;
177 	}
178 
179 	int ch;
180 	for (size_t n = 0; n < logfile_size; n++) {
181 		ch = fgetc(logfile);
182 		if (ch >= 0x20 && ch <= 0x7F)
183 			trace_text += char(ch);
184 		else
185 			trace_text.append(ascii3[ch & 0xFF]);
186 	}
187 	fclose(logfile);
188 
189 	size_t p1 = trace_text.find("UploadFile returns 0");
190 	size_t p2 = trace_text.find("Final Status: Success");
191 
192 	if ((p1 == string::npos) && (p2 == string::npos)) {
193 		tracefile_timeout--;
194 		if (!tracefile_timeout) {
195 			LOG_ERROR("%s", "TQSL trace file failed!");
196 			if (progdefaults.lotw_quiet_mode) {
197 				std::string alert;
198 				alert.assign(_("LoTW upload error!"));
199 				alert.append(_("\nView LoTW trace log:\n"));
200 				alert.append(lotw_log_fname);
201 				if (!lotw_alert_window) lotw_alert_window = new notify_dialog;
202 				lotw_alert_window->notify(alert.c_str(), 15.0);
203 				REQ(show_notifier, lotw_alert_window);
204 			}
205 			restore_lotwsdates();
206 			clear_lotw_recs_sent();
207 			return;
208 		}
209 		Fl::repeat_timeout(1.0, check_lotw_log);
210 		return;
211 	}
212 
213 	if (progdefaults.lotw_quiet_mode && progdefaults.lotw_show_delivery) {
214 		if (!lotw_alert_window) lotw_alert_window = new notify_dialog;
215 		std::string alert;
216 		alert.assign(_("LoTW upload OK"));
217 		if (p2 != std::string::npos) {
218 			alert.append("\n").append(trace_text.substr(p2));
219 			p1 = alert.find("<CR>");
220 			if (p1 != std::string::npos) alert.erase(p1);
221 		}
222 		lotw_alert_window->notify(alert.c_str(), 5.0);
223 		REQ(show_notifier, lotw_alert_window);
224 LOG_INFO("%s", alert.c_str());
225 	}
226 	if (progdefaults.xml_logbook)
227 		xml_update_lotw();
228 	clear_lotw_recs_sent();
229 	return;
230 }
231 
send_to_lotw(void *)232 void send_to_lotw(void *)
233 {
234 	if (progdefaults.lotw_pathname.empty())
235 		return;
236 	if (str_lotw.empty()) return;
237 
238 	lotw_send_fname = LoTWDir;
239 	lotw_send_fname.append("fldigi_lotw_").append(zdate());
240 	lotw_send_fname.append("_").append(ztime());
241 	lotw_send_fname.append(".adi");
242 
243 	ofstream outfile(lotw_send_fname.c_str());
244 	outfile << str_lotw;
245 	outfile.close();
246 
247 	str_lotw.clear();
248 
249 	lotw_log_fname = LoTWDir;
250 	lotw_log_fname.append("lotw_trace.txt"); // LoTW trace file
251 	rotate_log(lotw_log_fname);
252 	remove(lotw_log_fname.c_str());
253 
254 	string pstring;
255 	pstring.assign("\"").append(progdefaults.lotw_pathname).append("\"");
256 	pstring.append(" -d -u -a compliant");
257 
258 	if (progdefaults.submit_lotw_password)
259 		pstring.append(" -p \"").append(progdefaults.lotw_pwd).append("\"");
260 
261 	if (!progdefaults.lotw_location.empty())
262 		pstring.append(" -l \"").append(progdefaults.lotw_location).append("\"");
263 
264 	pstring.append(" -t \"").append(lotw_log_fname).append("\"");
265 
266 	pstring.append(" \"").append(lotw_send_fname).append("\"");
267 
268 	if (progdefaults.lotw_quiet_mode)
269 		pstring.append(" -q");
270 
271 	LOG_DEBUG("LoTW command string: %s", pstring.c_str());
272 
273 	start_process(pstring);
274 
275 	tracefile_timeout = progdefaults.tracefile_timeout;
276 	Fl::add_timeout(0, check_lotw_log);
277 }
278 
lotw_rec(cQsoRec & rec)279 string lotw_rec(cQsoRec &rec)
280 {
281 	string temp;
282 	string strrec;
283 
284 	putadif(CALL, rec.getField(CALL), strrec);
285 
286 	putadif(ADIF_MODE, adif2export(rec.getField(ADIF_MODE)).c_str(), strrec);
287 
288 	string sm = adif2submode(rec.getField(ADIF_MODE));
289 	if (!sm.empty())
290 	putadif(SUBMODE, sm.c_str(), strrec);
291 
292 	temp = rec.getField(FREQ);
293 	temp.resize(7);
294 	putadif(FREQ, temp.c_str(), strrec);
295 
296 	putadif(QSO_DATE, rec.getField(QSO_DATE), strrec);
297 
298 	putadif(BAND, rec.getField(BAND), strrec);
299 
300 	temp = rec.getField(TIME_ON);
301 	if (temp.length() > 4) temp.erase(4);
302 	putadif(TIME_ON, temp.c_str(), strrec);
303 
304 	strrec.append("<EOR>\n");
305 
306 	return strrec;
307 }
308 
submit_lotw(cQsoRec & rec)309 void submit_lotw(cQsoRec &rec)
310 {
311 	string adifrec = lotw_rec(rec);
312 
313 	if (adifrec.empty()) return;
314 
315 	if (progdefaults.submit_lotw) {
316 		if (str_lotw.empty())
317 			str_lotw = "Fldigi LoTW upload file\n<ADIF_VER:5>2.2.7\n<EOH>\n";
318 		str_lotw.append(adifrec);
319 		Fl::awake(send_to_lotw);
320 	}
321 }
322 
submit_ADIF(cQsoRec & rec)323 void submit_ADIF(cQsoRec &rec)
324 {
325 	adif.erase();
326 
327 	putadif(QSO_DATE, rec.getField(QSO_DATE));
328 	putadif(TIME_ON, rec.getField(TIME_ON));
329 	putadif(QSO_DATE_OFF, rec.getField(QSO_DATE_OFF));
330 	putadif(TIME_OFF, rec.getField(TIME_OFF));
331 	putadif(CALL, rec.getField(CALL));
332 	putadif(FREQ, rec.getField(FREQ));
333 	putadif(ADIF_MODE, adif2export(rec.getField(ADIF_MODE)).c_str());
334 
335 	string sm = adif2submode(rec.getField(ADIF_MODE));
336 	if (!sm.empty())
337 		putadif(SUBMODE, sm.c_str());
338 
339 	putadif(RST_SENT, rec.getField(RST_SENT));
340 	putadif(RST_RCVD, rec.getField(RST_RCVD));
341 	putadif(TX_PWR, rec.getField(TX_PWR));
342 	putadif(NAME, rec.getField(NAME));
343 	putadif(QTH, rec.getField(QTH));
344 	putadif(STATE, rec.getField(STATE));
345 	putadif(VE_PROV, rec.getField(VE_PROV));
346 	putadif(COUNTRY, rec.getField(COUNTRY));
347 	putadif(GRIDSQUARE, rec.getField(GRIDSQUARE));
348 	putadif(STX, rec.getField(STX));
349 	putadif(SRX, rec.getField(SRX));
350 	putadif(XCHG1, rec.getField(XCHG1));
351 	putadif(MYXCHG, rec.getField(MYXCHG));
352 	notes = rec.getField(NOTES);
353 	for (size_t i = 0; i < notes.length(); i++)
354 	    if (notes[i] == '\n') notes[i] = ';';
355 	putadif(NOTES, notes.c_str());
356 // these fields will always be blank unless they are added to the main
357 // QSO log area.
358 	putadif(IOTA, rec.getField(IOTA));
359 	putadif(DXCC, rec.getField(DXCC));
360 	putadif(QSL_VIA, rec.getField(QSL_VIA));
361 	putadif(QSLRDATE, rec.getField(QSLRDATE));
362 	putadif(QSLSDATE, rec.getField(QSLSDATE));
363 	putadif(EQSLRDATE, rec.getField(EQSLRDATE));
364 	putadif(EQSLSDATE, rec.getField(EQSLSDATE));
365 	putadif(LOTWRDATE, rec.getField(LOTWRDATE));
366 	putadif(LOTWSDATE, rec.getField(LOTWSDATE));
367 
368 	writeADIF();
369 }
370 
371 //---------------------------------------------------------------------
372 // the following IPC message is compatible with xlog remote data spec.
373 //---------------------------------------------------------------------
374 
375 #if !defined(__WOE32__) && !defined(__APPLE__)
376 
377 #define addtomsg(x, y)	log_msg = log_msg + (x) + (y) + LOG_MSEPARATOR
378 
send_IPC_log(cQsoRec & rec)379 static void send_IPC_log(cQsoRec &rec)
380 {
381 	msgtype msgbuf;
382 	const char   LOG_MSEPARATOR[2] = {1,0};
383 	int msqid, len;
384 
385 	int mm, dd, yyyy;
386 	char szdate[9];
387 	char sztime[5];
388 	strncpy(szdate, rec.getField(QSO_DATE_OFF), 8);
389 	szdate[8] = 0;
390 	sscanf(&szdate[6], "%d", &dd); szdate[6] = 0;
391 	sscanf(&szdate[4], "%d", &mm); szdate[4] = 0;
392 	sscanf(szdate, "%d", &yyyy);
393 	Date logdate(mm, dd, yyyy);
394 
395 	log_msg.clear();
396 	log_msg = string("program:") + PACKAGE_NAME + string(" v ") + PACKAGE_VERSION + LOG_MSEPARATOR;
397 	addtomsg("version:",	LOG_MVERSION);
398 	addtomsg("date:",		logdate.szDate(5));
399 	memset(sztime, 0, sizeof(sztime) / sizeof(char));
400 	strncpy(sztime, rec.getField(TIME_ON), (sizeof(sztime) / sizeof(char)) - 1);
401 	addtomsg("TIME:",		sztime);
402 	memset(sztime, 0, 5);
403 	strncpy(sztime, rec.getField(TIME_OFF), 4);
404 	addtomsg("endtime:",            sztime);
405 	addtomsg("call:",		rec.getField(CALL));
406 	addtomsg("mhz:",		rec.getField(FREQ));
407 	addtomsg("mode:",		adif2export(rec.getField(ADIF_MODE)));
408 
409 	string sm = adif2submode(rec.getField(ADIF_MODE));
410 	if (!sm.empty())
411 		addtomsg("submode:",	sm.c_str());
412 
413 	addtomsg("tx:",			rec.getField(RST_SENT));
414 	addtomsg("rx:",			rec.getField(RST_RCVD));
415 	addtomsg("name:",		rec.getField(NAME));
416 	addtomsg("qth:",		rec.getField(QTH));
417 	addtomsg("state:",		rec.getField(STATE));
418 	addtomsg("province:",	        rec.getField(VE_PROV));
419 	addtomsg("country:",	        rec.getField(COUNTRY));
420 	addtomsg("locator:",	        rec.getField(GRIDSQUARE));
421 	addtomsg("serialout:",	        rec.getField(STX));
422 	addtomsg("serialin:",	        rec.getField(SRX));
423 	addtomsg("free1:",		rec.getField(XCHG1));
424 	notes = rec.getField(NOTES);
425 	for (size_t i = 0; i < notes.length(); i++)
426 	    if (notes[i] == '\n') notes[i] = ';';
427 	addtomsg("notes:",		notes.c_str());
428 	addtomsg("power:",		rec.getField(TX_PWR));
429 
430 	strncpy(msgbuf.mtext, log_msg.c_str(), sizeof(msgbuf.mtext));
431 	msgbuf.mtext[sizeof(msgbuf.mtext) - 1] = '\0';
432 
433 	if ((msqid = msgget(LOG_MKEY, 0666 | IPC_CREAT)) == -1) {
434 		LOG_PERROR("msgget");
435 		return;
436 	}
437 	msgbuf.mtype = LOG_MTYPE;
438 	len = strlen(msgbuf.mtext) + 1;
439 	if (msgsnd(msqid, &msgbuf, len, IPC_NOWAIT) < 0)
440 		LOG_PERROR("msgsnd");
441 }
442 
443 #endif
444 
submit_record(cQsoRec & rec)445 void submit_record(cQsoRec &rec)
446 {
447 #if !defined(__WOE32__) && !defined(__APPLE__)
448 	send_IPC_log(rec);
449 #endif
450 	submit_ADIF(rec);
451 	if (progdefaults.submit_lotw)
452 		submit_lotw(rec);
453 	if (progdefaults.eqsl_when_logged)
454 		submit_eQSL(rec, "");
455 	n3fjp_add_record(rec);
456 }
457 
458 //---------------------------------------------------------------------
459 
460 extern void xml_add_record();
461 
submit_log(void)462 void submit_log(void)
463 {
464 	if (progdefaults.spot_when_logged) {
465 		if (!dxcluster_viewer->visible()) dxcluster_viewer->show();
466 		send_DXcluster_spot();
467 	}
468 
469 	if (progStatus.spot_log)
470 		spot_log(inpCall->value(), inpLoc->value());
471 
472 	logmode = mode_info[active_modem->get_mode()].adif_name;
473 
474 	if (progdefaults.xml_logbook && qsodb.isdirty()) {
475 		xml_add_record();
476 		qsodb.isdirty(0);
477 	} else
478 		AddRecord();
479 
480 	if (FD_logged_on) FD_add_record();
481 
482 //	if (progdefaults.eqsl_when_logged)
483 //		makeEQSL("");
484 
485 }
486 
487 //======================================================================
488 // thread to support sending log entry to eQSL
489 //======================================================================
490 
491 pthread_t* EQSLthread = 0;
492 pthread_mutex_t EQSLmutex = PTHREAD_MUTEX_INITIALIZER;
493 pthread_cond_t EQSLcond = PTHREAD_COND_INITIALIZER;
494 
495 static void *EQSL_loop(void *args);
496 static void EQSL_init(void);
497 
498 void EQSL_close(void);
499 void EQSL_send();
500 
501 static std::string EQSL_url = "";
502 static std::string SEND_url = "";
503 static std::string EQSL_xmlpage = "";
504 
505 static bool EQSLEXIT = false;
506 
507 static notify_dialog *eqsl_alert_window = 0;
508 
update_eQSL_fields(void *)509 void update_eQSL_fields(void *)
510 {
511 	std::string call;
512 	std::string date;
513 	std::string mode = "MODE";
514 
515 	size_t p = EQSL_url.find("<CALL:");
516 	if (p == std::string::npos) return;
517 	p = EQSL_url.find(">", p);
518 	if (p == std::string::npos) return;
519 	p++;
520 	size_t p2 = EQSL_url.find("<", p);
521 	if (p2 == std::string::npos) return;
522 	call = EQSL_url.substr(p, p2 - p);
523 	p = EQSL_url.find("<QSO_DATE:");
524 	if (p == std::string::npos) return;
525 	p = EQSL_url.find(">", p);
526 	if (p == std::string::npos) return;
527 	p++;
528 	p2 = EQSL_url.find("<", p);
529 	if (p2 == std::string::npos) return;
530 	date = EQSL_url.substr(p, p2 - p);
531 	p = EQSL_url.find("<MODE:");
532 	if (p != std::string::npos) {
533 		p = EQSL_url.find(">", p);
534 		if (p != std::string::npos) {
535 			p2 = EQSL_url.find("<", ++p);
536 			if (p2 != std::string::npos)
537 				mode = EQSL_url.substr(p, p2 - p);
538 		}
539 	}
540 	p = EQSL_url.find("<SUBMODE:");
541 	if (p != std::string::npos) {
542 		p = EQSL_url.find(">", p);
543 		if (p != std::string::npos) {
544 			p2 = EQSL_url.find("<", ++p);
545 			if (p2 != std::string::npos) {
546 				std::string submode = EQSL_url.substr(p, p2 - p);
547 				if (!submode.empty()) mode = submode;
548 			}
549 		}
550 	}
551 	inpEQSLsentdate_log->value(date.c_str());
552 
553 	if (progdefaults.xml_logbook)
554 		xml_update_eqsl();
555 	else
556 		updateRecord();
557 
558 	std::string qsl_logged = "eQSL logged: ";
559 	qsl_logged.append(call).append(" on ").append(mode);
560 
561 	if (progdefaults.eqsl_show_delivery) {
562 		if (!eqsl_alert_window) eqsl_alert_window = new notify_dialog;
563 		eqsl_alert_window->notify(qsl_logged.c_str(), 5.0);
564 		REQ(show_notifier, eqsl_alert_window);
565 	}
566 
567 	LOG_INFO("%s", qsl_logged.c_str());
568 }
569 
cannot_connect_to_eqsl(void *)570 static void cannot_connect_to_eqsl(void *)
571 {
572 	std::string msg = "Cannot connect to www.eQSL.cc";
573 
574 	if (!eqsl_alert_window) eqsl_alert_window = new notify_dialog;
575 	eqsl_alert_window->notify(msg.c_str(), 5.0);
576 	REQ(show_notifier, eqsl_alert_window);
577 }
578 
eqsl_error(void *)579 static void eqsl_error(void *)
580 {
581 	size_t p = EQSL_xmlpage.find("Error:");
582 	size_t p2 = EQSL_xmlpage.find('\n', p);
583 	std::string errstr = "eQSL error:";
584 	errstr.append("testing 1.2.3");
585 	errstr.append(EQSL_xmlpage.substr(p, p2 - p - 1));
586 	errstr.append("\n").append(EQSL_url.substr(0,40));
587 
588 	if (!eqsl_alert_window) eqsl_alert_window = new notify_dialog;
589 	eqsl_alert_window->notify(errstr.c_str(), 5.0);
590 	REQ(show_notifier, eqsl_alert_window);
591 	LOG_ERROR("%s", errstr.c_str());
592 }
593 
EQSL_loop(void * args)594 static void *EQSL_loop(void *args)
595 {
596 	SET_THREAD_ID(EQSL_TID);
597 
598 	SET_THREAD_CANCEL();
599 
600 	for (;;) {
601 		TEST_THREAD_CANCEL();
602 		pthread_mutex_lock(&EQSLmutex);
603 		pthread_cond_wait(&EQSLcond, &EQSLmutex);
604 		pthread_mutex_unlock(&EQSLmutex);
605 
606 		if (EQSLEXIT)
607 			return NULL;
608 
609 		size_t p;
610 		if (get_http(SEND_url, EQSL_xmlpage, 5.0) <= 0) {
611 			REQ(cannot_connect_to_eqsl, (void *)0);
612 		}
613 		else if ((p = EQSL_xmlpage.find("Error:")) != std::string::npos) {
614 			REQ(eqsl_error, (void *)0);
615 		} else {
616 			REQ(update_eQSL_fields, (void *)0);
617 		}
618 
619 	}
620 	return NULL;
621 }
622 
EQSL_close(void)623 void EQSL_close(void)
624 {
625 	ENSURE_THREAD(FLMAIN_TID);
626 
627 	if (!EQSLthread)
628 		return;
629 
630 	CANCEL_THREAD(*EQSLthread);
631 
632 	pthread_mutex_lock(&EQSLmutex);
633 	EQSLEXIT = true;
634 	pthread_cond_signal(&EQSLcond);
635 	pthread_mutex_unlock(&EQSLmutex);
636 
637 	pthread_join(*EQSLthread, NULL);
638 	delete EQSLthread;
639 	EQSLthread = 0;
640 }
641 
EQSL_init(void)642 static void EQSL_init(void)
643 {
644 	ENSURE_THREAD(FLMAIN_TID);
645 
646 	if (EQSLthread)
647 		return;
648 	EQSLthread = new pthread_t;
649 	EQSLEXIT = false;
650 	if (pthread_create(EQSLthread, NULL, EQSL_loop, NULL) != 0) {
651 		LOG_PERROR("pthread_create");
652 		return;
653 	}
654 	MilliSleep(10);
655 }
656 
submit_eQSL(cQsoRec & rec,string msg)657 void submit_eQSL(cQsoRec &rec, string msg)
658 {
659 	string eQSL_data;
660 	string tempstr;
661 	char sztemp[100];
662 
663 	eQSL_data = "upload <adIF_ver:4>4.0 ";
664 	snprintf(sztemp, sizeof(sztemp),"<EQSL_USER:%d>%s<EQSL_PSWD:%d>%s",
665 		static_cast<int>(progdefaults.eqsl_id.length()),
666 		ucasestr(progdefaults.eqsl_id).c_str(),
667 		static_cast<int>(progdefaults.eqsl_pwd.length()),
668 		progdefaults.eqsl_pwd.c_str());
669 	eQSL_data.append(sztemp);
670 	eQSL_data.append("<PROGRAMID:6>FLDIGI<EOH>");
671 	if (!progdefaults.eqsl_nick.empty()) {
672 		snprintf(sztemp, sizeof(sztemp), "<APP_EQSL_QTH_NICKNAME:%d>%s",
673 			static_cast<int>(progdefaults.eqsl_nick.length()),
674 			progdefaults.eqsl_nick.c_str());
675 		eQSL_data.append(sztemp);
676 	}
677 
678 	putadif(CALL, rec.getField(CALL), eQSL_data);
679 	putadif(ADIF_MODE, adif2export(rec.getField(ADIF_MODE)).c_str(), eQSL_data);
680 
681 	string sm = adif2submode(rec.getField(ADIF_MODE));
682 	if (!sm.empty())
683 		putadif(SUBMODE, sm.c_str(), eQSL_data);
684 
685 	tempstr = rec.getField(FREQ);
686 	tempstr.resize(7);
687 	putadif(FREQ, tempstr.c_str(), eQSL_data);
688 	putadif(BAND, rec.getField(BAND), eQSL_data);
689 	putadif(QSO_DATE, sDate_on.c_str(), eQSL_data);
690 	tempstr = rec.getField(TIME_ON);
691 	if (tempstr.length() > 4) tempstr.erase(4);
692 	putadif(TIME_ON, tempstr.c_str(), eQSL_data);
693 	putadif(RST_SENT, rec.getField(RST_SENT), eQSL_data);
694 
695 	size_t p = 0;
696 	if (msg.empty()) msg = progdefaults.eqsl_default_message;
697 	if (!msg.empty()) {
698 // replace message tags {CALL}, {NAME}, {MODE}
699 		while ((p = msg.find("{CALL}")) != std::string::npos)
700 			msg.replace(p, 6, inpCall->value());
701 		while ((p = msg.find("{NAME}")) != std::string::npos)
702 			msg.replace(p, 6, inpName->value());
703 		while ((p = msg.find("{MODE}")) != std::string::npos) {
704 			tempstr.assign(mode_info[active_modem->get_mode()].export_mode);
705 			sm = mode_info[active_modem->get_mode()].export_submode;
706 			if (!sm.empty())
707 				tempstr.append("/").append(sm);
708 			msg.replace(p, 6, tempstr);
709 		}
710 		snprintf(sztemp, sizeof(sztemp), "<QSLMSG:%d>%s",
711 			static_cast<int>(msg.length()),
712 			msg.c_str());
713 		eQSL_data.append(sztemp);
714 	}
715 	eQSL_data.append("<EOR>\n");
716 
717 	std::string eQSL_header = progdefaults.eqsl_www_url;
718 
719 	EQSL_url.assign(eQSL_header).append(eQSL_data);
720 
721 	tempstr.clear();
722 	for (size_t n = 0; n < eQSL_data.length(); n++) {
723 		if (eQSL_data[n] == ' ' || eQSL_data[n] == '\n') tempstr.append("%20");
724 		else if (eQSL_data[n] == '<') tempstr.append("%3C");
725 		else if (eQSL_data[n] == '>') tempstr.append("%3E");
726 		else if (eQSL_data[n] == '_') tempstr.append("%5F");
727 		else if (eQSL_data[n] == ':') tempstr.append("%3A");
728 		else if (eQSL_data[n] > ' ' && eQSL_data[n] <= '}')
729 			tempstr += eQSL_data[n];
730 	}
731 
732 	eQSL_data = eQSL_header;
733 	eQSL_data.append(tempstr);
734 
735 	sendEQSL(eQSL_data.c_str());
736 
737 }
738 
sendEQSL(const char * url)739 void sendEQSL(const char *url)
740 {
741 	ENSURE_THREAD(FLMAIN_TID);
742 
743 	if (!EQSLthread)
744 		EQSL_init();
745 
746 	pthread_mutex_lock(&EQSLmutex);
747 	SEND_url = url;
748 	pthread_cond_signal(&EQSLcond);
749 	pthread_mutex_unlock(&EQSLmutex);
750 }
751 
752 // this function may be called from several places including macro
753 // expansion and execution
754 
makeEQSL(const char * message)755 void makeEQSL(const char *message)
756 {
757 	cQsoRec *rec;
758 	if (qsodb.nbrRecs() <= 0) return;
759 	rec = qsodb.getRec(qsodb.nbrRecs() - 1); // last record
760 	submit_eQSL(*rec, message);
761 }
762