evolution-3.6.4/mail/e-mail-reader-utils.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-reader-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-reader-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * e-mail-reader-utils.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  19  *
  20  */
  21 
  22 /* Miscellaneous utility functions used by EMailReader actions. */
  23 
  24 #ifdef HAVE_CONFIG_H
  25 #include <config.h>
  26 #endif
  27 
  28 #include "e-mail-reader-utils.h"
  29 
  30 #include <glib/gi18n.h>
  31 #include <libxml/tree.h>
  32 #include <gtkhtml/gtkhtml.h>
  33 #include <camel/camel.h>
  34 
  35 #include "libevolution-utils/e-alert-dialog.h"
  36 #include "filter/e-filter-rule.h"
  37 #include "misc/e-web-view.h"
  38 #include "shell/e-shell-utils.h"
  39 
  40 #include <libemail-engine/e-mail-folder-utils.h>
  41 #include <libemail-engine/e-mail-utils.h>
  42 #include <libemail-engine/mail-ops.h>
  43 #include <libemail-engine/mail-tools.h>
  44 
  45 #include "mail/e-mail-backend.h"
  46 #include "mail/e-mail-browser.h"
  47 #include "mail/e-mail-printer.h"
  48 #include "mail/e-mail-display.h"
  49 #include "mail/em-composer-utils.h"
  50 #include "mail/em-utils.h"
  51 #include "mail/mail-autofilter.h"
  52 #include "mail/mail-vfolder-ui.h"
  53 #include "mail/message-list.h"
  54 
  55 #include <em-format/e-mail-parser.h>
  56 #include <em-format/e-mail-part-utils.h>
  57 
  58 #define d(x)
  59 
  60 typedef struct _AsyncContext AsyncContext;
  61 
  62 struct _AsyncContext {
  63 	EActivity *activity;
  64 	CamelFolder *folder;
  65 	CamelMimeMessage *message;
  66 	EMailPartList *part_list;
  67 	EMailReader *reader;
  68 	CamelInternetAddress *address;
  69 	gchar *folder_name;
  70 	gchar *message_uid;
  71 
  72 	EMailReplyType reply_type;
  73 	EMailReplyStyle reply_style;
  74 	GtkPrintOperationAction print_action;
  75 	const gchar *filter_source;
  76 	gint filter_type;
  77 };
  78 
  79 static void
  80 async_context_free (AsyncContext *context)
  81 {
  82 	if (context->activity != NULL)
  83 		g_object_unref (context->activity);
  84 
  85 	if (context->folder != NULL)
  86 		g_object_unref (context->folder);
  87 
  88 	if (context->message != NULL)
  89 		g_object_unref (context->message);
  90 
  91 	if (context->part_list != NULL)
  92 		g_object_unref (context->part_list);
  93 
  94 	if (context->reader != NULL)
  95 		g_object_unref (context->reader);
  96 
  97 	if (context->address != NULL)
  98 		g_object_unref (context->address);
  99 
 100 	g_free (context->folder_name);
 101 	g_free (context->message_uid);
 102 
 103 	g_slice_free (AsyncContext, context);
 104 }
 105 
 106 static gboolean
 107 mail_reader_is_special_local_folder (const gchar *name)
 108 {
 109 	return (strcmp (name, "Drafts") == 0 ||
 110 		strcmp (name, "Inbox") == 0 ||
 111 		strcmp (name, "Outbox") == 0 ||
 112 		strcmp (name, "Sent") == 0 ||
 113 		strcmp (name, "Templates") == 0);
 114 }
 115 
 116 gboolean
 117 e_mail_reader_confirm_delete (EMailReader *reader)
 118 {
 119 	EShell *shell;
 120 	EMailBackend *backend;
 121 	EShellBackend *shell_backend;
 122 	EShellSettings *shell_settings;
 123 	CamelFolder *folder;
 124 	CamelStore *parent_store;
 125 	GtkWidget *check_button;
 126 	GtkWidget *container;
 127 	GtkWidget *dialog;
 128 	GtkWindow *window;
 129 	const gchar *label;
 130 	gboolean prompt_delete_in_vfolder;
 131 	gint response;
 132 
 133 	/* Remind users what deleting from a search folder does. */
 134 
 135 	g_return_val_if_fail (E_IS_MAIL_READER (reader), FALSE);
 136 
 137 	backend = e_mail_reader_get_backend (reader);
 138 	folder = e_mail_reader_get_folder (reader);
 139 	window = e_mail_reader_get_window (reader);
 140 
 141 	shell_backend = E_SHELL_BACKEND (backend);
 142 	shell = e_shell_backend_get_shell (shell_backend);
 143 	shell_settings = e_shell_get_shell_settings (shell);
 144 
 145 	prompt_delete_in_vfolder = e_shell_settings_get_boolean (
 146 		shell_settings, "mail-prompt-delete-in-vfolder");
 147 
 148 	parent_store = camel_folder_get_parent_store (folder);
 149 
 150 	if (!CAMEL_IS_VEE_STORE (parent_store))
 151 		return TRUE;
 152 
 153 	if (!prompt_delete_in_vfolder)
 154 		return TRUE;
 155 
 156 	dialog = e_alert_dialog_new_for_args (
 157 		window, "mail:ask-delete-vfolder-msg",
 158 		camel_folder_get_full_name (folder), NULL);
 159 
 160 	container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
 161 
 162 	label = _("Do not warn me again");
 163 	check_button = gtk_check_button_new_with_label (label);
 164 	gtk_box_pack_start (GTK_BOX (container), check_button, TRUE, TRUE, 6);
 165 	gtk_widget_show (check_button);
 166 
 167 	response = gtk_dialog_run (GTK_DIALOG (dialog));
 168 
 169 	if (response != GTK_RESPONSE_DELETE_EVENT)
 170 		e_shell_settings_set_boolean (
 171 			shell_settings,
 172 			"mail-prompt-delete-in-vfolder",
 173 			!gtk_toggle_button_get_active (
 174 			GTK_TOGGLE_BUTTON (check_button)));
 175 
 176 	gtk_widget_destroy (dialog);
 177 
 178 	return (response == GTK_RESPONSE_OK);
 179 }
 180 
 181 static void
 182 mail_reader_delete_folder_cb (CamelFolder *folder,
 183                               GAsyncResult *result,
 184                               AsyncContext *context)
 185 {
 186 	EAlertSink *alert_sink;
 187 	GError *error = NULL;
 188 
 189 	alert_sink = e_activity_get_alert_sink (context->activity);
 190 
 191 	e_mail_folder_remove_finish (folder, result, &error);
 192 
 193 	if (e_activity_handle_cancellation (context->activity, error)) {
 194 		g_error_free (error);
 195 
 196 	} else if (error != NULL) {
 197 		e_alert_submit (
 198 			alert_sink, "mail:no-delete-folder",
 199 			camel_folder_get_full_name (folder),
 200 			error->message, NULL);
 201 		g_error_free (error);
 202 
 203 	} else {
 204 		e_activity_set_state (
 205 			context->activity, E_ACTIVITY_COMPLETED);
 206 	}
 207 
 208 	async_context_free (context);
 209 }
 210 
 211 void
 212 e_mail_reader_delete_folder (EMailReader *reader,
 213                              CamelFolder *folder)
 214 {
 215 	EMailBackend *backend;
 216 	EMailSession *session;
 217 	EShell *shell;
 218 	EAlertSink *alert_sink;
 219 	CamelStore *parent_store;
 220 	CamelProvider *provider;
 221 	MailFolderCache *folder_cache;
 222 	GtkWindow *parent = e_shell_get_active_window (NULL);
 223 	GtkWidget *dialog;
 224 	gboolean store_is_local;
 225 	const gchar *display_name;
 226 	const gchar *full_name;
 227 	CamelFolderInfoFlags flags = 0;
 228 	gboolean have_flags;
 229 
 230 	g_return_if_fail (E_IS_MAIL_READER (reader));
 231 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
 232 
 233 	full_name = camel_folder_get_full_name (folder);
 234 	display_name = camel_folder_get_display_name (folder);
 235 	parent_store = camel_folder_get_parent_store (folder);
 236 	provider = camel_service_get_provider (CAMEL_SERVICE (parent_store));
 237 
 238 	store_is_local = (provider->flags & CAMEL_PROVIDER_IS_LOCAL) != 0;
 239 
 240 	backend = e_mail_reader_get_backend (reader);
 241 	session = e_mail_backend_get_session (backend);
 242 
 243 	alert_sink = e_mail_reader_get_alert_sink (reader);
 244 	folder_cache = e_mail_session_get_folder_cache (session);
 245 
 246 	if (store_is_local &&
 247 		mail_reader_is_special_local_folder (full_name)) {
 248 		e_alert_submit (
 249 			alert_sink, "mail:no-delete-special-folder",
 250 			display_name, NULL);
 251 		return;
 252 	}
 253 
 254 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 255 
 256 	if (!store_is_local && !e_shell_get_online (shell))
 257 	{
 258 		e_alert_submit (
 259 			alert_sink, "mail:online-operation",
 260 			display_name, NULL);
 261 		return;
 262 	}
 263 
 264 	have_flags = mail_folder_cache_get_folder_info_flags (
 265 		folder_cache, folder, &flags);
 266 
 267 	if (have_flags && (flags & CAMEL_FOLDER_SYSTEM)) {
 268 		e_alert_submit (
 269 			alert_sink, "mail:no-delete-special-folder",
 270 			display_name, NULL);
 271 		return;
 272 	}
 273 
 274 	if (have_flags && (flags & CAMEL_FOLDER_CHILDREN)) {
 275 		if (CAMEL_IS_VEE_STORE (parent_store))
 276 			dialog = e_alert_dialog_new_for_args (
 277 				parent, "mail:ask-delete-vfolder",
 278 				display_name, NULL);
 279 		else
 280 			dialog = e_alert_dialog_new_for_args (
 281 				parent, "mail:ask-delete-folder",
 282 				display_name, NULL);
 283 	} else {
 284 		if (CAMEL_IS_VEE_STORE (parent_store))
 285 			dialog = e_alert_dialog_new_for_args (
 286 				parent, "mail:ask-delete-vfolder-nochild",
 287 				display_name, NULL);
 288 		else
 289 			dialog = e_alert_dialog_new_for_args (
 290 				parent, "mail:ask-delete-folder-nochild",
 291 				display_name, NULL);
 292 	}
 293 
 294 	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
 295 		EActivity *activity;
 296 		AsyncContext *context;
 297 		GCancellable *cancellable;
 298 
 299 		activity = e_mail_reader_new_activity (reader);
 300 		cancellable = e_activity_get_cancellable (activity);
 301 
 302 		context = g_slice_new0 (AsyncContext);
 303 		context->activity = activity;
 304 		context->reader = g_object_ref (reader);
 305 
 306 		/* Disable the dialog until the activity finishes. */
 307 		gtk_widget_set_sensitive (dialog, FALSE);
 308 
 309 		/* Destroy the dialog once the activity finishes. */
 310 		g_object_set_data_full (
 311 			G_OBJECT (activity), "delete-dialog",
 312 			dialog, (GDestroyNotify) gtk_widget_destroy);
 313 
 314 		e_mail_folder_remove (
 315 			folder, G_PRIORITY_DEFAULT,
 316 			cancellable, (GAsyncReadyCallback)
 317 			mail_reader_delete_folder_cb, context);
 318 	} else
 319 		gtk_widget_destroy (dialog);
 320 }
 321 
 322 static void
 323 mail_reader_delete_folder_name_cb (GObject *source_object,
 324                                    GAsyncResult *result,
 325                                    gpointer user_data)
 326 {
 327 	CamelStore *store;
 328 	CamelFolder *folder;
 329 	AsyncContext *context;
 330 	EAlertSink *alert_sink;
 331 	GError *error = NULL;
 332 
 333 	store = CAMEL_STORE (source_object);
 334 	context = (AsyncContext *) user_data;
 335 
 336 	alert_sink = e_activity_get_alert_sink (context->activity);
 337 
 338 	/* XXX The returned CamelFolder is a borrowed reference. */
 339 	folder = camel_store_get_folder_finish (store, result, &error);
 340 
 341 	if (e_activity_handle_cancellation (context->activity, error)) {
 342 		g_error_free (error);
 343 
 344 	} else if (error != NULL) {
 345 		e_alert_submit (
 346 			alert_sink, "mail:no-delete-folder",
 347 			context->folder_name, error->message, NULL);
 348 		g_error_free (error);
 349 
 350 	} else {
 351 		e_activity_set_state (
 352 			context->activity, E_ACTIVITY_COMPLETED);
 353 		e_mail_reader_delete_folder (context->reader, folder);
 354 	}
 355 
 356 	async_context_free (context);
 357 }
 358 
 359 void
 360 e_mail_reader_delete_folder_name (EMailReader *reader,
 361                                   CamelStore *store,
 362                                   const gchar *folder_name)
 363 {
 364 	EActivity *activity;
 365 	AsyncContext *context;
 366 	GCancellable *cancellable;
 367 
 368 	g_return_if_fail (E_IS_MAIL_READER (reader));
 369 	g_return_if_fail (CAMEL_IS_STORE (store));
 370 	g_return_if_fail (folder_name != NULL);
 371 
 372 	activity = e_mail_reader_new_activity (reader);
 373 	cancellable = e_activity_get_cancellable (activity);
 374 
 375 	context = g_slice_new0 (AsyncContext);
 376 	context->activity = activity;
 377 	context->reader = g_object_ref (reader);
 378 	context->folder_name = g_strdup (folder_name);
 379 
 380 	camel_store_get_folder (
 381 		store, folder_name,
 382 		CAMEL_STORE_FOLDER_INFO_FAST,
 383 		G_PRIORITY_DEFAULT, cancellable,
 384 		mail_reader_delete_folder_name_cb,
 385 		context);
 386 }
 387 
 388 /* Helper for e_mail_reader_expunge_folder() */
 389 static void
 390 mail_reader_expunge_folder_cb (GObject *source_object,
 391                                GAsyncResult *result,
 392                                gpointer user_data)
 393 {
 394 	CamelFolder *folder;
 395 	AsyncContext *context;
 396 	EAlertSink *alert_sink;
 397 	GError *error = NULL;
 398 
 399 	folder = CAMEL_FOLDER (source_object);
 400 	context = (AsyncContext *) user_data;
 401 
 402 	alert_sink = e_activity_get_alert_sink (context->activity);
 403 
 404 	if (e_activity_handle_cancellation (context->activity, error)) {
 405 		g_error_free (error);
 406 
 407 	} else if (error != NULL) {
 408 		e_alert_submit (
 409 			alert_sink, "mail:no-expunge-folder",
 410 			camel_folder_get_display_name (folder),
 411 			error->message, NULL);
 412 		g_error_free (error);
 413 
 414 	} else {
 415 		e_activity_set_state (
 416 			context->activity, E_ACTIVITY_COMPLETED);
 417 	}
 418 
 419 	async_context_free (context);
 420 }
 421 
 422 void
 423 e_mail_reader_expunge_folder (EMailReader *reader,
 424                               CamelFolder *folder)
 425 {
 426 	GtkWindow *window;
 427 	const gchar *display_name;
 428 	gboolean proceed;
 429 
 430 	g_return_if_fail (E_IS_MAIL_READER (reader));
 431 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
 432 
 433 	window = e_mail_reader_get_window (reader);
 434 	display_name = camel_folder_get_display_name (folder);
 435 
 436 	proceed = em_utils_prompt_user (
 437 		window, "prompt-on-expunge",
 438 		"mail:ask-expunge", display_name, NULL);
 439 
 440 	if (proceed) {
 441 		EActivity *activity;
 442 		AsyncContext *context;
 443 		GCancellable *cancellable;
 444 
 445 		activity = e_mail_reader_new_activity (reader);
 446 		cancellable = e_activity_get_cancellable (activity);
 447 
 448 		context = g_slice_new0 (AsyncContext);
 449 		context->activity = activity;
 450 		context->reader = g_object_ref (reader);
 451 
 452 		e_mail_folder_expunge (
 453 			folder, G_PRIORITY_DEFAULT, cancellable,
 454 			mail_reader_expunge_folder_cb, context);
 455 	}
 456 }
 457 
 458 /* Helper for e_mail_reader_expunge_folder_name() */
 459 static void
 460 mail_reader_expunge_folder_name_cb (GObject *source_object,
 461                                     GAsyncResult *result,
 462                                     gpointer user_data)
 463 {
 464 	CamelStore *store;
 465 	CamelFolder *folder;
 466 	AsyncContext *context;
 467 	EAlertSink *alert_sink;
 468 	GError *error = NULL;
 469 
 470 	store = CAMEL_STORE (source_object);
 471 	context = (AsyncContext *) user_data;
 472 
 473 	alert_sink = e_activity_get_alert_sink (context->activity);
 474 
 475 	/* XXX The returned CamelFolder is a borrowed reference. */
 476 	folder = camel_store_get_folder_finish (store, result, &error);
 477 
 478 	if (e_activity_handle_cancellation (context->activity, error)) {
 479 		g_error_free (error);
 480 
 481 	} else if (error != NULL) {
 482 		e_alert_submit (
 483 			alert_sink, "mail:no-expunge-folder",
 484 			context->folder_name, error->message, NULL);
 485 		g_error_free (error);
 486 
 487 	} else {
 488 		e_activity_set_state (
 489 			context->activity, E_ACTIVITY_COMPLETED);
 490 		e_mail_reader_expunge_folder (context->reader, folder);
 491 	}
 492 
 493 	async_context_free (context);
 494 }
 495 
 496 void
 497 e_mail_reader_expunge_folder_name (EMailReader *reader,
 498                                    CamelStore *store,
 499                                    const gchar *folder_name)
 500 {
 501 	EActivity *activity;
 502 	AsyncContext *context;
 503 	GCancellable *cancellable;
 504 
 505 	g_return_if_fail (E_IS_MAIL_READER (reader));
 506 	g_return_if_fail (CAMEL_IS_STORE (store));
 507 	g_return_if_fail (folder_name != NULL);
 508 
 509 	activity = e_mail_reader_new_activity (reader);
 510 	cancellable = e_activity_get_cancellable (activity);
 511 
 512 	context = g_slice_new0 (AsyncContext);
 513 	context->activity = activity;
 514 	context->reader = g_object_ref (reader);
 515 	context->folder_name = g_strdup (folder_name);
 516 
 517 	camel_store_get_folder (
 518 		store, folder_name,
 519 		CAMEL_STORE_FOLDER_INFO_FAST,
 520 		G_PRIORITY_DEFAULT, cancellable,
 521 		mail_reader_expunge_folder_name_cb,
 522 		context);
 523 }
 524 
 525 /* Helper for e_mail_reader_refresh_folder() */
 526 static void
 527 mail_reader_refresh_folder_cb (GObject *source_object,
 528                                GAsyncResult *result,
 529                                gpointer user_data)
 530 {
 531 	CamelFolder *folder;
 532 	AsyncContext *context;
 533 	EAlertSink *alert_sink;
 534 	GError *error = NULL;
 535 
 536 	folder = CAMEL_FOLDER (source_object);
 537 	context = (AsyncContext *) user_data;
 538 
 539 	alert_sink = e_activity_get_alert_sink (context->activity);
 540 
 541 	if (e_activity_handle_cancellation (context->activity, error)) {
 542 		g_error_free (error);
 543 
 544 	} else if (error != NULL) {
 545 		e_alert_submit (
 546 			alert_sink, "mail:no-refresh-folder",
 547 			camel_folder_get_display_name (folder),
 548 			error->message, NULL);
 549 		g_error_free (error);
 550 
 551 	} else {
 552 		e_activity_set_state (
 553 			context->activity, E_ACTIVITY_COMPLETED);
 554 	}
 555 
 556 	async_context_free (context);
 557 }
 558 
 559 void
 560 e_mail_reader_refresh_folder (EMailReader *reader,
 561                               CamelFolder *folder)
 562 {
 563 	EActivity *activity;
 564 	AsyncContext *context;
 565 	GCancellable *cancellable;
 566 
 567 	g_return_if_fail (E_IS_MAIL_READER (reader));
 568 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
 569 
 570 	activity = e_mail_reader_new_activity (reader);
 571 	cancellable = e_activity_get_cancellable (activity);
 572 
 573 	context = g_slice_new0 (AsyncContext);
 574 	context->activity = activity;
 575 	context->reader = g_object_ref (reader);
 576 
 577 	camel_folder_refresh_info (
 578 		folder, G_PRIORITY_DEFAULT, cancellable,
 579 		mail_reader_refresh_folder_cb, context);
 580 }
 581 
 582 /* Helper for e_mail_reader_refresh_folder_name() */
 583 static void
 584 mail_reader_refresh_folder_name_cb (GObject *source_object,
 585                                     GAsyncResult *result,
 586                                     gpointer user_data)
 587 {
 588 	CamelStore *store;
 589 	CamelFolder *folder;
 590 	AsyncContext *context;
 591 	EAlertSink *alert_sink;
 592 	GError *error = NULL;
 593 
 594 	store = CAMEL_STORE (source_object);
 595 	context = (AsyncContext *) user_data;
 596 
 597 	alert_sink = e_activity_get_alert_sink (context->activity);
 598 
 599 	/* XXX The returned CamelFolder is a borrowed reference. */
 600 	folder = camel_store_get_folder_finish (store, result, &error);
 601 
 602 	if (e_activity_handle_cancellation (context->activity, error)) {
 603 		g_error_free (error);
 604 
 605 	} else if (error != NULL) {
 606 		e_alert_submit (
 607 			alert_sink, "mail:no-refresh-folder",
 608 			context->folder_name, error->message, NULL);
 609 		g_error_free (error);
 610 
 611 	} else {
 612 		e_activity_set_state (
 613 			context->activity, E_ACTIVITY_COMPLETED);
 614 		e_mail_reader_refresh_folder (context->reader, folder);
 615 	}
 616 
 617 	async_context_free (context);
 618 }
 619 
 620 void
 621 e_mail_reader_refresh_folder_name (EMailReader *reader,
 622                                    CamelStore *store,
 623                                    const gchar *folder_name)
 624 {
 625 	EActivity *activity;
 626 	AsyncContext *context;
 627 	GCancellable *cancellable;
 628 
 629 	g_return_if_fail (E_IS_MAIL_READER (reader));
 630 	g_return_if_fail (CAMEL_IS_STORE (store));
 631 	g_return_if_fail (folder_name != NULL);
 632 
 633 	activity = e_mail_reader_new_activity (reader);
 634 	cancellable = e_activity_get_cancellable (activity);
 635 
 636 	context = g_slice_new0 (AsyncContext);
 637 	context->activity = activity;
 638 	context->reader = g_object_ref (reader);
 639 	context->folder_name = g_strdup (folder_name);
 640 
 641 	camel_store_get_folder (
 642 		store, folder_name,
 643 		CAMEL_STORE_FOLDER_INFO_FAST,
 644 		G_PRIORITY_DEFAULT, cancellable,
 645 		mail_reader_refresh_folder_name_cb,
 646 		context);
 647 }
 648 
 649 /* Helper for e_mail_reader_unsubscribe_folder_name() */
 650 static void
 651 mail_reader_unsubscribe_folder_name_cb (GObject *source_object,
 652                                         GAsyncResult *result,
 653                                         gpointer user_data)
 654 {
 655 	CamelSubscribable *subscribable;
 656 	AsyncContext *context;
 657 	EAlertSink *alert_sink;
 658 	GError *error = NULL;
 659 
 660 	subscribable = CAMEL_SUBSCRIBABLE (source_object);
 661 	context = (AsyncContext *) user_data;
 662 
 663 	alert_sink = e_activity_get_alert_sink (context->activity);
 664 
 665 	camel_subscribable_unsubscribe_folder_finish (
 666 		subscribable, result, &error);
 667 
 668 	if (e_activity_handle_cancellation (context->activity, error)) {
 669 		g_error_free (error);
 670 
 671 	} else if (error != NULL) {
 672 		e_alert_submit (
 673 			alert_sink, "mail:folder-unsubscribe",
 674 			context->folder_name, error->message, NULL);
 675 		g_error_free (error);
 676 
 677 	} else {
 678 		e_activity_set_state (
 679 			context->activity, E_ACTIVITY_COMPLETED);
 680 	}
 681 
 682 	async_context_free (context);
 683 }
 684 
 685 void
 686 e_mail_reader_unsubscribe_folder_name (EMailReader *reader,
 687                                        CamelStore *store,
 688                                        const gchar *folder_name)
 689 {
 690 	EActivity *activity;
 691 	AsyncContext *context;
 692 	GCancellable *cancellable;
 693 
 694 	g_return_if_fail (E_IS_MAIL_READER (reader));
 695 	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (store));
 696 	g_return_if_fail (folder_name != NULL);
 697 
 698 	activity = e_mail_reader_new_activity (reader);
 699 	cancellable = e_activity_get_cancellable (activity);
 700 
 701 	context = g_slice_new0 (AsyncContext);
 702 	context->activity = activity;
 703 	context->reader = g_object_ref (reader);
 704 	context->folder_name = g_strdup (folder_name);
 705 
 706 	camel_subscribable_unsubscribe_folder (
 707 		CAMEL_SUBSCRIBABLE (store), folder_name,
 708 		G_PRIORITY_DEFAULT, cancellable,
 709 		mail_reader_unsubscribe_folder_name_cb,
 710 		context);
 711 }
 712 
 713 guint
 714 e_mail_reader_mark_selected (EMailReader *reader,
 715                              guint32 mask,
 716                              guint32 set)
 717 {
 718 	CamelFolder *folder;
 719 	GPtrArray *uids;
 720 	guint ii;
 721 
 722 	g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
 723 
 724 	folder = e_mail_reader_get_folder (reader);
 725 
 726 	if (folder == NULL)
 727 		return 0;
 728 
 729 	camel_folder_freeze (folder);
 730 	uids = e_mail_reader_get_selected_uids (reader);
 731 
 732 	for (ii = 0; ii < uids->len; ii++)
 733 		camel_folder_set_message_flags (
 734 			folder, uids->pdata[ii], mask, set);
 735 
 736 	em_utils_uids_free (uids);
 737 
 738 	camel_folder_thaw (folder);
 739 
 740 	return ii;
 741 }
 742 static void
 743 copy_tree_state (EMailReader *src_reader,
 744                  EMailReader *des_reader)
 745 {
 746 	GtkWidget *src_mlist, *des_mlist;
 747 	gchar *state;
 748 
 749 	g_return_if_fail (src_reader != NULL);
 750 	g_return_if_fail (des_reader != NULL);
 751 
 752 	src_mlist = e_mail_reader_get_message_list (src_reader);
 753 	if (!src_mlist)
 754 		return;
 755 
 756 	des_mlist = e_mail_reader_get_message_list (des_reader);
 757 	if (!des_mlist)
 758 		return;
 759 
 760 	state = e_tree_get_state (E_TREE (src_mlist));
 761 	if (state)
 762 		e_tree_set_state (E_TREE (des_mlist), state);
 763 	g_free (state);
 764 
 765 	message_list_set_search (MESSAGE_LIST (des_mlist), MESSAGE_LIST (src_mlist)->search);
 766 }
 767 
 768 guint
 769 e_mail_reader_open_selected (EMailReader *reader)
 770 {
 771 	EShell *shell;
 772 	EMailBackend *backend;
 773 	ESourceRegistry *registry;
 774 	CamelFolder *folder;
 775 	GtkWindow *window;
 776 	GPtrArray *views;
 777 	GPtrArray *uids;
 778 	guint ii;
 779 
 780 	g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
 781 
 782 	backend = e_mail_reader_get_backend (reader);
 783 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 784 	registry = e_shell_get_registry (shell);
 785 
 786 	folder = e_mail_reader_get_folder (reader);
 787 	uids = e_mail_reader_get_selected_uids (reader);
 788 	window = e_mail_reader_get_window (reader);
 789 
 790 	if (!em_utils_ask_open_many (window, uids->len)) {
 791 		em_utils_uids_free (uids);
 792 		return 0;
 793 	}
 794 
 795 	if (em_utils_folder_is_drafts (registry, folder) ||
 796 		em_utils_folder_is_outbox (registry, folder) ||
 797 		em_utils_folder_is_templates (registry, folder)) {
 798 		em_utils_edit_messages (reader, folder, uids, TRUE);
 799 		return uids->len;
 800 	}
 801 
 802 	views = g_ptr_array_new ();
 803 
 804 	/* For vfolders we need to edit the original, not the vfolder copy. */
 805 	for (ii = 0; ii < uids->len; ii++) {
 806 		const gchar *uid = uids->pdata[ii];
 807 		CamelFolder *real_folder;
 808 		CamelMessageInfo *info;
 809 		gchar *real_uid;
 810 
 811 		if (!CAMEL_IS_VEE_FOLDER (folder)) {
 812 			g_ptr_array_add (views, g_strdup (uid));
 813 			continue;
 814 		}
 815 
 816 		info = camel_folder_get_message_info (folder, uid);
 817 		if (info == NULL)
 818 			continue;
 819 
 820 		real_folder = camel_vee_folder_get_location (
 821 			CAMEL_VEE_FOLDER (folder),
 822 			(CamelVeeMessageInfo *) info, &real_uid);
 823 
 824 		if (em_utils_folder_is_drafts (registry, real_folder) ||
 825 			em_utils_folder_is_outbox (registry, real_folder)) {
 826 			GPtrArray *edits;
 827 
 828 			edits = g_ptr_array_new ();
 829 			g_ptr_array_add (edits, real_uid);
 830 			em_utils_edit_messages (
 831 				reader, real_folder, edits, TRUE);
 832 		} else {
 833 			g_free (real_uid);
 834 			g_ptr_array_add (views, g_strdup (uid));
 835 		}
 836 
 837 		camel_folder_free_message_info (folder, info);
 838 	}
 839 
 840 	for (ii = 0; ii < views->len; ii++) {
 841 		const gchar *uid = views->pdata[ii];
 842 		GtkWidget *browser;
 843 		MessageList *ml;
 844 
 845 		browser = e_mail_browser_new (
 846 			backend, folder, uid,
 847 			E_MAIL_FORMATTER_MODE_NORMAL);
 848 
 849 		e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
 850 		e_mail_reader_set_message (E_MAIL_READER (browser), uid);
 851 
 852 		ml = MESSAGE_LIST (e_mail_reader_get_message_list (
 853 			E_MAIL_READER (browser)));
 854 		message_list_freeze (ml);
 855 
 856 		copy_tree_state (reader, E_MAIL_READER (browser));
 857 		e_mail_reader_set_group_by_threads (
 858 			E_MAIL_READER (browser),
 859 			e_mail_reader_get_group_by_threads (reader));
 860 
 861 		message_list_thaw (ml);
 862 		gtk_widget_show (browser);
 863 	}
 864 
 865 	g_ptr_array_foreach (views, (GFunc) g_free, NULL);
 866 	g_ptr_array_free (views, TRUE);
 867 
 868 	em_utils_uids_free (uids);
 869 
 870 	return ii;
 871 }
 872 
 873 static gboolean
 874 destroy_printing_activity (EActivity *activity)
 875 {
 876 	g_object_unref (activity);
 877 
 878 	return FALSE;
 879 }
 880 
 881 static void
 882 printing_done_cb (EMailPrinter *printer,
 883                   GtkPrintOperation *operation,
 884                   GtkPrintOperationResult result,
 885                   gpointer user_data)
 886 {
 887 	EActivity *activity = user_data;
 888 
 889 	if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
 890 
 891 		EAlertSink *alert_sink;
 892 		GError *error = NULL;
 893 
 894 		alert_sink = e_activity_get_alert_sink (activity);
 895 		gtk_print_operation_get_error (operation, &error);
 896 
 897 		if (error != NULL) {
 898 			e_alert_submit (
 899 				alert_sink, "mail:printing-failed",
 900 				error->message, NULL);
 901 			g_error_free (error);
 902 		}
 903 
 904 		g_object_unref (activity);
 905 		g_object_unref (printer);
 906 		return;
 907 	}
 908 
 909 	/* Set activity as completed, and keep it displayed for a few seconds
 910 	 * so that user can actually see the the printing was sucesfully finished. */
 911 	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
 912 
 913 	/* We can't destroy the printer and associated WebKitWebView directly from
 914 	 * here, because this callback is a handler of a WebKit's signal. This
 915 	 * will destroy the printer later, together with the activity */
 916 	g_object_set_data_full (
 917 		G_OBJECT (activity),
 918 		"printer", printer, (GDestroyNotify) g_object_unref);
 919 
 920 	g_timeout_add_seconds_full (
 921 		G_PRIORITY_DEFAULT, 3,
 922 		(GSourceFunc) destroy_printing_activity, activity, NULL);
 923 }
 924 
 925 struct _MessagePrintingContext {
 926 	EMailReader *reader;
 927 	CamelFolder *folder;
 928 	gchar *message_uid;
 929 	GtkPrintOperationAction action;
 930 
 931 	EActivity *activity;
 932 };
 933 
 934 static void
 935 free_message_printing_context (struct _MessagePrintingContext *context)
 936 {
 937 	g_return_if_fail (context != NULL);
 938 
 939 	g_clear_object (&context->reader);
 940 	g_clear_object (&context->folder);
 941 	g_clear_object (&context->activity);
 942 
 943 	if (context->message_uid)
 944 		g_free (context->message_uid);
 945 
 946 	g_free (context);
 947 }
 948 
 949 static void
 950 mail_reader_do_print_message (GObject *object,
 951                               GAsyncResult *result,
 952                               gpointer user_data)
 953 {
 954 	EMailReader *reader = E_MAIL_READER (object);
 955 	EMailDisplay *mail_display;
 956 	EActivity *activity;
 957 	GCancellable *cancellable;
 958 	EMailPrinter *printer;
 959 	EMailPartList *part_list;
 960 	struct _MessagePrintingContext *context = user_data;
 961 
 962 	activity = e_mail_reader_new_activity (context->reader);
 963 	e_activity_set_text (activity, _("Printing"));
 964 	e_activity_set_state (activity, E_ACTIVITY_RUNNING);
 965 	cancellable = e_activity_get_cancellable (activity);
 966 
 967 	part_list = e_mail_reader_parse_message_finish (reader, result);
 968 
 969 	printer = e_mail_printer_new (part_list);
 970 	g_signal_connect (
 971 		printer, "done",
 972 		G_CALLBACK (printing_done_cb), activity);
 973 
 974 	mail_display = e_mail_reader_get_mail_display (reader);
 975 
 976 	e_mail_printer_print (printer, context->action,
 977 		e_mail_display_get_formatter (mail_display),
 978 		cancellable);
 979 
 980 	free_message_printing_context (context);
 981 }
 982 
 983 static void
 984 mail_reader_get_message_to_print_ready_cb (GObject *object,
 985                                            GAsyncResult *result,
 986                                            gpointer user_data)
 987 {
 988 	CamelMimeMessage *message;
 989 	struct _MessagePrintingContext *context = user_data;
 990 
 991 	message = camel_folder_get_message_finish (CAMEL_FOLDER (object), result, NULL);
 992 	if (!CAMEL_IS_MIME_MESSAGE (message)) {
 993 		free_message_printing_context (context);
 994 		return;
 995 	}
 996 
 997 	/* "Retrieving message" activity (or NULL) */
 998 	g_clear_object (&context->activity);
 999 
1000 	e_mail_reader_parse_message (
1001 		context->reader, context->folder, context->message_uid,
1002 		message, NULL, mail_reader_do_print_message, context);
1003 }
1004 
1005 void
1006 e_mail_reader_print (EMailReader *reader,
1007                      GtkPrintOperationAction action)
1008 {
1009 	struct _MessagePrintingContext *context;
1010 	MessageList *message_list;
1011 
1012 	context = g_new0 (struct _MessagePrintingContext, 1);
1013 
1014 	message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
1015 	context->reader = g_object_ref (reader);
1016 	context->message_uid = g_strdup (message_list->cursor_uid);
1017 	context->folder = g_object_ref (e_mail_reader_get_folder (reader));
1018 	context->activity = e_mail_reader_new_activity (reader);
1019 	context->action = action;
1020 
1021 	g_return_if_fail (E_IS_MAIL_READER (reader));
1022 
1023 	camel_folder_get_message (
1024 		context->folder, context->message_uid,
1025 		G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
1026 		(GAsyncReadyCallback) mail_reader_get_message_to_print_ready_cb,
1027 		context);
1028 }
1029 
1030 static void
1031 mail_reader_remove_attachments_cb (CamelFolder *folder,
1032                                    GAsyncResult *result,
1033                                    AsyncContext *context)
1034 {
1035 	EAlertSink *alert_sink;
1036 	GError *error = NULL;
1037 
1038 	alert_sink = e_mail_reader_get_alert_sink (context->reader);
1039 
1040 	e_mail_folder_remove_attachments_finish (folder, result, &error);
1041 
1042 	if (e_activity_handle_cancellation (context->activity, error)) {
1043 		g_error_free (error);
1044 
1045 	} else if (error != NULL) {
1046 		e_alert_submit (
1047 			alert_sink,
1048 			"mail:remove-attachments",
1049 			error->message, NULL);
1050 		g_error_free (error);
1051 	}
1052 
1053 	async_context_free (context);
1054 }
1055 
1056 void
1057 e_mail_reader_remove_attachments (EMailReader *reader)
1058 {
1059 	EActivity *activity;
1060 	AsyncContext *context;
1061 	GCancellable *cancellable;
1062 	CamelFolder *folder;
1063 	GPtrArray *uids;
1064 
1065 	g_return_if_fail (E_IS_MAIL_READER (reader));
1066 
1067 	folder = e_mail_reader_get_folder (reader);
1068 	uids = e_mail_reader_get_selected_uids (reader);
1069 	g_return_if_fail (uids != NULL);
1070 
1071 	/* XXX Either e_mail_reader_get_selected_uids()
1072 	 *     or MessageList should do this itself. */
1073 	g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1074 
1075 	/* Remove attachments asynchronously. */
1076 
1077 	activity = e_mail_reader_new_activity (reader);
1078 	cancellable = e_activity_get_cancellable (activity);
1079 
1080 	context = g_slice_new0 (AsyncContext);
1081 	context->activity = activity;
1082 	context->reader = g_object_ref (reader);
1083 
1084 	e_mail_folder_remove_attachments (
1085 		folder, uids, G_PRIORITY_DEFAULT,
1086 		cancellable, (GAsyncReadyCallback)
1087 		mail_reader_remove_attachments_cb,
1088 		context);
1089 
1090 	g_ptr_array_unref (uids);
1091 }
1092 
1093 static void
1094 mail_reader_remove_duplicates_cb (CamelFolder *folder,
1095                                   GAsyncResult *result,
1096                                   AsyncContext *context)
1097 {
1098 	EAlertSink *alert_sink;
1099 	GHashTable *duplicates;
1100 	GtkWindow *parent_window;
1101 	guint n_duplicates;
1102 	GError *error = NULL;
1103 
1104 	alert_sink = e_mail_reader_get_alert_sink (context->reader);
1105 	parent_window = e_mail_reader_get_window (context->reader);
1106 
1107 	duplicates = e_mail_folder_find_duplicate_messages_finish (
1108 		folder, result, &error);
1109 
1110 	if (e_activity_handle_cancellation (context->activity, error)) {
1111 		g_warn_if_fail (duplicates == NULL);
1112 		async_context_free (context);
1113 		g_error_free (error);
1114 		return;
1115 
1116 	} else if (error != NULL) {
1117 		g_warn_if_fail (duplicates == NULL);
1118 		e_alert_submit (
1119 			alert_sink,
1120 			"mail:find-duplicate-messages",
1121 			error->message, NULL);
1122 		async_context_free (context);
1123 		g_error_free (error);
1124 		return;
1125 	}
1126 
1127 	g_return_if_fail (duplicates != NULL);
1128 
1129 	/* Finalize the activity here so we don't leave a message in
1130 	 * the task bar while prompting the user for confirmation. */
1131 	e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1132 	g_object_unref (context->activity);
1133 	context->activity = NULL;
1134 
1135 	n_duplicates = g_hash_table_size (duplicates);
1136 
1137 	if (n_duplicates == 0) {
1138 		em_utils_prompt_user (
1139 			parent_window, NULL,
1140 			"mail:info-no-remove-duplicates",
1141 			camel_folder_get_display_name (folder), NULL);
1142 	} else {
1143 		gchar *confirmation;
1144 		gboolean proceed;
1145 
1146 		confirmation = g_strdup_printf (ngettext (
1147 			/* Translators: %s is replaced with a folder
1148 			 * name %u with count of duplicate messages. */
1149 			"Folder '%s' contains %u duplicate message. "
1150 			"Are you sure you want to delete it?",
1151 			"Folder '%s' contains %u duplicate messages. "
1152 			"Are you sure you want to delete them?",
1153 			n_duplicates),
1154 			camel_folder_get_display_name (folder),
1155 			n_duplicates);
1156 
1157 		proceed = em_utils_prompt_user (
1158 			parent_window, NULL,
1159 			"mail:ask-remove-duplicates",
1160 			confirmation, NULL);
1161 
1162 		if (proceed) {
1163 			GHashTableIter iter;
1164 			gpointer key;
1165 
1166 			camel_folder_freeze (folder);
1167 
1168 			g_hash_table_iter_init (&iter, duplicates);
1169 
1170 			/* Mark duplicate messages for deletion. */
1171 			while (g_hash_table_iter_next (&iter, &key, NULL))
1172 				camel_folder_delete_message (folder, key);
1173 
1174 			camel_folder_thaw (folder);
1175 		}
1176 
1177 		g_free (confirmation);
1178 	}
1179 
1180 	g_hash_table_destroy (duplicates);
1181 
1182 	async_context_free (context);
1183 }
1184 
1185 void
1186 e_mail_reader_remove_duplicates (EMailReader *reader)
1187 {
1188 	EActivity *activity;
1189 	AsyncContext *context;
1190 	GCancellable *cancellable;
1191 	CamelFolder *folder;
1192 	GPtrArray *uids;
1193 
1194 	g_return_if_fail (E_IS_MAIL_READER (reader));
1195 
1196 	folder = e_mail_reader_get_folder (reader);
1197 	uids = e_mail_reader_get_selected_uids (reader);
1198 	g_return_if_fail (uids != NULL);
1199 
1200 	/* XXX Either e_mail_reader_get_selected_uids()
1201 	 *     or MessageList should do this itself. */
1202 	g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1203 
1204 	/* Find duplicate messages asynchronously. */
1205 
1206 	activity = e_mail_reader_new_activity (reader);
1207 	cancellable = e_activity_get_cancellable (activity);
1208 
1209 	context = g_slice_new0 (AsyncContext);
1210 	context->activity = activity;
1211 	context->reader = g_object_ref (reader);
1212 
1213 	e_mail_folder_find_duplicate_messages (
1214 		folder, uids, G_PRIORITY_DEFAULT,
1215 		cancellable, (GAsyncReadyCallback)
1216 		mail_reader_remove_duplicates_cb,
1217 		context);
1218 
1219 	g_ptr_array_unref (uids);
1220 }
1221 
1222 /* Helper for e_mail_reader_reply_to_message()
1223  * XXX This function belongs in e-html-utils.c */
1224 static gboolean
1225 html_contains_nonwhitespace (const gchar *html,
1226                              gint len)
1227 {
1228 	const gchar *cp;
1229 	gunichar uc = 0;
1230 
1231 	if (html == NULL || len <= 0)
1232 		return FALSE;
1233 
1234 	cp = html;
1235 
1236 	while (cp != NULL && cp - html < len) {
1237 		uc = g_utf8_get_char (cp);
1238 		if (uc == 0)
1239 			break;
1240 
1241 		if (uc == '<') {
1242 			/* skip until next '>' */
1243 			uc = g_utf8_get_char (cp);
1244 			while (uc != 0 && uc != '>' && cp - html < len) {
1245 				cp = g_utf8_next_char (cp);
1246 				uc = g_utf8_get_char (cp);
1247 			}
1248 			if (uc == 0)
1249 				break;
1250 		} else if (uc == '&') {
1251 			/* sequence '&nbsp;' is a space */
1252 			if (g_ascii_strncasecmp (cp, "&nbsp;", 6) == 0)
1253 				cp = cp + 5;
1254 			else
1255 				break;
1256 		} else if (!g_unichar_isspace (uc))
1257 			break;
1258 
1259 		cp = g_utf8_next_char (cp);
1260 	}
1261 
1262 	return cp - html < len - 1 && uc != 0;
1263 }
1264 
1265 static void
1266 mail_reader_reply_message_parsed (GObject *object,
1267                                   GAsyncResult *result,
1268                                   gpointer user_data)
1269 {
1270 	EShell *shell;
1271 	EMailBackend *backend;
1272 	EMailReader *reader = E_MAIL_READER (object);
1273 	EMailPartList *part_list;
1274 	AsyncContext *context = user_data;
1275 
1276 	part_list = e_mail_reader_parse_message_finish (reader, result);
1277 
1278 	backend = e_mail_reader_get_backend (context->reader);
1279 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1280 
1281 	em_utils_reply_to_message (
1282 		shell, part_list->message,
1283 		context->folder, context->message_uid,
1284 		context->reply_type, context->reply_style,
1285 		part_list, context->address);
1286 
1287 	g_object_unref (part_list);
1288 	async_context_free (context);
1289 }
1290 
1291 static void
1292 mail_reader_get_message_ready_cb (CamelFolder *folder,
1293                                   GAsyncResult *result,
1294                                   AsyncContext *context)
1295 {
1296 	EAlertSink *alert_sink;
1297 	CamelMimeMessage *message;
1298 	GError *error = NULL;
1299 
1300 	alert_sink = e_mail_reader_get_alert_sink (context->reader);
1301 
1302 	message = camel_folder_get_message_finish (folder, result, &error);
1303 
1304 	if (e_activity_handle_cancellation (context->activity, error)) {
1305 		g_warn_if_fail (message == NULL);
1306 		async_context_free (context);
1307 		g_error_free (error);
1308 		return;
1309 
1310 	} else if (error != NULL) {
1311 		g_warn_if_fail (message == NULL);
1312 		e_alert_submit (
1313 			alert_sink, "mail:no-retrieve-message",
1314 			error->message, NULL);
1315 		async_context_free (context);
1316 		g_error_free (error);
1317 		return;
1318 	}
1319 
1320 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1321 
1322 	e_mail_reader_parse_message (
1323 		context->reader, context->folder,
1324 		context->message_uid, message, NULL,
1325 		mail_reader_reply_message_parsed, context);
1326 }
1327 
1328 void
1329 e_mail_reader_reply_to_message (EMailReader *reader,
1330                                 CamelMimeMessage *src_message,
1331                                 EMailReplyType reply_type)
1332 {
1333 	EShell *shell;
1334 	EMailBackend *backend;
1335 	EShellBackend *shell_backend;
1336 	EMailDisplay *display;
1337 	EMailPartList *part_list = NULL;
1338 	GtkWidget *message_list;
1339 	CamelMimeMessage *new_message;
1340 	CamelInternetAddress *address = NULL;
1341 	CamelFolder *folder;
1342 	EMailReplyStyle reply_style;
1343 	EWebView *web_view;
1344 	struct _camel_header_raw *header;
1345 	const gchar *uid;
1346 	gchar *selection = NULL;
1347 	gint length;
1348 	gchar *mail_uri;
1349 	CamelObjectBag *registry;
1350 
1351 	/* This handles quoting only selected text in the reply.  If
1352 	 * nothing is selected or only whitespace is selected, fall
1353 	 * back to the normal em_utils_reply_to_message(). */
1354 
1355 	g_return_if_fail (E_IS_MAIL_READER (reader));
1356 
1357 	backend = e_mail_reader_get_backend (reader);
1358 	folder = e_mail_reader_get_folder (reader);
1359 	display = e_mail_reader_get_mail_display (reader);
1360 	message_list = e_mail_reader_get_message_list (reader);
1361 	reply_style = e_mail_reader_get_reply_style (reader);
1362 
1363 	shell_backend = E_SHELL_BACKEND (backend);
1364 	shell = e_shell_backend_get_shell (shell_backend);
1365 
1366 	web_view = E_WEB_VIEW (display);
1367 
1368 	if (reply_type == E_MAIL_REPLY_TO_RECIPIENT) {
1369 		const gchar *uri;
1370 
1371 		uri = e_web_view_get_selected_uri (web_view);
1372 
1373 		if (uri) {
1374 			CamelURL *curl;
1375 
1376 			curl = camel_url_new (uri, NULL);
1377 
1378 			if (curl && curl->path && *curl->path) {
1379 				address = camel_internet_address_new ();
1380 				if (camel_address_decode (
1381 						CAMEL_ADDRESS (address),
1382 						curl->path) < 0) {
1383 					g_object_unref (address);
1384 					address = NULL;
1385 				}
1386 			}
1387 
1388 			if (curl)
1389 				camel_url_free (curl);
1390 		}
1391 	}
1392 
1393 	uid = MESSAGE_LIST (message_list)->cursor_uid;
1394 	g_return_if_fail (uid != NULL);
1395 
1396 	if (!gtk_widget_get_visible (GTK_WIDGET (web_view)))
1397 		goto whole_message;
1398 
1399 	registry = e_mail_part_list_get_registry ();
1400 	mail_uri = e_mail_part_build_uri (folder, uid, NULL, NULL);
1401 	part_list = camel_object_bag_get (registry, mail_uri);
1402 	g_free (mail_uri);
1403 
1404 	if (!part_list)
1405 		goto whole_message;
1406 
1407 	if (src_message == NULL) {
1408 		src_message = part_list->message;
1409 		if (src_message != NULL)
1410 			g_object_ref (src_message);
1411 
1412 		g_object_unref (part_list);
1413 		part_list = NULL;
1414 
1415 		g_return_if_fail (src_message != NULL);
1416 	} else {
1417 		g_object_unref (part_list);
1418 		part_list = NULL;
1419 	}
1420 
1421 	if (!e_web_view_is_selection_active (web_view))
1422 		goto whole_message;
1423 
1424 	selection = e_web_view_get_selection_html (web_view);
1425 	if (selection == NULL || *selection == '\0')
1426 		goto whole_message;
1427 
1428 	length = strlen (selection);
1429 	if (!html_contains_nonwhitespace (selection, length))
1430 		goto whole_message;
1431 
1432 	new_message = camel_mime_message_new ();
1433 
1434 	/* Filter out "content-*" headers. */
1435 	header = CAMEL_MIME_PART (src_message)->headers;
1436 	while (header != NULL) {
1437 		if (g_ascii_strncasecmp (header->name, "content-", 8) != 0)
1438 			camel_medium_add_header (
1439 				CAMEL_MEDIUM (new_message),
1440 				header->name, header->value);
1441 
1442 		header = header->next;
1443 	}
1444 
1445 	camel_mime_part_set_encoding (
1446 		CAMEL_MIME_PART (new_message),
1447 		CAMEL_TRANSFER_ENCODING_8BIT);
1448 
1449 	camel_mime_part_set_content (
1450 		CAMEL_MIME_PART (new_message),
1451 		selection, length, "text/html");
1452 
1453 	g_object_unref (src_message);
1454 
1455 	em_utils_reply_to_message (
1456 		shell, new_message, folder, uid,
1457 		reply_type, reply_style, NULL, address);
1458 
1459 	if (address)
1460 		g_object_unref (address);
1461 
1462 	g_object_unref (new_message);
1463 
1464 	g_free (selection);
1465 
1466 	return;
1467 
1468 whole_message:
1469 	if (src_message == NULL) {
1470 		EActivity *activity;
1471 		AsyncContext *context;
1472 		GCancellable *cancellable;
1473 
1474 		activity = e_mail_reader_new_activity (reader);
1475 		cancellable = e_activity_get_cancellable (activity);
1476 
1477 		context = g_slice_new0 (AsyncContext);
1478 		context->activity = activity;
1479 		context->folder = g_object_ref (folder);
1480 		context->reader = g_object_ref (reader);
1481 		context->address = address; /* takes ownership of it, if set */
1482 		context->message_uid = g_strdup (uid);
1483 		context->reply_type = reply_type;
1484 		context->reply_style = reply_style;
1485 
1486 		camel_folder_get_message (
1487 			context->folder, context->message_uid,
1488 			G_PRIORITY_DEFAULT, cancellable,
1489 			(GAsyncReadyCallback) mail_reader_get_message_ready_cb,
1490 			context);
1491 
1492 		return;
1493 	}
1494 
1495 	em_utils_reply_to_message (
1496 		shell, src_message, folder, uid,
1497 		reply_type, reply_style, part_list, address);
1498 
1499 	if (address)
1500 		g_object_unref (address);
1501 }
1502 
1503 static void
1504 mail_reader_save_messages_cb (CamelFolder *folder,
1505                               GAsyncResult *result,
1506                               AsyncContext *context)
1507 {
1508 	EAlertSink *alert_sink;
1509 	GError *error = NULL;
1510 
1511 	alert_sink = e_mail_reader_get_alert_sink (context->reader);
1512 
1513 	e_mail_folder_save_messages_finish (folder, result, &error);
1514 
1515 	if (e_activity_handle_cancellation (context->activity, error)) {
1516 		g_error_free (error);
1517 
1518 	} else if (error != NULL) {
1519 		e_alert_submit (
1520 			alert_sink,
1521 			"mail:save-messages",
1522 			error->message, NULL);
1523 		g_error_free (error);
1524 	}
1525 
1526 	async_context_free (context);
1527 }
1528 
1529 void
1530 e_mail_reader_save_messages (EMailReader *reader)
1531 {
1532 	EShell *shell;
1533 	EActivity *activity;
1534 	AsyncContext *context;
1535 	EMailBackend *backend;
1536 	GCancellable *cancellable;
1537 	EShellBackend *shell_backend;
1538 	CamelMessageInfo *info;
1539 	CamelFolder *folder;
1540 	GFile *destination;
1541 	GPtrArray *uids;
1542 	const gchar *message_uid;
1543 	const gchar *title;
1544 	gchar *suggestion = NULL;
1545 
1546 	folder = e_mail_reader_get_folder (reader);
1547 	backend = e_mail_reader_get_backend (reader);
1548 
1549 	uids = e_mail_reader_get_selected_uids (reader);
1550 	g_return_if_fail (uids != NULL && uids->len > 0);
1551 	message_uid = g_ptr_array_index (uids, 0);
1552 
1553 	/* XXX Either e_mail_reader_get_selected_uids()
1554 	 *     or MessageList should do this itself. */
1555 	g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free);
1556 
1557 	title = ngettext ("Save Message", "Save Messages", uids->len);
1558 
1559 	/* Suggest as a filename the subject of the first message. */
1560 	info = camel_folder_get_message_info (folder, message_uid);
1561 	if (info != NULL) {
1562 		const gchar *subject;
1563 
1564 		subject = camel_message_info_subject (info);
1565 		if (subject != NULL)
1566 			suggestion = g_strconcat (subject, ".mbox", NULL);
1567 		camel_folder_free_message_info (folder, info);
1568 	}
1569 
1570 	if (suggestion == NULL) {
1571 		const gchar *basename;
1572 
1573 		/* Translators: This is part of a suggested file name
1574 		 * used when saving a message or multiple messages to
1575 		 * mbox format, when the first message doesn't have a
1576 		 * subject.  The extension ".mbox" is appended to the
1577 		 * string; for example "Message.mbox". */
1578 		basename = ngettext ("Message", "Messages", uids->len);
1579 		suggestion = g_strconcat (basename, ".mbox", NULL);
1580 	}
1581 
1582 	shell_backend = E_SHELL_BACKEND (backend);
1583 	shell = e_shell_backend_get_shell (shell_backend);
1584 
1585 	destination = e_shell_run_save_dialog (
1586 		shell, title, suggestion,
1587 		"*.mbox:application/mbox,message/rfc822", NULL, NULL);
1588 
1589 	if (destination == NULL) {
1590 		g_ptr_array_unref (uids);
1591 		return;
1592 	}
1593 
1594 	/* Save messages asynchronously. */
1595 
1596 	activity = e_mail_reader_new_activity (reader);
1597 	cancellable = e_activity_get_cancellable (activity);
1598 
1599 	context = g_slice_new0 (AsyncContext);
1600 	context->activity = activity;
1601 	context->reader = g_object_ref (reader);
1602 
1603 	e_mail_folder_save_messages (
1604 		folder, uids,
1605 		destination, G_PRIORITY_DEFAULT,
1606 		cancellable, (GAsyncReadyCallback)
1607 		mail_reader_save_messages_cb, context);
1608 
1609 	g_object_unref (destination);
1610 	g_ptr_array_unref (uids);
1611 }
1612 
1613 void
1614 e_mail_reader_select_next_message (EMailReader *reader,
1615                                    gboolean or_else_previous)
1616 {
1617 	GtkWidget *message_list;
1618 	gboolean hide_deleted;
1619 	gboolean success;
1620 
1621 	g_return_if_fail (E_IS_MAIL_READER (reader));
1622 
1623 	hide_deleted = e_mail_reader_get_hide_deleted (reader);
1624 	message_list = e_mail_reader_get_message_list (reader);
1625 
1626 	success = message_list_select (
1627 		MESSAGE_LIST (message_list),
1628 		MESSAGE_LIST_SELECT_NEXT, 0, 0);
1629 
1630 	if (!success && (hide_deleted || or_else_previous))
1631 		message_list_select (
1632 			MESSAGE_LIST (message_list),
1633 			MESSAGE_LIST_SELECT_PREVIOUS, 0, 0);
1634 }
1635 
1636 /* Helper for e_mail_reader_create_filter_from_selected() */
1637 static void
1638 mail_reader_create_filter_cb (CamelFolder *folder,
1639                               GAsyncResult *result,
1640                               AsyncContext *context)
1641 {
1642 	EMailBackend *backend;
1643 	EMailSession *session;
1644 	EAlertSink *alert_sink;
1645 	CamelMimeMessage *message;
1646 	GError *error = NULL;
1647 
1648 	alert_sink = e_activity_get_alert_sink (context->activity);
1649 
1650 	message = camel_folder_get_message_finish (folder, result, &error);
1651 
1652 	if (e_activity_handle_cancellation (context->activity, error)) {
1653 		g_warn_if_fail (message == NULL);
1654 		async_context_free (context);
1655 		g_error_free (error);
1656 		return;
1657 
1658 	} else if (error != NULL) {
1659 		g_warn_if_fail (message == NULL);
1660 		e_alert_submit (
1661 			alert_sink, "mail:no-retrieve-message",
1662 			error->message, NULL);
1663 		async_context_free (context);
1664 		g_error_free (error);
1665 		return;
1666 	}
1667 
1668 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1669 
1670 	/* Finalize the activity here so we don't leave a message
1671 	 * in the task bar while displaying the filter editor. */
1672 	e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1673 	g_object_unref (context->activity);
1674 	context->activity = NULL;
1675 
1676 	backend = e_mail_reader_get_backend (context->reader);
1677 	session = e_mail_backend_get_session (backend);
1678 
1679 	filter_gui_add_from_message (
1680 		session, message,
1681 		context->filter_source,
1682 		context->filter_type);
1683 
1684 	g_object_unref (message);
1685 
1686 	async_context_free (context);
1687 }
1688 
1689 void
1690 e_mail_reader_create_filter_from_selected (EMailReader *reader,
1691                                            gint filter_type)
1692 {
1693 	EShell *shell;
1694 	EActivity *activity;
1695 	EMailBackend *backend;
1696 	AsyncContext *context;
1697 	GCancellable *cancellable;
1698 	ESourceRegistry *registry;
1699 	CamelFolder *folder;
1700 	GPtrArray *uids;
1701 	const gchar *filter_source;
1702 	const gchar *message_uid;
1703 
1704 	g_return_if_fail (E_IS_MAIL_READER (reader));
1705 
1706 	backend = e_mail_reader_get_backend (reader);
1707 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1708 	registry = e_shell_get_registry (shell);
1709 
1710 	folder = e_mail_reader_get_folder (reader);
1711 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
1712 
1713 	if (em_utils_folder_is_sent (registry, folder))
1714 		filter_source = E_FILTER_SOURCE_OUTGOING;
1715 	else if (em_utils_folder_is_outbox (registry, folder))
1716 		filter_source = E_FILTER_SOURCE_OUTGOING;
1717 	else
1718 		filter_source = E_FILTER_SOURCE_INCOMING;
1719 
1720 	uids = e_mail_reader_get_selected_uids (reader);
1721 	g_return_if_fail (uids != NULL && uids->len == 1);
1722 	message_uid = g_ptr_array_index (uids, 0);
1723 
1724 	activity = e_mail_reader_new_activity (reader);
1725 	cancellable = e_activity_get_cancellable (activity);
1726 
1727 	context = g_slice_new0 (AsyncContext);
1728 	context->activity = activity;
1729 	context->reader = g_object_ref (reader);
1730 	context->filter_source = filter_source;
1731 	context->filter_type = filter_type;
1732 
1733 	camel_folder_get_message (
1734 		folder, message_uid, G_PRIORITY_DEFAULT,
1735 		cancellable, (GAsyncReadyCallback)
1736 		mail_reader_create_filter_cb, context);
1737 
1738 	em_utils_uids_free (uids);
1739 }
1740 
1741 /* Helper for e_mail_reader_create_vfolder_from_selected() */
1742 static void
1743 mail_reader_create_vfolder_cb (CamelFolder *folder,
1744                                GAsyncResult *result,
1745                                AsyncContext *context)
1746 {
1747 	EMailBackend *backend;
1748 	EMailSession *session;
1749 	EAlertSink *alert_sink;
1750 	CamelMimeMessage *message;
1751 	CamelFolder *use_folder;
1752 	GError *error = NULL;
1753 
1754 	alert_sink = e_activity_get_alert_sink (context->activity);
1755 
1756 	message = camel_folder_get_message_finish (folder, result, &error);
1757 
1758 	if (e_activity_handle_cancellation (context->activity, error)) {
1759 		g_warn_if_fail (message == NULL);
1760 		async_context_free (context);
1761 		g_error_free (error);
1762 		return;
1763 
1764 	} else if (error != NULL) {
1765 		g_warn_if_fail (message == NULL);
1766 		e_alert_submit (
1767 			alert_sink, "mail:no-retrieve-message",
1768 			error->message, NULL);
1769 		async_context_free (context);
1770 		g_error_free (error);
1771 		return;
1772 	}
1773 
1774 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1775 
1776 	/* Finalize the activity here so we don't leave a message
1777 	 * in the task bar while displaying the vfolder editor. */
1778 	e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
1779 	g_object_unref (context->activity);
1780 	context->activity = NULL;
1781 
1782 	backend = e_mail_reader_get_backend (context->reader);
1783 	session = e_mail_backend_get_session (backend);
1784 
1785 	use_folder = context->folder;
1786 	if (CAMEL_IS_VEE_FOLDER (use_folder)) {
1787 		CamelStore *parent_store;
1788 		CamelVeeFolder *vfolder;
1789 
1790 		parent_store = camel_folder_get_parent_store (use_folder);
1791 		vfolder = CAMEL_VEE_FOLDER (use_folder);
1792 
1793 		if (CAMEL_IS_VEE_STORE (parent_store) &&
1794 		    vfolder == camel_vee_store_get_unmatched_folder (CAMEL_VEE_STORE (parent_store))) {
1795 			/* use source folder instead of the Unmatched folder */
1796 			use_folder = camel_vee_folder_get_vee_uid_folder (
1797 				vfolder, context->message_uid);
1798 		}
1799 	}
1800 
1801 	vfolder_gui_add_from_message (
1802 		session, message,
1803 		context->filter_type,
1804 		use_folder);
1805 
1806 	g_object_unref (message);
1807 
1808 	async_context_free (context);
1809 }
1810 
1811 void
1812 e_mail_reader_create_vfolder_from_selected (EMailReader *reader,
1813                                             gint vfolder_type)
1814 {
1815 	EActivity *activity;
1816 	AsyncContext *context;
1817 	GCancellable *cancellable;
1818 	CamelFolder *folder;
1819 	GPtrArray *uids;
1820 	const gchar *message_uid;
1821 
1822 	g_return_if_fail (E_IS_MAIL_READER (reader));
1823 
1824 	folder = e_mail_reader_get_folder (reader);
1825 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
1826 
1827 	uids = e_mail_reader_get_selected_uids (reader);
1828 	g_return_if_fail (uids != NULL && uids->len == 1);
1829 	message_uid = g_ptr_array_index (uids, 0);
1830 
1831 	activity = e_mail_reader_new_activity (reader);
1832 	cancellable = e_activity_get_cancellable (activity);
1833 
1834 	context = g_slice_new0 (AsyncContext);
1835 	context->activity = activity;
1836 	context->folder = g_object_ref (folder);
1837 	context->reader = g_object_ref (reader);
1838 	context->message_uid = g_strdup (message_uid);
1839 	context->filter_type = vfolder_type;
1840 
1841 	camel_folder_get_message (
1842 		folder, message_uid, G_PRIORITY_DEFAULT,
1843 		cancellable, (GAsyncReadyCallback)
1844 		mail_reader_create_vfolder_cb, context);
1845 
1846 	em_utils_uids_free (uids);
1847 }
1848 
1849 static EMailReaderHeader *
1850 emr_header_from_xmldoc (xmlDocPtr doc)
1851 {
1852 	EMailReaderHeader *h;
1853 	xmlNodePtr root;
1854 	xmlChar *name;
1855 
1856 	if (doc == NULL)
1857 		return NULL;
1858 
1859 	root = doc->children;
1860 	if (strcmp ((gchar *) root->name, "header") != 0)
1861 		return NULL;
1862 
1863 	name = xmlGetProp (root, (const guchar *)"name");
1864 	if (name == NULL)
1865 		return NULL;
1866 
1867 	h = g_malloc0 (sizeof (EMailReaderHeader));
1868 	h->name = g_strdup ((gchar *) name);
1869 	xmlFree (name);
1870 
1871 	if (xmlHasProp (root, (const guchar *)"enabled"))
1872 		h->enabled = 1;
1873 	else
1874 		h->enabled = 0;
1875 
1876 	return h;
1877 }
1878 
1879 /**
1880  * e_mail_reader_header_from_xml
1881  * @xml: XML configuration data
1882  *
1883  * Parses passed XML data, which should be of
1884  * the format <header name="foo" enabled />, and
1885  * returns a EMailReaderHeader structure, or NULL if there
1886  * is an error.
1887  **/
1888 EMailReaderHeader *
1889 e_mail_reader_header_from_xml (const gchar *xml)
1890 {
1891 	EMailReaderHeader *header;
1892 	xmlDocPtr doc;
1893 
1894 	if (!(doc = xmlParseDoc ((guchar *) xml)))
1895 		return NULL;
1896 
1897 	header = emr_header_from_xmldoc (doc);
1898 	xmlFreeDoc (doc);
1899 
1900 	return header;
1901 }
1902 
1903 /**
1904  * e_mail_reader_header_to_xml
1905  * @header: header from which to generate XML
1906  *
1907  * Returns the passed header as a XML structure,
1908  * or NULL on error
1909  */
1910 gchar *
1911 e_mail_reader_header_to_xml (EMailReaderHeader *header)
1912 {
1913 	xmlDocPtr doc;
1914 	xmlNodePtr root;
1915 	xmlChar *xml;
1916 	gchar *out;
1917 	gint size;
1918 
1919 	g_return_val_if_fail (header != NULL, NULL);
1920 	g_return_val_if_fail (header->name != NULL, NULL);
1921 
1922 	doc = xmlNewDoc ((const guchar *)"1.0");
1923 
1924 	root = xmlNewDocNode (doc, NULL, (const guchar *)"header", NULL);
1925 	xmlSetProp (root, (const guchar *)"name", (guchar *) header->name);
1926 	if (header->enabled)
1927 		xmlSetProp (root, (const guchar *)"enabled", NULL);
1928 
1929 	xmlDocSetRootElement (doc, root);
1930 	xmlDocDumpMemory (doc, &xml, &size);
1931 	xmlFreeDoc (doc);
1932 
1933 	out = g_malloc (size + 1);
1934 	memcpy (out, xml, size);
1935 	out[size] = '\0';
1936 	xmlFree (xml);
1937 
1938 	return out;
1939 }
1940 
1941 /**
1942  * e_mail_reader_header_free
1943  * @header: header to free
1944  *
1945  * Frees the memory associated with the passed header
1946  * structure.
1947  */
1948 void
1949 e_mail_reader_header_free (EMailReaderHeader *header)
1950 {
1951 	if (header == NULL)
1952 		return;
1953 
1954 	g_free (header->name);
1955 	g_free (header);
1956 }
1957 
1958 static void
1959 mail_reader_parse_message_run (GSimpleAsyncResult *simple,
1960                                GObject *object,
1961                                GCancellable *cancellable)
1962 {
1963 	EMailReader *reader = E_MAIL_READER (object);
1964 	CamelObjectBag *registry;
1965 	EMailPartList *part_list;
1966 	AsyncContext *async_context;
1967 	gchar *mail_uri;
1968 
1969 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1970 
1971 	registry = e_mail_part_list_get_registry ();
1972 
1973 	mail_uri = e_mail_part_build_uri (
1974 		async_context->folder,
1975 		async_context->message_uid, NULL, NULL);
1976 
1977 	part_list = camel_object_bag_reserve (registry, mail_uri);
1978 	if (part_list == NULL) {
1979 		EMailBackend *mail_backend;
1980 		EMailSession *mail_session;
1981 		EMailParser *parser;
1982 
1983 		mail_backend = e_mail_reader_get_backend (reader);
1984 		mail_session = e_mail_backend_get_session (mail_backend);
1985 
1986 		parser = e_mail_parser_new (CAMEL_SESSION (mail_session));
1987 
1988 		part_list = e_mail_parser_parse_sync (
1989 			parser,
1990 			async_context->folder,
1991 			async_context->message_uid,
1992 			async_context->message,
1993 			cancellable);
1994 
1995 		g_object_unref (parser);
1996 
1997 		if (part_list == NULL)
1998 			camel_object_bag_abort (registry, mail_uri);
1999 		else
2000 			camel_object_bag_add (registry, mail_uri, part_list);
2001 	}
2002 
2003 	g_free (mail_uri);
2004 
2005 	async_context->part_list = part_list;
2006 }
2007 
2008 void
2009 e_mail_reader_parse_message (EMailReader *reader,
2010                              CamelFolder *folder,
2011                              const gchar *message_uid,
2012                              CamelMimeMessage *message,
2013                              GCancellable *cancellable,
2014                              GAsyncReadyCallback callback,
2015                              gpointer user_data)
2016 {
2017 	GSimpleAsyncResult *simple;
2018 	AsyncContext *async_context;
2019 	EActivity *activity;
2020 
2021 	g_return_if_fail (E_IS_MAIL_READER (reader));
2022 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
2023 	g_return_if_fail (message_uid != NULL);
2024 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
2025 
2026 	activity = e_mail_reader_new_activity (reader);
2027 	e_activity_set_cancellable (activity, cancellable);
2028 	e_activity_set_text (activity, _("Parsing message"));
2029 
2030 	async_context = g_slice_new0 (AsyncContext);
2031 	async_context->activity = activity;  /* takes ownership */
2032 	async_context->folder = g_object_ref (folder);
2033 	async_context->message_uid = g_strdup (message_uid);
2034 	async_context->message = g_object_ref (message);
2035 
2036 	simple = g_simple_async_result_new (
2037 		G_OBJECT (reader), callback, user_data,
2038 		e_mail_reader_parse_message);
2039 
2040 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2041 
2042 	g_simple_async_result_set_op_res_gpointer (
2043 		simple, async_context, (GDestroyNotify) async_context_free);
2044 
2045 	g_simple_async_result_run_in_thread (
2046 		simple, mail_reader_parse_message_run,
2047 		G_PRIORITY_DEFAULT, cancellable);
2048 
2049 	g_object_unref (simple);
2050 }
2051 
2052 EMailPartList *
2053 e_mail_reader_parse_message_finish (EMailReader *reader,
2054                                     GAsyncResult *result)
2055 {
2056 	GSimpleAsyncResult *simple;
2057 	AsyncContext *async_context;
2058 
2059 	g_return_val_if_fail (
2060 		g_simple_async_result_is_valid (
2061 		result, G_OBJECT (reader),
2062 		e_mail_reader_parse_message), NULL);
2063 
2064 	simple = G_SIMPLE_ASYNC_RESULT (result);
2065 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2066 
2067 	if (async_context->part_list != NULL)
2068 		g_object_ref (async_context->part_list);
2069 
2070 	return async_context->part_list;
2071 }