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