evolution-3.6.4/widgets/misc/e-attachment.c

Location Tool Test ID Function Issue
e-attachment.c:2045:38 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
e-attachment.c:2045:38 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
e-attachment.c:2344:38 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
e-attachment.c:2344:38 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
   1 /*
   2  * e-attachment.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 #ifdef HAVE_CONFIG_H
  23 #include <config.h>
  24 #endif
  25 
  26 #include "e-attachment.h"
  27 
  28 #include <errno.h>
  29 #include <glib/gi18n.h>
  30 #include <glib/gstdio.h>
  31 
  32 #include <libedataserver/libedataserver.h>
  33 
  34 #include "e-util/e-icon-factory.h"
  35 #include "e-util/e-util.h"
  36 #include "e-util/e-mktemp.h"
  37 #include "e-attachment-store.h"
  38 
  39 #define E_ATTACHMENT_GET_PRIVATE(obj) \
  40 	(G_TYPE_INSTANCE_GET_PRIVATE \
  41 	((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
  42 
  43 /* Fallback Icon */
  44 #define DEFAULT_ICON_NAME	"mail-attachment"
  45 
  46 /* Emblems */
  47 #define EMBLEM_CANCELLED	"gtk-cancel"
  48 #define EMBLEM_LOADING		"emblem-downloads"
  49 #define EMBLEM_SAVING		"document-save"
  50 #define EMBLEM_ENCRYPT_WEAK	"security-low"
  51 #define EMBLEM_ENCRYPT_STRONG	"security-high"
  52 #define EMBLEM_ENCRYPT_UNKNOWN	"security-medium"
  53 #define EMBLEM_SIGN_BAD		"stock_signature-bad"
  54 #define EMBLEM_SIGN_GOOD	"stock_signature-ok"
  55 #define EMBLEM_SIGN_UNKNOWN	"stock_signature"
  56 
  57 /* Attributes needed for EAttachmentStore columns. */
  58 #define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
  59 
  60 struct _EAttachmentPrivate {
  61 	GFile *file;
  62 	GIcon *icon;
  63 	GFileInfo *file_info;
  64 	GCancellable *cancellable;
  65 	CamelMimePart *mime_part;
  66 	guint emblem_timeout_id;
  67 	gchar *disposition;
  68 	gint percent;
  69 	gint64 last_percent_notify; /* to avoid excessive notifications */
  70 
  71 	guint can_show : 1;
  72 	guint loading  : 1;
  73 	guint saving   : 1;
  74 	guint shown    : 1;
  75 
  76 	camel_cipher_validity_encrypt_t encrypted;
  77 	camel_cipher_validity_sign_t signed_;
  78 
  79 	/* This is a reference to our row in an EAttachmentStore,
  80 	 * serving as a means of broadcasting "row-changed" signals.
  81 	 * If we are removed from the store, we lazily free the
  82 	 * reference when it is found to be to be invalid. */
  83 	GtkTreeRowReference *reference;
  84 };
  85 
  86 enum {
  87 	PROP_0,
  88 	PROP_CAN_SHOW,
  89 	PROP_DISPOSITION,
  90 	PROP_ENCRYPTED,
  91 	PROP_FILE,
  92 	PROP_FILE_INFO,
  93 	PROP_ICON,
  94 	PROP_LOADING,
  95 	PROP_MIME_PART,
  96 	PROP_PERCENT,
  97 	PROP_REFERENCE,
  98 	PROP_SAVING,
  99 	PROP_SHOWN,
 100 	PROP_SIGNED
 101 };
 102 
 103 G_DEFINE_TYPE (
 104 	EAttachment,
 105 	e_attachment,
 106 	G_TYPE_OBJECT)
 107 
 108 static gboolean
 109 create_system_thumbnail (EAttachment *attachment,
 110                          GIcon **icon)
 111 {
 112 	GFile *file;
 113 	GFile *icon_file;
 114 	gchar *thumbnail = NULL;
 115 
 116 	g_return_val_if_fail (attachment != NULL, FALSE);
 117 	g_return_val_if_fail (icon != NULL, FALSE);
 118 
 119 	file = e_attachment_get_file (attachment);
 120 
 121 	if (file && g_file_has_uri_scheme (file, "file")) {
 122 		gchar *path = g_file_get_path (file);
 123 		if (path) {
 124 			thumbnail = e_icon_factory_create_thumbnail (path);
 125 			g_free (path);
 126 		}
 127 	}
 128 
 129 	if (thumbnail == NULL)
 130 		return FALSE;
 131 
 132 	icon_file = g_file_new_for_path (thumbnail);
 133 
 134 	if (*icon)
 135 		g_object_unref (*icon);
 136 
 137 	*icon = g_file_icon_new (icon_file);
 138 
 139 	g_object_unref (icon_file);
 140 
 141 	if (file) {
 142 		GFileInfo *file_info;
 143 		const gchar *attribute;
 144 
 145 		file_info = e_attachment_get_file_info (attachment);
 146 		attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
 147 
 148 		if (file_info != NULL)
 149 			g_file_info_set_attribute_byte_string (
 150 				file_info, attribute, thumbnail);
 151 	}
 152 
 153 	g_free (thumbnail);
 154 
 155 	return TRUE;
 156 }
 157 
 158 static gchar *
 159 attachment_get_default_charset (void)
 160 {
 161 	GSettings *settings;
 162 	gchar *charset;
 163 
 164 	/* XXX This doesn't really belong here. */
 165 
 166 	settings = g_settings_new ("org.gnome.evolution.mail");
 167 	charset = g_settings_get_string (settings, "composer-charset");
 168 	if (charset == NULL || *charset == '\0') {
 169 		g_free (charset);
 170 		/* FIXME This was "/apps/evolution/mail/format/charset",
 171 		 *       not sure it relates to "charset" */
 172 		charset = g_settings_get_string (settings, "charset");
 173 		if (charset == NULL || *charset == '\0') {
 174 			g_free (charset);
 175 			charset = NULL;
 176 		}
 177 	}
 178 	g_object_unref (settings);
 179 
 180 	if (charset == NULL)
 181 		charset = g_strdup (camel_iconv_locale_charset ());
 182 
 183 	if (charset == NULL)
 184 		charset = g_strdup ("us-ascii");
 185 
 186 	return charset;
 187 }
 188 
 189 static void
 190 attachment_update_file_info_columns (EAttachment *attachment)
 191 {
 192 	GtkTreeRowReference *reference;
 193 	GtkTreeModel *model;
 194 	GtkTreePath *path;
 195 	GtkTreeIter iter;
 196 	GFileInfo *file_info;
 197 	const gchar *content_type;
 198 	const gchar *description;
 199 	const gchar *display_name;
 200 	gchar *content_desc;
 201 	gchar *display_size;
 202 	gchar *caption;
 203 	goffset size;
 204 
 205 	reference = e_attachment_get_reference (attachment);
 206 	if (!gtk_tree_row_reference_valid (reference))
 207 		return;
 208 
 209 	file_info = e_attachment_get_file_info (attachment);
 210 	if (file_info == NULL)
 211 		return;
 212 
 213 	model = gtk_tree_row_reference_get_model (reference);
 214 	path = gtk_tree_row_reference_get_path (reference);
 215 	gtk_tree_model_get_iter (model, &iter, path);
 216 	gtk_tree_path_free (path);
 217 
 218 	content_type = g_file_info_get_content_type (file_info);
 219 	display_name = g_file_info_get_display_name (file_info);
 220 	size = g_file_info_get_size (file_info);
 221 
 222 	content_desc = g_content_type_get_description (content_type);
 223 	display_size = g_format_size_for_display (size);
 224 
 225 	description = e_attachment_get_description (attachment);
 226 	if (description == NULL || *description == '\0')
 227 		description = display_name;
 228 
 229 	if (size > 0)
 230 		caption = g_strdup_printf (
 231 			"%s\n(%s)", description, display_size);
 232 	else
 233 		caption = g_strdup (description);
 234 
 235 	gtk_list_store_set (
 236 		GTK_LIST_STORE (model), &iter,
 237 		E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
 238 		E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_desc,
 239 		E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description,
 240 		E_ATTACHMENT_STORE_COLUMN_SIZE, size,
 241 		-1);
 242 
 243 	g_free (content_desc);
 244 	g_free (display_size);
 245 	g_free (caption);
 246 }
 247 
 248 static void
 249 attachment_update_icon_column (EAttachment *attachment)
 250 {
 251 	GtkTreeRowReference *reference;
 252 	GtkTreeModel *model;
 253 	GtkTreePath *path;
 254 	GtkTreeIter iter;
 255 	GFileInfo *file_info;
 256 	GCancellable *cancellable;
 257 	GIcon *icon = NULL;
 258 	const gchar *emblem_name = NULL;
 259 	const gchar *thumbnail_path = NULL;
 260 
 261 	reference = e_attachment_get_reference (attachment);
 262 	if (!gtk_tree_row_reference_valid (reference))
 263 		return;
 264 
 265 	model = gtk_tree_row_reference_get_model (reference);
 266 	path = gtk_tree_row_reference_get_path (reference);
 267 	gtk_tree_model_get_iter (model, &iter, path);
 268 	gtk_tree_path_free (path);
 269 
 270 	cancellable = attachment->priv->cancellable;
 271 	file_info = e_attachment_get_file_info (attachment);
 272 
 273 	if (file_info != NULL) {
 274 		icon = g_file_info_get_icon (file_info);
 275 		thumbnail_path = g_file_info_get_attribute_byte_string (
 276 			file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
 277 	}
 278 
 279 	/* Prefer the thumbnail if we have one. */
 280 	if (thumbnail_path != NULL && *thumbnail_path != '\0') {
 281 		GFile *file;
 282 
 283 		file = g_file_new_for_path (thumbnail_path);
 284 		icon = g_file_icon_new (file);
 285 		g_object_unref (file);
 286 
 287 	/* Try the system thumbnailer. */
 288 	} else if (create_system_thumbnail (attachment, &icon)) {
 289 		/* Nothing to do, just use the icon. */
 290 
 291 	/* Else use the standard icon for the content type. */
 292 	} else if (icon != NULL)
 293 		g_object_ref (icon);
 294 
 295 	/* Last ditch fallback.  (GFileInfo not yet loaded?) */
 296 	else
 297 		icon = g_themed_icon_new (DEFAULT_ICON_NAME);
 298 
 299 	/* Pick an emblem, limit one.  Choices listed by priority. */
 300 
 301 	if (g_cancellable_is_cancelled (cancellable))
 302 		emblem_name = EMBLEM_CANCELLED;
 303 
 304 	else if (e_attachment_get_loading (attachment))
 305 		emblem_name = EMBLEM_LOADING;
 306 
 307 	else if (e_attachment_get_saving (attachment))
 308 		emblem_name = EMBLEM_SAVING;
 309 
 310 	else if (e_attachment_get_encrypted (attachment))
 311 		switch (e_attachment_get_encrypted (attachment)) {
 312 			case CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK:
 313 				emblem_name = EMBLEM_ENCRYPT_WEAK;
 314 				break;
 315 
 316 			case CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED:
 317 				emblem_name = EMBLEM_ENCRYPT_UNKNOWN;
 318 				break;
 319 
 320 			case CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG:
 321 				emblem_name = EMBLEM_ENCRYPT_STRONG;
 322 				break;
 323 
 324 			default:
 325 				g_warn_if_reached ();
 326 				break;
 327 		}
 328 
 329 	else if (e_attachment_get_signed (attachment))
 330 		switch (e_attachment_get_signed (attachment)) {
 331 			case CAMEL_CIPHER_VALIDITY_SIGN_GOOD:
 332 				emblem_name = EMBLEM_SIGN_GOOD;
 333 				break;
 334 
 335 			case CAMEL_CIPHER_VALIDITY_SIGN_BAD:
 336 				emblem_name = EMBLEM_SIGN_BAD;
 337 				break;
 338 
 339 			case CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN:
 340 			case CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY:
 341 				emblem_name = EMBLEM_SIGN_UNKNOWN;
 342 				break;
 343 
 344 			default:
 345 				g_warn_if_reached ();
 346 				break;
 347 		}
 348 
 349 	if (emblem_name != NULL) {
 350 		GIcon *emblemed_icon;
 351 		GEmblem *emblem;
 352 
 353 		emblemed_icon = g_themed_icon_new (emblem_name);
 354 		emblem = g_emblem_new (emblemed_icon);
 355 		g_object_unref (emblemed_icon);
 356 
 357 		emblemed_icon = g_emblemed_icon_new (icon, emblem);
 358 		g_object_unref (emblem);
 359 		g_object_unref (icon);
 360 
 361 		icon = emblemed_icon;
 362 	}
 363 
 364 	gtk_list_store_set (
 365 		GTK_LIST_STORE (model), &iter,
 366 		E_ATTACHMENT_STORE_COLUMN_ICON, icon,
 367 		-1);
 368 
 369 	/* Cache the icon to reuse for things like drag-n-drop. */
 370 	if (attachment->priv->icon != NULL)
 371 		g_object_unref (attachment->priv->icon);
 372 	attachment->priv->icon = icon;
 373 	g_object_notify (G_OBJECT (attachment), "icon");
 374 }
 375 
 376 static void
 377 attachment_update_progress_columns (EAttachment *attachment)
 378 {
 379 	GtkTreeRowReference *reference;
 380 	GtkTreeModel *model;
 381 	GtkTreePath *path;
 382 	GtkTreeIter iter;
 383 	gboolean loading;
 384 	gboolean saving;
 385 	gint percent;
 386 
 387 	reference = e_attachment_get_reference (attachment);
 388 	if (!gtk_tree_row_reference_valid (reference))
 389 		return;
 390 
 391 	model = gtk_tree_row_reference_get_model (reference);
 392 	path = gtk_tree_row_reference_get_path (reference);
 393 	gtk_tree_model_get_iter (model, &iter, path);
 394 	gtk_tree_path_free (path);
 395 
 396 	/* Don't show progress bars until we have progress to report. */
 397 	percent = e_attachment_get_percent (attachment);
 398 	loading = e_attachment_get_loading (attachment) && (percent > 0);
 399 	saving = e_attachment_get_saving (attachment) && (percent > 0);
 400 
 401 	gtk_list_store_set (
 402 		GTK_LIST_STORE (model), &iter,
 403 		E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
 404 		E_ATTACHMENT_STORE_COLUMN_PERCENT, percent,
 405 		E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
 406 		-1);
 407 }
 408 
 409 static void
 410 attachment_set_loading (EAttachment *attachment,
 411                         gboolean loading)
 412 {
 413 	GtkTreeRowReference *reference;
 414 
 415 	reference = e_attachment_get_reference (attachment);
 416 
 417 	attachment->priv->percent = 0;
 418 	attachment->priv->loading = loading;
 419 	attachment->priv->last_percent_notify = 0;
 420 
 421 	g_object_freeze_notify (G_OBJECT (attachment));
 422 	g_object_notify (G_OBJECT (attachment), "percent");
 423 	g_object_notify (G_OBJECT (attachment), "loading");
 424 	g_object_thaw_notify (G_OBJECT (attachment));
 425 
 426 	if (gtk_tree_row_reference_valid (reference)) {
 427 		GtkTreeModel *model;
 428 		model = gtk_tree_row_reference_get_model (reference);
 429 		g_object_notify (G_OBJECT (model), "num-loading");
 430 	}
 431 }
 432 
 433 static void
 434 attachment_set_saving (EAttachment *attachment,
 435                        gboolean saving)
 436 {
 437 	attachment->priv->percent = 0;
 438 	attachment->priv->saving = saving;
 439 	attachment->priv->last_percent_notify = 0;
 440 
 441 	g_object_freeze_notify (G_OBJECT (attachment));
 442 	g_object_notify (G_OBJECT (attachment), "percent");
 443 	g_object_notify (G_OBJECT (attachment), "saving");
 444 	g_object_thaw_notify (G_OBJECT (attachment));
 445 }
 446 
 447 static void
 448 attachment_progress_cb (goffset current_num_bytes,
 449                         goffset total_num_bytes,
 450                         EAttachment *attachment)
 451 {
 452 	gint new_percent;
 453 
 454 	/* Avoid dividing by zero. */
 455 	if (total_num_bytes == 0)
 456 		return;
 457 
 458 	/* do not notify too often, 5 times per second is sufficient */
 459 	if (g_get_monotonic_time () - attachment->priv->last_percent_notify < 200000)
 460 		return;
 461 
 462 	attachment->priv->last_percent_notify = g_get_monotonic_time ();
 463 
 464 	new_percent = (current_num_bytes * 100) / total_num_bytes;
 465 
 466 	if (new_percent != attachment->priv->percent) {
 467 		attachment->priv->percent = new_percent;
 468 		g_object_notify (G_OBJECT (attachment), "percent");
 469 	}
 470 }
 471 
 472 static gboolean
 473 attachment_cancelled_timeout_cb (EAttachment *attachment)
 474 {
 475 	attachment->priv->emblem_timeout_id = 0;
 476 	g_cancellable_reset (attachment->priv->cancellable);
 477 
 478 	attachment_update_icon_column (attachment);
 479 
 480 	return FALSE;
 481 }
 482 
 483 static void
 484 attachment_cancelled_cb (EAttachment *attachment)
 485 {
 486 	/* Reset the GCancellable after one second.  This causes a
 487 	 * cancel emblem to be briefly shown on the attachment icon
 488 	 * as visual feedback that an operation was cancelled. */
 489 
 490 	if (attachment->priv->emblem_timeout_id > 0)
 491 		g_source_remove (attachment->priv->emblem_timeout_id);
 492 
 493 	attachment->priv->emblem_timeout_id = g_timeout_add_seconds (
 494 		1, (GSourceFunc) attachment_cancelled_timeout_cb, attachment);
 495 
 496 	attachment_update_icon_column (attachment);
 497 }
 498 
 499 static void
 500 attachment_set_property (GObject *object,
 501                          guint property_id,
 502                          const GValue *value,
 503                          GParamSpec *pspec)
 504 {
 505 	switch (property_id) {
 506 		case PROP_CAN_SHOW:
 507 			e_attachment_set_can_show (
 508 				E_ATTACHMENT (object),
 509 				g_value_get_boolean (value));
 510 			return;
 511 
 512 		case PROP_DISPOSITION:
 513 			e_attachment_set_disposition (
 514 				E_ATTACHMENT (object),
 515 				g_value_get_string (value));
 516 			return;
 517 
 518 		case PROP_ENCRYPTED:
 519 			e_attachment_set_encrypted (
 520 				E_ATTACHMENT (object),
 521 				g_value_get_int (value));
 522 			return;
 523 
 524 		case PROP_FILE:
 525 			e_attachment_set_file (
 526 				E_ATTACHMENT (object),
 527 				g_value_get_object (value));
 528 			return;
 529 
 530 		case PROP_SHOWN:
 531 			e_attachment_set_shown (
 532 				E_ATTACHMENT (object),
 533 				g_value_get_boolean (value));
 534 			return;
 535 
 536 		case PROP_MIME_PART:
 537 			e_attachment_set_mime_part (
 538 				E_ATTACHMENT (object),
 539 				g_value_get_boxed (value));
 540 			return;
 541 
 542 		case PROP_REFERENCE:
 543 			e_attachment_set_reference (
 544 				E_ATTACHMENT (object),
 545 				g_value_get_boxed (value));
 546 			return;
 547 
 548 		case PROP_SIGNED:
 549 			e_attachment_set_signed (
 550 				E_ATTACHMENT (object),
 551 				g_value_get_int (value));
 552 			return;
 553 	}
 554 
 555 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 556 }
 557 
 558 static void
 559 attachment_get_property (GObject *object,
 560                          guint property_id,
 561                          GValue *value,
 562                          GParamSpec *pspec)
 563 {
 564 	switch (property_id) {
 565 		case PROP_CAN_SHOW:
 566 			g_value_set_boolean (
 567 				value, e_attachment_get_can_show (
 568 				E_ATTACHMENT (object)));
 569 			return;
 570 
 571 		case PROP_DISPOSITION:
 572 			g_value_set_string (
 573 				value, e_attachment_get_disposition (
 574 				E_ATTACHMENT (object)));
 575 			return;
 576 
 577 		case PROP_ENCRYPTED:
 578 			g_value_set_int (
 579 				value, e_attachment_get_encrypted (
 580 				E_ATTACHMENT (object)));
 581 			return;
 582 
 583 		case PROP_FILE:
 584 			g_value_set_object (
 585 				value, e_attachment_get_file (
 586 				E_ATTACHMENT (object)));
 587 			return;
 588 
 589 		case PROP_FILE_INFO:
 590 			g_value_set_object (
 591 				value, e_attachment_get_file_info (
 592 				E_ATTACHMENT (object)));
 593 			return;
 594 
 595 		case PROP_ICON:
 596 			g_value_set_object (
 597 				value, e_attachment_get_icon (
 598 				E_ATTACHMENT (object)));
 599 			return;
 600 
 601 		case PROP_SHOWN:
 602 			g_value_set_boolean (
 603 				value, e_attachment_get_shown (
 604 				E_ATTACHMENT (object)));
 605 			return;
 606 
 607 		case PROP_LOADING:
 608 			g_value_set_boolean (
 609 				value, e_attachment_get_loading (
 610 				E_ATTACHMENT (object)));
 611 			return;
 612 
 613 		case PROP_MIME_PART:
 614 			g_value_set_boxed (
 615 				value, e_attachment_get_mime_part (
 616 				E_ATTACHMENT (object)));
 617 			return;
 618 
 619 		case PROP_PERCENT:
 620 			g_value_set_int (
 621 				value, e_attachment_get_percent (
 622 				E_ATTACHMENT (object)));
 623 			return;
 624 
 625 		case PROP_REFERENCE:
 626 			g_value_set_boxed (
 627 				value, e_attachment_get_reference (
 628 				E_ATTACHMENT (object)));
 629 			return;
 630 
 631 		case PROP_SAVING:
 632 			g_value_set_boolean (
 633 				value, e_attachment_get_saving (
 634 				E_ATTACHMENT (object)));
 635 			return;
 636 
 637 		case PROP_SIGNED:
 638 			g_value_set_int (
 639 				value, e_attachment_get_signed (
 640 				E_ATTACHMENT (object)));
 641 			return;
 642 	}
 643 
 644 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 645 }
 646 
 647 static void
 648 attachment_dispose (GObject *object)
 649 {
 650 	EAttachmentPrivate *priv;
 651 
 652 	priv = E_ATTACHMENT_GET_PRIVATE (object);
 653 
 654 	if (priv->file != NULL) {
 655 		g_object_unref (priv->file);
 656 		priv->file = NULL;
 657 	}
 658 
 659 	if (priv->icon != NULL) {
 660 		g_object_unref (priv->icon);
 661 		priv->icon = NULL;
 662 	}
 663 
 664 	if (priv->file_info != NULL) {
 665 		g_object_unref (priv->file_info);
 666 		priv->file_info = NULL;
 667 	}
 668 
 669 	if (priv->cancellable != NULL) {
 670 		g_object_unref (priv->cancellable);
 671 		priv->cancellable = NULL;
 672 	}
 673 
 674 	if (priv->mime_part != NULL) {
 675 		g_object_unref (priv->mime_part);
 676 		priv->mime_part = NULL;
 677 	}
 678 
 679 	if (priv->emblem_timeout_id > 0) {
 680 		g_source_remove (priv->emblem_timeout_id);
 681 		priv->emblem_timeout_id = 0;
 682 	}
 683 
 684 	/* This accepts NULL arguments. */
 685 	gtk_tree_row_reference_free (priv->reference);
 686 	priv->reference = NULL;
 687 
 688 	/* Chain up to parent's dispose() method. */
 689 	G_OBJECT_CLASS (e_attachment_parent_class)->dispose (object);
 690 }
 691 
 692 static void
 693 attachment_finalize (GObject *object)
 694 {
 695 	EAttachmentPrivate *priv;
 696 
 697 	priv = E_ATTACHMENT_GET_PRIVATE (object);
 698 
 699 	g_free (priv->disposition);
 700 
 701 	/* Chain up to parent's finalize() method. */
 702 	G_OBJECT_CLASS (e_attachment_parent_class)->finalize (object);
 703 }
 704 
 705 static void
 706 e_attachment_class_init (EAttachmentClass *class)
 707 {
 708 	GObjectClass *object_class;
 709 
 710 	g_type_class_add_private (class, sizeof (EAttachmentPrivate));
 711 
 712 	object_class = G_OBJECT_CLASS (class);
 713 	object_class->set_property = attachment_set_property;
 714 	object_class->get_property = attachment_get_property;
 715 	object_class->dispose = attachment_dispose;
 716 	object_class->finalize = attachment_finalize;
 717 
 718 	g_object_class_install_property (
 719 		object_class,
 720 		PROP_CAN_SHOW,
 721 		g_param_spec_boolean (
 722 			"can-show",
 723 			"Can Show",
 724 			NULL,
 725 			FALSE,
 726 			G_PARAM_READWRITE |
 727 			G_PARAM_CONSTRUCT));
 728 
 729 	g_object_class_install_property (
 730 		object_class,
 731 		PROP_DISPOSITION,
 732 		g_param_spec_string (
 733 			"disposition",
 734 			"Disposition",
 735 			NULL,
 736 			"attachment",
 737 			G_PARAM_READWRITE |
 738 			G_PARAM_CONSTRUCT));
 739 
 740 	/* FIXME Define a GEnumClass for this. */
 741 	g_object_class_install_property (
 742 		object_class,
 743 		PROP_ENCRYPTED,
 744 		g_param_spec_int (
 745 			"encrypted",
 746 			"Encrypted",
 747 			NULL,
 748 			CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
 749 			CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG,
 750 			CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
 751 			G_PARAM_READWRITE |
 752 			G_PARAM_CONSTRUCT));
 753 
 754 	g_object_class_install_property (
 755 		object_class,
 756 		PROP_FILE,
 757 		g_param_spec_object (
 758 			"file",
 759 			"File",
 760 			NULL,
 761 			G_TYPE_FILE,
 762 			G_PARAM_READWRITE |
 763 			G_PARAM_CONSTRUCT));
 764 
 765 	g_object_class_install_property (
 766 		object_class,
 767 		PROP_FILE_INFO,
 768 		g_param_spec_object (
 769 			"file-info",
 770 			"File Info",
 771 			NULL,
 772 			G_TYPE_FILE_INFO,
 773 			G_PARAM_READABLE));
 774 
 775 	g_object_class_install_property (
 776 		object_class,
 777 		PROP_ICON,
 778 		g_param_spec_object (
 779 			"icon",
 780 			"Icon",
 781 			NULL,
 782 			G_TYPE_ICON,
 783 			G_PARAM_READABLE));
 784 
 785 	g_object_class_install_property (
 786 		object_class,
 787 		PROP_LOADING,
 788 		g_param_spec_boolean (
 789 			"loading",
 790 			"Loading",
 791 			NULL,
 792 			FALSE,
 793 			G_PARAM_READABLE));
 794 
 795 	g_object_class_install_property (
 796 		object_class,
 797 		PROP_MIME_PART,
 798 		g_param_spec_object (
 799 			"mime-part",
 800 			"MIME Part",
 801 			NULL,
 802 			CAMEL_TYPE_MIME_PART,
 803 			G_PARAM_READWRITE));
 804 
 805 	g_object_class_install_property (
 806 		object_class,
 807 		PROP_PERCENT,
 808 		g_param_spec_int (
 809 			"percent",
 810 			"Percent",
 811 			NULL,
 812 			0,
 813 			100,
 814 			0,
 815 			G_PARAM_READABLE));
 816 
 817 	g_object_class_install_property (
 818 		object_class,
 819 		PROP_REFERENCE,
 820 		g_param_spec_boxed (
 821 			"reference",
 822 			"Reference",
 823 			NULL,
 824 			GTK_TYPE_TREE_ROW_REFERENCE,
 825 			G_PARAM_READWRITE));
 826 
 827 	g_object_class_install_property (
 828 		object_class,
 829 		PROP_SAVING,
 830 		g_param_spec_boolean (
 831 			"saving",
 832 			"Saving",
 833 			NULL,
 834 			FALSE,
 835 			G_PARAM_READABLE));
 836 
 837 	g_object_class_install_property (
 838 		object_class,
 839 		PROP_SHOWN,
 840 		g_param_spec_boolean (
 841 			"shown",
 842 			"Shown",
 843 			NULL,
 844 			FALSE,
 845 			G_PARAM_READWRITE |
 846 			G_PARAM_CONSTRUCT));
 847 
 848 	/* FIXME Define a GEnumClass for this. */
 849 	g_object_class_install_property (
 850 		object_class,
 851 		PROP_SIGNED,
 852 		g_param_spec_int (
 853 			"signed",
 854 			"Signed",
 855 			NULL,
 856 			CAMEL_CIPHER_VALIDITY_SIGN_NONE,
 857 			CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY,
 858 			CAMEL_CIPHER_VALIDITY_SIGN_NONE,
 859 			G_PARAM_READWRITE |
 860 			G_PARAM_CONSTRUCT));
 861 }
 862 
 863 static void
 864 e_attachment_init (EAttachment *attachment)
 865 {
 866 	attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment);
 867 	attachment->priv->cancellable = g_cancellable_new ();
 868 	attachment->priv->encrypted = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
 869 	attachment->priv->signed_ = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
 870 
 871 	g_signal_connect (
 872 		attachment, "notify::encrypted",
 873 		G_CALLBACK (attachment_update_icon_column), NULL);
 874 
 875 	g_signal_connect (
 876 		attachment, "notify::file-info",
 877 		G_CALLBACK (attachment_update_file_info_columns), NULL);
 878 
 879 	g_signal_connect (
 880 		attachment, "notify::file-info",
 881 		G_CALLBACK (attachment_update_icon_column), NULL);
 882 
 883 	g_signal_connect (
 884 		attachment, "notify::loading",
 885 		G_CALLBACK (attachment_update_icon_column), NULL);
 886 
 887 	g_signal_connect (
 888 		attachment, "notify::loading",
 889 		G_CALLBACK (attachment_update_progress_columns), NULL);
 890 
 891 	g_signal_connect (
 892 		attachment, "notify::percent",
 893 		G_CALLBACK (attachment_update_progress_columns), NULL);
 894 
 895 	g_signal_connect (
 896 		attachment, "notify::reference",
 897 		G_CALLBACK (attachment_update_file_info_columns), NULL);
 898 
 899 	g_signal_connect (
 900 		attachment, "notify::reference",
 901 		G_CALLBACK (attachment_update_icon_column), NULL);
 902 
 903 	g_signal_connect (
 904 		attachment, "notify::reference",
 905 		G_CALLBACK (attachment_update_progress_columns), NULL);
 906 
 907 	g_signal_connect (
 908 		attachment, "notify::saving",
 909 		G_CALLBACK (attachment_update_icon_column), NULL);
 910 
 911 	g_signal_connect (
 912 		attachment, "notify::saving",
 913 		G_CALLBACK (attachment_update_progress_columns), NULL);
 914 
 915 	g_signal_connect (
 916 		attachment, "notify::signed",
 917 		G_CALLBACK (attachment_update_icon_column), NULL);
 918 
 919 	g_signal_connect_swapped (
 920 		attachment->priv->cancellable, "cancelled",
 921 		G_CALLBACK (attachment_cancelled_cb), attachment);
 922 }
 923 
 924 EAttachment *
 925 e_attachment_new (void)
 926 {
 927 	return g_object_new (E_TYPE_ATTACHMENT, NULL);
 928 }
 929 
 930 EAttachment *
 931 e_attachment_new_for_path (const gchar *path)
 932 {
 933 	EAttachment *attachment;
 934 	GFile *file;
 935 
 936 	g_return_val_if_fail (path != NULL, NULL);
 937 
 938 	file = g_file_new_for_path (path);
 939 	attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
 940 	g_object_unref (file);
 941 
 942 	return attachment;
 943 }
 944 
 945 EAttachment *
 946 e_attachment_new_for_uri (const gchar *uri)
 947 {
 948 	EAttachment *attachment;
 949 	GFile *file;
 950 
 951 	g_return_val_if_fail (uri != NULL, NULL);
 952 
 953 	file = g_file_new_for_uri (uri);
 954 	attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
 955 	g_object_unref (file);
 956 
 957 	return attachment;
 958 }
 959 
 960 EAttachment *
 961 e_attachment_new_for_message (CamelMimeMessage *message)
 962 {
 963 	CamelDataWrapper *wrapper;
 964 	CamelMimePart *mime_part;
 965 	EAttachment *attachment;
 966 	GString *description;
 967 	const gchar *subject;
 968 
 969 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
 970 
 971 	mime_part = camel_mime_part_new ();
 972 	camel_mime_part_set_disposition (mime_part, "inline");
 973 	subject = camel_mime_message_get_subject (message);
 974 
 975 	/* To Translators: This text is set as a description of an attached
 976 	 * message when, for example, attaching it to a composer. When the
 977 	 * message to be attached has also filled Subject, then this text is
 978 	 * of form "Attached message - Subject", otherwise it's left as is. */
 979 	description = g_string_new (_("Attached message"));
 980 	if (subject != NULL)
 981 		g_string_append_printf (description, " - %s", subject);
 982 	camel_mime_part_set_description (mime_part, description->str);
 983 	g_string_free (description, TRUE);
 984 
 985 	wrapper = CAMEL_DATA_WRAPPER (message);
 986 	camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper);
 987 	camel_mime_part_set_content_type (mime_part, "message/rfc822");
 988 
 989 	attachment = e_attachment_new ();
 990 	e_attachment_set_mime_part (attachment, mime_part);
 991 	g_object_unref (mime_part);
 992 
 993 	return attachment;
 994 }
 995 
 996 void
 997 e_attachment_add_to_multipart (EAttachment *attachment,
 998                                CamelMultipart *multipart,
 999                                const gchar *default_charset)
