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