1 // ----------------------------------------------------------------------------
2 //      pskrep.cxx
3 //
4 // Copyright (C) 2008-2009
5 //              Stelios Bounanos, M0GLD
6 //
7 // This is a client for N1DQ's PSK Automatic Propagation Reporter
8 // (see http://pskreporter.info/).  Philip Gladstone, N1DQ, is
9 // thanked for his helpful explanation of the protocol.
10 //
11 //
12 // This file is part of fldigi.
13 //
14 // fldigi is free software; you can redistribute it and/or modify
15 // it under the terms of the GNU General Public License as published by
16 // the Free Software Foundation; either version 3 of the License, or
17 // (at your option) any later version.
18 //
19 // fldigi is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 // ----------------------------------------------------------------------------
27 
28 #include <config.h>
29 
30 #ifdef __MINGW32__
31 #  include "compat.h"
32 #endif
33 
34 #if HAVE_SYS_UTSNAME_H
35 #  include <sys/utsname.h>
36 #endif
37 
38 #include <sys/time.h>
39 #if HAVE_ARPA_INET_H
40 #  include <arpa/inet.h>
41 #endif
42 
43 #include <stdint.h>
44 #include <cstring>
45 #include <cstdlib>
46 
47 #include <string>
48 #include <sstream>
49 
50 #include <deque>
51 #include <vector>
52 #include <algorithm>
53 #include <fstream>
54 
55 #if GCC_VER_OK
56 #	if HAVE_STD_HASH
57 #		define MAP_TYPE std::unordered_map
58 #		define HASH_TYPE std::hash
59 #		include <unordered_map>
60 #	else
61 #		if 	HAVE_STD_TR1_HASH
62 #			define MAP_TYPE std::tr1::unordered_map
63 #			define HASH_TYPE std::tr1::hash
64 #			include <tr1/unordered_map>
65 #		endif
66 #	endif
67 #else
68 // use the non-standard gnu hash_map on gcc < 4.1.0
69 // which has a broken tr1::unordered_map::operator=
70 #	define MAP_TYPE __gnu_cxx::hash_map
71 #	define HASH_TYPE __gnu_cxx::hash
72 #	include <ext/hash_map>
73 namespace __gnu_cxx {
74 	// define the missing hash specialisation for std::string
75 	// using the 'const char*' hash function
76 	template<> struct hash<std::string> {
operator ()__gnu_cxx::hash77 		size_t operator()(const std::string& s) const { return __stl_hash_string(s.c_str()); }
78 	};
79 }
80 #endif
81 
82 #include <FL/Fl.H>
83 
84 #include "socket.h"
85 #include "re.h"
86 #include "debug.h"
87 #include "util.h"
88 #include "trx.h"
89 #include "fl_digi.h"
90 #include "main.h"
91 #include "configuration.h"
92 #include "globals.h"
93 #include "spot.h"
94 
95 #include "pskrep.h"
96 
97 LOG_FILE_SOURCE(debug::LOG_SPOTTER);
98 
99 //------------------------------------------------------------------------------
100 #include "threads.h"
101 
102 static pthread_mutex_t pskrep_mutex = PTHREAD_MUTEX_INITIALIZER;
103 //------------------------------------------------------------------------------
104 
105 // -------------------------------------------------------------------------------------------------
106 
107 // Try to flush the report queue every SEND_INTERVAL seconds.
108 #define SEND_INTERVAL 300
109 
110 // Ignore reports that are less than DUP_INTERVAL seconds older than
111 // a previously sent report for the same callsign and frequency band.
112 // Sent reports are also garbage-collected after DUP_INTERVAL seconds.
113 #define DUP_INTERVAL 1800
114 
115 // The first TEMPLATE_THRESHOLD packets will contain the long templates;
116 // the next TEMPLATE_THRESHOLD packets will include the short templates
117 #define TEMPLATE_THRESHOLD 3
118 // Resend short templates every TEMPLATE_INTERVAL seconds
119 #define TEMPLATE_INTERVAL 1800
120 
121 // Maximum send size
122 #define DGRAM_MAX (1500-14-24-8)
123 
124 #define PSKREP_QUEUE_FILE "pskrqueue.txt"
125 #define PSKREP_ID_FILE "pskrkey.txt"
126 
127 // -------------------------------------------------------------------------------------------------
128 
129 using namespace std;
130 
131 enum status_t { PSKR_STATUS_NEW, PSKR_STATUS_PENDING, PSKR_STATUS_SENT };
132 enum rtype_t { PSKREP_AUTO = 1, PSKREP_LOG = 2, PSKREP_MANUAL = 3 };
133 
134 struct rcpt_report_t
135 {
rcpt_report_trcpt_report_t136 	rcpt_report_t(trx_mode m = 0, long long f = 0, time_t t = 0,
137 		      rtype_t p = PSKREP_AUTO, string loc = "")
138 		: mode(m), freq(f), rtime(t), rtype(p),
139 		  status(PSKR_STATUS_NEW), locator(loc) { }
140 
141 	trx_mode mode;
142 	long long freq;
143 	time_t rtime;
144 	rtype_t rtype;
145 
146 	status_t status;
147 
148 	string locator;
149 };
150 
151 // A band_map_t holds a list of reception reports (for a particular callsign and band)
152 typedef deque<rcpt_report_t> band_map_t;
153 // A call_map_t holds reception reports for a particular callsign
154 typedef MAP_TYPE<band_t, band_map_t, HASH_TYPE<int> > call_map_t;
155 // A container of this type holds all reception reports, sorted by callsign and band
156 typedef MAP_TYPE<string, call_map_t> queue_t;
157 
158 class pskrep_sender
159 {
160 public:
161 	pskrep_sender(const string& call, const string& loc, const string& ant,
162 		      const string& host_, const string& port_,
163 		      const string& long_id_, const string& short_id_);
164 	~pskrep_sender();
165 
166 	bool append(const string& callsign, const band_map_t::value_type& r);
167 	bool send(void);
168 
169 private:
170 	void write_station_info(void);
171 	void write_preamble(void);
172 
173 	string recv_callsign, recv_locator, recv_antenna;
174 	string host, port;
175 	string long_id, short_id;
176 
177 	static const unsigned char long_station_info_template[];
178 	static const unsigned char short_station_info_template[];
179 	static const unsigned char rcpt_record_template[];
180 	vector<unsigned char> long_station_info;
181 	vector<unsigned char> short_station_info;
182 
183 	//uint32_t identifier;
184 	uint32_t sequence_number;
185 
186 	unsigned template_count;
187 	time_t last_template;
188 
189 	Socket* send_socket;
190 	unsigned char* dgram;
191 	size_t dgram_size;
192 	size_t report_offset;
193 
194 	void create_socket(void);
195 	pthread_t resolver_thread;
196 	static void* resolver(void* obj);
197 
198 	static const char hexsym[];
199 
200 	static size_t pad(size_t len, size_t mult);
201 };
202 
203 
204 class pskrep
205 {
206 public:
207 	pskrep(const string& call, const string& loc, const string& ant,
208 	       const string& host, const string& port,
209 	       const string& long_id, const string& short_id,
210 	       bool reg_auto, bool reg_log, bool reg_manual);
211 	~pskrep();
212 
213 	static void recv(trx_mode mode, int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj);
214 	static void log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
215 	static void manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
216 	bool progress(void);
count(void)217 	unsigned count(void) { return new_count; }
218 
219 	static fre_t locator_re;
220 private:
221 
222 	void append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype);
223 	void gc(void);
224 
225 	void load_queue(void);
226 	void save_queue(void);
227 
not_sent(const band_map_t::value_type & r)228 	static bool not_sent(const band_map_t::value_type& r)
229 	{
230 		return r.status != PSKR_STATUS_SENT;
231 	}
232 
233 	queue_t queue;
234 	pskrep_sender sender;
235 	unsigned new_count;
236 };
237 
238 fre_t pskrep::locator_re("[a-r]{2}[0-9]{2}[a-x]{2}", REG_EXTENDED | REG_NOSUB | REG_ICASE);
239 
240 #define SHORT_ID_SIZE 4
241 #define LONG_ID_SIZE 8
242 
243 // -------------------------------------------------------------------------------------------------
244 
245 static string error_string;
246 
pskrep_check(void)247 static bool pskrep_check(void)
248 {
249 	struct {
250 		const string* var;
251 		const char* msg;
252 	} check[] = {
253 		{ &progdefaults.myCall, "callsign" },
254 		{ &progdefaults.myLocator, "locator" },
255 		{ &progdefaults.myAntenna, "antenna info" },
256 	};
257 	for (size_t i = 0; i < sizeof(check)/sizeof(*check); i++) {
258 		if (check[i].var->empty()) {
259 			error_string.assign("Error: missing ").append(check[i].msg);
260 			return false;
261 		}
262 	}
263 	if (!pskrep::locator_re.match(progdefaults.myLocator.c_str())) {
264 		error_string = "Error: bad Maidenhead locator\ncheck Configure->Operator->Locator";
265 		return false;
266 	}
267 
268 	return true;
269 }
270 
pskrep_error(void)271 const char* pskrep_error(void)
272 {
273 	return error_string.c_str();
274 }
275 
pskrep_progress(void * obj)276 static void pskrep_progress(void* obj)
277 {
278 	if (reinterpret_cast<pskrep*>(obj)->progress())
279 		Fl::add_timeout(SEND_INTERVAL, pskrep_progress, obj);
280 	else
281 		pskrep_stop();
282 }
283 
pskrep_make_id(string & id,size_t len)284 static void pskrep_make_id(string& id, size_t len)
285 {
286 	id.resize(len);
287 
288 	ifstream f("/dev/urandom");
289 	if (f) {
290 		for (size_t i = 0; i < len; i++)
291 			while ((id[i] = f.get()) != EOF && !isgraph(id[i]));
292 		f.close();
293 	}
294 	else {
295 		unsigned seed = time(NULL);
296 		if (!progdefaults.myCall.empty())
297 			seed ^= simple_hash_str((const unsigned char*)progdefaults.myCall.c_str());
298 		srand(seed);
299 		for (size_t i = 0; i < len; i++)
300 			while (!isgraph(id[i] = rand() % 0x7F));
301 	}
302 }
303 
304 static pskrep* pskr = 0;
305 
pskrep_start(void)306 bool pskrep_start(void)
307 {
308 	if (pskr)
309 		return true;
310 	else if (!pskrep_check())
311 		return false;
312 
313 	// get identifier
314 	string fname = TempDir;
315 	fname.append(PSKREP_ID_FILE);
316 	ifstream in(fname.c_str());
317 	string long_id, short_id;
318 	if (in)
319 		in >> long_id >> short_id;
320 	if (!in || in.eof()) {
321 		in.close();
322 		pskrep_make_id(long_id, LONG_ID_SIZE);
323 		pskrep_make_id(short_id, SHORT_ID_SIZE);
324 
325 		ofstream out(fname.c_str());
326 		if (out)
327 			out << long_id << ' ' << short_id << '\n';
328 		else
329 			LOG_ERROR("Could not write identifiers (\"%s\", \"%s\") to %s",
330 				  long_id.c_str(), short_id.c_str(), fname.c_str());
331 	}
332 
333 	pskr = new pskrep(progdefaults.myCall, progdefaults.myLocator, progdefaults.myAntenna,
334 			  progdefaults.pskrep_host, progdefaults.pskrep_port, long_id, short_id,
335 			  progdefaults.pskrep_auto, progdefaults.pskrep_log, true);
336 	Fl::add_timeout(SEND_INTERVAL, pskrep_progress, pskr);
337 
338 	return true;
339 }
340 
pskrep_stop(void)341 void pskrep_stop(void)
342 {
343 	Fl::remove_timeout(pskrep_progress, pskr);
344 
345 	delete pskr;
346 	pskr = 0;
347 }
348 
pskrep_count(void)349 unsigned pskrep_count(void)
350 {
351 	return pskr ? pskr->count() : 0;
352 }
353 
354 // -------------------------------------------------------------------------------------------------
355 
pskrep(const string & call,const string & loc,const string & ant,const string & host,const string & port,const string & long_id,const string & short_id,bool reg_auto,bool reg_log,bool reg_manual)356 pskrep::pskrep(const string& call, const string& loc, const string& ant,
357 	       const string& host, const string& port,
358 	       const string& long_id, const string& short_id,
359 	       bool reg_auto, bool reg_log, bool reg_manual)
360 	: sender(call, loc, ant, host, port, long_id, short_id), new_count(0)
361 {
362 	if (reg_auto)
363 		spot_register_recv(pskrep::recv, this, PSKREP_RE, REG_EXTENDED | REG_ICASE);
364 	if (reg_log)
365 		spot_register_log(pskrep::log, this);
366 	if (reg_manual)
367 		spot_register_manual(pskrep::manual, this);
368 	load_queue();
369 }
370 
~pskrep()371 pskrep::~pskrep()
372 {
373 	spot_unregister_recv(pskrep::recv, this);
374 	spot_unregister_log(pskrep::log, this);
375 	spot_unregister_manual(pskrep::manual, this);
376 	save_queue();
377 }
378 
379 // This function is called by spot_recv() when its buffer matches our PSKREP_RE
recv(trx_mode mode,int afreq,const char * str,const regmatch_t * calls,size_t len,void * obj)380 void pskrep::recv(trx_mode mode, int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj)
381 {
382 	if (unlikely(calls[PSKREP_RE_INDEX].rm_so == -1 || calls[PSKREP_RE_INDEX].rm_eo == -1))
383 		return;
384 
385 	string call(str + calls[PSKREP_RE_INDEX].rm_so, calls[PSKREP_RE_INDEX].rm_eo - calls[PSKREP_RE_INDEX].rm_so);
386 	long long freq = afreq;
387 	if (!wf->USB())
388 		freq = -freq;
389 	freq += wf->rfcarrier();
390 	LOG_DEBUG("Spotted \"%s\" in buffer \"%s\"", call.c_str(), str);
391 
392 	reinterpret_cast<pskrep*>(obj)->append(call.c_str(), "", freq,
393 					       active_modem->get_mode(), time(NULL), PSKREP_AUTO);
394 }
395 
396 // This function is called by spot_log()
log(const char * call,const char * loc,long long freq,trx_mode mode,time_t rtime,void * obj)397 void pskrep::log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
398 {
399 	reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_LOG);
400 }
401 
402 // This function is called by spot_manual()
manual(const char * call,const char * loc,long long freq,trx_mode mode,time_t rtime,void * obj)403 void pskrep::manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
404 {
405 	reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_MANUAL);
406 }
407 
append(string call,const char * loc,long long freq,trx_mode mode,time_t rtime,rtype_t rtype)408 void pskrep::append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype)
409 {
410 	if (unlikely(call.empty()))
411 		return;
412 	transform(call.begin(), call.end(), call.begin(), static_cast<int (*)(int)>(toupper));
413 
414 	if (!progdefaults.pskrep_qrg)
415 		freq = 0LL;
416 
417 	if (*loc && !locator_re.match(loc))
418 		loc = "";
419 
420 	band_map_t& bandq = queue[call][band(freq)];
421 	if (bandq.empty() || rtime - bandq.back().rtime >= DUP_INTERVAL) { // add new
422 		bandq.push_back(rcpt_report_t(mode, freq, rtime, rtype, loc));
423 		LOG_VERBOSE("Added (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%d, time=%d, type=%u)",
424 			 call.c_str(), loc, mode_info[mode].adif_name,
425 			 static_cast<int>(freq),
426 			 static_cast<int>(rtime), rtype);
427 		new_count++;
428 		save_queue();
429 	}
430 	else if (!bandq.empty()) {
431 		band_map_t::value_type& r = bandq.back();
432 		if (r.status != PSKR_STATUS_SENT && *loc && r.locator != loc) { // update last
433 			r.locator = loc;
434 			r.rtype = rtype;
435 			LOG_VERBOSE("Updated (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%d, time=%d, type=%u)",
436 				 call.c_str(), loc, mode_info[r.mode].adif_name,
437 				 static_cast<int>(r.freq),
438 				 static_cast<int>(r.rtime), rtype);
439 			save_queue();
440 		}
441 	}
442 }
443 
444 // Handle queued reports
progress(void)445 bool pskrep::progress(void)
446 {
447 	if (queue.empty())
448 		return true;
449 
450 	unsigned nrep = 0;
451 	bool sender_full = false;
452 	for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i) {
453 		for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j) {
454 			for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k) {
455 				switch (k->status) {
456 				case PSKR_STATUS_NEW:
457 					if ((sender_full = !sender.append(i->first, *k)))
458 						goto send_reports;
459 					k->status = PSKR_STATUS_PENDING;
460 					nrep++;
461 					break;
462 				case PSKR_STATUS_PENDING: // sent in last cycle
463 					k->status = PSKR_STATUS_SENT;
464 				default:
465 					break;
466 				}
467 			}
468 		}
469 	}
470 
471 send_reports:
472 	LOG_VERBOSE("Found %u new report(s)", nrep);
473 	if (nrep) {
474 		if (!sender.send()) {
475 			LOG_ERROR("Sender failed, disabling pskreporter");
476 			return false;
477 		}
478 		return progress();
479 	}
480 
481 	gc();
482 	save_queue();
483 	return true;
484 }
485 
486 // Delete sent reports that are older than DUP_INTERVAL seconds
gc(void)487 void pskrep::gc(void)
488 {
489 	time_t threshold = time(NULL) - DUP_INTERVAL;
490 	unsigned rm = 0;
491 
492 	for (queue_t::iterator i = queue.begin(); i != queue.end() ; ) {
493 		for (call_map_t::iterator j = i->second.begin(); j != i->second.end() ; ) {
494 			band_map_t& b = j->second;
495 			band_map_t::iterator k = find_if(b.begin(), b.end(), not_sent);
496 			if (k != b.begin() && k == b.end())
497 				--k;
498 			rm += k - b.begin();
499 			k = b.erase(b.begin(), k);
500 
501 			if (k != b.end() && k->status == PSKR_STATUS_SENT && k->rtime <= threshold) {
502 				b.erase(k);
503 				rm++;
504 			}
505 
506 			if (b.empty())
507 				i->second.erase(j++);
508 			else
509 				++j;
510 		}
511 		if (i->second.empty())
512 			queue.erase(i++);
513 		else
514 			++i;
515 	}
516 
517 	LOG_DEBUG("Removed %u sent report(s)", rm);
518 }
519 
520 static ostream& operator<<(ostream& out, const rcpt_report_t& r);
521 static istream& operator>>(istream& in, rcpt_report_t& r);
522 static ostream& operator<<(ostream& out, const queue_t& q);
523 static istream& operator>>(istream& in, queue_t& q);
524 
save_queue(void)525 void pskrep::save_queue(void)
526 {
527 	string fname = TempDir;
528 	fname.append(PSKREP_QUEUE_FILE);
529 	ofstream out(fname.c_str());
530 
531 	if (out)
532 		out << queue;
533 	else
534 		LOG_ERROR("Could not write %s", fname.c_str());
535 }
536 
load_queue(void)537 void pskrep::load_queue(void)
538 {
539 	string fname = TempDir;
540 	fname.append(PSKREP_QUEUE_FILE);
541 	ifstream in(fname.c_str());
542 	if (!in)
543 		return;
544 
545 	in >> queue;
546 	// restore pending reports as new
547 	for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i)
548 		for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
549 			for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
550 				if (k->status == PSKR_STATUS_PENDING)
551 					k->status = PSKR_STATUS_NEW;
552 }
553 
554 // -------------------------------------------------------------------------------------------------
555 
556 // Text fields must be <= 254 bytes
557 #define MAX_TEXT_SIZE 254
558 // Records must be padded to a multiple of 4
559 #define PAD 4
560 
pskrep_sender(const string & call,const string & loc,const string & ant,const string & host_,const string & port_,const string & long_id_,const string & short_id_)561 pskrep_sender::pskrep_sender(const string& call, const string& loc, const string& ant,
562 			     const string& host_, const string& port_,
563 			     const string& long_id_, const string& short_id_)
564 	: recv_callsign(call, 0, MAX_TEXT_SIZE), recv_locator(loc, 0, MAX_TEXT_SIZE),
565 	  recv_antenna(ant, 0, MAX_TEXT_SIZE),
566 	  host(host_, 0, MAX_TEXT_SIZE), port(port_, 0, MAX_TEXT_SIZE),
567 	  long_id(long_id_, 0, LONG_ID_SIZE), short_id(short_id_, 0, SHORT_ID_SIZE),
568 	  sequence_number(0), template_count(2 * TEMPLATE_THRESHOLD), last_template(0),
569 	  send_socket(0), dgram_size(0), report_offset(0)
570 {
571 	create_socket();
572 	dgram = new unsigned char[DGRAM_MAX];
573 	write_station_info();
574 }
575 
~pskrep_sender()576 pskrep_sender::~pskrep_sender()
577 {
578 	delete send_socket;
579 	delete [] dgram;
580 }
581 
582 // fldigi uses 0x0219 as the long station info template id (bytes 4,5)
583 const unsigned char pskrep_sender::long_station_info_template[] = {
584 	0x00, 0x03, 0x00, 0x34, 0x02, 0x19, 0x00, 0x05, 0x00, 0x00,
585 	0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
586 	0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
587 	0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
588 	0x80, 0x08, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // decoderSoftware
589 	0x80, 0x09, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // anntennaInformation
590 	0x00, 0x00
591 };
592 
593 // fldigi uses 0x0218 as the short station info template id (bytes 4,5)
594 const unsigned char pskrep_sender::short_station_info_template[] = {
595 	0x00, 0x03, 0x00, 0x24, 0x02, 0x18, 0x00, 0x03, 0x00, 0x00,
596 	0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
597 	0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
598 	0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
599 	0x00, 0x00
600 };
601 
write_station_info(void)602 void pskrep_sender::write_station_info(void)
603 {
604 	char prog_info[MAX_TEXT_SIZE];
605 	size_t prog_len;
606 
607 	prog_len = snprintf(prog_info, sizeof(prog_info), "%s", PACKAGE_TARNAME "-" PACKAGE_VERSION);
608 	prog_len = MIN(prog_len, sizeof(prog_info));
609 	struct utsname u;
610 	if (uname(&u) != -1) {
611 		prog_len += snprintf(prog_info+prog_len, sizeof(prog_info)-prog_len, "/%s-%s", u.sysname, u.machine);
612 		prog_len = MIN(prog_len, sizeof(prog_info));
613 	}
614 
615 	size_t  call_len = recv_callsign.length(),
616 		loc_len = recv_locator.length(),
617 		ant_len = recv_antenna.length();
618 
619 	size_t long_len, short_len;
620 	// Long station info length
621 	long_len =  4        + // 4-byte header
622 		1 + call_len + // 1-byte call length + call string length
623 		1 + loc_len  + // 1-byte loc length  + loc string length
624 		8            + // 8-byte identifier
625 		1 + prog_len + // 1-byte prog length + prog string length
626 		1 + ant_len;   // 1-byte ant length  + ant string length
627 	long_len = pad(long_len, PAD);
628 	// Short station info length
629 	short_len = 4        + // 4-byte header
630 		1 + call_len + // 1-byte call length + call string length
631 		1 + loc_len  + // 1-byte loc length  + loc string length
632 		8;             // 8-byte identifier
633 	short_len = pad(short_len, PAD);
634 
635 	long_station_info.resize(long_len);
636 	short_station_info.resize(short_len);
637 	unsigned char* p;
638 	size_t npad;
639 
640 	// Write the long station info
641 	p = &long_station_info[0];
642 	// header
643 	memcpy(p, long_station_info_template + 4, 2);               p += 2;
644 	*reinterpret_cast<uint16_t*>(p) = htons(long_len);          p += sizeof(uint16_t);
645 	// call
646 	*p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
647 	// locator
648 	*p++ = loc_len;  memcpy(p, recv_locator.data(), loc_len);   p += loc_len;
649 	// identifier
650 	memcpy(p, long_id.data(), LONG_ID_SIZE);                    p += LONG_ID_SIZE;
651 	// program
652 	*p++ = prog_len; memcpy(p, prog_info, prog_len);            p += prog_len;
653 	// antenna
654 	*p++ = ant_len;  memcpy(p, recv_antenna.data(), ant_len);   p += ant_len;
655 	// pad
656 	npad = &long_station_info[0] + long_len - p;
657 	if (npad)
658 		memset(p, 0, npad);
659 	LOG_DEBUG("long_station_info=\"%s\"", str2hex(&long_station_info[0], long_len));
660 
661 	// Write the short station info
662 	p = &short_station_info[0];
663 	// header
664 	memcpy(p, short_station_info_template + 4, 2);              p += 2;
665 	*reinterpret_cast<uint16_t*>(p) = htons(short_len);         p += sizeof(uint16_t);
666 	// call
667 	*p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
668 	// locator
669 	*p++ = loc_len;  memcpy(p, recv_locator.data(), loc_len);   p += loc_len;
670 	// identifier
671 	memcpy(p, long_id.data(), LONG_ID_SIZE);                    p += LONG_ID_SIZE;
672 	// pad
673 	npad = &short_station_info[0] + short_len - p;
674 	if (npad)
675 		memset(p, 0, npad);
676 	LOG_DEBUG("short_station_info=\"%s\"", str2hex(&short_station_info[0], short_len));
677 }
678 
679 // fldigi uses 0x022C as the reception record template id (bytes 4,5)
680 const unsigned char pskrep_sender::rcpt_record_template[] = {
681 	0x00, 0x02, 0x00, 0x34, 0x02, 0x2C, 0x00, 0x06,
682 	0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // senderCallsign
683 	0x00, 0x96, 0x00, 0x04,                         // flowStartSeconds
684 	0x80, 0x05, 0x00, 0x04, 0x00, 0x00, 0x76, 0x8F, // frequency
685 	0x80, 0x0A, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // mode (adif string)
686 	0x80, 0x03, 0xff, 0xff, 0x00, 0x00, 0x76, 0x8F, // senderLocator (if known)
687 	0x80, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F  // flags (informationSource)
688 };
689 
write_preamble(void)690 void pskrep_sender::write_preamble(void)
691 {
692 	time_t now = time(NULL);
693 	unsigned char* p = dgram;
694 
695 	// header
696 	*p++ = 0x00; *p++ = 0x0A;   /* length written later */    p += 2;
697 	/* time written later */                                  p += sizeof(uint32_t);
698 	*reinterpret_cast<uint32_t*>(p) = htonl(sequence_number); p += sizeof(uint32_t);
699 	memcpy(p, short_id.data(), SHORT_ID_SIZE);                p += SHORT_ID_SIZE;
700 
701 	const unsigned char* station_info_template(long_station_info_template);
702 	size_t tlen;
703 	vector<unsigned char>* station_info(&long_station_info);
704 
705 	if (template_count == 0 && now - last_template >= TEMPLATE_INTERVAL)
706 		template_count = TEMPLATE_THRESHOLD;
707 	if (template_count > TEMPLATE_THRESHOLD) {
708 		station_info_template = long_station_info_template;
709 		tlen = sizeof(long_station_info_template);
710 		station_info = &long_station_info;
711 	}
712 	else if (template_count != 0) {
713 		station_info_template = short_station_info_template;
714 		tlen = sizeof(short_station_info_template);
715 		station_info = &short_station_info;
716 	}
717 	if (template_count > 0) {
718 		memcpy(p, rcpt_record_template, sizeof(rcpt_record_template)); p += sizeof(rcpt_record_template);
719 		memcpy(p, station_info_template, tlen);                        p += tlen;
720 		template_count--;
721 		last_template = now;
722 	}
723 
724 	// station info record
725 	memcpy(p, &(*station_info)[0], station_info->size());
726 	p += station_info->size();
727 
728 	report_offset = p - dgram;
729 	// write report record header
730 	memcpy(p, rcpt_record_template + 4, 2);
731 	p += 2; /* length written later */
732 	p += sizeof(uint16_t);
733 
734 	dgram_size = p - dgram;
735 }
736 
append(const string & callsign,const band_map_t::value_type & r)737 bool pskrep_sender::append(const string& callsign, const band_map_t::value_type& r)
738 {
739 	guard_lock dsp_lock(&pskrep_mutex);
740 
741 	if (dgram_size == 0)
742 		write_preamble();
743 
744 	size_t call_len = callsign.length();
745 	call_len = MIN(MAX_TEXT_SIZE, call_len);
746 
747 	const char* mode = mode_info[r.mode].adif_name;
748 	size_t mode_len = strlen(mode);
749 	mode_len = MIN(MAX_TEXT_SIZE, mode_len);
750 
751 	size_t loc_len = MIN(MAX_TEXT_SIZE, r.locator.length());
752 
753 	// call_len + call + time + freq + mode_len + mode + loc_len + loc + info
754 	size_t rlen = 1 + call_len + 4 + 4 + 1 + mode_len + 1 + loc_len + 1;
755 
756 	if (pad(rlen, PAD) + dgram_size > DGRAM_MAX) // datagram full
757 		return false;
758 
759 
760 	LOG_INFO("Appending report (call=%s mode=%s freq=%d time=%d type=%u)",
761 		 callsign.c_str(), mode_info[r.mode].adif_name,
762 		 static_cast<int>(r.freq),
763 		 static_cast<int>(r.rtime), r.rtype);
764 
765 	unsigned char* start = dgram + dgram_size;
766 	unsigned char* p = start;
767 	// call
768 	*p++ = call_len; memcpy(p, callsign.data(), call_len); p += call_len;
769 	// 4-byte reception time
770 	*reinterpret_cast<uint32_t*>(p) = htonl(r.rtime);      p += sizeof(uint32_t);
771 	// 4-byte freq
772 	*reinterpret_cast<uint32_t*>(p) = htonl(r.freq);       p += sizeof(uint32_t);
773 	// mode
774 	*p++ = mode_len; memcpy(p, mode, mode_len);            p += mode_len;
775 	// locator
776 	*p++ = loc_len; memcpy(p, r.locator.data(), loc_len);  p += loc_len;
777 	// info source
778 	*p++ = r.rtype;
779 
780 	LOG_DEBUG("                 \"%s\"", str2hex(start, p - start));
781 
782 	dgram_size += rlen;
783 	return true;
784 }
785 
resolver(void * obj)786 void* pskrep_sender::resolver(void* obj)
787 {
788 	pskrep_sender* s = reinterpret_cast<pskrep_sender*>(obj);
789 	try {
790 		s->send_socket = new Socket(Address(s->host.c_str(), s->port.c_str(), "udp"));
791 		s->send_socket->connect();
792 	}
793 	catch (const SocketException& e) {
794 		LOG_ERROR("Could not resolve %s: %s", s->host.c_str(), e.what());
795 	}
796 
797 	return NULL;
798 }
799 
create_socket(void)800 void pskrep_sender::create_socket(void)
801 {
802 	if (pthread_create(&resolver_thread, NULL, resolver, this) != 0)
803 		LOG_PERROR("pthread_create");
804 }
805 
send(void)806 bool pskrep_sender::send(void)
807 {
808 	if (!send_socket)
809 		return false;
810 
811 	// empty dgram or no reports (shouldn't happen)
812 	if (dgram_size == 0 || dgram_size == report_offset + 4) {
813 		stringstream info;
814 		info << dgram_size << " " << report_offset;
815 		LOG_DEBUG("Not sending empty dgram: %s", info.str().c_str());
816 		return false;
817 	}
818 
819 	// Finish writing the report record:
820 	//   do we need padding?
821 	size_t npad = (dgram_size - report_offset) % PAD;
822 	if (npad) {
823 		npad = PAD - npad;
824 		memset(dgram + dgram_size, 0x0, npad);
825 		dgram_size += npad;
826 	}
827 	//   write length
828 	*reinterpret_cast<uint16_t*>(dgram + report_offset + 2) = htons(dgram_size - report_offset);
829 
830 	// finish writing the datagram
831 	*reinterpret_cast<uint16_t*>(dgram + 2) = htons(dgram_size);
832 	*reinterpret_cast<uint32_t*>(dgram + 4) = htonl(time(NULL));
833 
834 	bool ret;
835 	{
836 		stringstream info;
837 		info << "(" << dgram_size << "): \"" << str2hex(dgram, dgram_size) << "\"";
838 		LOG_DEBUG("Sending datgram %s", info.str().c_str());
839 	}
840 	try {
841 		if ((size_t)send_socket->send(dgram, dgram_size) != dgram_size)
842 			throw SocketException("short write");
843 		ret = true;
844 	}
845 	catch (const SocketException& e) {
846 		LOG_ERROR("Could not send datagram to %s port %s: %s", host.c_str(), port.c_str(), e.what());
847 		ret = false;
848 	}
849 
850 	// increment this regardless of any errors
851 	sequence_number++;
852 	dgram_size = 0;
853 
854 	return ret;
855 }
856 
857 // Pad len to a multiple of mult
pad(size_t len,size_t mult)858 size_t pskrep_sender::pad(size_t len, size_t mult)
859 {
860 	size_t r = len % mult;
861 	return r ? len + mult - r : len;
862 }
863 
864 // -------------------------------------------------------------------------------------------------
865 
operator >>(istream & in,rtype_t & t)866 static istream& operator>>(istream& in, rtype_t& t)
867 {
868 	int i;
869 	in >> i;
870 	t = static_cast<rtype_t>(i);
871 	return in;
872 }
operator >>(istream & in,status_t & t)873 static istream& operator>>(istream& in, status_t& t)
874 {
875 	int i;
876 	in >> i;
877 	t = static_cast<status_t>(i);
878 	return in;
879 }
operator >>(istream & in,rcpt_report_t & r)880 static istream& operator>>(istream& in, rcpt_report_t& r)
881 {
882 	in >> r.mode >> r.freq  >> r.rtime >> r.rtype >> r.status >> r.locator;
883 	if (*r.locator.c_str() == '?') r.locator.clear();
884 	return in;
885 }
886 
operator <<(ostream & out,const rcpt_report_t & r)887 static ostream& operator<<(ostream& out, const rcpt_report_t& r)
888 {
889 	return out << r.mode << ' ' << r.freq << ' ' << r.rtime << ' '
890 		   << r.rtype << ' ' << r.status << ' ' << (r.locator.empty() ? "?" : r.locator);
891 }
operator <<(ostream & out,const queue_t & q)892 static ostream& operator<<(ostream& out, const queue_t& q)
893 {
894 	for (queue_t::const_iterator i = q.begin(); i != q.end(); ++i)
895 		for (call_map_t::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
896 			for (band_map_t::const_iterator k = j->second.begin(); k != j->second.end(); ++k)
897 				out << *k << "  " << j->first << "  " << i->first << '\n';
898 
899 	return out;
900 }
operator >>(istream & in,queue_t & q)901 static istream& operator>>(istream& in, queue_t& q)
902 {
903 	rcpt_report_t rep;
904 	int band;
905 	string call;
906 
907 	while (in >> rep >> band >> call)
908 	 	q[call][static_cast<band_t>(band)].push_back(rep);
909 
910 	return in;
911 }
912