1000 {
1001 	CamelContentType *content_type;
1002 	CamelDataWrapper *wrapper;
1003 	CamelMimePart *mime_part;
1004 
1005 	/* XXX EMsgComposer might be a better place for this function. */
1006 
1007 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1008 	g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
1009 
1010 	/* Still loading?  Too bad. */
1011 	mime_part = e_attachment_get_mime_part (attachment);
1012 	if (mime_part == NULL)
1013 		return;
1014 
1015 	content_type = camel_mime_part_get_content_type (mime_part);
1016 	wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1017 
1018 	if (CAMEL_IS_MULTIPART (wrapper))
1019 		goto exit;
1020 
1021 	/* For text content, determine the best encoding and character set. */
1022 	if (camel_content_type_is (content_type, "text", "*")) {
1023 		CamelTransferEncoding encoding;
1024 		CamelStream *filtered_stream;
1025 		CamelMimeFilter *filter;
1026 		CamelStream *stream;
1027 		const gchar *charset;
1028 
1029 		charset = camel_content_type_param (content_type, "charset");
1030 
1031 		/* Determine the best encoding by writing the MIME
1032 		 * part to a NULL stream with a "bestenc" filter. */
1033 		stream = camel_stream_null_new ();
1034 		filtered_stream = camel_stream_filter_new (stream);
1035 		filter = camel_mime_filter_bestenc_new (
1036 			CAMEL_BESTENC_GET_ENCODING);
1037 		camel_stream_filter_add (
1038 			CAMEL_STREAM_FILTER (filtered_stream),
1039 			CAMEL_MIME_FILTER (filter));
1040 		camel_data_wrapper_decode_to_stream_sync (
1041 			wrapper, filtered_stream, NULL, NULL);
1042 		g_object_unref (filtered_stream);
1043 		g_object_unref (stream);
1044 
1045 		/* Retrieve the best encoding from the filter. */
1046 		encoding = camel_mime_filter_bestenc_get_best_encoding (
1047 			CAMEL_MIME_FILTER_BESTENC (filter),
1048 			CAMEL_BESTENC_8BIT);
1049 		camel_mime_part_set_encoding (mime_part, encoding);
1050 		g_object_unref (filter);
1051 
1052 		if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
1053 			/* The text fits within us-ascii, so this is safe.
1054 			 * FIXME Check that this isn't iso-2022-jp? */
1055 			default_charset = "us-ascii";
1056 
1057 		} else if (charset == NULL && default_charset == NULL) {
1058 			default_charset = attachment_get_default_charset ();
1059 			/* FIXME Check that this fits within the
1060 			 *       default_charset and if not, find one
1061 			 *       that does and/or allow the user to
1062 			 *       specify. */
1063 		}
1064 
1065 		if (charset == NULL) {
1066 			gchar *type;
1067 
1068 			camel_content_type_set_param (
1069 				content_type, "charset", default_charset);
1070 			type = camel_content_type_format (content_type);
1071 			camel_mime_part_set_content_type (mime_part, type);
1072 			g_free (type);
1073 		}
1074 
1075 	/* Otherwise, unless it's a message/rfc822, Base64 encode it. */
1076 	} else if (!CAMEL_IS_MIME_MESSAGE (wrapper))
1077 		camel_mime_part_set_encoding (
1078 			mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
1079 
1080 exit:
1081 	camel_multipart_add_part (multipart, mime_part);
1082 }
1083 
1084 void
1085 e_attachment_cancel (EAttachment *attachment)
1086 {
1087 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1088 
1089 	g_cancellable_cancel (attachment->priv->cancellable);
1090 }
1091 
1092 gboolean
1093 e_attachment_get_can_show (EAttachment *attachment)
1094 {
1095 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1096 
1097 	return attachment->priv->can_show;
1098 }
1099 
1100 void
1101 e_attachment_set_can_show (EAttachment *attachment,
1102                            gboolean can_show)
1103 {
1104 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1105 
1106 	attachment->priv->can_show = can_show;
1107 
1108 	g_object_notify (G_OBJECT (attachment), "can-show");
1109 }
1110 
1111 const gchar *
1112 e_attachment_get_disposition (EAttachment *attachment)
1113 {
1114 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1115 
1116 	return attachment->priv->disposition;
1117 }
1118 
1119 void
1120 e_attachment_set_disposition (EAttachment *attachment,
1121                               const gchar *disposition)
1122 {
1123 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1124 
1125 	g_free (attachment->priv->disposition);
1126 	attachment->priv->disposition = g_strdup (disposition);
1127 
1128 	g_object_notify (G_OBJECT (attachment), "disposition");
1129 }
1130 
1131 GFile *
1132 e_attachment_get_file (EAttachment *attachment)
1133 {
1134 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1135 
1136 	return attachment->priv->file;
1137 }
1138 
1139 void
1140 e_attachment_set_file (EAttachment *attachment,
1141                        GFile *file)
1142 {
1143 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1144 
1145 	if (file != NULL) {
1146 		g_return_if_fail (G_IS_FILE (file));
1147 		g_object_ref (file);
1148 	}
1149 
1150 	if (attachment->priv->file != NULL)
1151 		g_object_unref (attachment->priv->file);
1152 
1153 	attachment->priv->file = file;
1154 
1155 	g_object_notify (G_OBJECT (attachment), "file");
1156 }
1157 
1158 GFileInfo *
1159 e_attachment_get_file_info (EAttachment *attachment)
1160 {
1161 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1162 
1163 	return attachment->priv->file_info;
1164 }
1165 
1166 void
1167 e_attachment_set_file_info (EAttachment *attachment,
1168                             GFileInfo *file_info)
1169 {
1170 	GtkTreeRowReference *reference;
1171 	GIcon *icon;
1172 
1173 	reference = e_attachment_get_reference (attachment);
1174 
1175 	if (file_info != NULL)
1176 		g_object_ref (file_info);
1177 
1178 	if (attachment->priv->file_info != NULL)
1179 		g_object_unref (attachment->priv->file_info);
1180 
1181 	attachment->priv->file_info = file_info;
1182 
1183 	/* If the GFileInfo contains a GThemedIcon, append a
1184 	 * fallback icon name to ensure we display something. */
1185 	icon = g_file_info_get_icon (file_info);
1186 	if (G_IS_THEMED_ICON (icon))
1187 		g_themed_icon_append_name (
1188 			G_THEMED_ICON (icon), DEFAULT_ICON_NAME);
1189 
1190 	g_object_notify (G_OBJECT (attachment), "file-info");
1191 
1192 	/* Tell the EAttachmentStore its total size changed. */
1193 	if (gtk_tree_row_reference_valid (reference)) {
1194 		GtkTreeModel *model;
1195 		model = gtk_tree_row_reference_get_model (reference);
1196 		g_object_notify (G_OBJECT (model), "total-size");
1197 	}
1198 }
1199 
1200 /**
1201  * e_attachment_get_mime_type:
1202  *
1203  * Returns mime_type part of the file_info as a newly allocated string,
1204  * which should be freed with g_free().
1205  * Returns NULL, if mime_type not found or set on the attachment.
1206  **/
1207 gchar *
1208 e_attachment_get_mime_type (EAttachment *attachment)
1209 {
1210 	GFileInfo *file_info;
1211 	const gchar *content_type;
1212 	gchar *mime_type;
1213 
1214 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1215 
1216 	file_info = e_attachment_get_file_info (attachment);
1217 	if (file_info == NULL)
1218 		return NULL;
1219 
1220 	content_type = g_file_info_get_content_type (file_info);
1221 	if (content_type == NULL)
1222 		return NULL;
1223 
1224 	mime_type = g_content_type_get_mime_type (content_type);
1225 	if (!mime_type)
1226 		return NULL;
1227 
1228 	camel_strdown (mime_type);
1229 
1230 	return mime_type;
1231 }
1232 
1233 GIcon *
1234 e_attachment_get_icon (EAttachment *attachment)
1235 {
1236 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1237 
1238 	return attachment->priv->icon;
1239 }
1240 
1241 gboolean
1242 e_attachment_get_loading (EAttachment *attachment)
1243 {
1244 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1245 
1246 	return attachment->priv->loading;
1247 }
1248 
1249 CamelMimePart *
1250 e_attachment_get_mime_part (EAttachment *attachment)
1251 {
1252 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1253 
1254 	return attachment->priv->mime_part;
1255 }
1256 
1257 void
1258 e_attachment_set_mime_part (EAttachment *attachment,
1259                             CamelMimePart *mime_part)
1260 {
1261 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1262 
1263 	if (mime_part != NULL) {
1264 		g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1265 		g_object_ref (mime_part);
1266 	}
1267 
1268 	if (attachment->priv->mime_part != NULL)
1269 		g_object_unref (attachment->priv->mime_part);
1270 
1271 	attachment->priv->mime_part = mime_part;
1272 
1273 	g_object_notify (G_OBJECT (attachment), "mime-part");
1274 }
1275 
1276 gint
1277 e_attachment_get_percent (EAttachment *attachment)
1278 {
1279 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
1280 
1281 	return attachment->priv->percent;
1282 }
1283 
1284 GtkTreeRowReference *
1285 e_attachment_get_reference (EAttachment *attachment)
1286 {
1287 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1288 
1289 	return attachment->priv->reference;
1290 }
1291 
1292 void
1293 e_attachment_set_reference (EAttachment *attachment,
1294                             GtkTreeRowReference *reference)
1295 {
1296 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1297 
1298 	if (reference != NULL)
1299 		reference = gtk_tree_row_reference_copy (reference);
1300 
1301 	gtk_tree_row_reference_free (attachment->priv->reference);
1302 	attachment->priv->reference = reference;
1303 
1304 	g_object_notify (G_OBJECT (attachment), "reference");
1305 }
1306 
1307 gboolean
1308 e_attachment_get_saving (EAttachment *attachment)
1309 {
1310 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1311 
1312 	return attachment->priv->saving;
1313 }
1314 
1315 gboolean
1316 e_attachment_get_shown (EAttachment *attachment)
1317 {
1318 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1319 
1320 	return attachment->priv->shown;
1321 }
1322 
1323 void
1324 e_attachment_set_shown (EAttachment *attachment,
1325                         gboolean shown)
1326 {
1327 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1328 
1329 	attachment->priv->shown = shown;
1330 
1331 	g_object_notify (G_OBJECT (attachment), "shown");
1332 }
1333 
1334 camel_cipher_validity_encrypt_t
1335 e_attachment_get_encrypted (EAttachment *attachment)
1336 {
1337 	g_return_val_if_fail (
1338 		E_IS_ATTACHMENT (attachment),
1339 		CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE);
1340 
1341 	return attachment->priv->encrypted;
1342 }
1343 
1344 void
1345 e_attachment_set_encrypted (EAttachment *attachment,
1346                             camel_cipher_validity_encrypt_t encrypted)
1347 {
1348 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1349 
1350 	attachment->priv->encrypted = encrypted;
1351 
1352 	g_object_notify (G_OBJECT (attachment), "encrypted");
1353 }
1354 
1355 camel_cipher_validity_sign_t
1356 e_attachment_get_signed (EAttachment *attachment)
1357 {
1358 	g_return_val_if_fail (
1359 		E_IS_ATTACHMENT (attachment),
1360 		CAMEL_CIPHER_VALIDITY_SIGN_NONE);
1361 
1362 	return attachment->priv->signed_;
1363 }
1364 
1365 void
1366 e_attachment_set_signed (EAttachment *attachment,
1367                          camel_cipher_validity_sign_t signed_)
1368 {
1369 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1370 
1371 	attachment->priv->signed_ = signed_;
1372 
1373 	g_object_notify (G_OBJECT (attachment), "signed");
1374 }
1375 
1376 const gchar *
1377 e_attachment_get_description (EAttachment *attachment)
1378 {
1379 	GFileInfo *file_info;
1380 	const gchar *attribute;
1381 
1382 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1383 
1384 	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
1385 	file_info = e_attachment_get_file_info (attachment);
1386 
1387 	if (file_info == NULL)
1388 		return NULL;
1389 
1390 	return g_file_info_get_attribute_string (file_info, attribute);
1391 }
1392 
1393 const gchar *
1394 e_attachment_get_thumbnail_path (EAttachment *attachment)
1395 {
1396 	GFileInfo *file_info;
1397 	const gchar *attribute;
1398 
1399 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1400 
1401 	attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
1402 	file_info = e_attachment_get_file_info (attachment);
1403 
1404 	if (file_info == NULL)
1405 		return NULL;
1406 
1407 	return g_file_info_get_attribute_byte_string (file_info, attribute);
1408 }
1409 
1410 gboolean
1411 e_attachment_is_rfc822 (EAttachment *attachment)
1412 {
1413 	gchar *mime_type;
1414 	gboolean is_rfc822;
1415 
1416 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1417 
1418 	mime_type = e_attachment_get_mime_type (attachment);
1419 	is_rfc822 = mime_type && g_ascii_strcasecmp (mime_type, "message/rfc822") == 0;
1420 	g_free (mime_type);
1421 
1422 	return is_rfc822;
1423 }
1424 
1425 GList *
1426 e_attachment_list_apps (EAttachment *attachment)
1427 {
1428 	GList *app_info_list;
1429 	GList *guessed_infos;
1430 	GFileInfo *file_info;
1431 	const gchar *content_type;
1432 	const gchar *display_name;
1433 	gboolean type_is_unknown;
1434 	gchar *allocated;
1435 
1436 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1437 
1438 	file_info = e_attachment_get_file_info (attachment);
1439 	if (file_info == NULL)
1440 		return NULL;
1441 
1442 	content_type = g_file_info_get_content_type (file_info);
1443 	display_name = g_file_info_get_display_name (file_info);
1444 	g_return_val_if_fail (content_type != NULL, NULL);
1445 
1446 	app_info_list = g_app_info_get_all_for_type (content_type);
1447 	type_is_unknown = g_content_type_is_unknown (content_type);
1448 
1449 	if (app_info_list != NULL && !type_is_unknown)
1450 		goto exit;
1451 
1452 	if (display_name == NULL)
1453 		goto exit;
1454 
1455 	allocated = g_content_type_guess (display_name, NULL, 0, NULL);
1456 	guessed_infos = g_app_info_get_all_for_type (allocated);
1457 	app_info_list = g_list_concat (guessed_infos, app_info_list);
1458 	g_free (allocated);
1459 
1460 exit:
1461 	return app_info_list;
1462 }
1463 
1464 /************************* e_attachment_load_async() *************************/
1465 
1466 typedef struct _LoadContext LoadContext;
1467 
1468 struct _LoadContext {
1469 	EAttachment *attachment;
1470 	CamelMimePart *mime_part;
1471 	GSimpleAsyncResult *simple;
1472 
1473 	GInputStream *input_stream;
1474 	GOutputStream *output_stream;
1475 	GFileInfo *file_info;
1476 	goffset total_num_bytes;
1477 	gssize bytes_read;
1478 	gchar buffer[4096];
1479 };
1480 
1481 /* Forward Declaration */
1482 static void
1483 attachment_load_stream_read_cb (GInputStream *input_stream,
1484                                 GAsyncResult *result,
1485                                 LoadContext *load_context);
1486 
1487 static LoadContext *
1488 attachment_load_context_new (EAttachment *attachment,
1489                              GAsyncReadyCallback callback,
1490                              gpointer user_data)
1491 {
1492 	LoadContext *load_context;
1493 	GSimpleAsyncResult *simple;
1494 
1495 	simple = g_simple_async_result_new (
1496 		G_OBJECT (attachment), callback,
1497 		user_data, e_attachment_load_async);
1498 
1499 	load_context = g_slice_new0 (LoadContext);
1500 	load_context->attachment = g_object_ref (attachment);
1501 	load_context->simple = simple;
1502 
1503 	attachment_set_loading (load_context->attachment, TRUE);
1504 
1505 	return load_context;
1506 }
1507 
1508 static void
1509 attachment_load_context_free (LoadContext *load_context)
1510 {
1511 	g_object_unref (load_context->attachment);
1512 
1513 	if (load_context->mime_part != NULL)
1514 		g_object_unref (load_context->mime_part);
1515 
1516 	if (load_context->simple)
1517 		g_object_unref (load_context->simple);
1518 
1519 	if (load_context->input_stream != NULL)
1520 		g_object_unref (load_context->input_stream);
1521 
1522 	if (load_context->output_stream != NULL)
1523 		g_object_unref (load_context->output_stream);
1524 
1525 	if (load_context->file_info != NULL)
1526 		g_object_unref (load_context->file_info);
1527 
1528 	g_slice_free (LoadContext, load_context);
1529 }
1530 
1531 static gboolean
1532 attachment_load_check_for_error (LoadContext *load_context,
1533                                  GError *error)
1534 {
1535 	GSimpleAsyncResult *simple;
1536 
1537 	if (error == NULL)
1538 		return FALSE;
1539 
1540 	simple = load_context->simple;
1541 	g_simple_async_result_take_error (simple, error);
1542 	g_simple_async_result_complete (simple);
1543 
1544 	attachment_load_context_free (load_context);
1545 
1546 	return TRUE;
1547 }
1548 
1549 static void
1550 attachment_load_finish (LoadContext *load_context)
1551 {
1552 	GFileInfo *file_info;
1553 	EAttachment *attachment;
1554 	GMemoryOutputStream *output_stream;
1555 	GSimpleAsyncResult *simple;
1556 	CamelDataWrapper *wrapper;
1557 	CamelMimePart *mime_part;
1558 	CamelStream *stream;
1559 	const gchar *attribute;
1560 	const gchar *content_type;
1561 	const gchar *display_name;
1562 	const gchar *description;
1563 	const gchar *disposition;
1564 	gchar *mime_type;
1565 	gpointer data;
1566 	gsize size;
1567 
1568 	simple = load_context->simple;
1569 
1570 	file_info = load_context->file_info;
1571 	attachment = load_context->attachment;
1572 	output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
1573 
1574 	if (e_attachment_is_rfc822 (attachment))
1575 		wrapper = (CamelDataWrapper *) camel_mime_message_new ();
1576 	else
1577 		wrapper = camel_data_wrapper_new ();
1578 
1579 	content_type = g_file_info_get_content_type (file_info);
1580 	mime_type = g_content_type_get_mime_type (content_type);
1581 
1582 	data = g_memory_output_stream_get_data (output_stream);
1583 	size = g_memory_output_stream_get_data_size (output_stream);
1584 
1585 	stream = camel_stream_mem_new_with_buffer (data, size);
1586 	camel_data_wrapper_construct_from_stream_sync (
1587 		wrapper, stream, NULL, NULL);
1588 	camel_data_wrapper_set_mime_type (wrapper, mime_type);
1589 	camel_stream_close (stream, NULL, NULL);
1590 	g_object_unref (stream);
1591 
1592 	mime_part = camel_mime_part_new ();
1593 	camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper);
1594 
1595 	g_object_unref (wrapper);
1596 	g_free (mime_type);
1597 
1598 	display_name = g_file_info_get_display_name (file_info);
1599 	if (display_name != NULL)
1600 		camel_mime_part_set_filename (mime_part, display_name);
1601 
1602 	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
1603 	description = g_file_info_get_attribute_string (file_info, attribute);
1604 	if (description != NULL)
1605 		camel_mime_part_set_description (mime_part, description);
1606 
1607 	disposition = e_attachment_get_disposition (attachment);
1608 	if (disposition != NULL)
1609 		camel_mime_part_set_disposition (mime_part, disposition);
1610 
1611 	/* Correctly report the size of zero length special files. */
1612 	if (g_file_info_get_size (file_info) == 0)
1613 		g_file_info_set_size (file_info, size);
1614 
1615 	load_context->mime_part = mime_part;
1616 
1617 	g_simple_async_result_set_op_res_gpointer (
1618 		simple, load_context, (GDestroyNotify) attachment_load_context_free);
1619 
1620 	g_simple_async_result_complete (simple);
1621 
1622 	/* make sure it's freed on operation end */
1623 	load_context->simple = NULL;
1624 	g_object_unref (simple);
1625 }
1626 
1627 static void
1628 attachment_load_write_cb (GOutputStream *output_stream,
1629                           GAsyncResult *result,
1630                           LoadContext *load_context)
1631 {
1632 	EAttachment *attachment;
1633 	GCancellable *cancellable;
1634 	GInputStream *input_stream;
1635 	gssize bytes_written;
1636 	GError *error = NULL;
1637 
1638 	bytes_written = g_output_stream_write_finish (
1639 		output_stream, result, &error);
1640 
1641 	if (attachment_load_check_for_error (load_context, error))
1642 		return;
1643 
1644 	attachment = load_context->attachment;
1645 	cancellable = attachment->priv->cancellable;
1646 	input_stream = load_context->input_stream;
1647 
1648 	attachment_progress_cb (
1649 		g_seekable_tell (G_SEEKABLE (output_stream)),
1650 		load_context->total_num_bytes, attachment);
1651 
1652 	if (bytes_written < load_context->bytes_read) {
1653 		g_memmove (
1654 			load_context->buffer,
1655 			load_context->buffer + bytes_written,
1656 			load_context->bytes_read - bytes_written);
1657 		load_context->bytes_read -= bytes_written;
1658 
1659 		g_output_stream_write_async (
1660 			output_stream,
1661 			load_context->buffer,
1662 			load_context->bytes_read,
1663 			G_PRIORITY_DEFAULT, cancellable,
1664 			(GAsyncReadyCallback) attachment_load_write_cb,
1665 			load_context);
1666 	} else
1667 		g_input_stream_read_async (
1668 			input_stream,
1669 			load_context->buffer,
1670 			sizeof (load_context->buffer),
1671 			G_PRIORITY_DEFAULT, cancellable,
1672 			(GAsyncReadyCallback) attachment_load_stream_read_cb,
1673 			load_context);
1674 }
1675 
1676 static void
1677 attachment_load_stream_read_cb (GInputStream *input_stream,
1678                                 GAsyncResult *result,
1679                                 LoadContext *load_context)
1680 {
1681 	EAttachment *attachment;
1682 	GCancellable *cancellable;
1683 	GOutputStream *output_stream;
1684 	gssize bytes_read;
1685 	GError *error = NULL;
1686 
1687 	bytes_read = g_input_stream_read_finish (
1688 		input_stream, result, &error);
1689 
1690 	if (attachment_load_check_for_error (load_context, error))
1691 		return;
1692 
1693 	if (bytes_read == 0) {
1694 		attachment_load_finish (load_context);
1695 		return;
1696 	}
1697 
1698 	attachment = load_context->attachment;
1699 	cancellable = attachment->priv->cancellable;
1700 	output_stream = load_context->output_stream;
1701 	load_context->bytes_read = bytes_read;
1702 
1703 	g_output_stream_write_async (
1704 		output_stream,
1705 		load_context->buffer,
1706 		load_context->bytes_read,
1707 		G_PRIORITY_DEFAULT, cancellable,
1708 		(GAsyncReadyCallback) attachment_load_write_cb,
1709 		load_context);
1710 }
1711 
1712 static void
1713 attachment_load_file_read_cb (GFile *file,
1714                               GAsyncResult *result,
1715                               LoadContext *load_context)
1716 {
1717 	EAttachment *attachment;
1718 	GCancellable *cancellable;
1719 	GFileInputStream *input_stream;
1720 	GOutputStream *output_stream;
1721 	GError *error = NULL;
1722 
1723 	/* Input stream might be NULL, so don't use cast macro. */
1724 	input_stream = g_file_read_finish (file, result, &error);
1725 	load_context->input_stream = (GInputStream *) input_stream;
1726 
1727 	if (attachment_load_check_for_error (load_context, error))
1728 		return;
1729 
1730 	/* Load the contents into a GMemoryOutputStream. */
1731 	output_stream = g_memory_output_stream_new (
1732 		NULL, 0, g_realloc, g_free);
1733 
1734 	attachment = load_context->attachment;
1735 	cancellable = attachment->priv->cancellable;
1736 	load_context->output_stream = output_stream;
1737 
1738 	g_input_stream_read_async (
1739 		load_context->input_stream,
1740 		load_context->buffer,
1741 		sizeof (load_context->buffer),
1742 		G_PRIORITY_DEFAULT, cancellable,
1743 		(GAsyncReadyCallback) attachment_load_stream_read_cb,
1744 		load_context);
1745 }
1746 
1747 static void
1748 attachment_load_query_info_cb (GFile *file,
1749                                GAsyncResult *result,
1750                                LoadContext *load_context)
1751 {
1752 	EAttachment *attachment;
1753 	GCancellable *cancellable;
1754 	GFileInfo *file_info;
1755 	GError *error = NULL;
1756 
1757 	attachment = load_context->attachment;
1758 	cancellable = attachment->priv->cancellable;
1759 
1760 	file_info = g_file_query_info_finish (file, result, &error);
1761 	if (attachment_load_check_for_error (load_context, error))
1762 		return;
1763 
1764 	e_attachment_set_file_info (attachment, file_info);
1765 	load_context->file_info = file_info;
1766 
1767 	load_context->total_num_bytes = g_file_info_get_size (file_info);
1768 
1769 	g_file_read_async (
1770 		file, G_PRIORITY_DEFAULT,
1771 		cancellable, (GAsyncReadyCallback)
1772 		attachment_load_file_read_cb, load_context);
1773 }
1774 
1775 #define ATTACHMENT_LOAD_CONTEXT "attachment-load-context-data"
1776 
1777 static void
1778 attachment_load_from_mime_part_thread (GSimpleAsyncResult *simple,
1779                                        GObject *object,
1780                                        GCancellable *cancellable)
1781 {
1782 	LoadContext *load_context;
1783 	GFileInfo *file_info;
1784 	EAttachment *attachment;
1785 	CamelContentType *content_type;
1786 	CamelMimePart *mime_part;
1787 	const gchar *attribute;
1788 	const gchar *string;
1789 	gchar *allocated, *decoded_string = NULL;
1790 	CamelStream *null;
1791 	CamelDataWrapper *dw;
1792 
1793 	load_context = g_object_get_data (G_OBJECT (simple), ATTACHMENT_LOAD_CONTEXT);
1794 	g_return_if_fail (load_context != NULL);
1795 	g_object_set_data (G_OBJECT (simple), ATTACHMENT_LOAD_CONTEXT, NULL);
1796 
1797 	attachment = load_context->attachment;
1798 	mime_part = e_attachment_get_mime_part (attachment);
1799 
1800 	file_info = g_file_info_new ();
1801 	load_context->file_info = file_info;
1802 
1803 	content_type = camel_mime_part_get_content_type (mime_part);
1804 	allocated = camel_content_type_simple (content_type);
1805 	if (allocated != NULL) {
1806 		GIcon *icon;
1807 		gchar *cp;
1808 
1809 		/* GIO expects lowercase MIME types. */
1810 		for (cp = allocated; *cp != '\0'; cp++)
1811 			*cp = g_ascii_tolower (*cp);
1812 
1813 		/* Swap the MIME type for a content type. */
1814 		cp = g_content_type_from_mime_type (allocated);
1815 		g_free (allocated);
1816 		allocated = cp;
1817 
1818 		/* Use the MIME part's filename if we have to. */
1819 		if (g_content_type_is_unknown (allocated)) {
1820 			string = camel_mime_part_get_filename (mime_part);
1821 			if (string != NULL) {
1822 				g_free (allocated);
1823 				allocated = g_content_type_guess (
1824 					string, NULL, 0, NULL);
1825 			}
1826 		}
1827 
1828 		g_file_info_set_content_type (file_info, allocated);
1829 
1830 		icon = g_content_type_get_icon (allocated);
1831 		if (icon != NULL) {
1832 			g_file_info_set_icon (file_info, icon);
1833 			g_object_unref (icon);
1834 		}
1835 	}
1836 	g_free (allocated);
1837 
1838 	/* Strip any path components from the filename. */
1839 	string = camel_mime_part_get_filename (mime_part);
1840 	if (string == NULL) {
1841 		/* Translators: Default attachment filename. */
1842 		string = _("attachment.dat");
1843 
1844 		if (camel_content_type_is (content_type, "message", "rfc822")) {
1845 			CamelMimeMessage *msg = NULL;
1846 			const gchar *subject = NULL;
1847 
1848 			if (CAMEL_IS_MIME_MESSAGE (mime_part)) {
1849 				msg = CAMEL_MIME_MESSAGE (mime_part);
1850 			} else {
1851 				CamelDataWrapper *content;
1852 
1853 				content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1854 				if (CAMEL_IS_MIME_MESSAGE (content))
1855 					msg = CAMEL_MIME_MESSAGE (content);
1856 			}
1857 
1858 			if (msg)
1859 				subject = camel_mime_message_get_subject (msg);
1860 
1861 			if (subject && *subject)
1862 				string = subject;
1863 		}
1864 	} else {
1865 		decoded_string = camel_header_decode_string (string, "UTF-8");
1866 		if (decoded_string && *decoded_string && !g_str_equal (decoded_string, string)) {
1867 			string = decoded_string;
1868 		} else {
1869 			g_free (decoded_string);
1870 			decoded_string = NULL;
1871 		}
1872 	}
1873 	allocated = g_path_get_basename (string);
1874 	g_file_info_set_display_name (file_info, allocated);
1875 	g_free (decoded_string);
1876 	g_free (allocated);
1877 
1878 	attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
1879 	string = camel_mime_part_get_description (mime_part);
1880 	if (string != NULL)
1881 		g_file_info_set_attribute_string (
1882 			file_info, attribute, string);
1883 
1884 	dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1885 	null = camel_stream_null_new ();
1886 	/* this actually downloads the part and makes it available later */
1887 	camel_data_wrapper_decode_to_stream_sync (dw, null, attachment->priv->cancellable, NULL);
1888 	g_file_info_set_size (file_info, CAMEL_STREAM_NULL (null)->written);
1889 	g_object_unref (null);
1890 
1891 	load_context->mime_part = g_object_ref (mime_part);
1892 
1893 	/* make sure it's freed on operation end */
1894 	g_object_unref (load_context->simple);
1895 	load_context->simple = NULL;
1896 
1897 	g_simple_async_result_set_op_res_gpointer (
1898 		simple, load_context,
1899 		(GDestroyNotify) attachment_load_context_free);
1900 }
1901 
1902 void
1903 e_attachment_load_async (EAttachment *attachment,
1904                          GAsyncReadyCallback callback,
1905                          gpointer user_data)
1906 {
1907 	LoadContext *load_context;
1908 	GCancellable *cancellable;
1909 	CamelMimePart *mime_part;
1910 	GFile *file;
1911 
1912 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
1913 
1914 	if (e_attachment_get_loading (attachment)) {
1915 		g_simple_async_report_error_in_idle (
1916 			G_OBJECT (attachment), callback, user_data,
1917 			G_IO_ERROR, G_IO_ERROR_BUSY,
1918 			_("A load operation is already in progress"));
1919 		return;
1920 	}
1921 
1922 	if (e_attachment_get_saving (attachment)) {
1923 		g_simple_async_report_error_in_idle (
1924 			G_OBJECT (attachment), callback, user_data,
1925 			G_IO_ERROR, G_IO_ERROR_BUSY,
1926 			_("A save operation is already in progress"));
1927 		return;
1928 	}
1929 
1930 	file = e_attachment_get_file (attachment);
1931 	mime_part = e_attachment_get_mime_part (attachment);
1932 	g_return_if_fail (file != NULL || mime_part != NULL);
1933 
1934 	load_context = attachment_load_context_new (
1935 		attachment, callback, user_data);
1936 
1937 	cancellable = attachment->priv->cancellable;
1938 	g_cancellable_reset (cancellable);
1939 
1940 	if (file != NULL) {
1941 		g_file_query_info_async (
1942 			file, ATTACHMENT_QUERY,
1943 			G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
1944 			cancellable, (GAsyncReadyCallback)
1945 			attachment_load_query_info_cb, load_context);
1946 
1947 	} else if (mime_part != NULL) {
1948 		g_object_set_data (G_OBJECT (load_context->simple), ATTACHMENT_LOAD_CONTEXT, load_context);
1949 
1950 		g_simple_async_result_run_in_thread (
1951 			load_context->simple,
1952 			attachment_load_from_mime_part_thread,
1953 			G_PRIORITY_DEFAULT,
1954 			cancellable);
1955 	}
1956 }
1957 
1958 gboolean
1959 e_attachment_load_finish (EAttachment *attachment,
1960                           GAsyncResult *result,
1961                           GError **error)
1962 {
1963 	GSimpleAsyncResult *simple;
1964 	const LoadContext *load_context;
1965 
1966 	g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1967 	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
1968 
1969 	simple = G_SIMPLE_ASYNC_RESULT (result);
1970 	load_context = g_simple_async_result_get_op_res_gpointer (simple);
1971 	if (load_context && load_context->mime_part) {
1972 		const gchar *string;
1973 
1974 		string = camel_mime_part_get_disposition (load_context->mime_part);
1975 		e_attachment_set_disposition (attachment, string);
1976 
1977 		e_attachment_set_file_info (attachment, load_context->file_info);
1978 		e_attachment_set_mime_part (attachment, load_context->mime_part);
1979 	}
1980 
1981 	g_simple_async_result_propagate_error (simple, error);
1982 
1983 	attachment_set_loading (attachment, FALSE);
1984 
1985 	return (load_context != NULL);
1986 }
1987 
1988 void
1989 e_attachment_load_handle_error (EAttachment *attachment,
1990                                 GAsyncResult *result,
1991                                 GtkWindow *parent)
1992 {
1993 	GtkWidget *dialog;
1994 	GFileInfo *file_info;
1995 	GtkTreeRowReference *reference;
1996 	const gchar *display_name;
1997 	const gchar *primary_text;
1998 	GError *error = NULL;
1999 
2000 	g_return_if_fail (E_IS_ATTACHMENT (attachment));
2001 	g_return_if_fail (G_IS_ASYNC_RESULT (result));
2002 	g_return_if_fail (!parent || GTK_IS_WINDOW (parent));
2003 
2004 	if (e_attachment_load_finish (attachment, result, &error))
2005 		return;
2006 
2007 	/* XXX Calling EAttachmentStore functions from here violates
2008 	 *     the abstraction, but for now it's not hurting anything. */
2009 	reference = e_attachment_get_reference (attachment);
2010 	if (gtk_tree_row_reference_valid (reference)) {
2011 		GtkTreeModel *model;
2012 
2013 		model = gtk_tree_row_reference_get_model (reference);
2014 
2015 		e_attachment_store_remove_attachment (
2016 			E_ATTACHMENT_STORE (model), attachment);
2017 	}
2018 
2019 	/* Ignore cancellations. */
2020 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2021 		g_error_free (error);
2022 		return;
2023 	}
2024 
2025 	file_info = e_attachment_get_file_info (attachment);
2026 
2027 	if (file_info != NULL)
2028 		display_name = g_file_info_get_display_name (file_info);
2029 	else
2030 		display_name = NULL;
2031 
2032 	if (display_name != NULL)
2033 		primary_text = g_strdup_printf (
2034 			_("Could not load '%s'"), display_name);
2035 	else
2036 		primary_text = g_strdup_printf (
2037 			_("Could not load the attachment"));
2038 
2039 	dialog = gtk_message_dialog_new_with_markup (
2040 		parent, GTK_DIALOG_DESTROY_WITH_PARENT,
2041 		GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
2042 		"<big><b>%s</b></big>", primary_text);
2043 
2044 	gtk_message_dialog_format_secondary_text (
2045 		GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2046 2047 gtk_dialog_run (GTK_DIALOG (dialog)); 2048 2049 gtk_widget_destroy (dialog); 2050 g_error_free (error); 2051 } 2052 2053 gboolean 2054 e_attachment_load (EAttachment *attachment, 2055 GError **error) 2056 { 2057 EAsyncClosure *closure; 2058 GAsyncResult *result; 2059 gboolean success; 2060 2061 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); 2062 2063 closure = e_async_closure_new (); 2064 2065 e_attachment_load_async (attachment, e_async_closure_callback, closure); 2066 2067 result = e_async_closure_wait (closure); 2068 2069 success = e_attachment_load_finish (attachment, result, error); 2070 2071 e_async_closure_free (closure); 2072 2073 return success; 2074 } 2075 2076 /************************* e_attachment_open_async() *************************/ 2077 2078 typedef struct _OpenContext OpenContext; 2079 2080 struct _OpenContext { 2081 EAttachment *attachment; 2082 GSimpleAsyncResult *simple; 2083 2084 GAppInfo *app_info; 2085 }; 2086 2087 static OpenContext * 2088 attachment_open_context_new (EAttachment *attachment, 2089 GAsyncReadyCallback callback, 2090 gpointer user_data) 2091 { 2092 OpenContext *open_context; 2093 GSimpleAsyncResult *simple; 2094 2095 simple = g_simple_async_result_new ( 2096 G_OBJECT (attachment), callback, 2097 user_data, e_attachment_open_async); 2098 2099 open_context = g_slice_new0 (OpenContext); 2100 open_context->attachment = g_object_ref (attachment); 2101 open_context->simple = simple; 2102 2103 return open_context; 2104 } 2105 2106 static void 2107 attachment_open_context_free (OpenContext *open_context) 2108 { 2109 g_object_unref (open_context->attachment); 2110 g_object_unref (open_context->simple); 2111 2112 if (open_context->app_info != NULL) 2113 g_object_unref (open_context->app_info); 2114 2115 g_slice_free (OpenContext, open_context); 2116 } 2117 2118 static gboolean 2119 attachment_open_check_for_error (OpenContext *open_context, 2120 GError *error) 2121 { 2122 GSimpleAsyncResult *simple; 2123 2124 if (error == NULL) 2125 return FALSE; 2126 2127 simple = open_context->simple; 2128 g_simple_async_result_take_error (simple, error); 2129 g_simple_async_result_complete (simple); 2130 2131 attachment_open_context_free (open_context); 2132 2133 return TRUE; 2134 } 2135 2136 static void 2137 attachment_open_file (GFile *file, 2138 OpenContext *open_context) 2139 { 2140 GdkAppLaunchContext *context; 2141 GSimpleAsyncResult *simple; 2142 gboolean success; 2143 GError *error = NULL; 2144 2145 simple = open_context->simple; 2146 2147 context = gdk_app_launch_context_new (); 2148 2149 if (open_context->app_info != NULL) { 2150 GList *file_list; 2151 2152 file_list = g_list_prepend (NULL, file); 2153 success = g_app_info_launch ( 2154 open_context->app_info, file_list, 2155 G_APP_LAUNCH_CONTEXT (context), &error); 2156 g_list_free (file_list); 2157 } else { 2158 gchar *uri; 2159 2160 uri = g_file_get_uri (file); 2161 success = g_app_info_launch_default_for_uri ( 2162 uri, G_APP_LAUNCH_CONTEXT (context), &error); 2163 g_free (uri); 2164 } 2165 2166 g_object_unref (context); 2167 2168 g_simple_async_result_set_op_res_gboolean (simple, success); 2169 2170 if (error != NULL) 2171 g_simple_async_result_take_error (simple, error); 2172 2173 g_simple_async_result_complete (simple); 2174 attachment_open_context_free (open_context); 2175 } 2176 2177 static void 2178 attachment_open_save_finished_cb (EAttachment *attachment, 2179 GAsyncResult *result, 2180 OpenContext *open_context) 2181 { 2182 GFile *file; 2183 gchar *path; 2184 GError *error = NULL; 2185 2186 file = e_attachment_save_finish (attachment, result, &error); 2187 2188 if (attachment_open_check_for_error (open_context, error)) 2189 return; 2190 2191 /* Make the temporary file read-only. 2192 * 2193 * This step is non-critical, so if an error occurs just 2194 * emit a warning and move on. 2195 * 2196 * XXX I haven't figured out how to do this through GIO. 2197 * Attempting to set the "access::can-write" attribute via 2198 * g_file_set_attribute() returned G_IO_ERROR_NOT_SUPPORTED 2199 * and the only other possibility I see is "unix::mode", 2200 * which is obviously not portable. 2201 */ 2202 path = g_file_get_path (file); 2203 #ifndef G_OS_WIN32 2204 if (g_chmod (path, S_IRUSR | S_IRGRP | S_IROTH) < 0) 2205 g_warning ("%s", g_strerror (errno)); 2206 #endif 2207 g_free (path); 2208 2209 attachment_open_file (file, open_context); 2210 g_object_unref (file); 2211 } 2212 2213 static void 2214 attachment_open_save_temporary (OpenContext *open_context) 2215 { 2216 GFile *temp_directory; 2217 gchar *template; 2218 gchar *path; 2219 GError *error = NULL; 2220 2221 errno = 0; 2222 2223 /* Save the file to a temporary directory. 2224 * We use a directory so the files can retain their basenames. 2225 * XXX This could trigger a blocking temp directory cleanup. */ 2226 template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ()); 2227 path = e_mkdtemp (template); 2228 g_free (template); 2229 2230 /* XXX Let's hope errno got set properly. */ 2231 if (path == NULL) 2232 g_set_error ( 2233 &error, G_FILE_ERROR, 2234 g_file_error_from_errno (errno), 2235 "%s", g_strerror (errno)); 2236 2237 /* We already know if there's an error, but this does the cleanup. */ 2238 if (attachment_open_check_for_error (open_context, error)) 2239 return; 2240 2241 temp_directory = g_file_new_for_path (path); 2242 2243 e_attachment_save_async ( 2244 open_context->attachment, 2245 temp_directory, (GAsyncReadyCallback) 2246 attachment_open_save_finished_cb, open_context); 2247 2248 g_object_unref (temp_directory); 2249 g_free (path); 2250 } 2251 2252 void 2253 e_attachment_open_async (EAttachment *attachment, 2254 GAppInfo *app_info, 2255 GAsyncReadyCallback callback, 2256 gpointer user_data) 2257 { 2258 OpenContext *open_context; 2259 CamelMimePart *mime_part; 2260 GFile *file; 2261 2262 g_return_if_fail (E_IS_ATTACHMENT (attachment)); 2263 2264 file = e_attachment_get_file (attachment); 2265 mime_part = e_attachment_get_mime_part (attachment); 2266 g_return_if_fail (file != NULL || mime_part != NULL); 2267 2268 open_context = attachment_open_context_new ( 2269 attachment, callback, user_data); 2270 2271 if (G_IS_APP_INFO (app_info)) 2272 open_context->app_info = g_object_ref (app_info); 2273 2274 /* If the attachment already references a GFile, we can launch 2275 * the application directly. Otherwise we have to save the MIME 2276 * part to a temporary file and launch the application from that. */ 2277 if (file != NULL) { 2278 attachment_open_file (file, open_context); 2279 2280 } else if (mime_part != NULL) 2281 attachment_open_save_temporary (open_context); 2282 } 2283 2284 gboolean 2285 e_attachment_open_finish (EAttachment *attachment, 2286 GAsyncResult *result, 2287 GError **error) 2288 { 2289 GSimpleAsyncResult *simple; 2290 gboolean success; 2291 2292 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); 2293 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); 2294 2295 simple = G_SIMPLE_ASYNC_RESULT (result); 2296 success = g_simple_async_result_get_op_res_gboolean (simple); 2297 g_simple_async_result_propagate_error (simple, error); 2298 2299 return success; 2300 } 2301 2302 void 2303 e_attachment_open_handle_error (EAttachment *attachment, 2304 GAsyncResult *result, 2305 GtkWindow *parent) 2306 { 2307 GtkWidget *dialog; 2308 GFileInfo *file_info; 2309 const gchar *display_name; 2310 const gchar *primary_text; 2311 GError *error = NULL; 2312 2313 g_return_if_fail (E_IS_ATTACHMENT (attachment)); 2314 g_return_if_fail (G_IS_ASYNC_RESULT (result)); 2315 g_return_if_fail (GTK_IS_WINDOW (parent)); 2316 2317 if (e_attachment_open_finish (attachment, result, &error)) 2318 return; 2319 2320 /* Ignore cancellations. */ 2321 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) 2322 return; 2323 2324 file_info = e_attachment_get_file_info (attachment); 2325 2326 if (file_info != NULL) 2327 display_name = g_file_info_get_display_name (file_info); 2328 else 2329 display_name = NULL; 2330 2331 if (display_name != NULL) 2332 primary_text = g_strdup_printf ( 2333 _("Could not open '%s'"), display_name); 2334 else 2335 primary_text = g_strdup_printf ( 2336 _("Could not open the attachment")); 2337 2338 dialog = gtk_message_dialog_new_with_markup ( 2339 parent, GTK_DIALOG_DESTROY_WITH_PARENT, 2340 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, 2341 "<big><b>%s</b></big>", primary_text); 2342 2343 gtk_message_dialog_format_secondary_text ( 2344 GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2345 2346 gtk_dialog_run (GTK_DIALOG (dialog)); 2347 2348 gtk_widget_destroy (dialog); 2349 g_error_free (error); 2350 } 2351 2352 gboolean 2353 e_attachment_open (EAttachment *attachment, 2354 GAppInfo *app_info, 2355 GError **error) 2356 { 2357 EAsyncClosure *closure; 2358 GAsyncResult *result; 2359 gboolean success; 2360 2361 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); 2362 2363 closure = e_async_closure_new (); 2364 2365 e_attachment_open_async (attachment, app_info, e_async_closure_callback, closure); 2366 2367 result = e_async_closure_wait (closure); 2368 2369 success = e_attachment_open_finish (attachment, result, error); 2370 2371 e_async_closure_free (closure); 2372 2373 return success; 2374 } 2375 2376 /************************* e_attachment_save_async() *************************/ 2377 2378 typedef struct _SaveContext SaveContext; 2379 2380 struct _SaveContext { 2381 EAttachment *attachment; 2382 GSimpleAsyncResult *simple; 2383 2384 GFile *directory; 2385 GFile *destination; 2386 GInputStream *input_stream; 2387 GOutputStream *output_stream; 2388 goffset total_num_bytes; 2389 gssize bytes_read; 2390 gchar buffer[4096]; 2391 gint count; 2392 }; 2393 2394 /* Forward Declaration */ 2395 static void 2396 attachment_save_read_cb (GInputStream *input_stream, 2397 GAsyncResult *result, 2398 SaveContext *save_context); 2399 2400 static SaveContext * 2401 attachment_save_context_new (EAttachment *attachment, 2402 GAsyncReadyCallback callback, 2403 gpointer user_data) 2404 { 2405 SaveContext *save_context; 2406 GSimpleAsyncResult *simple; 2407 2408 simple = g_simple_async_result_new ( 2409 G_OBJECT (attachment), callback, 2410 user_data, e_attachment_save_async); 2411 2412 save_context = g_slice_new0 (SaveContext); 2413 save_context->attachment = g_object_ref (attachment); 2414 save_context->simple = simple; 2415 2416 attachment_set_saving (save_context->attachment, TRUE); 2417 2418 return save_context; 2419 } 2420 2421 static void 2422 attachment_save_context_free (SaveContext *save_context) 2423 { 2424 g_object_unref (save_context->attachment); 2425 g_object_unref (save_context->simple); 2426 2427 if (save_context->directory != NULL) 2428 g_object_unref (save_context->directory); 2429 2430 if (save_context->destination != NULL) 2431 g_object_unref (save_context->destination); 2432 2433 if (save_context->input_stream != NULL) 2434 g_object_unref (save_context->input_stream); 2435 2436 if (save_context->output_stream != NULL) 2437 g_object_unref (save_context->output_stream); 2438 2439 g_slice_free (SaveContext, save_context); 2440 } 2441 2442 static gboolean 2443 attachment_save_check_for_error (SaveContext *save_context, 2444 GError *error) 2445 { 2446 GSimpleAsyncResult *simple; 2447 2448 if (error == NULL) 2449 return FALSE; 2450 2451 simple = save_context->simple; 2452 g_simple_async_result_take_error (simple, error); 2453 g_simple_async_result_complete (simple); 2454 2455 attachment_save_context_free (save_context); 2456 2457 return TRUE; 2458 } 2459 2460 static GFile * 2461 attachment_save_new_candidate (SaveContext *save_context) 2462 { 2463 GFile *candidate; 2464 GFileInfo *file_info; 2465 EAttachment *attachment; 2466 const gchar *display_name = NULL; 2467 gchar *basename; 2468 2469 attachment = save_context->attachment; 2470 file_info = e_attachment_get_file_info (attachment); 2471 2472 if (file_info != NULL) 2473 display_name = g_file_info_get_display_name (file_info); 2474 if (display_name == NULL) 2475 /* Translators: Default attachment filename. */ 2476 display_name = _("attachment.dat"); 2477 2478 if (save_context->count == 0) 2479 basename = g_strdup (display_name); 2480 else { 2481 GString *string; 2482 const gchar *ext; 2483 gsize length; 2484 2485 string = g_string_sized_new (strlen (display_name)); 2486 ext = g_utf8_strchr (display_name, -1, '.'); 2487 2488 if (ext != NULL) 2489 length = ext - display_name; 2490 else 2491 length = strlen (display_name); 2492 2493 g_string_append_len (string, display_name, length); 2494 g_string_append_printf (string, " (%d)", save_context->count); 2495 g_string_append (string, (ext != NULL) ? ext : ""); 2496 2497 basename = g_string_free (string, FALSE); 2498 } 2499 2500 save_context->count++; 2501 2502 candidate = g_file_get_child (save_context->directory, basename); 2503 2504 g_free (basename); 2505 2506 return candidate; 2507 } 2508 2509 static void 2510 attachment_save_write_cb (GOutputStream *output_stream, 2511 GAsyncResult *result, 2512 SaveContext *save_context) 2513 { 2514 EAttachment *attachment; 2515 GCancellable *cancellable; 2516 GInputStream *input_stream; 2517 gssize bytes_written; 2518 GError *error = NULL; 2519 2520 bytes_written = g_output_stream_write_finish ( 2521 output_stream, result, &error); 2522 2523 if (attachment_save_check_for_error (save_context, error)) 2524 return; 2525 2526 attachment = save_context->attachment; 2527 cancellable = attachment->priv->cancellable; 2528 input_stream = save_context->input_stream; 2529 2530 if (bytes_written < save_context->bytes_read) { 2531 g_memmove ( 2532 save_context->buffer, 2533 save_context->buffer + bytes_written, 2534 save_context->bytes_read - bytes_written); 2535 save_context->bytes_read -= bytes_written; 2536 2537 g_output_stream_write_async ( 2538 output_stream, 2539 save_context->buffer, 2540 save_context->bytes_read, 2541 G_PRIORITY_DEFAULT, cancellable, 2542 (GAsyncReadyCallback) attachment_save_write_cb, 2543 save_context); 2544 } else 2545 g_input_stream_read_async ( 2546 input_stream, 2547 save_context->buffer, 2548 sizeof (save_context->buffer), 2549 G_PRIORITY_DEFAULT, cancellable, 2550 (GAsyncReadyCallback) attachment_save_read_cb, 2551 save_context); 2552 } 2553 2554 static void 2555 attachment_save_read_cb (GInputStream *input_stream, 2556 GAsyncResult *result, 2557 SaveContext *save_context) 2558 { 2559 EAttachment *attachment; 2560 GCancellable *cancellable; 2561 GOutputStream *output_stream; 2562 gssize bytes_read; 2563 GError *error = NULL; 2564 2565 bytes_read = g_input_stream_read_finish ( 2566 input_stream, result, &error); 2567 2568 if (attachment_save_check_for_error (save_context, error)) 2569 return; 2570 2571 if (bytes_read == 0) { 2572 GSimpleAsyncResult *simple; 2573 GFile *destination; 2574 2575 /* Steal the destination. */ 2576 destination = save_context->destination; 2577 save_context->destination = NULL; 2578 2579 simple = save_context->simple; 2580 g_simple_async_result_set_op_res_gpointer ( 2581 simple, destination, (GDestroyNotify) g_object_unref); 2582 g_simple_async_result_complete (simple); 2583 2584 attachment_save_context_free (save_context); 2585 2586 return; 2587 } 2588 2589 attachment = save_context->attachment; 2590 cancellable = attachment->priv->cancellable; 2591 output_stream = save_context->output_stream; 2592 save_context->bytes_read = bytes_read; 2593 2594 attachment_progress_cb ( 2595 g_seekable_tell (G_SEEKABLE (input_stream)), 2596 save_context->total_num_bytes, attachment); 2597 2598 g_output_stream_write_async ( 2599 output_stream, 2600 save_context->buffer, 2601 save_context->bytes_read, 2602 G_PRIORITY_DEFAULT, cancellable, 2603 (GAsyncReadyCallback) attachment_save_write_cb, 2604 save_context); 2605 } 2606 2607 static void 2608 attachment_save_got_output_stream (SaveContext *save_context) 2609 { 2610 GCancellable *cancellable; 2611 GInputStream *input_stream; 2612 CamelDataWrapper *wrapper; 2613 CamelMimePart *mime_part; 2614 CamelStream *stream; 2615 EAttachment *attachment; 2616 GByteArray *buffer; 2617 2618 attachment = save_context->attachment; 2619 cancellable = attachment->priv->cancellable; 2620 mime_part = e_attachment_get_mime_part (attachment); 2621 2622 /* Decode the MIME part to an in-memory buffer. We have to do 2623 * this because CamelStream is synchronous-only, and using threads 2624 * is dangerous because CamelDataWrapper is not reentrant. */ 2625 buffer = g_byte_array_new (); 2626 stream = camel_stream_mem_new (); 2627 camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buffer); 2628 wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); 2629 camel_data_wrapper_decode_to_stream_sync (wrapper, stream, NULL, NULL); 2630 g_object_unref (stream); 2631 2632 /* Load the buffer into a GMemoryInputStream. 2633 * But watch out for zero length MIME parts. */ 2634 input_stream = g_memory_input_stream_new (); 2635 if (buffer->len > 0) 2636 g_memory_input_stream_add_data ( 2637 G_MEMORY_INPUT_STREAM (input_stream), 2638 buffer->data, (gssize) buffer->len, 2639 (GDestroyNotify) g_free); 2640 save_context->input_stream = input_stream; 2641 save_context->total_num_bytes = (goffset) buffer->len; 2642 g_byte_array_free (buffer, FALSE); 2643 2644 g_input_stream_read_async ( 2645 input_stream, 2646 save_context->buffer, 2647 sizeof (save_context->buffer), 2648 G_PRIORITY_DEFAULT, cancellable, 2649 (GAsyncReadyCallback) attachment_save_read_cb, 2650 save_context); 2651 } 2652 2653 static void 2654 attachment_save_create_cb (GFile *destination, 2655 GAsyncResult *result, 2656 SaveContext *save_context) 2657 { 2658 EAttachment *attachment; 2659 GCancellable *cancellable; 2660 GFileOutputStream *output_stream; 2661 GError *error = NULL; 2662 2663 /* Output stream might be NULL, so don't use cast macro. */ 2664 output_stream = g_file_create_finish (destination, result, &error); 2665 save_context->output_stream = (GOutputStream *) output_stream; 2666 2667 attachment = save_context->attachment; 2668 cancellable = attachment->priv->cancellable; 2669 2670 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { 2671 destination = attachment_save_new_candidate (save_context); 2672 2673 g_file_create_async ( 2674 destination, G_FILE_CREATE_NONE, 2675 G_PRIORITY_DEFAULT, cancellable, 2676 (GAsyncReadyCallback) attachment_save_create_cb, 2677 save_context); 2678 2679 g_object_unref (destination); 2680 g_error_free (error); 2681 return; 2682 } 2683 2684 if (attachment_save_check_for_error (save_context, error)) 2685 return; 2686 2687 save_context->destination = g_object_ref (destination); 2688 attachment_save_got_output_stream (save_context); 2689 } 2690 2691 static void 2692 attachment_save_replace_cb (GFile *destination, 2693 GAsyncResult *result, 2694 SaveContext *save_context) 2695 { 2696 GFileOutputStream *output_stream; 2697 GError *error = NULL; 2698 2699 /* Output stream might be NULL, so don't use cast macro. */ 2700 output_stream = g_file_replace_finish (destination, result, &error); 2701 save_context->output_stream = (GOutputStream *) output_stream; 2702 2703 if (attachment_save_check_for_error (save_context, error)) 2704 return; 2705 2706 save_context->destination = g_object_ref (destination); 2707 attachment_save_got_output_stream (save_context); 2708 } 2709 2710 static void 2711 attachment_save_query_info_cb (GFile *destination, 2712 GAsyncResult *result, 2713 SaveContext *save_context) 2714 { 2715 EAttachment *attachment; 2716 GCancellable *cancellable; 2717 GFileInfo *file_info; 2718 GFileType file_type; 2719 GError *error = NULL; 2720 2721 attachment = save_context->attachment; 2722 cancellable = attachment->priv->cancellable; 2723 2724 file_info = g_file_query_info_finish (destination, result, &error); 2725 2726 /* G_IO_ERROR_NOT_FOUND just means we're creating a new file. */ 2727 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { 2728 g_error_free (error); 2729 goto replace; 2730 } 2731 2732 if (attachment_save_check_for_error (save_context, error)) 2733 return; 2734 2735 file_type = g_file_info_get_file_type (file_info); 2736 g_object_unref (file_info); 2737 2738 if (file_type == G_FILE_TYPE_DIRECTORY) { 2739 save_context->directory = g_object_ref (destination); 2740 destination = attachment_save_new_candidate (save_context); 2741 2742 g_file_create_async ( 2743 destination, G_FILE_CREATE_NONE, 2744 G_PRIORITY_DEFAULT, cancellable, 2745 (GAsyncReadyCallback) attachment_save_create_cb, 2746 save_context); 2747 2748 g_object_unref (destination); 2749 2750 return; 2751 } 2752 2753 replace: 2754 g_file_replace_async ( 2755 destination, NULL, FALSE, 2756 G_FILE_CREATE_REPLACE_DESTINATION, 2757 G_PRIORITY_DEFAULT, cancellable, 2758 (GAsyncReadyCallback) attachment_save_replace_cb, 2759 save_context); 2760 } 2761 2762 void 2763 e_attachment_save_async (EAttachment *attachment, 2764 GFile *destination, 2765 GAsyncReadyCallback callback, 2766 gpointer user_data) 2767 { 2768 SaveContext *save_context; 2769 GCancellable *cancellable; 2770 2771 g_return_if_fail (E_IS_ATTACHMENT (attachment)); 2772 g_return_if_fail (G_IS_FILE (destination)); 2773 2774 if (e_attachment_get_loading (attachment)) { 2775 g_simple_async_report_error_in_idle ( 2776 G_OBJECT (attachment), callback, user_data, 2777 G_IO_ERROR, G_IO_ERROR_BUSY, 2778 _("A load operation is already in progress")); 2779 return; 2780 } 2781 2782 if (e_attachment_get_saving (attachment)) { 2783 g_simple_async_report_error_in_idle ( 2784 G_OBJECT (attachment), callback, user_data, 2785 G_IO_ERROR, G_IO_ERROR_BUSY, 2786 _("A save operation is already in progress")); 2787 return; 2788 } 2789 2790 if (e_attachment_get_mime_part (attachment) == NULL) { 2791 g_simple_async_report_error_in_idle ( 2792 G_OBJECT (attachment), callback, user_data, 2793 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, 2794 _("Attachment contents not loaded")); 2795 return; 2796 } 2797 2798 save_context = attachment_save_context_new ( 2799 attachment, callback, user_data); 2800 2801 cancellable = attachment->priv->cancellable; 2802 g_cancellable_reset (cancellable); 2803 2804 /* First we need to know if destination is a directory. */ 2805 g_file_query_info_async ( 2806 destination, G_FILE_ATTRIBUTE_STANDARD_TYPE, 2807 G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 2808 cancellable, (GAsyncReadyCallback) 2809 attachment_save_query_info_cb, save_context); 2810 } 2811 2812 GFile * 2813 e_attachment_save_finish (EAttachment *attachment, 2814 GAsyncResult *result, 2815 GError **error) 2816 { 2817 GSimpleAsyncResult *simple; 2818 GFile *destination; 2819 2820 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); 2821 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); 2822 2823 simple = G_SIMPLE_ASYNC_RESULT (result); 2824 destination = g_simple_async_result_get_op_res_gpointer (simple); 2825 if (destination != NULL) 2826 g_object_ref (destination); 2827 g_simple_async_result_propagate_error (simple, error); 2828 2829 attachment_set_saving (attachment, FALSE); 2830 2831 return destination; 2832 } 2833 2834 void 2835 e_attachment_save_handle_error (EAttachment *attachment, 2836 GAsyncResult *result, 2837 GtkWindow *parent) 2838 { 2839 GFile *file; 2840 GFileInfo *file_info; 2841 GtkWidget *dialog; 2842 const gchar *display_name; 2843 const gchar *primary_text; 2844 GError *error = NULL; 2845 2846 g_return_if_fail (E_IS_ATTACHMENT (attachment)); 2847 g_return_if_fail (G_IS_ASYNC_RESULT (result)); 2848 g_return_if_fail (GTK_IS_WINDOW (parent)); 2849 2850 file = e_attachment_save_finish (attachment, result, &error); 2851 2852 if (file != NULL) { 2853 g_object_unref (file); 2854 return; 2855 } 2856 2857 /* Ignore cancellations. */ 2858 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) 2859 return; 2860 2861 file_info = e_attachment_get_file_info (attachment); 2862 2863 if (file_info != NULL) 2864 display_name = g_file_info_get_display_name (file_info); 2865 else 2866 display_name = NULL; 2867 2868 if (display_name != NULL) 2869 primary_text = g_strdup_printf ( 2870 _("Could not save '%s'"), display_name); 2871 else 2872 primary_text = g_strdup_printf ( 2873 _("Could not save the attachment")); 2874 2875 dialog = gtk_message_dialog_new_with_markup ( 2876 parent, GTK_DIALOG_DESTROY_WITH_PARENT, 2877 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, 2878 "<big><b>%s</b></big>", primary_text); 2879 2880 gtk_message_dialog_format_secondary_text ( 2881 GTK_MESSAGE_DIALOG (dialog), "%s", error->message); 2882 2883 gtk_dialog_run (GTK_DIALOG (dialog)); 2884 2885 gtk_widget_destroy (dialog); 2886 g_error_free (error); 2887 } 2888 2889 gboolean 2890 e_attachment_save (EAttachment *attachment, 2891 GFile *in_destination, 2892 GFile **out_destination, 2893 GError **error) 2894 { 2895 EAsyncClosure *closure; 2896 GAsyncResult *result; 2897 2898 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); 2899 g_return_val_if_fail (out_destination != NULL, FALSE); 2900 2901 closure = e_async_closure_new (); 2902 2903 e_attachment_save_async (attachment, in_destination, e_async_closure_callback, closure); 2904 2905 result = e_async_closure_wait (closure); 2906 2907 *out_destination = e_attachment_save_finish (attachment, result, error); 2908 2909 e_async_closure_free (closure); 2910 2911 return *out_destination != NULL; 2912 }