1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2015 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include <stdio.h>
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <errno.h>
34 
35 #include "defs.h"
36 #include "main.h"
37 #include "prefs.h"
38 #include "prefs_gtk.h"
39 #include "prefs_common.h"
40 #include "utils.h"
41 #include "gtkutils.h"
42 #include "password.h"
43 #include "codeconv.h"
44 #include "file-utils.h"
45 
46 #define CL(x)	(((gulong) (x) >> (gulong) 8) & 0xFFUL)
47 #define RGB_FROM_GDK_COLOR(c) \
48 	((CL(c.red)   << (gulong) 16) | \
49 	 (CL(c.green) << (gulong)  8) | \
50 	 (CL(c.blue)))
51 
52 typedef enum
53 {
54 	DUMMY_PARAM
55 } DummyEnum;
56 
57 static GHashTable *whole_cache = NULL;
58 
59 static gboolean prefs_read_config_from_cache(PrefParam *param, const gchar *label,
60 			       const gchar *rcfile);
61 
62 static void prefs_config_parse_one_line(PrefParam	*param,
63 					 const gchar	*buf);
64 
prefs_read_config(PrefParam * param,const gchar * label,const gchar * rcfile,const gchar * encoding)65 void prefs_read_config(PrefParam *param, const gchar *label,
66 		       const gchar *rcfile, const gchar *encoding)
67 {
68 	FILE *fp;
69 	gchar buf[PREFSBUFSIZE];
70 	gchar *block_label;
71 
72 	cm_return_if_fail(param != NULL);
73 	cm_return_if_fail(label != NULL);
74 	cm_return_if_fail(rcfile != NULL);
75 
76 	if (encoding != NULL)
77 		g_warning("Encoding is ignored");
78 
79 	debug_print("Reading configuration...\n");
80 
81 	prefs_set_default(param);
82 
83 	if (whole_cache != NULL) {
84 		if (prefs_read_config_from_cache(param, label, rcfile) == TRUE)
85 			return;
86 	}
87 
88 	if ((fp = claws_fopen(rcfile, "rb")) == NULL) {
89 		if (ENOENT != errno) FILE_OP_ERROR(rcfile, "claws_fopen");
90 		return;
91 	}
92 
93 	block_label = g_strdup_printf("[%s]", label);
94 
95 	/* search aiming block */
96 	while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
97 		gint val;
98 
99 		if (encoding) {
100 			gchar *conv_str;
101 
102 			conv_str = conv_codeset_strdup
103 				(buf, encoding, CS_INTERNAL);
104 			if (!conv_str)
105 				conv_str = g_strdup(buf);
106 			val = strncmp
107 				(conv_str, block_label, strlen(block_label));
108 			g_free(conv_str);
109 		} else
110 			val = strncmp(buf, block_label, strlen(block_label));
111 		if (val == 0) {
112 			debug_print("Found %s\n", block_label);
113 			break;
114 		}
115 	}
116 	g_free(block_label);
117 
118 	while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
119 		strretchomp(buf);
120 		/* reached next block */
121 		if (buf[0] == '[') break;
122 		if (buf[0] == '#') continue;
123 
124 		if (encoding) {
125 			gchar *conv_str;
126 
127 			conv_str = conv_codeset_strdup
128 				(buf, encoding, CS_INTERNAL);
129 			if (!conv_str)
130 				conv_str = g_strdup(buf);
131 			prefs_config_parse_one_line(param, conv_str);
132 			g_free(conv_str);
133 		} else
134 			prefs_config_parse_one_line(param, buf);
135 	}
136 
137 	debug_print("Finished reading configuration.\n");
138 	claws_fclose(fp);
139 }
140 
prefs_config_parse_one_line(PrefParam * param,const gchar * buf)141 static void prefs_config_parse_one_line(PrefParam *param, const gchar *buf)
142 {
143 	gint i;
144 	gint name_len;
145 	const gchar *value;
146 	GdkColor color;
147 
148 	for (i = 0; param[i].name != NULL; i++) {
149 		name_len = strlen(param[i].name);
150 		if (g_ascii_strncasecmp(buf, param[i].name, name_len))
151 			continue;
152 		if (buf[name_len] != '=')
153 			continue;
154 		value = buf + name_len + 1;
155 		/* debug_print("%s = %s\n", param[i].name, value); */
156 
157 		switch (param[i].type) {
158 		case P_STRING:
159 		case P_PASSWORD:
160 		{
161 			gchar *tmp = NULL;
162 
163 			if (*value) {
164 				if (g_utf8_validate(value, -1, NULL))
165 					tmp = g_strdup(value);
166 				else {
167 					tmp = conv_codeset_strdup(value,
168 						    conv_get_locale_charset_str_no_utf8(),
169 						    CS_INTERNAL);
170 				}
171 			} else {
172 				tmp = g_strdup("");
173 			}
174 			if (!tmp) {
175 				g_warning("Failed to convert character set.");
176 				tmp = g_strdup(value);
177 			}
178 			g_free(*((gchar **)param[i].data));
179 			*((gchar **)param[i].data) = tmp;
180 			break;
181 		}
182 		case P_INT:
183 			*((gint *)param[i].data) =
184 				(gint)atoi(value);
185 			break;
186 		case P_BOOL:
187 			*((gboolean *)param[i].data) =
188 				(*value == '0' || *value == '\0')
189 					? FALSE : TRUE;
190 			break;
191 		case P_ENUM:
192 			*((DummyEnum *)param[i].data) =
193 				(DummyEnum)atoi(value);
194 			break;
195 		case P_USHORT:
196 			*((gushort *)param[i].data) =
197 				(gushort)atoi(value);
198 			break;
199 		case P_COLOR:
200 			if (gdk_color_parse(value, &color)) {
201 				*((gulong *)param[i].data) = RGB_FROM_GDK_COLOR(color);
202 			}
203 			else
204 				/* be compatible and accept ints */
205 				*((gulong *)param[i].data) = strtoul(value, 0, 10);
206 			break;
207 		default:
208 			break;
209 		}
210 	}
211 }
212 
213 #define TRY(func) \
214 if (!(func)) \
215 { \
216 	g_warning("Failed to write configuration to file"); \
217 	if (orig_fp) claws_fclose(orig_fp); \
218 	prefs_file_close_revert(pfile); \
219 	g_free(rcpath); \
220 	g_free(block_label); \
221 	return; \
222 } \
223 
prefs_write_config(PrefParam * param,const gchar * label,const gchar * rcfile)224 void prefs_write_config(PrefParam *param, const gchar *label,
225 		        const gchar *rcfile)
226 {
227 	FILE *orig_fp;
228 	PrefFile *pfile;
229 	gchar *rcpath;
230 	gchar buf[PREFSBUFSIZE];
231 	gchar *block_label = NULL;
232 	gboolean block_matched = FALSE;
233 
234 	cm_return_if_fail(param != NULL);
235 	cm_return_if_fail(label != NULL);
236 	cm_return_if_fail(rcfile != NULL);
237 
238 	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
239 	if ((orig_fp = claws_fopen(rcpath, "rb")) == NULL) {
240 		if (ENOENT != errno) FILE_OP_ERROR(rcpath, "claws_fopen");
241 	}
242 
243 	if ((pfile = prefs_write_open(rcpath)) == NULL) {
244 		g_warning("Failed to write configuration to file");
245 		if (orig_fp) claws_fclose(orig_fp);
246 		g_free(rcpath);
247 		return;
248 	}
249 
250 	block_label = g_strdup_printf("[%s]", label);
251 
252 	/* search aiming block */
253 	if (orig_fp) {
254 		while (claws_fgets(buf, sizeof(buf), orig_fp) != NULL) {
255 			gint val;
256 
257 			val = strncmp(buf, block_label, strlen(block_label));
258 			if (val == 0) {
259 				debug_print("Found %s\n", block_label);
260 				block_matched = TRUE;
261 				break;
262 			} else
263 				TRY(claws_fputs(buf, pfile->fp) != EOF);
264 		}
265 	}
266 
267 	TRY(fprintf(pfile->fp, "%s\n", block_label) > 0);
268 
269 	/* write all param data to file */
270 	TRY(prefs_write_param(param, pfile->fp) == 0);
271 
272 	if (block_matched) {
273 		gboolean in_dup_block = FALSE;
274 		while (claws_fgets(buf, sizeof(buf), orig_fp) != NULL) {
275 			/* next block */
276 			if (buf[0] == '[') {
277 				TRY(claws_fputc('\n', pfile->fp) != EOF &&
278 				    claws_fputs(buf, pfile->fp)  != EOF);
279 				break;
280 			}
281 		}
282 		while (claws_fgets(buf, sizeof(buf), orig_fp) != NULL) {
283 			if (buf[0] == '[') {
284 				if (!strncmp(buf, block_label,
285 						strlen(block_label)))
286 					in_dup_block = TRUE;
287 				else
288 					in_dup_block = FALSE;
289 			}
290 			if (!in_dup_block)
291 				TRY(claws_fputs(buf, pfile->fp) != EOF);
292 		}
293 	}
294 
295 	g_free(block_label);
296 	block_label = NULL;
297 
298 	if (orig_fp) claws_fclose(orig_fp);
299 	if (prefs_file_close(pfile) < 0)
300 		g_warning("Failed to write configuration to file");
301 	g_free(rcpath);
302 
303 	debug_print("Configuration is saved.\n");
304 }
305 
prefs_write_param(PrefParam * param,FILE * fp)306 gint prefs_write_param(PrefParam *param, FILE *fp)
307 {
308 	gint i;
309 	gchar buf[PREFSBUFSIZE] = "";
310 
311 	for (i = 0; param[i].name != NULL; i++) {
312 		switch (param[i].type) {
313 		case P_STRING:
314 		{
315 			gchar *tmp = NULL;
316 
317 			if (*((gchar **)param[i].data)) {
318 				if (g_utf8_validate(*((gchar **)param[i].data), -1, NULL))
319 					tmp = g_strdup(*((gchar **)param[i].data));
320 				else {
321 					tmp = conv_codeset_strdup(*((gchar **)param[i].data),
322 						conv_get_locale_charset_str_no_utf8(),
323 						CS_INTERNAL);
324 					if (!tmp)
325 						tmp = g_strdup(*((gchar **)param[i].data));
326 				}
327 			}
328 
329 			g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name,
330 				   tmp ? tmp : "");
331 
332 			g_free(tmp);
333 			break;
334 		}
335 		case P_PASSWORD:
336 			buf[0] = '\0'; /* Passwords are written to password store. */
337 			break;
338 		case P_INT:
339 			g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
340 				   *((gint *)param[i].data));
341 			break;
342 		case P_BOOL:
343 			g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
344 				   *((gboolean *)param[i].data));
345 			break;
346 		case P_ENUM:
347 			g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
348 				   *((DummyEnum *)param[i].data));
349 			break;
350 		case P_USHORT:
351 			g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
352 				   *((gushort *)param[i].data));
353 			break;
354 		case P_COLOR:
355 			g_snprintf(buf, sizeof buf,  "%s=#%6.6lx\n", param[i].name,
356 				   *((gulong *) param[i].data));
357 			break;
358 		default:
359 			/* unrecognized, fail */
360 			debug_print("Unrecognized parameter type\n");
361 			return -1;
362 		}
363 
364 		if (buf[0] != '\0') {
365 			if (claws_fputs(buf, fp) == EOF) {
366 				perror("claws_fputs");
367 				return -1;
368 			}
369 		}
370 	}
371 
372 	return 0;
373 }
374 
prefs_set_default(PrefParam * param)375 void prefs_set_default(PrefParam *param)
376 {
377 	gint i;
378 	GdkColor color;
379 
380 	cm_return_if_fail(param != NULL);
381 
382 	for (i = 0; param[i].name != NULL; i++) {
383 		if (!param[i].data) continue;
384 
385 		switch (param[i].type) {
386 		case P_STRING:
387 			g_free(*((gchar **)param[i].data));
388 			if (param[i].defval != NULL) {
389 				if (!strncasecmp(param[i].defval, "ENV_", 4)) {
390 					const gchar *envstr;
391 					gchar *tmp;
392 
393 					envstr = g_getenv(param[i].defval + 4);
394 					tmp = envstr && *envstr ?
395 						conv_codeset_strdup(envstr,
396 								    conv_get_locale_charset_str(),
397 								    CS_INTERNAL)
398 						: g_strdup("");
399 					if (!tmp) {
400 						g_warning("Failed to convert character set.");
401 						tmp = g_strdup(envstr);
402 					}
403 					*((gchar **)param[i].data) = tmp;
404 				} else if (param[i].defval[0] == '~')
405 					*((gchar **)param[i].data) =
406 						g_strconcat(get_home_dir(),
407 							    param[i].defval + 1,
408 							    NULL);
409 				else
410 					*((gchar **)param[i].data) =
411 						g_strdup(param[i].defval);
412 			} else
413 				*((gchar **)param[i].data) = NULL;
414 			break;
415 		case P_PASSWORD:
416 			g_free(*((gchar **)param[i].data));
417 			if (param[i].defval != NULL) {
418 				if (param[i].defval[0] != '\0')
419 					*((gchar **)param[i].data) =
420 						g_strdup(param[i].defval);
421 				else
422 					*((gchar **)param[i].data) = NULL;
423 			} else
424 				*((gchar **)param[i].data) = NULL;
425 			break;
426 		case P_INT:
427 			if (param[i].defval != NULL)
428 				*((gint *)param[i].data) =
429 					(gint)atoi(param[i].defval);
430 			else if (!strcmp(param[i].name, "config_version"))
431 				*((gint *)param[i].data) = CLAWS_CONFIG_VERSION;
432 			else
433 				*((gint *)param[i].data) = 0;
434 			break;
435 		case P_BOOL:
436 			if (param[i].defval != NULL) {
437 				if (!g_ascii_strcasecmp(param[i].defval, "TRUE"))
438 					*((gboolean *)param[i].data) = TRUE;
439 				else
440 					*((gboolean *)param[i].data) = atoi(param[i].defval) ? TRUE : FALSE;
441 			} else
442 				*((gboolean *)param[i].data) = FALSE;
443 			break;
444 		case P_ENUM:
445 			if (param[i].defval != NULL)
446 				*((DummyEnum*)param[i].data) =
447 					(DummyEnum)atoi(param[i].defval);
448 			else
449 				*((DummyEnum *)param[i].data) = 0;
450 			break;
451 		case P_USHORT:
452 			if (param[i].defval != NULL)
453 				*((gushort *)param[i].data) = (gushort)atoi(param[i].defval);
454 			else
455 				*((gushort *)param[i].data) = 0;
456 			break;
457 		case P_COLOR:
458 			if (param[i].defval != NULL && gdk_color_parse(param[i].defval, &color))
459 				*((gulong *)param[i].data) = RGB_FROM_GDK_COLOR(color);
460 			else if (param[i].defval)
461 				/* be compatible and accept ints */
462 				*((gulong *)param[i].data) = strtoul(param[i].defval, 0, 10);
463 			else
464 				*((gulong *)param[i].data) = 0;
465 			break;
466 		default:
467 			break;
468 		}
469 	}
470 }
471 
prefs_free(PrefParam * param)472 void prefs_free(PrefParam *param)
473 {
474 	gint i;
475 
476 	cm_return_if_fail(param != NULL);
477 
478 	for (i = 0; param[i].name != NULL; i++) {
479 		if (!param[i].data) continue;
480 
481 		switch (param[i].type) {
482 		case P_STRING:
483 		case P_PASSWORD:
484 			g_free(*((gchar **)param[i].data));
485 			break;
486 		default:
487 			break;
488 		}
489 	}
490 }
491 
prefs_button_toggled(GtkToggleButton * toggle_btn,GtkWidget * widget)492 void prefs_button_toggled(GtkToggleButton *toggle_btn, GtkWidget *widget)
493 {
494 	gboolean is_active;
495 
496 	is_active = gtk_toggle_button_get_active(toggle_btn);
497 	gtk_widget_set_sensitive(widget, is_active);
498 }
499 
prefs_button_toggled_reverse(GtkToggleButton * toggle_btn,GtkWidget * widget)500 void prefs_button_toggled_reverse(GtkToggleButton *toggle_btn, GtkWidget *widget)
501 {
502 	gboolean is_active;
503 
504 	is_active = gtk_toggle_button_get_active(toggle_btn);
505 	gtk_widget_set_sensitive(widget, !is_active);
506 }
507 
prefs_set_dialog(PrefParam * param)508 void prefs_set_dialog(PrefParam *param)
509 {
510 	gint i;
511 
512 	for (i = 0; param[i].name != NULL; i++) {
513 		if (param[i].widget_set_func)
514 			param[i].widget_set_func(&param[i]);
515 	}
516 }
517 
prefs_set_data_from_dialog(PrefParam * param)518 void prefs_set_data_from_dialog(PrefParam *param)
519 {
520 	gint i;
521 
522 	for (i = 0; param[i].name != NULL; i++) {
523 		if (param[i].data_set_func)
524 			param[i].data_set_func(&param[i]);
525 	}
526 }
527 
prefs_set_dialog_to_default(PrefParam * param)528 void prefs_set_dialog_to_default(PrefParam *param)
529 {
530 	gint	   i;
531 	PrefParam  tmpparam;
532 	gchar	  *str_data = NULL;
533 	gint	   int_data;
534 	gushort    ushort_data;
535 	gboolean   bool_data;
536 	DummyEnum  enum_data;
537 
538 	for (i = 0; param[i].name != NULL; i++) {
539 		if (!param[i].widget_set_func) continue;
540 
541 		tmpparam = param[i];
542 
543 		switch (tmpparam.type) {
544 		case P_STRING:
545 			if (tmpparam.defval) {
546 				if (!g_ascii_strncasecmp(tmpparam.defval, "ENV_", 4)) {
547 					str_data = g_strdup(g_getenv(param[i].defval + 4));
548 					tmpparam.data = &str_data;
549 					break;
550 				} else if (tmpparam.defval[0] == '~') {
551 					str_data =
552 						g_strconcat(get_home_dir(),
553 							    param[i].defval + 1,
554 							    NULL);
555 					tmpparam.data = &str_data;
556 					break;
557 				}
558 			}
559 			tmpparam.data = &tmpparam.defval;
560 			break;
561 		case P_PASSWORD:
562 			tmpparam.data = &tmpparam.defval;
563 			break;
564 		case P_INT:
565 			if (tmpparam.defval)
566 				int_data = atoi(tmpparam.defval);
567 			else
568 				int_data = 0;
569 			tmpparam.data = &int_data;
570 			break;
571 		case P_USHORT:
572 			if (tmpparam.defval)
573 				ushort_data = atoi(tmpparam.defval);
574 			else
575 				ushort_data = 0;
576 			tmpparam.data = &ushort_data;
577 			break;
578 		case P_BOOL:
579 			if (tmpparam.defval) {
580 				if (!g_ascii_strcasecmp(tmpparam.defval, "TRUE"))
581 					bool_data = TRUE;
582 				else
583 					bool_data = atoi(tmpparam.defval)
584 						? TRUE : FALSE;
585 			} else
586 				bool_data = FALSE;
587 			tmpparam.data = &bool_data;
588 			break;
589 		case P_ENUM:
590 			if (tmpparam.defval)
591 				enum_data = (DummyEnum)atoi(tmpparam.defval);
592 			else
593 				enum_data = 0;
594 			tmpparam.data = &enum_data;
595 			break;
596 		case P_OTHER:
597 		default:
598 			break;
599 		}
600 		tmpparam.widget_set_func(&tmpparam);
601 		g_free(str_data);
602 		str_data = NULL;
603 	}
604 }
605 
prefs_set_data_from_entry(PrefParam * pparam)606 void prefs_set_data_from_entry(PrefParam *pparam)
607 {
608 	gchar **str;
609 	const gchar *entry_str;
610 
611 	cm_return_if_fail(*pparam->widget != NULL);
612 
613 	entry_str = gtk_entry_get_text(GTK_ENTRY(*pparam->widget));
614 
615 	switch (pparam->type) {
616 	case P_STRING:
617 		str = (gchar **)pparam->data;
618 		g_free(*str);
619 		*str = entry_str[0] ? g_strdup(entry_str) : NULL;
620 		break;
621 	case P_USHORT:
622 		*((gushort *)pparam->data) = atoi(entry_str);
623 		break;
624 	case P_INT:
625 		*((gint *)pparam->data) = atoi(entry_str);
626 		break;
627 	default:
628 		g_warning("Invalid PrefType for GtkEntry widget: %d",
629 			  pparam->type);
630 	}
631 }
632 
prefs_set_escaped_data_from_entry(PrefParam * pparam)633 void prefs_set_escaped_data_from_entry(PrefParam *pparam)
634 {
635 	gchar **str;
636 
637 	cm_return_if_fail(*pparam->widget != NULL);
638 
639 	switch (pparam->type) {
640 	case P_STRING:
641 		str = (gchar **)pparam->data;
642 		g_free(*str);
643 		*str = pref_get_pref_from_entry(GTK_ENTRY(*pparam->widget));
644 		break;
645 	default:
646 		g_warning("Invalid escaped PrefType for GtkEntry widget: %d",
647 			  pparam->type);
648 	}
649 }
650 
prefs_set_entry(PrefParam * pparam)651 void prefs_set_entry(PrefParam *pparam)
652 {
653 	gchar **str;
654 	cm_return_if_fail(*pparam->widget != NULL);
655 
656 	switch (pparam->type) {
657 	case P_STRING:
658 		str = (gchar **)pparam->data;
659 		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
660 				   *str ? *str : "");
661 		break;
662 	case P_INT:
663 		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
664 				   itos(*((gint *)pparam->data)));
665 		break;
666 	case P_USHORT:
667 		gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
668 				   itos(*((gushort *)pparam->data)));
669 		break;
670 	default:
671 		g_warning("Invalid PrefType for GtkEntry widget: %d",
672 			  pparam->type);
673 	}
674 }
675 
prefs_set_entry_from_escaped(PrefParam * pparam)676 void prefs_set_entry_from_escaped(PrefParam *pparam)
677 {
678 	gchar **str;
679 
680 	cm_return_if_fail(*pparam->widget != NULL);
681 
682 	switch (pparam->type) {
683 	case P_STRING:
684 		str = (gchar **)pparam->data;
685 		pref_set_entry_from_pref(GTK_ENTRY(*pparam->widget),
686 				   *str ? *str : "");
687 		break;
688 	default:
689 		g_warning("Invalid escaped PrefType for GtkEntry widget: %d",
690 			  pparam->type);
691 	}
692 }
693 
prefs_set_data_from_text(PrefParam * pparam)694 void prefs_set_data_from_text(PrefParam *pparam)
695 {
696 	gchar **str;
697 	gchar *text = NULL, *tp = NULL;
698 	gchar *tmp, *tmpp;
699 
700 	cm_return_if_fail(*pparam->widget != NULL);
701 
702 	switch (pparam->type) {
703 	case P_STRING:
704 		str = (gchar **)pparam->data;
705 		g_free(*str);
706 		if (GTK_IS_EDITABLE(*pparam->widget)) {   /* need? */
707 			tp = text = gtk_editable_get_chars
708 					(GTK_EDITABLE(*pparam->widget), 0, -1);
709 		} else if (GTK_IS_TEXT_VIEW(*pparam->widget)) {
710 			GtkTextView *textview = GTK_TEXT_VIEW(*pparam->widget);
711 			GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview);
712 			GtkTextIter start, end;
713 			gtk_text_buffer_get_start_iter(buffer, &start);
714 			gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
715 			tp = text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
716 		}
717 
718 		cm_return_if_fail (tp && text);
719 
720 		if (text[0] == '\0') {
721 			*str = NULL;
722 			g_free(text);
723 			break;
724 		}
725 
726 		Xalloca(tmpp = tmp, strlen(text) * 2 + 1,
727 			{ *str = NULL; break; });
728 		while (*tp) {
729 			if (*tp == '\n') {
730 				*tmpp++ = '\\';
731 				*tmpp++ = 'n';
732 				tp++;
733 			} else
734 				*tmpp++ = *tp++;
735 		}
736 		*tmpp = '\0';
737 		*str = g_strdup(tmp);
738 		g_free(text);
739 		break;
740 	default:
741 		g_warning("Invalid PrefType for GtkText widget: %d",
742 			  pparam->type);
743 	}
744 }
745 
prefs_set_escaped_data_from_text(PrefParam * pparam)746 void prefs_set_escaped_data_from_text(PrefParam *pparam)
747 {
748 	gchar **str;
749 
750 	cm_return_if_fail(*pparam->widget != NULL);
751 
752 	switch (pparam->type) {
753 	case P_STRING:
754 		str = (gchar **)pparam->data;
755 		g_free(*str);
756 		*str = pref_get_pref_from_textview(GTK_TEXT_VIEW(*pparam->widget));
757 		break;
758 	default:
759 		g_warning("Invalid escaped PrefType for GtkText widget: %d",
760 			  pparam->type);
761 	}
762 }
763 
prefs_set_text(PrefParam * pparam)764 void prefs_set_text(PrefParam *pparam)
765 {
766 	gchar *buf, *sp, *bufp;
767 	gchar **str;
768 	GtkTextView *text;
769 	GtkTextBuffer *buffer;
770 	GtkTextIter iter;
771 
772 	cm_return_if_fail(*pparam->widget != NULL);
773 
774 	switch (pparam->type) {
775 	case P_STRING:
776 		str = (gchar **)pparam->data;
777 		if (*str) {
778 			bufp = buf = alloca(strlen(*str) + 1);
779 			if (!buf) buf = "";
780 			else {
781 				sp = *str;
782 				while (*sp) {
783 					if (*sp == '\\' && *(sp + 1) == 'n') {
784 						*bufp++ = '\n';
785 						sp += 2;
786 					} else
787 						*bufp++ = *sp++;
788 				}
789 				*bufp = '\0';
790 			}
791 		} else
792 			buf = "";
793 
794 		text = GTK_TEXT_VIEW(*pparam->widget);
795 		buffer = gtk_text_view_get_buffer(text);
796 		gtk_text_buffer_set_text(buffer, "", -1);
797 		gtk_text_buffer_get_start_iter(buffer, &iter);
798 		gtk_text_buffer_insert(buffer, &iter, buf, -1);
799 		break;
800 	default:
801 		g_warning("Invalid PrefType for GtkTextView widget: %d",
802 			  pparam->type);
803 	}
804 }
805 
prefs_set_text_from_escaped(PrefParam * pparam)806 void prefs_set_text_from_escaped(PrefParam *pparam)
807 {
808 	gchar **str;
809 
810 	cm_return_if_fail(*pparam->widget != NULL);
811 
812 	switch (pparam->type) {
813 	case P_STRING:
814 		str = (gchar **)pparam->data;
815 		pref_set_textview_from_pref(GTK_TEXT_VIEW(*pparam->widget),
816 				 *str ? *str : "");
817 		break;
818 	default:
819 		g_warning("Invalid escaped PrefType for GtkTextView widget: %d",
820 			  pparam->type);
821 	}
822 }
823 
prefs_set_data_from_toggle(PrefParam * pparam)824 void prefs_set_data_from_toggle(PrefParam *pparam)
825 {
826 	cm_return_if_fail(pparam->type == P_BOOL);
827 	cm_return_if_fail(*pparam->widget != NULL);
828 
829 	*((gboolean *)pparam->data) =
830 		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*pparam->widget));
831 }
832 
prefs_set_toggle(PrefParam * pparam)833 void prefs_set_toggle(PrefParam *pparam)
834 {
835 	cm_return_if_fail(pparam->type == P_BOOL);
836 	cm_return_if_fail(*pparam->widget != NULL);
837 
838 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(*pparam->widget),
839 				     *((gboolean *)pparam->data));
840 }
841 
prefs_set_data_from_spinbtn(PrefParam * pparam)842 void prefs_set_data_from_spinbtn(PrefParam *pparam)
843 {
844 	cm_return_if_fail(*pparam->widget != NULL);
845 
846 	switch (pparam->type) {
847 	case P_INT:
848 		*((gint *)pparam->data) =
849 			gtk_spin_button_get_value_as_int
850 			(GTK_SPIN_BUTTON(*pparam->widget));
851 		break;
852 	case P_USHORT:
853 		*((gushort *)pparam->data) =
854 			(gushort)gtk_spin_button_get_value_as_int
855 			(GTK_SPIN_BUTTON(*pparam->widget));
856 		break;
857 	default:
858 		g_warning("Invalid PrefType for GtkSpinButton widget: %d",
859 			  pparam->type);
860 	}
861 }
862 
prefs_set_spinbtn(PrefParam * pparam)863 void prefs_set_spinbtn(PrefParam *pparam)
864 {
865 	cm_return_if_fail(*pparam->widget != NULL);
866 
867 	switch (pparam->type) {
868 	case P_INT:
869 		gtk_spin_button_set_value(GTK_SPIN_BUTTON(*pparam->widget),
870 					  (gfloat)*((gint *)pparam->data));
871 		break;
872 	case P_USHORT:
873 		gtk_spin_button_set_value(GTK_SPIN_BUTTON(*pparam->widget),
874 					  (gfloat)*((gushort *)pparam->data));
875 		break;
876 	default:
877 		g_warning("Invalid PrefType for GtkSpinButton widget: %d",
878 			  pparam->type);
879 	}
880 }
881 
882 static GSList *prefs_pages = NULL;
883 
prefs_gtk_window_closed_cb(PrefsWindow * prefswindow)884 static void prefs_gtk_window_closed_cb(PrefsWindow *prefswindow)
885 {
886 	if (prefswindow == NULL)
887 		return;
888 
889 	if (prefswindow->dialog_response > PREFSWINDOW_RESPONSE_CANCEL)
890 		prefs_common_write_config();
891 }
892 
prefs_gtk_open(void)893 void prefs_gtk_open(void)
894 {
895 	prefswindow_open(_("Preferences"), prefs_pages, NULL,
896 			&prefs_common.prefswin_width, &prefs_common.prefswin_height,
897 			NULL, prefs_gtk_window_closed_cb, prefs_gtk_window_closed_cb);
898 }
899 
prefs_gtk_register_page(PrefsPage * page)900 void prefs_gtk_register_page(PrefsPage *page)
901 {
902 	prefs_pages = g_slist_append(prefs_pages, page);
903 }
904 
prefs_gtk_unregister_page(PrefsPage * page)905 void prefs_gtk_unregister_page(PrefsPage *page)
906 {
907 	prefs_pages = g_slist_remove(prefs_pages, page);
908 }
909 
prefs_destroy_whole_cache(gpointer to_free)910 static void prefs_destroy_whole_cache(gpointer to_free)
911 {
912 	GHashTable *table = (GHashTable *)to_free;
913 	g_hash_table_destroy(table);
914 }
915 
prefs_destroy_file_cache(gpointer to_free)916 static void prefs_destroy_file_cache(gpointer to_free)
917 {
918 	GHashTable *table = (GHashTable *)to_free;
919 	g_hash_table_destroy(table);
920 }
921 
prefs_cache_sections(GHashTable * file_cache,const gchar * rcfile)922 static int prefs_cache_sections(GHashTable *file_cache, const gchar *rcfile)
923 {
924 	FILE *fp = NULL;
925 	gchar buf[PREFSBUFSIZE];
926 	GHashTable *section_cache = NULL;
927 
928 	if (rcfile)
929 		fp = claws_fopen(rcfile, "rb");
930 	if (!fp) {
931 		debug_print("cache: %s: %s\n", rcfile?rcfile:"(null)", g_strerror(errno));
932 		return -1;
933 	}
934 
935 	while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
936 		strretchomp(buf);
937 		if (buf[0] == '\0')
938 			continue;
939 		if (buf[0] == '#')
940 			continue; /* comment */
941 		if (buf[0] == '[') { /* new section */
942 			gchar *blockname = g_strdup(buf+1);
943 
944 			if (strrchr(blockname, ']'))
945 				*strrchr(blockname, ']') = '\0';
946 
947 			if ((section_cache = g_hash_table_lookup(file_cache, blockname)) == NULL) {
948 				debug_print("new section '%s'\n", blockname);
949 				section_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
950 						g_free, NULL);
951 				g_hash_table_insert(file_cache,
952 					blockname, section_cache);
953 			} else {
954 				debug_print("section '%s' already done\n", blockname);
955 				g_free(blockname);
956 				section_cache = NULL;
957 				continue;
958 			}
959 		} else {
960 			if (!section_cache) {
961 				debug_print("skipping stuff %s with no section\n", buf);
962 				continue;
963 			} else {
964 				gchar *pref;
965 
966 				if (!strchr(buf, '=')) {
967 					/* plugins do differently */
968 					continue;
969 				}
970 				pref = g_strdup(buf);
971 
972 				//debug_print("new pref '%s'\n", pref);
973 				g_hash_table_insert(section_cache, pref, GINT_TO_POINTER(1));
974 			}
975 		}
976 	}
977 	claws_fclose(fp);
978 	return 0;
979 }
980 
prefs_cache(const gchar * rcfile)981 static int prefs_cache(const gchar *rcfile)
982 {
983 	GHashTable *file_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
984 					g_free, prefs_destroy_file_cache);
985 
986 	debug_print("new file '%s'\n", rcfile?rcfile:"(null)");
987 	g_hash_table_insert(whole_cache, g_strdup(rcfile), file_cache);
988 
989 	return prefs_cache_sections(file_cache, rcfile);
990 }
991 
prefs_prepare_cache(void)992 void prefs_prepare_cache(void)
993 {
994 	gchar *clawsrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
995 	gchar *folderitemrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FOLDERITEM_RC, NULL);
996 	gchar *accountrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL);
997 
998 	if (whole_cache == NULL) {
999 		whole_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
1000 				g_free, prefs_destroy_whole_cache);
1001 	} else {
1002 		debug_print("already cached\n");
1003 		g_free(clawsrc);
1004 		g_free(folderitemrc);
1005 		g_free(accountrc);
1006 		return;
1007 	}
1008 	if (prefs_cache(clawsrc) < 0 ||
1009 	    prefs_cache(folderitemrc) < 0 ||
1010 	    prefs_cache(accountrc) < 0)
1011 		prefs_destroy_cache();
1012 
1013 	g_free(clawsrc);
1014 	g_free(folderitemrc);
1015 	g_free(accountrc);
1016 }
1017 
prefs_destroy_cache(void)1018 void prefs_destroy_cache(void)
1019 {
1020 	if (!whole_cache) {
1021 		debug_print("no cache\n");
1022 		return;
1023 	}
1024 	debug_print("destroying cache\n");
1025 	g_hash_table_destroy(whole_cache);
1026 	whole_cache = NULL;
1027 	return;
1028 }
1029 
prefs_parse_cache(gpointer key,gpointer value,gpointer user_data)1030 static void prefs_parse_cache(gpointer key, gpointer value, gpointer user_data)
1031 {
1032 	gchar *pref = (gchar *)key;
1033 
1034 	PrefParam *param = (PrefParam *)user_data;
1035 
1036 	prefs_config_parse_one_line(param, pref);
1037 }
1038 
prefs_read_config_from_cache(PrefParam * param,const gchar * label,const gchar * rcfile)1039 static gboolean prefs_read_config_from_cache(PrefParam *param, const gchar *label,
1040 			       const gchar *rcfile)
1041 {
1042 	GHashTable *sections_table = NULL;
1043 	GHashTable *values_table = NULL;
1044 	sections_table = g_hash_table_lookup(whole_cache, rcfile);
1045 
1046 	if (sections_table == NULL) {
1047 		g_warning("Can't find %s in the whole cache", rcfile?rcfile:"(null)");
1048 		return FALSE;
1049 	}
1050 	values_table = g_hash_table_lookup(sections_table, label);
1051 
1052 	if (values_table == NULL) {
1053 		debug_print("no '%s' section in '%s' cache\n", label?label:"(null)", rcfile?rcfile:"(null)");
1054 		return TRUE;
1055 	}
1056 	g_hash_table_foreach(values_table, prefs_parse_cache, param);
1057 	return TRUE;
1058 }
1059