From 8fbd4ab4bd5fdbaa7f126b3f1d733e8dc9e8a958 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Sat, 6 May 2000 22:57:38 +0000 Subject: New code to configure identity, mail source, and mail transport. * mail-config.c: New code to configure identity, mail source, and mail transport. (mail_config_druid): A druid using the config widgets. (Only allows configuration of a single identity, source, and transport.) * mail-ops.c (check_configured): New function to make sure the user has configured stuff, and call the druid if not. (fetch_mail, send_msg, send_to_url, reply, forward_msg): Call check_configured (composer_send_cb): Make this pass the message to a CamelTransport rather than just printing it to stdout. * folder-browser-factory.c (development_warning): Add a warning about sending mail, since you can do that now. svn path=/trunk/; revision=2842 --- mail/ChangeLog | 18 + mail/Makefile.am | 1 + mail/folder-browser-factory.c | 5 +- mail/mail-config.c | 1010 +++++++++++++++++++++++++++++++++++++++++ mail/mail-ops.c | 154 +++++-- mail/mail.h | 3 + 6 files changed, 1149 insertions(+), 42 deletions(-) create mode 100644 mail/mail-config.c (limited to 'mail') diff --git a/mail/ChangeLog b/mail/ChangeLog index 467fc882a1..db7e063473 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,4 +1,22 @@ +2000-05-06 Dan Winship + + * mail-config.c: New code to configure identity, mail source, and + mail transport. + (mail_config_druid): A druid using the config widgets. (Only + allows configuration of a single identity, source, and transport.) + + * mail-ops.c (check_configured): New function to make sure the + user has configured stuff, and call the druid if not. + (fetch_mail, send_msg, send_to_url, reply, forward_msg): Call + check_configured + (composer_send_cb): Make this pass the message to a CamelTransport + rather than just printing it to stdout. + + * folder-browser-factory.c (development_warning): Add a warning + about sending mail, since you can do that now. + 2000-05-06 Chris Toshok + * .cvsignore: ignore evolution-mail.pure * Makefile.am: add support for building evolution-mail.pure diff --git a/mail/Makefile.am b/mail/Makefile.am index 6f0e1ed43b..6ea1b09721 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -31,6 +31,7 @@ evolution_mail_SOURCES = \ folder-browser.h \ folder-browser-factory.c \ folder-browser-factory.h \ + mail-config.c \ mail-display.c \ mail-format.c \ mail-identify.c \ diff --git a/mail/folder-browser-factory.c b/mail/folder-browser-factory.c index d2bb9c6b29..e941e54197 100644 --- a/mail/folder-browser-factory.c +++ b/mail/folder-browser-factory.c @@ -85,8 +85,9 @@ development_warning () _("This is a development version of Evolution.\n" "Using the mail component on your mail files\n" "is extremely hazardous.\n\n" - "Do not run this program on your real mail\n " - "and do not give it access to your real mail server.\n\n" + "Do not run this program on your real mail,\n" + "do not give it access to your real mail server,\n" + "and do not send mail to real people with it.\n\n" "You have been warned\n")); gtk_widget_show (label); diff --git a/mail/mail-config.c b/mail/mail-config.c new file mode 100644 index 0000000000..869ed58ee4 --- /dev/null +++ b/mail/mail-config.c @@ -0,0 +1,1010 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* mail-config.c: Mail configuration dialogs/wizard. */ + +/* + * Author: + * Dan Winship + * + * Copyright 2000 Helix Code, Inc. (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#include + +#include +#include + +#include "mail.h" +#include "e-util/e-html-utils.h" +#include "e-util/e-setup.h" + +struct service_type { + CamelProvider *provider; + CamelService *service; + GList *authtypes; +}; + +struct identity_record { + char *name, *address, *organization, *sigfile; +}; + +static char *username = NULL; + + +/* HTML Helpers */ + +static void +html_size_req (GtkWidget *widget, GtkRequisition *requisition) +{ + requisition->height = GTK_LAYOUT (widget)->height; +} + +/* Returns a GtkHTML which is already inside a GtkScrolledWindow. If + * @white is TRUE, the GtkScrolledWindow will be inside a GtkFrame. + */ +static GtkWidget * +html_new (gboolean white) +{ + GtkWidget *html, *scrolled, *frame; + GtkStyle *style; + + html = gtk_html_new (); + GTK_LAYOUT (html)->height = 0; + gtk_signal_connect (GTK_OBJECT (html), "size_request", + GTK_SIGNAL_FUNC (html_size_req), NULL); + gtk_html_set_editable (GTK_HTML (html), FALSE); + style = gtk_rc_get_style (html); + gtk_html_set_default_background_color (GTK_HTML (html), + white ? &style->white : + &style->bg[0]); + gtk_widget_set_sensitive (html, FALSE); + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, + GTK_POLICY_NEVER); + gtk_container_add (GTK_CONTAINER (scrolled), html); + + if (white) { + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), + GTK_SHADOW_ETCHED_IN); + gtk_container_add (GTK_CONTAINER (frame), scrolled); + gtk_widget_show_all (frame); + } else + gtk_widget_show_all (scrolled); + + return html; +} + +static void +put_html (GtkHTML *html, char *text) +{ + GtkHTMLStreamHandle *handle; + + text = e_text_to_html (text, E_TEXT_TO_HTML_CONVERT_NL); + handle = gtk_html_begin (html, ""); + gtk_html_write (html, handle, "", 12); + gtk_html_write (html, handle, text, strlen (text)); + gtk_html_write (html, handle, "", 14); + g_free (text); + gtk_html_end (html, handle, GTK_HTML_STREAM_OK); +} + + +/* Identity page */ + +static void +identity_note_doneness (GtkObject *page, gpointer user_data) +{ + GtkWidget *exit_button; + GtkEntry *entry; + char *data; + + exit_button = gtk_object_get_data (page, "exit_button"); + + entry = gtk_object_get_data (page, "name"); + data = gtk_entry_get_text (entry); + if (data && *data) { + entry = gtk_object_get_data (page, "addr"); + data = gtk_entry_get_text (entry); + } + + gtk_widget_set_sensitive (exit_button, data && *data); +} + +static void +prepare_identity (GnomeDruidPage *page, gpointer arg1, gpointer user_data) +{ + identity_note_doneness (user_data, NULL); +} + +static gboolean +identity_next (GnomeDruidPage *page, gpointer arg1, gpointer user_data) +{ + GtkObject *box = user_data; + GtkEntry *addr = gtk_object_get_data (box, "addr"); + char *data, *at; + + /* FIXME: we need more sanity checking than this. */ + + data = gtk_entry_get_text (addr); + at = strchr (data, '@'); + if (!at || !strchr (at + 1, '.')) { + GtkWidget *parent = + gtk_widget_get_ancestor (GTK_WIDGET (page), + GTK_TYPE_WINDOW); + gnome_error_dialog_parented ("Email address must be of the " + "form \"user@domain\".", + GTK_WINDOW (parent)); + return TRUE; + } + + g_free (username); + username = g_strndup (data, at - data); + + return FALSE; +} + +static void +destroy_identity (GtkObject *table, gpointer idrecp) +{ + struct identity_record *idrec = idrecp; + GtkEditable *editable; + + editable = gtk_object_get_data (table, "name"); + idrec->name = gtk_editable_get_chars (editable, 0, -1); + editable = gtk_object_get_data (table, "addr"); + idrec->address = gtk_editable_get_chars (editable, 0, -1); + editable = gtk_object_get_data (table, "org"); + idrec->organization = gtk_editable_get_chars (editable, 0, -1); + editable = gtk_object_get_data (table, "sig"); + idrec->sigfile = gtk_editable_get_chars (editable, 0, -1); +} + +static void +create_identity_page (GtkWidget *vbox, struct identity_record *idrec) +{ + GtkWidget *html, *table; + GtkWidget *name, *addr, *org, *sig; + GtkWidget *name_entry, *addr_entry, *org_entry, *sig_entry; + GtkWidget *hsep; + char *user; + struct passwd *pw; + + html = html_new (FALSE); + put_html (GTK_HTML (html), + _("Enter your name and email address to be used in " + "outgoing mail. You may also, optionally, enter the " + "name of your organization, and the name of a file " + "to read your signature from.")); + gtk_box_pack_start (GTK_BOX (vbox), html->parent, FALSE, TRUE, 0); + + table = gtk_table_new (5, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 10); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (vbox), "destroy", + GTK_SIGNAL_FUNC (destroy_identity), idrec); + + name = gtk_label_new (_("Full name:")); + gtk_table_attach (GTK_TABLE (table), name, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (name), 1, 0.5); + + name_entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), name_entry, 1, 2, 0, 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_object_set_data (GTK_OBJECT (vbox), "name", name_entry); + + user = getenv ("USER"); + if (user) + pw = getpwnam (user); + else + pw = getpwuid (getuid ()); + if (pw && pw->pw_gecos && *pw->pw_gecos) { + char *name; + int pos = 0; + + name = g_strndup (pw->pw_gecos, strcspn (pw->pw_gecos, ",")); + gtk_editable_insert_text (GTK_EDITABLE (name_entry), + name, strlen (name), &pos); + } + + addr = gtk_label_new (_("Email address:")); + gtk_table_attach (GTK_TABLE (table), addr, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (addr), 1, 0.5); + + addr_entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), addr_entry, 1, 2, 1, 2, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_object_set_data (GTK_OBJECT (vbox), "addr", addr_entry); + + gtk_signal_connect_object (GTK_OBJECT (name_entry), "changed", + GTK_SIGNAL_FUNC (identity_note_doneness), + GTK_OBJECT (vbox)); + gtk_signal_connect_object (GTK_OBJECT (addr_entry), "changed", + GTK_SIGNAL_FUNC (identity_note_doneness), + GTK_OBJECT (vbox)); + + hsep = gtk_hseparator_new (); + gtk_table_attach (GTK_TABLE (table), hsep, 0, 2, 2, 3, + GTK_FILL, 0, 0, 8); + + org = gtk_label_new (_("Organization:")); + gtk_table_attach (GTK_TABLE (table), org, 0, 1, 3, 4, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (org), 1, 0.5); + + org_entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), org_entry, 1, 2, 3, 4, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_object_set_data (GTK_OBJECT (vbox), "org", org_entry); + + sig = gtk_label_new (_("Signature file:")); + gtk_table_attach (GTK_TABLE (table), sig, 0, 1, 4, 5, + GTK_FILL, GTK_FILL, 0, 0); + gtk_misc_set_alignment (GTK_MISC (sig), 1, 0); + + sig_entry = gnome_file_entry_new (NULL, _("Signature File")); + gtk_table_attach (GTK_TABLE (table), sig_entry, 1, 2, 4, 5, + GTK_FILL, 0, 0, 0); + gnome_file_entry_set_default_path (GNOME_FILE_ENTRY (sig_entry), + g_get_home_dir ()); + gtk_object_set_data (GTK_OBJECT (vbox), "sig", + gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (sig_entry))); + + gtk_widget_show_all (table); +} + + +/* Source/Transport pages */ + +static void +service_note_doneness (GtkObject *page, gpointer user_data) +{ + GtkObject *box; + GtkWidget *button; + GtkEntry *entry; + char *data; + gboolean sensitive = TRUE; + + entry = gtk_object_get_data (page, "server_entry"); + if (entry) { + data = gtk_entry_get_text (entry); + if (!data || !*data) + sensitive = FALSE; + } + + if (sensitive) { + entry = gtk_object_get_data (page, "user_entry"); + if (entry) { + data = gtk_entry_get_text (entry); + if (!data || !*data) + sensitive = FALSE; + } + } + + if (sensitive) { + entry = gtk_object_get_data (page, "path_entry"); + if (entry) { + data = gtk_entry_get_text (entry); + if (!data || !*data) + sensitive = FALSE; + } + } + + button = gtk_object_get_data (page, "autodetect"); + if (button) + gtk_widget_set_sensitive (button, sensitive); + + box = gtk_object_get_data (page, "box"); + button = gtk_object_get_data (box, "exit_button"); + if (button) + gtk_widget_set_sensitive (button, sensitive); +} + +static void +prepare_service (GnomeDruidPage *page, gpointer arg1, gpointer user_data) +{ + GtkObject *box = user_data; + GtkNotebook *notebook = gtk_object_get_data (box, "notebook"); + GtkWidget *table; + GtkEntry *entry; + + table = gtk_notebook_get_nth_page ( + notebook, gtk_notebook_get_current_page (notebook)); + + if (username) { + char *data = NULL; + + entry = gtk_object_get_data (GTK_OBJECT (table), "user_entry"); + if (entry) { + data = gtk_entry_get_text (entry); + if (!data || !*data) + gtk_entry_set_text (entry, username); + } + } + + service_note_doneness (GTK_OBJECT (table), NULL); +} + +static void +auth_menuitem_activate (GtkObject *menuitem, GtkHTML *html) +{ + CamelServiceAuthType *authtype; + + authtype = gtk_object_get_data (menuitem, "authtype"); + put_html (html, authtype->description); +} + +static void +fill_auth_menu (GtkOptionMenu *optionmenu, GtkHTML *html, GList *authtypes) +{ + CamelServiceAuthType *authtype; + GtkWidget *menu, *item, *firstitem = NULL; + + menu = gtk_menu_new (); + gtk_option_menu_set_menu (optionmenu, menu); + for (; authtypes; authtypes = authtypes->next) { + authtype = authtypes->data; + item = gtk_menu_item_new_with_label (_(authtype->name)); + if (!firstitem) + firstitem = item; + gtk_menu_append (GTK_MENU (menu), item); + gtk_object_set_data (GTK_OBJECT (item), "authtype", authtype); + gtk_signal_connect (GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (auth_menuitem_activate), + html); + } + gtk_widget_show_all (menu); + gtk_option_menu_set_history (optionmenu, 0); + if (firstitem) + auth_menuitem_activate (GTK_OBJECT (firstitem), html); +} + +static char * +get_service_url (GtkObject *table) +{ + CamelURL *url; + GtkEditable *editable; + GtkOptionMenu *auth_optionmenu; + char *url_str; + + url = g_new0 (CamelURL, 1); + url->protocol = g_strdup (gtk_object_get_data (table, "protocol")); + editable = gtk_object_get_data (table, "user_entry"); + if (editable) + url->user = gtk_editable_get_chars (editable, 0, -1); + editable = gtk_object_get_data (table, "server_entry"); + if (editable) + url->host = gtk_editable_get_chars (editable, 0, -1); + editable = gtk_object_get_data (table, "path_entry"); + if (editable) + url->path = gtk_editable_get_chars (editable, 0, -1); + + auth_optionmenu = gtk_object_get_data (table, "auth_optionmenu"); + if (auth_optionmenu) { + GtkWidget *menu, *item; + CamelServiceAuthType *authtype; + + menu = gtk_option_menu_get_menu (auth_optionmenu); + if (menu) { + item = gtk_menu_get_active (GTK_MENU (menu)); + authtype = gtk_object_get_data (GTK_OBJECT (item), + "authtype"); + if (*authtype->authproto) + url->authmech = g_strdup (authtype->authproto); + } + } + + url_str = camel_url_to_string (url, FALSE); + camel_url_free (url); + return url_str; +} + +static void +autodetect_cb (GtkWidget *button, GtkObject *table) +{ + char *url, *err; + CamelException *ex; + CamelService *service; + GList *authtypes; + GtkHTML *html; + GtkOptionMenu *optionmenu; + GtkWidget *parent; + int type; + + type = GPOINTER_TO_UINT (gtk_object_get_data (table, "service_type")); + url = get_service_url (table); + + ex = camel_exception_new (); + service = camel_session_get_service (default_session->session, + url, type, ex); + g_free (url); + if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) + goto error; + + authtypes = camel_service_query_auth_types (service, ex); + if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) + goto error; + + html = gtk_object_get_data (table, "auth_html"); + optionmenu = gtk_object_get_data (table, "auth_optionmenu"); + fill_auth_menu (optionmenu, html, authtypes); + return; + + error: + err = g_strdup_printf ("Could not detect supported " + "authentication types:\n%s", + camel_exception_get_description (ex)); + camel_exception_free (ex); + parent = gtk_widget_get_ancestor (button, GTK_TYPE_WINDOW); + gnome_error_dialog_parented (err, GTK_WINDOW (parent)); + g_free (err); +} + +static void +destroy_service (GtkObject *notebook, gpointer urlp) +{ + char **url = urlp; + GtkWidget *table; + int page; + + page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)); + table = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), page); + *url = get_service_url (GTK_OBJECT (table)); +} + +static void +add_row (GtkWidget *table, int row, const char *label_text, + const char *tag, int flag) +{ + GtkWidget *label, *entry; + + label = gtk_label_new (label_text); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); + + entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), entry, 1, 3, row, row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_signal_connect_object (GTK_OBJECT (entry), "changed", + GTK_SIGNAL_FUNC (service_note_doneness), + GTK_OBJECT (table)); + gtk_object_set_data (GTK_OBJECT (table), tag, entry); +} + +static GtkWidget * +create_source (struct service_type *st) +{ + GtkWidget *table; + GtkWidget *auth, *auth_optionmenu, *auth_html; + GtkWidget *autodetect; + int row, service_flags; + + table = gtk_table_new (5, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_table_set_col_spacings (GTK_TABLE (table), 10); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_object_set_data (GTK_OBJECT (table), "protocol", + st->provider->protocol); + gtk_object_set_data (GTK_OBJECT (table), "service_type", + GUINT_TO_POINTER (CAMEL_PROVIDER_STORE)); + + row = 0; + service_flags = st->service->url_flags & + ~CAMEL_SERVICE_URL_NEED_AUTH; + + if (service_flags & CAMEL_SERVICE_URL_NEED_HOST) { + add_row (table, row, _("Server:"), "server_entry", + CAMEL_SERVICE_URL_NEED_HOST); + row++; + } + + if (service_flags & CAMEL_SERVICE_URL_NEED_USER) { + add_row (table, row, _("Username:"), "user_entry", + CAMEL_SERVICE_URL_NEED_USER); + row++; + } + + if (service_flags & CAMEL_SERVICE_URL_NEED_PATH) { + add_row (table, row, _("Path:"), "path_entry", + CAMEL_SERVICE_URL_NEED_PATH); + row++; + } + + if (st->authtypes) { + auth = gtk_label_new (_("Authentication:")); + gtk_table_attach (GTK_TABLE (table), auth, 0, 1, + row, row + 1, GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (auth), 1, 0.5); + + auth_optionmenu = gtk_option_menu_new (); + gtk_table_attach (GTK_TABLE (table), auth_optionmenu, 1, 2, + row, row + 1, GTK_FILL | GTK_EXPAND, + 0, 0, 0); + gtk_object_set_data (GTK_OBJECT (table), "auth_optionmenu", + auth_optionmenu); + + autodetect = gtk_button_new_with_label (_("Detect supported types...")); + gtk_table_attach (GTK_TABLE (table), autodetect, 2, 3, + row, row + 1, 0, 0, 0, 0); + gtk_widget_set_sensitive (autodetect, FALSE); + gtk_signal_connect (GTK_OBJECT (autodetect), "clicked", + GTK_SIGNAL_FUNC (autodetect_cb), table); + gtk_object_set_data (GTK_OBJECT (table), "autodetect", + autodetect); + + auth_html = html_new (TRUE); + gtk_table_attach (GTK_TABLE (table), auth_html->parent->parent, + 0, 3, row + 1, row + 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_object_set_data (GTK_OBJECT (table), "auth_html", + auth_html); + + fill_auth_menu (GTK_OPTION_MENU (auth_optionmenu), + GTK_HTML (auth_html), st->authtypes); + + row += 2; + } + + if (row != 0) { + GtkWidget *check; + + check = gtk_check_button_new_with_label ( + _("Test these values before continuing")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); + gtk_table_attach (GTK_TABLE (table), check, 0, 3, + row, row + 1, GTK_FILL, GTK_FILL, 0, 0); + gtk_object_set_data (GTK_OBJECT (table), "check", check); + row += 1; + } + + gtk_table_resize (GTK_TABLE (table), row, 3); + gtk_widget_show_all (table); + + return table; +} + +static GtkWidget * +create_transport (struct service_type *st) +{ + GtkWidget *table; + GtkWidget *auth, *auth_optionmenu, *auth_html; + GtkWidget *autodetect; + int row, service_flags; + + table = gtk_table_new (5, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_table_set_col_spacings (GTK_TABLE (table), 10); + gtk_container_set_border_width (GTK_CONTAINER (table), 8); + gtk_object_set_data (GTK_OBJECT (table), "protocol", + st->provider->protocol); + gtk_object_set_data (GTK_OBJECT (table), "service_type", + GUINT_TO_POINTER (CAMEL_PROVIDER_TRANSPORT)); + + row = 0; + service_flags = st->service->url_flags & + ~CAMEL_SERVICE_URL_NEED_AUTH; + + if (service_flags & CAMEL_SERVICE_URL_NEED_HOST) { + add_row (table, row, _("Server:"), "server_entry", + CAMEL_SERVICE_URL_NEED_HOST); + row++; + } + + if (st->authtypes) { + auth = gtk_label_new (_("Authentication:")); + gtk_table_attach (GTK_TABLE (table), auth, 0, 1, + row, row + 1, GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (auth), 1, 0.5); + + auth_optionmenu = gtk_option_menu_new (); + gtk_table_attach (GTK_TABLE (table), auth_optionmenu, 1, 2, + row, row + 1, GTK_FILL | GTK_EXPAND, + 0, 0, 0); + gtk_object_set_data (GTK_OBJECT (table), "auth_optionmenu", + auth_optionmenu); + + autodetect = gtk_button_new_with_label (_("Detect supported types...")); + gtk_table_attach (GTK_TABLE (table), autodetect, 2, 3, + row, row + 1, 0, 0, 0, 0); + gtk_widget_set_sensitive (autodetect, FALSE); + gtk_object_set_data (GTK_OBJECT (table), "autodetect", + autodetect); + + auth_html = html_new (TRUE); + gtk_table_attach (GTK_TABLE (table), auth_html->parent->parent, + 0, 3, row + 1, row + 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + fill_auth_menu (GTK_OPTION_MENU (auth_optionmenu), + GTK_HTML (auth_html), st->authtypes); + + row += 2; + } + + if (row != 0) { + GtkWidget *check; + + check = gtk_check_button_new_with_label ( + _("Test these values before continuing")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); + gtk_table_attach (GTK_TABLE (table), check, 0, 3, + row, row + 1, GTK_FILL | GTK_EXPAND, + GTK_FILL, 0, 0); + gtk_object_set_data (GTK_OBJECT (table), "check", check); + row += 1; + } + + gtk_table_resize (GTK_TABLE (table), row, 3); + gtk_widget_show_all (table); + + return table; +} + +static void +stype_menuitem_activate (GtkObject *menuitem, GtkObject *table) +{ + GtkHTML *html; + char *text; + int page; + GtkNotebook *notebook; + + text = gtk_object_get_data (menuitem, "description"); + html = gtk_object_get_data (table, "html"); + put_html (html, text); + + page = GPOINTER_TO_UINT (gtk_object_get_data (menuitem, "page")); + notebook = gtk_object_get_data (table, "notebook"); + gtk_notebook_set_page (notebook, page); + service_note_doneness (GTK_OBJECT (gtk_notebook_get_nth_page (notebook, + page)), + NULL); +} + +/* Create the mail source/transport page. */ +static void +create_service_page (GtkWidget *vbox, const char *label_text, GList *services, + GtkWidget *(*create_service)(struct service_type *), + char **urlp) +{ + GtkWidget *hbox, *stype, *stype_optionmenu, *stype_menu; + GtkWidget *menuitem, *first_menuitem = NULL; + GtkWidget *stype_html, *notebook, *service; + int page; + GList *s; + + hbox = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); + + stype = gtk_label_new (label_text); + gtk_box_pack_start (GTK_BOX (hbox), stype, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (stype), 1, 0.5); + + stype_optionmenu = gtk_option_menu_new (); + gtk_box_pack_start (GTK_BOX (hbox), stype_optionmenu, TRUE, TRUE, 0); + stype_menu = gtk_menu_new (); + gtk_option_menu_set_menu (GTK_OPTION_MENU (stype_optionmenu), + stype_menu); + + stype_html = html_new (TRUE); + gtk_object_set_data (GTK_OBJECT (vbox), "html", stype_html); + gtk_box_pack_start (GTK_BOX (vbox), stype_html->parent->parent, + TRUE, TRUE, 0); + + notebook = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE); + gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0); + gtk_object_set_data (GTK_OBJECT (vbox), "notebook", notebook); + gtk_signal_connect (GTK_OBJECT (notebook), "destroy", + GTK_SIGNAL_FUNC (destroy_service), urlp); + + for (s = services, page = 0; s; s = s->next, page++) { + struct service_type *st = s->data; + + menuitem = gtk_menu_item_new_with_label (_(st->provider->name)); + if (!first_menuitem) + first_menuitem = menuitem; + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (stype_menuitem_activate), + vbox); + gtk_menu_append (GTK_MENU (stype_menu), menuitem); + + service = (*create_service) (st); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), service, + NULL); + gtk_object_set_data (GTK_OBJECT (service), "box", vbox); + + gtk_object_set_data (GTK_OBJECT (menuitem), "page", + GUINT_TO_POINTER (page)); + gtk_object_set_data (GTK_OBJECT (menuitem), "description", + st->provider->description); + } + + stype_menuitem_activate (GTK_OBJECT (first_menuitem), + GTK_OBJECT (vbox)); + gtk_option_menu_set_history (GTK_OPTION_MENU (stype_optionmenu), 0); + + gtk_widget_show_all (vbox); +} + +static void +create_source_page (GtkWidget *vbox, GList *sources, char **urlp) +{ + GtkWidget *html; + + html = html_new (FALSE); + put_html (GTK_HTML (html), + _("Select the kind of mail server you have, and enter " + "the relevant information about it.\n\nIf the server " + "requires authentication, you can click the " + "\"Detect supported types...\" button after entering " + "the other information.")); + gtk_box_pack_start (GTK_BOX (vbox), html->parent, FALSE, TRUE, 0); + + create_service_page (vbox, "Mail source type:", sources, + create_source, urlp); +} + +static void +create_transport_page (GtkWidget *vbox, GList *transports, char **urlp) +{ + GtkWidget *html; + + html = html_new (FALSE); + put_html (GTK_HTML (html), + _("Select the method you would like to use to deliver " + "your mail.")); + gtk_box_pack_start (GTK_BOX (vbox), html->parent, FALSE, TRUE, 0); + + create_service_page (vbox, "Mail transport type:", transports, + create_transport, urlp); +} + + +/* Generic stuff */ + +static GList * +add_service (GList *services, CamelProviderType type, CamelProvider *prov) +{ + CamelService *service; + CamelException *ex; + char *url; + struct service_type *st; + + ex = camel_exception_new (); + + url = g_strdup_printf ("%s:", prov->protocol); + service = camel_session_get_service (default_session->session, + url, type, ex); + g_free (url); + if (!service) { + camel_exception_free (ex); + return services; + } + + st = g_new (struct service_type, 1); + st->provider = prov; + st->service = service; + st->authtypes = camel_service_query_auth_types (st->service, ex); + camel_exception_free (ex); + + return g_list_append (services, st); +} + +static GdkImlibImage * +load_image (const char *name) +{ + char *path; + GdkImlibImage *image; + + path = g_strdup_printf ("/usr/local/share/images/evolution/%s", name); + image = gdk_imlib_load_image (path); + g_free (path); + + return image; +} + +static void +prepare_first (GnomeDruidPage *page, GnomeDruid *druid, gpointer user_data) +{ + gnome_druid_set_buttons_sensitive (druid, TRUE, TRUE, TRUE); +} + +static struct identity_record idrec; +static char *source, *transport; + +static void +cancel (GnomeDruid *druid, gpointer window) +{ + gtk_window_set_modal (window, FALSE); + gtk_widget_destroy (window); + gtk_main_quit (); +} + +static void +finish (GnomeDruidPage *page, gpointer arg1, gpointer window) +{ + char *path; + + cancel (arg1, window); + + /* According to the API docs, there's an easier way to do this, + * except that it doesn't work. Anyway, this will be replaced + * by GConf eventually. FIXME. + */ + + path = g_strdup_printf ("=%s/config=/mail/configured", evolution_dir); + gnome_config_set_bool (path, TRUE); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/id_name", evolution_dir); + gnome_config_set_string (path, idrec.name); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/id_addr", evolution_dir); + gnome_config_set_string (path, idrec.address); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/id_org", evolution_dir); + gnome_config_set_string (path, idrec.organization); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/id_sig", evolution_dir); + gnome_config_set_string (path, idrec.sigfile); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/source", evolution_dir); + gnome_config_set_string (path, source); + g_free (path); + + path = g_strdup_printf ("=%s/config=/mail/transport", evolution_dir); + gnome_config_set_string (path, transport); + g_free (path); +} + +void +mail_config_druid (void) +{ + GnomeDruid *druid; + GtkWidget *page, *window; + GnomeDruidPageStandard *dpage; + GList *providers, *p, *sources, *transports; + GdkImlibImage *mail_logo, *identity_logo; + GdkImlibImage *source_logo, *transport_logo; + + /* Fetch list of all providers. */ + providers = camel_session_list_providers (default_session->session, + TRUE); + sources = transports = NULL; + for (p = providers; p; p = p->next) { + CamelProvider *prov = p->data; + + if (prov->object_types[CAMEL_PROVIDER_STORE]) { + sources = add_service (sources, + CAMEL_PROVIDER_STORE, + prov); + } else if (prov->object_types[CAMEL_PROVIDER_TRANSPORT]) { + transports = add_service (transports, + CAMEL_PROVIDER_TRANSPORT, + prov); + } + } + + mail_logo = load_image ("evolution-inbox.png"); + identity_logo = load_image ("malehead.png"); + source_logo = mail_logo; + transport_logo = load_image ("envelope.png"); + + window = gtk_window_new (GTK_WINDOW_DIALOG); + druid = GNOME_DRUID (gnome_druid_new ()); + gtk_signal_connect (GTK_OBJECT (druid), "cancel", + GTK_SIGNAL_FUNC (cancel), window); + + /* Start page */ + page = gnome_druid_page_start_new_with_vals (_("Mail Configuration"), + "blah blah blah", + mail_logo, NULL); + gnome_druid_page_start_set_logo_bg_color ( + GNOME_DRUID_PAGE_START (page), + &GNOME_DRUID_PAGE_START (page)->background_color); + gnome_druid_append_page (druid, GNOME_DRUID_PAGE (page)); + gtk_signal_connect (GTK_OBJECT (page), "prepare", + GTK_SIGNAL_FUNC (prepare_first), NULL); + gtk_widget_show_all (page); + + + /* Identity page */ + page = gnome_druid_page_standard_new_with_vals (_("Identity"), + identity_logo); + dpage = GNOME_DRUID_PAGE_STANDARD (page); + gnome_druid_page_standard_set_logo_bg_color (dpage, + &dpage->background_color); + gtk_container_set_border_width (GTK_CONTAINER (dpage->vbox), 8); + gtk_box_set_spacing (GTK_BOX (dpage->vbox), 5); + create_identity_page (dpage->vbox, &idrec); + gtk_object_set_data (GTK_OBJECT (dpage->vbox), "exit_button", + druid->next); + gnome_druid_append_page (druid, GNOME_DRUID_PAGE (page)); + gtk_signal_connect (GTK_OBJECT (page), "prepare", + GTK_SIGNAL_FUNC (prepare_identity), dpage->vbox); + gtk_signal_connect (GTK_OBJECT (page), "next", + GTK_SIGNAL_FUNC (identity_next), dpage->vbox); + gtk_widget_show (page); + + + /* Source page */ + page = gnome_druid_page_standard_new_with_vals (_("Mail Source"), + source_logo); + dpage = GNOME_DRUID_PAGE_STANDARD (page); + gnome_druid_page_standard_set_logo_bg_color (dpage, + &dpage->background_color); + gtk_container_set_border_width (GTK_CONTAINER (dpage->vbox), 8); + gtk_box_set_spacing (GTK_BOX (dpage->vbox), 5); + create_source_page (dpage->vbox, sources, &source); + gtk_object_set_data (GTK_OBJECT (dpage->vbox), "exit_button", + druid->next); + gnome_druid_append_page (druid, GNOME_DRUID_PAGE (page)); + gtk_signal_connect (GTK_OBJECT (page), "prepare", + GTK_SIGNAL_FUNC (prepare_service), dpage->vbox); + gtk_widget_show (page); + + + /* Transport page */ + page = gnome_druid_page_standard_new_with_vals (_("Mail Transport"), + transport_logo); + dpage = GNOME_DRUID_PAGE_STANDARD (page); + gnome_druid_page_standard_set_logo_bg_color (dpage, + &dpage->background_color); + gtk_container_set_border_width (GTK_CONTAINER (dpage->vbox), 8); + gtk_box_set_spacing (GTK_BOX (dpage->vbox), 5); + create_transport_page (dpage->vbox, transports, &transport); + gtk_object_set_data (GTK_OBJECT (dpage->vbox), "exit_button", + druid->next); + gnome_druid_append_page (druid, GNOME_DRUID_PAGE (page)); + gtk_signal_connect (GTK_OBJECT (page), "prepare", + GTK_SIGNAL_FUNC (prepare_service), dpage->vbox); + gtk_widget_show (page); + + + /* Finish page */ + page = gnome_druid_page_finish_new_with_vals (_("Mail Configuration"), + "blah blah blah done", + mail_logo, NULL); + gnome_druid_page_finish_set_logo_bg_color ( + GNOME_DRUID_PAGE_FINISH (page), + &GNOME_DRUID_PAGE_FINISH (page)->background_color); + gnome_druid_append_page (druid, GNOME_DRUID_PAGE (page)); + gtk_signal_connect (GTK_OBJECT (page), "finish", + GTK_SIGNAL_FUNC (finish), window); + gtk_widget_show_all (page); + + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (druid)); + + gtk_widget_show (GTK_WIDGET (druid)); + gtk_widget_show (window); + gtk_widget_queue_resize (window); + gnome_druid_set_buttons_sensitive (druid, FALSE, TRUE, TRUE); + + gtk_window_set_modal (GTK_WINDOW (window), TRUE); + gtk_main (); +} diff --git a/mail/mail-ops.c b/mail/mail-ops.c index b5c34b4b4a..34239f8f59 100644 --- a/mail/mail-ops.c +++ b/mail/mail-ops.c @@ -36,9 +36,11 @@ #endif static void -mail_exception_dialog (char *head, CamelException *ex, GtkWindow *window) +mail_exception_dialog (char *head, CamelException *ex, gpointer widget) { char *msg; + GtkWindow *window = + GTK_WINDOW (gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW)); msg = g_strdup_printf ("%s:\n%s", head, camel_exception_get_description (ex)); @@ -46,34 +48,48 @@ mail_exception_dialog (char *head, CamelException *ex, GtkWindow *window) g_free (msg); } +static gboolean +check_configured (void) +{ + char *path; + gboolean configured; + + path = g_strdup_printf ("=%s/config=/mail/configured", evolution_dir); + if (gnome_config_get_bool (path)) { + g_free (path); + return TRUE; + } + + mail_config_druid (); + + configured = gnome_config_get_bool (path); + g_free (path); + return configured; +} + void fetch_mail (GtkWidget *button, gpointer user_data) { FolderBrowser *fb = FOLDER_BROWSER (user_data); - GtkWindow *window; CamelException *ex; CamelStore *store = NULL; CamelFolder *folder = NULL, *outfolder = NULL; int nmsgs, i; CamelMimeMessage *msg = NULL; char *path, *url = NULL; - gboolean get_remote; - window = GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (fb), - GTK_TYPE_WINDOW)); + if (!check_configured ()) + return; - /* FIXME: We want a better config solution than this. */ - path = g_strdup_printf ("=%s/config=/mail/get_remote", evolution_dir); - get_remote = gnome_config_get_bool_with_default (path, FALSE); - g_free (path); - path = g_strdup_printf ("=%s/config=/mail/remote_url", evolution_dir); - url = gnome_config_get_string_with_default (path, NULL); + path = g_strdup_printf ("=%s/config=/mail/source", evolution_dir); + url = gnome_config_get_string (path); g_free (path); - if (!get_remote || !url) { - if (url) - g_free (url); + if (!url) { + GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (fb), + GTK_TYPE_WINDOW); + gnome_error_dialog_parented ("You have no remote mail source " - "configured", window); + "configured", GTK_WINDOW (win)); return; } @@ -101,8 +117,7 @@ fetch_mail (GtkWidget *button, gpointer user_data) camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "Couldn't create temporary " "mbox: %s", g_strerror (errno)); - mail_exception_dialog ("Unable to move mail", ex, - window); + mail_exception_dialog ("Unable to move mail", ex, fb); goto cleanup; } close (tmpfd); @@ -112,8 +127,7 @@ fetch_mail (GtkWidget *button, gpointer user_data) switch (camel_movemail (source, tmp_mbox, ex)) { case -1: - mail_exception_dialog ("Unable to move mail", ex, - window); + mail_exception_dialog ("Unable to move mail", ex, fb); /* FALL THROUGH */ case 0: @@ -126,8 +140,7 @@ fetch_mail (GtkWidget *button, gpointer user_data) strrchr (tmp_mbox, '/') + 1, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { - mail_exception_dialog ("Unable to move mail", ex, - window); + mail_exception_dialog ("Unable to move mail", ex, fb); g_free (tmp_mbox); goto cleanup; } @@ -136,34 +149,34 @@ fetch_mail (GtkWidget *button, gpointer user_data) url, ex); if (!store) { mail_exception_dialog ("Unable to get new mail", - ex, window); + ex, fb); goto cleanup; } camel_service_connect_with_url (CAMEL_SERVICE (store), url, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to get new mail", - ex, window); + ex, fb); goto cleanup; } folder = camel_store_get_folder (store, "inbox", ex); if (!folder) { mail_exception_dialog ("Unable to get new mail", - ex, window); + ex, fb); goto cleanup; } } camel_folder_open (folder, FOLDER_OPEN_READ, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { - mail_exception_dialog ("Unable to get new mail", ex, window); + mail_exception_dialog ("Unable to get new mail", ex, fb); goto cleanup; } nmsgs = camel_folder_get_message_count (folder, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { - mail_exception_dialog ("Unable to get new mail", ex, window); + mail_exception_dialog ("Unable to get new mail", ex, fb); goto cleanup; } @@ -174,13 +187,13 @@ fetch_mail (GtkWidget *button, gpointer user_data) "inbox", ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to open inbox to store mail", - ex, window); + ex, fb); goto cleanup; } camel_folder_open (outfolder, FOLDER_OPEN_WRITE, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to open inbox to store mail", - ex, window); + ex, fb); goto cleanup; } @@ -188,21 +201,21 @@ fetch_mail (GtkWidget *button, gpointer user_data) msg = camel_folder_get_message_by_number (folder, i, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to read message", - ex, window); + ex, fb); goto cleanup; } camel_folder_append_message (outfolder, msg, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to write message", - ex, window); + ex, fb); goto cleanup; } camel_folder_delete_message_by_number (folder, i, ex); if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { mail_exception_dialog ("Unable to delete message", - ex, window); + ex, fb); goto cleanup; } } @@ -241,17 +254,67 @@ fetch_mail (GtkWidget *button, gpointer user_data) static void composer_send_cb (EMsgComposer *composer, gpointer data) { + static CamelTransport *transport = NULL; + static char *from = NULL; + CamelException *ex; CamelMimeMessage *message; - CamelStream *stream; - int stdout_dup; + char *name, *addr, *path; + + ex = camel_exception_new (); + + if (!from) { + CamelInternetAddress *ciaddr; + + path = g_strdup_printf ("=%s/config=/mail/id_name", + evolution_dir); + name = gnome_config_get_string (path); + g_assert (name); + g_free (path); + path = g_strdup_printf ("=%s/config=/mail/id_addr", + evolution_dir); + addr = gnome_config_get_string (path); + g_assert (addr); + g_free (path); + + ciaddr = camel_internet_address_new (); + camel_internet_address_add (ciaddr, name, addr); + + from = camel_address_encode (CAMEL_ADDRESS (ciaddr)); + } + + if (!transport) { + char *url; + + path = g_strdup_printf ("=%s/config=/mail/transport", + evolution_dir); + url = gnome_config_get_string (path); + g_assert (url); + g_free (path); + + transport = camel_session_get_transport ( + default_session->session, url, ex); + if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { + mail_exception_dialog ("Could not load mail transport", + ex, composer); + camel_exception_free (ex); + return; + } + } message = e_msg_composer_get_message (composer); - stdout_dup = dup (1); - stream = camel_stream_fs_new_with_fd (stdout_dup); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), - stream); - camel_stream_close (stream); + camel_mime_message_set_from (message, from); + camel_medium_add_header (CAMEL_MEDIUM (message), "X-Mailer", + "Evolution (Developer Preview)"); + camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0); + + camel_transport_send (transport, CAMEL_MEDIUM (message), ex); + if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { + mail_exception_dialog ("Could not send message", ex, composer); + camel_exception_free (ex); + gtk_object_unref (GTK_OBJECT (message)); + return; + } gtk_object_unref (GTK_OBJECT (message)); gtk_object_destroy (GTK_OBJECT (composer)); @@ -263,6 +326,9 @@ send_msg (GtkWidget *widget, gpointer user_data) { GtkWidget *composer; + if (!check_configured ()) + return; + composer = e_msg_composer_new (); gtk_signal_connect (GTK_OBJECT (composer), "send", @@ -276,6 +342,9 @@ send_to_url (const char *url) { GtkWidget *composer; + if (!check_configured ()) + return; + composer = e_msg_composer_new_from_url (url); gtk_signal_connect (GTK_OBJECT (composer), "send", @@ -288,6 +357,9 @@ reply (FolderBrowser *fb, gboolean to_all) { EMsgComposer *composer; + if (!check_configured ()) + return; + composer = mail_generate_reply (fb->mail_display->current_message, to_all); @@ -316,6 +388,9 @@ forward_msg (GtkWidget *button, gpointer user_data) FolderBrowser *fb; EMsgComposer *composer; + if (!check_configured ()) + return; + fb = FOLDER_BROWSER (user_data); composer = mail_generate_forward (fb->mail_display->current_message, TRUE, TRUE); @@ -360,8 +435,7 @@ expunge_folder (GtkWidget *button, gpointer user_data) /* this always throws an error, when it shouldn't? */ #if 0 if (camel_exception_get_id (&ex) != CAMEL_EXCEPTION_NONE) { - GtkWindow *window = GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (fb), GTK_TYPE_WINDOW)); - mail_exception_dialog ("Unable to expunge deleted messages", &ex, window); + mail_exception_dialog ("Unable to expunge deleted messages", &ex, fb); } #endif } diff --git a/mail/mail.h b/mail/mail.h index 5c67d547d8..cb41f3cc7e 100644 --- a/mail/mail.h +++ b/mail/mail.h @@ -25,6 +25,9 @@ /* folder-browser-factory */ void folder_browser_factory_init (void); +/* mail-config */ +void mail_config_druid (void); + /* mail-format */ void mail_format_mime_message (CamelMimeMessage *mime_message, GtkBox *box); -- cgit