1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* mail-session.c: handles the session information and resource manipulation */
4 * Copyright 2001 Ximian, Inc. (www.ximian.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
29 #include <gtk/gtkdialog.h>
30 #include <gtk/gtkstock.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtkmessagedialog.h>
33 #include <gtk/gtktogglebutton.h>
34 #include <gtk/gtkbox.h>
35 #include <gtk/gtkvbox.h>
36 #include <gtk/gtkcheckbutton.h>
38 #include <gconf/gconf-client.h>
40 #include <libgnome/gnome-config.h>
41 #include <libgnome/gnome-sound.h>
43 #include <camel/camel.h> /* FIXME: this is where camel_init is defined, it shouldn't include everything else */
44 #include "camel/camel-filter-driver.h"
45 #include <camel/camel-i18n.h>
47 #include "em-filter-context.h"
48 #include "em-filter-rule.h"
49 #include "mail-component.h"
50 #include "mail-config.h"
51 #include "mail-session.h"
52 #include "mail-tools.h"
55 #include <libedataserverui/e-passwords.h>
56 #include "libedataserver/e-msgport.h"
57 #include "e-util/e-error.h"
61 CamelSession
*session
;
62 static int session_check_junk_notify_id
= -1;
64 #define MAIL_SESSION_TYPE (mail_session_get_type ())
65 #define MAIL_SESSION(obj) (CAMEL_CHECK_CAST((obj), MAIL_SESSION_TYPE, MailSession))
66 #define MAIL_SESSION_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_SESSION_TYPE, MailSessionClass))
67 #define MAIL_IS_SESSION(o) (CAMEL_CHECK_TYPE((o), MAIL_SESSION_TYPE))
69 #define MAIL_SESSION_LOCK(s, l) (e_mutex_lock(((MailSession *)s)->l))
70 #define MAIL_SESSION_UNLOCK(s, l) (e_mutex_unlock(((MailSession *)s)->l))
72 typedef struct _MailSession
{
73 CamelSession parent_object
;
80 MailAsyncEvent
*async
;
83 typedef struct _MailSessionClass
{
84 CamelSessionClass parent_class
;
88 static CamelSessionClass
*ms_parent_class
;
90 static char *get_password(CamelSession
*session
, CamelService
*service
, const char *domain
, const char *prompt
, const char *item
, guint32 flags
, CamelException
*ex
);
91 static void forget_password(CamelSession
*session
, CamelService
*service
, const char *domain
, const char *item
, CamelException
*ex
);
92 static gboolean
alert_user(CamelSession
*session
, CamelSessionAlertType type
, const char *prompt
, gboolean cancel
);
93 static CamelFilterDriver
*get_filter_driver(CamelSession
*session
, const char *type
, CamelException
*ex
);
95 static void ms_thread_status(CamelSession
*session
, CamelSessionThreadMsg
*msg
, const char *text
, int pc
);
96 static void *ms_thread_msg_new(CamelSession
*session
, CamelSessionThreadOps
*ops
, unsigned int size
);
97 static void ms_thread_msg_free(CamelSession
*session
, CamelSessionThreadMsg
*m
);
100 init (MailSession
*session
)
102 session
->lock
= e_mutex_new(E_MUTEX_REC
);
103 session
->async
= mail_async_event_new();
107 finalise (MailSession
*session
)
109 if (session_check_junk_notify_id
!= -1)
110 gconf_client_notify_remove (mail_config_get_gconf_client (), session_check_junk_notify_id
);
112 mail_async_event_destroy(session
->async
);
113 e_mutex_destroy(session
->lock
);
117 class_init (MailSessionClass
*mail_session_class
)
119 CamelSessionClass
*camel_session_class
= CAMEL_SESSION_CLASS (mail_session_class
);
121 /* virtual method override */
122 camel_session_class
->get_password
= get_password
;
123 camel_session_class
->forget_password
= forget_password
;
124 camel_session_class
->alert_user
= alert_user
;
125 camel_session_class
->get_filter_driver
= get_filter_driver
;
127 camel_session_class
->thread_msg_new
= ms_thread_msg_new
;
128 camel_session_class
->thread_msg_free
= ms_thread_msg_free
;
129 camel_session_class
->thread_status
= ms_thread_status
;
133 mail_session_get_type (void)
135 static CamelType mail_session_type
= CAMEL_INVALID_TYPE
;
137 if (mail_session_type
== CAMEL_INVALID_TYPE
) {
138 ms_parent_class
= (CamelSessionClass
*)camel_session_get_type();
139 mail_session_type
= camel_type_register (
140 camel_session_get_type (),
142 sizeof (MailSession
),
143 sizeof (MailSessionClass
),
144 (CamelObjectClassInitFunc
) class_init
,
146 (CamelObjectInitFunc
) init
,
147 (CamelObjectFinalizeFunc
) finalise
);
150 return mail_session_type
;
155 make_key (CamelService
*service
, const char *item
)
160 key
= camel_url_to_string (service
->url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
162 key
= g_strdup (item
);
167 /* ********************************************************************** */
170 get_password (CamelSession
*session
, CamelService
*service
, const char *domain
,
171 const char *prompt
, const char *item
, guint32 flags
, CamelException
*ex
)
175 EAccount
*account
= NULL
;
177 url
= service
?camel_url_to_string(service
->url
, CAMEL_URL_HIDE_ALL
):NULL
;
179 if (!strcmp(item
, "popb4smtp_uri")) {
180 /* not 100% mt safe, but should be ok */
182 && (account
= mail_config_get_account_by_transport_url(url
)))
183 ret
= g_strdup(account
->source
->url
);
185 char *key
= make_key(service
, item
);
186 EAccountService
*config_service
= NULL
;
191 ret
= e_passwords_get_password(domain
, key
);
192 if (ret
== NULL
|| (flags
& CAMEL_SESSION_PASSWORD_REPROMPT
)) {
198 if ((account
= mail_config_get_account_by_source_url(url
)))
199 config_service
= account
->source
;
200 else if ((account
= mail_config_get_account_by_transport_url(url
)))
201 config_service
= account
->transport
;
204 remember
= config_service
?config_service
->save_passwd
:FALSE
;
207 title
= g_strdup_printf (_("Enter Password for %s"), account
->name
);
209 title
= g_strdup (_("Enter Password"));
211 if ((flags
& CAMEL_SESSION_PASSWORD_STATIC
) != 0)
212 eflags
= E_PASSWORDS_REMEMBER_NEVER
;
213 else if (config_service
== NULL
)
214 eflags
= E_PASSWORDS_REMEMBER_SESSION
;
216 eflags
= E_PASSWORDS_REMEMBER_FOREVER
;
218 if (flags
& CAMEL_SESSION_PASSWORD_REPROMPT
)
219 eflags
|= E_PASSWORDS_REPROMPT
;
221 if (flags
& CAMEL_SESSION_PASSWORD_SECRET
)
222 eflags
|= E_PASSWORDS_SECRET
;
224 /* HACK: breaks abstraction ...
225 e_account_writable doesn't use the eaccount, it also uses the same writable key for
226 source and transport */
227 if (!e_account_writable(NULL
, E_ACCOUNT_SOURCE_SAVE_PASSWD
))
228 eflags
|= E_PASSWORDS_DISABLE_REMEMBER
;
230 ret
= e_passwords_ask_password(title
, domain
, key
, prompt
, eflags
, &remember
, NULL
);
234 if (ret
&& config_service
)
235 mail_config_service_set_save_passwd(config_service
, remember
);
244 camel_exception_set(ex
, CAMEL_EXCEPTION_USER_CANCEL
, _("User canceled operation."));
250 forget_password (CamelSession
*session
, CamelService
*service
, const char *domain
, const char *item
, CamelException
*ex
)
252 char *key
= make_key (service
, item
);
254 e_passwords_forget_password (domain
?domain
:"Mail", key
);
258 /* ********************************************************************** */
260 static GtkDialog
*message_dialog
;
261 static EDList message_list
= E_DLIST_INITIALISER(message_list
);
263 struct _user_message_msg
{
264 struct _mail_msg msg
;
266 CamelSessionAlertType type
;
269 unsigned int allow_cancel
:1;
270 unsigned int result
:1;
271 unsigned int ismain
:1;
274 static void do_user_message (struct _mail_msg
*mm
);
276 /* clicked, send back the reply */
278 user_message_response (GtkDialog
*dialog
, int button
, struct _user_message_msg
*m
)
280 gtk_widget_destroy ((GtkWidget
*) dialog
);
282 message_dialog
= NULL
;
284 /* if !allow_cancel, then we've already replied */
285 if (m
->allow_cancel
) {
286 m
->result
= button
== GTK_RESPONSE_OK
;
287 e_msgport_reply((EMsg
*)m
);
290 /* check for pendings */
291 if ((m
= (struct _user_message_msg
*)e_dlist_remhead(&message_list
)))
292 do_user_message((struct _mail_msg
*)m
);
296 user_message_destroy_notify (struct _user_message_msg
*m
, GObject
*deadbeef
)
298 message_dialog
= NULL
;
301 /* This is kinda ugly/inefficient, but oh well, it works */
302 static const char *error_type
[] = {
303 "mail:session-message-info", "mail:session-message-warning", "mail:session-message-error",
304 "mail:session-message-info-cancel", "mail:session-message-warning-cancel", "mail:session-message-error-cancel"
308 do_user_message (struct _mail_msg
*mm
)
310 struct _user_message_msg
*m
= (struct _user_message_msg
*)mm
;
313 if (!m
->ismain
&& message_dialog
!= NULL
) {
314 e_dlist_addtail (&message_list
, (EDListNode
*)m
);
319 case CAMEL_SESSION_ALERT_INFO
:
322 case CAMEL_SESSION_ALERT_WARNING
:
325 case CAMEL_SESSION_ALERT_ERROR
:
335 message_dialog
= (GtkDialog
*)e_error_new(NULL
, error_type
[type
], m
->prompt
, NULL
);
336 g_object_set ((GObject
*) message_dialog
, "allow_shrink", TRUE
, "allow_grow", TRUE
, NULL
);
338 /* We only need to wait for the result if we allow cancel otherwise show but send result back instantly */
339 if (m
->allow_cancel
) {
341 user_message_response(message_dialog
, gtk_dialog_run (message_dialog
), m
);
343 g_signal_connect (message_dialog
, "response", G_CALLBACK (user_message_response
), m
);
344 gtk_widget_show ((GtkWidget
*) message_dialog
);
347 g_signal_connect (message_dialog
, "response", G_CALLBACK (gtk_widget_destroy
), message_dialog
);
348 g_object_weak_ref ((GObject
*) message_dialog
, (GWeakNotify
) user_message_destroy_notify
, m
);
349 gtk_widget_show ((GtkWidget
*) message_dialog
);
355 free_user_message(struct _mail_msg
*mm
)
357 struct _user_message_msg
*m
= (struct _user_message_msg
*)mm
;
362 static struct _mail_msg_op user_message_op
= { NULL
, do_user_message
, NULL
, free_user_message
};
365 alert_user(CamelSession
*session
, CamelSessionAlertType type
, const char *prompt
, gboolean cancel
)
367 MailSession
*mail_session
= MAIL_SESSION (session
);
368 struct _user_message_msg
*m
, *r
;
369 EMsgPort
*user_message_reply
= NULL
;
372 if (!mail_session
->interactive
)
376 user_message_reply
= e_msgport_new ();
377 m
= mail_msg_new (&user_message_op
, user_message_reply
, sizeof (*m
));
378 m
->ismain
= pthread_self() == mail_gui_thread
;
380 m
->prompt
= g_strdup(prompt
);
381 m
->allow_cancel
= cancel
;
384 do_user_message((struct _mail_msg
*)m
);
386 extern EMsgPort
*mail_gui_port2
;
388 e_msgport_put(mail_gui_port2
, (EMsg
*)m
);
392 e_msgport_wait(user_message_reply
);
393 r
= (struct _user_message_msg
*)e_msgport_get(user_message_reply
);
398 e_msgport_destroy(user_message_reply
);
406 get_folder (CamelFilterDriver
*d
, const char *uri
, void *data
, CamelException
*ex
)
408 return mail_tool_uri_to_folder(uri
, 0, ex
);
412 main_play_sound (CamelFilterDriver
*driver
, char *filename
, gpointer user_data
)
414 if (filename
&& *filename
)
415 gnome_sound_play (filename
);
420 camel_object_unref (session
);
424 session_play_sound (CamelFilterDriver
*driver
, const char *filename
, gpointer user_data
)
426 MailSession
*ms
= (MailSession
*) session
;
428 camel_object_ref (session
);
430 mail_async_event_emit (ms
->async
, MAIL_ASYNC_GUI
, (MailAsyncFunc
) main_play_sound
,
431 driver
, g_strdup (filename
), user_data
);
435 main_system_beep (CamelFilterDriver
*driver
, gpointer user_data
)
441 session_system_beep (CamelFilterDriver
*driver
, gpointer user_data
)
443 MailSession
*ms
= (MailSession
*) session
;
445 camel_object_ref (session
);
447 mail_async_event_emit (ms
->async
, MAIL_ASYNC_GUI
, (MailAsyncFunc
) main_system_beep
,
448 driver
, user_data
, NULL
);
451 static CamelFilterDriver
*
452 main_get_filter_driver (CamelSession
*session
, const char *type
, CamelException
*ex
)
454 CamelFilterDriver
*driver
;
455 FilterRule
*rule
= NULL
;
460 gconf
= mail_config_get_gconf_client ();
462 user
= g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
463 system
= EVOLUTION_PRIVDATADIR
"/filtertypes.xml";
464 fc
= (RuleContext
*) em_filter_context_new ();
465 rule_context_load (fc
, system
, user
);
468 driver
= camel_filter_driver_new (session
);
469 camel_filter_driver_set_folder_func (driver
, get_folder
, NULL
);
471 if (gconf_client_get_bool (gconf
, "/apps/evolution/mail/filters/log", NULL
)) {
472 MailSession
*ms
= (MailSession
*) session
;
474 if (ms
->filter_logfile
== NULL
) {
477 filename
= gconf_client_get_string (gconf
, "/apps/evolution/mail/filters/logfile", NULL
);
479 ms
->filter_logfile
= fopen (filename
, "a+");
484 if (ms
->filter_logfile
)
485 camel_filter_driver_set_logfile (driver
, ms
->filter_logfile
);
488 camel_filter_driver_set_shell_func (driver
, mail_execute_shell_command
, NULL
);
489 camel_filter_driver_set_play_sound_func (driver
, session_play_sound
, NULL
);
490 camel_filter_driver_set_system_beep_func (driver
, session_system_beep
, NULL
);
492 if ((!strcmp (type
, FILTER_SOURCE_INCOMING
) || !strcmp (type
, FILTER_SOURCE_JUNKTEST
))
493 && camel_session_check_junk (session
)) {
494 /* implicit junk check as 1st rule */
495 camel_filter_driver_add_rule (driver
, "Junk check", "(junk-test)", "(begin (set-system-flag \"junk\")(set-system-flag \"seen\"))");
498 if (strcmp (type
, FILTER_SOURCE_JUNKTEST
) != 0) {
499 GString
*fsearch
, *faction
;
501 fsearch
= g_string_new ("");
502 faction
= g_string_new ("");
504 if (!strcmp (type
, FILTER_SOURCE_DEMAND
))
505 type
= FILTER_SOURCE_INCOMING
;
507 /* add the user-defined rules next */
508 while ((rule
= rule_context_next_rule (fc
, rule
, type
))) {
509 g_string_truncate (fsearch
, 0);
510 g_string_truncate (faction
, 0);
512 filter_rule_build_code (rule
, fsearch
);
513 em_filter_rule_build_action ((EMFilterRule
*) rule
, faction
);
514 camel_filter_driver_add_rule (driver
, rule
->name
, fsearch
->str
, faction
->str
);
517 g_string_free (fsearch
, TRUE
);
518 g_string_free (faction
, TRUE
);
526 static CamelFilterDriver
*
527 get_filter_driver (CamelSession
*session
, const char *type
, CamelException
*ex
)
529 return (CamelFilterDriver
*) mail_call_main (MAIL_CALL_p_ppp
, (MailMainFunc
) main_get_filter_driver
,
533 /* TODO: This is very temporary, until we have a better way to do the progress reporting,
534 we just borrow a dummy mail-mt thread message and hook it onto out camel thread message */
536 static mail_msg_op_t ms_thread_ops_dummy
= { NULL
};
538 static void *ms_thread_msg_new(CamelSession
*session
, CamelSessionThreadOps
*ops
, unsigned int size
)
540 CamelSessionThreadMsg
*msg
= ms_parent_class
->thread_msg_new(session
, ops
, size
);
542 /* We create a dummy mail_msg, and then copy its cancellation port over to ours, so
543 we get cancellation and progress in common with hte existing mail code, for free */
545 struct _mail_msg
*m
= mail_msg_new(&ms_thread_ops_dummy
, NULL
, sizeof(struct _mail_msg
));
548 camel_operation_unref(msg
->op
);
550 camel_operation_ref(msg
->op
);
556 static void ms_thread_msg_free(CamelSession
*session
, CamelSessionThreadMsg
*m
)
558 mail_msg_free(m
->data
);
559 ms_parent_class
->thread_msg_free(session
, m
);
562 static void ms_thread_status(CamelSession
*session
, CamelSessionThreadMsg
*msg
, const char *text
, int pc
)
564 /* This should never be called since we bypass it in alloc! */
565 printf("Thread status '%s' %d%%\n", text
, pc
);
569 mail_session_get_password (const char *url_string
)
575 url
= camel_url_new (url_string
, NULL
);
576 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
577 camel_url_free (url
);
579 passwd
= e_passwords_get_password ("Mail", simple_url
);
587 mail_session_add_password (const char *url_string
,
593 url
= camel_url_new (url_string
, NULL
);
594 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
595 camel_url_free (url
);
597 e_passwords_add_password (simple_url
, passwd
);
603 mail_session_remember_password (const char *url_string
)
608 url
= camel_url_new (url_string
, NULL
);
609 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
610 camel_url_free (url
);
612 e_passwords_remember_password ("Mail", simple_url
);
618 mail_session_forget_password (const char *key
)
620 e_passwords_forget_password ("Mail", key
);
624 mail_session_check_junk_notify (GConfClient
*gconf
, guint id
, GConfEntry
*entry
, CamelSession
*session
)
628 g_return_if_fail (gconf_entry_get_key (entry
) != NULL
);
629 g_return_if_fail (gconf_entry_get_value (entry
) != NULL
);
631 key
= strrchr (gconf_entry_get_key (entry
), '/');
634 if (!strcmp (key
, "check_incoming"))
635 camel_session_set_check_junk (session
, gconf_value_get_bool (gconf_entry_get_value (entry
)));
640 mail_session_init (const char *base_directory
)
645 if (camel_init (base_directory
, TRUE
) != 0)
648 camel_provider_init();
650 session
= CAMEL_SESSION (camel_object_new (MAIL_SESSION_TYPE
));
652 camel_dir
= g_strdup_printf ("%s/mail", base_directory
);
653 camel_session_construct (session
, camel_dir
);
655 gconf
= mail_config_get_gconf_client ();
656 gconf_client_add_dir (gconf
, "/apps/evolution/mail/junk", GCONF_CLIENT_PRELOAD_ONELEVEL
, NULL
);
657 camel_session_set_check_junk (session
, gconf_client_get_bool (gconf
, "/apps/evolution/mail/junk/check_incoming", NULL
));
658 session_check_junk_notify_id
= gconf_client_notify_add (gconf
, "/apps/evolution/mail/junk",
659 (GConfClientNotifyFunc
) mail_session_check_junk_notify
,
660 session
, NULL
, NULL
);
661 session
->junk_plugin
= NULL
;
663 /* The shell will tell us to go online. */
664 camel_session_set_online ((CamelSession
*) session
, FALSE
);
670 mail_session_get_interactive (void)
672 return MAIL_SESSION (session
)->interactive
;
676 mail_session_set_interactive (gboolean interactive
)
678 MAIL_SESSION (session
)->interactive
= interactive
;
681 struct _user_message_msg
*um
;
683 d(printf ("Gone non-interactive, checking for outstanding interactive tasks\n"));
685 e_passwords_cancel();
687 /* flush/cancel pending user messages */
688 while ((um
= (struct _user_message_msg
*) e_dlist_remhead (&message_list
))) {
689 d(printf ("Flusing message request: %s\n", um
->prompt
));
690 e_msgport_reply((EMsg
*) um
);
693 /* and the current */
694 if (message_dialog
) {
695 d(printf("Destroying message dialogue\n"));
696 gtk_widget_destroy ((GtkWidget
*) message_dialog
);
702 mail_session_forget_passwords (BonoboUIComponent
*uih
, void *user_data
,
705 e_passwords_forget_passwords ();
709 mail_session_flush_filter_log (void)
711 MailSession
*ms
= (MailSession
*) session
;
713 if (ms
->filter_logfile
)
714 fflush (ms
->filter_logfile
);