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 <string>
26 #include <ctime>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <stdio.h>
30 #include <errno.h>
31
32 #include <FL/Fl.H>
33 #include <FL/Enumerations.H>
34 #include <FL/Fl_Window.H>
35 #include <FL/Fl_Button.H>
36 #include <FL/Fl_Group.H>
37 #include <FL/Fl_Sys_Menu_Bar.H>
38 #include <FL/x.H>
39 #include <FL/Fl_Help_Dialog.H>
40 #include <FL/Fl_Menu_Item.H>
41 #include <FL/Fl_File_Icon.H>
42 #include <FL/Fl_Input.H>
43 #include <FL/Fl_Check_Button.H>
44 #include <FL/fl_draw.H>
45 #include <FL/fl_ask.H>
46 #include <FL/Fl_Table_Row.H>
47 #include <FL/Fl_Browser.H>
48
49 #include <vector>
50
51 #include "mongoose.h"
52
53 #include "config.h"
54 #include "flmsg.h"
55 #include "templates.h"
56 #include "debug.h"
57 #include "util.h"
58 #include "gettext.h"
59 #include "flmsg_dialog.h"
60 #include "flmsg_config.h"
61 #include "flinput2.h"
62 #include "date.h"
63 #include "calendar.h"
64 #include "icons.h"
65 #include "fileselect.h"
66 #include "wrap.h"
67 #include "status.h"
68 #include "parse_xml.h"
69 #include "icons.h"
70 #include "threads.h"
71
72 #include "ext_string.h"
73
74 #ifdef WIN32
75 # include "flmsgrc.h"
76 # include "compat.h"
77 # define dirent fl_dirent_no_thanks
78 #endif
79
80 #include <FL/filename.H>
81 #include "dirent-check.h"
82
83 #include <FL/x.H>
84 #include <FL/Fl_Pixmap.H>
85 #include <FL/Fl_Image.H>
86
87 using namespace std;
88
89 extern int custom_select;
90
91 extern struct mg_server *server;
92
93 static string action_str = "\n<FORM ACTION=\"/handle_post_request\" METHOD=\"post\" \n";
94 static string submit_str = "\n<INPUT TYPE=\"submit\" VALUE=\"Submit Form\">\n</form ";
95 static string input_str = "<INPUT";
96 static string select_str = "<SELECT";
97 static string end_sel_str = "</SELECT";
98 static string textarea_str = "<TEXTAREA";
99 static string textend_str = "</TEXTAREA";
100 static string checked = "CHECKED";
101 static string checkedeq = "CHECKED=\"CHECKED\"";
102 static string selected = "SELECTED";
103
104 static string value_str = "VALUE=\"";
105 static string name_str = "NAME=\"";
106
107 static string text_type_str = "TYPE=\"TEXT\"";
108 static string radio_type_str = "TYPE=\"RADIO\"";
109 static string checkbox_type_str = "TYPE=\"CHECKBOX\"";
110 static string password_type_str = "TYPE=\"PASSWORD\"";
111 static string number_str = "TYPE=\"NUMBER\"";
112 static string date_str = "TYPE=\"DATE\"";
113 static string datetime_str = "TYPE=\"DATETIME\"";
114 static string datetime_local_str = "TYPE=\"DATETIME-LOCAL\"";
115 static string time_str = "TYPE=\"TIME\"";
116 static string week_str = "TYPE=\"WEEK\"";
117 static string month_str = "TYPE=\"MONTH\"";
118 static string tel_str = "TYPE=\"TEL\"";
119 static string email_str = "TYPE=\"EMAIL\"";
120
121 enum {
122 T_TEXT, T_RADIO, T_CHECKBOX, T_AREA, T_SELECT, T_PASSWORD,
123 T_NUMBER, T_DATE, T_DTIME, T_DTIME_LOCAL, T_TIME, T_WEEK, T_MONTH, T_TEL, T_EMAIL };
124
125 struct INPUT_TYPE {
126 int id;
127 string txt;
INPUT_TYPEINPUT_TYPE128 INPUT_TYPE(int i, string s) {
129 id = i;
130 txt = s;
131 }
132 };
133
134 static INPUT_TYPE input_types[] = {
135 INPUT_TYPE(T_TEXT, text_type_str),
136 INPUT_TYPE(T_PASSWORD, password_type_str),
137 INPUT_TYPE(T_AREA, textarea_str),
138 INPUT_TYPE(T_RADIO, radio_type_str),
139 INPUT_TYPE(T_CHECKBOX, checkbox_type_str),
140 INPUT_TYPE(T_SELECT, select_str),
141 INPUT_TYPE(T_NUMBER, number_str),
142 INPUT_TYPE(T_DATE, date_str),
143 INPUT_TYPE(T_DTIME, datetime_str),
144 INPUT_TYPE(T_DTIME_LOCAL, datetime_local_str),
145 INPUT_TYPE(T_TIME, time_str),
146 INPUT_TYPE(T_WEEK, week_str),
147 INPUT_TYPE(T_MONTH, month_str),
148 INPUT_TYPE(T_TEL, tel_str),
149 INPUT_TYPE(T_EMAIL, email_str)
150 };
151
152 struct NAME_VALUE {
153 int id;
154 string name;
155 string value;
NAME_VALUENAME_VALUE156 NAME_VALUE(int ID, string S1, string S2) {id = ID; name = S1; value = S2;}
157 };
158
159 // customform fields
160
161 string custombuffer;
162 string def_custom_filename = "";
163 string base_custom_filename = "";
164 string def_custom_TemplateName = "";
165
166 string custom_title = ":TITLE:";
167 string custom_msg = ":mg:";
168 string custom_field;
169
170 std::vector<NAME_VALUE> name_values;
171
172 string html_form;
173 string edit_txt;
174
escape(string & s)175 void escape(string &s)
176 {
177 size_t p;
178 p = 0;
179 while ((p = s.find("\r", p)) != string::npos)
180 s.erase(p,1);
181 p = 0;
182 while ((p = s.find("\n", p)) != string::npos) {
183 s.replace(p, 1, "\\n");
184 p += 2;
185 }
186 p = 0;
187 while ((p = s.find("\"", p)) != string::npos) {
188 if (p == 0 || s[p-1] != '\\') {
189 s.replace(p, 1, "\\\"");
190 p += 2;
191 } else p++;
192 }
193 }
194
unescape(string & s)195 void unescape(string &s)
196 {
197 size_t p;
198 p = 0;
199 while ((p = s.find("\\n", p)) != string::npos) {
200 s.replace(p,2,"\n");
201 p++;
202 }
203 p = 0;
204 while ((p = s.find("\\\"", p)) != string::npos) {
205 s.replace(p,2,"\"");
206 p++;
207 }
208 }
209
convert_case(string & s)210 int convert_case(string &s)
211 {
212 std::string s1, s3;
213 extstring s2;//s1;
214 s2.assign(s);
215
216 size_t p1 = s2.ufind("<body", 0);
217 if (p1 == std::string::npos)
218 return 1;
219 s1 = s2.substr(0, p1);
220 s2.erase(0, p1);
221 p1 = s2.ufind("</body", 0);
222 if (p1 == std::string::npos)
223 return 1;
224 s3 = s2.substr(p1);
225 s2.erase(p1);
226
227 if (s2.ufind("<FORM", 0) == string::npos ||
228 s2.ufind("</FORM", 0) == string::npos )
229 return 1;
230 s2.ureplace("<FORM");
231 s2.ureplace("</FORM");
232 s2.ureplace("<INPUT");
233 s2.ureplace("<TEXTAREA");
234 s2.ureplace("</TEXTAREA");
235 s2.ureplace("VALUE=\"");
236 s2.ureplace("NAME=\"");
237 s2.ureplace("TYPE=\"");
238 s2.ureplace("=\"TEXT\"");
239 s2.ureplace("=\"RADIO\"");
240 s2.ureplace("=\"CHECKBOX\"");
241 s2.ureplace("=\"PASSWORD\"");
242
243 s2.ureplace("=\"NUMBER\"");
244 s2.ureplace("=\"DATE\"");
245 s2.ureplace("=\"DATETIME\"");
246 s2.ureplace("=\"DATETIME-LOCAL\"");
247 s2.ureplace("=\"TIME\"");
248 s2.ureplace("=\"WEEK\"");
249 s2.ureplace("=\"MONTH\"");
250 s2.ureplace("=\"TEL\"");
251 s2.ureplace("=\"EMAIL\"");
252
253 s2.ureplace("SELECTED");
254 s2.ureplace("CHECKED");
255 s2.ureplace("<SELECT");
256 s2.ureplace("</SELECT");
257 // s2.ureplace("<OPTION");
258 s.assign(s1).append(s2).append(s3);
259 // s.assign(s1);
260 return 0;
261 }
262
263 // this function may be called by the main or the web_server thread
264 // reads the values associated with each form type and
265 // clears the html form of those value.
extract_fields()266 void extract_fields()
267 {
268 if (custom_select < 0) return;
269 string fname = CUSTOM_dir;
270 {
271 guard_lock web_lock(&mutex_web_server);
272 fname.append(custom_pairs[custom_select].file_name);
273 edit_txt.assign("CUSTOM_FORM,")
274 .append(custom_pairs[custom_select].file_name)
275 .append("\n");
276 }
277 FILE *html_file = fopen(fname.c_str(), "r");
278 char c = fgetc(html_file);
279 html_form.clear();
280
281 while (!feof(html_file)) {
282 html_form += c;
283 c = fgetc(html_file);
284 }
285
286 fclose(html_file);
287
288 if (convert_case(html_form)) {
289 html_form.clear();
290 custom_select = 0;
291 edit_txt.assign("INVALID HTML FORM DOCUMENT");
292 return;
293 }
294
295 size_t ptype, pstart, pend, pname, pvalue, p1, p2, p3;
296 string field_name;
297 string field_value;
298 string val;
299
300 name_values.clear();
301
302 for (size_t i = 0; i < sizeof(input_types) / sizeof(INPUT_TYPE); i++) {
303 ptype = html_form.find(input_types[i].txt);
304 while (ptype != string::npos) {
305 field_name.clear();
306 field_value.clear();
307 pstart = html_form.rfind("<", ptype);
308 pend = html_form.find(">", ptype);
309 pname = html_form.find(name_str, pstart);
310
311 if (pname == string::npos || pname > pend) {
312 ptype = html_form.find(input_types[i].txt, ptype + 1);
313 continue;
314 }
315
316 pname += name_str.length();
317 p1 = html_form.find("\"", pname);
318 field_name = html_form.substr(pname, p1 - pname);
319
320 switch (input_types[i].id) {
321 case T_TEXT: case T_PASSWORD: case T_NUMBER:
322 case T_DATE: case T_DTIME: case T_DTIME_LOCAL:
323 case T_TIME: case T_WEEK: case T_MONTH: case T_TEL: case T_EMAIL:
324 pvalue = html_form.find(value_str, pstart);
325 if (pvalue == string::npos || pvalue > pend) break;
326 pvalue += value_str.length();
327 p2 = html_form.find("\"", pvalue);
328 val = html_form.substr(pvalue, p2 - pvalue);
329 p3 = val.find(""");
330 while (p3 != string::npos)
331 val.replace(p3, 6, "\"");
332 escape(val);
333 field_value = val;
334 break;
335 case T_RADIO:
336 pvalue = html_form.find(value_str, pstart);
337 if (pvalue == string::npos || pvalue > pend) break;
338 pvalue += value_str.length();
339 p1 = html_form.find("\"", pvalue);
340 if (p1 < pend) {
341 field_name.append(".")
342 .append(html_form.substr(pvalue, p1 - pvalue));
343 field_value.clear();
344 p2 = html_form.find(checkedeq, pstart);
345 if (p2 < pend) {
346 field_value = "Y";//checked;
347 html_form.erase(p2 - 1, checkedeq.length() + 1);
348 pend = html_form.find(">", pstart);
349 } else {
350 p2 = html_form.find(checked, pstart);
351 if (p2 < pend) {
352 field_value = "Y";//checked;
353 html_form.erase(p2 - 1, checked.length() + 1);
354 pend = html_form.find(">", pstart);
355 }
356 }
357 }
358 break;
359 case T_CHECKBOX:
360 pvalue = html_form.find(value_str, pstart);
361 if (pvalue != string::npos && pvalue < pend) {
362 pvalue += value_str.length();
363 p1 = html_form.find("\"", pvalue);
364 if (p1 < pend) {
365 field_name.append(".")
366 .append(html_form.substr(pvalue, p1 - pvalue));
367 }
368 }
369 field_value.clear();
370 p2 = html_form.find(checkedeq, pstart);
371 if (p2 < pend) {
372 field_value = "Y";//"ON";
373 html_form.erase(p2 - 1, checkedeq.length() + 1);
374 pend = html_form.find(">", pstart);
375 } else {
376 p2 = html_form.find(checked, pstart);
377 if (p2 < pend) {
378 field_value = "Y";//"ON";
379 html_form.erase(p2 - 1, checked.length() + 1);
380 pend = html_form.find(">", pstart);
381 } else {
382 p2 = html_form.find(" ON", pstart);
383 if (p2 < pend) {
384 field_value = "Y";//"ON";
385 html_form.erase(p2, 3);
386 pend = html_form.find(">", pstart);
387 }
388 }
389 }
390 break;
391 case T_AREA: //extract
392 pvalue = pend + 1;
393 p1 = html_form.find(textend_str, pvalue);
394 if (p1 == string::npos) break;
395 if (p1 > pvalue)
396 val = html_form.substr(pvalue, p1 - pvalue);
397 else
398 val.clear();
399 html_form.erase(pvalue, val.length());
400 escape(val);
401 field_value.assign(val);
402 break;
403 case T_SELECT:
404 p3 = html_form.find(end_sel_str, pstart);
405 if (p3 == string::npos) break;
406 p2 = html_form.find(selected, pstart);
407 if (p2 != string::npos && p2 < p3) {
408 pvalue = html_form.rfind(value_str, p2);
409 if (pvalue != string::npos) {
410 pvalue += value_str.length();
411 p1 = html_form.find("\"", pvalue);
412 if (p1 < p2)
413 field_value = html_form.substr(pvalue, p1 - pvalue);
414 }
415 }
416 break;
417 default:
418 break;
419 }
420
421 edit_txt.append(field_name).append(",");
422 edit_txt.append(field_value).append("\n");
423 name_values.push_back(NAME_VALUE(input_types[i].id, field_name, field_value));
424
425 ptype = html_form.find(input_types[i].txt, ptype+1);
426 }
427 }
428 }
429
refresh_txt_custom_msg(void *)430 void refresh_txt_custom_msg(void *)
431 {
432 txt_custom_msg->clear();
433 txt_custom_msg->add(edit_txt.c_str());
434 }
435
436 // called by web server thread
get_html_vars(struct mg_connection * conn)437 void get_html_vars(struct mg_connection *conn)
438 {
439 size_t p;
440 if (custom_select < 0) return;
441
442 edit_txt.assign("CUSTOM_FORM,")
443 .append(custom_pairs[custom_select].file_name)
444 .append("\n");
445
446 string field, line, val;
447 for (size_t n = 0; n < name_values.size(); n++)
448 name_values[n].value.clear();
449
450 for (size_t n = 0; n < name_values.size(); n++) {
451 field = name_values[n].name;
452 if (name_values[n].id == T_CHECKBOX ||
453 name_values[n].id == T_RADIO) {
454 if ((p = field.find(".")) != string::npos)
455 field.erase(p);
456 }
457
458 int fld_len = mg_get_var_len(conn, field.c_str());
459 char buff[fld_len + 1];
460 memset(buff, 0, sizeof(buff));
461
462 mg_get_var(conn, field.c_str(), buff, sizeof(buff));
463
464 switch (name_values[n].id) {
465 case T_RADIO :
466 p = name_values[n].name.find(".");
467 if (name_values[n].name.substr(p+1) == buff)
468 name_values[n].value = "Y";//checked;
469 line.assign(name_values[n].name)
470 .append(",")
471 .append(name_values[n].value);
472 break;
473 case T_CHECKBOX :
474 if (strstr(buff, "on") == buff || strstr(buff, "ON") == buff)
475 name_values[n].value = "Y";//"ON";
476 line.assign(name_values[n].name)
477 .append(",")
478 .append(name_values[n].value);
479 break;
480 case T_AREA://get html vars
481 val = buff;
482 escape(val);
483 name_values[n].value = val;
484 line.assign(name_values[n].name).append(",").append(val);
485 break;
486 // T_TEXT, T_PASSWORD, T_SELECT, T_DATE, T_DATETIME ...
487 default :
488 name_values[n].value = buff;
489 line.assign(name_values[n].name)
490 .append(",")
491 .append(name_values[n].value);
492 }
493 edit_txt.append(line).append("\n");
494 }
495 Fl::awake(refresh_txt_custom_msg);
496 }
497
498 // modify the html form with the value strings
499
assign_values(string & html)500 void assign_values(string &html)
501 {
502 string nm, val, s1, s2, temp;
503 size_t p, p0, p1, p2, p3, pbeg, pend, pval, pnm;
504
505 for (size_t n = 0; n < name_values.size(); n++) {
506 switch (name_values[n].id) {
507 case T_TEXT : case T_PASSWORD : case T_NUMBER :
508 case T_DATE : case T_DTIME : case T_DTIME_LOCAL :
509 case T_TIME : case T_WEEK : case T_MONTH : case T_TEL : case T_EMAIL:
510 nm.assign("NAME=\"").append(name_values[n].name).append("\"");
511 pnm = html.find(nm);
512 if (pnm != string::npos) {
513 pnm += nm.length();
514 p1 = html.find(value_str, pnm);
515 p2 = html.find(">", pnm);
516 val = name_values[n].value;
517 unescape(val);
518 p3 = val.find("\"");
519 while (p3 != string::npos) {
520 val.replace(p3,1, """);
521 p3 = val.find("\"");
522 }
523 if (p1 < p2) {
524 p1 += value_str.length();
525 p2 = html.find("\"", p1);
526 html.replace(p1, p2 - p1, val);
527 } else {
528 temp.assign(" VALUE=\"").append(val).append("\"");
529 html.insert(p2, temp);
530 }
531 }
532 break;
533 case T_RADIO :
534 temp = name_values[n].name;
535 p = temp.find(".");
536 s1.assign(temp.substr(0, p));
537 s2.assign(temp.substr(p+1));
538 nm.assign(name_str).append(s1).append("\"");
539 val.assign(value_str).append(s2).append("\"");
540 pnm = html.find(nm);
541 while (pnm != string::npos) {
542 pbeg = html.rfind("<", pnm); // beginning of tag specifier
543 pend = html.find(">", pbeg); // end of tag specifier
544 pval = html.find(val, pbeg);
545 if (pval == string::npos || pval > pend) {
546 pnm = html.find(nm, pend);
547 continue;
548 }
549 // found name and value pair
550 if (name_values[n].value == checked ||
551 name_values[n].value == "Y")
552 html.insert(pend, string(" ").append(checked));
553 pend = html.find(">", pbeg);
554 pnm = html.find(nm, pend);
555 }
556 break;
557 case T_CHECKBOX :
558 nm.assign("NAME=\"").append(name_values[n].name).append("\"");
559 pnm = html.find(nm);
560 if (pnm != string::npos) {
561 pbeg = html.rfind("<", pnm);
562 pend = html.find(">", pbeg);
563 if (name_values[n].value == "ON" ||
564 name_values[n].value == "Y") {
565 html.insert(pend, string(" ").append(checked));
566 pend = html.find(">", pbeg);
567 }
568 }
569 break;
570 case T_AREA : //assign values
571 nm.assign("NAME=\"").append(name_values[n].name).append("\"");
572 pnm = html.find(nm);
573 if (pnm != string::npos) {
574 p1 = html.find(textend_str, pnm);
575 if (p1 == string::npos)
576 break;
577 p0 = html.rfind(">", p1) + 1;
578 val = name_values[n].value;
579 unescape(val);
580 if (p0 < p1) {
581 html.replace(p0, p1 - p0, val);
582 } else {
583 html.insert(p1, val);
584 }
585 }
586 break;
587 case T_SELECT :
588 nm.assign("NAME=\"").append(name_values[n].name).append("\"");
589 pnm = html.find(nm);
590 if (pnm != string::npos) {
591 p2 = html.find("</SELECT", pnm);
592 p0 = html.find(value_str, pnm);
593 while(p0 != string::npos && p0 < p2) {
594 p0 += value_str.length();
595 p1 = html.find("\"", p0);
596 if (p1 != string::npos && p1 < p2) {
597 p3 = html.find(">", p1);
598 if (html.substr(p0, p1 - p0) == name_values[n].value) {
599 html.replace(p1+1, p3 - p1 - 1, " SELECTED");
600 } else
601 html.replace(p1+1, p3 - p1 - 1, "");
602 }
603 p0 = html.find(value_str, p0);
604 }
605 }
606 break;
607 default :
608 break;
609 }
610 }
611 }
612
custom_editor(struct mg_connection * conn)613 void custom_editor(struct mg_connection *conn)
614 {
615 string html_edit = html_form;
616 size_t p = html_edit.find("<FORM");
617 if (p == string::npos) return;
618
619 html_edit.replace(p, 5, action_str);
620 p = html_edit.find("</FORM");
621 html_edit.replace(p, 6, submit_str);
622 assign_values(html_edit);
623 mg_send_data(conn, html_edit.c_str(), html_edit.length());
624 }
625
custom_viewer(struct mg_connection * conn)626 void custom_viewer(struct mg_connection *conn)
627 {
628 string html_view = html_form;
629
630 assign_values(html_view);
631
632 // add readonly attribute to all input controls
633 size_t pstart, ptext, pradio, pcheckbox, ppassword, pnumber,
634 pdate, pdtime, pdtime_local, pweek, pmonth,
635 ptel, pemail, pend;
636
637 pstart = html_view.find("<INPUT");
638 while (pstart != string::npos) {
639 pend = html_view.find(">", pstart);
640 ptext = html_view.find(text_type_str, pstart);
641 ppassword = html_view.find(password_type_str, pstart);
642 pnumber = html_view.find(number_str, pstart);
643 pradio = html_view.find(radio_type_str, pstart );
644 pcheckbox = html_view.find(checkbox_type_str, pstart);
645 pdate = html_view.find(date_str, pstart);
646 pdtime = html_view.find(datetime_str, pstart);
647 pdtime_local = html_view.find(datetime_local_str, pstart);
648 pweek = html_view.find(week_str, pstart);
649 pmonth = html_view.find(month_str, pstart);
650 ptel = html_view.find(tel_str, pstart);
651 pemail = html_view.find(email_str, pstart);
652
653 if (ppassword < pend) {
654 size_t pvalue = html_view.find("VALUE=\"", pstart);
655 if (pvalue < pend) {
656 pvalue += 7;
657 while (html_view[pvalue] != '\"') {
658 html_view[pvalue] = '*';
659 pvalue++;
660 }
661 }
662 html_view.replace(ppassword, password_type_str.length(), text_type_str);
663 html_view.insert(pstart + 6, " READONLY");
664 } else if (ptext < pend || pnumber < pend ||
665 pdate < pend || pdtime < pend || pdtime_local < pend ||
666 pweek < pend || pmonth < pend || ptel < pend || pemail < pend)
667 html_view.insert(pstart + 6, " READONLY");
668 else if (pradio < pend || pcheckbox < pend)
669 html_view.insert(pstart + 6, " DISABLED");
670 pstart = html_view.find("<INPUT", pend + 1);
671 }
672
673 pstart = html_view.find("<TEXTAREA");
674 while (pstart != string::npos) {
675 pend = html_view.find(">", pstart);
676 html_view.insert(pstart + 9, " READONLY");
677 pstart = html_view.find("<TEXTAREA", pend + 1);
678 }
679
680 pstart = html_view.find("<SELECT");
681 while (pstart != string::npos) {
682 pend = html_view.find(">", pstart);
683 html_view.insert(pstart + 7, " DISABLED");
684 pstart = html_view.find("<SELECT", pend + 1);
685 }
686
687 string fname = "";
688 size_t plc = custom_field.find(",");
689 size_t plf = custom_field.find("\n");
690 if ((plc != string::npos) && (plf != string::npos) && (plc < plf))
691 fname.append(custom_field.substr(plc+1, plf - plc - 1));
692 else
693 fname.append("RX_doc.html");
694 plc = fname.find(".");
695 if (plc != string::npos) fname.erase(plc);
696 fname.append(".htm");
697 fname.insert(0, FLMSG_temp_dir);
698
699 FILE *tempfile = fopen(fname.c_str(), "w");
700 fprintf(tempfile,"%s", html_view.c_str());
701 fclose(tempfile);
702
703 if (conn == (struct mg_connection *)0) {
704 open_url(fname.c_str());
705 return;
706 }
707
708 mg_send_data(conn, html_view.c_str(), html_view.length());
709 }
710
711 bool using_custom_template = false;
712
clear_customfields()713 void clear_customfields()
714 {
715 custom_field.clear();
716 extract_fields();
717 }
718
check_customfields()719 bool check_customfields()
720 {
721 return (custom_field != txt_custom_msg->buffer()->text());
722 }
723
update_customfields()724 void update_customfields()
725 {
726 custom_field = txt_custom_msg->buffer()->text();
727 }
728
clear_custom_form()729 void clear_custom_form()
730 {
731 clear_customfields();
732 txt_custom_msg->clear();
733 txt_custom_msg->add(edit_txt.c_str());
734 }
735
text_to_pairs()736 void text_to_pairs()
737 {
738 string val;
739 size_t p, p1;
740 int offset;
741 for (size_t n = 0; n < name_values.size(); n++) {
742 // look for LF+Field+COMMA
743 p = edit_txt.find("\n" + name_values[n].name + ",");
744 offset = 2; //Skip 2 characters (LF and COMMA)
745 // Not found, check if it is first field in the data string (not preceeded by LF).
746 if (p == string::npos) {
747 if (edit_txt.find(name_values[n].name + ",") == 0) {
748 p = 0;
749 offset = 1; //Skip 1 character (COMMA)
750 }
751 }
752 if (p != string::npos) {
753 p += name_values[n].name.length() + offset;
754 p1 = edit_txt.find("\n", p);
755 val = edit_txt.substr(p, p1 - p);
756 if (!val.empty() &&
757 (name_values[n].id == T_AREA || name_values[n].id == T_TEXT))
758 escape(val);
759 name_values[n].value = val;
760 }
761 }
762 }
763
pairs_to_text()764 void pairs_to_text()
765 {
766 edit_txt.clear();
767 edit_txt.assign("CUSTOM_FORM,")
768 .append(custom_pairs[custom_select].file_name)
769 .append("\n");
770 for (size_t n = 0; n < name_values.size(); n++) {
771 edit_txt.append(name_values[n].name)
772 .append(",")
773 .append(name_values[n].value)
774 .append("\n");
775 }
776 }
777
min_pairs_to_text()778 std::string min_pairs_to_text()
779 {
780 if (custom_select == -1) return "";
781 static std::string mintext;
782 mintext.clear();
783 mintext.assign("CUSTOM_FORM,")
784 .append(custom_pairs[custom_select].file_name)
785 .append("\n");
786 for (size_t n = 0; n < name_values.size(); n++) {
787 if (!name_values[n].value.empty())
788 mintext.append(name_values[n].name)
789 .append(",")
790 .append(name_values[n].value)
791 .append("\n");
792 }
793 return mintext;
794 }
795
update_customform()796 void update_customform()
797 {
798 extract_fields();
799 edit_txt = custom_field;
800 text_to_pairs();
801 pairs_to_text();
802 txt_custom_msg->clear();
803 txt_custom_msg->add(edit_txt.c_str());
804 }
805
read_custombuffer(string data)806 void read_custombuffer(string data)
807 {
808 size_t p0, p1;
809
810 clear_customfields();
811 read_header(data);
812 custom_field = findstr(data, custom_msg);
813
814 custom_select = -1;
815
816 p0 = custom_field.find("CUSTOM_FORM,");
817 if (p0 == string::npos) return;
818 p0 += 12;//strlen("CUSTOM_FORM,");
819 p1 = custom_field.find("\n", p0);
820 if (p1 == string::npos) return;
821 string fname = custom_field.substr(p0, p1-p0);
822 string html_fname = CUSTOM_dir;
823 html_fname.append(fname);
824
825 { // treat this block as a critical section
826 guard_lock web_lock(&mutex_web_server);
827 for (int i = 0; i < num_custom_entries; i++) {
828 if (fname == custom_pairs[i].file_name) {
829 custom_select = i;
830 break;
831 }
832 }
833 }
834 if (custom_select == -1) {
835 fl_alert2("Custom form %s not found!", fname.c_str());
836 return;
837 }
838 update_customform();
839 }
840
cb_custom_new()841 void cb_custom_new()
842 {
843 if (check_customfields()) {
844 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 1) {
845 update_header(CHANGED);
846 cb_custom_save_as();
847 }
848 }
849 clear_custom_form();
850 clear_header();
851 def_custom_filename = ICS_msg_dir;
852 def_custom_filename.append("new").append(CUSTOMFILE_EXT);
853 show_filename(def_custom_filename);
854 using_custom_template = false;
855 }
856
cb_custom_import()857 void cb_custom_import()
858 {
859 fl_alert2("Not implemented");
860 }
861
cb_custom_export()862 void cb_custom_export()
863 {
864 fl_alert2("Not implemented");
865 }
866
cb_custom_wrap_import(string wrapfilename,string inpbuffer)867 void cb_custom_wrap_import(string wrapfilename, string inpbuffer)
868 {
869 clear_custom_form();
870 read_custombuffer(inpbuffer);
871 def_custom_filename = ICS_msg_dir;
872 def_custom_filename.append(wrapfilename);
873 show_filename(def_custom_filename);
874 using_custom_template = false;
875 }
876
eval_custom_fsize()877 int eval_custom_fsize()
878 {
879 Ccrc16 chksum;
880 evalstr.assign("[WRAP:beg][WRAP:lf][WRAP:fn ");
881 evalstr.append(base_custom_filename).append("]");
882 update_customfields();
883 update_header(FROM);
884 evalstr.append(header("<customform>"));
885 string outbuf = lineout( custom_msg, custom_field );
886 if (outbuf.empty()) return 0;
887 compress_maybe( outbuf );
888 evalstr.append( outbuf );
889 evalstr.append("[WRAP:chksum ").append(chksum.scrc16(evalstr)).append("][WRAP:end]");
890 return evalstr.length();
891 }
892
cb_custom_wrap_export()893 void cb_custom_wrap_export()
894 {
895 if (check_customfields()) {
896 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
897 return;
898 update_header(CHANGED);
899 }
900 string wfields = min_pairs_to_text();
901 if (wfields.empty()) return;
902
903 if (base_custom_filename == string("new").append(CUSTOMFILE_EXT) ||
904 base_custom_filename == string("default").append(CUSTOMFILE_EXT) )
905 if (!cb_custom_save_as()) return;
906
907 string wrapfilename = WRAP_send_dir;
908 wrapfilename.append(base_custom_filename);
909 wrapfilename.append(WRAP_EXT);
910 const char *p = FSEL::saveas(
911 "Save as wrap file",
912 "Wrap file\t*.{wrap,WRAP}",
913 wrapfilename.c_str());
914 if (p) {
915 string pext = fl_filename_ext(p);
916 wrapfilename = p;
917 update_header(FROM);
918 custombuffer.assign(header("<customform>"));
919 string outbuf = lineout( custom_msg, wfields);
920 compress_maybe(outbuf);
921 custombuffer.append(outbuf);
922 export_wrapfile(base_custom_filename, wrapfilename, custombuffer, pext != WRAP_EXT);
923
924 custombuffer.assign(header("<customform>")).append(lineout( custom_msg, custom_field ));
925 write_custom(def_custom_filename);
926 }
927 }
928
cb_custom_wrap_autosend()929 void cb_custom_wrap_autosend()
930 {
931 if (check_customfields()) {
932 if (fl_choice2("Form modified, save?", "No", "Yes", NULL) == 0)
933 return;
934 update_header(CHANGED);
935 }
936 string wfields = min_pairs_to_text();
937 if (wfields.empty()) return;
938
939 if (base_custom_filename == string("new").append(CUSTOMFILE_EXT) ||
940 base_custom_filename == string("default").append(CUSTOMFILE_EXT) )
941 if (!cb_custom_save_as()) return;
942
943 update_header(FROM);
944 custombuffer.assign(header("<customform>"));
945 string outbuf = lineout( custom_msg, wfields);
946
947 compress_maybe(outbuf);
948 custombuffer.append(outbuf);
949 xfr_via_socket(base_custom_filename, custombuffer);
950
951 custombuffer.assign(header("<customform>")).append(lineout( custom_msg, custom_field ));
952 write_custom(def_custom_filename);
953 }
954
cb_custom_load_template()955 void cb_custom_load_template()
956 {
957 return;
958 }
959
cb_custom_save_template()960 void cb_custom_save_template()
961 {
962 if (!using_custom_template) {
963 cb_custom_save_as_template();
964 return;
965 }
966 string def_custom_filename = def_custom_TemplateName;
967 const char *p = FSEL::saveas(
968 "Save template file",
969 string("Template file\t*").append(CUSTOMTEMP_EXT).c_str(),
970 def_custom_filename.c_str());
971 if (p) {
972 update_header(CHANGED);
973 update_customfields();
974 custombuffer.assign(header("<customform>"));
975 custombuffer.append( lineout( custom_msg, custom_field ) );
976 write_custom(p);
977 }
978 }
979
cb_custom_save_as_template()980 void cb_custom_save_as_template()
981 {
982 string def_custom_filename = def_custom_TemplateName;
983 const char *p = FSEL::saveas(
984 "Save as template file",
985 string("Template file\t*").append(CUSTOMTEMP_EXT).c_str(),
986 def_custom_filename.c_str());
987 if (p) {
988 const char *pext = fl_filename_ext(p);
989 def_custom_TemplateName = p;
990 if (strlen(pext) == 0) def_custom_TemplateName.append(CUSTOMTEMP_EXT);
991 remove_spaces_from_filename(def_custom_TemplateName);
992 clear_header();
993 update_header(CHANGED);
994 update_customfields();
995 custombuffer.assign(header("<customform>"));
996 custombuffer.append( lineout( custom_msg, custom_field ) );
997 write_custom(def_custom_TemplateName);
998 show_filename(def_custom_TemplateName);
999 using_custom_template = true;
1000 }
1001 }
1002
cb_custom_open()1003 void cb_custom_open()
1004 {
1005 const char *p = FSEL::select(_("Open data file"), "custom form\t*.k2s",
1006 def_custom_filename.c_str());
1007 if (!p) return;
1008 if (strlen(p) == 0) return;
1009 if ((strstr(p, ".k2s") == NULL) && update_custom) {
1010 fl_alert2("Not a custom form");
1011 return;
1012 }
1013 clear_custom_form();
1014 read_data_file(p);
1015 using_custom_template = false;
1016 def_custom_filename = p;
1017 show_filename(def_custom_filename);
1018 }
1019
write_custom(string s)1020 void write_custom(string s)
1021 {
1022 if (custombuffer.empty())
1023 return;
1024 FILE *customfile = fopen(s.c_str(), "w");
1025 if (!customfile) return;
1026 fwrite(custombuffer.c_str(), custombuffer.length(), 1, customfile);
1027 fclose(customfile);
1028 }
1029
cb_custom_save_as()1030 bool cb_custom_save_as()
1031 {
1032 const char *p;
1033 string newfilename;
1034
1035 string name = named_file();
1036 if (!name.empty()) {
1037 name.append(".k2s");
1038 newfilename = ICS_msg_dir;
1039 newfilename.append(name);
1040 } else
1041 newfilename = def_custom_filename;
1042
1043 p = FSEL::saveas(_("Save data file"), "custom form\t*.k2s",
1044 newfilename.c_str());
1045
1046 if (!p) return false;
1047 if (strlen(p) == 0) return false;
1048
1049 if (progStatus.sernbr_fname) update_sernbr();
1050
1051 const char *pext = fl_filename_ext(p);
1052 def_custom_filename = p;
1053 if (strlen(pext) == 0) def_custom_filename.append(".k2s");
1054
1055 remove_spaces_from_filename(def_custom_filename);
1056 update_header(NEW);
1057 update_customfields();
1058 custombuffer.assign(header("<customform>"));
1059 custombuffer.append( lineout( custom_msg, custom_field ) );
1060 write_custom(def_custom_filename);
1061
1062 using_custom_template = false;
1063 show_filename(def_custom_filename);
1064 return true;
1065 }
1066
cb_custom_save()1067 void cb_custom_save()
1068 {
1069 if (base_custom_filename == "new.k2s" ||
1070 base_custom_filename == "default.k2s" ||
1071 using_custom_template == true) {
1072 cb_custom_save_as();
1073 return;
1074 }
1075 if (check_customfields()) update_header(CHANGED);
1076 update_customfields();
1077 custombuffer.assign(header("<customform>"));
1078 custombuffer.append( lineout( custom_msg, custom_field ) );
1079 write_custom(def_custom_filename);
1080 using_custom_template = false;
1081 }
1082
cb_custom_msg_type()1083 void cb_custom_msg_type()
1084 {
1085 if (tabs_msg_type->value() == tab_custom ) {
1086 show_filename(def_custom_filename);
1087 } else {
1088 show_filename(def_rg_filename);
1089 }
1090 }
1091
cb_custom_import_data()1092 void cb_custom_import_data()
1093 {
1094 def_custom_filename = CUSTOM_dir;
1095 def_custom_filename.append("default.custom");
1096
1097 const char *p = FSEL::select(
1098 _("Import custom data"),
1099 "custom file\t*.custom",
1100 def_custom_filename.c_str());
1101 if (!p) return;
1102 if (strlen(p) == 0) return;\
1103
1104 clear_custom_form();
1105 clear_estimate();
1106
1107 // open the custom file, read all data
1108 long filesize = 0;
1109 char *buff;
1110 FILE *custom_datafile;
1111
1112 custom_datafile = fopen (p, "r");
1113 if (!custom_datafile)
1114 return;
1115 // determine its size for buffer creation
1116 fseek (custom_datafile, 0, SEEK_END);
1117 filesize = ftell (custom_datafile);
1118 // test file integrity
1119 if (filesize == 0) {
1120 fl_alert2(_("Empty file"));
1121 fclose (custom_datafile);
1122 return;
1123 }
1124
1125 buff = new char[filesize + 1];
1126 memset(buff, 0, filesize + 1);
1127 // read the entire file into the buffer
1128 fseek (custom_datafile, 0, SEEK_SET);
1129 int retval = fread (buff, filesize, 1, custom_datafile);
1130 fclose (custom_datafile);
1131 if (retval != 1) {
1132 delete[] buff;
1133 return;
1134 }
1135
1136 custom_field = buff;
1137 // strip any cr-lf pairs if the file was a DOS text file
1138 size_t ptr = custom_field.find("\r\n");
1139 while (ptr != string::npos) {
1140 custom_field.erase(ptr, 1);
1141 ptr = custom_field.find("\r\n");
1142 }
1143 update_customform();
1144
1145 delete [] buff;
1146
1147 using_custom_template = false;
1148
1149 def_custom_filename = ICS_msg_dir;
1150 def_custom_filename.append(fl_filename_name(p));
1151 size_t pext = def_custom_filename.find(".custom");
1152 if (pext == string::npos) pext = def_custom_filename.find(".custom");
1153 if (pext != string::npos) def_custom_filename.erase(pext);
1154 def_custom_filename.append(".k2s");
1155 show_filename(def_custom_filename);
1156 estimate();
1157 }
1158
cb_custom_export_data(bool open_file)1159 void cb_custom_export_data(bool open_file)
1160 {
1161 update_customfields();
1162 if (custom_field.empty()) return;
1163
1164 string fname_name = fl_filename_name(def_custom_filename.c_str());
1165 size_t p = fname_name.rfind('.');
1166 if (p != string::npos) fname_name.erase(p);
1167
1168 string custom_name = CUSTOM_dir;
1169 custom_name.append(fname_name).append(".custom");
1170
1171 const char *pfilename = FSEL::saveas(
1172 _("Export custom data"),
1173 "custom file\t*.custom",
1174 custom_name.c_str());
1175
1176 if (!pfilename) return;
1177 if (strlen(pfilename) == 0) return;
1178 custom_name = pfilename;
1179 if (custom_name.find(".custom") == string::npos)
1180 custom_name.append(".custom");
1181
1182 FILE *customfile = fopen(custom_name.c_str(), "w");
1183 fprintf(customfile,"%s", custom_field.c_str());
1184 fclose(customfile);
1185
1186 if (open_file) {
1187 fl_alert2("If you modify the data you must save as custom and\nimport the modified file");
1188 open_url(pfilename);
1189 } else
1190 fl_alert2("Data written to %s", custom_name.c_str());
1191
1192 }
1193
custom_set_fname(const char * fn)1194 void custom_set_fname(const char *fn)
1195 {
1196 string fname = fn;
1197 size_t pext = fname.find(".custom");
1198 if (pext == string::npos) pext = fname.find(".custom");
1199 if (pext == string::npos) {
1200 txt_custom_msg->clear();
1201 return;
1202 }
1203 using_custom_template = false;
1204 def_custom_filename = ICS_msg_dir;
1205 def_custom_filename.append(fl_filename_name(fn));
1206 def_custom_filename.find(".custom");
1207 if (pext == string::npos) pext = def_custom_filename.find(".custom");
1208 if (pext != string::npos) def_custom_filename.erase(pext);
1209 def_custom_filename.append(".k2s");
1210 show_filename(def_custom_filename);
1211 }
1212
cb_custom_html(bool exit_after_print)1213 void cb_custom_html(bool exit_after_print)
1214 {
1215 if (custom_field.find("CUSTOM_FORM") == 0) {
1216 size_t plf = custom_field.find("\n");
1217 if (plf != string::npos) {
1218 string fname = CUSTOM_dir;
1219 fname.append(custom_field.substr(12, plf - 12));
1220 FILE *html_file = fopen(fname.c_str(), "r");
1221 char c;
1222
1223 html_form.clear();
1224 c = fgetc(html_file);
1225 while (!feof(html_file)) {
1226 html_form += c;
1227 c = fgetc(html_file);
1228 }
1229 fclose(html_file);
1230
1231 convert_case(html_form);
1232 if (exit_after_print) {
1233 custom_viewer((struct mg_connection *)0);
1234 return;
1235 }
1236
1237 handle_type = HANDLE_VIEW;
1238 string url = "http://127.0.0.1:";
1239 url.append(sz_srvr_portnbr);
1240 open_url(url.c_str());
1241 }
1242 }
1243 }
1244
cb_custom_textout()1245 void cb_custom_textout()
1246 {
1247 return;
1248 if (custom_field.find("CUSTOM_FORM") == 0) {
1249 size_t plf = custom_field.find("\n");
1250 if (plf != string::npos) {
1251 string tempfile = FLMSG_temp_dir;
1252 tempfile.append(custom_field.substr(12, plf - 12));
1253 tempfile.append(".txt");
1254 FILE *textfile = fopen(tempfile.c_str(), "w");
1255 fprintf(textfile,"%s", edit_txt.c_str());
1256 fclose(textfile);
1257 open_url(tempfile.c_str());
1258 }
1259 }
1260 }
1261
1262 //==============================================================================
1263 // Support for transfering / receiving custom FORM, html files
1264 //==============================================================================
1265
1266 string transfer_custom_buffer;
1267 string def_custom_transfer_filename;
1268
1269 string def_custom_rx_filename;
1270 string receive_custom_buffer;
1271
read_custom_transfer_buffer(string data)1272 void read_custom_transfer_buffer(string data)
1273 {
1274 const char *xfrstr = "<html_form>\n";
1275 size_t p1 = data.find(xfrstr);
1276 if (p1 != string::npos) p1 += strlen(xfrstr);
1277 data.erase(0, p1);
1278 receive_custom_buffer = data;
1279 read_header(data);
1280 }
1281
cb_custom_form_wrap_import(string wrapfilename,string inpbuffer)1282 void cb_custom_form_wrap_import(string wrapfilename, string inpbuffer)
1283 {
1284 read_custom_transfer_buffer(inpbuffer);
1285
1286 def_custom_rx_filename = CUSTOM_dir;
1287 def_custom_rx_filename.append(wrapfilename);
1288 txt_rcvd_custom_html_filename->value(def_custom_rx_filename.c_str());
1289 btn_save_custom_html_file->color(FL_RED);
1290 }
1291
eval_transfer_custom_form_fsize()1292 int eval_transfer_custom_form_fsize()
1293 {
1294 if (transfer_custom_buffer.empty()) return 0;
1295
1296 Ccrc16 chksum;
1297
1298 evalstr.assign("[WRAP:beg][WRAP:lf][WRAP:fn ");
1299 evalstr.append(fl_filename_name(def_custom_transfer_filename.c_str())).append("]");
1300 evalstr.append(header("<html_form>"));
1301
1302 string outbuf(transfer_custom_buffer);
1303 if (outbuf.empty()) return 0;
1304
1305 compress_maybe( outbuf, false );
1306
1307 evalstr.append( outbuf );
1308 string ck = chksum.scrc16(evalstr);
1309
1310 evalstr.append("[WRAP:chksum ").append(ck).append("][WRAP:end]");
1311
1312 return evalstr.length();
1313 }
1314
load_custom_html_file()1315 void load_custom_html_file()
1316 {
1317 if (def_custom_filename.empty()) return;
1318 string fname = CUSTOM_dir;
1319 fname.append(def_custom_transfer_filename);
1320 transfer_custom_buffer.clear();
1321
1322 FILE *dfile = fopen(fname.c_str(), "rb");
1323 if (!dfile) {
1324 show_filename("ERROR");
1325 transfer_custom_buffer.clear();
1326 return;
1327 }
1328 fseek(dfile, 0, SEEK_END);
1329 size_t fsize = ftell(dfile);
1330 if (fsize <= 0) {
1331 fclose (dfile);
1332 return;
1333 }
1334 fseek(dfile, 0, SEEK_SET);
1335 transfer_custom_buffer.resize(fsize);
1336 size_t r = fread((void *)transfer_custom_buffer.c_str(), 1, fsize, dfile);
1337 fclose(dfile);
1338 if (r != fsize) {
1339 show_filename("ERROR");
1340 transfer_custom_buffer.clear();
1341 return;
1342 }
1343 estimate();
1344 }
1345
cb_transfer_custom_html()1346 void cb_transfer_custom_html()
1347 {
1348 if (transfer_custom_buffer.empty()) return;
1349
1350 update_header(FROM);
1351 string fbuff(header("<html_form>"));
1352 string outbuf(transfer_custom_buffer);
1353 compress_maybe(outbuf, false);
1354 fbuff.append(outbuf);
1355 xfr_via_socket(fl_filename_name(def_custom_transfer_filename.c_str()), fbuff);
1356 }
1357
cb_save_custom_html(Fl_Widget * w,void * d)1358 void cb_save_custom_html(Fl_Widget *w, void *d)
1359 {
1360 btn_save_custom_html_file->color(FL_BACKGROUND_COLOR);
1361
1362 if (receive_custom_buffer.empty()) return;
1363
1364 FILE *binfile = fopen(def_custom_rx_filename.c_str(), "wb");
1365 if (binfile) {
1366 fwrite(receive_custom_buffer.c_str(), receive_custom_buffer.length(), 1, binfile);
1367 fclose(binfile);
1368 }
1369 txt_rcvd_custom_html_filename->value("");
1370 update_custom_transfer();
1371 }
1372
cb_btn_select_custom_html(Fl_Widget * w,void * d)1373 void cb_btn_select_custom_html(Fl_Widget *w, void *d)
1374 {
1375 def_custom_transfer_filename = custom_files[custom_selector->index()];
1376 show_filename(def_custom_transfer_filename);
1377 load_custom_html_file();
1378 }
1379
custom_edit()1380 void custom_edit() {
1381
1382 handle_type = HANDLE_EDIT;
1383 string url = "http://127.0.0.1:";
1384 url.append(sz_srvr_portnbr);
1385 open_url(url.c_str());
1386 }
1387