Updated Traditional Chinese translation.
[evolution.git] / mail / mail-session.c
blobf38fcd98fdae45fd571a91ed0058646551abca91
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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdlib.h>
27 #include <string.h>
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"
53 #include "mail-mt.h"
54 #include "mail-ops.h"
55 #include <libedataserverui/e-passwords.h>
56 #include "libedataserver/e-msgport.h"
57 #include "e-util/e-error.h"
59 #define d(x)
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;
75 gboolean interactive;
76 FILE *filter_logfile;
78 EMutex *lock;
80 MailAsyncEvent *async;
81 } MailSession;
83 typedef struct _MailSessionClass {
84 CamelSessionClass parent_class;
86 } MailSessionClass;
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);
99 static void
100 init (MailSession *session)
102 session->lock = e_mutex_new(E_MUTEX_REC);
103 session->async = mail_async_event_new();
106 static void
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);
116 static void
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;
132 static CamelType
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 (),
141 "MailSession",
142 sizeof (MailSession),
143 sizeof (MailSessionClass),
144 (CamelObjectClassInitFunc) class_init,
145 NULL,
146 (CamelObjectInitFunc) init,
147 (CamelObjectFinalizeFunc) finalise);
150 return mail_session_type;
154 static char *
155 make_key (CamelService *service, const char *item)
157 char *key;
159 if (service)
160 key = camel_url_to_string (service->url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
161 else
162 key = g_strdup (item);
164 return key;
167 /* ********************************************************************** */
169 static char *
170 get_password (CamelSession *session, CamelService *service, const char *domain,
171 const char *prompt, const char *item, guint32 flags, CamelException *ex)
173 char *url;
174 char *ret = NULL;
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 */
181 if (url
182 && (account = mail_config_get_account_by_transport_url(url)))
183 ret = g_strdup(account->source->url);
184 } else {
185 char *key = make_key(service, item);
186 EAccountService *config_service = NULL;
188 if (domain == NULL)
189 domain = "Mail";
191 ret = e_passwords_get_password(domain, key);
192 if (ret == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
193 guint32 eflags;
194 gboolean remember;
195 char *title;
197 if (url) {
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;
206 if (account)
207 title = g_strdup_printf (_("Enter Password for %s"), account->name);
208 else
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;
215 else
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);
232 g_free(title);
234 if (ret && config_service)
235 mail_config_service_set_save_passwd(config_service, remember);
238 g_free(key);
241 g_free(url);
243 if (ret == NULL)
244 camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled operation."));
246 return ret;
249 static void
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);
255 g_free (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;
267 char *prompt;
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 */
277 static void
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);
295 static void
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"
307 static void
308 do_user_message (struct _mail_msg *mm)
310 struct _user_message_msg *m = (struct _user_message_msg *)mm;
311 int type;
313 if (!m->ismain && message_dialog != NULL) {
314 e_dlist_addtail (&message_list, (EDListNode *)m);
315 return;
318 switch (m->type) {
319 case CAMEL_SESSION_ALERT_INFO:
320 type = 0;
321 break;
322 case CAMEL_SESSION_ALERT_WARNING:
323 type = 1;
324 break;
325 case CAMEL_SESSION_ALERT_ERROR:
326 type = 2;
327 break;
328 default:
329 type = 0;
332 if (m->allow_cancel)
333 type += 3;
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) {
340 if (m->ismain) {
341 user_message_response(message_dialog, gtk_dialog_run (message_dialog), m);
342 } else {
343 g_signal_connect (message_dialog, "response", G_CALLBACK (user_message_response), m);
344 gtk_widget_show ((GtkWidget *) message_dialog);
346 } else {
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);
350 mail_msg_free(m);
354 static void
355 free_user_message(struct _mail_msg *mm)
357 struct _user_message_msg *m = (struct _user_message_msg *)mm;
359 g_free(m->prompt);
362 static struct _mail_msg_op user_message_op = { NULL, do_user_message, NULL, free_user_message };
364 static gboolean
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;
370 gboolean ret;
372 if (!mail_session->interactive)
373 return FALSE;
375 if (cancel)
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;
379 m->type = type;
380 m->prompt = g_strdup(prompt);
381 m->allow_cancel = cancel;
383 if (m->ismain)
384 do_user_message((struct _mail_msg *)m);
385 else {
386 extern EMsgPort *mail_gui_port2;
388 e_msgport_put(mail_gui_port2, (EMsg *)m);
391 if (cancel) {
392 e_msgport_wait(user_message_reply);
393 r = (struct _user_message_msg *)e_msgport_get(user_message_reply);
394 g_assert(m == r);
396 ret = m->result;
397 mail_msg_free(m);
398 e_msgport_destroy(user_message_reply);
399 } else
400 ret = TRUE;
402 return ret;
405 static CamelFolder *
406 get_folder (CamelFilterDriver *d, const char *uri, void *data, CamelException *ex)
408 return mail_tool_uri_to_folder(uri, 0, ex);
411 static void
412 main_play_sound (CamelFilterDriver *driver, char *filename, gpointer user_data)
414 if (filename && *filename)
415 gnome_sound_play (filename);
416 else
417 gdk_beep ();
419 g_free (filename);
420 camel_object_unref (session);
423 static void
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);
434 static void
435 main_system_beep (CamelFilterDriver *driver, gpointer user_data)
437 gdk_beep ();
440 static void
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;
456 char *user, *system;
457 GConfClient *gconf;
458 RuleContext *fc;
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);
466 g_free (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) {
475 char *filename;
477 filename = gconf_client_get_string (gconf, "/apps/evolution/mail/filters/logfile", NULL);
478 if (filename) {
479 ms->filter_logfile = fopen (filename, "a+");
480 g_free (filename);
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);
521 g_object_unref (fc);
523 return driver;
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,
530 session, type, ex);
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 */
544 if (msg) {
545 struct _mail_msg *m = mail_msg_new(&ms_thread_ops_dummy, NULL, sizeof(struct _mail_msg));
547 msg->data = m;
548 camel_operation_unref(msg->op);
549 msg->op = m->cancel;
550 camel_operation_ref(msg->op);
553 return msg;
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);
568 char *
569 mail_session_get_password (const char *url_string)
571 CamelURL *url;
572 char *simple_url;
573 char *passwd;
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);
581 g_free (simple_url);
583 return passwd;
586 void
587 mail_session_add_password (const char *url_string,
588 const char *passwd)
590 CamelURL *url;
591 char *simple_url;
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);
599 g_free (simple_url);
602 void
603 mail_session_remember_password (const char *url_string)
605 CamelURL *url;
606 char *simple_url;
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);
614 g_free (simple_url);
617 void
618 mail_session_forget_password (const char *key)
620 e_passwords_forget_password ("Mail", key);
623 static void
624 mail_session_check_junk_notify (GConfClient *gconf, guint id, GConfEntry *entry, CamelSession *session)
626 gchar *key;
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), '/');
632 if (key) {
633 key ++;
634 if (!strcmp (key, "check_incoming"))
635 camel_session_set_check_junk (session, gconf_value_get_bool (gconf_entry_get_value (entry)));
639 void
640 mail_session_init (const char *base_directory)
642 char *camel_dir;
643 GConfClient *gconf;
645 if (camel_init (base_directory, TRUE) != 0)
646 exit (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);
666 g_free (camel_dir);
669 gboolean
670 mail_session_get_interactive (void)
672 return MAIL_SESSION (session)->interactive;
675 void
676 mail_session_set_interactive (gboolean interactive)
678 MAIL_SESSION (session)->interactive = interactive;
680 if (!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);
701 void
702 mail_session_forget_passwords (BonoboUIComponent *uih, void *user_data,
703 const char *path)
705 e_passwords_forget_passwords ();
708 void
709 mail_session_flush_filter_log (void)
711 MailSession *ms = (MailSession *) session;
713 if (ms->filter_logfile)
714 fflush (ms->filter_logfile);