1 // ----------------------------------------------------------------------------
2 // flarq.cxx - Fast Light ARQ Application
3 //
4 // ----------------------------------------------------------------------------
5 // Copyright (C) 2014
6 //              David Freese, W1HKJ
7 //
8 // This file is part of fldigi
9 //
10 // fldigi is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // fldigi is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 // ----------------------------------------------------------------------------
23 
24 #include <config.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <iostream>
28 #include <fstream>
29 #include <cstring>
30 #include <ctime>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 
38 #include <FL/Fl.H>
39 #include <FL/Fl_Widget.H>
40 #include <FL/Enumerations.H>
41 #include <FL/Fl_Window.H>
42 #include <FL/Fl_Button.H>
43 #include <FL/Fl_Group.H>
44 #include <FL/Fl_Sys_Menu_Bar.H>
45 #include <FL/x.H>
46 #include <FL/Fl_Help_Dialog.H>
47 #include <FL/Fl_Menu_Item.H>
48 
49 // this tests depends on a modified FL/filename.H in the Fltk-1.3.0
50 // change
51 //#  if defined(WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
52 // to
53 //#  if defined(WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) && !defined(__WOE32__)
54 
55 #include <FL/filename.H>
56 
57 #ifdef __MINGW32__
58 #	include "compat.h"
59 #endif
60 
61 #include <dirent.h>
62 
63 #include "fileselect.h"
64 #include "socket.h"
65 
66 #include "debug.h"
67 #include "threads.h"
68 #include "flarq.h"
69 #include "flarqenv.h"
70 #include "flmisc.h"
71 #include "stacktrace.h"
72 #include "icons.h"
73 #include "arq.h"
74 #include "arqdialogs.h"
75 #include "b64.h"
76 #include "gettext.h"
77 
78 #include "xml_server.h"
79 
80 #include "FTextView.h"
81 
82 debug::level_e debug::level = debug::INFO_LEVEL;
83 unsigned int debug::mask;
log(debug::level_e,const char *,const char *,int line,const char * ...)84 void debug::log(debug::level_e, const char*, const char*, int line, const char* ...) {}
85 
86 #define FLDIGI_port "7322"
87 #define MPSK_port "3122"
88 
89 #define FLARQ_XML_PORT 7422
90 
91 #define MPSK_TX   "TX"
92 #define MPSK_RX   "RX"
93 #define MPSK_TX2RX "RX_AFTER_TX"
94 #define MPSK_BYTE 25
95 #define MPSK_CMD  26
96 #define MPSK_END  27
97 #define MPSK_ISTX 28
98 #define MPSK_ISRX 29
99 #define MPSK_ISCMD 30
100 #define MPSK_CMDEND 31
101 
102 // directory structures for all NBEMS applications
103 static void checkdirectories(void);
104 std::string HomeDir;
105 std::string NBEMS_dir;
106 std::string ARQ_dir;
107 std::string ARQ_files_dir;
108 std::string ARQ_recv_dir;
109 std::string ARQ_send_dir;
110 std::string ARQ_mail_dir;
111 std::string ARQ_mail_in_dir;
112 std::string ARQ_mail_out_dir;
113 std::string ARQ_mail_sent_dir;
114 std::string WRAP_dir;
115 std::string WRAP_recv_dir;
116 std::string WRAP_send_dir;
117 std::string WRAP_auto_dir;
118 std::string ICS_dir;
119 std::string ICS_msg_dir;
120 std::string ICS_tmp_dir;
121 
122 std::string MailFileName = "";
123 std::string MailSaveFileName = "";
124 std::string Logfile;
125 
126 struct dirent *entry;
127 DIR *dp;
128 
129 bool   SendingEmail = false;
130 
131 bool SHOWDEBUG = false;
132 
133 extern void STATUSprint(std::string s);
134 
135 Fl_Text_Buffer_mod *txtbuffARQ;
136 Fl_Text_Buffer_mod *txtbuffRX;
137 Fl_Text_Buffer_mod *txtbuffComposer;
138 
139 Fl_Double_Window *arqwin = 0;
140 Fl_Double_Window *dlgconfig = 0;
141 Fl_Double_Window *outdialog = 0;
142 Fl_Double_Window *composer = 0;
143 
144 using namespace std;
145 
146 arq *digi_arq;
147 bool traffic = false;
148 bool sendingfile = false;
149 int  arqstate = 0;
150 bool configured = false;
151 
152 bool ioMPSK = false;
153 std::string arq_address =  "127.0.0.1";
154 std::string arq_port = FLDIGI_port;
155 
156 std::string RX;
157 std::string TX;
158 
159 std::string teststring;
160 
161 std::string statusmsg;
162 
163 std::string MyCall;
164 std::string beacontext;
165 
166 #if !defined(__APPLE__) && !defined(__WOE32__) && USE_X
167 Pixmap flarq_icon_pixmap;
168 #endif
169 
170 Socket *tcpip = 0;
171 std::string txbuffer;
172 std::string cmdbuffer;
173 std::string rxbuffer;
174 
175 size_t  bufsize = 0;
176 size_t  bufptr = 0;
177 
178 bool isRxChar = false;
179 bool isCmdChar = false;
180 bool isTxChar = false;
181 bool inLoop = false;
182 
183 int    exponent = 5;
184 int	   txdelay = 500;
185 int    iretries = 5;
186 long   iwaittime = 10000;
187 long   itimeout = 60000;
188 int    idtimer = 10;
189 int	   bcnInterval = 30;
190 
191 enum {OFF, ON, WAIT};
192 int    autobeacon = OFF;
193 bool   beaconrcvd = false;
194 bool   restart_beacon = false;
195 
196 int    blocksSent = 0;
197 
198 int mainX = 0, mainY = 0, mainW = 600, mainH = 500;
199 
200 float  vers = 0;
201 float  version;
202 
203 const char *ASCII[32] = {
204 	"<NUL>", "<SOH>", "<STX>", "<ETX>", // 0x00 - 0x03
205 	"<EOT>", "<ENQ>", "<ACK>", "<BEL>", // 0x04 - 0x07
206 	"<BX>",  "<TAB>", "<LF>",  "<VT>",  // 0x08 - 0x0B
207 	"<FF>",  "<CR>",  "<SO>",  "<SI>",  // 0x0C - 0x0F
208 	"<DLE>", "<DC1>", "<DC2>", "<DC3>", // 0x10 - 0x13
209 	"<DC4>", "<NAK>", "<SYN>", "<ETB>", // 0x14 - 0x17
210 	"<CAN>", "<EM>",  "<SUB>", "<ESC>", // 0x18 - 0x1B
211 	"<FS>",  "<GS>",  "<RS>",  "<US>"   // 0x1C - 0x1F
212 };
213 
214 std::string AsciiChars;
215 
216 std::string incomingText = "";
217 std::string txtarqload = "";
218 std::string rxfname = "";
219 std::string arqstart = "ARQ::STX\n";
220 std::string arqend   = "ARQ::ETX\n";
221 std::string arqfile = "ARQ:FILE::";
222 std::string arqemail = "ARQ:EMAIL::\n";
223 std::string arqascii = "ARQ:ENCODING::ASCII\n";
224 std::string arqbase64 = "ARQ:ENCODING::BASE64\n";
225 std::string arqsizespec = "ARQ:SIZE::";
226 size_t startpos = std::string::npos;
227 size_t endpos = std::string::npos;
228 size_t fnamepos = std::string::npos;
229 size_t indx = std::string::npos;
230 size_t sizepos = std::string::npos;
231 size_t lfpos = std::string::npos;
232 size_t arqPayloadSize = 0;
233 bool haveemail = false;
234 bool rxARQfile = false;
235 bool rxARQhavesize = false;
236 bool rxTextReady = false;
237 
238 time_t StartTime_t;
239 time_t EndTime_t;
240 time_t TransferTime_t;
241 double TransferTime;
242 
243 //=============================================================================
244 // email selector
245 //=============================================================================
246 
247 int datedir = 1;
248 int todir = 1;
249 int subdir = 1;
250 std::string sendfilename = "";
251 
cb_SortByDate()252 void cb_SortByDate()
253 {
254 	if (datedir == 0) {
255 		tblOutgoing->sort(1, false);
256 		datedir = 1;
257 	} else {
258 		tblOutgoing->sort(1, true);
259 		datedir = 0;
260 	}
261 	tblOutgoing->redraw();
262 }
263 
cb_SortByTo()264 void cb_SortByTo()
265 {
266 	if (todir == 0) {
267 		tblOutgoing->sort(2, false);
268 		todir = 1;
269 	} else {
270 		tblOutgoing->sort(2, true);
271 		todir = 0;
272 	}
273 	tblOutgoing->redraw();
274 }
275 
cb_SortBySubj()276 void cb_SortBySubj()
277 {
278 	if (subdir == 0) {
279 		tblOutgoing->sort(3, false);
280 		subdir = 1;
281 	} else {
282 		tblOutgoing->sort(3, true);
283 		subdir = 0;
284 	}
285 	tblOutgoing->redraw();
286 }
287 
sendOK()288 void sendOK()
289 {
290 	outdialog->hide();
291 	int sel = tblOutgoing->value();
292 	if (sel >= 0)
293 		sendfilename = tblOutgoing->valueAt(sel,0);
294 	else
295 		sendfilename.clear();
296 }
297 
sendCancel()298 void sendCancel()
299 {
300 	outdialog->hide();
301 	sendfilename.clear();
302 }
303 
selectTrafficOut(bool ComposerOnly)304 void selectTrafficOut(bool ComposerOnly)
305 {
306 	if (outdialog == 0) {
307 		outdialog = arq_SendSelect();
308 		outdialog->xclass(PACKAGE_TARNAME);
309 		tblOutgoing->addHiddenColumn ("fnbr"); // column #0
310 		tblOutgoing->addColumn ("Date", 180); // column #1
311 		tblOutgoing->addColumn ("To", 120); // column #2
312 		tblOutgoing->addColumn ("Subj", 196); // column #3
313 		tblOutgoing->colcallback (1, cb_SortByDate);
314 		tblOutgoing->columnAlign(1, FL_ALIGN_LEFT);
315 		tblOutgoing->colcallback (2, cb_SortByTo);
316 		tblOutgoing->columnAlign(2, FL_ALIGN_LEFT);
317 		tblOutgoing->colcallback (3, cb_SortBySubj);
318 		tblOutgoing->columnAlign(3, FL_ALIGN_LEFT);
319 		tblOutgoing->allowSort(true);
320 		tblOutgoing->rowSize (14);
321 		tblOutgoing->headerSize (14);
322 		tblOutgoing->allowResize (true);
323 		tblOutgoing->gridEnabled (true);
324 	}
325 	tblOutgoing->clear();
326 	std::string fline, fname, fdate, fto, fsubj;
327 	char szline[10000];
328 	size_t p;
329 
330 	std::string folder = ARQ_mail_out_dir;
331 	dp = 0;
332 	dp = opendir(folder.c_str());
333 	if (dp == 0) {
334 		std::string nfound = folder;
335 		nfound += " not found";
336 		fl_message("%s", nfound.c_str());
337 		return;
338 	}
339 
340 	int nummails = 0;
341 	while ((entry = readdir(dp)) != 0) {
342 		if (entry->d_name[0] == '.')
343 			continue;
344 		fname = folder; fname.append(entry->d_name);
345 		if (fname.find(".eml") == std::string::npos)
346 			continue;
347 		int validlines = 0;
348 		ifstream emailtxt(fname.c_str());
349 		while (!emailtxt.eof()) {
350 			memset(szline, 0, 10000);
351 			emailtxt.getline(szline,10000);
352 			fline = szline;
353 			if ((p = fline.find("Date: ")) != std::string::npos) {
354 				fdate = fline.substr(p + 6);
355 				validlines++;
356 				continue;
357 			}
358 			if ((p = fline.find("To: ")) != std::string::npos) {
359 				fto = fline.substr(p + 4);
360 				p = fto.find('@');
361 				if (p != std::string::npos) fto.replace(p,1,"@@");
362 				p = fto.find("<");
363 				if (p != std::string::npos) fto.erase(p,1);
364 				p = fto.find(">");
365 				if (p != std::string::npos) fto.erase(p,1);
366 				validlines++;
367 				continue;
368 			}
369 			if ((p = fline.find("Subject: ")) != std::string::npos) {
370 				fsubj = fline.substr(p + 9);
371 				validlines++;
372 				continue;
373 			}
374 			if ((p = fline.find("//FLARQ COMPOSER")) != std::string::npos)
375 				validlines++;
376 		}
377 		emailtxt.close();
378 		if ((!ComposerOnly && validlines >= 3) || (ComposerOnly && validlines == 4)) {
379 			tblOutgoing->addRow (4, fname.c_str(), fdate.c_str(), fto.c_str(), fsubj.c_str());
380 			nummails++;
381 		}
382 	}
383 	if (nummails) {
384 		tblOutgoing->FirstRow();
385 		outdialog->show();
386 		while (outdialog->shown())
387 			Fl::wait();
388 	} else
389 		fl_message2("No emails for delivery");
390 }
391 
392 //======================================================================================
393 // simple email composer
394 //======================================================================================
395 extern bool fileExists(std::string fname);
396 
cb_CancelComposeMail()397 void cb_CancelComposeMail()
398 {
399 	composer->hide();
400 }
401 
readComposedFile(std::string filename)402 void readComposedFile(std::string filename)
403 {
404 	ifstream textfile;
405 	textfile.open(filename.c_str());
406 	if (textfile) {
407 		char szline[10000];
408 		std::string fline, tempstr;
409 		size_t p;
410 		txtMailText->clear();
411 		inpMailFrom->value("");
412 		inpMailTo->value("");
413 		inpMailSubj->value("");
414 		while (!textfile.eof()) {
415 			memset(szline,0, 10000);
416 			textfile.getline(szline,10000);
417 			fline = szline;
418 			if ((p = fline.find("//FLARQ COMPOSER")) != std::string::npos)
419 				continue;
420 			if ((p = fline.find("Date: ")) != std::string::npos)
421 				continue;
422 			if ((p = fline.find("Content-Type:")) != std::string::npos)
423 				continue;
424 			if ((p = fline.find("From: ")) != std::string::npos) {
425 				tempstr = fline.substr(p + 6);
426 				inpMailFrom->value(tempstr.c_str());
427 				continue;
428 			}
429 			if ((p = fline.find("To: ")) != std::string::npos) {
430 				tempstr = fline.substr(p + 4);
431 				p = tempstr.find("<");
432 				if (p != std::string::npos) tempstr.erase(p,1);
433 				p = tempstr.find(">");
434 				if (p != std::string::npos) tempstr.erase(p,1);
435 				inpMailTo->value(tempstr.c_str());
436 				continue;
437 			}
438 			if ((p = fline.find("Subject: ")) != std::string::npos) {
439 				inpMailSubj->value(fline.substr(p + 9).c_str());
440 				continue;
441 			}
442 			if (strlen(szline) == 0 && txtbuffComposer->length() == 0) continue;
443 			txtMailText->add(szline);
444 			txtMailText->add("\n");
445 		}
446 		textfile.close();
447 	}
448 }
449 
cb_UseTemplate()450 void cb_UseTemplate()
451 {
452 	std::string templatename = ARQ_mail_out_dir;
453 	const char *p = FSEL::select("Load Template file", "*.tpl", templatename.c_str());
454 	if (!p) return;
455 	if (!*p) return;
456 	readComposedFile(p);
457 }
458 
cb_ClearComposer()459 void cb_ClearComposer()
460 {
461 	sendfilename.clear();
462 	txtMailText->clear();
463 	inpMailFrom->value("");
464 	inpMailTo->value("");
465 	inpMailSubj->value("");
466 }
467 
nextEmailFile(std::string fname)468 std::string nextEmailFile(std::string fname)
469 {
470 	int nbr = 0;
471 	char szNbr[20];
472 	std::string name;
473 	std::string ext;
474 	std::string nuname;
475 	size_t p;
476 
477 	p = fname.find_last_of('.');
478 	if (p != std::string::npos) {
479 		ext = fname.substr(p);
480 		name = fname.substr(0,p);
481 	} else {
482 		ext = "";
483 		name = fname;
484 	}
485 
486 	do {
487 		nbr++;
488 		nuname = name;
489 #ifdef __WOE32__
490 		snprintf(szNbr, sizeof(szNbr), "-%-d", nbr);
491 #else
492 		snprintf(szNbr, sizeof(szNbr), "%-d", nbr);
493 #endif
494 		nuname.append(szNbr);
495 		nuname.append(ext);
496 	} while (fileExists(nuname));
497 	return nuname;
498 }
499 
saveComposedText(std::string filename)500 void saveComposedText(std::string filename)
501 {
502 	ofstream textfile;
503 	textfile.open(filename.c_str());
504 	textfile << "//FLARQ COMPOSER" << endl;
505 	char szmaildt[100];
506 	time_t maildt = time(NULL);
507 	struct tm *gmt = gmtime(&maildt);
508 	strftime(szmaildt, sizeof(szmaildt), "%x %X", gmt);
509 	textfile << "Date: " << szmaildt << endl;
510 	textfile << "From: " << inpMailFrom->value() << endl;
511 	textfile << "To: " << inpMailTo->value() << endl;
512 	textfile << "Subject: " << inpMailSubj->value() << endl;
513 	textfile << "Content-Type: text/plain; charset=\"UTF-8\"" << endl;
514 	textfile << endl;
515 	textfile << txtbuffComposer->text() << endl;
516 	textfile.close();
517 	cb_ClearComposer();
518 }
519 
SaveComposeMail()520 void SaveComposeMail()
521 {
522 	if (strlen(inpMailTo->value()) == 0 ||
523 		strlen(inpMailSubj->value()) == 0)
524 		return;
525 	if (sendfilename.empty()) {
526 		sendfilename = ARQ_mail_out_dir;
527 		sendfilename += "flarqmail.eml";
528 		sendfilename = nextEmailFile(sendfilename);
529 	}
530 	saveComposedText(sendfilename);
531 }
532 
SaveComposeTemplate()533 void SaveComposeTemplate()
534 {
535 	std::string templatename = ARQ_mail_out_dir;
536 	const char *p = FSEL::saveas("Save Template file", "*.tpl", templatename.c_str());
537 	if (!p) return;
538 	if (!*p) return;
539 
540 	saveComposedText(p);
541 }
542 
cb_SaveComposeMail()543 void cb_SaveComposeMail()
544 {
545 	if (Fl::event_state(FL_SHIFT))
546 		SaveComposeTemplate();
547 	else
548 		SaveComposeMail();
549 }
550 
cb_OpenComposeMail()551 void cb_OpenComposeMail()
552 {
553 	sendfilename.clear();
554 	selectTrafficOut(true);
555 	if (sendfilename.empty())
556 		return;
557 	readComposedFile(sendfilename);
558 }
559 
ComposeMail()560 void ComposeMail()
561 {
562 	if (composer == 0) {
563 		composer = arq_composer();
564 		composer->xclass(PACKAGE_TARNAME);
565 		txtbuffComposer = txtMailText->buffer();
566 		txtMailText->wrap_mode(1,80);
567 	}
568 	txtbuffComposer->text("");
569 	inpMailFrom->value("");
570 	inpMailTo->value("");
571 	inpMailSubj->value("");
572 
573 	composer->show();
574 }
575 
576 //======================================================================================
577 
noCR(std::string s)578 std::string noCR(std::string s)
579 {
580 	std::string text = s;
581 	size_t p;
582 	while ((p = text.find('\r')) != std::string::npos)
583 		text.erase(p,1);
584 	return text;
585 }
586 
createAsciiChars()587 void createAsciiChars()
588 {
589 	AsciiChars = "";
590 	AsciiChars += 0x09; // tab
591 	AsciiChars += 0x0A; // lf
592 	AsciiChars += 0x0D; // cr
593 	for (int n = 20; n < 256; n++) AsciiChars += n;
594 }
595 
initVals()596 void initVals()
597 {
598 	MyCall = "NOCALL";
599 	exponent = digi_arq->getExponent();
600 	iretries = digi_arq->getRetries();
601 	itimeout = digi_arq->getTimeout();
602 	txdelay = digi_arq->getTxDelay();
603 	iwaittime = digi_arq->getWaitTime();
604 	bcnInterval = 30;
605 	beacontext = "";
606 	cbMenuConfig();
607 	digi_arq->myCall(MyCall.c_str());
608 
609 }
610 
testDirectory(std::string dir)611 void testDirectory(std::string dir)
612 {
613 	std::string tstdir = ARQ_dir;
614 	tstdir += '/';
615 	tstdir.append(dir);
616 	ifstream test(tstdir.c_str());
617 	if (!test)
618 		mkdir(tstdir.c_str(), 0777);
619 	else
620 		test.close();
621 }
622 
readConfig()623 void readConfig()
624 {
625 	extern void cbMenuConfig();
626 	std::string configfname = ARQ_dir;
627 	configfname.append("flarq.config");
628 	ifstream configfile(configfname.c_str());
629 	if (configfile) {
630 		char tempstr[200];
631 		configfile.getline(tempstr,200);
632 		sscanf(tempstr,"%f", &vers);
633 		if (int(vers*10) != int(version*10))
634 			initVals();
635 		else {
636 			configfile >> MyCall;
637 			configfile >> exponent;
638 			configfile >> txdelay;
639 			configfile >> iretries;
640 			configfile >> iwaittime;
641 			configfile >> itimeout;
642 			configfile >> bcnInterval;
643 			configfile >> mainX;
644 			configfile >> mainY;
645 			configfile >> mainW;
646 			configfile >> mainH;
647 			configfile.ignore();
648 			configfile.getline(tempstr, 200);
649 			beacontext = tempstr;
650 			configfile >> restart_beacon;
651 
652 			digi_arq->myCall(MyCall.c_str());
653 			digi_arq->setExponent(exponent);
654 			digi_arq->setRetries(iretries);
655 			digi_arq->setTimeout(itimeout);
656 			digi_arq->setTxDelay(txdelay);
657 			digi_arq->setWaitTime(iwaittime);
658 		}
659 		configfile.close();
660 	} else
661 		initVals();
662 
663 }
664 
saveConfig()665 void saveConfig()
666 {
667 	std::string configfname = ARQ_dir;
668 	configfname.append("flarq.config");
669 	ofstream configfile(configfname.c_str());
670 	if (configfile) {
671 		int mainX = arqwin->x();
672 		int mainY = arqwin->y();
673 		int mainW = arqwin->w();
674 		int mainH = arqwin->h();
675 		configfile << VERSION << endl;
676 		configfile << MyCall << endl;
677 		configfile << exponent << endl;
678 		configfile << txdelay << endl;
679 		configfile << iretries << endl;
680 		configfile << iwaittime << endl;
681 		configfile << itimeout << endl;
682 		configfile << bcnInterval << endl;
683 		configfile << mainX << endl;
684 		configfile << mainY << endl;
685 		configfile << mainW << endl;
686 		configfile << mainH << endl;
687 		configfile << beacontext.c_str() << endl;
688 		configfile << restart_beacon << endl;
689 		configfile.close();
690 	}
691 }
692 
cbSetConfig()693 void cbSetConfig()
694 {
695 	digi_arq->setExponent(exponent);
696 	digi_arq->setRetries(iretries);
697 	digi_arq->setTimeout(itimeout);
698 	digi_arq->setTxDelay(txdelay);
699 	digi_arq->setWaitTime(iwaittime);
700 }
701 
closeConfig()702 void closeConfig()
703 {
704 	if (dlgconfig)
705 		dlgconfig->hide();
706 	cbSetConfig();
707 }
708 
cb_idtimer()709 void cb_idtimer()
710 {
711 	digi_arq->set_idtimer();
712 }
713 
cbMenuConfig()714 void cbMenuConfig()
715 {
716 	if (!dlgconfig) {
717 		dlgconfig = arq_configure();
718 		dlgconfig->xclass(PACKAGE_TARNAME);
719 		choiceBlockSize->add("16");
720 		choiceBlockSize->add("32");
721 		choiceBlockSize->add("64");
722 		choiceBlockSize->add("128");
723 		choiceBlockSize->add("256");
724 	}
725 	choiceBlockSize->index(exponent - 4);
726 	choiceBlockSize->redraw();
727 	dlgconfig->show();
728 }
729 
cbMenuAbout()730 void cbMenuAbout()
731 {
732 	fl_message2("flarq - ARQ client\nversion: %s\nw1hkj@@w1hkj.com", VERSION);
733 }
734 
735 std::string txhold = "";
736 
737 //=============================================================================
738 
mpsk_on()739 void mpsk_on()
740 {
741 	std::string s;
742 	s.append(1, MPSK_CMD).append(MPSK_TX).append(1, MPSK_END);
743 	try {
744 		tcpip->send(s);
745 	}
746 	catch (const SocketException& e) {
747 		cerr << e.what() << '\n';
748 	}
749 }
750 
mpsk_off_after_buffer_sent()751 void mpsk_off_after_buffer_sent()
752 {
753 	std::string s;
754 	s.append(1, MPSK_CMD).append(MPSK_TX2RX).append(1, MPSK_END);
755 	try {
756 		tcpip->send(s);
757 	}
758 	catch (const SocketException& e) {
759 		cerr << e.what() << '\n';
760 	}
761 }
762 
mpsk_off()763 void mpsk_off()
764 {
765 	std::string s;
766 	s.append(1, MPSK_CMD).append(MPSK_RX).append(1, MPSK_END);
767 	try {
768 		tcpip->send(s);
769 	}
770 	catch (const SocketException& e) {
771 		cerr << e.what() << '\n';
772 	}
773 }
774 
MPSK_client_transmit(const std::string & s)775 void MPSK_client_transmit(const std::string& s)
776 {
777 	if (s.empty())
778 		return;
779 	std::string tosend;
780 	tosend.reserve(s.length() * 2);
781 	for (size_t i = 0; i < s.length(); i++)
782 		tosend.append(1, MPSK_BYTE).append(1, s[i]);
783 
784 	try {
785 		mpsk_on();
786 		tcpip->send(tosend);
787 		mpsk_off_after_buffer_sent();
788 	}
789 	catch (const SocketException& e) {
790 		cerr << e.what() << '\n';
791 	}
792 }
793 
MPSK_Socket_rcv_loop(void *)794 void MPSK_Socket_rcv_loop(void *)
795 {
796 	if (inLoop) Fl::wait(0.1);
797 	inLoop = true;
798 
799 	char cs;
800 
801 	try {
802 		while (tcpip->wait(0)) {
803 			tcpip->recv(&cs, 1);
804 
805 			if (isRxChar) {
806 				rxbuffer += cs;
807 				isRxChar = false;
808 				continue;
809 			}
810 			if (isTxChar) {
811 				if (cs < 28 || cs > 31)
812 					txbuffer += cs;
813 				isTxChar = false;
814 				continue;
815 			}
816 			if (isCmdChar) {
817 				if (cs == MPSK_CMDEND) {
818 					isCmdChar = false;
819 					if (cmdbuffer.find("RX_AFTER_TX OK") != std::string::npos) {
820 						rxbuffer += 0x06;
821 						cmdbuffer.clear();
822 						txbuffer.clear();
823 					}
824 					continue;
825 				}
826 				cmdbuffer += cs;
827 				continue;
828 			}
829 			if (cs == MPSK_ISRX) {
830 				isRxChar = true;
831 				continue;
832 			}
833 			if (cs == MPSK_ISCMD) {
834 				isCmdChar = true;
835 				continue;
836 			}
837 			if (cs == MPSK_ISTX) {
838 				isTxChar = true;
839 				continue;
840 			}
841 		}
842 	}
843 	catch (const SocketException& e) {
844 		cerr << e.what() << '\n';
845 	}
846 
847 	inLoop = false;
848 	Fl::add_timeout(0.01, MPSK_Socket_rcv_loop);
849 }
850 
851 //=============================================================================
852 
client_transmit(const std::string & s)853 void client_transmit(const std::string& s )
854 {
855 	try {
856 		if (!s.empty())
857 			tcpip->send(s);
858 	}
859 	catch (const SocketException& e) {
860 		cerr << e.what() << '\n';
861 	}
862 }
863 
Socket_rcv_loop(void *)864 void Socket_rcv_loop(void *)
865 {
866 	if (inLoop)
867 		Fl::wait(0.1);
868 	inLoop = true;
869 
870 	try {
871 		tcpip->set_nonblocking(true);
872 		tcpip->recv(rxbuffer);
873 		tcpip->set_nonblocking(false);
874 	}
875 	catch (const SocketException& e) {
876 		cerr << e.what() << '\n';
877 	}
878 
879 	inLoop = false;
880 	Fl::add_timeout(0.01, Socket_rcv_loop);
881 }
882 
client_receive(char & c)883 bool client_receive(char &c)
884 {
885 	if (inLoop) Fl::wait(0.1);
886 	inLoop = true;
887 	bufsize = rxbuffer.length();
888 	if (bufsize) {
889 		if (bufptr < bufsize) {
890 			c = rxbuffer[bufptr++];
891 			inLoop = false;
892 			return true;
893 		}
894 	}
895 	rxbuffer.clear();
896 	bufsize = 0;
897 	bufptr = 0;
898 	inLoop = false;
899 	return false;
900 }
901 
902 int autobeaconcounter = 0;
903 char bcnMsg[40];
904 
arqAutoBeacon(void *)905 void arqAutoBeacon(void *)
906 {
907 	if (autobeacon == ON) {
908 		int currstate = digi_arq->state();
909 		btnCONNECT->deactivate();
910 		btnCONNECT->redraw();
911 		if (currstate > 0x7F) {
912 			txtBeaconing->value("Beaconing");
913 			btnBEACON->deactivate();
914 			btnBEACON->redraw();
915 			Fl::repeat_timeout(1.0, arqAutoBeacon);
916 			return;
917 		} else if (currstate == DOWN || currstate == TIMEDOUT) {
918 			if (autobeaconcounter == 0) {
919 				digi_arq->sendBeacon( beacontext );
920 				autobeaconcounter = bcnInterval;
921 				Fl::repeat_timeout(1.0 + txdelay / 1000.0, arqAutoBeacon);
922 			} else {
923 				snprintf(bcnMsg, sizeof(bcnMsg), "Bcn in: %d sec", autobeaconcounter);
924 				txtBeaconing->value(bcnMsg);
925 				btnBEACON->label("STOP");
926 				btnBEACON->redraw_label();
927 				btnBEACON->activate();
928 				btnBEACON->redraw();
929 				autobeaconcounter--;
930 				Fl::repeat_timeout(1.0, arqAutoBeacon);
931 			}
932 			return;
933 		} else {
934 			autobeaconcounter = 0;
935 			btnBEACON->value(0);
936 			btnBEACON->label("Beacon");
937 			btnBEACON->redraw_label();
938 			btnBEACON->activate();
939 			btnBEACON->redraw();
940 			txtBeaconing->value("Beacon Off");
941 		}
942 	} else if (autobeacon == OFF) {
943 		autobeaconcounter = 0;
944 		btnCONNECT->activate();
945 		btnCONNECT->redraw();
946 		btnBEACON->value(0);
947 		btnBEACON->label("Beacon");
948 		btnBEACON->redraw_label();
949 		btnBEACON->activate();
950 		btnBEACON->redraw();
951 		txtBeaconing->value("Beacon Off");
952 	} else { // autobeacon == WAIT
953 		btnBEACON->deactivate();
954 		btnBEACON->redraw();
955 		Fl::repeat_timeout(1.0, arqAutoBeacon);
956 	}
957 }
958 
arqBEACON()959 void arqBEACON()
960 {
961 	if (autobeacon != ON) {
962 		autobeacon = ON;
963 		btnBEACON->value(1);
964 		Fl::add_timeout(0.01, arqAutoBeacon);
965 	} else {
966 		autobeacon = OFF;
967 		btnBEACON->value(0);
968 	}
969 }
970 
printstring(std::string s)971 void printstring(std::string s)
972 {
973 	for (size_t n = 0; n < s.length(); n++)
974 		if (s[n] < ' ') printf("<%02d>",(int)s[n]);
975 		else printf("%c", s[n]);
976 	printf("\n");
977 }
978 
arqCLOSE()979 void arqCLOSE()
980 {
981 	tcpip->close();
982 	saveConfig();
983 	exit_server();
984 	exit(0);
985 }
986 
clearText()987 void clearText()
988 {
989 	txtarqload = "";
990 	txtbuffARQ->text("");
991 	txtStatus->value("");
992 	txtStatus2->value("");
993 }
994 
restart()995 void restart()
996 {
997 	TX.clear();
998 	rxfname = "";
999 	rxTextReady = false;
1000 	prgStatus->value(0.0);
1001 	prgStatus->label("");
1002 	prgStatus->redraw();
1003 	prgStatus->redraw_label();
1004 	rxARQfile = false;
1005 	sendingfile = false;
1006 	incomingText.clear();
1007 	clearText();
1008 }
1009 
arqCONNECT()1010 void arqCONNECT()
1011 {
1012 	int state = Fl::event_state();
1013 	if ((state & FL_CTRL) == FL_CTRL) {
1014 		digi_arq->restart_arq();
1015 		txtURCALL->value("");
1016 		restart();
1017 		return;
1018 	}
1019 	if (digi_arq->state() < ARQ_CONNECTED) {
1020 		if (strlen(txtURCALL->value()) > 0)
1021 			digi_arq->connect(txtURCALL->value());
1022 	} else {
1023 		if (rxARQfile || sendingfile)
1024 			abortTransfer();
1025 		else {
1026 			restart();
1027 			digi_arq->disconnect();
1028 			txtURCALL->value("");
1029 		}
1030 	}
1031 }
1032 
fileExists(std::string fname)1033 bool fileExists(std::string fname)
1034 {
1035 	ifstream test(fname.c_str());
1036 	if (test) {
1037 		test.close();
1038 		return true;
1039 	}
1040 	return false;
1041 }
1042 
nextFileName(std::string fname)1043 std::string nextFileName(std::string fname)
1044 {
1045 	int nbr = 0;
1046 	char szNbr[20];
1047 	std::string name;
1048 	std::string ext;
1049 	std::string nuname;
1050 	size_t p;
1051 
1052 	p = fname.find_last_of('.');
1053 	if (p != std::string::npos) {
1054 		ext = fname.substr(p);
1055 		name = fname.substr(0,p);
1056 	} else {
1057 		ext = "";
1058 		name = fname;
1059 	}
1060 
1061 	do {
1062 		nbr++;
1063 		nuname = name;
1064 		snprintf(szNbr, sizeof(szNbr), ".dup%-d", nbr);
1065 		nuname.append(szNbr);
1066 		nuname.append(ext);
1067 	} while (fileExists(nuname));
1068 
1069 	return nuname;
1070 }
1071 
saveEmailFile()1072 void saveEmailFile()
1073 {
1074 	static char xfrmsg[80];
1075 	std::string tempname;
1076 
1077 	time(&EndTime_t);
1078 	TransferTime = difftime(EndTime_t, StartTime_t);
1079 	snprintf(xfrmsg, sizeof(xfrmsg), "Transfer Completed in %4.0f sec's", TransferTime);
1080 
1081 	std::string savetoname = ARQ_mail_in_dir;
1082 
1083 	if (rxfname.find(".eml") == std::string::npos)
1084 		rxfname.append(".eml");
1085 	savetoname.append(rxfname);
1086 	while (fileExists(savetoname))
1087 		savetoname = nextFileName(savetoname);
1088 
1089 	ofstream tfile(savetoname.c_str(), ios::binary);
1090 	if (tfile) {
1091 		tfile << txtarqload;
1092 		tfile.close();
1093 	}
1094 
1095 	txtStatus->value(xfrmsg);
1096 	rxfname = "";
1097 	txtarqload = "";
1098 	rxTextReady = false;
1099 }
1100 
saveRxFile()1101 void saveRxFile()
1102 {
1103 	static char xfrmsg[80];
1104 	time(&EndTime_t);
1105 	TransferTime = difftime(EndTime_t, StartTime_t);
1106 	snprintf(xfrmsg, sizeof(xfrmsg), "Transfer Completed in %4.0f sec's", TransferTime);
1107 
1108 	std::string savetoname = ARQ_recv_dir;
1109 	savetoname.append(rxfname);
1110 	if (fileExists(savetoname))
1111 		savetoname = nextFileName(savetoname);
1112 
1113 	ofstream tfile(savetoname.c_str(), ios::binary);
1114 	if (tfile) {
1115 		tfile << txtarqload;
1116 		tfile.close();
1117 	}
1118 
1119 	txtStatus->value(xfrmsg);
1120 	rxfname = "";
1121 	txtarqload = "";
1122 	rxTextReady = false;
1123 }
1124 
payloadText(std::string s)1125 void payloadText(std::string s)
1126 {
1127 	static char szPercent[10];
1128 	std::string text = noCR(s);
1129 
1130 	txtARQ->insert(text.c_str());
1131 	txtARQ->show_insert_position();
1132 	txtARQ->redraw();
1133 
1134 	incomingText.append (s);
1135 
1136 	if (!rxARQfile)
1137 		if ((startpos = incomingText.find(arqstart)) != std::string::npos) {
1138 			rxARQfile = true;
1139 			startpos += arqstart.length();
1140 			time(&StartTime_t);
1141 		}
1142 	if (rxARQfile) {
1143 		if (!rxARQhavesize) {
1144 			if ( (sizepos = incomingText.find(arqsizespec)) != std::string::npos) {
1145 				sizepos += arqsizespec.length();
1146 				if ((lfpos = incomingText.find('\n', sizepos)) != std::string::npos) {
1147 					std::string sizechars = incomingText.substr(sizepos, lfpos - sizepos);
1148 					unsigned int tempnbr;
1149 					sscanf(sizechars.c_str(), "%u", &tempnbr);
1150 					arqPayloadSize = tempnbr;
1151 					rxARQhavesize = true;
1152 					char statusmsg[40];
1153 					snprintf(statusmsg, sizeof(statusmsg), "Rcvg: %d",
1154 						static_cast<int>(arqPayloadSize));
1155 					txtStatus->value(statusmsg);
1156 				}
1157 			}
1158 		} else {
1159 			if (startpos != std::string::npos) {
1160 				float partial = incomingText.length() - startpos;
1161 				snprintf(szPercent, sizeof(szPercent), "%3.0f %%", 100.0 * partial / arqPayloadSize);
1162 				prgStatus->value( partial / arqPayloadSize );
1163 				prgStatus->label(szPercent);
1164 			 } else {
1165 				prgStatus->value(0.0);
1166 				prgStatus->label("");
1167 			 }
1168 			prgStatus->redraw();
1169 			prgStatus->redraw_label();
1170 		}
1171 		if ((endpos = incomingText.find(arqend)) != std::string::npos) {
1172 			haveemail = false;
1173 			fnamepos = incomingText.find(arqfile);
1174 			fnamepos += arqfile.length();
1175 			indx = incomingText.find('\n', fnamepos);
1176 			rxfname = incomingText.substr(fnamepos, indx - fnamepos);
1177 			txtarqload = incomingText.substr(startpos, endpos - startpos);
1178 			if (incomingText.find(arqbase64) != std::string::npos) {
1179 				base64 b64;
1180 				txtarqload = b64.decode(txtarqload);
1181 			}
1182 			if (incomingText.find(arqemail) != std::string::npos)
1183 				haveemail = true;
1184 			startpos = std::string::npos;
1185 			endpos = std::string::npos;
1186 			fnamepos = std::string::npos;
1187 			indx = std::string::npos;
1188 			sizepos = std::string::npos;
1189 			lfpos = std::string::npos;
1190 			arqPayloadSize = 0;
1191 			rxARQfile = false;
1192 			rxARQhavesize = false;
1193 			rxTextReady = true;
1194 			if (incomingText.find("FLMSG_XFR") != std::string::npos)
1195 				xml_rx_text_ready = true;
1196 			incomingText = "";
1197 			txtStatus->value("");
1198 			prgStatus->value(0.0);
1199 			prgStatus->label("");
1200 			prgStatus->redraw();
1201 			prgStatus->redraw_label();
1202 		}
1203 	}
1204 }
1205 
cbClearText()1206 void cbClearText()
1207 {
1208 	txtbuffARQ->text("");
1209 }
1210 
abortedTransfer()1211 void abortedTransfer()
1212 {
1213 	restart();
1214 	txtARQ->insert("transfer aborted\n");
1215 	btnCONNECT->activate();
1216 }
1217 
abortTransfer()1218 void abortTransfer()
1219 {
1220 	sendingfile = false;
1221 	SendingEmail = false;
1222 	rxARQfile = false;
1223 	btnCONNECT->label("ABORTING");
1224 	btnCONNECT->redraw_label();
1225 	btnCONNECT->deactivate();
1226 	digi_arq->abort();
1227 }
1228 
rxBeaconCallsign(std::string s)1229 void rxBeaconCallsign(std::string s)
1230 {
1231 	txtURCALL->value(s.c_str());
1232 	beaconrcvd = true;
1233 }
1234 
moveEmailFile()1235 void moveEmailFile()
1236 {
1237 	if (MailFileName.empty()) return;
1238 	if (MailSaveFileName.empty()) return;
1239 
1240 	ifstream infile(MailFileName.c_str(), ios::in | ios::binary);
1241 
1242 	if (MailSaveFileName.find(".eml") == std::string::npos)
1243 		MailSaveFileName.append(".eml");
1244 	while (fileExists(MailSaveFileName))
1245 		MailSaveFileName = nextFileName(MailSaveFileName);
1246 
1247 	ofstream outfile(MailSaveFileName.c_str(), ios::out | ios::binary);
1248 	char c;
1249 
1250 	if (infile && outfile) {
1251 		while (!infile.eof()) {
1252 			infile.get(c);
1253 			outfile.put(c);
1254 		}
1255 		infile.close();
1256 		outfile.close();
1257 		unlink(MailFileName.c_str());
1258 	}
1259 	MailFileName.clear();
1260 	MailSaveFileName.clear();
1261 }
1262 
1263 
sendEmailFile()1264 void sendEmailFile()
1265 {
1266 	if (arqstate < ARQ_CONNECTED) {
1267 		fl_alert2("Not connected");
1268 		return;
1269 	}
1270 	sendfilename.clear();
1271 	selectTrafficOut(false);
1272 
1273 	if (sendfilename.empty())
1274 		return;
1275 
1276 	char cin;
1277 	size_t txtsize;
1278 	std::string textin = "";
1279 	char sizemsg[40];
1280 	size_t p;
1281 
1282 	ifstream textfile;
1283 	textfile.open(sendfilename.c_str(), ios::binary);
1284 	if (textfile) {
1285 		for (size_t i = 0; i < sendfilename.length(); i++)
1286 			if (sendfilename[i] == '\\') sendfilename[i] = '/';
1287 		MailFileName = sendfilename;
1288 		TX.erase();
1289 		TX.append(arqfile);
1290 		MailSaveFileName = ARQ_mail_sent_dir;
1291 		p = sendfilename.find_last_of('/');
1292 		if (p != std::string::npos) p++;
1293 		MailSaveFileName.append(sendfilename.substr(p));
1294 		TX.append(sendfilename.substr(p));
1295 		TX.append("\n");
1296 		TX.append(arqemail);
1297 
1298 // only allow ASCII printable characters
1299 		while (textfile.get(cin)) textin += (cin & 0xFF);
1300 		textfile.close();
1301 		if ( textin.find_first_not_of(AsciiChars) != std::string::npos) {
1302 			fl_alert2("File contains non-ASCII bytes and must be sent as binary.");
1303 			return;
1304 		}
1305 
1306 		textfile.close();
1307 		txtsize = textin.length();
1308 		arqPayloadSize = txtsize;
1309 		blocksSent = 0;
1310 		snprintf(sizemsg, sizeof(sizemsg), "ARQ:SIZE::%d\n",
1311 			static_cast<int>(txtsize));
1312 		TX.append(sizemsg);
1313 		TX.append(arqstart);
1314 		TX.append(textin);
1315 		TX.append(arqend);
1316 		traffic = true;
1317 		statusmsg = "Sending email: ";
1318 		statusmsg.append(sendfilename.substr(p));
1319 		txtStatus->value(statusmsg.c_str());
1320 		SendingEmail = true;
1321 		sendingfile = true;
1322 		cbClearText();
1323 		return;
1324 	}
1325 
1326 	traffic = false;
1327 	sendingfile = false;
1328 	SendingEmail = false;
1329 }
1330 
1331 
sendAsciiFile()1332 void sendAsciiFile()
1333 {
1334 	if (arqstate < ARQ_CONNECTED) {
1335 		fl_alert2("Not connected");
1336 		return;
1337 	}
1338 	std::string readfromname = ARQ_send_dir;
1339 	readfromname.append(rxfname);
1340 	const char *p = FSEL::select("ARQ text file", "*.txt\t*", readfromname.c_str());
1341 	char cin;
1342 	size_t txtsize;
1343 	std::string textin = "";
1344 	char sizemsg[40];
1345 	if (p && *p) {
1346 		ifstream textfile;
1347 		textfile.open(p);
1348 		if (textfile) {
1349 			TX.erase();
1350 			TX.append(arqfile);
1351 			p = fl_filename_name(p);
1352 			TX.append(p);
1353 			TX.append("\n");
1354 			TX.append(arqascii);
1355 
1356 			while (textfile.get(cin)) textin += (cin & 0xFF);
1357 			textfile.close();
1358 			if ( textin.find_first_not_of(AsciiChars) != std::string::npos) {
1359 				fl_alert2("File contains non-ASCII bytes and must be sent as binary.");
1360 				return;
1361 			}
1362 
1363 			txtsize = textin.length();
1364 			arqPayloadSize = txtsize;
1365 			blocksSent = 0;
1366 			snprintf(sizemsg, sizeof(sizemsg), "ARQ:SIZE::%d\n",
1367 				static_cast<int>(txtsize));
1368 			TX.append(sizemsg);
1369 			TX.append(arqstart);
1370 			TX.append(textin);
1371 			TX.append(arqend);
1372 			traffic = true;
1373 			sendingfile = true;
1374 			statusmsg = "Sending ASCII file: ";
1375 			statusmsg.append(p);
1376 				txtStatus->value(statusmsg.c_str());
1377 			cbClearText();
1378 			return;
1379 		}
1380 	}
1381 	traffic = false;
1382 	sendingfile = false;
1383 }
1384 
sendImageFile()1385 void sendImageFile()
1386 {
1387 	if (arqstate < ARQ_CONNECTED) {
1388 		fl_alert2("Not connected");
1389 		return;
1390 	}
1391 	const char *p = FSEL::select(_("ARQ image file"), "Images\t*.{png,jpg,bmp}", "");
1392 	char cin;
1393 	size_t b64size;
1394 	std::string textin = "";
1395 	std::string b64text;
1396 	base64 b64(true);
1397 	char sizemsg[40];
1398 	if (p && *p) {
1399 		ifstream textfile;
1400 		textfile.open(p, ios::binary);
1401 		if (textfile) {
1402 			TX.erase();
1403 			TX.append(arqfile);
1404 			p = fl_filename_name(p);
1405 			TX.append(p);
1406 			TX.append("\n");
1407 			TX.append(arqbase64);
1408 			while (textfile.get(cin))
1409 				textin += cin;
1410 			textfile.close();
1411 			b64text = b64.encode(textin);
1412 			b64size = b64text.length();
1413 			snprintf(sizemsg, sizeof(sizemsg), "ARQ:SIZE::%d\n",
1414 				static_cast<int>(b64size));
1415 			arqPayloadSize = b64size;
1416 			blocksSent = 0;
1417 			TX.append(sizemsg);
1418 			TX.append(arqstart);
1419 			TX.append(b64text);
1420 			TX.append(arqend);
1421 			traffic = true;
1422 			sendingfile = true;
1423 			statusmsg = "Sending image: ";
1424 			statusmsg.append(p);
1425 				txtStatus->value(statusmsg.c_str());
1426 			cbClearText();
1427 			return;
1428 		}
1429 	}
1430 	traffic = false;
1431 	sendingfile = false;
1432 }
1433 
sendBinaryFile()1434 void sendBinaryFile()
1435 {
1436 	if (arqstate < ARQ_CONNECTED) {
1437 		fl_alert2("Not connected");
1438 		return;
1439 	}
1440 	const char *p = FSEL::select("ARQ file", "*", "");
1441 	char cin;
1442 	size_t b64size;
1443 	std::string textin = "";
1444 	std::string b64text;
1445 	base64 b64(true);
1446 	char sizemsg[40];
1447 	if (p && *p) {
1448 		ifstream textfile;
1449 		textfile.open(p, ios::binary);
1450 		if (textfile) {
1451 			TX.erase();
1452 			TX.append(arqfile);
1453 			p = fl_filename_name(p);
1454 			TX.append(p);
1455 			TX.append("\n");
1456 			TX.append(arqbase64);
1457 			while (textfile.get(cin))
1458 				textin += cin;
1459 			textfile.close();
1460 			b64text = b64.encode(textin);
1461 			b64size = b64text.length();
1462 			snprintf(sizemsg, sizeof(sizemsg), "ARQ:SIZE::%d\n",
1463 				static_cast<int>(b64size));
1464 			arqPayloadSize = b64size;
1465 			blocksSent = 0;
1466 			TX.append(sizemsg);
1467 			TX.append(arqstart);
1468 			TX.append(b64text);
1469 			TX.append(arqend);
1470 			traffic = true;
1471 			sendingfile = true;
1472 			statusmsg = "Sending binary: ";
1473 			statusmsg.append(p);
1474 				txtStatus->value(statusmsg.c_str());
1475 			cbClearText();
1476 			return;
1477 		}
1478 	}
1479 	traffic = false;
1480 	sendingfile = false;
1481 }
1482 
send_xml_text(std::string fname,std::string txt)1483 void send_xml_text(std::string fname, std::string txt)
1484 {
1485 	if (arqstate < ARQ_CONNECTED) {
1486 		fl_alert2("Not connected");
1487 		return;
1488 	}
1489 
1490 	size_t txtsize;
1491 	char sizemsg[40];
1492 
1493 	if (!txt.empty()) {
1494 		TX.erase();
1495 		TX.append(arqfile);
1496 		TX.append(fname);
1497 		TX.append("\n");
1498 		TX.append(arqascii);
1499 		txtsize = txt.length();
1500 		arqPayloadSize = txtsize;
1501 		blocksSent = 0;
1502 		snprintf(sizemsg, sizeof(sizemsg), "ARQ:SIZE::%d\n",
1503 			static_cast<int>(txtsize));
1504 		TX.append(sizemsg);
1505 		TX.append(arqstart);
1506 		TX.append(txt);
1507 		TX.append(arqend);
1508 		traffic = true;
1509 		sendingfile = true;
1510 		statusmsg = "Sending XML payload: ";
1511 		statusmsg.append(fname);
1512 			txtStatus->value(statusmsg.c_str());
1513 		cbClearText();
1514 		return;
1515 	}
1516 	traffic = false;
1517 	sendingfile = false;
1518 }
1519 
1520 static char statemsg[80];
1521 
dispState()1522 void dispState()
1523 {
1524 	static int last_state = DOWN;
1525 	int currstate = digi_arq->state();
1526 	static char xfrmsg[80];
1527 	static char szPercent[10];
1528 
1529 	arqstate = currstate & 0x7F;
1530 	if (last_state != currstate) {
1531 		last_state = currstate;
1532 		if (arqstate == DOWN  || arqstate == TIMEDOUT) {
1533 			if (btnCONNECT->active()) {
1534 				btnCONNECT->label("Connect");
1535 			}
1536 			btnBEACON->activate();
1537 			mnu->redraw();
1538 		}
1539 		else if (arqstate == ARQ_CONNECTED || arqstate == WAITING) {
1540 			if (btnCONNECT->active())
1541 				btnCONNECT->label("Disconnect");
1542 			if (!autobeacon)
1543 				btnBEACON->deactivate();
1544 			mnuSend->activate();
1545 			mnu->redraw();
1546 		}
1547 		if (rxARQfile || sendingfile) {
1548 			if (btnCONNECT->active())
1549 				btnCONNECT->label("Abort");
1550 		}
1551 		if (btnCONNECT->active())
1552 			btnCONNECT->redraw_label();
1553 
1554 		if (currstate <= 0x7F) { // receiving
1555 			switch (currstate) {
1556 				case ARQ_CONNECTING :
1557 					snprintf(statemsg, sizeof(statemsg), "CONNECTING: %d", digi_arq->getTimeLeft());
1558 					txtState->value(statemsg);
1559 					txtState->redraw();
1560 					autobeacon = WAIT;
1561 					break;
1562 				case WAITFORACK :
1563 					snprintf(statemsg, sizeof(statemsg), "WAITING FOR ACK   ");
1564 					txtState->value(statemsg);
1565 					txtState->redraw();
1566 					autobeacon = WAIT;
1567 					break;
1568 				case ABORT:
1569 				case ABORTING :
1570 					txtState->value("ABORTING XFR");
1571 					txtState->redraw();
1572 					autobeacon = WAIT;
1573 					break;
1574 				case WAITING :
1575 				case ARQ_CONNECTED :
1576 					char szState[80];
1577 					snprintf(szState, sizeof(szState),"CONNECTED - Quality = %4.2f",
1578 						digi_arq->quality());
1579 					indCONNECT->color(FL_GREEN);
1580 					indCONNECT->redraw();
1581 					txtBeaconing->value("");
1582 					txtState->value(szState);
1583 					txtURCALL->value( digi_arq->urCall().c_str() );
1584 					autobeacon = WAIT;
1585 					break;
1586 				case TIMEDOUT :
1587 					indCONNECT->color(FL_WHITE);
1588 					indCONNECT->redraw();
1589 					txtState->value("TIMED OUT");
1590 					txtStatus->value("");
1591 					if (restart_beacon && autobeacon != ON) {
1592 						autobeacon = ON;
1593 						Fl::remove_timeout(arqAutoBeacon);
1594 						Fl::add_timeout(1.0 + txdelay / 1000.0, arqAutoBeacon);
1595 					}
1596 					beaconrcvd = false;
1597 					break;
1598 				case DISCONNECT:
1599 				case DISCONNECTING:
1600 					txtState->value("DISCONNECTING");
1601 					break;
1602 				case DOWN :
1603 				default :
1604 					if (autobeacon != ON && restart_beacon) {
1605 						btnBEACON->activate();
1606 						Fl::remove_timeout(arqAutoBeacon);
1607 						Fl::add_timeout(1.0 + txdelay / 1000.0, arqAutoBeacon);
1608 					}
1609 					indCONNECT->color(FL_WHITE);
1610 					indCONNECT->redraw();
1611 					autobeacon = ON;
1612 					txtState->value("NOT CONNECTED");
1613 					txtStatus->value("");
1614 			}
1615 		}
1616 	}
1617 
1618 	if (sendingfile == true) {
1619 		if (digi_arq->transferComplete()) {
1620 			time(&EndTime_t);
1621 			TransferTime = difftime(EndTime_t,StartTime_t);
1622 			snprintf(xfrmsg, sizeof(xfrmsg), "Transfer Completed in %4.0f sec's", TransferTime);
1623 			txtStatus->value(xfrmsg);
1624 			blocksSent = 0;
1625 			prgStatus->value(0.0);
1626 			prgStatus->label("");
1627 			prgStatus->redraw();
1628 			prgStatus->redraw_label();
1629 			sendingfile = false;
1630 		}
1631 		else {
1632 			prgStatus->value( digi_arq->percentSent());
1633 			snprintf(szPercent, sizeof(szPercent), "%3.0f %%", 100.0 * digi_arq->percentSent());
1634 			prgStatus->label(szPercent);
1635 			prgStatus->redraw();
1636 			prgStatus->redraw_label();
1637 		}
1638 	}
1639 	if (SendingEmail == true) {
1640 		if (digi_arq->transferComplete()) {
1641 			time(&EndTime_t);
1642 			TransferTime = difftime(EndTime_t,StartTime_t);
1643 			snprintf(xfrmsg, sizeof(xfrmsg), "Transfer Completed in %4.0f sec's", TransferTime);
1644 			txtStatus->value(xfrmsg);
1645 			moveEmailFile();
1646 			blocksSent = 0;
1647 			prgStatus->value(0.0);
1648 			prgStatus->label("");
1649 			prgStatus->redraw();
1650 			prgStatus->redraw_label();
1651 			SendingEmail = false;
1652 		}
1653 		else {
1654 			prgStatus->value( digi_arq->percentSent());
1655 			snprintf(szPercent, sizeof(szPercent), "%3.0f %%", 100.0 * digi_arq->percentSent());
1656 			prgStatus->label(szPercent);
1657 			prgStatus->redraw();
1658 			prgStatus->redraw_label();
1659 		}
1660 	}
1661 }
1662 
mainloop(void *)1663 void mainloop(void *)
1664 {
1665 
1666 	if (traffic) {
1667 		digi_arq->sendText(TX);
1668 		traffic = false;
1669 		time(&StartTime_t);
1670 	}
1671 	dispState();
1672 	if (rxTextReady) {
1673 		if (haveemail)
1674 			saveEmailFile();
1675 		else if (!xml_rx_text_ready)
1676 			saveRxFile();
1677 	}
1678 	Fl::repeat_timeout(0.1, mainloop);
1679 }
1680 
changeMyCall(const char * nucall)1681 void changeMyCall(const char *nucall)
1682 {
1683 	int currstate = digi_arq->state();
1684 	if (currstate != DOWN)
1685 		return;
1686 
1687 	MyCall = nucall;
1688 	for (size_t i = 0; i < MyCall.length(); i++)
1689 		MyCall[i] = toupper(MyCall[i]);
1690 
1691 	txtMyCall->value(MyCall.c_str());
1692 	digi_arq->myCall(MyCall.c_str());
1693 
1694 	std::string title = "flarq ";
1695 	title.append(VERSION);
1696 	title.append(" - ");
1697 	title.append(MyCall);
1698 	arqwin->label(title.c_str());
1699 
1700 }
1701 
changeBeaconText(const char * txt)1702 void changeBeaconText(const char *txt)
1703 {
1704 	beacontext = txt;
1705 }
1706 
TALKprint(std::string s)1707 void TALKprint(std::string s)
1708 {
1709 	txtRX->insert(s.c_str());
1710 	txtRX->show_insert_position();
1711 	txtRX->redraw();
1712 }
1713 
clear_STATUS(void * arg)1714 void clear_STATUS(void* arg)
1715 {
1716 	txtStatus2->value("");
1717 }
1718 
1719 
STATUSprint(std::string s,double disptime)1720 void STATUSprint(std::string s, double disptime)
1721 {
1722 	txtStatus2->value(s.c_str());
1723 	if (disptime > 0.0) {
1724 		Fl::remove_timeout( clear_STATUS );
1725 		Fl::add_timeout( disptime, clear_STATUS );
1726 	}
1727 }
1728 
cbSendTalk()1729 void cbSendTalk()
1730 {
1731 	std::string tosend;
1732 	tosend = txtTX->value();
1733 	if (tosend.empty()) return;
1734 	tosend += '\n';
1735 	digi_arq->sendPlainText(tosend);
1736 	txtTX->value("");
1737 	txtRX->insert(tosend.c_str());
1738 	txtRX->show_insert_position();
1739 	txtRX->redraw();
1740 }
1741 
arqlog(std::string nom,std::string s)1742 void arqlog(std::string nom,std::string s)
1743 {
1744 	static char szGMT[80];
1745 	tm *now;
1746 	time_t tm;
1747 	std::string strdebug;
1748 
1749 	time(&tm);
1750 	now = localtime( &tm );
1751 	strftime(szGMT, sizeof(szGMT), "[%X] ", now);
1752 
1753 	for (unsigned int i = 0; i < s.length(); i++)
1754 		if (s[i] < 32)
1755 			strdebug += ASCII[(int)s[i]];
1756 		else
1757 			strdebug += s[i];
1758 	ofstream logfile(Logfile.c_str(), ios::app);
1759 	if (logfile)
1760 		logfile << nom << szGMT << strdebug << endl;
1761 }
1762 
DEBUGrxprint(std::string s)1763 void DEBUGrxprint(std::string s)
1764 {
1765 	std::string text = noCR(s);
1766 	txtRX->insert(text.c_str());
1767 	txtRX->show_insert_position();
1768 	txtRX->redraw();
1769 	arqlog("<RX>",s);
1770 }
1771 
DEBUGtxprint(std::string s)1772 void DEBUGtxprint(std::string s)
1773 {
1774 	std::string text = noCR(s);
1775 	txtRX->insert(text.c_str());
1776 	txtRX->show_insert_position();
1777 	txtRX->redraw();
1778 	arqlog("<TX>",s);
1779 }
1780 
TXecho(std::string s)1781 void TXecho(std::string s)
1782 {
1783 	blocksSent += s.length();
1784 	std::string text = noCR(s);
1785 	txtARQ->insert(text.c_str());
1786 	txtARQ->show_insert_position();
1787 	txtARQ->redraw();
1788 }
1789 
1790 
style_unfinished_cb(int,void *)1791 void style_unfinished_cb(int, void*) {
1792 }
1793 
cbClearTalk()1794 void cbClearTalk()
1795 {
1796 	txtbuffRX->text("");
1797 }
1798 
cb_arqwin(Fl_Widget *,void *)1799 void cb_arqwin(Fl_Widget *, void*)
1800 {
1801 	arqCLOSE();
1802 }
1803 
1804 std::string pname;
1805 
main(int argc,char * argv[])1806 int main (int argc, char *argv[] )
1807 {
1808 	sscanf(VERSION, "%f", &version);
1809 
1810 	set_unexpected(handle_unexpected);
1811 	set_terminate(diediedie);
1812 	setup_signal_handlers();
1813 
1814 	NBEMS_dir.clear();
1815 	{
1816 		std::string appname = pname = argv[0];
1817 		std::string appdir = argv[0];
1818 
1819 #ifdef __WIN32__
1820 		size_t p = appdir.find("FL_APPS\\");
1821 		if (p == std::string::npos) p = appdir.find("FL_APPS/");
1822 		if (p == std::string::npos) {
1823 			char dirbuf[FL_PATH_MAX + 1];
1824 			fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$USERPROFILE/");
1825 			NBEMS_dir.assign(dirbuf);
1826 		} else
1827 			NBEMS_dir.assign(appdir.substr(0, p + 8));
1828 		NBEMS_dir.append("NBEMS.files/");
1829 #else
1830 		char dirbuf[FL_PATH_MAX + 1];
1831 		fl_filename_absolute(dirbuf, sizeof(dirbuf), argv[0]);
1832 		appdir.assign(dirbuf);
1833 		size_t p = appdir.rfind("flarq");
1834 		if (p != std::string::npos)
1835 			appdir.erase(p);
1836 		p = appdir.find("FL_APPS/");
1837 		if (p != std::string::npos)
1838 			NBEMS_dir.assign(appdir.substr(0, p + 8));
1839 		else {
1840 			fl_filename_expand(dirbuf, FL_PATH_MAX, "$HOME/");
1841 			NBEMS_dir = dirbuf;
1842 		}
1843 
1844 		DIR *isdir = 0;
1845 		std::string test_dir;
1846 		test_dir.assign(NBEMS_dir).append("NBEMS.files/");
1847 		isdir = opendir(test_dir.c_str());
1848 		if (isdir) {
1849 			NBEMS_dir = test_dir;
1850 			closedir(isdir);
1851 		} else {
1852 			NBEMS_dir.append(".nbems/");
1853 		}
1854 #endif
1855 		HomeDir = NBEMS_dir;
1856 	}
1857 
1858 	checkdirectories();
1859 
1860 	Logfile = ARQ_dir;
1861 	Logfile.append("/").append("arqlog.txt");
1862 
1863 	set_platform_ui();
1864 
1865 	generate_option_help();
1866 	generate_version_text();
1867 
1868 	int arg_idx;
1869 	if (Fl::args(argc, argv, arg_idx, parse_args) != argc) {
1870 		cerr << PACKAGE_NAME << ": bad option `" << argv[arg_idx]
1871 		     << "'\nTry `" << PACKAGE_NAME
1872 		     << " --help' for more information.\n";
1873 		exit(EXIT_FAILURE);
1874 	}
1875 
1876 	createAsciiChars(); // allowable ASCII text chars for ".txt" type of files
1877 
1878 	FSEL::create();
1879 	arqwin = arq_dialog();
1880 	arqwin->callback(cb_arqwin);
1881 	arqwin->xclass(PACKAGE_TARNAME);
1882 
1883 	// FL_NORMAL_SIZE may have changed; update the menu items
1884 	for (int i = 0; i < menu_mnu->size() - 1; i++)
1885 		if (menu_mnu[i].text)
1886 			menu_mnu[i].labelsize(FL_NORMAL_SIZE);
1887 
1888 
1889 	txtbuffRX = txtRX->buffer();
1890 	txtRX->wrap_mode(1,80);
1891 
1892 	txtbuffARQ = txtARQ->buffer();
1893 	txtARQ->wrap_mode(1,80);
1894 
1895 	digi_arq = new arq();
1896 
1897 	try {
1898 		tcpip = new Socket(Address(arq_address.c_str(), arq_port.c_str()));
1899 		tcpip->set_timeout(0.01);
1900 		tcpip->connect();
1901 	}
1902 	catch (const SocketException& e) {
1903 		std::string errmsg;
1904 		errmsg.append("Could not connect to modem program.\nPlease start ");
1905 		if (ioMPSK)
1906 			errmsg.append("MultiPSK");
1907 		else
1908 			errmsg.append("fldigi");
1909 		errmsg.append(" before flarq.");
1910 		fl_alert2("%s", errmsg.c_str());
1911 		exit(EXIT_FAILURE);
1912 	}
1913 
1914 	if (ioMPSK)
1915 		Fl::add_timeout(0.1, MPSK_Socket_rcv_loop);
1916 	else
1917 		Fl::add_timeout(0.1, Socket_rcv_loop);
1918 
1919 // the following sequence of assigning callback functions is mandatory
1920 // for the arq class to function
1921 	if (ioMPSK)
1922 		digi_arq->setSendFunc (MPSK_client_transmit);
1923 	else
1924 		digi_arq->setSendFunc (client_transmit);
1925 	digi_arq->setGetCFunc (client_receive);
1926 	digi_arq->setAbortedTransfer(abortedTransfer);
1927 	digi_arq->setDisconnected(restart);
1928 	digi_arq->setrxUrCall (rxBeaconCallsign);
1929 
1930 	digi_arq->setPrintRX (payloadText);
1931 	digi_arq->setPrintTX (TXecho);
1932 	digi_arq->setPrintTALK (TALKprint);
1933 	digi_arq->setPrintSTATUS (STATUSprint);
1934 
1935 	if (SHOWDEBUG) {
1936 		digi_arq->setPrintRX_DEBUG (DEBUGrxprint);
1937 		digi_arq->setPrintTX_DEBUG (DEBUGtxprint);
1938 	}
1939 
1940 	digi_arq->start_arq();
1941 
1942 	readConfig();
1943 
1944 	std::string title = "flarq ";
1945 	title.append(VERSION);
1946 	title.append(" - ");
1947 	title.append(MyCall);
1948 	arqwin->label(title.c_str());
1949 
1950 	arqwin->resize(mainX, mainY, mainW, mainH);
1951 
1952 	txtBeaconing->value("Beacon Off");
1953 
1954 	Fl::add_timeout(0.1, mainloop);
1955 
1956 #ifdef __WOE32__
1957 #  ifndef IDI_ICON
1958 #    define IDI_ICON 101
1959 #  endif
1960 	arqwin->icon((char*)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
1961 #elif !defined(__APPLE__) && USE_X
1962 	make_pixmap(&flarq_icon_pixmap, flarq_icon, argc, argv);
1963 	arqwin->icon((char *)flarq_icon_pixmap);
1964 #endif
1965 
1966 	start_xml_server(FLARQ_XML_PORT);
1967 
1968 	arqwin->show(argc, argv);
1969 	return Fl::run();
1970 }
1971 
checkdirectories(void)1972 static void checkdirectories(void)
1973 {
1974 	struct DIRS {
1975 		std::string& dir;
1976 		const char* suffix;
1977 		void (*new_dir_func)(void);
1978 	};
1979 
1980 	DIRS NBEMS_dirs[] = {
1981 		{ NBEMS_dir,         0, 0 },
1982 		{ ARQ_dir,           "ARQ", 0 },
1983 		{ ARQ_files_dir,     "ARQ/files", 0 },
1984 		{ ARQ_recv_dir,      "ARQ/recv", 0 },
1985 		{ ARQ_send_dir,      "ARQ/send", 0 },
1986 		{ ARQ_mail_dir,      "ARQ/mail", 0 },
1987 		{ ARQ_mail_in_dir,   "ARQ/mail/in", 0 },
1988 		{ ARQ_mail_out_dir,  "ARQ/mail/out", 0 },
1989 		{ ARQ_mail_sent_dir, "ARQ/mail/sent", 0 },
1990 		{ WRAP_dir,          "WRAP", 0 },
1991 		{ WRAP_recv_dir,     "WRAP/recv", 0 },
1992 		{ WRAP_send_dir,     "WRAP/send", 0 },
1993 		{ WRAP_auto_dir,     "WRAP/auto", 0 },
1994 		{ ICS_dir,           "ICS", 0 },
1995 		{ ICS_msg_dir,       "ICS/messages", 0 },
1996 		{ ICS_tmp_dir,       "ICS/templates", 0 },
1997 	};
1998 
1999 	int r;
2000 
2001 	for (size_t i = 0; i < sizeof(NBEMS_dirs)/sizeof(*NBEMS_dirs); i++) {
2002 		if (NBEMS_dirs[i].suffix)
2003 			NBEMS_dirs[i].dir.assign(NBEMS_dir).append(NBEMS_dirs[i].suffix).append("/");
2004 
2005 		if ((r = mkdir(NBEMS_dirs[i].dir.c_str(), 0777)) == -1 && errno != EEXIST) {
2006 			cerr << _("Could not make directory") << ' ' << NBEMS_dirs[i].dir
2007 			     << ": " << strerror(errno) << '\n';
2008 			exit(EXIT_FAILURE);
2009 		}
2010 		else if (r == 0 && NBEMS_dirs[i].new_dir_func)
2011 			NBEMS_dirs[i].new_dir_func();
2012 	}
2013 }
2014 
2015 // This function may be called by the QRZ thread
cb_mnuVisitURL(Fl_Widget *,void * arg)2016 void cb_mnuVisitURL(Fl_Widget*, void* arg)
2017 {
2018 	const char* url = reinterpret_cast<const char *>(arg);
2019 #ifndef __WOE32__
2020 	const char* browsers[] = {
2021 #  ifdef __APPLE__
2022 		getenv("FLDIGI_BROWSER"), // valid for any OS - set by user
2023 		"open"                    // OS X
2024 #  else
2025 		"fl-xdg-open",            // Puppy Linux
2026 		"xdg-open",               // other Unix-Linux distros
2027 		getenv("FLDIGI_BROWSER"), // force use of spec'd browser
2028 		getenv("BROWSER"),        // most Linux distributions
2029 		"sensible-browser",
2030 		"firefox",
2031 		"mozilla"                 // must be something out there!
2032 #  endif
2033 	};
2034 	switch (fork()) {
2035 	case 0:
2036 #  ifndef NDEBUG
2037 		unsetenv("MALLOC_CHECK_");
2038 		unsetenv("MALLOC_PERTURB_");
2039 #  endif
2040 		for (size_t i = 0; i < sizeof(browsers)/sizeof(browsers[0]); i++)
2041 			if (browsers[i])
2042 				execlp(browsers[i], browsers[i], url, (char*)0);
2043 		exit(EXIT_FAILURE);
2044 	case -1:
2045 		fl_alert2(_("Could not run a web browser:\n%s\n\n"
2046 			 "Open this URL manually:\n%s"),
2047 			 strerror(errno), url);
2048 	}
2049 #else
2050 	// gurgle... gurgle... HOWL
2051 	// "The return value is cast as an HINSTANCE for backward
2052 	// compatibility with 16-bit Windows applications. It is
2053 	// not a true HINSTANCE, however. The only thing that can
2054 	// be done with the returned HINSTANCE is to cast it to an
2055 	// int and compare it with the value 32 or one of the error
2056 	// codes below." (Error codes omitted to preserve sanity).
2057 	if ((INT_PTR)ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL) <= 32)
2058 		fl_alert2(_("Could not open url:\n%s\n"), url);
2059 #endif
2060 }
2061 
open_nbems_file_folder()2062 void open_nbems_file_folder()
2063 {
2064 	cb_mnuVisitURL(0, (void*)NBEMS_dir.c_str());
2065 }
2066