talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

prefs_gtk.c (25321B)


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