1 /* $Id$ */
2 /* Copyright (c) 2006-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Mailer */
4 /* This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, version 3 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
15 
16 
17 
18 #include <dirent.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <libintl.h>
26 #include <openssl/err.h>
27 #include <openssl/ssl.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <Desktop.h>
30 #include "../include/Mailer/plugin.h"
31 #include "folder.h"
32 #include "message.h"
33 #include "compose.h"
34 #include "callbacks.h"
35 #include "mailer.h"
36 #include "../config.h"
37 #define _(string) gettext(string)
38 #define N_(string) (string)
39 #include "common.c"
40 
41 
42 /* constants */
43 #ifndef PREFIX
44 # define PREFIX		"/usr/local"
45 #endif
46 #ifndef LIBDIR
47 # define LIBDIR		PREFIX "/lib"
48 #endif
49 #ifndef PLUGINDIR
50 # define PLUGINDIR	LIBDIR "/Mailer"
51 #endif
52 
53 
54 /* Mailer */
55 /* private */
56 /* types */
57 typedef enum _MailerPluginColumn
58 {
59 	MPC_NAME = 0,
60 	MPC_ENABLED,
61 	MPC_ICON,
62 	MPC_NAME_DISPLAY,
63 	MPC_PLUGIN,
64 	MPC_MAILERPLUGINDEFINITION,
65 	MPC_MAILERPLUGIN,
66 	MPC_WIDGET
67 } MailerPluginColumn;
68 #define MPC_LAST MPC_WIDGET
69 #define MPC_COUNT (MPC_LAST + 1)
70 
71 struct _Mailer
72 {
73 	Account ** available; /* XXX consider using another data type */
74 	unsigned int available_cnt;
75 
76 	Account ** account;
77 	unsigned int account_cnt;
78 	Account * account_cur;
79 	Folder * folder_cur;
80 	Message * message_cur;
81 
82 	guint source;
83 
84 	/* configuration */
85 	Config * config;
86 
87 	/* SSL */
88 	SSL_CTX * ssl_ctx;
89 
90 	/* widgets */
91 	/* folders */
92 	GtkWidget * fo_window;
93 #if GTK_CHECK_VERSION(2, 18, 0)
94 	GtkWidget * fo_infobar;
95 	GtkWidget * fo_infobar_label;
96 #endif
97 	GtkTreeStore * fo_store;
98 	GtkWidget * fo_view;
99 	/* headers */
100 	GtkWidget * he_window;
101 	GtkWidget * he_view;
102 	GtkTreeViewColumn * he_view_from;
103 	GtkTreeViewColumn * he_view_to;
104 	/* body */
105 	GtkWidget * bo_window;
106 	GtkWidget * hdr_vbox;
107 	GtkWidget * hdr_subject;
108 	GtkWidget * hdr_from;
109 	GtkWidget * hdr_to;
110 	GtkWidget * hdr_date;
111 	GtkTextBuffer * bo_buffer;
112 	GtkWidget * bo_view;
113 	GtkWidget * statusbar;
114 	gint statusbar_id;
115 	GtkWidget * st_online;
116 	/* plug-ins */
117 	GtkWidget * pl_window;
118 	GtkWidget * pl_view;
119 	GtkListStore * pl_store;
120 	GtkWidget * pl_combo;
121 	GtkWidget * pl_box;
122 	MailerPluginHelper pl_helper;
123 	/* about */
124 	GtkWidget * ab_window;
125 	/* preferences */
126 	GtkWidget * pr_window;
127 	GtkWidget * pr_accounts;
128 	GtkWidget * pr_messages_font;
129 	GtkListStore * pr_plugins_store;
130 	GtkWidget * pr_plugins;
131 };
132 
133 /* FIXME use a more elegant model with an AccountMessage directly */
134 typedef void (*MailerForeachMessageCallback)(Mailer * mailer,
135 		GtkTreeModel * model, GtkTreeIter * iter);
136 
137 
138 /* constants */
139 static const char * _title[3] =
140 {
141 	N_("New account"), N_("Account settings"), N_("Account confirmation")
142 };
143 
144 
145 /* variables */
146 #ifdef EMBEDDED
147 static const DesktopAccel _mailer_accel[] =
148 {
149 	{ G_CALLBACK(on_quit),		GDK_CONTROL_MASK,	GDK_KEY_Q },
150 	{ G_CALLBACK(on_view_source),	GDK_CONTROL_MASK,	GDK_KEY_U },
151 	{ NULL,				0,			0	  }
152 };
153 #endif
154 
155 
156 #ifndef EMBEDDED
157 static const DesktopMenu _menu_file[] =
158 {
159 	{ N_("_New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose",
160 		GDK_CONTROL_MASK, GDK_KEY_N },
161 	{ N_("_Open..."), G_CALLBACK(on_file_open), GTK_STOCK_OPEN,
162 		GDK_CONTROL_MASK, GDK_KEY_O },
163 	{ "", NULL, NULL, 0, 0 },
164 	{ N_("Send / Receive"), G_CALLBACK(on_file_send_receive),
165 		"stock_mail-send-receive", GDK_CONTROL_MASK, GDK_KEY_R },
166 	{ "", NULL, NULL, 0, 0 },
167 	{ N_("_Print"), NULL, GTK_STOCK_PRINT, GDK_CONTROL_MASK, GDK_KEY_P },
168 	{ N_("Print pre_view"), NULL, GTK_STOCK_PRINT_PREVIEW, GDK_CONTROL_MASK,
169 		0 },
170 	{ "", NULL, NULL, 0, 0 },
171 	{ N_("_Quit"), G_CALLBACK(on_file_quit), GTK_STOCK_QUIT,
172 		GDK_CONTROL_MASK, GDK_KEY_Q },
173 	{ NULL, NULL, NULL, 0, 0 }
174 };
175 
176 static const DesktopMenu _menu_edit[] =
177 {
178 	{ N_("_Cut"), G_CALLBACK(on_edit_cut), GTK_STOCK_CUT, GDK_CONTROL_MASK,
179 		GDK_KEY_X },
180 	{ N_("Cop_y"), G_CALLBACK(on_edit_copy), GTK_STOCK_COPY,
181 		GDK_CONTROL_MASK, GDK_KEY_C },
182 	{ N_("_Paste"), G_CALLBACK(on_edit_paste), GTK_STOCK_PASTE,
183 		GDK_CONTROL_MASK, GDK_KEY_V },
184 	{ "", NULL, NULL, 0, 0 },
185 	{ N_("Select _All"), G_CALLBACK(on_edit_select_all),
186 #if GTK_CHECK_VERSION(2, 10, 0)
187 		GTK_STOCK_SELECT_ALL,
188 #else
189 		"edit-select-all",
190 #endif
191 		GDK_CONTROL_MASK, GDK_KEY_A },
192 	{ N_("_Unselect all"), G_CALLBACK(on_edit_unselect_all), NULL, 0, 0 },
193 	{ "", NULL, NULL, 0, 0 },
194 	{ N_("_Preferences"), G_CALLBACK(on_edit_preferences),
195 		GTK_STOCK_PREFERENCES, 0, 0 },
196 	{ NULL, NULL, NULL, 0, 0 }
197 };
198 
199 static const DesktopMenu _menu_message[] =
200 {
201 	{ N_("_Reply"), G_CALLBACK(on_message_reply), "stock_mail-reply", 0,
202 		0 },
203 	{ N_("Reply to _All"), G_CALLBACK(on_message_reply_to_all),
204 		"stock_mail-reply-to-all", 0, 0 },
205 	{ N_("_Forward"), G_CALLBACK(on_message_forward), "stock_mail-forward",
206 		0, 0 },
207 	{ "", NULL, NULL, 0, 0 },
208 	{ N_("Save _As..."), G_CALLBACK(on_message_save_as), GTK_STOCK_SAVE_AS,
209 		GDK_CONTROL_MASK, GDK_KEY_S },
210 	{ "", NULL, NULL, 0, 0 },
211 	{ N_("_Delete"), G_CALLBACK(on_message_delete), GTK_STOCK_DELETE, 0,
212 		GDK_KEY_Delete },
213 	{ "", NULL, NULL, 0, 0 },
214 	{ N_("_View source"), G_CALLBACK(on_message_view_source), NULL,
215 		GDK_CONTROL_MASK, GDK_KEY_U },
216 	{ NULL, NULL, NULL, 0, 0 }
217 };
218 
219 static const DesktopMenu _menu_help[] =
220 {
221 	{ N_("_Contents"), G_CALLBACK(on_help_contents), "help-contents", 0,
222 		GDK_KEY_F1 },
223 #if GTK_CHECK_VERSION(2, 6, 0)
224 	{ N_("_About"), G_CALLBACK(on_help_about), GTK_STOCK_ABOUT, 0, 0 },
225 #else
226 	{ N_("_About"), G_CALLBACK(on_help_about), NULL, 0, 0 },
227 #endif
228 	{ NULL, NULL, NULL, 0, 0 }
229 };
230 
231 static const DesktopMenubar _mailer_menubar[] =
232 {
233 	{ N_("_File"), _menu_file },
234 	{ N_("_Edit"), _menu_edit },
235 	{ N_("_Message"), _menu_message },
236 	{ N_("_Help"), _menu_help },
237 	{ NULL, NULL }
238 };
239 #endif
240 
241 static DesktopToolbar _mailer_fo_toolbar[] =
242 {
243 	{ N_("New message"), G_CALLBACK(on_new_mail), "stock_mail-compose", 0,
244 		0, NULL },
245 #ifdef EMBEDDED
246 	{ N_("Open message"), G_CALLBACK(on_open), GTK_STOCK_OPEN,
247 		GDK_CONTROL_MASK, GDK_KEY_O, NULL },
248 #endif
249 	{ "", NULL, NULL, 0, 0, NULL },
250 	{ N_("Send / Receive"), G_CALLBACK(on_file_send_receive),
251 		"stock_mail-send-receive", 0, 0, NULL },
252 	{ N_("Stop"), NULL, GTK_STOCK_STOP, 0, GDK_KEY_Escape, NULL },
253 	{ "", NULL, NULL, 0, 0, NULL },
254 #ifndef EMBEDDED
255 	{ N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL },
256 	{ N_("Reply to all"), G_CALLBACK(on_reply_to_all),
257 		"stock_mail-reply-to-all", 0, 0, NULL },
258 	{ N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0,
259 		NULL},
260 #endif
261 	{ N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL },
262 	{ N_("Print"), NULL, GTK_STOCK_PRINT, 0, 0, NULL },
263 #ifdef EMBEDDED
264 	{ "", NULL, NULL, 0, 0, NULL },
265 	{ N_("Preferences"), G_CALLBACK(on_preferences), GTK_STOCK_PREFERENCES,
266 		0, 0, NULL },
267 #endif
268 	{ NULL, NULL, NULL, 0, 0, NULL }
269 };
270 
271 #ifdef EMBEDDED
272 static DesktopToolbar _mailer_he_toolbar[] =
273 {
274 	{ N_("New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", 0,
275 		0, NULL },
276 	{ "", NULL, NULL, 0, 0, NULL },
277 	{ N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL },
278 	{ N_("Reply to all"), G_CALLBACK(on_reply_to_all),
279 		"stock_mail-reply-to-all", 0, 0, NULL },
280 	{ N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0,
281 		NULL},
282 	{ N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL },
283 	{ NULL, NULL, NULL, 0, 0, NULL }
284 };
285 
286 static DesktopToolbar _mailer_bo_toolbar[] =
287 {
288 	{ N_("New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", 0,
289 		0, NULL },
290 	{ "", NULL, NULL, 0, 0, NULL },
291 	{ N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL },
292 	{ N_("Reply to all"), G_CALLBACK(on_reply_to_all),
293 		"stock_mail-reply-to-all", 0, 0, NULL },
294 	{ N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0,
295 		NULL},
296 	{ N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL },
297 	{ N_("Print"), NULL, GTK_STOCK_PRINT, 0, 0, NULL },
298 	{ NULL, NULL, NULL, 0, 0, NULL }
299 };
300 #endif
301 
302 
303 /* prototypes */
304 /* accessors */
305 static gboolean _mailer_plugin_is_enabled(Mailer * mailer, char const * plugin);
306 static char const * _mailer_get_font(Mailer * mailer);
307 static char * _mailer_get_config_filename(void);
308 static GList * _mailer_get_selected_headers(Mailer * mailer);
309 
310 /* useful */
311 static int _mailer_config_load_account(Mailer * mailer, char const * name);
312 static gboolean _mailer_confirm(Mailer * mailer, char const * message);
313 static void _mailer_foreach_message_selected(Mailer * mailer,
314 		MailerForeachMessageCallback callback);
315 static void _mailer_refresh_plugin(Mailer * mailer);
316 static void _mailer_refresh_title(Mailer * mailer);
317 static void _mailer_update_status(Mailer * mailer);
318 static void _mailer_update_view(Mailer * mailer);
319 
320 /* callbacks */
321 static void _mailer_on_online_toggled(gpointer data);
322 static void _mailer_on_plugin_combo_changed(gpointer data);
323 
324 
325 /* public */
326 /* functions */
327 /* mailer_new */
328 static int _new_accounts(Mailer * mailer);
329 static GtkWidget * _new_folders_view(Mailer * mailer);
330 static void _on_folders_changed(GtkTreeSelection * selection, gpointer data);
331 static GtkWidget * _new_headers_view(Mailer * mailer);
332 static GtkWidget * _new_headers(Mailer * mailer);
333 static GtkTreeViewColumn * _headers_view_column_icon(GtkTreeView * view,
334 		char const * title, int id, int sortid);
335 static GtkTreeViewColumn * _headers_view_column_text(GtkTreeView * view,
336 		char const * title, int id, int sortid, int boldid);
337 static void _on_headers_changed(GtkTreeSelection * selection, gpointer data);
338 static gboolean _new_idle(gpointer data);
339 static void _idle_config_load(Mailer * mailer);
340 static void _idle_plugins_load(Mailer * mailer);
341 
mailer_new(void)342 Mailer * mailer_new(void)
343 {
344 	Mailer * mailer;
345 	GtkAccelGroup * group;
346 	GtkWidget * vbox;
347 	GtkWidget * vbox2;
348 	GtkWidget * hbox;
349 #ifndef EMBEDDED
350 	GtkWidget * hpaned;
351 	GtkWidget * hpaned2;
352 	GtkWidget * vpaned;
353 #endif
354 	GtkCellRenderer * renderer;
355 	GtkWidget * widget;
356 	char buf[128];
357 
358 	if((mailer = object_new(sizeof(*mailer))) == NULL)
359 	{
360 		error_print("mailer");
361 		return NULL;
362 	}
363 	/* accounts */
364 	_new_accounts(mailer);
365 	mailer->account = NULL;
366 	mailer->account_cnt = 0;
367 	mailer->account_cur = NULL;
368 	mailer->folder_cur = NULL;
369 	mailer->message_cur = NULL;
370 	/* plug-ins */
371 	mailer->pl_helper.mailer = mailer;
372 	mailer->pl_helper.error = mailer_error;
373 	/* ssl */
374 	SSL_load_error_strings();
375 	SSL_library_init();
376 #ifndef OPENSSL_NO_SSL3
377 	if((mailer->ssl_ctx = SSL_CTX_new(SSLv3_client_method())) == NULL
378 #else
379 	if((mailer->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL
380 #endif
381 			|| SSL_CTX_set_cipher_list(mailer->ssl_ctx,
382 				SSL_DEFAULT_CIPHER_LIST) != 1
383 			|| SSL_CTX_load_verify_locations(mailer->ssl_ctx, NULL,
384 				"/etc/openssl/certs") != 1)
385 	{
386 		mailer_error(NULL, ERR_error_string(ERR_get_error(), buf), 1);
387 		if(mailer->ssl_ctx != NULL)
388 			SSL_CTX_free(mailer->ssl_ctx);
389 		mailer->ssl_ctx = NULL;
390 	}
391 #if 0 /* XXX nicer for the server (knows why we shutdown) but not for us */
392 	else
393 		SSL_CTX_set_verify(mailer->ssl_ctx, SSL_VERIFY_PEER, NULL);
394 #endif
395 	/* widgets */
396 	group = gtk_accel_group_new();
397 	mailer->fo_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
398 	gtk_window_add_accel_group(GTK_WINDOW(mailer->fo_window), group);
399 	g_object_unref(group);
400 #ifndef EMBEDDED
401 	gtk_window_set_default_size(GTK_WINDOW(mailer->fo_window), 800, 600);
402 	gtk_window_set_title(GTK_WINDOW(mailer->fo_window), PACKAGE);
403 #else
404 	gtk_window_set_default_size(GTK_WINDOW(mailer->fo_window), 200, 300);
405 	gtk_window_set_title(GTK_WINDOW(mailer->fo_window),
406 			_(PACKAGE " - Folders"));
407 #endif
408 #if GTK_CHECK_VERSION(2, 6, 0)
409 	gtk_window_set_icon_name(GTK_WINDOW(mailer->fo_window), "mailer");
410 #endif
411 	g_signal_connect_swapped(G_OBJECT(mailer->fo_window), "delete-event",
412 			G_CALLBACK(on_closex), mailer);
413 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
414 	/* menubar */
415 #ifndef EMBEDDED
416 	widget = desktop_menubar_create(_mailer_menubar, mailer, group);
417 	gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
418 #else
419 	desktop_accel_create(_mailer_accel, mailer, group);
420 #endif
421 	/* toolbar */
422 	widget = desktop_toolbar_create(_mailer_fo_toolbar, mailer, group);
423 	gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
424 #if GTK_CHECK_VERSION(2, 18, 0)
425 	/* infobar */
426 	mailer->fo_infobar = gtk_info_bar_new_with_buttons(GTK_STOCK_CLOSE,
427 			GTK_RESPONSE_CLOSE, NULL);
428 	gtk_info_bar_set_message_type(GTK_INFO_BAR(mailer->fo_infobar),
429 			GTK_MESSAGE_ERROR);
430 	g_signal_connect(mailer->fo_infobar, "close", G_CALLBACK(
431 				gtk_widget_hide), NULL);
432 	g_signal_connect(mailer->fo_infobar, "response", G_CALLBACK(
433 				gtk_widget_hide), NULL);
434 	widget = gtk_info_bar_get_content_area(GTK_INFO_BAR(
435 				mailer->fo_infobar));
436 	mailer->fo_infobar_label = gtk_label_new(NULL);
437 	gtk_widget_show(mailer->fo_infobar_label);
438 	gtk_box_pack_start(GTK_BOX(widget), mailer->fo_infobar_label, TRUE,
439 			TRUE, 0);
440 	gtk_widget_set_no_show_all(mailer->fo_infobar, TRUE);
441 	gtk_box_pack_start(GTK_BOX(vbox), mailer->fo_infobar, FALSE, TRUE, 0);
442 #endif
443 	/* folders */
444 	widget = gtk_scrolled_window_new(NULL, NULL);
445 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
446 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
447 	mailer->fo_view = _new_folders_view(mailer);
448 	gtk_container_add(GTK_CONTAINER(widget), mailer->fo_view);
449 #ifndef EMBEDDED
450 	hpaned = gtk_hpaned_new();
451 	gtk_paned_set_position(GTK_PANED(hpaned), 160);
452 	gtk_paned_add1(GTK_PANED(hpaned), widget);
453 	vpaned = gtk_vpaned_new();
454 	gtk_paned_set_position(GTK_PANED(vpaned), 160);
455 	gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
456 #else
457 	gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
458 #endif
459 	/* statusbar */
460 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
461 	mailer->st_online = gtk_toggle_button_new();
462 	g_signal_connect_swapped(mailer->st_online, "toggled", G_CALLBACK(
463 				_mailer_on_online_toggled), mailer);
464 	/* FIXME set icon and callback */
465 	gtk_box_pack_start(GTK_BOX(hbox), mailer->st_online, FALSE, TRUE, 0);
466 	mailer->statusbar = gtk_statusbar_new();
467 	mailer->statusbar_id = 0;
468 	gtk_box_pack_start(GTK_BOX(hbox), mailer->statusbar, TRUE, TRUE, 0);
469 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
470 	gtk_container_add(GTK_CONTAINER(mailer->fo_window), vbox);
471 	gtk_widget_show_all(vbox);
472 	/* messages list */
473 #ifndef EMBEDDED
474 	mailer->he_window = mailer->fo_window;
475 #else
476 	group = gtk_accel_group_new();
477 	mailer->he_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
478 	gtk_window_add_accel_group(GTK_WINDOW(mailer->he_window), group);
479 	g_object_unref(group);
480 	gtk_window_set_default_size(GTK_WINDOW(mailer->he_window), 200, 300);
481 # if GTK_CHECK_VERSION(2, 6, 0)
482 	gtk_window_set_icon_name(GTK_WINDOW(mailer->he_window), "mailer");
483 # endif
484 	gtk_window_set_title(GTK_WINDOW(mailer->he_window), _("Message list"));
485 	g_signal_connect_swapped(G_OBJECT(mailer->he_window), "delete-event",
486 			G_CALLBACK(on_headers_closex), mailer);
487 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
488 	gtk_container_add(GTK_CONTAINER(mailer->he_window), vbox);
489 	widget = desktop_toolbar_create(_mailer_he_toolbar, mailer, group);
490 	gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
491 #endif
492 	widget = gtk_scrolled_window_new(NULL, NULL);
493 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
494 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
495 	mailer->he_view = _new_headers_view(mailer);
496 	gtk_container_add(GTK_CONTAINER(widget), mailer->he_view);
497 #ifndef EMBEDDED
498 	gtk_paned_add1(GTK_PANED(vpaned), widget);
499 #else
500 	gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
501 #endif
502 	gtk_widget_show_all(vbox);
503 	/* messages body */
504 #ifndef EMBEDDED
505 	mailer->bo_window = mailer->fo_window;
506 #else
507 	group = gtk_accel_group_new();
508 	mailer->bo_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
509 	gtk_window_add_accel_group(GTK_WINDOW(mailer->bo_window), group);
510 	g_object_unref(group);
511 	gtk_window_set_default_size(GTK_WINDOW(mailer->bo_window), 200, 300);
512 # if GTK_CHECK_VERSION(2, 6, 0)
513 	gtk_window_set_icon_name(GTK_WINDOW(mailer->bo_window), "mailer");
514 # endif
515 	gtk_window_set_title(GTK_WINDOW(mailer->bo_window), _("Message"));
516 	g_signal_connect_swapped(G_OBJECT(mailer->bo_window), "delete-event",
517 			G_CALLBACK(on_body_closex), mailer);
518 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
519 	gtk_container_add(GTK_CONTAINER(mailer->bo_window), vbox);
520 	widget = desktop_toolbar_create(_mailer_bo_toolbar, mailer, group);
521 	gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
522 #endif
523 	vbox2 = _new_headers(mailer);
524 	mailer->bo_buffer = gtk_text_buffer_new(NULL);
525 	mailer->bo_view = gtk_text_view_new_with_buffer(mailer->bo_buffer);
526 	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(mailer->bo_view), FALSE);
527 	gtk_text_view_set_editable(GTK_TEXT_VIEW(mailer->bo_view), FALSE);
528 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(mailer->bo_view),
529 			GTK_WRAP_WORD_CHAR);
530 	widget = gtk_scrolled_window_new(NULL, NULL);
531 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
532 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
533 	gtk_container_add(GTK_CONTAINER(widget), mailer->bo_view);
534 	gtk_box_pack_start(GTK_BOX(vbox2), widget, TRUE, TRUE, 0);
535 #ifndef EMBEDDED
536 	gtk_paned_add2(GTK_PANED(vpaned), vbox2);
537 #else
538 	gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
539 #endif
540 	/* plug-ins */
541 #ifndef EMBEDDED
542 	mailer->pl_window = mailer->fo_window;
543 	mailer->pl_view = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
544 #else
545 	group = gtk_accel_group_new();
546 	mailer->pl_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
547 	gtk_window_add_accel_group(GTK_WINDOW(mailer->pl_window), group);
548 	g_object_unref(group);
549 	gtk_window_set_default_size(GTK_WINDOW(mailer->pl_window), 200, 300);
550 # if GTK_CHECK_VERSION(2, 6, 0)
551 	gtk_window_set_icon_name(GTK_WINDOW(mailer->pl_window), "mailer");
552 # endif
553 	gtk_window_set_title(GTK_WINDOW(mailer->pl_window), _("Plug-ins"));
554 	g_signal_connect_swapped(G_OBJECT(mailer->pl_window), "delete-event",
555 			G_CALLBACK(on_plugins_closex), mailer);
556 	mailer->pl_view = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
557 	gtk_container_add(GTK_CONTAINER(mailer->pl_window), mailer->pl_view);
558 #endif
559 	mailer->pl_store = gtk_list_store_new(MPC_COUNT, G_TYPE_STRING,
560 			G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING,
561 			G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
562 			G_TYPE_POINTER);
563 	mailer->pl_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(
564 				mailer->pl_store));
565 	g_signal_connect_swapped(G_OBJECT(mailer->pl_combo), "changed",
566 			G_CALLBACK(_mailer_on_plugin_combo_changed), mailer);
567 	renderer = gtk_cell_renderer_pixbuf_new();
568 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(mailer->pl_combo), renderer,
569 			FALSE);
570 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(mailer->pl_combo),
571 			renderer, "pixbuf", MPC_ICON, NULL);
572 	renderer = gtk_cell_renderer_text_new();
573 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(mailer->pl_combo),
574 			renderer, TRUE);
575 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(mailer->pl_combo),
576 			renderer, "text", MPC_NAME_DISPLAY, NULL);
577 	gtk_box_pack_start(GTK_BOX(mailer->pl_view), mailer->pl_combo, FALSE,
578 			TRUE, 0);
579 	mailer->pl_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
580 	gtk_box_pack_start(GTK_BOX(mailer->pl_view), mailer->pl_box, TRUE, TRUE,
581 			0);
582 	gtk_widget_set_no_show_all(mailer->pl_view, TRUE);
583 	gtk_container_set_border_width(GTK_CONTAINER(mailer->pl_view), 4);
584 #ifndef EMBEDDED
585 	hpaned2 = gtk_hpaned_new();
586 	gtk_paned_pack1(GTK_PANED(hpaned2), vpaned, TRUE, TRUE);
587 	gtk_paned_pack2(GTK_PANED(hpaned2), mailer->pl_view, FALSE, TRUE);
588 	gtk_paned_add2(GTK_PANED(hpaned), hpaned2);
589 #endif
590 	gtk_widget_show_all(vbox);
591 	/* widgets */
592 	mailer->ab_window = NULL;
593 	mailer->pr_window = NULL;
594 	/* show window */
595 	gtk_widget_hide(mailer->hdr_vbox);
596 	_mailer_update_status(mailer);
597 	gtk_widget_show(mailer->pl_window);
598 	gtk_widget_show(mailer->fo_window);
599 	/* load configuration */
600 	mailer->source = g_idle_add(_new_idle, mailer);
601 	return mailer;
602 }
603 
_new_accounts(Mailer * mailer)604 static int _new_accounts(Mailer * mailer)
605 {
606 	int ret = 0;
607 	char * dirname;
608 	DIR * dir;
609 	struct dirent * de;
610 	size_t len;
611 #ifdef __APPLE__
612 	char const ext[] = ".dylib";
613 #else
614 	char const ext[] = ".so";
615 #endif
616 	Account ** p;
617 
618 	mailer->available = NULL;
619 	mailer->available_cnt = 0;
620 	if((dirname = string_new_append(PLUGINDIR, "/account", NULL)) == NULL)
621 		return -1;
622 	if((dir = opendir(dirname)) == NULL)
623 	{
624 		error_set_code(1, "%s: %s", dirname, strerror(errno));
625 		string_delete(dirname);
626 		return error_print("mailer");
627 	}
628 	for(de = readdir(dir); de != NULL; de = readdir(dir))
629 	{
630 		if((len = strlen(de->d_name)) < sizeof(ext)
631 				|| strcmp(ext, &de->d_name[
632 					len - sizeof(ext) + 1]) != 0)
633 			continue;
634 		de->d_name[len - sizeof(ext) + 1] = '\0';
635 		if((p = realloc(mailer->available, (mailer->available_cnt + 1)
636 						* sizeof(*p))) == NULL)
637 		{
638 			error_set_print("mailer", 1, "%s", strerror(errno));
639 			continue;
640 		}
641 		mailer->available = p;
642 		if((p[mailer->available_cnt] = account_new(NULL, de->d_name,
643 						NULL, NULL)) == NULL)
644 		{
645 			error_print("mailer");
646 			continue;
647 		}
648 		mailer->available_cnt++;
649 	}
650 	if(closedir(dir) != 0)
651 		ret = error_set_print("mailer", 1, "%s: %s", dirname, strerror(
652 					errno));
653 	string_delete(dirname);
654 	return ret;
655 }
656 
_new_folders_view(Mailer * mailer)657 static GtkWidget * _new_folders_view(Mailer * mailer)
658 {
659 	GtkWidget * widget;
660 	GtkCellRenderer * renderer;
661 	GtkTreeSelection * treesel;
662 
663 	mailer->fo_store = gtk_tree_store_new(MFC_COUNT, G_TYPE_POINTER,
664 			G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER,
665 			GDK_TYPE_PIXBUF, G_TYPE_STRING);
666 	widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mailer->fo_store));
667 	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(widget), FALSE);
668 	renderer = gtk_cell_renderer_pixbuf_new();
669 	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), -1,
670 			NULL, renderer, "pixbuf", MFC_ICON, NULL);
671 	renderer = gtk_cell_renderer_text_new();
672 	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), -1,
673 			_("Folders"), renderer, "text", MFC_NAME, NULL);
674 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
675 	g_signal_connect(G_OBJECT(treesel), "changed", G_CALLBACK(
676 				_on_folders_changed), mailer);
677 	return widget;
678 }
679 
_on_folders_changed(GtkTreeSelection * selection,gpointer data)680 static void _on_folders_changed(GtkTreeSelection * selection, gpointer data)
681 {
682 	Mailer * mailer = data;
683 	GtkTreeModel * model;
684 	GtkTreeIter iter;
685 	GtkTreePath * path;
686 	gboolean sent;
687 
688 #ifdef DEBUG
689 	fprintf(stderr, "DEBUG: %s()\n", __func__);
690 #endif
691 	mailer->message_cur = NULL;
692 	if(gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
693 	{
694 		mailer->account_cur = NULL;
695 		mailer->folder_cur = NULL;
696 		gtk_tree_view_set_model(GTK_TREE_VIEW(mailer->he_view), NULL);
697 		_mailer_update_status(mailer);
698 		return;
699 	}
700 	/* get current folder */
701 	gtk_tree_model_get(model, &iter, MFC_FOLDER, &mailer->folder_cur, -1);
702 	/* get current account */
703 	path = gtk_tree_model_get_path(model, &iter);
704 	while(gtk_tree_path_get_depth(path) > 1 && gtk_tree_path_up(path));
705 	gtk_tree_model_get_iter(model, &iter, path);
706 	gtk_tree_model_get(model, &iter, MFC_ACCOUNT, &mailer->account_cur, -1);
707 	gtk_tree_path_free(path);
708 	/* display relevant columns */
709 	sent = (mailer->folder_cur != NULL && folder_get_type(
710 				mailer->folder_cur) == FT_SENT) ? TRUE : FALSE;
711 	gtk_tree_view_column_set_visible(mailer->he_view_from, !sent);
712 	gtk_tree_view_column_set_visible(mailer->he_view_to, sent);
713 	_mailer_update_view(mailer);
714 #ifdef EMBEDDED
715 	if(model != NULL)
716 		gtk_window_present(GTK_WINDOW(mailer->he_window));
717 #endif
718 }
719 
_new_headers_view(Mailer * mailer)720 static GtkWidget * _new_headers_view(Mailer * mailer)
721 {
722 	GtkWidget * widget;
723 	GtkTreeView * treeview;
724 	GtkTreeSelection * treesel;
725 
726 	widget = gtk_tree_view_new();
727 	treeview = GTK_TREE_VIEW(widget);
728 	gtk_tree_view_set_reorderable(treeview, FALSE);
729 	gtk_tree_view_set_rules_hint(treeview, TRUE);
730 	_headers_view_column_icon(treeview, "", MHC_ICON, MHC_READ);
731 	_headers_view_column_text(treeview, _("Subject"), MHC_SUBJECT,
732 			MHC_SUBJECT, MHC_WEIGHT);
733 	mailer->he_view_from = _headers_view_column_text(treeview, _("From"),
734 			MHC_FROM, MHC_FROM, MHC_WEIGHT);
735 	mailer->he_view_to = _headers_view_column_text(treeview, _("To"),
736 			MHC_TO, MHC_TO, MHC_WEIGHT);
737 	_headers_view_column_text(treeview, _("Date"), MHC_DATE_DISPLAY,
738 			MHC_DATE, MHC_WEIGHT);
739 	treesel = gtk_tree_view_get_selection(treeview);
740 	gtk_tree_selection_set_mode(treesel, GTK_SELECTION_MULTIPLE);
741 	g_signal_connect(G_OBJECT(treesel), "changed", G_CALLBACK(
742 				_on_headers_changed), mailer);
743 	return widget;
744 }
745 
_on_headers_changed(GtkTreeSelection * selection,gpointer data)746 static void _on_headers_changed(GtkTreeSelection * selection, gpointer data)
747 {
748 	Mailer * mailer = data;
749 	GtkTreeModel * model;
750 	GList * sel;
751 	GtkTreeIter iter;
752 	gchar * p;
753 	gchar * q;
754 	gchar * r;
755 	Message * message;
756 
757 #ifdef DEBUG
758 	fprintf(stderr, "DEBUG: %s()\n", __func__);
759 #endif
760 	sel = gtk_tree_selection_get_selected_rows(selection, &model);
761 	if(sel == NULL || sel->next != NULL) /* empty or multiple */
762 	{
763 		mailer->message_cur = NULL;
764 		gtk_widget_hide(mailer->hdr_vbox);
765 		gtk_text_view_set_buffer(GTK_TEXT_VIEW(mailer->bo_view),
766 				mailer->bo_buffer);
767 		g_list_foreach(sel, (GFunc)gtk_tree_path_free, NULL);
768 		g_list_free(sel);
769 		return;
770 	}
771 	gtk_tree_model_get_iter(model, &iter, sel->data);
772 	gtk_tree_model_get(model, &iter, MHC_MESSAGE, &message, -1);
773 	mailer->message_cur = message;
774 	gtk_tree_model_get(model, &iter, MHC_SUBJECT, &p, -1);
775 	gtk_label_set_text(GTK_LABEL(mailer->hdr_subject), p);
776 	g_free(p);
777 	gtk_tree_model_get(model, &iter, MHC_FROM, &p, MHC_FROM_EMAIL, &q, -1);
778 	if(q == NULL || strlen(q) == 0 || strcmp(p, q) == 0)
779 		r = g_strdup(p);
780 	else
781 		r = g_strdup_printf("%s <%s>", p, q);
782 	gtk_label_set_text(GTK_LABEL(mailer->hdr_from), r);
783 	g_free(p);
784 	g_free(q);
785 	g_free(r);
786 	gtk_tree_model_get(model, &iter, MHC_TO, &p, MHC_TO_EMAIL, &q, -1);
787 	if(q == NULL || strlen(q) == 0 || strcmp(p, q) == 0)
788 		r = g_strdup(p);
789 	else
790 		r = g_strdup_printf("%s <%s>", p, q);
791 	gtk_label_set_text(GTK_LABEL(mailer->hdr_to), r);
792 	g_free(p);
793 	g_free(q);
794 	g_free(r);
795 	gtk_tree_model_get(model, &iter, MHC_DATE_DISPLAY, &p, -1);
796 	gtk_label_set_text(GTK_LABEL(mailer->hdr_date), p);
797 	g_free(p);
798 	message_set_read(message, TRUE);
799 	gtk_widget_show(mailer->hdr_vbox);
800 	_mailer_update_view(mailer);
801 	g_list_foreach(sel, (GFunc)gtk_tree_path_free, NULL);
802 	g_list_free(sel);
803 #ifdef EMBEDDED
804 	gtk_window_present(GTK_WINDOW(mailer->bo_window));
805 #endif
806 }
807 
_headers_view_column_icon(GtkTreeView * view,char const * title,int id,int sortid)808 static GtkTreeViewColumn * _headers_view_column_icon(GtkTreeView * view,
809 		char const * title, int id, int sortid)
810 {
811 	GtkCellRenderer * renderer;
812 	GtkTreeViewColumn * column;
813 
814 	renderer = gtk_cell_renderer_pixbuf_new();
815 	column = gtk_tree_view_column_new_with_attributes(title, renderer,
816 			"pixbuf", id, NULL);
817 	gtk_tree_view_column_set_sort_column_id(column, sortid);
818 	gtk_tree_view_append_column(view, column);
819 	return column;
820 }
821 
_headers_view_column_text(GtkTreeView * view,char const * title,int id,int sortid,int weightid)822 static GtkTreeViewColumn * _headers_view_column_text(GtkTreeView * view,
823 		char const * title, int id, int sortid, int weightid)
824 {
825 	GtkCellRenderer * renderer;
826 	GtkTreeViewColumn * column;
827 
828 	renderer = gtk_cell_renderer_text_new();
829 	g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END,
830 			NULL);
831 	column = gtk_tree_view_column_new_with_attributes(title, renderer,
832 			"text", id, (weightid >= 0) ? "weight" : NULL, weightid,
833 			NULL);
834 #if GTK_CHECK_VERSION(2, 4, 0)
835 	gtk_tree_view_column_set_expand(column, TRUE);
836 #endif
837 	gtk_tree_view_column_set_resizable(column, TRUE);
838 	gtk_tree_view_column_set_sort_column_id(column, sortid);
839 	gtk_tree_view_append_column(view, column);
840 	return column;
841 }
842 
_new_headers(Mailer * mailer)843 static GtkWidget * _new_headers(Mailer * mailer)
844 {
845 	struct
846 	{
847 		char * hdr;
848 		GtkWidget ** widget;
849 	} widgets[] =
850 	{
851 		{ N_(" Subject: "),	NULL	},
852 		{ N_(" From: "),	NULL	},
853 		{ N_(" To: "),		NULL	},
854 		{ N_(" Date: "),	NULL	},
855 		{ NULL,			NULL	}
856 	};
857 	int i;
858 	GtkWidget * vbox;
859 	GtkWidget * hbox;
860 	GtkWidget * widget;
861 	GtkSizeGroup * group;
862 	PangoFontDescription * bold;
863 
864 	widgets[0].widget = &mailer->hdr_subject;
865 	widgets[1].widget = &mailer->hdr_from;
866 	widgets[2].widget = &mailer->hdr_to;
867 	widgets[3].widget = &mailer->hdr_date;
868 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
869 	mailer->hdr_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
870 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
871 	bold = pango_font_description_new();
872 	pango_font_description_set_weight(bold, PANGO_WEIGHT_BOLD);
873 	for(i = 0; widgets[i].hdr != NULL; i++)
874 	{
875 		hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
876 		widget = gtk_label_new(_(widgets[i].hdr));
877 		gtk_widget_override_font(widget, bold);
878 #if GTK_CHECK_VERSION(3, 14, 0)
879 		g_object_set(widget, "halign", GTK_ALIGN_END,
880 				"valign", GTK_ALIGN_START, NULL);
881 #else
882 		gtk_misc_set_alignment(GTK_MISC(widget), 1.0, 0.0);
883 #endif
884 		gtk_size_group_add_widget(GTK_SIZE_GROUP(group), widget);
885 		gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
886 		widget = gtk_label_new("");
887 		*(widgets[i].widget) = widget;
888 		gtk_label_set_ellipsize(GTK_LABEL(widget),
889 				PANGO_ELLIPSIZE_MIDDLE);
890 #if GTK_CHECK_VERSION(3, 14, 0)
891 		g_object_set(widget, "halign", GTK_ALIGN_START,
892 				"valign", GTK_ALIGN_START, NULL);
893 #else
894 		gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.0);
895 #endif
896 		gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
897 		gtk_box_pack_start(GTK_BOX(mailer->hdr_vbox), hbox, FALSE,
898 				FALSE, 0);
899 	}
900 	gtk_box_pack_start(GTK_BOX(vbox), mailer->hdr_vbox, FALSE, FALSE, 0);
901 	pango_font_description_free(bold);
902 	return vbox;
903 }
904 
_new_idle(gpointer data)905 static gboolean _new_idle(gpointer data)
906 {
907 	Mailer * mailer = data;
908 
909 	_idle_config_load(mailer);
910 	_idle_plugins_load(mailer);
911 	return FALSE;
912 }
913 
_idle_config_load(Mailer * mailer)914 static void _idle_config_load(Mailer * mailer)
915 {
916 	char * filename;
917 	char const * value;
918 	PangoFontDescription * font;
919 	char * p;
920 	char * q;
921 
922 	mailer->source = 0;
923 	if((mailer->config = config_new()) == NULL)
924 		return;
925 	if((filename = _mailer_get_config_filename()) == NULL)
926 		return;
927 	if(config_load(mailer->config, filename) != 0)
928 		mailer_error(NULL, error_get(NULL), 1);
929 	free(filename);
930 	value = _mailer_get_font(mailer);
931 	font = pango_font_description_from_string(value);
932 	gtk_widget_override_font(mailer->bo_view, font);
933 	pango_font_description_free(font);
934 	/* check if we are online */
935 	if((p = config_get(mailer->config, NULL, "online")) == NULL
936 			|| strtol(p, NULL, 10) != 0)
937 		mailer_set_online(mailer, TRUE);
938 	else
939 		mailer_set_online(mailer, FALSE);
940 	/* load accounts */
941 	if((value = config_get(mailer->config, NULL, "accounts")) == NULL
942 			|| value[0] == '\0')
943 		return;
944 	if((p = strdup(value)) == NULL)
945 		return;
946 	value = p;
947 	for(q = p; *q != '\0'; q++)
948 	{
949 		if(*q != ',')
950 			continue;
951 		*q = '\0';
952 		_mailer_config_load_account(mailer, value);
953 		value = q + 1;
954 	}
955 	if(value[0] != '\0')
956 		_mailer_config_load_account(mailer, value);
957 	free(p);
958 }
959 
_idle_plugins_load(Mailer * mailer)960 static void _idle_plugins_load(Mailer * mailer)
961 {
962 	char const * plugins;
963 	char * p;
964 	char * q;
965 	size_t i;
966 
967 	if((plugins = config_get(mailer->config, NULL, "plugins")) == NULL
968 			|| strlen(plugins) == 0)
969 		return;
970 	if((p = strdup(plugins)) == NULL)
971 		return; /* XXX report error */
972 	for(q = p, i = 0;;)
973 	{
974 		if(q[i] == '\0')
975 		{
976 			mailer_load(mailer, q);
977 			break;
978 		}
979 		if(q[i++] != ',')
980 			continue;
981 		q[i - 1] = '\0';
982 		mailer_load(mailer, q);
983 		q += i;
984 		i = 0;
985 	}
986 	free(p);
987 }
988 
989 
990 /* mailer_delete */
991 static void _delete_plugins(Mailer * mailer);
992 
mailer_delete(Mailer * mailer)993 void mailer_delete(Mailer * mailer)
994 {
995 	unsigned int i;
996 
997 	_delete_plugins(mailer);
998 	if(mailer->ssl_ctx != NULL)
999 		SSL_CTX_free(mailer->ssl_ctx);
1000 	if(mailer->source != 0)
1001 		g_source_remove(mailer->source);
1002 	for(i = 0; i < mailer->available_cnt; i++)
1003 		account_delete(mailer->available[i]);
1004 	free(mailer->available);
1005 	for(i = 0; i < mailer->account_cnt; i++)
1006 		account_delete(mailer->account[i]);
1007 	free(mailer->account);
1008 	g_object_unref(mailer->pl_store);
1009 	object_delete(mailer);
1010 }
1011 
_delete_plugins(Mailer * mailer)1012 static void _delete_plugins(Mailer * mailer)
1013 {
1014 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pl_store);
1015 	GtkTreeIter iter;
1016 	gboolean valid;
1017 	MailerPluginDefinition * mpd;
1018 	MailerPlugin * mp;
1019 	Plugin * plugin;
1020 
1021 	for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
1022 			valid = gtk_tree_model_iter_next(model, &iter))
1023 	{
1024 		gtk_tree_model_get(model, &iter, MPC_PLUGIN, &plugin,
1025 				MPC_MAILERPLUGINDEFINITION, &mpd,
1026 				MPC_MAILERPLUGIN, &mp, -1);
1027 		if(mpd->destroy != NULL)
1028 			mpd->destroy(mp);
1029 		plugin_delete(plugin);
1030 	}
1031 }
1032 
1033 
1034 /* accessors */
1035 /* mailer_get_config */
mailer_get_config(Mailer * mailer,char const * variable)1036 char const * mailer_get_config(Mailer * mailer, char const * variable)
1037 {
1038 	return config_get(mailer->config, NULL, variable);
1039 }
1040 
1041 
1042 /* mailer_get_ssl_context */
mailer_get_ssl_context(Mailer * mailer)1043 SSL_CTX * mailer_get_ssl_context(Mailer * mailer)
1044 {
1045 	return mailer->ssl_ctx;
1046 }
1047 
1048 
1049 /* mailer_is_online */
mailer_is_online(Mailer * mailer)1050 gboolean mailer_is_online(Mailer * mailer)
1051 {
1052 	return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1053 				mailer->st_online));
1054 }
1055 
1056 
1057 /* mailer_set_online */
mailer_set_online(Mailer * mailer,gboolean online)1058 void mailer_set_online(Mailer * mailer, gboolean online)
1059 {
1060 	GtkWidget * image;
1061 	size_t i;
1062 
1063 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mailer->st_online),
1064 			online);
1065 	image = gtk_image_new_from_stock(online ? GTK_STOCK_CONNECT
1066 			: GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU);
1067 	gtk_button_set_image(GTK_BUTTON(mailer->st_online), image);
1068 	for(i = 0; i < mailer->account_cnt; i++)
1069 		if(online)
1070 			account_start(mailer->account[i]);
1071 		else
1072 			account_stop(mailer->account[i]);
1073 }
1074 
1075 
1076 /* mailer_set_status */
mailer_set_status(Mailer * mailer,char const * status)1077 void mailer_set_status(Mailer * mailer, char const * status)
1078 {
1079 	GtkStatusbar * sb;
1080 
1081 	if(status == NULL)
1082 	{
1083 		_mailer_update_status(mailer);
1084 		return;
1085 	}
1086 	sb = GTK_STATUSBAR(mailer->statusbar);
1087 	if(mailer->statusbar_id != 0)
1088 		gtk_statusbar_remove(sb, gtk_statusbar_get_context_id(sb, ""),
1089 				mailer->statusbar_id);
1090 	mailer->statusbar_id = gtk_statusbar_push(sb,
1091 			gtk_statusbar_get_context_id(sb, ""), status);
1092 }
1093 
1094 
1095 /* useful */
1096 /* mailer_error */
mailer_error(Mailer * mailer,char const * message,int ret)1097 int mailer_error(Mailer * mailer, char const * message, int ret)
1098 {
1099 #if !GTK_CHECK_VERSION(2, 18, 0)
1100 	GtkWidget * dialog;
1101 #endif
1102 
1103 	if(mailer == NULL)
1104 		return error_set_print("mailer", ret, "%s", message);
1105 #if GTK_CHECK_VERSION(2, 18, 0)
1106 	/* info bar */
1107 	gtk_label_set_text(GTK_LABEL(mailer->fo_infobar_label), message);
1108 	gtk_widget_show(mailer->fo_infobar);
1109 #else
1110 	/* dialog window */
1111 	dialog = gtk_message_dialog_new(GTK_WINDOW(mailer->fo_window),
1112 			GTK_DIALOG_DESTROY_WITH_PARENT,
1113 			GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
1114 # if GTK_CHECK_VERSION(2, 6, 0)
1115 			"%s", _("Error"));
1116 	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1117 # endif
1118 			"%s", message);
1119 	gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
1120 	gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(
1121 				mailer->fo_window));
1122 	g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
1123 				gtk_widget_destroy), NULL);
1124 	gtk_widget_show(dialog);
1125 #endif
1126 	return ret;
1127 }
1128 
1129 
1130 /* mailer_refresh_all */
mailer_refresh_all(Mailer * mailer)1131 void mailer_refresh_all(Mailer * mailer)
1132 {
1133 	size_t i;
1134 
1135 	for(i = 0; i < mailer->account_cnt; i++)
1136 		account_refresh(mailer->account[i]);
1137 }
1138 
1139 
1140 /* mailer_account_add */
mailer_account_add(Mailer * mailer,Account * account)1141 int mailer_account_add(Mailer * mailer, Account * account)
1142 {
1143 	Account ** p;
1144 
1145 #ifdef DEBUG
1146 	fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void *)account);
1147 #endif
1148 	if((p = realloc(mailer->account, sizeof(*p) * (mailer->account_cnt
1149 						+ 1))) == NULL)
1150 		return -mailer_error(mailer, "realloc", 1);
1151 	mailer->account = p;
1152 	mailer->account[mailer->account_cnt] = account;
1153 	mailer->account_cnt++;
1154 	account_store(account, mailer->fo_store);
1155 	/* XXX check (and report) errors */
1156 	if(mailer_is_online(mailer))
1157 		account_start(account);
1158 	return 0;
1159 }
1160 
1161 
1162 #if 0 /* FIXME deprecate? */
1163 /* mailer_account_disable */
1164 int mailer_account_disable(Mailer * mailer, Account * account)
1165 {
1166 	unsigned int i;
1167 
1168 	for(i = 0; i < mailer->account_cnt; i++)
1169 		if(mailer->account[i] == account)
1170 			break;
1171 	if(i == mailer->account_cnt)
1172 		return 1;
1173 	account_set_enabled(account, 0);
1174 	return 0;
1175 }
1176 
1177 
1178 /* mailer_account_enable */
1179 int mailer_account_enable(Mailer * mailer, Account * account)
1180 {
1181 	unsigned int i;
1182 
1183 	for(i = 0; i < mailer->account_cnt; i++)
1184 		if(mailer->account[i] == account)
1185 			break;
1186 	if(i == mailer->account_cnt)
1187 		return 1;
1188 	account_set_enabled(account, 1);
1189 	return 0;
1190 }
1191 #endif
1192 
1193 
1194 /* mailer_compose */
mailer_compose(Mailer * mailer)1195 void mailer_compose(Mailer * mailer)
1196 {
1197 	Compose * compose;
1198 	Account * account;
1199 	char const * title;
1200 	char const * name;
1201 	char const * email;
1202 	char const * organization;
1203 	gchar * p;
1204 
1205 	if((compose = compose_new(mailer->config)) == NULL)
1206 		return; /* XXX report error */
1207 	if((account = mailer->account_cur) == NULL)
1208 	{
1209 		if(mailer->account_cnt == 0)
1210 			return;
1211 		account = mailer->account[0];
1212 	}
1213 	title = account_get_title(account);
1214 	if((name = config_get(mailer->config, title, "identity_name")) != NULL
1215 			&& name[0] == '\0')
1216 		name = NULL;
1217 	if((email = config_get(mailer->config, title, "identity_email")) != NULL
1218 			&& email[0] == '\0')
1219 		email = NULL;
1220 	if((p = g_strdup_printf("%s%s%s%s", (name != NULL) ? name : "",
1221 					(name != NULL && email != NULL) ? " <"
1222 					: "", (email != NULL) ? email : "",
1223 					(name != NULL && email != NULL) ? ">"
1224 					: "")) != NULL)
1225 	{
1226 		compose_set_from(compose, p);
1227 		g_free(p);
1228 	}
1229 	organization = config_get(mailer->config, title,
1230 			"identity_organization");
1231 	if(organization != NULL && organization[0] != '\0')
1232 		compose_set_header(compose, "Organization:", organization,
1233 				TRUE);
1234 }
1235 
1236 
1237 /* mailer_delete_selected */
1238 static void _mailer_delete_selected_foreach(GtkTreeRowReference * reference,
1239 		Mailer * mailer);
1240 
mailer_delete_selected(Mailer * mailer)1241 void mailer_delete_selected(Mailer * mailer)
1242 {
1243 	/* FIXME figure which area is focused first (deleting folders) */
1244 	GtkTreeModel * model;
1245 	GList * selected;
1246 	GList * s;
1247 	GtkTreePath * path;
1248 	GtkTreeRowReference * reference;
1249 
1250 	if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view)))
1251 			== NULL)
1252 		return;
1253 	if((selected = _mailer_get_selected_headers(mailer)) == NULL)
1254 		return;
1255 	/* FIXME move messages to trash first */
1256 	if(_mailer_confirm(mailer, _("The messages selected will be deleted.\n"
1257 					"Continue?")) != TRUE)
1258 	{
1259 		g_list_free(selected);
1260 		return;
1261 	}
1262 	for(s = g_list_first(selected); s != NULL; s = g_list_next(s))
1263 	{
1264 		if((path = s->data) == NULL)
1265 			continue;
1266 		reference = gtk_tree_row_reference_new(model, path);
1267 		s->data = reference;
1268 		gtk_tree_path_free(path);
1269 	}
1270 	g_list_foreach(selected, (GFunc)_mailer_delete_selected_foreach,
1271 			mailer);
1272 	g_list_free(selected);
1273 }
1274 
_mailer_delete_selected_foreach(GtkTreeRowReference * reference,Mailer * mailer)1275 static void _mailer_delete_selected_foreach(GtkTreeRowReference * reference,
1276 		Mailer * mailer)
1277 {
1278 	GtkTreeModel * model;
1279 	GtkTreePath * path;
1280 	GtkTreeIter iter;
1281 	Message * message;
1282 
1283 	if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view)))
1284 			== NULL)
1285 		return;
1286 	if(reference == NULL)
1287 		return;
1288 	if((path = gtk_tree_row_reference_get_path(reference)) == NULL)
1289 		return;
1290 	if(gtk_tree_model_get_iter(model, &iter, path) == TRUE)
1291 		gtk_tree_model_get(model, &iter, MHC_MESSAGE, &message, -1);
1292 	gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1293 	gtk_tree_path_free(path);
1294 }
1295 
1296 
1297 /* mailer_copy */
mailer_copy(Mailer * mailer)1298 void mailer_copy(Mailer * mailer)
1299 {
1300 	GtkTextBuffer * buffer;
1301 	GtkClipboard * clipboard;
1302 
1303 	if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window))
1304 			== mailer->bo_view)
1305 	{
1306 		buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
1307 					mailer->bo_view));
1308 		clipboard = gtk_widget_get_clipboard(mailer->bo_view,
1309 				GDK_SELECTION_CLIPBOARD);
1310 		gtk_text_buffer_copy_clipboard(buffer, clipboard);
1311 	}
1312 	/* FIXME implement the other widgets */
1313 }
1314 
1315 
1316 /* mailer_cut */
mailer_cut(Mailer * mailer)1317 void mailer_cut(Mailer * mailer)
1318 {
1319 	GtkTextBuffer * buffer;
1320 	GtkClipboard * clipboard;
1321 
1322 	if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window))
1323 			== mailer->bo_view) /* XXX copies, really */
1324 	{
1325 		buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
1326 					mailer->bo_view));
1327 		clipboard = gtk_widget_get_clipboard(mailer->bo_view,
1328 				GDK_SELECTION_CLIPBOARD);
1329 		gtk_text_buffer_cut_clipboard(buffer, clipboard, FALSE);
1330 	}
1331 	/* FIXME implement the other widgets */
1332 }
1333 
1334 
1335 /* mailer_load */
mailer_load(Mailer * mailer,char const * plugin)1336 int mailer_load(Mailer * mailer, char const * plugin)
1337 {
1338 	Plugin * p;
1339 	MailerPluginDefinition * mpd;
1340 	MailerPlugin * mp;
1341 	GtkWidget * widget;
1342 	GtkTreeIter iter;
1343 	GtkIconTheme * theme;
1344 	GdkPixbuf * icon = NULL;
1345 
1346 #ifdef DEBUG
1347 	fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, plugin);
1348 #endif
1349 	if(_mailer_plugin_is_enabled(mailer, plugin))
1350 		return 0;
1351 	if((p = plugin_new(LIBDIR, PACKAGE, "plugins", plugin)) == NULL)
1352 		return -mailer_error(NULL, error_get(NULL), 1);
1353 	if((mpd = plugin_lookup(p, "plugin")) == NULL)
1354 	{
1355 		plugin_delete(p);
1356 		return -mailer_error(NULL, error_get(NULL), 1);
1357 	}
1358 	if(mpd->init == NULL || mpd->destroy == NULL
1359 			|| (mp = mpd->init(&mailer->pl_helper)) == NULL)
1360 	{
1361 		plugin_delete(p);
1362 		return -mailer_error(NULL, error_get(NULL), 1);
1363 	}
1364 	theme = gtk_icon_theme_get_default();
1365 	if(mpd->icon != NULL)
1366 		icon = gtk_icon_theme_load_icon(theme, mpd->icon, 24, 0, NULL);
1367 	if(icon == NULL)
1368 		icon = gtk_icon_theme_load_icon(theme, "gnome-settings", 24, 0,
1369 				NULL);
1370 	widget = NULL;
1371 	if(mpd->get_widget != NULL && (widget = mpd->get_widget(mp)) != NULL)
1372 		gtk_widget_hide(widget);
1373 	/* FIXME hide from the list of active plug-ins if there is no widget */
1374 	gtk_list_store_append(mailer->pl_store, &iter);
1375 	gtk_list_store_set(mailer->pl_store, &iter, MPC_NAME, plugin,
1376 			MPC_ICON, icon, MPC_NAME_DISPLAY, mpd->name,
1377 			MPC_PLUGIN, p, MPC_MAILERPLUGINDEFINITION, mpd,
1378 			MPC_MAILERPLUGIN, mp, MPC_WIDGET, widget, -1);
1379 	if(widget == NULL)
1380 		return 0;
1381 	gtk_box_pack_start(GTK_BOX(mailer->pl_box), widget, TRUE, TRUE, 0);
1382 	if(gtk_widget_get_no_show_all(mailer->pl_view) == TRUE)
1383 	{
1384 		gtk_combo_box_set_active(GTK_COMBO_BOX(mailer->pl_combo), 0);
1385 		gtk_widget_set_no_show_all(mailer->pl_view, FALSE);
1386 		gtk_widget_show_all(mailer->pl_view);
1387 	}
1388 	return 0;
1389 }
1390 
1391 
1392 /* mailer_open_selected_source */
1393 static void _open_selected_source(Mailer * mailer, GtkTreeModel * model,
1394 		GtkTreeIter * iter);
1395 
mailer_open_selected_source(Mailer * mailer)1396 void mailer_open_selected_source(Mailer * mailer)
1397 {
1398 	_mailer_foreach_message_selected(mailer, _open_selected_source);
1399 }
1400 
_open_selected_source(Mailer * mailer,GtkTreeModel * model,GtkTreeIter * iter)1401 static void _open_selected_source(Mailer * mailer, GtkTreeModel * model,
1402 		GtkTreeIter * iter)
1403 {
1404 	GtkWidget * window;
1405 	GtkWidget * scrolled;
1406 	PangoFontDescription * font;
1407 	char const * p;
1408 	GtkWidget * widget;
1409 	GtkTextBuffer * tbuf;
1410 
1411 	gtk_tree_model_get(model, iter, MHC_MESSAGE, &mailer->message_cur, -1);
1412 	if(mailer->message_cur == NULL)
1413 		return;
1414 	if((tbuf = account_select_source(mailer->account_cur,
1415 					mailer->folder_cur,
1416 					mailer->message_cur)) == NULL)
1417 		return;
1418 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1419 	gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
1420 	/* XXX choose a better title */
1421 	gtk_window_set_title(GTK_WINDOW(window), _("Mailer - View source"));
1422 	scrolled = gtk_scrolled_window_new(NULL, NULL);
1423 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1424 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1425 	widget = gtk_text_view_new_with_buffer(tbuf);
1426 	if((p = config_get(mailer->config, NULL, "messages_font")) != NULL)
1427 	{
1428 		font = pango_font_description_from_string(p);
1429 		gtk_widget_override_font(widget, font);
1430 		pango_font_description_free(font);
1431 	}
1432 	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(widget), FALSE);
1433 	gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), FALSE);
1434 	gtk_container_add(GTK_CONTAINER(scrolled), widget);
1435 	gtk_container_add(GTK_CONTAINER(window), scrolled);
1436 	gtk_widget_show_all(window);
1437 	/* FIXME count the window */
1438 }
1439 
1440 
1441 /* mailer_paste */
mailer_paste(Mailer * mailer)1442 void mailer_paste(Mailer * mailer)
1443 {
1444 	GtkTextBuffer * buffer;
1445 	GtkClipboard * clipboard;
1446 
1447 	if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window))
1448 			== mailer->bo_view) /* XXX does nothing, really */
1449 	{
1450 		buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
1451 					mailer->bo_view));
1452 		clipboard = gtk_widget_get_clipboard(mailer->bo_view,
1453 				GDK_SELECTION_CLIPBOARD);
1454 		gtk_text_buffer_paste_clipboard(buffer, clipboard, NULL, FALSE);
1455 	}
1456 	/* FIXME implement the other widgets */
1457 }
1458 
1459 
1460 /* mailer_reply_selected */
1461 static void _reply_selected(Mailer * mailer, GtkTreeModel * model,
1462 		GtkTreeIter * iter);
1463 
mailer_reply_selected(Mailer * mailer)1464 void mailer_reply_selected(Mailer * mailer)
1465 {
1466 	_mailer_foreach_message_selected(mailer, _reply_selected);
1467 }
1468 
_reply_selected(Mailer * mailer,GtkTreeModel * model,GtkTreeIter * iter)1469 static void _reply_selected(Mailer * mailer, GtkTreeModel * model,
1470 		GtkTreeIter * iter)
1471 {
1472 	Compose * compose;
1473 	char * date;
1474 	char * from;
1475 	char * subject;
1476 	char * to;
1477 	char * p;
1478 	char const * q;
1479 	GtkTextBuffer * tbuf;
1480 	GtkTextIter start;
1481 	GtkTextIter end;
1482 
1483 	if((compose = compose_new(mailer->config)) == NULL)
1484 		return; /* XXX error message? */
1485 	gtk_tree_model_get(model, iter, MHC_DATE_DISPLAY, &date,
1486 			MHC_FROM_EMAIL, &from, MHC_SUBJECT, &subject,
1487 			MHC_TO_EMAIL, &to, -1);
1488 	/* from */
1489 	if(from != NULL)
1490 		compose_set_header(compose, "To:", from, TRUE);
1491 	/* to */
1492 	if(to != NULL)
1493 		compose_set_from(compose, to);
1494 	/* FIXME also set the In-Reply-To field */
1495 	/* subject */
1496 	q = N_("Re: ");
1497 	if(subject != NULL && strncasecmp(subject, q, strlen(q)) != 0
1498 			&& strncasecmp(subject, _(q), strlen(_(q))) != 0
1499 			&& (p = malloc(strlen(q) + strlen(subject) + 1))
1500 			!= NULL)
1501 	{
1502 		sprintf(p, "%s%s", q, subject);
1503 		free(subject);
1504 		subject = p;
1505 	}
1506 	compose_set_subject(compose, subject);
1507 	/* body */
1508 	compose_set_text(compose, "\nOn ");
1509 	compose_append_text(compose, date);
1510 	compose_append_text(compose, ", ");
1511 	compose_append_text(compose, from);
1512 	compose_append_text(compose, " wrote:\n");
1513 	tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mailer->bo_view));
1514 	for(gtk_text_buffer_get_start_iter(tbuf, &start), end = start;
1515 			gtk_text_iter_is_end(&start) != TRUE; start = end)
1516 	{
1517 		gtk_text_iter_forward_line(&end);
1518 		p = gtk_text_iter_get_text(&start, &end);
1519 		/* stop if we recognize an unquoted signature */
1520 		if(strcmp(p, "-- \n") == 0)
1521 		{
1522 			g_free(p);
1523 			break;
1524 		}
1525 		compose_append_text(compose, (p[0] == '>') ? ">" : "> ");
1526 		compose_append_text(compose, p);
1527 		g_free(p);
1528 	}
1529 	compose_append_signature(compose);
1530 	compose_set_modified(compose, FALSE);
1531 	compose_scroll_to_offset(compose, 0);
1532 	free(date);
1533 	free(from);
1534 	free(subject);
1535 }
1536 
1537 
1538 /* mailer_reply_selected_to_all */
1539 static void _reply_selected_to_all(Mailer * mailer, GtkTreeModel * model,
1540 		GtkTreeIter * iter);
mailer_reply_selected_to_all(Mailer * mailer)1541 void mailer_reply_selected_to_all(Mailer * mailer)
1542 {
1543 	_mailer_foreach_message_selected(mailer, _reply_selected_to_all);
1544 }
1545 
_reply_selected_to_all(Mailer * mailer,GtkTreeModel * model,GtkTreeIter * iter)1546 static void _reply_selected_to_all(Mailer * mailer, GtkTreeModel * model,
1547 		GtkTreeIter * iter)
1548 {
1549 	/* FIXME really implement */
1550 	_reply_selected(mailer, model, iter);
1551 }
1552 
1553 
1554 /* mailer_save_selected */
mailer_save_selected(Mailer * mailer,char const * filename)1555 gboolean mailer_save_selected(Mailer * mailer, char const * filename)
1556 {
1557 	if(mailer->message_cur == NULL)
1558 		return TRUE;
1559 	if(filename == NULL)
1560 		return mailer_save_selected_dialog(mailer);
1561 	return (message_save(mailer->message_cur, filename) == 0)
1562 		? TRUE : FALSE;
1563 }
1564 
1565 
1566 /* mailer_save_selected_dialog */
mailer_save_selected_dialog(Mailer * mailer)1567 gboolean mailer_save_selected_dialog(Mailer * mailer)
1568 {
1569 	gboolean ret;
1570 	GtkWidget * dialog;
1571 	char * filename = NULL;
1572 
1573 	if(mailer->message_cur == NULL)
1574 		return TRUE;
1575 	dialog = gtk_file_chooser_dialog_new(_("Save as..."),
1576 			GTK_WINDOW(mailer->he_window),
1577 			GTK_FILE_CHOOSER_ACTION_SAVE,
1578 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1579 			GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
1580 	if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1581 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
1582 					dialog));
1583 	gtk_widget_destroy(dialog);
1584 	if(filename == NULL)
1585 		return FALSE;
1586 	ret = mailer_save_selected(mailer, filename);
1587 	g_free(filename);
1588 	return ret;
1589 }
1590 
1591 
1592 /* mailer_select_all */
mailer_select_all(Mailer * mailer)1593 void mailer_select_all(Mailer * mailer)
1594 {
1595 	GtkTreeSelection * treesel;
1596 #if GTK_CHECK_VERSION(2, 4, 0)
1597 	GtkTextBuffer * tbuf;
1598 	GtkTextIter start;
1599 	GtkTextIter end;
1600 #endif
1601 
1602 #if GTK_CHECK_VERSION(2, 4, 0)
1603 	if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window))
1604 			== mailer->bo_view)
1605 	{
1606 		tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mailer->bo_view));
1607 		gtk_text_buffer_get_start_iter(tbuf, &start);
1608 		gtk_text_buffer_get_end_iter(tbuf, &end);
1609 		gtk_text_buffer_select_range(tbuf, &start, &end);
1610 		return;
1611 	}
1612 #endif
1613 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mailer->he_view));
1614 	gtk_tree_selection_select_all(treesel);
1615 }
1616 
1617 
1618 /* messages */
mailer_message_open(Mailer * mailer,char const * filename)1619 gboolean mailer_message_open(Mailer * mailer, char const * filename)
1620 {
1621 	Message * message;
1622 	Compose * compose;
1623 
1624 	if(filename == NULL)
1625 		return mailer_message_open_dialog(mailer);
1626 	if((message = message_new_open(mailer, filename)) == NULL)
1627 	{
1628 		mailer_error(mailer, error_get(NULL), 1);
1629 		return FALSE;
1630 	}
1631 	compose = compose_new_open(mailer->config, message);
1632 	message_delete(message);
1633 	return (compose != NULL) ? TRUE : FALSE;
1634 }
1635 
1636 
1637 /* mailer_message_open_dialog */
mailer_message_open_dialog(Mailer * mailer)1638 gboolean mailer_message_open_dialog(Mailer * mailer)
1639 {
1640 	gboolean ret;
1641 	GtkWidget * dialog;
1642 	char * filename = NULL;
1643 
1644 	dialog = gtk_file_chooser_dialog_new(_("Open..."),
1645 			GTK_WINDOW(mailer->he_window),
1646 			GTK_FILE_CHOOSER_ACTION_OPEN,
1647 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1648 			GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
1649 	if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1650 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
1651 					dialog));
1652 	gtk_widget_destroy(dialog);
1653 	if(filename == NULL)
1654 		return FALSE;
1655 	ret = mailer_message_open(mailer, filename);
1656 	g_free(filename);
1657 	return ret;
1658 }
1659 
1660 
1661 /* mailer_show_about */
1662 static gboolean _about_on_closex(gpointer data);
1663 
mailer_show_about(Mailer * mailer,gboolean show)1664 void mailer_show_about(Mailer * mailer, gboolean show)
1665 {
1666 	GtkWidget * dialog;
1667 
1668 	if(mailer->ab_window != NULL)
1669 	{
1670 		if(show)
1671 			gtk_window_present(GTK_WINDOW(mailer->ab_window));
1672 		else
1673 			gtk_widget_hide(mailer->ab_window);
1674 		return;
1675 	}
1676 	dialog = desktop_about_dialog_new();
1677 	mailer->ab_window = dialog;
1678 	g_signal_connect_swapped(G_OBJECT(dialog), "delete-event", G_CALLBACK(
1679 				_about_on_closex), mailer);
1680 	gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(
1681 				mailer->fo_window));
1682 	desktop_about_dialog_set_name(dialog, PACKAGE);
1683 	desktop_about_dialog_set_version(dialog, VERSION);
1684 	desktop_about_dialog_set_authors(dialog, _authors);
1685 	desktop_about_dialog_set_comments(dialog, _comments);
1686 	desktop_about_dialog_set_copyright(dialog, _copyright);
1687 	desktop_about_dialog_set_license(dialog, _license);
1688 	desktop_about_dialog_set_logo_icon_name(dialog, "mailer");
1689 	desktop_about_dialog_set_translator_credits(dialog,
1690 			_("translator-credits"));
1691 	desktop_about_dialog_set_website(dialog, _website);
1692 	gtk_widget_show(dialog);
1693 }
1694 
_about_on_closex(gpointer data)1695 static gboolean _about_on_closex(gpointer data)
1696 {
1697 	Mailer * mailer = data;
1698 
1699 	gtk_widget_hide(mailer->ab_window);
1700 	return TRUE;
1701 }
1702 
1703 
1704 /* mailer_show_body */
mailer_show_body(Mailer * mailer,gboolean show)1705 void mailer_show_body(Mailer * mailer, gboolean show)
1706 {
1707 	if(show == TRUE)
1708 		gtk_window_present(GTK_WINDOW(mailer->bo_window));
1709 	else
1710 		gtk_widget_hide(mailer->bo_window);
1711 }
1712 
1713 
1714 /* mailer_show_headers */
mailer_show_headers(Mailer * mailer,gboolean show)1715 void mailer_show_headers(Mailer * mailer, gboolean show)
1716 {
1717 	if(show == TRUE)
1718 		gtk_window_present(GTK_WINDOW(mailer->he_window));
1719 	else
1720 		gtk_widget_hide(mailer->he_window);
1721 }
1722 
1723 
1724 /* mailer_show_plugins */
mailer_show_plugins(Mailer * mailer,gboolean show)1725 void mailer_show_plugins(Mailer * mailer, gboolean show)
1726 {
1727 	if(show == TRUE)
1728 		gtk_window_present(GTK_WINDOW(mailer->pl_window));
1729 	else
1730 		gtk_widget_hide(mailer->pl_window);
1731 }
1732 
1733 
1734 /* mailer_show_preferences */
1735 typedef enum _AccountColumn
1736 {
1737 	AC_DATA = 0,
1738 	AC_ACTIVE,
1739 	AC_ENABLED,
1740 	AC_TITLE,
1741 	AC_TYPE,
1742 	AC_WIDGET
1743 } AccountColumn;
1744 #define AC_LAST AC_WIDGET
1745 #define AC_COUNT (AC_LAST + 1)
1746 
1747 static void _preferences_accounts(Mailer * mailer, GtkWidget * notebook);
1748 static void _preferences_display(Mailer * mailer, GtkWidget * notebook);
1749 static void _preferences_plugins(Mailer * mailer, GtkWidget * notebook);
1750 static void _preferences_set(Mailer * mailer);
1751 static void _preferences_set_plugins(Mailer * mailer);
1752 
1753 /* callbacks */
1754 static gboolean _on_preferences_closex(gpointer data);
1755 static void _on_preferences_response(GtkWidget * widget, gint response,
1756 		gpointer data);
1757 static void _on_preferences_account_delete(gpointer data);
1758 static void _on_preferences_account_edit(gpointer data);
1759 static void _on_preferences_account_move_down(gpointer data);
1760 static void _on_preferences_account_move_up(gpointer data);
1761 static void _on_preferences_account_new(gpointer data);
1762 static void _on_preferences_account_toggle(GtkCellRendererToggle * renderer,
1763 		char * path, gpointer data);
1764 static void _on_preferences_cancel(gpointer data);
1765 static void _on_preferences_ok(gpointer data);
1766 static int _preferences_ok_accounts(Mailer * mailer);
1767 static int _preferences_ok_display(Mailer * mailer);
1768 static int _preferences_ok_plugins(Mailer * mailer);
1769 static int _preferences_ok_save(Mailer * mailer);
1770 static void _preferences_on_plugin_toggled(GtkCellRendererToggle * renderer,
1771 		char * path, gpointer data);
1772 
mailer_show_preferences(Mailer * mailer,gboolean show)1773 void mailer_show_preferences(Mailer * mailer, gboolean show)
1774 {
1775 	GtkWidget * vbox;
1776 	GtkWidget * notebook;
1777 
1778 	if(mailer->pr_window != NULL)
1779 	{
1780 		if(show)
1781 			gtk_window_present(GTK_WINDOW(mailer->pr_window));
1782 		else
1783 			gtk_widget_hide(mailer->pr_window);
1784 		return;
1785 	}
1786 	mailer->pr_window = gtk_dialog_new_with_buttons(
1787 			_("Mailer preferences"), GTK_WINDOW(mailer->fo_window),
1788 			GTK_DIALOG_DESTROY_WITH_PARENT,
1789 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1790 			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1791 #ifndef EMBEDDED
1792 	gtk_window_set_default_size(GTK_WINDOW(mailer->pr_window), 400, 300);
1793 #else
1794 	gtk_window_set_default_size(GTK_WINDOW(mailer->pr_window), 200, 300);
1795 #endif
1796 	g_signal_connect_swapped(G_OBJECT(mailer->pr_window), "delete-event",
1797 			G_CALLBACK(_on_preferences_closex), mailer);
1798 	g_signal_connect(G_OBJECT(mailer->pr_window), "response",
1799 			G_CALLBACK(_on_preferences_response), mailer);
1800 #if GTK_CHECK_VERSION(2, 14, 0)
1801 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(mailer->pr_window));
1802 #else
1803 	vbox = GTK_DIALOG(mailer->pr_window)->vbox;
1804 #endif
1805 	notebook = gtk_notebook_new();
1806 	/* accounts */
1807 	_preferences_accounts(mailer, notebook);
1808 	/* display */
1809 	_preferences_display(mailer, notebook);
1810 	/* plug-ins */
1811 	_preferences_plugins(mailer, notebook);
1812 	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
1813 	_preferences_set(mailer);
1814 	gtk_widget_show_all(vbox);
1815 	if(show)
1816 		gtk_widget_show(mailer->pr_window);
1817 	else
1818 		gtk_widget_hide(mailer->pr_window);
1819 }
1820 
_preferences_accounts(Mailer * mailer,GtkWidget * notebook)1821 static void _preferences_accounts(Mailer * mailer, GtkWidget * notebook)
1822 {
1823 	GtkWidget * vbox2;
1824 	GtkWidget * vbox3;
1825 	GtkWidget * hbox;
1826 	GtkWidget * widget;
1827 	GtkListStore * store;
1828 	GtkCellRenderer * renderer;
1829 	size_t i;
1830 	Account * ac;
1831 	GtkTreeIter iter;
1832 
1833 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1834 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
1835 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1836 	widget = gtk_scrolled_window_new(NULL, NULL);
1837 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget),
1838 			GTK_SHADOW_IN);
1839 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
1840 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1841 	store = gtk_list_store_new(AC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN,
1842 			G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
1843 			G_TYPE_POINTER);
1844 	for(i = 0; i < mailer->account_cnt; i++)
1845 	{
1846 		ac = mailer->account[i];
1847 		gtk_list_store_append(store, &iter);
1848 		gtk_list_store_set(store, &iter, AC_DATA, ac, AC_ACTIVE, TRUE,
1849 				AC_ENABLED, account_get_enabled(ac),
1850 				AC_TITLE, account_get_title(ac),
1851 				AC_TYPE, account_get_type(ac), -1);
1852 	}
1853 	mailer->pr_accounts = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
1854 				store));
1855 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mailer->pr_accounts),
1856 			TRUE);
1857 	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(mailer->pr_accounts),
1858 			FALSE);
1859 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mailer->pr_accounts), TRUE);
1860 	renderer = gtk_cell_renderer_toggle_new();
1861 	g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(
1862 				_on_preferences_account_toggle), store);
1863 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts),
1864 			gtk_tree_view_column_new_with_attributes(_("Enabled"),
1865 				renderer, "active", AC_ENABLED, NULL));
1866 	renderer = gtk_cell_renderer_text_new();
1867 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts),
1868 			gtk_tree_view_column_new_with_attributes(_("Name"),
1869 				renderer, "text", AC_TITLE, NULL));
1870 	renderer = gtk_cell_renderer_text_new();
1871 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts),
1872 			gtk_tree_view_column_new_with_attributes(_("Type"),
1873 				renderer, "text", AC_TYPE, NULL));
1874 	gtk_container_add(GTK_CONTAINER(widget), mailer->pr_accounts);
1875 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
1876 	vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1877 	/* new account */
1878 	widget = gtk_button_new_from_stock(GTK_STOCK_NEW);
1879 	g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
1880 				_on_preferences_account_new), mailer);
1881 	gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0);
1882 	/* edit account */
1883 #if GTK_CHECK_VERSION(2, 6, 0)
1884 	widget = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1885 #else
1886 	widget = gtk_button_new_with_mnemonic("_Edit");
1887 #endif
1888 	g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
1889 				_on_preferences_account_edit), mailer);
1890 	gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0);
1891 	/* delete account */
1892 	widget = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1893 	g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
1894 				_on_preferences_account_delete), mailer);
1895 	gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0);
1896 	/* move up */
1897 	widget = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
1898 	g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
1899 				_on_preferences_account_move_up), mailer);
1900 	gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0);
1901 	/* move down */
1902 	widget = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
1903 	g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
1904 				_on_preferences_account_move_down), mailer);
1905 	gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0);
1906 	gtk_box_pack_start(GTK_BOX(hbox), vbox3, FALSE, TRUE, 0);
1907 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0);
1908 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
1909 				_("Accounts")));
1910 }
1911 
_preferences_display(Mailer * mailer,GtkWidget * notebook)1912 static void _preferences_display(Mailer * mailer, GtkWidget * notebook)
1913 {
1914 	GtkSizeGroup * group;
1915 	GtkWidget * vbox2;
1916 	GtkWidget * hbox;
1917 	GtkWidget * widget;
1918 
1919 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1920 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1921 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
1922 	/* default font */
1923 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1924 	widget = gtk_label_new(_("Messages font:"));
1925 #if GTK_CHECK_VERSION(3, 14, 0)
1926 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1927 #else
1928 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1929 #endif
1930 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1931 	mailer->pr_messages_font = gtk_font_button_new();
1932 	widget = mailer->pr_messages_font;
1933 	gtk_size_group_add_widget(group, widget);
1934 	gtk_font_button_set_use_font(GTK_FONT_BUTTON(widget), TRUE);
1935 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
1936 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
1937 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
1938 				_("Display")));
1939 }
1940 
_preferences_plugins(Mailer * mailer,GtkWidget * notebook)1941 static void _preferences_plugins(Mailer * mailer, GtkWidget * notebook)
1942 {
1943 	GtkWidget * vbox;
1944 	GtkWidget * widget;
1945 	GtkCellRenderer * renderer;
1946 	GtkTreeViewColumn * column;
1947 
1948 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1949 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1950 	widget = gtk_scrolled_window_new(NULL, NULL);
1951 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget),
1952 			GTK_SHADOW_IN);
1953 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
1954 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1955 	mailer->pr_plugins_store = gtk_list_store_new(4, G_TYPE_STRING,
1956 			G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
1957 	mailer->pr_plugins = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
1958 				mailer->pr_plugins_store));
1959 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mailer->pr_plugins),
1960 			FALSE);
1961 	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(mailer->pr_plugins), FALSE);
1962 	renderer = gtk_cell_renderer_toggle_new();
1963 	g_signal_connect(renderer, "toggled", G_CALLBACK(
1964 				_preferences_on_plugin_toggled), mailer);
1965 	column = gtk_tree_view_column_new_with_attributes(_("Enabled"),
1966 			renderer, "active", 1, NULL);
1967 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_plugins), column);
1968 	column = gtk_tree_view_column_new_with_attributes(NULL,
1969 			gtk_cell_renderer_pixbuf_new(), "pixbuf", 2, NULL);
1970 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_plugins), column);
1971 	column = gtk_tree_view_column_new_with_attributes(_("Name"),
1972 			gtk_cell_renderer_text_new(), "text", 3, NULL);
1973 	gtk_tree_view_column_set_sort_column_id(column, 3);
1974 	gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_plugins), column);
1975 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
1976 				mailer->pr_plugins_store), 3,
1977 			GTK_SORT_ASCENDING);
1978 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mailer->pr_plugins), TRUE);
1979 	gtk_container_add(GTK_CONTAINER(widget), mailer->pr_plugins);
1980 	gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
1981 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, gtk_label_new(
1982 				_("Plug-ins")));
1983 }
1984 
_preferences_set(Mailer * mailer)1985 static void _preferences_set(Mailer * mailer)
1986 {
1987 	char const * p;
1988 
1989 	p = _mailer_get_font(mailer);
1990 	gtk_font_button_set_font_name(GTK_FONT_BUTTON(mailer->pr_messages_font),
1991 			p);
1992 	_preferences_set_plugins(mailer);
1993 }
1994 
_preferences_set_plugins(Mailer * mailer)1995 static void _preferences_set_plugins(Mailer * mailer)
1996 {
1997 	DIR * dir;
1998 	struct dirent * de;
1999 	GtkIconTheme * theme;
2000 #ifdef __APPLE__
2001 	char const ext[] = ".dylib";
2002 #else
2003 	char const ext[] = ".so";
2004 #endif
2005 	size_t len;
2006 	Plugin * p;
2007 	MailerPluginDefinition * mpd;
2008 	GtkTreeIter iter;
2009 	gboolean enabled;
2010 	GdkPixbuf * icon;
2011 
2012 	gtk_list_store_clear(mailer->pr_plugins_store);
2013 	if((dir = opendir(LIBDIR "/" PACKAGE "/plugins")) == NULL)
2014 		return;
2015 	theme = gtk_icon_theme_get_default();
2016 	while((de = readdir(dir)) != NULL)
2017 	{
2018 		if((len = strlen(de->d_name)) < sizeof(ext))
2019 			continue;
2020 		if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0)
2021 			continue;
2022 		de->d_name[len - sizeof(ext) + 1] = '\0';
2023 #ifdef DEBUG
2024 		fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, de->d_name);
2025 #endif
2026 		if((p = plugin_new(LIBDIR, PACKAGE, "plugins", de->d_name))
2027 				== NULL)
2028 			continue;
2029 		if((mpd = plugin_lookup(p, "plugin")) == NULL)
2030 		{
2031 			plugin_delete(p);
2032 			continue;
2033 		}
2034 		enabled = _mailer_plugin_is_enabled(mailer, de->d_name);
2035 		icon = (mpd->icon != NULL) ? gtk_icon_theme_load_icon(theme,
2036 				mpd->icon, 24, 0, NULL) : NULL;
2037 		if(icon == NULL)
2038 			icon = gtk_icon_theme_load_icon(theme, "gnome-settings",
2039 					24, 0, NULL);
2040 		gtk_list_store_append(mailer->pr_plugins_store, &iter);
2041 		gtk_list_store_set(mailer->pr_plugins_store, &iter,
2042 				MPC_NAME, de->d_name, MPC_ENABLED, enabled,
2043 				MPC_ICON, icon, MPC_NAME_DISPLAY, mpd->name,
2044 				-1);
2045 		plugin_delete(p);
2046 	}
2047 	closedir(dir);
2048 }
2049 
_on_preferences_closex(gpointer data)2050 static gboolean _on_preferences_closex(gpointer data)
2051 {
2052 	Mailer * mailer = data;
2053 
2054 	gtk_widget_hide(mailer->pr_window);
2055 	return TRUE;
2056 }
2057 
_on_preferences_response(GtkWidget * widget,gint response,gpointer data)2058 static void _on_preferences_response(GtkWidget * widget, gint response,
2059 		gpointer data)
2060 {
2061 	Mailer * mailer = data;
2062 
2063 	gtk_widget_hide(widget);
2064 	if(response == GTK_RESPONSE_OK)
2065 		_on_preferences_ok(mailer);
2066 	else if(response == GTK_RESPONSE_CANCEL)
2067 		_on_preferences_cancel(mailer);
2068 }
2069 
2070 
2071 /* _on_preferences_account_new */
2072 /* types */
2073 typedef struct _AccountData
2074 {
2075 	Mailer * mailer;
2076 	char * title;
2077 	AccountIdentity identity;
2078 	unsigned int available;
2079 	Account * account;
2080 	GtkWidget * assistant;
2081 	GtkWidget * settings;
2082 	GtkWidget * confirm;
2083 } AccountData;
2084 
2085 /* functions */
2086 static GtkWidget * _assistant_account_select(AccountData * ad);
2087 static GtkWidget * _assistant_account_config(AccountConfig * config);
2088 
2089 #if !GTK_CHECK_VERSION(2, 10, 0)
2090 # include "gtkassistant.c"
2091 #endif
2092 static void _on_assistant_cancel(GtkWidget * widget, gpointer data);
2093 static void _on_assistant_close(GtkWidget * widget, gpointer data);
2094 static void _on_assistant_apply(gpointer data);
2095 static void _on_assistant_prepare(GtkWidget * widget, GtkWidget * page,
2096 		gpointer data);
2097 static void _on_entry_changed(GtkWidget * widget, gpointer data);
2098 static void _on_account_type_changed(GtkWidget * widget, gpointer data);
2099 
_on_preferences_account_new(gpointer data)2100 static void _on_preferences_account_new(gpointer data)
2101 {
2102 	Mailer * mailer = data;
2103 	AccountData * ad;
2104 	GtkAssistant * assistant;
2105 	GtkWidget * page;
2106 
2107 	if(mailer->available_cnt == 0)
2108 	{
2109 		mailer_error(mailer, _("No account plug-in available"), 0);
2110 		return;
2111 	}
2112 	if((ad = malloc(sizeof(*ad))) == NULL)
2113 	{
2114 		mailer_error(mailer, strerror(errno), 0);
2115 		return;
2116 	}
2117 	ad->mailer = mailer;
2118 	ad->title = strdup("");
2119 	memset(&(ad->identity), 0, sizeof(ad->identity));
2120 	ad->available = 0;
2121 	ad->account = NULL;
2122 	ad->assistant = gtk_assistant_new();
2123 	assistant = GTK_ASSISTANT(ad->assistant);
2124 	g_signal_connect(G_OBJECT(ad->assistant), "cancel", G_CALLBACK(
2125 				_on_assistant_cancel), ad);
2126 	g_signal_connect(G_OBJECT(ad->assistant), "close", G_CALLBACK(
2127 				_on_assistant_close), ad);
2128 	g_signal_connect_swapped(G_OBJECT(ad->assistant), "apply", G_CALLBACK(
2129 				_on_assistant_apply), ad);
2130 	g_signal_connect(G_OBJECT(ad->assistant), "prepare", G_CALLBACK(
2131 				_on_assistant_prepare), ad);
2132 	/* plug-in selection */
2133 	page = _assistant_account_select(ad);
2134 	gtk_assistant_append_page(assistant, page);
2135 	gtk_assistant_set_page_title(assistant, page, _(_title[0]));
2136 	gtk_assistant_set_page_type(assistant, page, GTK_ASSISTANT_PAGE_INTRO);
2137 	gtk_assistant_set_page_complete(assistant, page, FALSE);
2138 	/* plug-in preferences */
2139 	page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2140 	g_object_ref(page); /* XXX currently leaks memory */
2141 	ad->settings = page;
2142 	gtk_widget_show(page);
2143 	gtk_assistant_append_page(assistant, page);
2144 	gtk_assistant_set_page_title(assistant, page, _(_title[1]));
2145 	gtk_assistant_set_page_type(assistant, page,
2146 			GTK_ASSISTANT_PAGE_CONTENT);
2147 	gtk_assistant_set_page_complete(assistant, page, TRUE);
2148 	/* confirmation page */
2149 	page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2150 	ad->confirm = page;
2151 	gtk_widget_show(page);
2152 	gtk_assistant_append_page(assistant, page);
2153 	gtk_assistant_set_page_title(assistant, page, _(_title[2]));
2154 	gtk_assistant_set_page_type(assistant, page,
2155 			GTK_ASSISTANT_PAGE_CONFIRM);
2156 	gtk_assistant_set_page_complete(assistant, page, TRUE);
2157 	gtk_widget_show(ad->assistant);
2158 }
2159 
_on_assistant_cancel(GtkWidget * widget,gpointer data)2160 static void _on_assistant_cancel(GtkWidget * widget, gpointer data)
2161 {
2162 	_on_assistant_close(widget, data);
2163 }
2164 
_on_assistant_close(GtkWidget * widget,gpointer data)2165 static void _on_assistant_close(GtkWidget * widget, gpointer data)
2166 {
2167 	AccountData * ad = data;
2168 
2169 	if(ad->account != NULL)
2170 		account_delete(ad->account);
2171 	free(ad);
2172 	gtk_widget_destroy(widget);
2173 }
2174 
_on_assistant_apply(gpointer data)2175 static void _on_assistant_apply(gpointer data)
2176 {
2177 	AccountData * ad = data;
2178 	GtkTreeModel * model;
2179 	GtkTreeIter iter;
2180 
2181 	/* XXX check for errors */
2182 	account_init(ad->account);
2183 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(ad->mailer->pr_accounts));
2184 	gtk_list_store_append(GTK_LIST_STORE(model), &iter);
2185 #ifdef DEBUG
2186 	fprintf(stderr, "%s%p%s%s%s%s\n", "AC_DATA ", (void *)ad->account,
2187 			", AC_ACTIVE FALSE, AC_ENABLED TRUE, AC_TITLE ",
2188 			account_get_title(ad->account), ", AC_TYPE ",
2189 			account_get_type(ad->account));
2190 #endif
2191 	gtk_list_store_set(GTK_LIST_STORE(model), &iter,
2192 			AC_DATA, ad->account, AC_ACTIVE, FALSE,
2193 			AC_ENABLED, TRUE,
2194 			AC_TITLE, account_get_title(ad->account),
2195 			AC_TYPE, account_get_type(ad->account), -1);
2196 	ad->account = NULL;
2197 	/* _on_assistant_close is then automatically called */
2198 }
2199 
2200 /* on_assistant_prepare */
2201 static GtkWidget * _account_display(Account * account);
2202 
_on_assistant_prepare(GtkWidget * widget,GtkWidget * page,gpointer data)2203 static void _on_assistant_prepare(GtkWidget * widget, GtkWidget * page,
2204 		gpointer data)
2205 {
2206 	static int old = 0;
2207 	AccountData * ad = data;
2208 	unsigned int i;
2209 	Account * ac;
2210 
2211 	i = gtk_assistant_get_current_page(GTK_ASSISTANT(widget));
2212 	gtk_window_set_title(GTK_WINDOW(widget), _(_title[i]));
2213 	if(i == 1)
2214 	{
2215 		gtk_container_remove(GTK_CONTAINER(page), ad->settings);
2216 		if(old == 0)
2217 		{
2218 			if(ad->account != NULL)
2219 				account_delete(ad->account);
2220 			ac = ad->mailer->available[ad->available];
2221 			ad->account = account_new(ad->mailer,
2222 					account_get_type(ac), ad->title, NULL);
2223 		}
2224 		if(ad->account == NULL)
2225 		{
2226 			mailer_error(ad->mailer, error_get(NULL), 0);
2227 			gtk_assistant_set_current_page(GTK_ASSISTANT(widget),
2228 					0);
2229 			ad->settings = _assistant_account_select(ad);
2230 		}
2231 		else
2232 			ad->settings = _assistant_account_config(
2233 					account_get_config(ad->account));
2234 		gtk_container_add(GTK_CONTAINER(page), ad->settings);
2235 		gtk_widget_show_all(ad->settings);
2236 	}
2237 	else if(i == 2)
2238 	{
2239 		gtk_container_remove(GTK_CONTAINER(page), ad->confirm);
2240 		ad->confirm = _account_display(ad->account);
2241 		gtk_container_add(GTK_CONTAINER(page), ad->confirm);
2242 	}
2243 	old = i;
2244 }
2245 
2246 /* _assistant_account_select */
2247 static void _on_account_name_changed(GtkWidget * widget, gpointer data);
2248 static void _account_add_label(GtkWidget * box, PangoFontDescription * desc,
2249 		GtkSizeGroup * group, char const * text);
2250 
_assistant_account_select(AccountData * ad)2251 static GtkWidget * _assistant_account_select(AccountData * ad)
2252 {
2253 	GtkWidget * vbox;
2254 	GtkWidget * hbox;
2255 	GtkSizeGroup * group;
2256 	PangoFontDescription * desc;
2257 	GtkWidget * widget;
2258 	unsigned int i;
2259 
2260 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2261 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
2262 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2263 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2264 	desc = pango_font_description_new();
2265 	pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
2266 	_account_add_label(hbox, desc, group, _("Account name"));
2267 	widget = gtk_entry_new();
2268 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2269 				_on_account_name_changed), ad);
2270 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2271 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2272 	/* default identity */
2273 	/* FIXME seems to not be remembered */
2274 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2275 	_account_add_label(hbox, NULL, group, _("Your name"));
2276 	widget = gtk_entry_new();
2277 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2278 				_on_entry_changed), &(ad->identity.from));
2279 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2280 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2281 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2282 	_account_add_label(hbox, NULL, group, _("e-mail address"));
2283 	widget = gtk_entry_new();
2284 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2285 				_on_entry_changed), &(ad->identity.email));
2286 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2287 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2288 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2289 	_account_add_label(hbox, NULL, group, _("Type of account"));
2290 #if GTK_CHECK_VERSION(2, 24, 0)
2291 	widget = gtk_combo_box_text_new();
2292 #else
2293 	widget = gtk_combo_box_new_text();
2294 #endif
2295 	/* XXX this works because there is no plug-in list reload
2296 	 *     would it be implemented this will need validation later */
2297 	for(i = 0; i < ad->mailer->available_cnt; i++)
2298 #if GTK_CHECK_VERSION(2, 24, 0)
2299 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget),
2300 				account_get_name(ad->mailer->available[i]));
2301 #else
2302 		gtk_combo_box_append_text(GTK_COMBO_BOX(widget),
2303 				account_get_name(ad->mailer->available[i]));
2304 #endif
2305 	gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
2306 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2307 				_on_account_type_changed), ad);
2308 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2309 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2310 	pango_font_description_free(desc);
2311 	gtk_widget_show_all(vbox);
2312 	return vbox;
2313 }
2314 
_on_account_name_changed(GtkWidget * widget,gpointer data)2315 static void _on_account_name_changed(GtkWidget * widget, gpointer data)
2316 {
2317 	AccountData * ad = data;
2318 	int current;
2319 	GtkWidget * page;
2320 
2321 	_on_entry_changed(widget, &ad->title);
2322 	current = gtk_assistant_get_current_page(GTK_ASSISTANT(ad->assistant));
2323 	page = gtk_assistant_get_nth_page(GTK_ASSISTANT(ad->assistant),
2324 			current);
2325 	gtk_assistant_set_page_complete(GTK_ASSISTANT(ad->assistant), page,
2326 			strlen(ad->title) ? TRUE : FALSE);
2327 }
2328 
_account_add_label(GtkWidget * box,PangoFontDescription * desc,GtkSizeGroup * group,char const * text)2329 static void _account_add_label(GtkWidget * box, PangoFontDescription * desc,
2330 		GtkSizeGroup * group, char const * text)
2331 {
2332 	static char buf[80]; /* XXX hard-coded size */
2333 	GtkWidget * label;
2334 
2335 	snprintf(buf, sizeof(buf), "%s:", text);
2336 	label = gtk_label_new(buf);
2337 	if(desc != NULL)
2338 		gtk_widget_override_font(label, desc);
2339 	if(group != NULL)
2340 		gtk_size_group_add_widget(group, label);
2341 #if GTK_CHECK_VERSION(3, 14, 0)
2342 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2343 #else
2344 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2345 #endif
2346 	gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0);
2347 }
2348 
2349 /* _assistant_account_config */
2350 static GtkWidget * _update_string(AccountConfig * config,
2351 		PangoFontDescription * desc, GtkSizeGroup * group);
2352 static GtkWidget * _update_password(AccountConfig * config,
2353 		PangoFontDescription * desc, GtkSizeGroup * group);
2354 static GtkWidget * _update_file(AccountConfig * config,
2355 		PangoFontDescription * desc, GtkSizeGroup * group);
2356 static GtkWidget * _update_uint16(AccountConfig * config,
2357 		PangoFontDescription * desc, GtkSizeGroup * group);
2358 static GtkWidget * _update_boolean(AccountConfig * config);
2359 
_assistant_account_config(AccountConfig * config)2360 static GtkWidget * _assistant_account_config(AccountConfig * config)
2361 {
2362 	GtkWidget * vbox;
2363 	GtkSizeGroup * group;
2364 	GtkWidget * widget;
2365 	size_t i;
2366 
2367 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2368 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
2369 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2370 	for(i = 0; config != NULL && config[i].type != ACT_NONE; i++)
2371 	{
2372 		switch(config[i].type)
2373 		{
2374 			case ACT_STRING:
2375 				widget = _update_string(&config[i], NULL,
2376 						group);
2377 				break;
2378 			case ACT_PASSWORD:
2379 				widget = _update_password(&config[i], NULL,
2380 						group);
2381 				break;
2382 			case ACT_FILE:
2383 				widget = _update_file(&config[i], NULL, group);
2384 				break;
2385 			case ACT_UINT16:
2386 				widget = _update_uint16(&config[i], NULL,
2387 						group);
2388 				break;
2389 			case ACT_BOOLEAN:
2390 				widget = _update_boolean(&config[i]);
2391 				break;
2392 			case ACT_SEPARATOR:
2393 				widget = gtk_separator_new(
2394 						GTK_ORIENTATION_HORIZONTAL);
2395 				break;
2396 			default: /* should not happen */
2397 				assert(0);
2398 				continue;
2399 		}
2400 		gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
2401 	}
2402 	return vbox;
2403 }
2404 
_update_string(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2405 static GtkWidget * _update_string(AccountConfig * config,
2406 		PangoFontDescription * desc, GtkSizeGroup * group)
2407 {
2408 	GtkWidget * hbox;
2409 	GtkWidget * widget;
2410 
2411 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2412 	_account_add_label(hbox, desc, group, config->title);
2413 	widget = gtk_entry_new();
2414 	if(config->value != NULL)
2415 		gtk_entry_set_text(GTK_ENTRY(widget), config->value);
2416 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2417 				_on_entry_changed), &config->value);
2418 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2419 	return hbox;
2420 }
2421 
_update_password(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2422 static GtkWidget * _update_password(AccountConfig * config,
2423 		PangoFontDescription * desc, GtkSizeGroup * group)
2424 {
2425 	GtkWidget * hbox;
2426 	GtkWidget * widget;
2427 
2428 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2429 	_account_add_label(hbox, desc, group, config->title);
2430 	widget = gtk_entry_new();
2431 	gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
2432 	if(config->value != NULL)
2433 		gtk_entry_set_text(GTK_ENTRY(widget), config->value);
2434 	g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK(
2435 				_on_entry_changed), &config->value);
2436 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2437 	return hbox;
2438 }
2439 
2440 static void _on_file_activated(GtkWidget * widget, gpointer data);
2441 
_update_file(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2442 static GtkWidget * _update_file(AccountConfig * config,
2443 		PangoFontDescription * desc, GtkSizeGroup * group)
2444 {
2445 	GtkWidget * hbox;
2446 	GtkWidget * widget;
2447 
2448 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2449 	_account_add_label(hbox, desc, group, config->title);
2450 	widget = gtk_file_chooser_button_new(_("Choose file"),
2451 			GTK_FILE_CHOOSER_ACTION_OPEN);
2452 	if(config->value != NULL)
2453 		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget),
2454 				config->value);
2455 	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(widget),
2456 			config->title);
2457 	g_signal_connect(G_OBJECT(widget), "file-set", G_CALLBACK(
2458 				_on_file_activated), &config->value);
2459 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2460 	return hbox;
2461 }
2462 
_on_file_activated(GtkWidget * widget,gpointer data)2463 static void _on_file_activated(GtkWidget * widget, gpointer data)
2464 {
2465 	char * filename;
2466 	char ** value = data;
2467 	char * p = NULL;
2468 
2469 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
2470 #ifdef DEBUG
2471 	fprintf(stderr, "DEBUG: %s(%s)\n", __func__, filename);
2472 #endif
2473 	if(filename == NULL)
2474 		free(*value);
2475 	else if((p = realloc(*value, strlen(filename) + 1)) == NULL)
2476 	{
2477 		mailer_error(NULL, strerror(errno), 0);
2478 		return;
2479 	}
2480 	*value = p;
2481 	if(filename != NULL)
2482 		strcpy(p, filename);
2483 }
2484 
2485 static void _on_uint16_changed(GtkWidget * widget, gpointer data);
2486 
_update_uint16(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2487 static GtkWidget * _update_uint16(AccountConfig * config,
2488 		PangoFontDescription * desc, GtkSizeGroup * group)
2489 {
2490 	GtkWidget * hbox;
2491 	GtkWidget * widget;
2492 	uint16_t u16 = (intptr_t)(config->value);
2493 	gdouble value = u16;
2494 
2495 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2496 	_account_add_label(hbox, desc, group, config->title);
2497 	widget = gtk_spin_button_new_with_range(0, 65535, 1);
2498 	gtk_spin_button_set_digits(GTK_SPIN_BUTTON(widget), 0);
2499 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), value);
2500 	g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(
2501 				_on_uint16_changed), &config->value);
2502 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2503 	return hbox;
2504 }
2505 
_on_uint16_changed(GtkWidget * widget,gpointer data)2506 static void _on_uint16_changed(GtkWidget * widget, gpointer data)
2507 {
2508 	int * value = data;
2509 
2510 	*value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
2511 #ifdef DEBUG
2512 	fprintf(stderr, "DEBUG: new value is %d\n", *value);
2513 #endif
2514 }
2515 
2516 static void _on_boolean_toggled(GtkWidget * widget, gpointer data);
_update_boolean(AccountConfig * config)2517 static GtkWidget * _update_boolean(AccountConfig * config)
2518 {
2519 	GtkWidget * widget;
2520 
2521 	widget = gtk_check_button_new_with_label(config->title);
2522 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
2523 			config->value != NULL);
2524 	g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(
2525 				_on_boolean_toggled), &config->value);
2526 	return widget;
2527 }
2528 
_on_boolean_toggled(GtkWidget * widget,gpointer data)2529 static void _on_boolean_toggled(GtkWidget * widget, gpointer data)
2530 {
2531 	int * value = data;
2532 
2533 	*value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2534 }
2535 
2536 static GtkWidget * _display_string(AccountConfig * config,
2537 		PangoFontDescription * desc, GtkSizeGroup * group);
2538 static GtkWidget * _display_password(AccountConfig * config,
2539 		PangoFontDescription * desc, GtkSizeGroup * group);
2540 static GtkWidget * _display_file(AccountConfig * config,
2541 		PangoFontDescription * desc, GtkSizeGroup * group);
2542 static GtkWidget * _display_uint16(AccountConfig * config,
2543 		PangoFontDescription * desc, GtkSizeGroup * group);
2544 static GtkWidget * _display_boolean(AccountConfig * config,
2545 		PangoFontDescription * desc, GtkSizeGroup * group);
_account_display(Account * account)2546 static GtkWidget * _account_display(Account * account)
2547 {
2548 	AccountConfig * config;
2549 	AccountConfig p;
2550 	GtkWidget * vbox;
2551 	GtkSizeGroup * group;
2552 	PangoFontDescription * desc;
2553 	GtkWidget * widget;
2554 	unsigned int i;
2555 
2556 	config = account_get_config(account);
2557 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2558 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
2559 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2560 	p.name = NULL;
2561 	p.title = _("Account name");
2562 	p.value = (void *)account_get_title(account);
2563 	desc = pango_font_description_new();
2564 	pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
2565 	widget = _display_string(&p, desc, group);
2566 	gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
2567 	for(i = 0; config[i].type != ACT_NONE; i++)
2568 	{
2569 		switch(config[i].type)
2570 		{
2571 			case ACT_STRING:
2572 				widget = _display_string(&config[i], desc,
2573 						group);
2574 				break;
2575 			case ACT_PASSWORD:
2576 				widget = _display_password(&config[i], desc,
2577 						group);
2578 				break;
2579 			case ACT_FILE:
2580 				widget = _display_file(&config[i], desc, group);
2581 				break;
2582 			case ACT_UINT16:
2583 				widget = _display_uint16(&config[i], desc,
2584 						group);
2585 				break;
2586 			case ACT_BOOLEAN:
2587 				widget = _display_boolean(&config[i], desc,
2588 						group);
2589 				break;
2590 			case ACT_SEPARATOR:
2591 				widget = gtk_separator_new(
2592 						GTK_ORIENTATION_HORIZONTAL);
2593 				break;
2594 			default: /* should not happen */
2595 				assert(0);
2596 				continue;
2597 		}
2598 		gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
2599 	}
2600 	pango_font_description_free(desc);
2601 	gtk_widget_show_all(vbox);
2602 	return vbox;
2603 }
2604 
_display_string(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2605 static GtkWidget * _display_string(AccountConfig * config,
2606 		PangoFontDescription * desc, GtkSizeGroup * group)
2607 {
2608 	GtkWidget * hbox;
2609 	GtkWidget * widget;
2610 
2611 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2612 	_account_add_label(hbox, desc, group, config->title);
2613 	widget = gtk_label_new(config->value);
2614 #if GTK_CHECK_VERSION(3, 14, 0)
2615 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2616 #else
2617 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2618 #endif
2619 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2620 	return hbox;
2621 }
2622 
_display_file(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2623 static GtkWidget * _display_file(AccountConfig * config,
2624 		PangoFontDescription * desc, GtkSizeGroup * group)
2625 {
2626 	return _display_string(config, desc, group);
2627 }
2628 
_display_password(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2629 static GtkWidget * _display_password(AccountConfig * config,
2630 		PangoFontDescription * desc, GtkSizeGroup * group)
2631 {
2632 	GtkWidget * hbox;
2633 	GtkWidget * widget;
2634 
2635 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2636 	_account_add_label(hbox, desc, group, config->title);
2637 	widget = gtk_label_new(_("hidden"));
2638 	desc = pango_font_description_new();
2639 	pango_font_description_set_style(desc, PANGO_STYLE_ITALIC);
2640 	gtk_widget_override_font(widget, desc);
2641 #if GTK_CHECK_VERSION(3, 14, 0)
2642 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2643 #else
2644 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2645 #endif
2646 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2647 	return hbox;
2648 }
2649 
_display_uint16(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2650 static GtkWidget * _display_uint16(AccountConfig * config,
2651 		PangoFontDescription * desc, GtkSizeGroup * group)
2652 {
2653 	GtkWidget * hbox;
2654 	GtkWidget * widget;
2655 	char buf[6];
2656 	uint16_t u16 = (intptr_t)config->value;
2657 
2658 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2659 	_account_add_label(hbox, desc, group, config->title);
2660 	snprintf(buf, sizeof(buf), "%hu", u16);
2661 	widget = gtk_label_new(buf);
2662 #if GTK_CHECK_VERSION(3, 14, 0)
2663 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2664 #else
2665 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2666 #endif
2667 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2668 	return hbox;
2669 }
2670 
_display_boolean(AccountConfig * config,PangoFontDescription * desc,GtkSizeGroup * group)2671 static GtkWidget * _display_boolean(AccountConfig * config,
2672 		PangoFontDescription * desc, GtkSizeGroup * group)
2673 {
2674 	GtkWidget * hbox;
2675 	GtkWidget * widget;
2676 
2677 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2678 	_account_add_label(hbox, desc, group, config->title);
2679 	widget = gtk_label_new(config->value != 0 ? _("Yes") : _("No"));
2680 #if GTK_CHECK_VERSION(3, 14, 0)
2681 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2682 #else
2683 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2684 #endif
2685 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2686 	return hbox;
2687 }
2688 
_on_entry_changed(GtkWidget * widget,gpointer data)2689 static void _on_entry_changed(GtkWidget * widget, gpointer data)
2690 {
2691 	const char * text;
2692 	char ** value = data;
2693 	char * p;
2694 
2695 	text = gtk_entry_get_text(GTK_ENTRY(widget));
2696 	if((p = realloc(*value, strlen(text) + 1)) == NULL)
2697 	{
2698 		mailer_error(NULL, strerror(errno), 0);
2699 		return;
2700 	}
2701 	*value = p;
2702 	strcpy(p, text);
2703 }
2704 
_on_account_type_changed(GtkWidget * widget,gpointer data)2705 static void _on_account_type_changed(GtkWidget * widget, gpointer data)
2706 {
2707 	AccountData * ad = data;
2708 
2709 	ad->available = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
2710 #ifdef DEBUG
2711 	fprintf(stderr, "%s%u%s", _("Account type "), ad->available,
2712 			_(" active\n"));
2713 #endif
2714 }
2715 
_on_preferences_account_toggle(GtkCellRendererToggle * renderer,char * path,gpointer data)2716 static void _on_preferences_account_toggle(GtkCellRendererToggle * renderer,
2717 		char * path, gpointer data)
2718 {
2719 	GtkListStore * store = data;
2720 	GtkTreeIter iter;
2721 
2722 	gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path);
2723 	gtk_list_store_set(store, &iter, AC_ENABLED,
2724 			!gtk_cell_renderer_toggle_get_active(renderer), -1);
2725 }
2726 
2727 /* _on_preferences_account_edit */
2728 static GtkWidget * _account_edit(Mailer * mailer, Account * account);
2729 static gboolean _account_edit_on_closex(GtkWidget * widget, GdkEvent * event,
2730 		gpointer data);
2731 static void _account_edit_on_response(GtkWidget * widget, gint response,
2732 		gpointer data);
2733 
_on_preferences_account_edit(gpointer data)2734 static void _on_preferences_account_edit(gpointer data)
2735 {
2736 	Mailer * mailer = data;
2737 	GtkTreeSelection * selection;
2738 	GtkTreeModel * model;
2739 	GtkTreeIter iter;
2740 	Account * account;
2741 	GtkWidget * widget;
2742 
2743 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
2744 				mailer->pr_accounts));
2745 	if(gtk_tree_selection_get_selected(selection, &model, &iter) != TRUE)
2746 		return;
2747 	gtk_tree_model_get(model, &iter, AC_DATA, &account, AC_WIDGET, &widget,
2748 			-1);
2749 	if(widget != NULL)
2750 	{
2751 		gtk_window_present(GTK_WINDOW(widget));
2752 		return;
2753 	}
2754 	widget = _account_edit(mailer, account);
2755 	gtk_list_store_set(GTK_LIST_STORE(model), &iter, AC_WIDGET, widget, -1);
2756 }
2757 
_account_edit(Mailer * mailer,Account * account)2758 static GtkWidget * _account_edit(Mailer * mailer, Account * account)
2759 {
2760 	char const * title;
2761 	char const * p;
2762 	GtkWidget * window;
2763 	char buf[80];
2764 	GtkWidget * content;
2765 	GtkWidget * notebook;
2766 	GtkWidget * vbox;
2767 	GtkWidget * frame;
2768 	GtkWidget * vbox2;
2769 	GtkWidget * widget;
2770 	GtkWidget * hbox;
2771 	GtkSizeGroup * group;
2772 
2773 	title = account_get_title(account);
2774 	snprintf(buf, sizeof(buf), "%s%s", _("Edit account: "), title);
2775 	window = gtk_dialog_new_with_buttons(buf, GTK_WINDOW(mailer->pr_window),
2776 			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,
2777 			GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK,
2778 			NULL);
2779 	g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(
2780 				_account_edit_on_closex), NULL);
2781 	g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(
2782 				_account_edit_on_response), NULL);
2783 #if GTK_CHECK_VERSION(2, 14, 0)
2784 	content = gtk_dialog_get_content_area(GTK_DIALOG(window));
2785 #else
2786 	content = GTK_DIALOG(window)->vbox;
2787 #endif
2788 	gtk_container_set_border_width(GTK_CONTAINER(content), 4);
2789 	notebook = gtk_notebook_new();
2790 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2791 	/* account tab */
2792 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2793 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
2794 	/* account name */
2795 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2796 	widget = gtk_label_new(_("Account name:"));
2797 #if GTK_CHECK_VERSION(3, 14, 0)
2798 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2799 #else
2800 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2801 #endif
2802 	gtk_size_group_add_widget(group, widget);
2803 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
2804 	widget = gtk_entry_new();
2805 	gtk_entry_set_text(GTK_ENTRY(widget), title);
2806 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2807 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
2808 	/* identity */
2809 	frame = gtk_frame_new(_("Identity:"));
2810 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2811 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
2812 	/* identity: name */
2813 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2814 	widget = gtk_label_new(_("Name:"));
2815 #if GTK_CHECK_VERSION(3, 14, 0)
2816 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2817 #else
2818 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2819 #endif
2820 	gtk_size_group_add_widget(group, widget);
2821 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
2822 	widget = gtk_entry_new();
2823 	if((p = config_get(mailer->config, title, "identity_name")) != NULL)
2824 		gtk_entry_set_text(GTK_ENTRY(widget), p);
2825 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2826 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2827 	/* identity: address */
2828 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2829 	widget = gtk_label_new(_("Address:"));
2830 #if GTK_CHECK_VERSION(3, 14, 0)
2831 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2832 #else
2833 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2834 #endif
2835 	gtk_size_group_add_widget(group, widget);
2836 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
2837 	widget = gtk_entry_new();
2838 	if((p = config_get(mailer->config, title, "identity_email")) != NULL)
2839 		gtk_entry_set_text(GTK_ENTRY(widget), p);
2840 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2841 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2842 	/* identity: organization */
2843 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2844 	widget = gtk_label_new(_("Organization:"));
2845 #if GTK_CHECK_VERSION(3, 14, 0)
2846 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
2847 #else
2848 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
2849 #endif
2850 	gtk_size_group_add_widget(group, widget);
2851 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
2852 	widget = gtk_entry_new();
2853 	if((p = config_get(mailer->config, title, "identity_organization"))
2854 			!= NULL)
2855 		gtk_entry_set_text(GTK_ENTRY(widget), p);
2856 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
2857 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2858 	gtk_container_add(GTK_CONTAINER(frame), vbox2);
2859 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
2860 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, gtk_label_new(
2861 				_("Account")));
2862 	/* settings tab */
2863 	/* FIXME this affects the account directly (eg cancel does not work) */
2864 	widget = _assistant_account_config(account_get_config(account));
2865 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, gtk_label_new(
2866 				_("Settings")));
2867 	gtk_box_pack_start(GTK_BOX(content), notebook, TRUE, TRUE, 0);
2868 	gtk_widget_show_all(window);
2869 	return window;
2870 }
2871 
_account_edit_on_closex(GtkWidget * widget,GdkEvent * event,gpointer data)2872 static gboolean _account_edit_on_closex(GtkWidget * widget, GdkEvent * event,
2873 		gpointer data)
2874 {
2875 	_account_edit_on_response(widget, GTK_RESPONSE_CANCEL, data);
2876 	return TRUE;
2877 }
2878 
_account_edit_on_response(GtkWidget * widget,gint response,gpointer data)2879 static void _account_edit_on_response(GtkWidget * widget, gint response,
2880 		gpointer data)
2881 {
2882 	/* FIXME really implement */
2883 	gtk_widget_hide(widget);
2884 }
2885 
_on_preferences_account_delete(gpointer data)2886 static void _on_preferences_account_delete(gpointer data)
2887 {
2888 	Mailer * mailer = data;
2889 	GtkTreePath * path;
2890 	GtkTreeModel * model;
2891 	GtkTreeIter iter;
2892 
2893 	gtk_tree_view_get_cursor(GTK_TREE_VIEW(mailer->pr_accounts), &path,
2894 			NULL);
2895 	if(path == NULL)
2896 		return;
2897 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->pr_accounts));
2898 	gtk_tree_model_get_iter(model, &iter, path);
2899 	gtk_tree_path_free(path);
2900 	gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
2901 	/* FIXME non-interface code (flag account as deleted and on ok apply) */
2902 }
2903 
_on_preferences_account_move_down(gpointer data)2904 static void _on_preferences_account_move_down(gpointer data)
2905 {
2906 	Mailer * mailer = data;
2907 	GtkTreeModel * model;
2908 	GtkTreeIter iter;
2909 	GtkTreeIter iter2;
2910 	GtkTreeSelection * treesel;
2911 
2912 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(
2913 				mailer->pr_accounts));
2914 	if(!gtk_tree_selection_get_selected(treesel, &model, &iter))
2915 		return;
2916 	iter2 = iter;
2917 	if(!gtk_tree_model_iter_next(model, &iter))
2918 		return;
2919 	gtk_list_store_swap(GTK_LIST_STORE(model), &iter, &iter2);
2920 }
2921 
_on_preferences_account_move_up(gpointer data)2922 static void _on_preferences_account_move_up(gpointer data)
2923 {
2924 	Mailer * mailer = data;
2925 	GtkTreeModel * model;
2926 	GtkTreeIter iter;
2927 	GtkTreeIter iter2;
2928 	GtkTreePath * path;
2929 	GtkTreeSelection * treesel;
2930 
2931 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(
2932 				mailer->pr_accounts));
2933 	if(!gtk_tree_selection_get_selected(treesel, &model, &iter))
2934 		return;
2935 	path = gtk_tree_model_get_path(model, &iter);
2936 	gtk_tree_path_prev(path);
2937 	gtk_tree_model_get_iter(model, &iter2, path);
2938 	gtk_tree_path_free(path);
2939 	gtk_list_store_swap(GTK_LIST_STORE(model), &iter, &iter2);
2940 }
2941 
_on_preferences_cancel(gpointer data)2942 static void _on_preferences_cancel(gpointer data)
2943 {
2944 	Mailer * mailer = data;
2945 
2946 	gtk_widget_hide(mailer->pr_window);
2947 	_preferences_set(mailer);
2948 }
2949 
_on_preferences_ok(gpointer data)2950 static void _on_preferences_ok(gpointer data)
2951 {
2952 	Mailer * mailer = data;
2953 
2954 	mailer_show_preferences(mailer, FALSE);
2955 	if(_preferences_ok_accounts(mailer) != 0
2956 			|| _preferences_ok_display(mailer) != 0
2957 			|| _preferences_ok_plugins(mailer) != 0
2958 			|| _preferences_ok_save(mailer) != 0)
2959 		mailer_error(mailer, _("An error occured while saving"
2960 					" preferences"), 0);
2961 }
2962 
_preferences_ok_accounts(Mailer * mailer)2963 static int _preferences_ok_accounts(Mailer * mailer)
2964 {
2965 	GtkTreeModel * model;
2966 	GtkTreeIter iter;
2967 	GtkTreeModel * view_model;
2968 	gboolean loop;
2969 	Account * account;
2970 	gboolean active;
2971 	gboolean enabled;
2972 	char * title;
2973 	size_t title_len;
2974 	char * accounts = NULL;
2975 	size_t accounts_len = 0;
2976 	char * p;
2977 
2978 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->pr_accounts));
2979 	view_model = gtk_tree_view_get_model(GTK_TREE_VIEW(
2980 				mailer->fo_view));
2981 	for(loop = gtk_tree_model_get_iter_first(model, &iter); loop == TRUE;
2982 			loop = gtk_tree_model_iter_next(model, &iter))
2983 	{
2984 		gtk_tree_model_get(model, &iter, AC_DATA, &account,
2985 				AC_ACTIVE, &active, AC_ENABLED, &enabled,
2986 				AC_TITLE, &title, -1);
2987 		title_len = strlen(title);
2988 		if(account_config_save(account, mailer->config) != 0)
2989 			return 1;
2990 		if((p = realloc(accounts, accounts_len + title_len + 2))
2991 				== NULL)
2992 		{
2993 			free(accounts);
2994 			return 1;
2995 		}
2996 		accounts = p;
2997 		sprintf(&accounts[accounts_len], "%s%s", accounts_len ? ","
2998 				: "", title);
2999 		accounts_len += title_len + (accounts_len ? 1 : 0);
3000 		if(active)
3001 		{
3002 			if(enabled)
3003 				continue;
3004 #if 0 /* FIXME API+behaviour change here */
3005 			if(mailer_account_disable(mailer, account) == 0)
3006 				gtk_list_store_set(GTK_LIST_STORE(model), &iter,
3007 						AC_ACTIVE, FALSE, -1);
3008 #endif
3009 		}
3010 		else if(enabled && mailer_account_add(mailer, account) == 0)
3011 			gtk_list_store_set(GTK_LIST_STORE(model), &iter,
3012 					AC_ACTIVE, TRUE, -1);
3013 	}
3014 	/* FIXME delete accounts that need to be */
3015 	/* FIXME force a refresh of the ones remaining and not just added */
3016 #ifdef DEBUG
3017 	fprintf(stderr, "DEBUG: saved accounts \"%s\"\n", accounts);
3018 #endif
3019 	config_set(mailer->config, "", "accounts", accounts);
3020 	free(accounts);
3021 	return 0;
3022 }
3023 
_preferences_ok_display(Mailer * mailer)3024 static int _preferences_ok_display(Mailer * mailer)
3025 {
3026 	PangoFontDescription * font = NULL;
3027 	char const * p;
3028 
3029 	p = gtk_font_button_get_font_name(GTK_FONT_BUTTON(
3030 				mailer->pr_messages_font));
3031 	config_set(mailer->config, "", "messages_font", p);
3032 	if(p != NULL)
3033 		font = pango_font_description_from_string(p);
3034 	gtk_widget_override_font(mailer->bo_view, font);
3035 	if(font != NULL)
3036 		pango_font_description_free(font);
3037 	return 0;
3038 }
3039 
_preferences_ok_plugins(Mailer * mailer)3040 static int _preferences_ok_plugins(Mailer * mailer)
3041 {
3042 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pr_plugins_store);
3043 	GtkTreeIter iter;
3044 	gboolean valid;
3045 	gchar * p;
3046 	gboolean enabled;
3047 	String * value = string_new("");
3048 	String * sep = "";
3049 
3050 	for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
3051 			valid = gtk_tree_model_iter_next(model, &iter))
3052 	{
3053 		gtk_tree_model_get(model, &iter, 0, &p, 1, &enabled, -1);
3054 		if(enabled)
3055 		{
3056 			mailer_load(mailer, p);
3057 			string_append(&value, sep);
3058 			string_append(&value, p);
3059 			sep = ",";
3060 		}
3061 		else if(_mailer_plugin_is_enabled(mailer, p))
3062 			mailer_unload(mailer, p);
3063 		g_free(p);
3064 	}
3065 	config_set(mailer->config, NULL, "plugins", value);
3066 	string_delete(value);
3067 	return 0;
3068 }
3069 
_preferences_ok_save(Mailer * mailer)3070 static int _preferences_ok_save(Mailer * mailer)
3071 {
3072 	int ret;
3073 	char * p;
3074 
3075 	if((p = _mailer_get_config_filename()) == NULL)
3076 		return 1;
3077 	ret = config_save(mailer->config, p);
3078 	free(p);
3079 	return ret;
3080 }
3081 
_preferences_on_plugin_toggled(GtkCellRendererToggle * renderer,char * path,gpointer data)3082 static void _preferences_on_plugin_toggled(GtkCellRendererToggle * renderer,
3083 		char * path, gpointer data)
3084 {
3085 	Mailer * mailer = data;
3086 	GtkTreeIter iter;
3087 
3088 	gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(
3089 				mailer->pr_plugins_store), &iter, path);
3090 	gtk_list_store_set(mailer->pr_plugins_store, &iter, 1,
3091 			!gtk_cell_renderer_toggle_get_active(renderer), -1);
3092 }
3093 
3094 
3095 /* mailer_unload */
mailer_unload(Mailer * mailer,char const * plugin)3096 int mailer_unload(Mailer * mailer, char const * plugin)
3097 {
3098 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pl_store);
3099 	GtkTreeIter iter;
3100 	gboolean valid;
3101 	gchar * p;
3102 	Plugin * pp;
3103 	MailerPluginDefinition * mpd;
3104 	MailerPlugin * mp;
3105 	gboolean enabled = FALSE;
3106 
3107 	/* XXX this code is duplicated with _mailer_plugin_is_enabled() */
3108 	for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
3109 			valid = gtk_tree_model_iter_next(model, &iter))
3110 	{
3111 		gtk_tree_model_get(model, &iter, MPC_NAME, &p, MPC_PLUGIN, &pp,
3112 				MPC_MAILERPLUGINDEFINITION, &mpd,
3113 				MPC_MAILERPLUGIN, &mp, -1);
3114 		enabled = (strcmp(p, plugin) == 0) ? TRUE : FALSE;
3115 		g_free(p);
3116 		if(enabled)
3117 			break;
3118 	}
3119 	if(enabled != TRUE)
3120 		return 0;
3121 	gtk_list_store_remove(mailer->pl_store, &iter);
3122 	if(mpd->destroy != NULL)
3123 		mpd->destroy(mp);
3124 	plugin_delete(pp);
3125 	return 0;
3126 }
3127 
3128 
3129 /* mailer_unselect_all */
mailer_unselect_all(Mailer * mailer)3130 void mailer_unselect_all(Mailer * mailer)
3131 {
3132 	GtkTreeSelection * treesel;
3133 #if GTK_CHECK_VERSION(2, 4, 0)
3134 	GtkTextBuffer * tbuf;
3135 	GtkTextMark * mark;
3136 	GtkTextIter iter;
3137 #endif
3138 
3139 	if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window))
3140 			== mailer->bo_view)
3141 	{
3142 #if GTK_CHECK_VERSION(2, 4, 0)
3143 		tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mailer->bo_view));
3144 		mark = gtk_text_buffer_get_mark(tbuf, "insert");
3145 		gtk_text_buffer_get_iter_at_mark(tbuf, &iter, mark);
3146 		gtk_text_buffer_select_range(tbuf, &iter, &iter);
3147 #endif
3148 		return;
3149 	}
3150 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mailer->he_view));
3151 	gtk_tree_selection_unselect_all(treesel);
3152 }
3153 
3154 
3155 /* private */
3156 /* functions */
3157 /* accessors */
3158 /* mailer_get_config_filename */
_mailer_get_config_filename(void)3159 static char * _mailer_get_config_filename(void)
3160 	/* FIXME consider replacing with mailer_save_config() */
3161 {
3162 	char const * homedir;
3163 	char * filename;
3164 
3165 	if((homedir = getenv("HOME")) == NULL)
3166 		homedir = g_get_home_dir();
3167 	if((filename = malloc(strlen(homedir) + sizeof(MAILER_CONFIG_FILE) + 1))
3168 			== NULL)
3169 		return NULL;
3170 	sprintf(filename, "%s/%s", homedir, MAILER_CONFIG_FILE);
3171 	return filename;
3172 }
3173 
3174 
3175 /* mailer_plugin_is_enabled */
_mailer_plugin_is_enabled(Mailer * mailer,char const * plugin)3176 static gboolean _mailer_plugin_is_enabled(Mailer * mailer, char const * plugin)
3177 {
3178 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pl_store);
3179 	GtkTreeIter iter;
3180 	gchar * p;
3181 	gboolean valid;
3182 	int res;
3183 
3184 	for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
3185 			valid = gtk_tree_model_iter_next(model, &iter))
3186 	{
3187 		gtk_tree_model_get(model, &iter, MPC_NAME, &p, -1);
3188 		res = strcmp(p, plugin);
3189 		g_free(p);
3190 		if(res == 0)
3191 			return TRUE;
3192 	}
3193 	return FALSE;
3194 }
3195 
3196 
3197 /* mailer_get_font */
_mailer_get_font(Mailer * mailer)3198 static char const * _mailer_get_font(Mailer * mailer)
3199 {
3200 	char const * p;
3201 	char * q = NULL;
3202 	GtkSettings * settings;
3203 	PangoFontDescription * desc;
3204 
3205 	if((p = mailer_get_config(mailer, "messages_font")) != NULL)
3206 		return p;
3207 	settings = gtk_settings_get_default();
3208 	g_object_get(G_OBJECT(settings), "gtk-font-name", &q, NULL);
3209 	if(q == NULL)
3210 		return MAILER_MESSAGES_FONT;
3211 	desc = pango_font_description_from_string(q);
3212 	g_free(q);
3213 	pango_font_description_set_family(desc, "monospace");
3214 	q = pango_font_description_to_string(desc);
3215 	config_set(mailer->config, NULL, "messages_font", q);
3216 	g_free(q);
3217 	pango_font_description_free(desc);
3218 	if((p = config_get(mailer->config, NULL, "messages_font")) != NULL)
3219 		return p;
3220 	return MAILER_MESSAGES_FONT;
3221 }
3222 
3223 
3224 /* mailer_get_selected_headers */
_mailer_get_selected_headers(Mailer * mailer)3225 static GList * _mailer_get_selected_headers(Mailer * mailer)
3226 {
3227 	GtkTreeModel * model;
3228 	GtkTreeSelection * treesel;
3229 
3230 	if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view)))
3231 			== NULL)
3232 		return NULL;
3233 	if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(
3234 						mailer->he_view))) == NULL)
3235 		return NULL;
3236 	return gtk_tree_selection_get_selected_rows(treesel, NULL);
3237 }
3238 
3239 
3240 /* useful */
3241 /* mailer_config_load_account */
_mailer_config_load_account(Mailer * mailer,char const * name)3242 static int _mailer_config_load_account(Mailer * mailer, char const * name)
3243 {
3244 	Account * account;
3245 	char const * type;
3246 
3247 #ifdef DEBUG
3248 	fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
3249 #endif
3250 	if((type = config_get(mailer->config, name, "type")) == NULL)
3251 		return -1;
3252 	if((account = account_new(mailer, type, name, mailer->fo_store))
3253 			== NULL)
3254 		return -mailer_error(mailer, error_get(NULL), 1);
3255 	if(account_init(account) != 0
3256 			|| account_config_load(account, mailer->config) != 0
3257 			|| mailer_account_add(mailer, account) != 0)
3258 	{
3259 		account_delete(account);
3260 		return -1;
3261 	}
3262 	return 0;
3263 }
3264 
3265 
3266 /* mailer_confirm */
_mailer_confirm(Mailer * mailer,char const * message)3267 static gboolean _mailer_confirm(Mailer * mailer, char const * message)
3268 {
3269 	GtkWidget * dialog;
3270 	int res;
3271 
3272 	dialog = gtk_message_dialog_new(GTK_WINDOW(mailer->fo_window),
3273 			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3274 			GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
3275 #if GTK_CHECK_VERSION(2, 8, 0)
3276 			"%s", _("Question"));
3277 	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
3278 #endif
3279 			"%s", message);
3280 	gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
3281 	res = gtk_dialog_run(GTK_DIALOG(dialog));
3282 	gtk_widget_destroy(dialog);
3283 	return (res == GTK_RESPONSE_YES) ? TRUE : FALSE;
3284 }
3285 
3286 
3287 /* mailer_foreach_message_selected */
_mailer_foreach_message_selected(Mailer * mailer,MailerForeachMessageCallback callback)3288 static void _mailer_foreach_message_selected(Mailer * mailer,
3289 		MailerForeachMessageCallback callback)
3290 {
3291 	GtkTreeModel * model;
3292 	GtkTreeSelection * treesel;
3293 	GList * selected;
3294 	GList * s;
3295 	GtkTreePath * path;
3296 	GtkTreeIter iter;
3297 
3298 	if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view)))
3299 			== NULL)
3300 		return;
3301 	if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(
3302 						mailer->he_view))) == NULL)
3303 		return;
3304 	if((selected = gtk_tree_selection_get_selected_rows(treesel, NULL))
3305 			== NULL)
3306 		return;
3307 	for(s = g_list_first(selected); s != NULL; s = g_list_next(s))
3308 	{
3309 		if((path = s->data) == NULL)
3310 			continue;
3311 		gtk_tree_model_get_iter(model, &iter, path);
3312 		callback(mailer, model, &iter);
3313 	}
3314 	g_list_free(selected);
3315 }
3316 
3317 
3318 /* mailer_refresh_plugin */
_mailer_refresh_plugin(Mailer * mailer)3319 static void _mailer_refresh_plugin(Mailer * mailer)
3320 {
3321 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pl_store);
3322 	GtkTreeIter iter;
3323 	MailerPluginDefinition * mpd;
3324 	MailerPlugin * mp;
3325 
3326 	if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(mailer->pl_combo), &iter)
3327 			!= TRUE)
3328 		return;
3329 	gtk_tree_model_get(model, &iter, MPC_MAILERPLUGINDEFINITION, &mpd,
3330 			MPC_MAILERPLUGIN, &mp, -1);
3331 	if(mpd->refresh == NULL)
3332 		return;
3333 	mpd->refresh(mp, mailer->folder_cur, mailer->message_cur);
3334 }
3335 
3336 
3337 /* mailer_refresh_title */
_mailer_refresh_title(Mailer * mailer)3338 static void _mailer_refresh_title(Mailer * mailer)
3339 {
3340 	char buf[80];
3341 
3342 	if(mailer->folder_cur == NULL)
3343 	{
3344 		gtk_window_set_title(GTK_WINDOW(mailer->he_window), PACKAGE);
3345 		return;
3346 	}
3347 	snprintf(buf, sizeof(buf), "%s - %s (%s)", PACKAGE,
3348 			folder_get_name(mailer->folder_cur),
3349 			account_get_title(mailer->account_cur));
3350 	gtk_window_set_title(GTK_WINDOW(mailer->he_window), buf);
3351 }
3352 
3353 
3354 /* mailer_update_status */
_mailer_update_status(Mailer * mailer)3355 static void _mailer_update_status(Mailer * mailer)
3356 {
3357 	GtkTreeModel * store;
3358 	int cnt;
3359 	char buf[256];
3360 
3361 	if((store = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view)))
3362 			!= NULL)
3363 	{
3364 		cnt = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store),
3365 				NULL);
3366 		snprintf(buf, sizeof(buf), _("%s/%s: %d %s"),
3367 				account_get_name(mailer->account_cur),
3368 				folder_get_name(mailer->folder_cur), cnt,
3369 				(cnt > 1) ? _("messages") : _("message"));
3370 	}
3371 	else
3372 		snprintf(buf, sizeof(buf), "%s", _("Ready"));
3373 	mailer_set_status(mailer, buf);
3374 }
3375 
3376 
3377 /* mailer_update_view */
_mailer_update_view(Mailer * mailer)3378 static void _mailer_update_view(Mailer * mailer)
3379 {
3380 	GtkTreeStore * store;
3381 	GtkTreeModel * model;
3382 	GtkTextBuffer * tbuf;
3383 
3384 	/* display headers */
3385 	if(mailer->folder_cur != NULL && (store = folder_get_messages(
3386 					mailer->folder_cur)) != NULL)
3387 	{
3388 		model = GTK_TREE_MODEL(store);
3389 		/* display message */
3390 		tbuf = account_select(mailer->account_cur, mailer->folder_cur,
3391 				mailer->message_cur);
3392 		gtk_text_view_set_buffer(GTK_TEXT_VIEW(mailer->bo_view), tbuf);
3393 	}
3394 	else
3395 		model = NULL;
3396 	gtk_tree_view_set_model(GTK_TREE_VIEW(mailer->he_view), model);
3397 	_mailer_refresh_plugin(mailer);
3398 	_mailer_refresh_title(mailer);
3399 	_mailer_update_status(mailer);
3400 }
3401 
3402 
3403 /* callbacks */
3404 /* mailer_on_online_toggled */
_mailer_on_online_toggled(gpointer data)3405 static void _mailer_on_online_toggled(gpointer data)
3406 {
3407 	Mailer * mailer = data;
3408 	gboolean online;
3409 
3410 	online = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
3411 				mailer->st_online));
3412 	mailer_set_online(mailer, online);
3413 }
3414 
3415 
3416 /* mailer_on_plugin_combo_changed */
_mailer_on_plugin_combo_changed(gpointer data)3417 static void _mailer_on_plugin_combo_changed(gpointer data)
3418 {
3419 	Mailer * mailer = data;
3420 	GtkTreeModel * model = GTK_TREE_MODEL(mailer->pl_store);
3421 	GtkTreeIter iter;
3422 	gboolean valid;
3423 	GtkWidget * widget;
3424 
3425 	for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
3426 			valid = gtk_tree_model_iter_next(model, &iter))
3427 	{
3428 		gtk_tree_model_get(GTK_TREE_MODEL(mailer->pl_store), &iter,
3429 				MPC_WIDGET, &widget, -1);
3430 		gtk_widget_hide(widget);
3431 	}
3432 	if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(mailer->pl_combo), &iter)
3433 			!= TRUE)
3434 		return;
3435 	gtk_tree_model_get(GTK_TREE_MODEL(mailer->pl_store), &iter, MPC_WIDGET,
3436 			&widget, -1);
3437 	gtk_widget_show(widget);
3438 	_mailer_refresh_plugin(mailer);
3439 }
3440