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.
28 #include <glib/gstdio.h>
32 #include <gconf/gconf-client.h>
34 #include <libgnome/gnome-config.h>
35 #include <libgnome/gnome-sound.h>
37 #include <libedataserverui/e-passwords.h>
38 #include <libedataserver/e-msgport.h>
40 #include <camel/camel.h> /* FIXME: this is where camel_init is defined, it shouldn't include everything else */
41 #include <camel/camel-filter-driver.h>
42 #include <camel/camel-i18n.h>
44 #include "e-util/e-error.h"
45 #include "e-util/e-util-private.h"
47 #include "em-filter-context.h"
48 #include "em-filter-rule.h"
49 #include "mail-component.h"
50 #include "mail-config.h"
53 #include "mail-session.h"
54 #include "mail-tools.h"
58 CamelSession
*session
;
59 static int session_check_junk_notify_id
= -1;
61 #define MAIL_SESSION_TYPE (mail_session_get_type ())
62 #define MAIL_SESSION(obj) (CAMEL_CHECK_CAST((obj), MAIL_SESSION_TYPE, MailSession))
63 #define MAIL_SESSION_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_SESSION_TYPE, MailSessionClass))
64 #define MAIL_IS_SESSION(o) (CAMEL_CHECK_TYPE((o), MAIL_SESSION_TYPE))
66 typedef struct _MailSession
{
67 CamelSession parent_object
;
72 MailAsyncEvent
*async
;
75 typedef struct _MailSessionClass
{
76 CamelSessionClass parent_class
;
80 static CamelSessionClass
*ms_parent_class
;
82 static char *get_password(CamelSession
*session
, CamelService
*service
, const char *domain
, const char *prompt
, const char *item
, guint32 flags
, CamelException
*ex
);
83 static void forget_password(CamelSession
*session
, CamelService
*service
, const char *domain
, const char *item
, CamelException
*ex
);
84 static gboolean
alert_user(CamelSession
*session
, CamelSessionAlertType type
, const char *prompt
, gboolean cancel
);
85 static CamelFilterDriver
*get_filter_driver(CamelSession
*session
, const char *type
, CamelException
*ex
);
87 static void ms_thread_status(CamelSession
*session
, CamelSessionThreadMsg
*msg
, const char *text
, int pc
);
88 static void *ms_thread_msg_new(CamelSession
*session
, CamelSessionThreadOps
*ops
, unsigned int size
);
89 static void ms_thread_msg_free(CamelSession
*session
, CamelSessionThreadMsg
*m
);
92 init (MailSession
*session
)
94 session
->async
= mail_async_event_new();
98 finalise (MailSession
*session
)
100 if (session_check_junk_notify_id
!= -1)
101 gconf_client_notify_remove (mail_config_get_gconf_client (), session_check_junk_notify_id
);
103 mail_async_event_destroy(session
->async
);
107 class_init (MailSessionClass
*mail_session_class
)
109 CamelSessionClass
*camel_session_class
= CAMEL_SESSION_CLASS (mail_session_class
);
111 /* virtual method override */
112 camel_session_class
->get_password
= get_password
;
113 camel_session_class
->forget_password
= forget_password
;
114 camel_session_class
->alert_user
= alert_user
;
115 camel_session_class
->get_filter_driver
= get_filter_driver
;
117 camel_session_class
->thread_msg_new
= ms_thread_msg_new
;
118 camel_session_class
->thread_msg_free
= ms_thread_msg_free
;
119 camel_session_class
->thread_status
= ms_thread_status
;
123 mail_session_get_type (void)
125 static CamelType mail_session_type
= CAMEL_INVALID_TYPE
;
127 if (mail_session_type
== CAMEL_INVALID_TYPE
) {
128 ms_parent_class
= (CamelSessionClass
*)camel_session_get_type();
129 mail_session_type
= camel_type_register (
130 camel_session_get_type (),
132 sizeof (MailSession
),
133 sizeof (MailSessionClass
),
134 (CamelObjectClassInitFunc
) class_init
,
136 (CamelObjectInitFunc
) init
,
137 (CamelObjectFinalizeFunc
) finalise
);
140 return mail_session_type
;
145 make_key (CamelService
*service
, const char *item
)
150 key
= camel_url_to_string (service
->url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
152 key
= g_strdup (item
);
157 /* ********************************************************************** */
160 get_password (CamelSession
*session
, CamelService
*service
, const char *domain
,
161 const char *prompt
, const char *item
, guint32 flags
, CamelException
*ex
)
165 EAccount
*account
= NULL
;
167 url
= service
?camel_url_to_string(service
->url
, CAMEL_URL_HIDE_ALL
):NULL
;
169 if (!strcmp(item
, "popb4smtp_uri")) {
170 /* not 100% mt safe, but should be ok */
172 && (account
= mail_config_get_account_by_transport_url(url
)))
173 ret
= g_strdup(account
->source
->url
);
175 char *key
= make_key(service
, item
);
176 EAccountService
*config_service
= NULL
;
181 ret
= e_passwords_get_password(domain
, key
);
182 if (ret
== NULL
|| (flags
& CAMEL_SESSION_PASSWORD_REPROMPT
)) {
188 if ((account
= mail_config_get_account_by_source_url(url
)))
189 config_service
= account
->source
;
190 else if ((account
= mail_config_get_account_by_transport_url(url
)))
191 config_service
= account
->transport
;
194 remember
= config_service
?config_service
->save_passwd
:FALSE
;
196 if (flags
& CAMEL_SESSION_PASSPHRASE
) {
198 title
= g_strdup_printf (_("Enter Passphrase for %s"), account
->name
);
200 title
= g_strdup (_("Enter Passphrase"));
203 title
= g_strdup_printf (_("Enter Password for %s"), account
->name
);
205 title
= g_strdup (_("Enter Password"));
207 if ((flags
& CAMEL_SESSION_PASSWORD_STATIC
) != 0)
208 eflags
= E_PASSWORDS_REMEMBER_NEVER
;
209 else if (config_service
== NULL
)
210 eflags
= E_PASSWORDS_REMEMBER_SESSION
;
212 eflags
= E_PASSWORDS_REMEMBER_FOREVER
;
214 if (flags
& CAMEL_SESSION_PASSWORD_REPROMPT
)
215 eflags
|= E_PASSWORDS_REPROMPT
;
217 if (flags
& CAMEL_SESSION_PASSWORD_SECRET
)
218 eflags
|= E_PASSWORDS_SECRET
;
220 if (flags
& CAMEL_SESSION_PASSPHRASE
)
221 eflags
|= E_PASSWORDS_PASSPHRASE
;
223 /* HACK: breaks abstraction ...
224 e_account_writable doesn't use the eaccount, it also uses the same writable key for
225 source and transport */
226 if (!e_account_writable(NULL
, E_ACCOUNT_SOURCE_SAVE_PASSWD
))
227 eflags
|= E_PASSWORDS_DISABLE_REMEMBER
;
229 ret
= e_passwords_ask_password(title
, domain
, key
, prompt
, eflags
, &remember
, NULL
);
233 if (ret
&& config_service
)
234 mail_config_service_set_save_passwd(config_service
, remember
);
243 camel_exception_set(ex
, CAMEL_EXCEPTION_USER_CANCEL
, _("User canceled operation."));
249 forget_password (CamelSession
*session
, CamelService
*service
, const char *domain
, const char *item
, CamelException
*ex
)
251 char *key
= make_key (service
, item
);
253 e_passwords_forget_password (domain
?domain
:"Mail", key
);
257 /* ********************************************************************** */
259 static GtkDialog
*message_dialog
;
260 static EDList message_list
= E_DLIST_INITIALISER(message_list
);
262 struct _user_message_msg
{
263 struct _mail_msg msg
;
265 CamelSessionAlertType type
;
268 unsigned int allow_cancel
:1;
269 unsigned int result
:1;
270 unsigned int ismain
:1;
273 static void do_user_message (struct _mail_msg
*mm
);
275 /* clicked, send back the reply */
277 user_message_response (GtkDialog
*dialog
, int button
, struct _user_message_msg
*m
)
279 gtk_widget_destroy ((GtkWidget
*) dialog
);
281 message_dialog
= NULL
;
283 /* if !allow_cancel, then we've already replied */
284 if (m
->allow_cancel
) {
285 m
->result
= button
== GTK_RESPONSE_OK
;
286 e_msgport_reply((EMsg
*)m
);
289 /* check for pendings */
290 if ((m
= (struct _user_message_msg
*)e_dlist_remhead(&message_list
)))
291 do_user_message((struct _mail_msg
*)m
);
295 user_message_destroy_notify (struct _user_message_msg
*m
, GObject
*deadbeef
)
297 message_dialog
= NULL
;
300 /* This is kinda ugly/inefficient, but oh well, it works */
301 static const char *error_type
[] = {
302 "mail:session-message-info", "mail:session-message-warning", "mail:session-message-error",
303 "mail:session-message-info-cancel", "mail:session-message-warning-cancel", "mail:session-message-error-cancel"
307 do_user_message (struct _mail_msg
*mm
)
309 struct _user_message_msg
*m
= (struct _user_message_msg
*)mm
;
312 if (!m
->ismain
&& message_dialog
!= NULL
) {
313 e_dlist_addtail (&message_list
, (EDListNode
*)m
);
318 case CAMEL_SESSION_ALERT_INFO
:
321 case CAMEL_SESSION_ALERT_WARNING
:
324 case CAMEL_SESSION_ALERT_ERROR
:
334 message_dialog
= (GtkDialog
*)e_error_new(NULL
, error_type
[type
], m
->prompt
, NULL
);
335 g_object_set ((GObject
*) message_dialog
, "allow_shrink", TRUE
, "allow_grow", TRUE
, NULL
);
337 /* We only need to wait for the result if we allow cancel otherwise show but send result back instantly */
338 if (m
->allow_cancel
) {
340 user_message_response(message_dialog
, gtk_dialog_run (message_dialog
), m
);
342 g_signal_connect (message_dialog
, "response", G_CALLBACK (user_message_response
), m
);
343 gtk_widget_show ((GtkWidget
*) message_dialog
);
346 g_signal_connect (message_dialog
, "response", G_CALLBACK (gtk_widget_destroy
), message_dialog
);
347 g_object_weak_ref ((GObject
*) message_dialog
, (GWeakNotify
) user_message_destroy_notify
, m
);
348 gtk_widget_show ((GtkWidget
*) message_dialog
);
354 free_user_message(struct _mail_msg
*mm
)
356 struct _user_message_msg
*m
= (struct _user_message_msg
*)mm
;
361 static struct _mail_msg_op user_message_op
= { NULL
, do_user_message
, NULL
, free_user_message
};
364 alert_user(CamelSession
*session
, CamelSessionAlertType type
, const char *prompt
, gboolean cancel
)
366 MailSession
*mail_session
= MAIL_SESSION (session
);
367 struct _user_message_msg
*m
, *r
;
368 EMsgPort
*user_message_reply
= NULL
;
371 if (!mail_session
->interactive
)
375 user_message_reply
= e_msgport_new ();
376 m
= mail_msg_new (&user_message_op
, user_message_reply
, sizeof (*m
));
377 m
->ismain
= pthread_equal(pthread_self(), mail_gui_thread
);
379 m
->prompt
= g_strdup(prompt
);
380 m
->allow_cancel
= cancel
;
383 do_user_message((struct _mail_msg
*)m
);
385 extern EMsgPort
*mail_gui_port2
;
387 e_msgport_put(mail_gui_port2
, (EMsg
*)m
);
391 r
= (struct _user_message_msg
*)e_msgport_wait(user_message_reply
);
396 e_msgport_destroy(user_message_reply
);
404 get_folder (CamelFilterDriver
*d
, const char *uri
, void *data
, CamelException
*ex
)
406 return mail_tool_uri_to_folder(uri
, 0, ex
);
410 main_play_sound (CamelFilterDriver
*driver
, char *filename
, gpointer user_data
)
412 if (filename
&& *filename
)
413 gnome_sound_play (filename
);
418 camel_object_unref (session
);
422 session_play_sound (CamelFilterDriver
*driver
, const char *filename
, gpointer user_data
)
424 MailSession
*ms
= (MailSession
*) session
;
426 camel_object_ref (session
);
428 mail_async_event_emit (ms
->async
, MAIL_ASYNC_GUI
, (MailAsyncFunc
) main_play_sound
,
429 driver
, g_strdup (filename
), user_data
);
433 main_system_beep (CamelFilterDriver
*driver
, gpointer user_data
)
439 session_system_beep (CamelFilterDriver
*driver
, gpointer user_data
)
441 MailSession
*ms
= (MailSession
*) session
;
443 camel_object_ref (session
);
445 mail_async_event_emit (ms
->async
, MAIL_ASYNC_GUI
, (MailAsyncFunc
) main_system_beep
,
446 driver
, user_data
, NULL
);
449 static CamelFilterDriver
*
450 main_get_filter_driver (CamelSession
*session
, const char *type
, CamelException
*ex
)
452 CamelFilterDriver
*driver
;
453 FilterRule
*rule
= NULL
;
458 gconf
= mail_config_get_gconf_client ();
460 user
= g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
461 system
= g_build_filename (EVOLUTION_PRIVDATADIR
, "filtertypes.xml", NULL
);
462 fc
= (RuleContext
*) em_filter_context_new ();
463 rule_context_load (fc
, system
, user
);
467 driver
= camel_filter_driver_new (session
);
468 camel_filter_driver_set_folder_func (driver
, get_folder
, NULL
);
470 if (gconf_client_get_bool (gconf
, "/apps/evolution/mail/filters/log", NULL
)) {
471 MailSession
*ms
= (MailSession
*) session
;
473 if (ms
->filter_logfile
== NULL
) {
476 filename
= gconf_client_get_string (gconf
, "/apps/evolution/mail/filters/logfile", NULL
);
478 ms
->filter_logfile
= g_fopen (filename
, "a+");
483 if (ms
->filter_logfile
)
484 camel_filter_driver_set_logfile (driver
, ms
->filter_logfile
);
487 camel_filter_driver_set_shell_func (driver
, mail_execute_shell_command
, NULL
);
488 camel_filter_driver_set_play_sound_func (driver
, session_play_sound
, NULL
);
489 camel_filter_driver_set_system_beep_func (driver
, session_system_beep
, NULL
);
491 if ((!strcmp (type
, FILTER_SOURCE_INCOMING
) || !strcmp (type
, FILTER_SOURCE_JUNKTEST
))
492 && camel_session_check_junk (session
)) {
493 /* implicit junk check as 1st rule */
494 camel_filter_driver_add_rule (driver
, "Junk check", "(junk-test)", "(begin (set-system-flag \"junk\")(set-system-flag \"seen\"))");
497 if (strcmp (type
, FILTER_SOURCE_JUNKTEST
) != 0) {
498 GString
*fsearch
, *faction
;
500 fsearch
= g_string_new ("");
501 faction
= g_string_new ("");
503 if (!strcmp (type
, FILTER_SOURCE_DEMAND
))
504 type
= FILTER_SOURCE_INCOMING
;
506 /* add the user-defined rules next */
507 while ((rule
= rule_context_next_rule (fc
, rule
, type
))) {
508 g_string_truncate (fsearch
, 0);
509 g_string_truncate (faction
, 0);
511 filter_rule_build_code (rule
, fsearch
);
512 em_filter_rule_build_action ((EMFilterRule
*) rule
, faction
);
513 camel_filter_driver_add_rule (driver
, rule
->name
, fsearch
->str
, faction
->str
);
516 g_string_free (fsearch
, TRUE
);
517 g_string_free (faction
, TRUE
);
525 static CamelFilterDriver
*
526 get_filter_driver (CamelSession
*session
, const char *type
, CamelException
*ex
)
528 return (CamelFilterDriver
*) mail_call_main (MAIL_CALL_p_ppp
, (MailMainFunc
) main_get_filter_driver
,
532 /* TODO: This is very temporary, until we have a better way to do the progress reporting,
533 we just borrow a dummy mail-mt thread message and hook it onto out camel thread message */
535 static mail_msg_op_t ms_thread_ops_dummy
= { NULL
};
537 static void *ms_thread_msg_new(CamelSession
*session
, CamelSessionThreadOps
*ops
, unsigned int size
)
539 CamelSessionThreadMsg
*msg
= ms_parent_class
->thread_msg_new(session
, ops
, size
);
541 /* We create a dummy mail_msg, and then copy its cancellation port over to ours, so
542 we get cancellation and progress in common with hte existing mail code, for free */
544 struct _mail_msg
*m
= mail_msg_new(&ms_thread_ops_dummy
, NULL
, sizeof(struct _mail_msg
));
547 camel_operation_unref(msg
->op
);
549 camel_operation_ref(msg
->op
);
555 static void ms_thread_msg_free(CamelSession
*session
, CamelSessionThreadMsg
*m
)
557 mail_msg_free(m
->data
);
558 ms_parent_class
->thread_msg_free(session
, m
);
561 static void ms_thread_status(CamelSession
*session
, CamelSessionThreadMsg
*msg
, const char *text
, int pc
)
563 /* This should never be called since we bypass it in alloc! */
564 printf("Thread status '%s' %d%%\n", text
, pc
);
568 mail_session_get_password (const char *url_string
)
574 url
= camel_url_new (url_string
, NULL
);
575 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
576 camel_url_free (url
);
578 passwd
= e_passwords_get_password ("Mail", simple_url
);
586 mail_session_add_password (const char *url_string
,
592 url
= camel_url_new (url_string
, NULL
);
593 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
594 camel_url_free (url
);
596 e_passwords_add_password (simple_url
, passwd
);
602 mail_session_remember_password (const char *url_string
)
607 url
= camel_url_new (url_string
, NULL
);
608 simple_url
= camel_url_to_string (url
, CAMEL_URL_HIDE_PASSWORD
| CAMEL_URL_HIDE_PARAMS
);
609 camel_url_free (url
);
611 e_passwords_remember_password ("Mail", simple_url
);
617 mail_session_forget_password (const char *key
)
619 e_passwords_forget_password ("Mail", key
);
623 mail_session_check_junk_notify (GConfClient
*gconf
, guint id
, GConfEntry
*entry
, CamelSession
*session
)
627 g_return_if_fail (gconf_entry_get_key (entry
) != NULL
);
628 g_return_if_fail (gconf_entry_get_value (entry
) != NULL
);
630 key
= strrchr (gconf_entry_get_key (entry
), '/');
633 if (!strcmp (key
, "check_incoming"))
634 camel_session_set_check_junk (session
, gconf_value_get_bool (gconf_entry_get_value (entry
)));
639 mail_session_init (const char *base_directory
)
644 if (camel_init (base_directory
, TRUE
) != 0)
647 camel_provider_init();
649 session
= CAMEL_SESSION (camel_object_new (MAIL_SESSION_TYPE
));
651 camel_dir
= g_strdup_printf ("%s/mail", base_directory
);
652 camel_session_construct (session
, camel_dir
);
654 gconf
= mail_config_get_gconf_client ();
655 gconf_client_add_dir (gconf
, "/apps/evolution/mail/junk", GCONF_CLIENT_PRELOAD_ONELEVEL
, NULL
);
656 camel_session_set_check_junk (session
, gconf_client_get_bool (gconf
, "/apps/evolution/mail/junk/check_incoming", NULL
));
657 session_check_junk_notify_id
= gconf_client_notify_add (gconf
, "/apps/evolution/mail/junk",
658 (GConfClientNotifyFunc
) mail_session_check_junk_notify
,
659 session
, NULL
, NULL
);
660 session
->junk_plugin
= NULL
;
662 /* The shell will tell us to go online. */
663 camel_session_set_online ((CamelSession
*) session
, FALSE
);
669 mail_session_get_interactive (void)
671 return MAIL_SESSION (session
)->interactive
;
675 mail_session_set_interactive (gboolean interactive
)
677 MAIL_SESSION (session
)->interactive
= interactive
;
680 struct _user_message_msg
*um
;
682 d(printf ("Gone non-interactive, checking for outstanding interactive tasks\n"));
684 e_passwords_cancel();
686 /* flush/cancel pending user messages */
687 while ((um
= (struct _user_message_msg
*) e_dlist_remhead (&message_list
))) {
688 d(printf ("Flusing message request: %s\n", um
->prompt
));
689 e_msgport_reply((EMsg
*) um
);
692 /* and the current */
693 if (message_dialog
) {
694 d(printf("Destroying message dialogue\n"));
695 gtk_widget_destroy ((GtkWidget
*) message_dialog
);
701 mail_session_forget_passwords (BonoboUIComponent
*uih
, void *user_data
,
704 e_passwords_forget_passwords ();
708 mail_session_flush_filter_log (void)
710 MailSession
*ms
= (MailSession
*) session
;
712 if (ms
->filter_logfile
)
713 fflush (ms
->filter_logfile
);