624 lines
17 KiB
C
624 lines
17 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* Authors: Jeffrey Stedfast <fejj@ximian.com>
|
|
*
|
|
* Copyright 2003 Ximian, Inc. (www.ximian.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 Street #330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "mail-mt.h"
|
|
#include "mail-ops.h"
|
|
#include "mail-tools.h"
|
|
#include "mail-config.h"
|
|
#include "mail-session.h"
|
|
#include "mail-send-recv.h"
|
|
|
|
#include <e-util/e-dialog-utils.h> /* e_notice */
|
|
|
|
#include "em-utils.h"
|
|
#include "em-composer-utils.h"
|
|
|
|
struct emcs_t {
|
|
unsigned int ref_count;
|
|
|
|
CamelFolder *drafts_folder;
|
|
char *drafts_uid;
|
|
|
|
CamelFolder *folder;
|
|
guint32 flags, set;
|
|
char *uid;
|
|
};
|
|
|
|
static struct emcs_t *
|
|
emcs_new (void)
|
|
{
|
|
struct emcs_t *emcs;
|
|
|
|
emcs = g_new (struct emcs_t, 1);
|
|
emcs->ref_count = 1;
|
|
emcs->drafts_folder = NULL;
|
|
emcs->drafts_uid = NULL;
|
|
emcs->folder = NULL;
|
|
emcs->flags = 0;
|
|
emcs->set = 0;
|
|
emcs->uid = NULL;
|
|
|
|
return emcs;
|
|
}
|
|
|
|
static void
|
|
free_emcs (struct emcs_t *emcs)
|
|
{
|
|
if (emcs->drafts_folder)
|
|
camel_object_unref (emcs->drafts_folder);
|
|
g_free (emcs->drafts_uid);
|
|
|
|
if (emcs->folder)
|
|
camel_object_unref (emcs->folder);
|
|
g_free (emcs->uid);
|
|
g_free (emcs);
|
|
}
|
|
|
|
static void
|
|
emcs_ref (struct emcs_t *emcs)
|
|
{
|
|
emcs->ref_count++;
|
|
}
|
|
|
|
static void
|
|
emcs_unref (struct emcs_t *emcs)
|
|
{
|
|
emcs->ref_count--;
|
|
if (emcs->ref_count == 0)
|
|
free_emcs (emcs);
|
|
}
|
|
|
|
static void
|
|
composer_destroy_cb (gpointer user_data, GObject *deadbeef)
|
|
{
|
|
emcs_unref (user_data);
|
|
}
|
|
|
|
static gboolean
|
|
ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EABDestination **recipients)
|
|
{
|
|
gboolean show_again, res;
|
|
GConfClient *gconf;
|
|
GString *str;
|
|
int i;
|
|
|
|
gconf = mail_config_get_gconf_client ();
|
|
|
|
if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL))
|
|
return TRUE;
|
|
|
|
/* FIXME: this wording sucks */
|
|
str = g_string_new (_("You are sending an HTML-formatted message. Please make sure that\n"
|
|
"the following recipients are willing and able to receive HTML mail:\n"));
|
|
for (i = 0; recipients[i] != NULL; ++i) {
|
|
if (!eab_destination_get_html_mail_pref (recipients[i])) {
|
|
const char *name;
|
|
|
|
name = eab_destination_get_textrep (recipients[i], FALSE);
|
|
|
|
g_string_append_printf (str, " %s\n", name);
|
|
}
|
|
}
|
|
|
|
g_string_append (str, _("Send anyway?"));
|
|
res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, "%s", str->str);
|
|
g_string_free (str, TRUE);
|
|
|
|
gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", show_again, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
ask_confirm_for_empty_subject (EMsgComposer *composer)
|
|
{
|
|
gboolean show_again, res;
|
|
GConfClient *gconf;
|
|
|
|
gconf = mail_config_get_gconf_client ();
|
|
|
|
if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", NULL))
|
|
return TRUE;
|
|
|
|
res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
|
|
_("This message has no subject.\nReally send?"));
|
|
|
|
gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", show_again, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case)
|
|
{
|
|
gboolean show_again, res;
|
|
const char *first_text;
|
|
GConfClient *gconf;
|
|
|
|
gconf = mail_config_get_gconf_client ();
|
|
|
|
if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", NULL))
|
|
return TRUE;
|
|
|
|
/* If the user is mailing a hidden contact list, it is possible for
|
|
them to create a message with only Bcc recipients without really
|
|
realizing it. To try to avoid being totally confusing, I've changed
|
|
this dialog to provide slightly different text in that case, to
|
|
better explain what the hell is going on. */
|
|
|
|
if (hidden_list_case) {
|
|
first_text = _("Since the contact list you are sending to "
|
|
"is configured to hide the list's addresses, "
|
|
"this message will contain only Bcc recipients.");
|
|
} else {
|
|
first_text = _("This message contains only Bcc recipients.");
|
|
}
|
|
|
|
res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
|
|
"%s\n%s", first_text,
|
|
_("It is possible that the mail server may reveal the recipients "
|
|
"by adding an Apparently-To header.\nSend anyway?"));
|
|
|
|
gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", show_again, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
struct _send_data {
|
|
struct emcs_t *emcs;
|
|
EMsgComposer *composer;
|
|
gboolean send;
|
|
};
|
|
|
|
static void
|
|
composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info,
|
|
int queued, const char *appended_uid, void *data)
|
|
{
|
|
struct emcs_t *emcs;
|
|
struct _send_data *send = data;
|
|
|
|
emcs = send->emcs;
|
|
|
|
if (queued) {
|
|
if (emcs && emcs->drafts_folder) {
|
|
/* delete the old draft message */
|
|
camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid,
|
|
CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
|
|
CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
|
|
camel_object_unref (emcs->drafts_folder);
|
|
emcs->drafts_folder = NULL;
|
|
g_free (emcs->drafts_uid);
|
|
emcs->drafts_uid = NULL;
|
|
}
|
|
|
|
if (emcs && emcs->folder) {
|
|
/* set any replied flags etc */
|
|
camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set);
|
|
camel_object_unref (emcs->folder);
|
|
emcs->folder = NULL;
|
|
g_free (emcs->uid);
|
|
emcs->uid = NULL;
|
|
}
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (send->composer));
|
|
|
|
if (send->send && camel_session_is_online (session)) {
|
|
/* queue a message send */
|
|
mail_send ();
|
|
}
|
|
} else {
|
|
if (!emcs) {
|
|
/* disconnect the previous signal handlers */
|
|
g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0,
|
|
0, NULL, em_utils_composer_send_cb, NULL);
|
|
g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0,
|
|
0, NULL, em_utils_composer_save_draft_cb, NULL);
|
|
|
|
/* reconnect to the signals using a non-NULL emcs for the callback data */
|
|
em_composer_utils_setup_default_callbacks (send->composer);
|
|
}
|
|
|
|
e_msg_composer_set_enable_autosave (send->composer, TRUE);
|
|
gtk_widget_show (GTK_WIDGET (send->composer));
|
|
}
|
|
|
|
camel_message_info_free (info);
|
|
|
|
if (send->emcs)
|
|
emcs_unref (send->emcs);
|
|
|
|
g_object_unref (send->composer);
|
|
g_free (send);
|
|
}
|
|
|
|
static CamelMimeMessage *
|
|
composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_object_data)
|
|
{
|
|
CamelMimeMessage *message = NULL;
|
|
EABDestination **recipients, **recipients_bcc;
|
|
gboolean send_html, confirm_html;
|
|
CamelInternetAddress *cia;
|
|
int hidden = 0, shown = 0;
|
|
int num = 0, num_bcc = 0;
|
|
const char *subject;
|
|
GConfClient *gconf;
|
|
EAccount *account;
|
|
int i;
|
|
|
|
gconf = mail_config_get_gconf_client ();
|
|
|
|
/* We should do all of the validity checks based on the composer, and not on
|
|
the created message, as extra interaction may occur when we get the message
|
|
(e.g. to get a passphrase to sign a message) */
|
|
|
|
/* get the message recipients */
|
|
recipients = e_msg_composer_get_recipients (composer);
|
|
|
|
cia = camel_internet_address_new ();
|
|
|
|
/* see which ones are visible/present, etc */
|
|
if (recipients) {
|
|
for (i = 0; recipients[i] != NULL; i++) {
|
|
const char *addr = eab_destination_get_address (recipients[i]);
|
|
|
|
if (addr && addr[0]) {
|
|
camel_address_decode ((CamelAddress *) cia, addr);
|
|
if (camel_address_length ((CamelAddress *) cia) > 0) {
|
|
camel_address_remove ((CamelAddress *) cia, -1);
|
|
num++;
|
|
if (eab_destination_is_evolution_list (recipients[i])
|
|
&& !eab_destination_list_show_addresses (recipients[i])) {
|
|
hidden++;
|
|
} else {
|
|
shown++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
recipients_bcc = e_msg_composer_get_bcc (composer);
|
|
if (recipients_bcc) {
|
|
for (i = 0; recipients_bcc[i] != NULL; i++) {
|
|
const char *addr = eab_destination_get_address (recipients_bcc[i]);
|
|
|
|
if (addr && addr[0]) {
|
|
camel_address_decode ((CamelAddress *) cia, addr);
|
|
if (camel_address_length ((CamelAddress *) cia) > 0) {
|
|
camel_address_remove ((CamelAddress *) cia, -1);
|
|
num_bcc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
eab_destination_freev (recipients_bcc);
|
|
}
|
|
|
|
camel_object_unref (cia);
|
|
|
|
/* I'm sensing a lack of love, er, I mean recipients. */
|
|
if (num == 0 && !post) {
|
|
e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
|
|
_("You must specify recipients in order to send this message."));
|
|
goto finished;
|
|
}
|
|
|
|
if (num > 0 && (num == num_bcc || shown == 0)) {
|
|
/* this means that the only recipients are Bcc's */
|
|
if (!ask_confirm_for_only_bcc (composer, shown == 0))
|
|
goto finished;
|
|
}
|
|
|
|
send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL);
|
|
confirm_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL);
|
|
|
|
/* Only show this warning if our default is to send html. If it isn't, we've
|
|
manually switched into html mode in the composer and (presumably) had a good
|
|
reason for doing this. */
|
|
if (e_msg_composer_get_send_html (composer) && send_html && confirm_html) {
|
|
gboolean html_problem = FALSE;
|
|
|
|
if (recipients) {
|
|
for (i = 0; recipients[i] != NULL && !html_problem; i++) {
|
|
if (!eab_destination_get_html_mail_pref (recipients[i]))
|
|
html_problem = TRUE;
|
|
}
|
|
}
|
|
|
|
if (html_problem) {
|
|
html_problem = !ask_confirm_for_unwanted_html_mail (composer, recipients);
|
|
if (html_problem)
|
|
goto finished;
|
|
}
|
|
}
|
|
|
|
/* Check for no subject */
|
|
subject = e_msg_composer_get_subject (composer);
|
|
if (subject == NULL || subject[0] == '\0') {
|
|
if (!ask_confirm_for_empty_subject (composer))
|
|
goto finished;
|
|
}
|
|
|
|
/* actually get the message now, this will sign/encrypt etc */
|
|
message = e_msg_composer_get_message (composer, save_html_object_data);
|
|
if (message == NULL)
|
|
goto finished;
|
|
|
|
/* Add info about the sending account */
|
|
account = e_msg_composer_get_preferred_account (composer);
|
|
|
|
if (account) {
|
|
camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Account", account->name);
|
|
camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Transport", account->transport->url);
|
|
camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Fcc", account->sent_folder_uri);
|
|
if (account->id->organization && *account->id->organization)
|
|
camel_medium_set_header (CAMEL_MEDIUM (message), "Organization", account->id->organization);
|
|
}
|
|
|
|
/* Get the message recipients and 'touch' them, boosting their use scores */
|
|
if (recipients)
|
|
eab_destination_touchv (recipients);
|
|
|
|
finished:
|
|
|
|
if (recipients)
|
|
eab_destination_freev (recipients);
|
|
|
|
return message;
|
|
}
|
|
|
|
static void
|
|
got_post_folder (char *uri, CamelFolder *folder, void *data)
|
|
{
|
|
CamelFolder **fp = data;
|
|
|
|
*fp = folder;
|
|
|
|
if (folder)
|
|
camel_object_ref (folder);
|
|
}
|
|
|
|
void
|
|
em_utils_composer_send_cb (EMsgComposer *composer, gpointer user_data)
|
|
{
|
|
extern CamelFolder *outbox_folder;
|
|
CamelMimeMessage *message;
|
|
CamelMessageInfo *info;
|
|
struct _send_data *send;
|
|
gboolean post = FALSE;
|
|
CamelFolder *folder;
|
|
XEvolution *xev;
|
|
char *url;
|
|
|
|
url = e_msg_composer_hdrs_get_post_to ((EMsgComposerHdrs *) composer->hdrs);
|
|
if (url && *url) {
|
|
post = TRUE;
|
|
|
|
mail_msg_wait (mail_get_folder (url, 0, got_post_folder, &folder, mail_thread_new));
|
|
|
|
if (!folder) {
|
|
g_free (url);
|
|
return;
|
|
}
|
|
} else {
|
|
folder = outbox_folder;
|
|
camel_object_ref (folder);
|
|
}
|
|
|
|
g_free (url);
|
|
|
|
message = composer_get_message (composer, post, FALSE);
|
|
if (!message)
|
|
return;
|
|
|
|
if (post) {
|
|
/* Remove the X-Evolution* headers if we are in Post-To mode */
|
|
xev = mail_tool_remove_xevolution_headers (message);
|
|
mail_tool_destroy_xevolution (xev);
|
|
}
|
|
|
|
info = camel_message_info_new ();
|
|
info->flags = CAMEL_MESSAGE_SEEN;
|
|
|
|
send = g_malloc (sizeof (*send));
|
|
send->emcs = user_data;
|
|
if (send->emcs)
|
|
emcs_ref (send->emcs);
|
|
send->send = !post;
|
|
send->composer = composer;
|
|
g_object_ref (composer);
|
|
gtk_widget_hide (GTK_WIDGET (composer));
|
|
|
|
e_msg_composer_set_enable_autosave (composer, FALSE);
|
|
|
|
mail_append_mail (folder, message, info, composer_send_queued_cb, send);
|
|
camel_object_unref (message);
|
|
camel_object_unref (folder);
|
|
}
|
|
|
|
struct _save_draft_info {
|
|
struct emcs_t *emcs;
|
|
EMsgComposer *composer;
|
|
int quit;
|
|
};
|
|
|
|
static void
|
|
save_draft_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, int ok,
|
|
const char *appended_uid, void *user_data)
|
|
{
|
|
struct _save_draft_info *sdi = user_data;
|
|
struct emcs_t *emcs;
|
|
CORBA_Environment ev;
|
|
|
|
if (!ok)
|
|
goto done;
|
|
CORBA_exception_init (&ev);
|
|
GNOME_GtkHTML_Editor_Engine_runCommand (sdi->composer->editor_engine, "saved", &ev);
|
|
CORBA_exception_free (&ev);
|
|
|
|
if ((emcs = sdi->emcs) == NULL) {
|
|
emcs = emcs_new ();
|
|
|
|
/* disconnect the previous signal handlers */
|
|
g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_send_cb), NULL);
|
|
g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_save_draft_cb), NULL);
|
|
|
|
/* reconnect to the signals using a non-NULL emcs for the callback data */
|
|
em_composer_utils_setup_default_callbacks (sdi->composer);
|
|
}
|
|
|
|
if (emcs->drafts_folder) {
|
|
/* delete the original draft message */
|
|
camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid,
|
|
CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
|
|
CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
|
|
camel_object_unref (emcs->drafts_folder);
|
|
emcs->drafts_folder = NULL;
|
|
g_free (emcs->drafts_uid);
|
|
emcs->drafts_uid = NULL;
|
|
}
|
|
|
|
if (emcs->folder) {
|
|
/* set the replied flags etc */
|
|
camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set);
|
|
camel_object_unref (emcs->folder);
|
|
emcs->folder = NULL;
|
|
g_free (emcs->uid);
|
|
emcs->uid = NULL;
|
|
}
|
|
|
|
if (appended_uid) {
|
|
camel_object_ref (folder);
|
|
emcs->drafts_folder = folder;
|
|
emcs->drafts_uid = g_strdup (appended_uid);
|
|
}
|
|
|
|
if (sdi->quit)
|
|
gtk_widget_destroy (GTK_WIDGET (sdi->composer));
|
|
|
|
done:
|
|
g_object_unref (sdi->composer);
|
|
if (sdi->emcs)
|
|
emcs_unref (sdi->emcs);
|
|
g_free (info);
|
|
g_free (sdi);
|
|
}
|
|
|
|
static void
|
|
save_draft_folder (char *uri, CamelFolder *folder, gpointer data)
|
|
{
|
|
CamelFolder **save = data;
|
|
|
|
if (folder) {
|
|
*save = folder;
|
|
camel_object_ref (folder);
|
|
}
|
|
}
|
|
|
|
void
|
|
em_utils_composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer user_data)
|
|
{
|
|
extern char *default_drafts_folder_uri;
|
|
extern CamelFolder *drafts_folder;
|
|
struct _save_draft_info *sdi;
|
|
CamelFolder *folder = NULL;
|
|
CamelMimeMessage *msg;
|
|
CamelMessageInfo *info;
|
|
EAccount *account;
|
|
|
|
account = e_msg_composer_get_preferred_account (composer);
|
|
if (account && account->drafts_folder_uri &&
|
|
strcmp (account->drafts_folder_uri, default_drafts_folder_uri) != 0) {
|
|
int id;
|
|
|
|
id = mail_get_folder (account->drafts_folder_uri, 0, save_draft_folder, &folder, mail_thread_new);
|
|
mail_msg_wait (id);
|
|
|
|
if (!folder) {
|
|
if (!em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL,
|
|
_("Unable to open the drafts folder for this account.\n"
|
|
"Would you like to use the default drafts folder?")))
|
|
return;
|
|
|
|
folder = drafts_folder;
|
|
camel_object_ref (drafts_folder);
|
|
}
|
|
} else {
|
|
folder = drafts_folder;
|
|
camel_object_ref (folder);
|
|
}
|
|
|
|
msg = e_msg_composer_get_message_draft (composer);
|
|
|
|
info = g_new0 (CamelMessageInfo, 1);
|
|
info->flags = CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN;
|
|
|
|
sdi = g_malloc (sizeof (struct _save_draft_info));
|
|
sdi->composer = composer;
|
|
g_object_ref (composer);
|
|
sdi->emcs = user_data;
|
|
if (sdi->emcs)
|
|
emcs_ref (sdi->emcs);
|
|
sdi->quit = quit;
|
|
|
|
mail_append_mail (folder, msg, info, save_draft_done, sdi);
|
|
camel_object_unref (folder);
|
|
camel_object_unref (msg);
|
|
}
|
|
|
|
|
|
void
|
|
em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, const char *uid,
|
|
guint32 flags, guint32 set, CamelFolder *drafts, const char *drafts_uid)
|
|
{
|
|
struct emcs_t *emcs;
|
|
|
|
emcs = emcs_new ();
|
|
|
|
if (folder && uid) {
|
|
camel_object_ref (folder);
|
|
emcs->folder = folder;
|
|
emcs->uid = g_strdup (uid);
|
|
emcs->flags = flags;
|
|
emcs->set = set;
|
|
}
|
|
|
|
if (drafts && drafts_uid) {
|
|
camel_object_ref (drafts);
|
|
emcs->drafts_folder = drafts;
|
|
emcs->drafts_uid = g_strdup (drafts_uid);
|
|
}
|
|
|
|
g_signal_connect (composer, "send", G_CALLBACK (em_utils_composer_send_cb), emcs);
|
|
g_signal_connect (composer, "save-draft", G_CALLBACK (em_utils_composer_save_draft_cb), emcs);
|
|
|
|
g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, emcs);
|
|
}
|