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