1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 //              David Freese, W1HKJ
4 //
5 // This file is part of flmsg
6 //
7 // flrig is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // flrig is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20 
21 #include <stdlib.h>
22 #include <iostream>
23 #include <fstream>
24 #include <cstring>
25 #include <ctime>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <errno.h>
30 
31 #include <FL/Fl.H>
32 #include <FL/Enumerations.H>
33 #include <FL/Fl_Window.H>
34 #include <FL/Fl_Button.H>
35 #include <FL/Fl_Group.H>
36 #include <FL/Fl_Sys_Menu_Bar.H>
37 #include <FL/x.H>
38 #include <FL/Fl_Help_Dialog.H>
39 #include <FL/Fl_Menu_Item.H>
40 #include <FL/Fl_File_Icon.H>
41 
42 #include "config.h"
43 #include "flmsg_config.h"
44 
45 #include "flmsg.h"
46 #include "templates.h"
47 #include "debug.h"
48 #include "util.h"
49 #include "gettext.h"
50 #include "flmsg_dialog.h"
51 #include "flinput2.h"
52 #include "date.h"
53 #include "calendar.h"
54 #include "icons.h"
55 #include "fileselect.h"
56 #include "wrap.h"
57 #include "status.h"
58 #include "parse_xml.h"
59 
60 #ifdef WIN32
61 #  include "flmsgrc.h"
62 #  include "compat.h"
63 #  define dirent fl_dirent_no_thanks
64 #endif
65 
66 #include <FL/filename.H>
67 #include "dirent-check.h"
68 
69 #include <FL/x.H>
70 #include <FL/Fl_Pixmap.H>
71 #include <FL/Fl_Image.H>
72 
73 using namespace std;
74 
75 // ICS213 fields
76 // compatibility fields required to read older data files
77 
78 string a_213_inc	= "<inc:";
79 string a_213_to		= "<to:";
80 string a_213_fm		= "<fm:";
81 string a_213_p1		= "<p1:";
82 string a_213_p2		= "<p2:";
83 string a_213_subj	= "<sb:";
84 string a_213_d1		= "<d1:";
85 string a_213_t1		= "<t1:";
86 string a_213_msg	= "<mg:";
87 string a_213_s1		= "<s1:";
88 string a_213_p3		= "<p3:";
89 string a_213_reply	= "<rp:";
90 string a_213_d2		= "<d2:";
91 string a_213_t2		= "<t2:";
92 string a_213_s2		= "<s2:";
93 string a_213_p4		= "<p4:";
94 
95 FIELD afields[] = {
96 { a_213_inc,	"", (void **)&txt_213_inc,		't' },
97 { a_213_to,		"", (void **)&txt_213_to,		't' },
98 { a_213_p1,		"", (void **)&txt_213_p1,		't' },
99 { a_213_fm,		"", (void **)&txt_213_fm,		't' },
100 { a_213_p2,		"", (void **)&txt_213_p2,		't' },
101 { a_213_d1,		"", (void **)&txt_213_d1,		'd' },
102 { a_213_t1,		"", (void **)&txt_213_t1,		't' },
103 { a_213_subj,	"", (void **)&txt_213_subj,		't' },
104 { a_213_s1,		"", (void **)&txt_213_s1,		't' },
105 { a_213_p3,		"", (void **)&txt_213_p3,		't' },
106 { a_213_s2,		"", (void **)&txt_213_s2,		't' },
107 { a_213_p4,		"", (void **)&txt_213_p4,		't' },
108 { a_213_d2,		"", (void **)&txt_213_d2,		'd' },
109 { a_213_t2,		"", (void **)&txt_213_t2,		't' },
110 { a_213_msg,	"", (void **)&txt_213_msg,		'e' },
111 { a_213_reply,	"", (void **)&txt_213_reply,	'e' } };
112 
113 string _213_inc = ":inc:";
114 string _213_to = ":to:";
115 string _213_fm = ":fm:";
116 string _213_p1 = ":p1:";
117 string _213_p2 = ":p2:";
118 string _213_subj = ":sb:";
119 string _213_d1 = ":d1:";
120 string _213_t1 = ":t1:";
121 string _213_msg = ":mg:";
122 string _213_s1 = ":s1:";
123 string _213_p3 = ":p3:";
124 string _213_reply = ":rp:";
125 string _213_d2 = ":d2:";
126 string _213_t2 = ":t2:";
127 string _213_s2 = ":s2:";
128 string _213_p4 = ":p4:";
129 
130 string ICS_213_msg = "";
131 string ICS_213_reply = "";
132 string buffer;
133 
134 FIELD fields[] = {
135 { _213_inc,		"", (void **)&txt_213_inc,		't' },
136 { _213_to,		"", (void **)&txt_213_to,		't' },
137 { _213_p1,		"", (void **)&txt_213_p1,		't' },
138 { _213_fm,		"", (void **)&txt_213_fm,		't' },
139 { _213_p2,		"", (void **)&txt_213_p2,		't' },
140 { _213_d1,		"", (void **)&txt_213_d1,		'd' },
141 { _213_t1,		"", (void **)&txt_213_t1,		't' },
142 { _213_subj,	"", (void **)&txt_213_subj,		't' },
143 { _213_s1,		"", (void **)&txt_213_s1,		't' },
144 { _213_p3,		"", (void **)&txt_213_p3,		't' },
145 { _213_s2,		"", (void **)&txt_213_s2,		't' },
146 { _213_p4,		"", (void **)&txt_213_p4,		't' },
147 { _213_d2,		"", (void **)&txt_213_d2,		'd' },
148 { _213_t2,		"", (void **)&txt_213_t2,		't' },
149 { _213_msg,		"", (void **)&txt_213_msg,		'e' },
150 { _213_reply,	"", (void **)&txt_213_reply,	'e' } };
151 
152 bool using_213Template = false;
153 
154 string base_213_filename = "";
155 string def_213_filename = "";
156 string def_213_TemplateName = "";
157 
158 int numfields = sizeof(fields) / sizeof(FIELD);
159 
cb_SetDate1()160 void cb_SetDate1()
161 {
162 	txt_213_d1->value(szDate(progStatus.dtformat));
163 }
164 
cb_SetDate2()165 void cb_SetDate2()
166 {
167 	txt_213_d2->value(szDate(progStatus.dtformat));
168 }
169 
cb_SetTime1()170 void cb_SetTime1()
171 {
172 	txt_213_t1->value(szTime(progStatus.UTC));
173 }
174 
cb_SetTime2()175 void cb_SetTime2()
176 {
177 	txt_213_t2->value(szTime(progStatus.UTC));
178 }
179 
clear_fields()180 void clear_fields()
181 {
182 	for (int i = 0; i < numfields; i++)
183 		fields[i].f_data.clear();
184 }
185 
check_fields()186 bool check_fields()
187 {
188 	for (int i = 0; i < numfields; i++) {
189 		if (fields[i].w_type == 'd') {
190 			if (fields[i].f_data != ((Fl_DateInput *)(*fields[i].w))->value())
191 				return true;
192 		} else if (fields[i].w_type == 't') {
193 			if (fields[i].f_data != ((Fl_Input2 *)(*fields[i].w))->value())
194 				return true;
195 		} else if (fields[i].w_type == 'e') {
196 			if (fields[i].f_data!= ((FTextEdit *)(*fields[i].w))->buffer()->text())
197 				return true;
198 		}
199 	}
200 	return false;
201 }
202 
update_fields()203 void update_fields()
204 {
205 	bool changed = false;
206 	for (int i = 0; i < numfields; i++) {
207 		if (fields[i].w_type == 'd') {
208 			if (fields[i].f_data != ((Fl_DateInput *)(*fields[i].w))->value()) {
209 				changed = true;;
210 				fields[i].f_data = ((Fl_DateInput *)(*fields[i].w))->value();
211 			}
212 		} else if (fields[i].w_type == 't') {
213 			if (fields[i].f_data != ((Fl_Input2 *)(*fields[i].w))->value()) {
214 				changed = true;;
215 				fields[i].f_data = ((Fl_Input2 *)(*fields[i].w))->value();
216 			}
217 		} else if (fields[i].w_type == 'e') {
218 			if (fields[i].f_data!= ((FTextEdit *)(*fields[i].w))->buffer()->text()) {
219 				changed = true;;
220 				fields[i].f_data = ((FTextEdit *)(*fields[i].w))->buffer()->text();
221 			}
222 		}
223 	}
224 	if (changed) update_header(CHANGED);
225 }
226 
clear_213_form()227 void clear_213_form()
228 {
229 	clear_fields();
230 	txt_213_inc->value("");
231 	txt_213_to->value("");
232 	txt_213_p1->value("");
233 	txt_213_fm->value("");
234 	txt_213_p2->value("");
235 	txt_213_subj->value("");
236 	txt_213_d1->value("");
237 	txt_213_t1->value("");
238 	txt_213_msg->clear();
239 	txt_213_s1->value("");
240 	txt_213_p3->value("");
241 	txt_213_reply->clear();
242 	txt_213_s2->value("");
243 	txt_213_d2->value("");
244 	txt_213_t2->value("");
245 	txt_213_p4->value("");
246 	update_fields();
247 }
248 
update_form213()249 void update_form213()
250 {
251 	for (int i = 0; i < numfields; i++) {
252 		if (fields[i].w_type == 'd')
253 			((Fl_DateInput *)(*fields[i].w))->value(fields[i].f_data.c_str());
254 		else if (fields[i].w_type == 't')
255 			((Fl_Input2 *)(*fields[i].w))->value(fields[i].f_data.c_str());
256 		else if (fields[i].w_type == 'e') {
257 			((FTextEdit *)(*fields[i].w))->clear();
258 			((FTextEdit *)(*fields[i].w))->add(fields[i].f_data.c_str());
259 		}
260 	}
261 }
262 
make_buffer(bool compress=false)263 void make_buffer(bool compress = false)
264 {
265 	string mbuff;
266 	mbuff.clear();
267 	for (int i = 0; i < numfields; i++)
268 		mbuff.append( lineout( fields[i].f_type, fields[i].f_data ) );
269 	if (compress) compress_maybe(mbuff);
270 	buffer.append(mbuff);
271 }
272 
read_213_buffer(string data)273 void read_213_buffer(string data)
274 {
275 	bool data_ok = false;
276 	clear_fields();
277 	read_header(data);
278 	for (int i = 0; i < numfields; i++) {
279 		fields[i].f_data = findstr(data, fields[i].f_type);
280 		if (!fields[i].f_data.empty()) data_ok = true;
281 	}
282 	if (!data_ok)
283 		for (int i = 0; i < numfields; i++)
284 			fields[i].f_data = findstr(data, afields[i].f_type);
285 
286 	update_form213();
287 }
288 
cb_213_new()289 void cb_213_new()
290 {
291 	if (check_fields()) {
292 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 1) {
293 			update_header(CHANGED);
294 			cb_213_save();
295 		}
296 	}
297 	clear_213_form();
298 	clear_header();
299 	def_213_filename = ICS_msg_dir;
300 	def_213_filename.append("new").append(F213_EXT);
301 	using_213Template = false;
302 	show_filename(def_213_filename);
303 }
304 
cb_213_import()305 void cb_213_import()
306 {
307 	string def_213_filename = ICS_dir;
308 	def_213_filename.append("DEFAULT.XML");
309 	const char *p = FSEL::select(
310 		"Open Qforms xml file",
311 		"Qforms xml\t*.{xml,XML}",
312 		def_213_filename.c_str());
313 	if (p){
314 		clear_213_form();
315 		clear_header();
316 		qform_ics_import(p);
317 		using_213Template = false;
318 	}
319 }
320 
cb_213_export()321 void cb_213_export()
322 {
323 	string def_213_filename = ICS_dir;
324 	def_213_filename.append(base_213_filename);
325 	def_213_filename.append(".XML");
326 	const char *p = FSEL::saveas(
327 			"Open Qforms xml file",
328 			"Qforms xml\t*.{xml,XML}",
329 			def_213_filename.c_str());
330 	if (p) {
331 		const char *pext = fl_filename_ext(p);
332 		def_213_filename = p;
333 		update_fields();
334 		if (strlen(pext) == 0) def_213_filename.append(".XML");
335 		qform_ics_export(def_213_filename);
336 	}
337 }
338 
cb_213_wrap_import(string wrapfilename,string inpbuffer)339 void cb_213_wrap_import(string wrapfilename, string inpbuffer)
340 {
341 	clear_213_form();
342 	read_213_buffer(inpbuffer);
343 	def_213_filename = ICS_msg_dir;
344 	def_213_filename.append(wrapfilename);
345 	using_213Template = false;
346 	show_filename(def_213_filename);
347 }
348 
eval_213_fsize()349 int eval_213_fsize()
350 {
351 	Ccrc16 chksum;
352 	evalstr.assign("[WRAP:beg][WRAP:lf][WRAP:fn ");
353 	evalstr.append(base_213_filename).append("]");
354 	update_fields();
355 	update_header(FROM);
356 	evalstr.append(header("<ics213>"));
357 	buffer.clear();
358 	make_buffer(true);
359 	if (buffer.empty()) return 0;
360 	compress_maybe( buffer );
361 	evalstr.append( buffer );
362 	evalstr.append("[WRAP:chksum ").append(chksum.scrc16(evalstr)).append("][WRAP:end]");
363 	return evalstr.length();
364 }
365 
cb_213_wrap_export()366 void cb_213_wrap_export()
367 {
368 	if (check_fields()) {
369 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
370 			return;
371 		update_header(CHANGED);
372 	}
373 
374 	if (base_213_filename == string("new").append(F213_EXT) ||
375 		base_213_filename == string("default").append(F213_EXT) )
376 		if (!cb_213_save_as()) return;
377 
378 	string wrapfilename = WRAP_send_dir;
379 	wrapfilename.append(base_213_filename);
380 	wrapfilename.append(WRAP_EXT);
381 	const char *p = FSEL::saveas(
382 			"Save as wrap file",
383 			"Wrap file\t*.{wrap,WRAP}",
384 			wrapfilename.c_str());
385 	if (p) {
386 		string pext = fl_filename_ext(p);
387 		wrapfilename = p;
388 		update_header(FROM);
389 		update_fields();
390 		buffer.assign(header("<ics213>"));
391 		make_buffer(true);
392 		export_wrapfile(base_213_filename, wrapfilename, buffer, pext != WRAP_EXT);
393 
394 		buffer.assign(header("<ics213>"));
395 		make_buffer(false);
396 		write_213(def_213_filename);
397 	}
398 }
399 
cb_213_wrap_autosend()400 void cb_213_wrap_autosend()
401 {
402 	if (check_fields()) {
403 		if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
404 			return;
405 		update_header(CHANGED);
406 	}
407 
408 	if (base_213_filename == string("new").append(F213_EXT) ||
409 		base_213_filename == string("default").append(F213_EXT) )
410 		if (!cb_213_save_as()) return;
411 
412 	update_header(FROM);
413 	update_fields();
414 	buffer.assign(header("<ics213>"));
415 	make_buffer(true);
416 	xfr_via_socket(base_213_filename, buffer);
417 
418 	buffer.assign(header("<ics213>"));
419 	make_buffer(false);
420 	write_213(def_213_filename);
421 }
422 
cb_213_load_template()423 void cb_213_load_template()
424 {
425 	string def_213_filename = def_213_TemplateName;
426 	const char *p = FSEL::select(
427 			"Open template file",
428 			string("Template file\t*").append(T213_EXT).c_str(),
429 			def_213_filename.c_str());
430 	if (p) {
431 		clear_213_form();
432 		read_data_file(p);
433 		def_213_TemplateName = p;
434 		show_filename(def_213_TemplateName);
435 		using_213Template = true;
436 	}
437 }
438 
cb_213_save_template()439 void cb_213_save_template()
440 {
441 	if (!using_213Template) {
442 		cb_save_as_template();
443 		return;
444 	}
445 	string def_213_filename = def_213_TemplateName;
446 	const char *p = FSEL::saveas(
447 			"Save template file",
448 			string("Template file\t*").append(T213_EXT).c_str(),
449 			def_213_filename.c_str());
450 	if (p) {
451 		update_header(CHANGED);
452 		update_fields();
453 		buffer.assign(header("<ics213>"));
454 		make_buffer();
455 		write_213(p);
456 	}
457 }
458 
cb_213_save_as_template()459 void cb_213_save_as_template()
460 {
461 	string def_213_filename = def_213_TemplateName;
462 	const char *p = FSEL::saveas(
463 			"Save as template file",
464 			string("Template file\t*").append(T213_EXT).c_str(),
465 			def_213_filename.c_str());
466 	if (p) {
467 		const char *pext = fl_filename_ext(p);
468 		def_213_TemplateName = p;
469 		if (strlen(pext) == 0) def_213_TemplateName.append(T213_EXT);
470 		remove_spaces_from_filename(def_213_TemplateName);
471 		clear_header();
472 		update_header(CHANGED);
473 		update_fields();
474 		buffer.assign(header("<ics213>"));
475 		make_buffer();
476 		write_213(def_213_TemplateName);
477 		show_filename(def_213_TemplateName);
478 		using_213Template = true;
479 	}
480 }
481 
cb_213_open()482 void cb_213_open()
483 {
484 	const char *p = FSEL::select(_("Open data file"), "ICS-213\t*.{213,f2s}",
485 					def_213_filename.c_str());
486 	if (!p) return;
487 	if (strlen(p) == 0) return;
488 	clear_213_form();
489 	read_data_file(p);
490 	using_213Template = false;
491 	def_213_filename = p;
492 	show_filename(def_213_filename);
493 }
494 
write_213(string s)495 void write_213(string s)
496 {
497 	FILE *icsfile = fopen(s.c_str(), "w");
498 	if (!icsfile) return;
499 
500 	fwrite(buffer.c_str(), buffer.length(), 1, icsfile);
501 	fclose(icsfile);
502 }
503 
cb_213_save_as()504 bool cb_213_save_as()
505 {
506 	const char *p;
507 	string newfilename;
508 
509 	string name = named_file();
510 	if (!name.empty()) {
511 	name.append(F213_EXT);
512 		newfilename = ICS_msg_dir;
513 		newfilename.append(name);
514 	} else
515 		newfilename = def_213_filename;
516 
517 	p = FSEL::saveas(_("Save data file"), "ICS-213\t*.{213T,f2t}",
518 					newfilename.c_str());
519 
520 	if (!p) return false;
521 	if (strlen(p) == 0) return false;
522 
523 	if (progStatus.sernbr_fname) update_sernbr();
524 
525 	const char *pext = fl_filename_ext(p);
526 	def_213_filename = p;
527 	if (strlen(pext) == 0) def_213_filename.append(F213_EXT);
528 
529 	remove_spaces_from_filename(def_213_filename);
530 	update_header(NEW);
531 	update_fields();
532 	buffer.assign(header("<ics213>"));
533 	make_buffer();
534 	write_213(def_213_filename);
535 
536 	using_213Template = false;
537 	show_filename(def_213_filename);
538 	return true;
539 }
540 
cb_213_save()541 void cb_213_save()
542 {
543 	if (base_213_filename == std::string("new").append(F213_EXT) ||
544 		base_213_filename == std::string("default").append(F213_EXT) ||
545 		using_213Template == true) {
546 		cb_213_save_as();
547 		return;
548 	}
549 	if (check_fields()) update_header(CHANGED);
550 	update_fields();
551 	buffer.assign(header("<ics213>"));
552 	make_buffer();
553 	write_213(def_213_filename);
554 	using_213Template = false;
555 }
556 
cb_213_html()557 void cb_213_html()
558 {
559 	string fname_name = fl_filename_name(def_213_filename.c_str());
560 	size_t p = fname_name.rfind('.');
561 	if (p != string::npos) fname_name.erase(p);
562 
563 	string icsname = ICS_dir;
564 	icsname.append(fname_name);
565 	icsname.append(".html");
566 
567 	string html_text = "";
568 
569 	update_fields();
570 	string form = ics213_html_template;
571 
572 	replacestr(form, TITLE, fname_name);
573 	for (int i = 0; i < numfields; i++) {
574 		if (fields[i].w_type != 'e')
575 			replacestr( form, fields[i].f_type, fields[i].f_data );
576 		else {
577 			string ww = wordwrap(fields[i].f_data, progStatus.charcount);
578 			int lfcount = 0;
579 			size_t pos = 0;
580 			while ((pos = ww.find("\n", pos)) != string::npos){
581 				lfcount++; pos++;
582 			}
583 			if (lfcount < 10) for (int i = 0; i < (10 - lfcount); i++) ww.append("\n");
584 			html_text.assign("<pre>");
585 			html_text.append(ww);
586 			html_text.append("</pre>");
587 			replacestr( form, fields[i].f_type, html_text );
588 		}
589 	}
590 
591 	for (int i = 0; i < numfields; i++)
592 		replacestr( form, fields[i].f_type, fields[i].f_data );
593 
594 	FILE *icsfile = fopen(icsname.c_str(), "w");
595 	fprintf(icsfile,"%s", form.c_str());
596 	fclose(icsfile);
597 
598 	open_url(icsname.c_str());
599 }
600 
cb_213_textout()601 void cb_213_textout()
602 {
603 	string icsname = ICS_dir;
604 	icsname.append("ics213.txt");
605 
606 	update_fields();
607 	string form = ics213_text_template;
608 
609 	for (int i = 0; i < numfields; i++)
610 		replacestr( form, fields[i].f_type, fields[i].f_data);
611 
612 	FILE *icsfile = fopen(icsname.c_str(), "w");
613 	fprintf(icsfile,"%s", form.c_str());
614 	fclose(icsfile);
615 
616 	open_url(icsname.c_str());
617 }
618