Updated Traditional Chinese translation(Hong Kong). Updated Traditional
[evolution.git] / mail / mail-session.c
blobee5febe99dda2953877c7ba746c5019d38d86b04
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 */
3 /*
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.
22 #include <config.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include <glib.h>
28 #include <glib/gstdio.h>
30 #include <gtk/gtk.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"
51 #include "mail-mt.h"
52 #include "mail-ops.h"
53 #include "mail-session.h"
54 #include "mail-tools.h"
56 #define d(x)
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;
69 gboolean interactive;
70 FILE *filter_logfile;
72 MailAsyncEvent *async;
73 } MailSession;
75 typedef struct _MailSessionClass {
76 CamelSessionClass parent_class;
78 } MailSessionClass;
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);
91 static void
92 init (MailSession *session)
94 session->async = mail_async_event_new();
97 static void
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);
106 static void
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;
122 static CamelType
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 (),
131 "MailSession",
132 sizeof (MailSession),
133 sizeof (MailSessionClass),
134 (CamelObjectClassInitFunc) class_init,
135 NULL,
136 (CamelObjectInitFunc) init,
137 (CamelObjectFinalizeFunc) finalise);
140 return mail_session_type;
144 static char *
145 make_key (CamelService *service, const char *item)
147 char *key;
149 if (service)
150 key = camel_url_to_string (service->url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
151 else
152 key = g_strdup (item);
154 return key;
157 /* ********************************************************************** */
159 static char *
160 get_password (CamelSession *session, CamelService *service, const char *domain,
161 const char *prompt, const char *item, guint32 flags, CamelException *ex)
163 char *url;
164 char *ret = NULL;
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 */
171 if (url
172 && (account = mail_config_get_account_by_transport_url(url)))
173 ret = g_strdup(account->source->url);
174 } else {
175 char *key = make_key(service, item);
176 EAccountService *config_service = NULL;
178 if (domain == NULL)
179 domain = "Mail";
181 ret = e_passwords_get_password(domain, key);
182 if (ret == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
183 guint32 eflags;
184 gboolean remember;
185 char *title;
187 if (url) {
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) {
197 if (account)
198 title = g_strdup_printf (_("Enter Passphrase for %s"), account->name);
199 else
200 title = g_strdup (_("Enter Passphrase"));
201 } else {
202 if (account)
203 title = g_strdup_printf (_("Enter Password for %s"), account->name);
204 else
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;
211 else
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);
231 g_free(title);
233 if (ret && config_service)
234 mail_config_service_set_save_passwd(config_service, remember);
237 g_free(key);
240 g_free(url);
242 if (ret == NULL)
243 camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled operation."));
245 return ret;
248 static void
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);
254 g_free (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;
266 char *prompt;
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 */
276 static void
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);
294 static void
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"
306 static void
307 do_user_message (struct _mail_msg *mm)
309 struct _user_message_msg *m = (struct _user_message_msg *)mm;
310 int type;
312 if (!m->ismain && message_dialog != NULL) {
313 e_dlist_addtail (&message_list, (EDListNode *)m);
314 return;
317 switch (m->type) {
318 case CAMEL_SESSION_ALERT_INFO:
319 type = 0;
320 break;
321 case CAMEL_SESSION_ALERT_WARNING:
322 type = 1;
323 break;
324 case CAMEL_SESSION_ALERT_ERROR:
325 type = 2;
326 break;
327 default:
328 type = 0;
331 if (m->allow_cancel)
332 type += 3;
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) {
339 if (m->ismain) {
340 user_message_response(message_dialog, gtk_dialog_run (message_dialog), m);
341 } else {
342 g_signal_connect (message_dialog, "response", G_CALLBACK (user_message_response), m);
343 gtk_widget_show ((GtkWidget *) message_dialog);
345 } else {
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);
349 mail_msg_free(m);
353 static void
354 free_user_message(struct _mail_msg *mm)
356 struct _user_message_msg *m = (struct _user_message_msg *)mm;
358 g_free(m->prompt);
361 static struct _mail_msg_op user_message_op = { NULL, do_user_message, NULL, free_user_message };
363 static gboolean
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;
369 gboolean ret;
371 if (!mail_session->interactive)
372 return FALSE;
374 if (cancel)
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);
378 m->type = type;
379 m->prompt = g_strdup(prompt);
380 m->allow_cancel = cancel;
382 if (m->ismain)
383 do_user_message((struct _mail_msg *)m);
384 else {
385 extern EMsgPort *mail_gui_port2;
387 e_msgport_put(mail_gui_port2, (EMsg *)m);
390 if (cancel) {
391 r = (struct _user_message_msg *)e_msgport_wait(user_message_reply);
392 g_assert(m == r);
394 ret = m->result;
395 mail_msg_free(m);
396 e_msgport_destroy(user_message_reply);
397 } else
398 ret = TRUE;
400 return ret;
403 static CamelFolder *
404 get_folder (CamelFilterDriver *d, const char *uri, void *data, CamelException *ex)
406 return mail_tool_uri_to_folder(uri, 0, ex);
409 static void
410 main_play_sound (CamelFilterDriver *driver, char *filename, gpointer user_data)
412 if (filename && *filename)
413 gnome_sound_play (filename);
414 else
415 gdk_beep ();
417 g_free (filename);
418 camel_object_unref (session);
421 static void
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);
432 static void
433 main_system_beep (CamelFilterDriver *driver, gpointer user_data)
435 gdk_beep ();
438 static void
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;
454 char *user, *system;
455 GConfClient *gconf;
456 RuleContext *fc;
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);
464 g_free (system);
465 g_free (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) {
474 char *filename;
476 filename = gconf_client_get_string (gconf, "/apps/evolution/mail/filters/logfile", NULL);
477 if (filename) {
478 ms->filter_logfile = g_fopen (filename, "a+");
479 g_free (filename);
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);
520 g_object_unref (fc);
522 return driver;
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,
529 session, type, ex);
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 */
543 if (msg) {
544 struct _mail_msg *m = mail_msg_new(&ms_thread_ops_dummy, NULL, sizeof(struct _mail_msg));
546 msg->data = m;
547 camel_operation_unref(msg->op);
548 msg->op = m->cancel;
549 camel_operation_ref(msg->op);
552 return msg;
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);
567 char *
568 mail_session_get_password (const char *url_string)
570 CamelURL *url;
571 char *simple_url;
572 char *passwd;
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);
580 g_free (simple_url);
582 return passwd;
585 void
586 mail_session_add_password (const char *url_string,
587 const char *passwd)
589 CamelURL *url;
590 char *simple_url;
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);
598 g_free (simple_url);
601 void
602 mail_session_remember_password (const char *url_string)
604 CamelURL *url;
605 char *simple_url;
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);
613 g_free (simple_url);
616 void
617 mail_session_forget_password (const char *key)
619 e_passwords_forget_password ("Mail", key);
622 static void
623 mail_session_check_junk_notify (GConfClient *gconf, guint id, GConfEntry *entry, CamelSession *session)
625 gchar *key;
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), '/');
631 if (key) {
632 key ++;
633 if (!strcmp (key, "check_incoming"))
634 camel_session_set_check_junk (session, gconf_value_get_bool (gconf_entry_get_value (entry)));
638 void
639 mail_session_init (const char *base_directory)
641 char *camel_dir;
642 GConfClient *gconf;
644 if (camel_init (base_directory, TRUE) != 0)
645 exit (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);
665 g_free (camel_dir);
668 gboolean
669 mail_session_get_interactive (void)
671 return MAIL_SESSION (session)->interactive;
674 void
675 mail_session_set_interactive (gboolean interactive)
677 MAIL_SESSION (session)->interactive = interactive;
679 if (!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);
700 void
701 mail_session_forget_passwords (BonoboUIComponent *uih, void *user_data,
702 const char *path)
704 e_passwords_forget_passwords ();
707 void
708 mail_session_flush_filter_log (void)
710 MailSession *ms = (MailSession *) session;
712 if (ms->filter_logfile)
713 fflush (ms->filter_logfile);