1 // ----------------------------------------------------------------------------
2 // rx_extract.cxx extract delineated data stream to file
3 //
4 // Copyright 2009 W1HKJ, Dave Freese
5 //
6 // This file is part of fldigi.
7 //
8 // Fldigi is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
12 //
13 // Fldigi is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
20 // ----------------------------------------------------------------------------
21
22 #include <config.h>
23
24 #include <unistd.h>
25 #include <iostream>
26 #include <fstream>
27 #include <string>
28
29 #include <FL/filename.H>
30 #include "fileselect.h"
31
32 #include "gettext.h"
33 #include "rx_extract.h"
34 #include "main.h"
35 #include "status.h"
36 #include "fl_digi.h"
37 #include "configuration.h"
38 #include "confdialog.h"
39 #include "debug.h"
40 #include "icons.h"
41 #include "qrunner.h"
42 #include "timeops.h"
43 #include "xmlrpc.h"
44
45 #include "audio_alert.h"
46
47 using namespace std;
48
49 static const char *wrap_beg = "[WRAP:beg]";
50 static const char *wrap_end = "[WRAP:end]";
51 static const char *flmsg = "<flmsg>";
52
53 static const char flamp_beg[] = ">FLAMP";
54 static const char flamp_end[] = ":EOT}";
55
56 #ifdef __WIN32__
57 const char *txtWrapInfo = _("\
58 Detect the occurance of [WRAP:beg] and [WRAP:end]\n\
59 Save tags and all enclosed text to date-time stamped file, ie:\n\
60 NBEMS.files\\WRAP\\recv\\extract-20090127-092515.wrap");
61 #else
62 const char *txtWrapInfo = _("\
63 Detect the occurance of [WRAP:beg] and [WRAP:end]\n\
64 Save tags and all enclosed text to date-time stamped file, ie:\n\
65 ~/.nbems/WRAP/recv/extract-20090127-092515.wrap");
66 #endif
67
68 #define bufsize 64
69 string rx_extract_buff;
70 string rx_buff;
71 string rx_extract_msg;
72
73 bool extract_wrap = false;
74 bool extract_flamp = false;
75 bool extract_arq = false;
76
77 bool bInit = false;
78
79 char dttm[64];
80
rx_extract_reset()81 void rx_extract_reset()
82 {
83 rx_buff.clear();
84 rx_extract_buff.assign(' ', bufsize);
85 extract_wrap = false;
86 extract_flamp = false;
87 extract_arq = false;
88 put_status("");
89 }
90
rx_extract_timer(void *)91 void rx_extract_timer(void *)
92 {
93 rx_extract_msg = "Extract timed out";
94 put_status(rx_extract_msg.c_str(), 20, STATUS_CLEAR);
95 rx_extract_reset();
96
97 if (trx_state != STATE_RX) return;
98 if (audio_alert && progdefaults.ENABLE_RX_EXTRACT_TIMED_OUT)
99 audio_alert->alert(progdefaults.RX_EXTRACT_TIMED_OUT);
100
101 }
102
rx_add_timer()103 void rx_add_timer()
104 {
105 Fl::add_timeout(progdefaults.extract_timeout, rx_extract_timer, NULL);
106 }
107
rx_remove_timer()108 void rx_remove_timer()
109 {
110 Fl::remove_timeout(rx_extract_timer);
111 }
112
invoke_flmsg()113 void invoke_flmsg()
114 {
115 string cmd = progdefaults.flmsg_pathname;
116
117 REQ(rx_remove_timer);
118 struct tm tim;
119 time_t t;
120 time(&t);
121 gmtime_r(&t, &tim);
122 strftime(dttm, sizeof(dttm), "%Y%m%d-%H%M%S", &tim);
123
124 string outfilename = FLMSG_WRAP_recv_dir;
125 outfilename.append("extract-");
126 outfilename.append(dttm);
127 outfilename.append(".wrap");
128 ofstream extractstream(outfilename.c_str(), ios::binary);
129 if (extractstream) {
130 extractstream << rx_buff;
131 extractstream.close();
132 }
133 rx_extract_msg = "File saved in ";
134 rx_extract_msg.append(FLMSG_WRAP_recv_dir);
135 put_status(rx_extract_msg.c_str(), 20, STATUS_CLEAR);
136
137 if (trx_state != STATE_RX) return;
138 if (audio_alert && progdefaults.ENABLE_RX_EXTRACT_MSG_RCVD)
139 audio_alert->alert(progdefaults.RX_EXTRACT_MSG_RCVD);
140
141 if (flmsg_is_online && progdefaults.flmsg_transfer_direct) {
142 guard_lock autolock(server_mutex);
143 flmsg_data.append(rx_buff);
144 return;
145 }
146
147 if (progdefaults.open_nbems_folder)
148 open_recv_folder(FLMSG_WRAP_recv_dir.c_str());
149
150 if ((progdefaults.open_flmsg || progdefaults.open_flmsg_print) &&
151 (rx_buff.find(flmsg) != string::npos) &&
152 !progdefaults.flmsg_pathname.empty()) {
153
154 #ifdef __MINGW32__
155 cmd.append(" -title ").append(dttm);
156 cmd.append(" --flmsg-dir ").append("\"").append(FLMSG_dir).append("\"");
157
158 if (progdefaults.open_flmsg_print && progdefaults.open_flmsg)
159 cmd.append(" --b");
160 else if (progdefaults.open_flmsg_print)
161 cmd.append(" --p");
162 cmd.append(" \"").append(outfilename).append("\"");
163 char *cmdstr = strdup(cmd.c_str());
164 STARTUPINFO si;
165 PROCESS_INFORMATION pi;
166 memset(&si, 0, sizeof(si));
167 si.cb = sizeof(si);
168 memset(&pi, 0, sizeof(pi));
169 if (!CreateProcess( NULL, cmdstr,
170 NULL, NULL, FALSE,
171 CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
172 LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
173 CloseHandle(pi.hProcess);
174 CloseHandle(pi.hThread);
175 free (cmdstr);
176 #else
177 string params = "";
178 static string ap[10];// = cmd;//"";
179 string param = "";
180
181 size_t p = cmd.find(" -");
182 if (p != string::npos) {
183 param.assign(cmd.substr(p));
184 cmd = cmd.substr(0,p);
185 }
186 for (int i = 0; i < 10; i++) ap[i].clear();
187
188 int n = 0;
189 ap[n++] = "-title"; ap[n++] = dttm;
190 ap[n++] = "--flmsg-dir"; ap[n++] = FLMSG_dir;
191 if (progdefaults.open_flmsg_print && progdefaults.open_flmsg)
192 ap[n++] = " --b";//params = " --b";
193 else if (progdefaults.open_flmsg_print)
194 ap[n++] = " --p";//params = " --p";
195 ap[n++] = outfilename;
196
197 switch (fork()) {
198 case 0:
199 # ifndef NDEBUG
200 unsetenv("MALLOC_CHECK_");
201 unsetenv("MALLOC_PERTURB_");
202 # endif
203 switch (n) {
204 case 1:
205 execlp(
206 (char*)cmd.c_str(), (char*)cmd.c_str(),
207 (char*)ap[0].c_str(),
208 (char*)0);
209 break;
210 case 2:
211 execlp(
212 (char*)cmd.c_str(), (char*)cmd.c_str(),
213 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
214 (char*)0);
215 break;
216 case 3:
217 execlp(
218 (char*)cmd.c_str(), (char*)cmd.c_str(),
219 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
220 (char*)ap[2].c_str(),
221 (char*)0);
222 break;
223 case 4:
224 execlp(
225 (char*)cmd.c_str(), (char*)cmd.c_str(),
226 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
227 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
228 (char*)0);
229 break;
230 case 5:
231 execlp(
232 (char*)cmd.c_str(), (char*)cmd.c_str(),
233 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
234 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
235 (char*)ap[4].c_str(),
236 (char*)0);
237 break;
238 case 6:
239 execlp(
240 (char*)cmd.c_str(), (char*)cmd.c_str(),
241 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
242 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
243 (char*)ap[4].c_str(), (char*)ap[5].c_str(),
244 (char*)0);
245 break;
246 case 7:
247 execlp(
248 (char*)cmd.c_str(), (char*)cmd.c_str(),
249 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
250 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
251 (char*)ap[4].c_str(), (char*)ap[5].c_str(),
252 (char*)ap[6].c_str(),
253 (char*)0);
254 break;
255 case 8:
256 execlp(
257 (char*)cmd.c_str(), (char*)cmd.c_str(),
258 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
259 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
260 (char*)ap[4].c_str(), (char*)ap[5].c_str(),
261 (char*)ap[6].c_str(), (char*)ap[7].c_str(),
262 (char*)0);
263 break;
264 case 9:
265 execlp(
266 (char*)cmd.c_str(), (char*)cmd.c_str(),
267 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
268 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
269 (char*)ap[4].c_str(), (char*)ap[5].c_str(),
270 (char*)ap[6].c_str(), (char*)ap[7].c_str(),
271 (char*)ap[8].c_str(),
272 (char*)0);
273 break;
274 case 10:
275 execlp(
276 (char*)cmd.c_str(), (char*)cmd.c_str(),
277 (char*)ap[0].c_str(), (char*)ap[1].c_str(),
278 (char*)ap[2].c_str(), (char*)ap[3].c_str(),
279 (char*)ap[4].c_str(), (char*)ap[5].c_str(),
280 (char*)ap[6].c_str(), (char*)ap[7].c_str(),
281 (char*)ap[8].c_str(), (char*)ap[9].c_str(),
282 (char*)0);
283 break;
284 default : ;
285 }
286 exit(EXIT_FAILURE);
287 case -1:
288 fl_alert2(_("Could not start flmsg"));
289 }
290 #endif
291 }
292 }
293
start_flmsg()294 void start_flmsg()
295 {
296 string cmd = progdefaults.flmsg_pathname;
297
298 #ifdef __MINGW32__
299 char *cmdstr = strdup(cmd.c_str());
300 STARTUPINFO si;
301 PROCESS_INFORMATION pi;
302 memset(&si, 0, sizeof(si));
303 si.cb = sizeof(si);
304 memset(&pi, 0, sizeof(pi));
305 if (!CreateProcess( NULL, cmdstr,
306 NULL, NULL, FALSE,
307 CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
308 LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
309 CloseHandle(pi.hProcess);
310 CloseHandle(pi.hThread);
311 free (cmdstr);
312 #else
313 switch (fork()) {
314 case 0:
315 execlp((char*)cmd.c_str(), (char*)cmd.c_str(), (char*)0, (char*)0);
316 exit(EXIT_FAILURE);
317 case -1:
318 fl_alert2(_("Could not start flmsg"));
319 default: ;
320 }
321 #endif
322 }
323
rx_extract_add(int c)324 void rx_extract_add(int c)
325 {
326 if (!c) return;
327 check_nbems_dirs();
328
329 if (!bInit) {
330 rx_extract_reset();
331 bInit = true;
332 }
333 char ch = (char)c;
334
335 rx_extract_buff = rx_extract_buff.substr(1);
336 rx_extract_buff += ch;
337
338 // rx_extract_buff must contain in order:
339 // ~1c - arq_soh + connect request
340 // ; - arq_dle character
341 // ~4 - arq_eoh
342 // before auto starting flmsg
343
344 size_t p1 = rx_extract_buff.find("~1c");
345 size_t p2 = rx_extract_buff.find(";");
346 size_t p3 = rx_extract_buff.find("~4");
347
348 if ( (p1 != string::npos) && (p2 != string::npos) && (p3 != string::npos) &&
349 (p1 < p2) && (p2 < p3)) {
350 if (!flmsg_is_online) start_flmsg();
351 rx_extract_buff.assign(' ', bufsize);
352 return;
353 }
354
355 if (!extract_arq &&
356 (rx_extract_buff.find("ARQ:FILE::FLMSG_XFR") != string::npos) ) {
357 extract_arq = true;
358 REQ(rx_remove_timer);
359 REQ(rx_add_timer);
360 rx_extract_buff.assign(' ', bufsize);
361 rx_extract_msg = "Extracting ARQ msg";
362 put_status(rx_extract_msg.c_str());
363 return;
364 } else if (extract_arq) {
365 REQ(rx_remove_timer);
366 REQ(rx_add_timer);
367 if (rx_extract_buff.find("ARQ::ETX"))
368 rx_extract_reset();
369 return;
370 } else if (!extract_flamp &&
371 (rx_extract_buff.find(flamp_beg) != string::npos) ) {
372 extract_flamp = true;
373 rx_extract_buff.assign(' ', bufsize);
374 rx_extract_msg = "Extracting FLAMP";
375 put_status(rx_extract_msg.c_str());
376 return;
377 } else if (extract_flamp) {
378 REQ(rx_remove_timer);
379 REQ(rx_add_timer);
380 if (rx_extract_buff.find(flamp_end) != string::npos)
381 rx_extract_reset();
382 return;
383 } else if (!extract_wrap &&
384 (rx_extract_buff.find(wrap_beg) != string::npos) ) {
385 rx_buff.assign(wrap_beg);
386 rx_extract_msg = "Extracting WRAP/FLMSG";
387 put_status(rx_extract_msg.c_str());
388 extract_wrap = true;
389 REQ(rx_remove_timer);
390 REQ(rx_add_timer);
391 return;
392 } else if (extract_wrap) {
393 rx_buff += ch;
394 REQ(rx_remove_timer);
395 REQ(rx_add_timer);
396 if (rx_extract_buff.find(wrap_end) != string::npos) {
397 invoke_flmsg();
398 rx_extract_reset();
399 }
400 }
401 }
402
select_flmsg_pathname()403 void select_flmsg_pathname()
404 {
405 txt_flmsg_pathname->value(progdefaults.flmsg_pathname.c_str());
406 txt_flmsg_pathname->redraw();
407
408 #ifdef __APPLE__
409 open_recv_folder("/Applications/");
410 return;
411 #else
412 string deffilename = progdefaults.flmsg_pathname;
413 # ifdef __MINGW32__
414 if (deffilename.empty())
415 deffilename = "C:\\Program Files\\";
416 const char *p = FSEL::select(_("Locate flmsg executable"), _("flmsg.exe\t*.exe"), deffilename.c_str());
417 # else
418 if (deffilename.empty())
419 deffilename = "/usr/local/bin/";
420 const char *p = FSEL::select(_("Locate flmsg executable"), _("flmsg\t*"), deffilename.c_str());
421 # endif
422 if (!p) return;
423 if (!*p) return;
424
425 progdefaults.flmsg_pathname = p;
426 progdefaults.changed = true;
427 txt_flmsg_pathname->value(p);
428
429 #endif
430 }
431
432 // this only works on Linux and Unix
433 // not Windoze or
434 // OS X to find binaries in the /Applications/ directory structure
435
find_pathto_exectable(string & binpath,string executable)436 bool find_pathto_exectable(string &binpath, string executable)
437 {
438 size_t endindex = 0;
439
440 binpath.clear();
441
442 // Get the PATH environment variable as pointer to string
443 // The strings in the environment list are of the form name=value.
444 // As typically implemented, getenv() returns a pointer to a string within
445 // the environment list. The caller must take care not to modify this string,
446 // since that would change the environment of the process.
447 //
448 // The implementation of getenv() is not required to be reentrant. The string
449 // pointed to by the return value of getenv() may be statically allocated, and
450 // can be modified by a subsequent call to getenv(), putenv(3), setenv(3), or
451 // unsetenv(3).
452
453 char *environment = getenv("PATH");
454
455 if (environment == NULL) return false;
456
457 string env = environment;
458 string testpath = "";
459
460 char endchar = ':';
461
462 // Parse single PATH string into directories
463 while (!env.empty()) {
464 endindex = env.find(endchar);
465 testpath = env.substr(0, endindex);
466
467 testpath.append("/"); // insert linux, unix, osx OS-correct delimiter
468 testpath.append(executable); // append executable name
469
470 // Most portable way to check if a file exists: Try to open it.
471 FILE *checkexists = NULL;
472 checkexists = fl_fopen( testpath.c_str(), "r" ); // try to open file readonly
473 if (checkexists) { // if the file successfully opened, it exists.
474 fclose(checkexists);
475 binpath = testpath;
476 return true;
477 }
478 if (endindex == string::npos)
479 env.clear();
480 else
481 env.erase(0, endindex + 1);
482 }
483 return false;
484 }
485
select_binary_pathname(string deffilename)486 string select_binary_pathname(string deffilename)
487 {
488 #ifdef __APPLE__
489 open_recv_folder("/Applications/");
490 return "";
491 #else
492 # ifdef __MINGW32__
493 deffilename = "C:\\Program Files\\";
494 const char *p = FSEL::select(_("Locate executable"), _("*.exe"), deffilename.c_str());
495 # else
496 deffilename = "/usr/local/bin/";
497 const char *p = FSEL::select(_("Locate binary"), _("*"), deffilename.c_str());
498 # endif
499 string executable = "";
500 if (p && *p) executable = p;
501 // do not allow recursion !!
502 if (executable.find("fldigi") != string::npos) return "";
503 return executable;
504 #endif
505 }
506
507