Files
evolution/mail/em-folder-utils.c
Jonathon Jongsma c2ee0afbc8 Rename EError to EAlert to match general use better
The EError mechanism is used both for error dialogs as well as basic alerts or
user prompts, so we should give it a more general name which matches this use.

This patch also cleans up a few includes of e-alert.h (formerly e-error.h) that
were not actually being used.

https://bugzilla.gnome.org/show_bug.cgi?id=602963
2009-11-30 13:33:04 -06:00

765 lines
20 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Authors:
* Jeffrey Stedfast <fejj@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <libxml/tree.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib/gi18n.h>
#include <camel/camel-session.h>
#include <camel/camel-vee-store.h>
#include <camel/camel-vtrash-folder.h>
#include <camel/camel-stream-mem.h>
#include <camel/camel-file-utils.h>
#include <camel/camel-stream-fs.h>
#include "e-util/e-mktemp.h"
#include "e-util/e-alert.h"
#include "em-vfolder-rule.h"
#include "mail-mt.h"
#include "mail-ops.h"
#include "mail-tools.h"
#include "mail-config.h"
#include "mail-vfolder.h"
#include "mail-folder-cache.h"
#include "em-utils.h"
#include "em-folder-tree.h"
#include "em-folder-tree-model.h"
#include "em-folder-utils.h"
#include "em-folder-selector.h"
#include "em-folder-properties.h"
#include "e-mail-local.h"
#include "e-mail-store.h"
#define d(x)
extern CamelSession *session;
static gboolean
emfu_is_special_local_folder (const gchar *name)
{
return (!strcmp (name, "Drafts") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent") || !strcmp (name, "Templates"));
}
struct _EMCopyFolders {
MailMsg base;
/* input data */
CamelStore *fromstore;
CamelStore *tostore;
gchar *frombase;
gchar *tobase;
gint delete;
};
static gchar *
emft_copy_folders__desc (struct _EMCopyFolders *m, gint complete)
{
return g_strdup_printf (_("Copying `%s' to `%s'"), m->frombase, m->tobase);
}
static void
emft_copy_folders__exec (struct _EMCopyFolders *m)
{
guint32 flags = CAMEL_STORE_FOLDER_INFO_FAST | CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
GList *pending = NULL, *deleting = NULL, *l;
GString *fromname, *toname;
CamelFolderInfo *fi;
const gchar *tmp;
gint fromlen;
if (!(fi = camel_store_get_folder_info (m->fromstore, m->frombase, flags, &m->base.ex)))
return;
pending = g_list_append (pending, fi);
toname = g_string_new ("");
fromname = g_string_new ("");
tmp = strrchr (m->frombase, '/');
if (tmp == NULL)
fromlen = 0;
else
fromlen = tmp - m->frombase + 1;
d(printf ("top name is '%s'\n", fi->full_name));
while (pending) {
CamelFolderInfo *info = pending->data;
pending = g_list_remove_link (pending, pending);
while (info) {
CamelFolder *fromfolder, *tofolder;
GPtrArray *uids;
gint deleted = 0;
if (info->child)
pending = g_list_append (pending, info->child);
if (m->tobase[0])
g_string_printf (toname, "%s/%s", m->tobase, info->full_name + fromlen);
else
g_string_printf (toname, "%s", info->full_name + fromlen);
d(printf ("Copying from '%s' to '%s'\n", info->full_name, toname->str));
/* This makes sure we create the same tree, e.g. from a nonselectable source */
/* Not sure if this is really the 'right thing', e.g. for spool stores, but it makes the ui work */
if ((info->flags & CAMEL_FOLDER_NOSELECT) == 0) {
d(printf ("this folder is selectable\n"));
if (m->tostore == m->fromstore && m->delete) {
camel_store_rename_folder (m->fromstore, info->full_name, toname->str, &m->base.ex);
if (camel_exception_is_set (&m->base.ex))
goto exception;
/* this folder no longer exists, unsubscribe it */
if (camel_store_supports_subscriptions (m->fromstore))
camel_store_unsubscribe_folder (m->fromstore, info->full_name, NULL);
deleted = 1;
} else {
if (!(fromfolder = camel_store_get_folder (m->fromstore, info->full_name, 0, &m->base.ex)))
goto exception;
if (!(tofolder = camel_store_get_folder (m->tostore, toname->str, CAMEL_STORE_FOLDER_CREATE, &m->base.ex))) {
camel_object_unref (fromfolder);
goto exception;
}
uids = camel_folder_get_uids (fromfolder);
camel_folder_transfer_messages_to (fromfolder, uids, tofolder, NULL, m->delete, &m->base.ex);
camel_folder_free_uids (fromfolder, uids);
if (m->delete && !camel_exception_is_set (&m->base.ex))
camel_folder_sync(fromfolder, TRUE, NULL);
camel_object_unref (fromfolder);
camel_object_unref (tofolder);
}
}
if (camel_exception_is_set (&m->base.ex))
goto exception;
else if (m->delete && !deleted)
deleting = g_list_prepend (deleting, info);
/* subscribe to the new folder if appropriate */
if (camel_store_supports_subscriptions (m->tostore)
&& !camel_store_folder_subscribed (m->tostore, toname->str))
camel_store_subscribe_folder (m->tostore, toname->str, NULL);
info = info->next;
}
}
/* delete the folders in reverse order from how we copyied them, if we are deleting any */
l = deleting;
while (l) {
CamelFolderInfo *info = l->data;
d(printf ("deleting folder '%s'\n", info->full_name));
/* FIXME: we need to do something with the exception
since otherwise the users sees a failed operation
with no error message or even any warnings */
if (camel_store_supports_subscriptions (m->fromstore))
camel_store_unsubscribe_folder (m->fromstore, info->full_name, NULL);
camel_store_delete_folder (m->fromstore, info->full_name, NULL);
l = l->next;
}
exception:
camel_store_free_folder_info (m->fromstore, fi);
g_list_free (deleting);
g_string_free (toname, TRUE);
g_string_free (fromname, TRUE);
}
static void
emft_copy_folders__free (struct _EMCopyFolders *m)
{
camel_object_unref (m->fromstore);
camel_object_unref (m->tostore);
g_free (m->frombase);
g_free (m->tobase);
}
static MailMsgInfo copy_folders_info = {
sizeof (struct _EMCopyFolders),
(MailMsgDescFunc) emft_copy_folders__desc,
(MailMsgExecFunc) emft_copy_folders__exec,
(MailMsgDoneFunc) NULL,
(MailMsgFreeFunc) emft_copy_folders__free
};
gint
em_folder_utils_copy_folders(CamelStore *fromstore, const gchar *frombase, CamelStore *tostore, const gchar *tobase, gint delete)
{
struct _EMCopyFolders *m;
gint seq;
m = mail_msg_new (&copy_folders_info);
camel_object_ref (fromstore);
m->fromstore = fromstore;
camel_object_ref (tostore);
m->tostore = tostore;
m->frombase = g_strdup (frombase);
m->tobase = g_strdup (tobase);
m->delete = delete;
seq = m->base.seq;
mail_msg_unordered_push (m);
return seq;
}
struct _copy_folder_data {
CamelFolderInfo *fi;
gboolean delete;
};
static void
emfu_copy_folder_selected (const gchar *uri, gpointer data)
{
struct _copy_folder_data *cfd = data;
CamelStore *fromstore = NULL, *tostore = NULL;
CamelStore *local_store;
const gchar *tobase = NULL;
CamelException ex;
CamelURL *url;
if (uri == NULL) {
g_free (cfd);
return;
}
camel_exception_init (&ex);
local_store = e_mail_local_get_store ();
if (!(fromstore = camel_session_get_store (session, cfd->fi->uri, &ex))) {
e_alert_run_dialog_for_args (e_shell_get_active_window (NULL),
cfd->delete?"mail:no-move-folder-notexist":"mail:no-copy-folder-notexist", cfd->fi->full_name, uri, ex.desc, NULL);
goto fail;
}
if (cfd->delete && fromstore == local_store && emfu_is_special_local_folder (cfd->fi->full_name)) {
GtkWidget *w;
w = e_alert_new_dialog_for_args (
e_shell_get_active_window (NULL), "mail:no-rename-special-folder",
cfd->fi->full_name, NULL);
em_utils_show_error_silent (w);
goto fail;
}
if (!(tostore = camel_session_get_store (session, uri, &ex))) {
e_alert_run_dialog_for_args (e_shell_get_active_window (NULL),
cfd->delete?"mail:no-move-folder-to-notexist":"mail:no-copy-folder-to-notexist", cfd->fi->full_name, uri, ex.desc, NULL);
goto fail;
}
url = camel_url_new (uri, NULL);
if (((CamelService *)tostore)->provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
tobase = url->fragment;
else if (url->path && url->path[0])
tobase = url->path+1;
if (tobase == NULL)
tobase = "";
em_folder_utils_copy_folders(fromstore, cfd->fi->full_name, tostore, tobase, cfd->delete);
camel_url_free (url);
fail:
if (fromstore)
camel_object_unref(fromstore);
if (tostore)
camel_object_unref(tostore);
camel_exception_clear (&ex);
g_free (cfd);
}
/* tree here is the 'destination' selector, not 'self' */
static gboolean
emfu_copy_folder_exclude(EMFolderTree *tree, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
struct _copy_folder_data *cfd = data;
gint fromvfolder, tovfolder;
gchar *touri;
guint flags;
gboolean is_store;
/* handles moving to/from vfolders */
fromvfolder = strncmp(cfd->fi->uri, "vfolder:", 8) == 0;
gtk_tree_model_get(model, iter, COL_STRING_URI, &touri, COL_UINT_FLAGS, &flags, COL_BOOL_IS_STORE, &is_store, -1);
tovfolder = strncmp(touri, "vfolder:", 8) == 0;
g_free(touri);
/* moving from vfolder to normal- not allowed */
if (fromvfolder && !tovfolder && cfd->delete)
return FALSE;
/* copy/move from normal folder to vfolder - not allowed */
if (!fromvfolder && tovfolder)
return FALSE;
/* copying to vfolder - not allowed */
if (tovfolder && !cfd->delete)
return FALSE;
return (flags & EMFT_EXCLUDE_NOINFERIORS) == 0;
}
/* FIXME: this interface references the folderinfo without copying it */
/* FIXME: these functions must be documented */
void
em_folder_utils_copy_folder (GtkWindow *parent,
CamelFolderInfo *folderinfo,
gint delete)
{
GtkWidget *dialog;
EMFolderTree *emft;
const gchar *label;
const gchar *title;
struct _copy_folder_data *cfd;
g_return_if_fail (folderinfo != NULL);
cfd = g_malloc (sizeof (*cfd));
cfd->fi = folderinfo;
cfd->delete = delete;
/* XXX Do we leak this reference. */
emft = (EMFolderTree *) em_folder_tree_new ();
em_folder_tree_set_excluded_func (
emft, emfu_copy_folder_exclude, cfd);
label = delete ? _("_Move") : _("C_opy");
title = delete ? _("Move Folder To") : _("Copy Folder To");
dialog = em_folder_selector_new (
parent, emft,
EM_FOLDER_SELECTOR_CAN_CREATE,
title, NULL, label);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
const gchar *uri;
uri = em_folder_selector_get_selected_uri (
EM_FOLDER_SELECTOR (dialog));
emfu_copy_folder_selected (uri, cfd);
}
gtk_widget_destroy (dialog);
}
static void
emfu_delete_done (CamelFolder *folder, gboolean removed, CamelException *ex, gpointer data)
{
GtkWidget *dialog = data;
if (ex && camel_exception_is_set (ex)) {
GtkWidget *w;
w = e_alert_new_dialog_for_args (
e_shell_get_active_window (NULL), "mail:no-delete-folder",
folder->full_name, camel_exception_get_description (ex), NULL);
em_utils_show_error_silent (w);
camel_exception_clear (ex);
}
if (dialog)
gtk_widget_destroy (dialog);
}
static void
emfu_delete_response (GtkWidget *dialog, gint response, gpointer data)
{
if (response == GTK_RESPONSE_OK) {
/* disable dialog until operation finishes */
gtk_widget_set_sensitive (dialog, FALSE);
mail_remove_folder (g_object_get_data ((GObject *) dialog, "folder"), emfu_delete_done, dialog);
} else {
gtk_widget_destroy (dialog);
}
}
/* FIXME: these functions must be documented */
void
em_folder_utils_delete_folder (CamelFolder *folder)
{
CamelStore *local_store;
GtkWindow *parent = e_shell_get_active_window (NULL);
GtkWidget *dialog;
gint flags = 0;
local_store = e_mail_local_get_store ();
if (folder->parent_store == local_store && emfu_is_special_local_folder (folder->full_name)) {
dialog = e_alert_new_dialog_for_args (
parent, "mail:no-delete-special-folder",
folder->full_name, NULL);
em_utils_show_error_silent (dialog);
return;
}
if (mail_folder_cache_get_folder_info_flags (folder, &flags) && (flags & CAMEL_FOLDER_SYSTEM))
{
e_alert_run_dialog_for_args (parent,"mail:no-delete-special-folder", folder->name, NULL);
return;
}
camel_object_ref (folder);
dialog = e_alert_new_dialog_for_args (parent,
(folder->parent_store && CAMEL_IS_VEE_STORE(folder->parent_store))?"mail:ask-delete-vfolder":"mail:ask-delete-folder",
folder->full_name, NULL);
g_object_set_data_full ((GObject *) dialog, "folder", folder, camel_object_unref);
g_signal_connect (dialog, "response", G_CALLBACK (emfu_delete_response), NULL);
gtk_widget_show (dialog);
}
struct _EMCreateFolder {
MailMsg base;
/* input data */
CamelStore *store;
gchar *full_name;
gchar *parent;
gchar *name;
/* output data */
CamelFolderInfo *fi;
/* callback data */
void (* done) (CamelFolderInfo *fi, gpointer user_data);
gpointer user_data;
};
/* Temporary Structure to hold data to pass across function */
struct _EMCreateFolderTempData
{
EMFolderTree * emft;
EMFolderSelector * emfs;
gchar *uri;
};
static gchar *
emfu_create_folder__desc (struct _EMCreateFolder *m)
{
return g_strdup_printf (_("Creating folder `%s'"), m->full_name);
}
static void
emfu_create_folder__exec (struct _EMCreateFolder *m)
{
d(printf ("creating folder parent='%s' name='%s' full_name='%s'\n", m->parent, m->name, m->full_name));
if ((m->fi = camel_store_create_folder (m->store, m->parent, m->name, &m->base.ex))) {
if (camel_store_supports_subscriptions (m->store))
camel_store_subscribe_folder (m->store, m->full_name, &m->base.ex);
}
}
static void
emfu_create_folder__done (struct _EMCreateFolder *m)
{
if (m->done)
m->done (m->fi, m->user_data);
}
static void
emfu_create_folder__free (struct _EMCreateFolder *m)
{
camel_store_free_folder_info (m->store, m->fi);
camel_object_unref (m->store);
g_free (m->full_name);
g_free (m->parent);
g_free (m->name);
}
static MailMsgInfo create_folder_info = {
sizeof (struct _EMCreateFolder),
(MailMsgDescFunc) emfu_create_folder__desc,
(MailMsgExecFunc) emfu_create_folder__exec,
(MailMsgDoneFunc) emfu_create_folder__done,
(MailMsgFreeFunc) emfu_create_folder__free
};
static gint
emfu_create_folder_real (CamelStore *store, const gchar *full_name, void (* done) (CamelFolderInfo *fi, gpointer user_data), gpointer user_data)
{
gchar *name, *namebuf = NULL;
struct _EMCreateFolder *m;
const gchar *parent;
gint id;
namebuf = g_strdup (full_name);
if (!(name = strrchr (namebuf, '/'))) {
name = namebuf;
parent = "";
} else {
*name++ = '\0';
parent = namebuf;
}
m = mail_msg_new (&create_folder_info);
camel_object_ref (store);
m->store = store;
m->full_name = g_strdup (full_name);
m->parent = g_strdup (parent);
m->name = g_strdup (name);
m->user_data = user_data;
m->done = done;
g_free (namebuf);
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
static void
new_folder_created_cb (CamelFolderInfo *fi, gpointer user_data)
{
struct _EMCreateFolderTempData *emcftd=user_data;
if (fi) {
gtk_widget_destroy ((GtkWidget *) emcftd->emfs);
/* Exapnding newly created folder */
if (emcftd->emft)
em_folder_tree_set_selected ((EMFolderTree *) emcftd->emft, emcftd->uri, GPOINTER_TO_INT(g_object_get_data ((GObject *)emcftd->emft, "select")) ? FALSE : TRUE);
}
g_object_unref (emcftd->emfs);
g_free (emcftd->uri);
g_free (emcftd);
}
static void
emfu_popup_new_folder_response (EMFolderSelector *emfs, gint response, gpointer data)
{
EMFolderTreeModelStoreInfo *si;
GtkTreeModel *model;
const gchar *uri, *path;
CamelException ex;
CamelStore *store;
struct _EMCreateFolderTempData *emcftd;
if (response != GTK_RESPONSE_OK) {
gtk_widget_destroy ((GtkWidget *) emfs);
return;
}
uri = em_folder_selector_get_selected_uri (emfs);
path = em_folder_selector_get_selected_path (emfs);
d(printf ("Creating new folder: %s (%s)\n", path, uri));
g_print ("DEBUG: %s (%s)\n", path, uri);
camel_exception_init (&ex);
if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) {
camel_exception_clear (&ex);
return;
}
model = gtk_tree_view_get_model (GTK_TREE_VIEW (emfs->emft));
si = em_folder_tree_model_lookup_store_info (
EM_FOLDER_TREE_MODEL (model), store);
if (si == NULL) {
camel_object_unref (store);
g_return_if_reached();
}
/* HACK: we need to create vfolders using the vfolder editor */
if (CAMEL_IS_VEE_STORE(store)) {
EMVFolderRule *rule;
/* ensures vfolder is running */
vfolder_load_storage ();
rule = em_vfolder_rule_new();
e_filter_rule_set_name((EFilterRule *)rule, path);
vfolder_gui_add_rule(rule);
gtk_widget_destroy((GtkWidget *)emfs);
} else {
/* Temp data to pass to create_folder_real function */
emcftd = (struct _EMCreateFolderTempData *) g_malloc(sizeof(struct _EMCreateFolderTempData));
emcftd->emfs = emfs;
emcftd->uri = g_strdup (uri);
emcftd->emft = (EMFolderTree *) data;
g_object_ref (emfs);
emfu_create_folder_real (si->store, path, new_folder_created_cb, emcftd);
}
camel_object_unref (store);
camel_exception_clear (&ex);
}
/* FIXME: these functions must be documented */
void
em_folder_utils_create_folder (CamelFolderInfo *folderinfo, EMFolderTree *emft, GtkWindow *parent)
{
EMFolderTree *folder_tree;
GtkWidget *dialog;
folder_tree = (EMFolderTree *) em_folder_tree_new ();
dialog = em_folder_selector_create_new (
parent, folder_tree, 0,
_("Create Folder"),
_("Specify where to create the folder:"));
if (folderinfo != NULL)
em_folder_selector_set_selected ((EMFolderSelector *) dialog, folderinfo->uri);
g_signal_connect (dialog, "response", G_CALLBACK (emfu_popup_new_folder_response), emft);
gtk_widget_show (dialog);
}
struct _folder_unsub_t {
MailMsg base;
gchar *folder_uri;
};
static gchar *
emfu_unsubscribe_folder__desc (struct _folder_unsub_t *msg)
{
return g_strdup_printf (
_("Unsubscribing from folder \"%s\""), msg->folder_uri);
}
static void
emfu_unsubscribe_folder__exec (struct _folder_unsub_t *msg)
{
CamelStore *store;
CamelURL *url;
const gchar *path = NULL;
gint url_flags;
store = camel_session_get_store (
session, msg->folder_uri, &msg->base.ex);
if (store == NULL)
return;
url = camel_url_new (msg->folder_uri, NULL);
url_flags = CAMEL_SERVICE (store)->provider->url_flags;
if (url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
path = url->fragment;
else if (url->path != NULL && *url->path != '\0')
path = url->path + 1;
if (path != NULL)
camel_store_unsubscribe_folder (store, path, &msg->base.ex);
camel_url_free (url);
}
static void
emfu_unsubscribe_folder__free (struct _folder_unsub_t *msg)
{
g_free (msg->folder_uri);
}
static MailMsgInfo unsubscribe_info = {
sizeof (struct _folder_unsub_t),
(MailMsgDescFunc) emfu_unsubscribe_folder__desc,
(MailMsgExecFunc) emfu_unsubscribe_folder__exec,
(MailMsgDoneFunc) NULL,
(MailMsgFreeFunc) emfu_unsubscribe_folder__free
};
void
em_folder_utils_unsubscribe_folder (const gchar *folder_uri)
{
struct _folder_unsub_t *unsub;
g_return_if_fail (folder_uri != NULL);
unsub = mail_msg_new (&unsubscribe_info);
unsub->folder_uri = g_strdup (folder_uri);
mail_msg_unordered_push (unsub);
}
const gchar *
em_folder_utils_get_icon_name (guint32 flags)
{
const gchar *icon_name;
switch (flags & CAMEL_FOLDER_TYPE_MASK) {
case CAMEL_FOLDER_TYPE_INBOX:
icon_name = "mail-inbox";
break;
case CAMEL_FOLDER_TYPE_OUTBOX:
icon_name = "mail-outbox";
break;
case CAMEL_FOLDER_TYPE_TRASH:
icon_name = "user-trash";
break;
case CAMEL_FOLDER_TYPE_JUNK:
icon_name = "mail-mark-junk";
break;
case CAMEL_FOLDER_TYPE_SENT:
icon_name = "mail-sent";
break;
default:
if (flags & CAMEL_FOLDER_SHARED_TO_ME)
icon_name = "stock_shared-to-me";
else if (flags & CAMEL_FOLDER_SHARED_BY_ME)
icon_name = "stock_shared-by-me";
else if (flags & CAMEL_FOLDER_VIRTUAL)
icon_name = "folder-saved-search";
else
icon_name = "folder";
}
return icon_name;
}