Commit 59da2bdf authored by Milan Crha's avatar Milan Crha

Bug #632683 - Remove-duplicates should work on selection

parent 1d8d943f
......@@ -406,81 +406,144 @@ action_mail_remove_attachments_cb (GtkAction *action, EMailReader *reader)
{
CamelFolder *folder;
GPtrArray *uids;
gint i, j;
folder = e_mail_reader_get_folder (reader);
uids = e_mail_reader_get_selected_uids (reader);
camel_folder_freeze (folder);
for (i = 0; i < (uids ? uids->len : 0); i++) {
CamelMimeMessage *message;
CamelDataWrapper *containee;
gchar *uid;
mail_remove_attachments (folder, uids);
}
uid = g_ptr_array_index (uids, i);
static gchar *
get_message_checksum (CamelFolder *folder, CamelMimeMessage *msg)
{
static const GChecksumType duplicate_csum = G_CHECKSUM_SHA256;
/* retrieve the message from the CamelFolder */
message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
if (!message) {
continue;
}
CamelDataWrapper *content;
CamelStream *mem;
GByteArray *buffer;
gchar *digest = NULL;
containee = camel_medium_get_content (CAMEL_MEDIUM (message));
if (containee == NULL) {
continue;
}
if (!msg)
return NULL;
if (CAMEL_IS_MULTIPART (containee)) {
gboolean deleted = FALSE;
gint parts;
parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
for (j = 0; j < parts; j++) {
CamelMimePart *mpart = camel_multipart_get_part (CAMEL_MULTIPART (containee), j);
const gchar *disposition = camel_mime_part_get_disposition (mpart);
if (disposition && (!strcmp (disposition, "attachment") || !strcmp (disposition, "inline"))) {
gchar *desc;
const gchar *filename;
filename = camel_mime_part_get_filename (mpart);
desc = g_strdup_printf (_("File \"%s\" has been removed."), filename ? filename : "");
camel_mime_part_set_disposition (mpart, "inline");
camel_mime_part_set_content (mpart, desc, strlen (desc), "text/plain");
camel_mime_part_set_content_type (mpart, "text/plain");
deleted = TRUE;
}
}
/* get message contents */
content = camel_medium_get_content ((CamelMedium *) msg);
if (!content)
return NULL;
if (deleted) {
/* copy the original message with the deleted attachment */
CamelMessageInfo *info, *newinfo;
guint32 flags;
GError *error = NULL;
/* calculate checksum */
mem = camel_stream_mem_new ();
camel_data_wrapper_decode_to_stream_sync (content, mem, NULL, NULL);
info = camel_folder_get_message_info (folder, uid);
newinfo = camel_message_info_new_from_header (NULL, CAMEL_MIME_PART (message)->headers);
flags = camel_folder_get_message_flags (folder, uid);
buffer = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
if (buffer)
digest = g_compute_checksum_for_data (duplicate_csum, buffer->data, buffer->len);
/* make a copy of the message */
camel_message_info_set_flags (newinfo, flags, flags);
camel_folder_append_message_sync (folder, message, newinfo, NULL, NULL, &error);
g_object_unref (mem);
if (!error) {
/* marked the original message deleted */
camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
}
return digest;
}
static gboolean
message_is_duplicated (GHashTable *messages, guint64 id, gchar *digest)
{
gchar *hash_digest = g_hash_table_lookup (messages, &id);
if (!hash_digest)
return FALSE;
return g_str_equal (digest, hash_digest);
}
static void
remove_duplicates_got_messages_cb (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, gpointer data)
{
EMailReader *reader = data;
GtkWindow *parent;
GHashTable *messages;
GPtrArray *dups;
gint ii;
g_return_if_fail (folder != NULL);
g_return_if_fail (CAMEL_IS_FOLDER (folder));
g_return_if_fail (uids != NULL);
g_return_if_fail (msgs != NULL);
g_return_if_fail (msgs->len <= uids->len);
g_return_if_fail (reader != NULL);
g_return_if_fail (E_IS_MAIL_READER (reader));
parent = e_mail_reader_get_window (reader);
messages = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
dups = g_ptr_array_new ();
for (ii = 0; ii < msgs->len; ii++) {
CamelMessageInfo *msg_info = camel_folder_get_message_info (folder, uids->pdata[ii]);
const CamelSummaryMessageID *mid = camel_message_info_message_id (msg_info);
guint32 flags = camel_message_info_flags (msg_info);
camel_folder_free_message_info (folder, info);
camel_message_info_free (newinfo);
if (!(flags & CAMEL_MESSAGE_DELETED)) {
gchar *digest = get_message_checksum (folder, msgs->pdata[ii]);
if (error)
g_error_free (error);
if (digest) {
if (message_is_duplicated (messages, mid->id.id, digest)) {
g_ptr_array_add (dups, uids->pdata[ii]);
g_free (digest);
} else {
guint64 *id;
id = g_new0 (guint64, 1);
*id = mid->id.id;
g_hash_table_insert (messages, id, digest);
}
}
}
camel_message_info_free (msg_info);
}
camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
camel_folder_thaw (folder);
if (dups->len == 0) {
em_utils_prompt_user (parent, NULL, "mail:info-no-remove-duplicates", camel_folder_get_name (folder), NULL);
} else {
gchar *msg = g_strdup_printf (ngettext (
/* Translators: %s is replaced with a folder name
%d with count of duplicate messages. */
_("Folder '%s' contains %d duplicate message. Are you sure you want to delete it?"),
_("Folder '%s' contains %d duplicate messages. Are you sure you want to delete them?"),
dups->len),
camel_folder_get_name (folder), dups->len);
if (em_utils_prompt_user (parent, NULL, "mail:ask-remove-duplicates", msg, NULL)) {
camel_folder_freeze (folder);
for (ii = 0; ii < dups->len; ii++)
camel_folder_delete_message (folder, g_ptr_array_index (dups, ii));
camel_folder_thaw (folder);
}
g_free (msg);
}
g_hash_table_destroy (messages);
g_ptr_array_free (dups, TRUE);
}
static void
action_mail_remove_duplicates (GtkAction *action, EMailReader *reader)
{
MessageList *message_list;
CamelFolder *folder;
GPtrArray *uids;
message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
uids = message_list_get_selected (message_list);
folder = message_list->folder;
if (!uids || uids->len <= 1) {
if (uids)
em_utils_uids_free (uids);
} else {
/* the function itself is freeing uids */
mail_get_messages (folder, uids, remove_duplicates_got_messages_cb, reader);
}
}
static void
......@@ -2107,6 +2170,13 @@ static GtkActionEntry mail_reader_entries[] = {
N_("Remove attachments"),
G_CALLBACK (action_mail_remove_attachments_cb) },
{ "mail-remove-duplicates",
NULL,
N_("Remove Du_plicate Messages"),
NULL,
N_("Checks selected messages for duplicates"),
G_CALLBACK (action_mail_remove_duplicates) },
{ "mail-reply-all",
NULL,
N_("Reply to _All"),
......@@ -2330,6 +2400,10 @@ static EPopupActionEntry mail_reader_popup_entries[] = {
NULL,
"mail-remove-attachments" },
{ "mail-popup-remove-duplicates",
NULL,
"mail-remove-duplicates" },
{ "mail-popup-reply-all",
NULL,
"mail-reply-all" },
......@@ -3165,6 +3239,11 @@ mail_reader_update_actions (EMailReader *reader,
action = e_mail_reader_get_action (reader, action_name);
gtk_action_set_sensitive (action, sensitive);
action_name = "mail-remove-duplicates";
sensitive = multiple_messages_selected;
action = e_mail_reader_get_action (reader, action_name);
gtk_action_set_sensitive (action, sensitive);
action_name = "mail-reply-all";
sensitive = have_enabled_account && single_message_selected;
action = e_mail_reader_get_action (reader, action_name);
......
......@@ -2599,3 +2599,142 @@ mail_disconnect_store (CamelStore *store)
return id;
}
/* ---------------------------------------------------------------------------------- */
struct _remove_attachments_msg {
MailMsg base;
CamelFolder *folder;
GPtrArray *uids;
};
static gchar *
remove_attachments_desc (struct _remove_attachments_msg *m)
{
return g_strdup_printf (_("Removing attachments"));
}
static void
remove_attachments_exec (struct _remove_attachments_msg *m,
GCancellable *cancellable,
GError **error)
{
CamelFolder *folder = m->folder;
GPtrArray *uids = m->uids;
gint ii, jj;
camel_folder_freeze (folder);
for (ii = 0; ii < (uids ? uids->len : 0); ii++) {
gint pc = ((ii + 1) * 100) / uids->len;
CamelMimeMessage *message;
CamelDataWrapper *containee;
gchar *uid;
uid = g_ptr_array_index (uids, ii);
/* retrieve the message from the CamelFolder */
message = camel_folder_get_message_sync (folder, uid, cancellable, NULL);
if (!message) {
camel_operation_progress (cancellable, pc);
continue;
}
containee = camel_medium_get_content (CAMEL_MEDIUM (message));
if (containee == NULL) {
camel_operation_progress (cancellable, pc);
continue;
}
if (CAMEL_IS_MULTIPART (containee)) {
gboolean deleted = FALSE;
gint parts;
parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
for (jj = 0; jj < parts; jj++) {
CamelMimePart *mpart = camel_multipart_get_part (CAMEL_MULTIPART (containee), jj);
const gchar *disposition = camel_mime_part_get_disposition (mpart);
if (disposition && (!strcmp (disposition, "attachment") || !strcmp (disposition, "inline"))) {
gchar *desc;
const gchar *filename;
filename = camel_mime_part_get_filename (mpart);
desc = g_strdup_printf (_("File \"%s\" has been removed."), filename ? filename : "");
camel_mime_part_set_disposition (mpart, "inline");
camel_mime_part_set_content (mpart, desc, strlen (desc), "text/plain");
camel_mime_part_set_content_type (mpart, "text/plain");
deleted = TRUE;
}
}
if (deleted) {
/* copy the original message with the deleted attachment */
CamelMessageInfo *info, *newinfo;
guint32 flags;
GError *local_error = NULL;
info = camel_folder_get_message_info (folder, uid);
newinfo = camel_message_info_new_from_header (NULL, CAMEL_MIME_PART (message)->headers);
flags = camel_folder_get_message_flags (folder, uid);
/* make a copy of the message */
camel_message_info_set_flags (newinfo, flags, flags);
camel_folder_append_message_sync (folder, message, newinfo, NULL, cancellable, &local_error);
if (!local_error) {
/* marked the original message deleted */
camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
}
camel_folder_free_message_info (folder, info);
camel_message_info_free (newinfo);
if (local_error) {
g_propagate_error (error, local_error);
break;
}
}
}
camel_operation_progress (cancellable, pc);
}
if (!error || !*error)
camel_folder_synchronize_sync (folder, FALSE, cancellable, error);
camel_folder_thaw (folder);
}
static void
remove_attachments_free (struct _remove_attachments_msg *m)
{
g_object_unref (m->folder);
em_utils_uids_free (m->uids);
}
static MailMsgInfo remove_attachments_info = {
sizeof (struct _remove_attachments_msg),
(MailMsgDescFunc) remove_attachments_desc,
(MailMsgExecFunc) remove_attachments_exec,
(MailMsgDoneFunc) NULL,
(MailMsgFreeFunc) remove_attachments_free
};
/* it takes ownership of 'uids' array */
gint
mail_remove_attachments (CamelFolder *folder, GPtrArray *uids)
{
struct _remove_attachments_msg *m;
gint id;
g_return_val_if_fail (folder != NULL, -1);
g_return_val_if_fail (uids != NULL, -1);
m = mail_msg_new (&remove_attachments_info);
m->folder = g_object_ref (folder);
m->uids = uids;
id = m->base.seq;
mail_msg_unordered_push (m);
return id;
}
......@@ -164,6 +164,7 @@ gint mail_check_service (EMailSession *session,
gpointer data);
gint mail_disconnect_store (CamelStore *store);
gint mail_remove_attachments (CamelFolder *folder, GPtrArray *uids);
G_END_DECLS
......
......@@ -447,133 +447,6 @@ action_mail_folder_rename_cb (GtkAction *action,
em_folder_tree_edit_selected (folder_tree);
}
static gchar *
get_message_checksum (CamelFolder *folder, const gchar *uid)
{
static const GChecksumType duplicate_csum = G_CHECKSUM_SHA256;
CamelMimeMessage *msg;
GError *error = NULL;
CamelDataWrapper *content;
CamelStream *mem;
GByteArray *buffer;
gchar *digest = NULL;
msg = camel_folder_get_message_sync (folder, uid, NULL, &error);
if (error || !msg) {
if (error)
g_error_free (error);;
return NULL;
}
/* get message contents */
content = camel_medium_get_content ((CamelMedium *) msg);
if (!content)
return NULL;
/* calculate checksum */
mem = camel_stream_mem_new ();
camel_data_wrapper_decode_to_stream_sync (content, mem, NULL, NULL);
buffer = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
if (buffer)
digest = g_compute_checksum_for_data (duplicate_csum, buffer->data, buffer->len);
g_object_unref (mem);
g_object_unref (msg);
return digest;
}
static gboolean
message_is_duplicated (GHashTable *messages, guint64 id, gchar *digest)
{
gchar *hash_digest = g_hash_table_lookup (messages, &id);
if (!hash_digest)
return FALSE;
return g_str_equal (digest, hash_digest);
}
static void
action_mail_folder_remove_duplicates (GtkAction *action, EMailShellView *mail_shell_view)
{
EShellView *shell_view;
EShellContent *shell_content;
GtkWindow *parent;
EMailReader *reader;
MessageList *message_list;
CamelFolder *folder;
GHashTable *messages;
GPtrArray *uids, *dups;
gint i;
shell_view = E_SHELL_VIEW (mail_shell_view);
shell_content = e_shell_view_get_shell_content (shell_view);
parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
reader = E_MAIL_READER (shell_content);
message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
uids = message_list_get_uids (message_list);
folder = message_list->folder;
messages = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
dups = g_ptr_array_new ();
for (i = 0; i < uids->len; i++) {
CamelMessageInfo *msg_info = camel_folder_get_message_info (folder, uids->pdata[i]);
const CamelSummaryMessageID *mid = camel_message_info_message_id (msg_info);
guint32 flags = camel_message_info_flags (msg_info);
if (!(flags & CAMEL_MESSAGE_DELETED)) {
gchar *digest = get_message_checksum (folder, uids->pdata[i]);
if (digest) {
if (message_is_duplicated (messages, mid->id.id, digest)) {
g_ptr_array_add (dups, uids->pdata[i]);
g_free (digest);
} else {
guint64 *id;
id = g_new0 (guint64, 1);
*id = mid->id.id;
g_hash_table_insert (messages, id, digest);
}
}
}
camel_message_info_free (msg_info);
}
if (dups->len == 0) {
em_utils_prompt_user (parent, NULL, "mail:info-no-remove-duplicates", camel_folder_get_name (folder), NULL);
} else {
gchar *msg = g_strdup_printf (ngettext (
/* Translators: %s is replaced with a folder name
%d with count of duplicate messages. */
_("Folder '%s' contains %d duplicate message. Are you sure you want to delete it?"),
_("Folder '%s' contains %d duplicate messages. Are you sure you want to delete them?"),
dups->len),
camel_folder_get_name (folder), dups->len);
if (em_utils_prompt_user (parent, NULL, "mail:ask-remove-duplicates", msg, NULL)) {
gint ii;
camel_folder_freeze (folder);
for (ii = 0; ii < dups->len; ii++)
camel_folder_delete_message (folder, g_ptr_array_index (dups, ii));
camel_folder_thaw (folder);
}
g_free (msg);
}
g_hash_table_destroy (messages);
em_utils_uids_free (uids);
g_ptr_array_free (dups, TRUE);
}
static void
action_mail_folder_select_thread_cb (GtkAction *action,
EMailShellView *mail_shell_view)
......@@ -1249,13 +1122,6 @@ static GtkActionEntry mail_entries[] = {
N_("Change the name of this folder"),
G_CALLBACK (action_mail_folder_rename_cb) },
{ "mail-folder-remove-duplicates",
NULL,
N_("Remo_ve Duplicate Messages"),
"",
N_("Remove all duplicate messages"),
G_CALLBACK (action_mail_folder_remove_duplicates) },
{ "mail-folder-select-thread",
NULL,
N_("Select Message _Thread"),
......
......@@ -85,8 +85,6 @@
E_SHELL_WINDOW_ACTION ((window), "mail-folder-rename")
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_ALL(window) \
E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-all")
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_REMOVE_DUPLICATES(window) \
E_SHELL_WINDOW_ACTION ((window), "mail-folder-remove-duplicates")
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_THREAD(window) \
E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-thread")
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_SUBTHREAD(window) \
......
......@@ -97,6 +97,7 @@
<menuitem action='mail-filters-apply'/>
<menuitem action='mail-check-for-junk'/>
<menuitem action='mail-remove-attachments'/>
<menuitem action='mail-remove-duplicates'/>
<separator/>
<menu action='mail-create-rule-menu'>
<menuitem action='mail-filter-on-subject'/>
......@@ -159,6 +160,7 @@
<menuitem action='mail-popup-save-as'/>
<menuitem action='mail-popup-print'/>
<menuitem action='mail-popup-remove-attachments'/>
<menuitem action='mail-popup-remove-duplicates'/>
</placeholder>
</popup>
<popup name='mail-preview-popup'>
......
......@@ -45,7 +45,6 @@
<menuitem action='mail-folder-select-thread'/>
<menuitem action='mail-folder-select-subthread'/>
<menuitem action='mail-folder-mark-all-as-read'/>
<menuitem action='mail-folder-remove-duplicates'/>
<menuitem action='mail-folder-expunge'/>
<separator/>
<menuitem action='mail-folder-rename'/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment