evolution-3.6.4/plugins/templates/templates.c

Location Tool Test ID Function Issue
templates.c:861:4 clang-analyzer Value stored to 'new_content_type' is never read
templates.c:861:4 clang-analyzer Value stored to 'new_content_type' is never read
   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Diego Escalante Urrelo <diegoe@gnome.org>
  18  *		Bharath Acharya <abharath@novell.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  * Copyright (C) 2008 - Diego Escalante Urrelo
  22  *
  23  */
  24 
  25 #ifdef HAVE_CONFIG_H
  26 #include <config.h>
  27 #endif
  28 
  29 #include <gtk/gtk.h>
  30 #include <glib/gi18n.h>
  31 #include <string.h>
  32 
  33 #include <libevolution-utils/e-alert-dialog.h>
  34 #include <e-util/e-config.h>
  35 #include <e-util/e-plugin.h>
  36 #include <e-util/e-util.h>
  37 
  38 #include <shell/e-shell-view.h>
  39 
  40 #include <libemail-engine/e-mail-session-utils.h>
  41 #include <libemail-engine/mail-ops.h>
  42 
  43 #include <mail/e-mail-reader.h>
  44 #include <mail/em-composer-utils.h>
  45 #include <mail/em-utils.h>
  46 #include <mail/message-list.h>
  47 
  48 #include <composer/e-msg-composer.h>
  49 
  50 #define CONF_KEY_TEMPLATE_PLACEHOLDERS "template-placeholders"
  51 
  52 typedef struct _AsyncContext AsyncContext;
  53 
  54 struct _AsyncContext {
  55 	EActivity *activity;
  56 	EMailReader *reader;
  57 	CamelMimeMessage *message;
  58 	CamelFolder *template_folder;
  59 	gchar *message_uid;
  60 	gchar *template_message_uid;
  61 };
  62 
  63 typedef struct {
  64 	GSettings   *settings;
  65 	GtkWidget   *treeview;
  66 	GtkWidget   *clue_add;
  67 	GtkWidget   *clue_edit;
  68 	GtkWidget   *clue_remove;
  69 	GtkListStore *store;
  70 } UIData;
  71 
  72 enum {
  73 	CLUE_KEYWORD_COLUMN,
  74 	CLUE_VALUE_COLUMN,
  75 	CLUE_N_COLUMNS
  76 };
  77 
  78 GtkWidget *	e_plugin_lib_get_configure_widget
  79 						(EPlugin *plugin);
  80 gboolean	init_composer_actions		(GtkUIManager *ui_manager,
  81 						 EMsgComposer *composer);
  82 gboolean	init_shell_actions		(GtkUIManager *ui_manager,
  83 						 EShellWindow *shell_window);
  84 gint		e_plugin_lib_enable		(EPlugin *plugin,
  85 						 gboolean enabled);
  86 
  87 /* Thanks to attachment reminder plugin for this*/
  88 static void commit_changes (UIData *ui);
  89 
  90 static void  key_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string,
  91 				   gchar *new_text,UIData *ui);
  92 
  93 static void  value_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string,
  94 				   gchar *new_text,UIData *ui);
  95 
  96 static gboolean clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath
  97 					*path, GtkTreeIter *iter, UIData *ui);
  98 
  99 static void templates_folder_msg_changed_cb (CamelFolder *folder,
 100 			      CamelFolderChangeInfo *change_info,
 101 			      EShellWindow *shell_window);
 102 
 103 static gboolean plugin_enabled;
 104 
 105 static void
 106 async_context_free (AsyncContext *context)
 107 {
 108 	if (context->activity != NULL)
 109 		g_object_unref (context->activity);
 110 
 111 	if (context->reader != NULL)
 112 		g_object_unref (context->reader);
 113 
 114 	if (context->message != NULL)
 115 		g_object_unref (context->message);
 116 
 117 	if (context->template_folder != NULL)
 118 		g_object_unref (context->template_folder);
 119 
 120 	g_free (context->message_uid);
 121 	g_free (context->template_message_uid);
 122 
 123 	g_slice_free (AsyncContext, context);
 124 }
 125 
 126 static void
 127 selection_changed (GtkTreeSelection *selection,
 128                    UIData *ui)
 129 {
 130 	GtkTreeModel *model;
 131 	GtkTreeIter iter;
 132 
 133 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 134 		gtk_widget_set_sensitive (ui->clue_edit, TRUE);
 135 		gtk_widget_set_sensitive (ui->clue_remove, TRUE);
 136 	} else {
 137 		gtk_widget_set_sensitive (ui->clue_edit, FALSE);
 138 		gtk_widget_set_sensitive (ui->clue_remove, FALSE);
 139 	}
 140 }
 141 
 142 static void
 143 destroy_ui_data (gpointer data)
 144 {
 145 	UIData *ui = (UIData *) data;
 146 
 147 	if (!ui)
 148 		return;
 149 
 150 	g_object_unref (ui->settings);
 151 	g_free (ui);
 152 }
 153 
 154 static void
 155 commit_changes (UIData *ui)
 156 {
 157 	GtkTreeModel *model = NULL;
 158 	GVariantBuilder b;
 159 	GtkTreeIter iter;
 160 	gboolean valid;
 161 	GVariant *v;
 162 
 163 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 164 	valid = gtk_tree_model_get_iter_first (model, &iter);
 165 
 166 	g_variant_builder_init (&b, G_VARIANT_TYPE ("as"));
 167 	while (valid) {
 168 		gchar *keyword, *value;
 169 		gchar *key;
 170 
 171 		gtk_tree_model_get (
 172 			model, &iter,
 173 			CLUE_KEYWORD_COLUMN, &keyword,
 174 			CLUE_VALUE_COLUMN, &value,
 175 			-1);
 176 
 177 		/* Check if the keyword and value are not empty */
 178 		if ((keyword) && (value) && (g_utf8_strlen (g_strstrip (keyword), -1) > 0)
 179 			&& (g_utf8_strlen (g_strstrip (value), -1) > 0)) {
 180 			key = g_strdup_printf ("%s=%s", keyword, value);
 181 			g_variant_builder_add (&b, "s", key);
 182 		}
 183 
 184 		g_free (keyword);
 185 		g_free (value);
 186 
 187 		valid = gtk_tree_model_iter_next (model, &iter);
 188 	}
 189 
 190 	v = g_variant_builder_end (&b);
 191 	g_settings_set_value (ui->settings, CONF_KEY_TEMPLATE_PLACEHOLDERS, v);
 192 	g_variant_unref (v);
 193 }
 194 
 195 static void
 196 clue_check_isempty (GtkTreeModel *model,
 197                     GtkTreePath *path,
 198                     GtkTreeIter *iter,
 199                     UIData *ui)
 200 {
 201 	GtkTreeSelection *selection;
 202 	gchar *keyword = NULL;
 203 	gboolean valid;
 204 
 205 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 206 	/* move to the previous node */
 207 	valid = gtk_tree_path_prev (path);
 208 
 209 	gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
 210 	if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0))
 211 		gtk_list_store_remove (ui->store, iter);
 212 
 213 	/* Check if we have a valid row to select. If not, then select
 214 	 * the previous row */
 215 	if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), iter)) {
 216 		gtk_tree_selection_select_iter (selection, iter);
 217 	} else {
 218 		if (path && valid) {
 219 			gtk_tree_model_get_iter (model, iter, path);
 220 			gtk_tree_selection_select_iter (selection, iter);
 221 		}
 222 	}
 223 
 224 	gtk_widget_grab_focus (ui->treeview);
 225 	g_free (keyword);
 226 }
 227 
 228 static gboolean
 229 clue_foreach_check_isempty (GtkTreeModel *model,
 230                             GtkTreePath *path,
 231                             GtkTreeIter *iter,
 232                             UIData *ui)
 233 {
 234 	gboolean valid;
 235 
 236 	valid = gtk_tree_model_get_iter_first (model, iter);
 237 	while (valid && gtk_list_store_iter_is_valid (ui->store, iter)) {
 238 		gchar *keyword = NULL;
 239 		gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
 240 		/* Check if the keyword is not empty and then emit the row-changed
 241 		signal (if we delete the row, then the iter gets corrupted) */
 242 		if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0))
 243 			gtk_tree_model_row_changed (model, path, iter);
 244 
 245 		g_free (keyword);
 246 		valid = gtk_tree_model_iter_next (model, iter);
 247 	}
 248 
 249 	return FALSE;
 250 }
 251 
 252 static void
 253 key_cell_edited_callback (GtkCellRendererText *cell,
 254                           gchar *path_string,
 255                           gchar *new_text,
 256                           UIData *ui)
 257 {
 258 	GtkTreeModel *model;
 259 	GtkTreeIter iter;
 260 	gchar *value;
 261 
 262 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 263 
 264 	gtk_tree_model_get_iter_from_string (model, &iter, path_string);
 265 
 266 	gtk_tree_model_get (model, &iter, CLUE_VALUE_COLUMN, &value, -1);
 267 	gtk_list_store_set (
 268 		GTK_LIST_STORE (model), &iter,
 269 		CLUE_KEYWORD_COLUMN, new_text, CLUE_VALUE_COLUMN, value, -1);
 270 	g_free (value);
 271 
 272 	commit_changes (ui);
 273 }
 274 
 275 static void
 276 value_cell_edited_callback (GtkCellRendererText *cell,
 277                             gchar *path_string,
 278                             gchar *new_text,
 279                             UIData *ui)
 280 {
 281 	GtkTreeModel *model;
 282 	GtkTreeIter iter;
 283 	gchar *keyword;
 284 
 285 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 286 
 287 	gtk_tree_model_get_iter_from_string (model, &iter, path_string);
 288 
 289 	gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
 290 
 291 	gtk_list_store_set (
 292 		GTK_LIST_STORE (model), &iter,
 293 		CLUE_KEYWORD_COLUMN, keyword, CLUE_VALUE_COLUMN, new_text, -1);
 294 	g_free (keyword);
 295 
 296 	commit_changes (ui);
 297 }
 298 
 299 static void
 300 clue_add_clicked (GtkButton *button,
 301                   UIData *ui)
 302 {
 303 	GtkTreeModel *model;
 304 	GtkTreeIter iter;
 305 	gchar *new_clue = NULL;
 306 	GtkTreeViewColumn *focus_col;
 307 	GtkTreePath *path;
 308 
 309 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 310 	gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) clue_foreach_check_isempty, ui);
 311 
 312 	/* Disconnect from signal so that we can create an empty row */
 313 	g_signal_handlers_disconnect_matched (
 314 		model, G_SIGNAL_MATCH_FUNC,
 315 		0, 0, NULL, clue_check_isempty, ui);
 316 
 317 	/* TODO : Trim and check for blank strings */
 318 	new_clue = g_strdup ("");
 319 	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 320 	gtk_list_store_set (
 321 		GTK_LIST_STORE (model), &iter,
 322 		CLUE_KEYWORD_COLUMN, new_clue, CLUE_VALUE_COLUMN, new_clue, -1);
 323 
 324 	focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN);
 325 	path = gtk_tree_model_get_path (model, &iter);
 326 
 327 	if (path) {
 328 		gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE);
 329 		gtk_tree_view_row_activated (GTK_TREE_VIEW (ui->treeview), path, focus_col);
 330 		gtk_tree_path_free (path);
 331 	}
 332 
 333 	/* We have done our job, connect back to the signal */
 334 	g_signal_connect (
 335 		model, "row-changed",
 336 		G_CALLBACK (clue_check_isempty), ui);
 337 }
 338 
 339 static void
 340 clue_remove_clicked (GtkButton *button,
 341                      UIData *ui)
 342 {
 343 	GtkTreeSelection *selection;
 344 	GtkTreeModel *model;
 345 	GtkTreeIter iter;
 346 	GtkTreePath *path;
 347 	gboolean valid;
 348 	gint len;
 349 
 350 	valid = FALSE;
 351 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 352 	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
 353 		return;
 354 
 355 	/* Get the path and move to the previous node :) */
 356 	path = gtk_tree_model_get_path (model, &iter);
 357 	if (path)
 358 		valid = gtk_tree_path_prev (path);
 359 
 360 	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 361 
 362 	len = gtk_tree_model_iter_n_children (model, NULL);
 363 	if (len > 0) {
 364 		if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), &iter)) {
 365 			gtk_tree_selection_select_iter (selection, &iter);
 366 		} else {
 367 			if (path && valid) {
 368 				gtk_tree_model_get_iter (model, &iter, path);
 369 				gtk_tree_selection_select_iter (selection, &iter);
 370 			}
 371 		}
 372 	} else {
 373 		gtk_widget_set_sensitive (ui->clue_edit, FALSE);
 374 		gtk_widget_set_sensitive (ui->clue_remove, FALSE);
 375 	}
 376 
 377 	gtk_widget_grab_focus (ui->treeview);
 378 	gtk_tree_path_free (path);
 379 
 380 	commit_changes (ui);
 381 }
 382 
 383 static void
 384 clue_edit_clicked (GtkButton *button,
 385                    UIData *ui)
 386 {
 387 	GtkTreeSelection *selection;
 388 	GtkTreeModel *model;
 389 	GtkTreePath *path;
 390 	GtkTreeIter iter;
 391 	GtkTreeViewColumn *focus_col;
 392 
 393 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 394 	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
 395 		return;
 396 
 397 	focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN);
 398 	path = gtk_tree_model_get_path (model, &iter);
 399 
 400 	if (path) {
 401 		gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE);
 402 		gtk_tree_path_free (path);
 403 	}
 404 }
 405 
 406 GtkWidget *
 407 e_plugin_lib_get_configure_widget (EPlugin *epl)
 408 {
 409 	GtkCellRenderer *renderer_key, *renderer_value;
 410 	GtkTreeSelection *selection;
 411 	GtkTreeIter iter;
 412 	GtkWidget *hbox;
 413 	gchar **clue_list;
 414 	gint i;
 415 	GtkTreeModel *model;
 416 	GtkWidget *templates_configuration_box;
 417 	GtkWidget *clue_container;
 418 	GtkWidget *scrolledwindow1;
 419 	GtkWidget *clue_treeview;
 420 	GtkWidget *vbuttonbox2;
 421 	GtkWidget *clue_add;
 422 	GtkWidget *clue_edit;
 423 	GtkWidget *clue_remove;
 424 
 425 	UIData *ui = g_new0 (UIData, 1);
 426 
 427 	templates_configuration_box = gtk_vbox_new (FALSE, 6);
 428 	gtk_widget_show (templates_configuration_box);
 429 	gtk_widget_set_size_request (templates_configuration_box, 385, 189);
 430 
 431 	clue_container = gtk_hbox_new (FALSE, 12);
 432 	gtk_widget_show (clue_container);
 433 	gtk_box_pack_start (GTK_BOX (templates_configuration_box), clue_container, TRUE, TRUE, 0);
 434 
 435 	scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
 436 	gtk_widget_show (scrolledwindow1);
 437 	gtk_box_pack_start (GTK_BOX (clue_container), scrolledwindow1, TRUE, TRUE, 0);
 438 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 439 
 440 	clue_treeview = gtk_tree_view_new ();
 441 	gtk_widget_show (clue_treeview);
 442 	gtk_container_add (GTK_CONTAINER (scrolledwindow1), clue_treeview);
 443 	gtk_container_set_border_width (GTK_CONTAINER (clue_treeview), 1);
 444 
 445 	vbuttonbox2 = gtk_vbutton_box_new ();
 446 	gtk_widget_show (vbuttonbox2);
 447 	gtk_box_pack_start (GTK_BOX (clue_container), vbuttonbox2, FALSE, TRUE, 0);
 448 	gtk_button_box_set_layout (GTK_BUTTON_BOX (vbuttonbox2), GTK_BUTTONBOX_START);
 449 	gtk_box_set_spacing (GTK_BOX (vbuttonbox2), 6);
 450 
 451 	clue_add = gtk_button_new_from_stock ("gtk-add");
 452 	gtk_widget_show (clue_add);
 453 	gtk_container_add (GTK_CONTAINER (vbuttonbox2), clue_add);
 454 	gtk_widget_set_can_default (clue_add, TRUE);
 455 
 456 	clue_edit = gtk_button_new_from_stock ("gtk-edit");
 457 	gtk_widget_show (clue_edit);
 458 	gtk_container_add (GTK_CONTAINER (vbuttonbox2), clue_edit);
 459 	gtk_widget_set_can_default (clue_edit, TRUE);
 460 
 461 	clue_remove = gtk_button_new_from_stock ("gtk-remove");
 462 	gtk_widget_show (clue_remove);
 463 	gtk_container_add (GTK_CONTAINER (vbuttonbox2), clue_remove);
 464 	gtk_widget_set_can_default (clue_remove, TRUE);
 465 
 466 	ui->settings = g_settings_new ("org.gnome.evolution.plugin.templates");
 467 
 468 	ui->treeview = clue_treeview;
 469 
 470 	ui->store = gtk_list_store_new (CLUE_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
 471 
 472 	gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (ui->store));
 473 
 474 	renderer_key = gtk_cell_renderer_text_new ();
 475 	gtk_tree_view_insert_column_with_attributes (
 476 		GTK_TREE_VIEW (ui->treeview), -1, _("Keywords"),
 477 		renderer_key, "text", CLUE_KEYWORD_COLUMN, NULL);
 478 	g_object_set (renderer_key, "editable", TRUE, NULL);
 479 	g_signal_connect (
 480 		renderer_key, "edited",
 481 		(GCallback) key_cell_edited_callback, ui);
 482 
 483 	renderer_value = gtk_cell_renderer_text_new ();
 484 	gtk_tree_view_insert_column_with_attributes (
 485 		GTK_TREE_VIEW (ui->treeview), -1, _("Values"),
 486 		renderer_value, "text", CLUE_VALUE_COLUMN, NULL);
 487 	g_object_set (renderer_value, "editable", TRUE, NULL);
 488 	g_signal_connect (
 489 		renderer_value, "edited",
 490 		(GCallback) value_cell_edited_callback, ui);
 491 
 492 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 493 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 494 	g_signal_connect (
 495 		selection, "changed",
 496 		G_CALLBACK (selection_changed), ui);
 497 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
 498 
 499 	ui->clue_add = clue_add;
 500 	g_signal_connect (
 501 		ui->clue_add, "clicked",
 502 		G_CALLBACK (clue_add_clicked), ui);
 503 
 504 	ui->clue_remove = clue_remove;
 505 	g_signal_connect (
 506 		ui->clue_remove, "clicked",
 507 		G_CALLBACK (clue_remove_clicked), ui);
 508 	gtk_widget_set_sensitive (ui->clue_remove, FALSE);
 509 
 510 	ui->clue_edit = clue_edit;
 511 	g_signal_connect (
 512 		ui->clue_edit, "clicked",
 513 		G_CALLBACK (clue_edit_clicked), ui);
 514 	gtk_widget_set_sensitive (ui->clue_edit, FALSE);
 515 
 516 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 517 	g_signal_connect (
 518 		model, "row-changed",
 519 		G_CALLBACK (clue_check_isempty), ui);
 520 
 521 	/* Populate tree view with values from GSettings */
 522 	clue_list = g_settings_get_strv (ui->settings, CONF_KEY_TEMPLATE_PLACEHOLDERS);
 523 
 524 	for (i = 0; clue_list[i] != NULL; i++) {
 525 		gchar **temp = g_strsplit (clue_list[i], "=", 2);
 526 		gtk_list_store_append (ui->store, &iter);
 527 		gtk_list_store_set (ui->store, &iter, CLUE_KEYWORD_COLUMN, temp[0], CLUE_VALUE_COLUMN, temp[1], -1);
 528 		g_strfreev (temp);
 529 	}
 530 
 531 	if (clue_list) {
 532 		g_strfreev (clue_list);
 533 	}
 534 
 535 	/* Add the list here */
 536 
 537 	hbox = gtk_vbox_new (FALSE, 0);
 538 
 539 	gtk_box_pack_start (GTK_BOX (hbox), templates_configuration_box, TRUE, TRUE, 0);
 540 
 541 	/* to let free data properly on destroy of configuration widget */
 542 	g_object_set_data_full (G_OBJECT (hbox), "myui-data", ui, destroy_ui_data);
 543 
 544 	return hbox;
 545 }
 546 
 547 /* Case insensitive version of strstr */
 548 static gchar *
 549 strstr_nocase (const gchar *haystack,
 550                const gchar *needle)
 551 {
 552 /* When _GNU_SOURCE is available, use the nonstandard extension of libc */
 553 #ifdef _GNU_SOURCE
 554 	g_return_val_if_fail (haystack, NULL);
 555 	g_return_Val_if_fail (needle, NULL);
 556 
 557 	return strcasestr (haystack, needle)
 558 #else
 559 /* Otherwise convert both, haystack and needle to lowercase and use good old strstr */
 560 	gchar *l_haystack;
 561 	gchar *l_needle;
 562 	gchar *pos;
 563 
 564 	g_return_val_if_fail (haystack, NULL);
 565 	g_return_val_if_fail (needle, NULL);
 566 
 567 	l_haystack = g_ascii_strdown (haystack, -1);
 568 	l_needle = g_ascii_strdown (needle, -1);
 569 	pos = strstr (l_haystack, l_needle);
 570 
 571 	/* Get actual position of the needle in the haystack instead of l_haystack or
 572 	 * leave it NULL */
 573 	if (pos)
 574 		pos = (gchar *)(haystack + (pos - l_haystack));
 575 
 576 	g_free (l_haystack);
 577 	g_free (l_needle);
 578 
 579 	return pos;
 580 #endif
 581 }
 582 
 583 /* Replaces $ORIG[variable] in given template by given replacement from the original message */
 584 static void
 585 replace_template_variable (GString *text,
 586                            const gchar *variable,
 587                            const gchar *replacement)
 588 {
 589 	const gchar *p, *next;
 590 	GString *str;
 591 	gint find_len;
 592 	gchar *find;
 593 
 594 	g_return_if_fail (text != NULL);
 595 	g_return_if_fail (variable != NULL);
 596 	g_return_if_fail (*variable);
 597 
 598 	find = g_strconcat ("$ORIG[", variable, "]", NULL);
 599 
 600 	find_len = strlen (find);
 601 	str = g_string_new ("");
 602 	p = text->str;
 603 	while (next = strstr_nocase (p, find), next) {
 604 		if (p < next)
 605 			g_string_append_len (str, p, next - p);
 606 		if (replacement && *replacement)
 607 			g_string_append (str, replacement);
 608 		p = next + find_len;
 609 	}
 610 	g_string_append (str, p);
 611 
 612 	g_string_assign (text, str->str);
 613 
 614 	g_string_free (str, TRUE);
 615 	g_free (find);
 616 }
 617 
 618 static void
 619 replace_email_addresses (GString *template,
 620                          CamelInternetAddress *internet_address,
 621                          const gchar *variable)
 622 {
 623 	gint address_index = 0;
 624 	GString *emails = g_string_new ("");
 625 	const gchar *address_name, *address_email;
 626 
 627 	g_return_if_fail (template);
 628 	g_return_if_fail (internet_address);
 629 	g_return_if_fail (variable);
 630 
 631 	while (camel_internet_address_get (internet_address, address_index, &address_name, &address_email)) {
 632 		gchar *address = camel_internet_address_format_address (address_name, address_email);
 633 
 634 		if (address_index > 0)
 635 			g_string_append_printf (emails, ", %s", address);
 636 		else
 637 			g_string_append_printf (emails, "%s", address);
 638 
 639 		address_index++;
 640 		g_free (address);
 641 	}
 642 	replace_template_variable (template, variable, emails->str);
 643 	g_string_free (emails, TRUE);
 644 }
 645 
 646 static CamelMimePart *
 647 fill_template (CamelMimeMessage *message,
 648                CamelMimePart *template)
 649 {
 650 	struct _camel_header_raw *header;
 651 	CamelContentType *ct;
 652 	CamelStream *stream;
 653 	CamelMimePart *return_part;
 654 	CamelMimePart *message_part = NULL;
 655 	CamelDataWrapper *dw;
 656 
 657 	CamelInternetAddress *internet_address;
 658 
 659 	GString *template_body;
 660 	GByteArray *byte_array;
 661 
 662 	gint i;
 663 	gboolean message_html, template_html;
 664 
 665 	ct = camel_mime_part_get_content_type (template);
 666 	template_html = ct && camel_content_type_is (ct, "text", "html");
 667 
 668 	message_html = FALSE;
 669 	/* When template is html, then prefer HTML part of the original message. Otherwise go for plaintext */
 670 	dw = camel_medium_get_content (CAMEL_MEDIUM (message));
 671 	if (CAMEL_IS_MULTIPART (dw)) {
 672 		CamelMultipart *multipart = CAMEL_MULTIPART (dw);
 673 
 674 		for (i = 0; i < camel_multipart_get_number (multipart); i++) {
 675 			CamelMimePart *part = camel_multipart_get_part (multipart, i);
 676 			CamelContentType *ct = camel_mime_part_get_content_type (part);
 677 
 678 			if (!ct)
 679 				continue;
 680 
 681 			if (camel_content_type_is (ct, "text", "html") && template_html) {
 682 				message_part = camel_multipart_get_part (multipart, i);
 683 				message_html = TRUE;
 684 				break;
 685 			} else if (camel_content_type_is (ct, "text", "plain") && message_html == FALSE) {
 686 				message_part = camel_multipart_get_part (multipart, i);
 687 			}
 688 		}
 689 	 } else
 690 		message_part  = CAMEL_MIME_PART (message);
 691 
 692 	/* Get content of the template */
 693 	stream = camel_stream_mem_new ();
 694 	camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (template)), stream, NULL, NULL);
 695 	camel_stream_flush (stream, NULL, NULL);
 696 	byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
 697 	template_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len);
 698 	g_object_unref (stream);
 699 
 700 	/* Replace all $ORIG[header_name] by respective values */
 701 	header = CAMEL_MIME_PART (message)->headers;
 702 	while (header) {
 703 		if (g_ascii_strncasecmp (header->name, "content-", 8) != 0 &&
 704 		    g_ascii_strncasecmp (header->name, "to", 2) != 0 &&
 705 		    g_ascii_strncasecmp (header->name, "cc", 2) != 0 &&
 706 		    g_ascii_strncasecmp (header->name, "bcc", 3) != 0 &&
 707 		    g_ascii_strncasecmp (header->name, "from", 4) != 0 &&
 708 		    g_ascii_strncasecmp (header->name, "subject", 7) != 0)
 709 			replace_template_variable (template_body, header->name, header->value);
 710 
 711 		header = header->next;
 712 	}
 713 
 714 	/* Now manually replace the *subject* header. The header->value for subject header could be
 715 	 * base64 encoded, so let camel_mime_message to decode it for us if needed */
 716 	replace_template_variable (template_body, "subject", camel_mime_message_get_subject (message));
 717 
 718 	/* Replace TO and FROM modifiers. */
 719 	internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
 720 	replace_email_addresses (template_body, internet_address, "to");
 721 
 722 	internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
 723 	replace_email_addresses (template_body, internet_address, "cc");
 724 
 725 	internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
 726 	replace_email_addresses (template_body, internet_address, "bcc");
 727 
 728 	internet_address = camel_mime_message_get_from (message);
 729 	replace_email_addresses (template_body, internet_address, "from");
 730 
 731 	/* Now extract body of the original message and replace the $ORIG[body] modifier in template */
 732 	if (message_part && strstr_nocase (template_body->str, "$ORIG[body]")) {
 733 		GString *message_body;
 734 
 735 		stream = camel_stream_mem_new ();
 736 		camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (message_part)), stream, NULL, NULL);
 737 		camel_stream_flush (stream, NULL, NULL);
 738 		byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
 739 		message_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len);
 740 		g_object_unref (stream);
 741 
 742 		if (template_html && !message_html) {
 743 			gchar *html = camel_text_to_html (
 744 				message_body->str,
 745 				CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
 746 				CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
 747 				CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
 748 				CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
 749 				CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
 750 			g_string_assign (message_body, html);
 751 			g_free (html);
 752 		} else if (!template_html && message_html) {
 753 			g_string_prepend (message_body, "<pre>");
 754 			g_string_append (message_body, "</pre>");
 755 		} /* Other cases should not occur. And even if they happen to do, there's nothing we can really do about it */
 756 
 757 		replace_template_variable (template_body, "body", message_body->str);
 758 		g_string_free (message_body, TRUE);
 759 	} else {
 760 		replace_template_variable (template_body, "body", "");
 761 	}
 762 
 763 	return_part = camel_mime_part_new ();
 764 
 765 	if (template_html)
 766 		camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/html");
 767 	else
 768 		camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/plain");
 769 
 770 	g_string_free (template_body, TRUE);
 771 
 772 	return return_part;
 773 }
 774 
 775 static void
 776 create_new_message (CamelFolder *folder,
 777                     GAsyncResult *result,
 778                     AsyncContext *context)
 779 {
 780 	EAlertSink *alert_sink;
 781 	CamelMimeMessage *new;
 782 	CamelMimeMessage *message;
 783 	CamelMimeMessage *template;
 784 	CamelMultipart *new_multipart;
 785 	CamelContentType *new_content_type = NULL;
 786 	CamelDataWrapper *dw;
 787 	struct _camel_header_raw *header;
 788 	EMailBackend *backend;
 789 	EMailSession *session;
 790 	EShell *shell;
 791 	const gchar *message_uid;
 792 	gint i;
 793 	GError *error = NULL;
 794 
 795 	CamelMimePart *template_part = NULL;
 796 	CamelMimePart *out_part = NULL;
 797 
 798 	alert_sink = e_activity_get_alert_sink (context->activity);
 799 
 800 	template = camel_folder_get_message_finish (folder, result, &error);
 801 
 802 	if (e_activity_handle_cancellation (context->activity, error)) {
 803 		g_warn_if_fail (template == NULL);
 804 		async_context_free (context);
 805 		g_error_free (error);
 806 		return;
 807 
 808 	} else if (error != NULL) {
 809 		g_warn_if_fail (template == NULL);
 810 		e_alert_submit (
 811 			alert_sink, "mail:no-retrieve-message",
 812 			error->message, NULL);
 813 		async_context_free (context);
 814 		g_error_free (error);
 815 		return;
 816 	}
 817 
 818 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (template));
 819 
 820 	message = context->message;
 821 	message_uid = context->message_uid;
 822 
 823 	backend = e_mail_reader_get_backend (context->reader);
 824 	session = e_mail_backend_get_session (backend);
 825 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 826 
 827 	folder = e_mail_session_get_local_folder (
 828 		session, E_MAIL_LOCAL_FOLDER_TEMPLATES);
 829 
 830 	new = camel_mime_message_new ();
 831 	new_multipart = camel_multipart_new ();
 832 	camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (new_multipart), "multipart/alternative");
 833 	camel_multipart_set_boundary (new_multipart, NULL);
 834 
 835 	dw = camel_medium_get_content (CAMEL_MEDIUM (template));
 836 	/* If template is a multipart, then try to use HTML. When no HTML part is available, use plaintext. Every other
 837 	 * add as an attachment */
 838 	if (CAMEL_IS_MULTIPART (dw)) {
 839 		for (i = 0; i < camel_multipart_get_number (CAMEL_MULTIPART (dw)); i++) {
 840 			CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (dw), i);
 841 			CamelContentType *ct = camel_mime_part_get_content_type (part);
 842 
 843 			if (ct && camel_content_type_is (ct, "text", "html")) {
 844 				new_content_type = ct;
 845 				template_part = camel_multipart_get_part (CAMEL_MULTIPART (dw), i);
 846 			} else if (ct && camel_content_type_is (ct, "text", "plain") && new_content_type == NULL) {
 847 				new_content_type = ct;
 848 				template_part = camel_multipart_get_part (CAMEL_MULTIPART (dw), i);
 849 			} else {
 850 				/* Copy any other parts (attachments...) to the output message */
 851 				camel_mime_part_set_disposition (part, "attachment");
 852 				camel_multipart_add_part (new_multipart, part);
 853 			}
 854 		}
 855 	} else {
 856 		CamelContentType *ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (template));
 857 
 858 		if (ct && (camel_content_type_is (ct, "text", "html") ||
 859 		    camel_content_type_is (ct, "text", "plain"))) {
 860 			template_part = CAMEL_MIME_PART (template);
 861 			new_content_type = ct;
Value stored to 'new_content_type' is never read
(emitted by clang-analyzer)

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

Value stored to 'new_content_type' is never read
(emitted by clang-analyzer)

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

862 } 863 } 864 865 /* Here replace all the modifiers in template body by values from message and return the newly created part */ 866 out_part = fill_template (message, template_part); 867 868 /* Assigning part directly to mime_message causes problem with "Content-type" header displaying 869 * in the HTML message (camel parsing bug?) */ 870 camel_multipart_add_part (new_multipart, out_part); 871 g_object_unref (out_part); 872 camel_medium_set_content (CAMEL_MEDIUM (new), CAMEL_DATA_WRAPPER (new_multipart)); 873 874 /* Add the headers from the message we are replying to, so CC and that 875 * stuff is preserved. Also replace any $ORIG[header-name] modifiers ignoring 876 * 'content-*' headers */ 877 header = CAMEL_MIME_PART (message)->headers; 878 while (header) { 879 if (g_ascii_strncasecmp (header->name, "content-", 8) != 0) { 880 881 /* Some special handling of the 'subject' header */ 882 if (g_ascii_strncasecmp (header->name, "subject", 7) == 0) { 883 GString *subject = g_string_new (camel_mime_message_get_subject (template)); 884 885 /* Now replace all possible $ORIG[]s in the subject line by values from original message */ 886 struct _camel_header_raw *m_header = CAMEL_MIME_PART (message)->headers; 887 while (m_header) { 888 if (g_ascii_strncasecmp (m_header->name, "content-", 8) != 0 && 889 g_ascii_strncasecmp (m_header->name, "subject", 7) !=0) 890 replace_template_variable (subject, m_header->name, m_header->value); 891 892 m_header = m_header->next; 893 } 894 /* Now replace $ORIG[subject] variable, handling possible base64 encryption */ 895 replace_template_variable ( 896 subject, "subject", 897 camel_mime_message_get_subject (message)); 898 header->value = g_strdup (subject->str); 899 g_string_free (subject, TRUE); 900 } 901 902 camel_medium_add_header ( 903 CAMEL_MEDIUM (new), 904 header->name, 905 header->value); 906 } 907 908 header = header->next; 909 } 910 911 /* Set the To: field to the same To: field of the message we are replying to. */ 912 camel_mime_message_set_recipients ( 913 new, CAMEL_RECIPIENT_TYPE_TO, 914 camel_mime_message_get_from (message)); 915 916 /* Copy the CC and BCC from the template.*/ 917 camel_mime_message_set_recipients ( 918 new, CAMEL_RECIPIENT_TYPE_CC, 919 camel_mime_message_get_recipients ( 920 template, CAMEL_RECIPIENT_TYPE_CC)); 921 922 camel_mime_message_set_recipients ( 923 new, CAMEL_RECIPIENT_TYPE_BCC, 924 camel_mime_message_get_recipients ( 925 template, CAMEL_RECIPIENT_TYPE_BCC)); 926 927 /* Create the composer */ 928 em_utils_edit_message (shell, folder, new, message_uid); 929 930 g_object_unref (template); 931 g_object_unref (new_multipart); 932 g_object_unref (new); 933 934 async_context_free (context); 935 } 936 937 static void 938 template_got_source_message (CamelFolder *folder, 939 GAsyncResult *result, 940 AsyncContext *context) 941 { 942 EAlertSink *alert_sink; 943 GCancellable *cancellable; 944 CamelMimeMessage *message; 945 GError *error = NULL; 946 947 alert_sink = e_activity_get_alert_sink (context->activity); 948 cancellable = e_activity_get_cancellable (context->activity); 949 950 message = camel_folder_get_message_finish (folder, result, &error); 951 952 if (e_activity_handle_cancellation (context->activity, error)) { 953 g_warn_if_fail (message == NULL); 954 async_context_free (context); 955 g_error_free (error); 956 return; 957 958 } else if (error != NULL) { 959 g_warn_if_fail (message == NULL); 960 e_alert_submit ( 961 alert_sink, "mail:no-retrieve-message", 962 error->message, NULL); 963 async_context_free (context); 964 g_error_free (error); 965 return; 966 } 967 968 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); 969 970 context->message = message; 971 972 /* Now fetch the template message. */ 973 974 camel_folder_get_message ( 975 context->template_folder, 976 context->template_message_uid, 977 G_PRIORITY_DEFAULT, cancellable, 978 (GAsyncReadyCallback) create_new_message, 979 context); 980 } 981 982 static void 983 action_reply_with_template_cb (GtkAction *action, 984 EShellView *shell_view) 985 { 986 EActivity *activity; 987 AsyncContext *context; 988 GCancellable *cancellable; 989 CamelFolder *folder, *template_folder; 990 EShellContent *shell_content; 991 EMailReader *reader; 992 GPtrArray *uids; 993 const gchar *message_uid; 994 const gchar *template_message_uid; 995 996 shell_content = e_shell_view_get_shell_content (shell_view); 997 reader = E_MAIL_READER (shell_content); 998 999 folder = e_mail_reader_get_folder (reader); 1000 g_return_if_fail (CAMEL_IS_FOLDER (folder)); 1001 1002 uids = e_mail_reader_get_selected_uids (reader); 1003 g_return_if_fail (uids != NULL && uids->len == 1); 1004 message_uid = g_ptr_array_index (uids, 0); 1005 1006 template_folder = g_object_get_data ( 1007 G_OBJECT (action), "template-folder"); 1008 template_message_uid = g_object_get_data ( 1009 G_OBJECT (action), "template-uid"); 1010 1011 activity = e_mail_reader_new_activity (reader); 1012 cancellable = e_activity_get_cancellable (activity); 1013 1014 context = g_slice_new0 (AsyncContext); 1015 context->activity = activity; 1016 context->reader = g_object_ref (reader); 1017 context->template_folder = g_object_ref (template_folder); 1018 context->message_uid = g_strdup (message_uid); 1019 context->template_message_uid = g_strdup (template_message_uid); 1020 1021 camel_folder_get_message ( 1022 folder, message_uid, G_PRIORITY_DEFAULT, 1023 cancellable, (GAsyncReadyCallback) 1024 template_got_source_message, context); 1025 1026 em_utils_uids_free (uids); 1027 } 1028 1029 static void 1030 build_template_menus_recurse (CamelStore *local_store, 1031 GtkUIManager *ui_manager, 1032 GtkActionGroup *action_group, 1033 const gchar *menu_path, 1034 guint *action_count, 1035 guint merge_id, 1036 CamelFolderInfo *folder_info, 1037 EShellView *shell_view) 1038 { 1039 EShellWindow *shell_window; 1040 1041 shell_window = e_shell_view_get_shell_window (shell_view); 1042 1043 while (folder_info != NULL) { 1044 CamelFolder *folder; 1045 GPtrArray *uids; 1046 GtkAction *action; 1047 const gchar *action_label; 1048 const gchar *display_name; 1049 gchar *action_name; 1050 gchar *path; 1051 guint ii; 1052 1053 display_name = folder_info->display_name; 1054 1055 /* FIXME Not passing a GCancellable or GError here. */ 1056 folder = camel_store_get_folder_sync ( 1057 local_store, folder_info->full_name, 0, NULL, NULL); 1058 1059 action_name = g_strdup_printf ( 1060 "templates-menu-%d", *action_count); 1061 *action_count = *action_count + 1; 1062 1063 /* To avoid having a Templates dir, we ignore the top level */ 1064 if (g_str_has_suffix (display_name, "Templates")) 1065 action_label = _("Templates"); 1066 else 1067 action_label = display_name; 1068 1069 action = gtk_action_new ( 1070 action_name, action_label, NULL, NULL); 1071 1072 gtk_action_group_add_action (action_group, action); 1073 1074 gtk_ui_manager_add_ui ( 1075 ui_manager, merge_id, menu_path, action_name, 1076 action_name, GTK_UI_MANAGER_MENU, FALSE); 1077 1078 /* Disconnect previous connection to avoid possible multiple calls because 1079 * folder is a persistent structure */ 1080 g_signal_handlers_disconnect_by_func ( 1081 folder, G_CALLBACK (templates_folder_msg_changed_cb), shell_window); 1082 g_signal_connect ( 1083 folder, "changed", 1084 G_CALLBACK (templates_folder_msg_changed_cb), 1085 shell_window); 1086 1087 path = g_strdup_printf ("%s/%s", menu_path, action_name); 1088 1089 g_object_unref (action); 1090 g_free (action_name); 1091 1092 /* Add submenus, if any. */ 1093 if (folder_info->child != NULL) 1094 build_template_menus_recurse ( 1095 local_store, 1096 ui_manager, action_group, 1097 path, action_count, merge_id, 1098 folder_info->child, shell_view); 1099 1100 if (!folder) { 1101 g_free (path); 1102 folder_info = folder_info->next; 1103 continue; 1104 } 1105 1106 /* Get the UIDs for this folder and add them to the menu. */ 1107 uids = camel_folder_get_uids (folder); 1108 for (ii = 0; uids && ii < uids->len; ii++) { 1109 CamelMimeMessage *template; 1110 const gchar *uid = uids->pdata[ii]; 1111 guint32 flags; 1112 1113 /* If the UIDs is marked for deletion, skip it. */ 1114 flags = camel_folder_get_message_flags (folder, uid); 1115 if (flags & CAMEL_MESSAGE_DELETED) 1116 continue; 1117 1118 /* FIXME Not passing a GCancellable or GError here. */ 1119 template = camel_folder_get_message_sync ( 1120 folder, uid, NULL, NULL); 1121 1122 /* FIXME Do something more intelligent with errors. */ 1123 if (template == NULL) 1124 continue; 1125 1126 action_label = 1127 camel_mime_message_get_subject (template); 1128 if (action_label == NULL || *action_label == '\0') 1129 action_label = _("No Title"); 1130 1131 action_name = g_strdup_printf ( 1132 "templates-item-%d", *action_count); 1133 *action_count = *action_count + 1; 1134 1135 action = gtk_action_new ( 1136 action_name, action_label, NULL, NULL); 1137 1138 g_object_set_data (G_OBJECT (action), "template-uid", (gpointer) uid); 1139 1140 g_object_set_data (G_OBJECT (action), "template-folder", folder); 1141 1142 g_signal_connect ( 1143 action, "activate", 1144 G_CALLBACK (action_reply_with_template_cb), 1145 shell_view); 1146 1147 gtk_action_group_add_action (action_group, action); 1148 1149 gtk_ui_manager_add_ui ( 1150 ui_manager, merge_id, path, action_name, 1151 action_name, GTK_UI_MANAGER_MENUITEM, FALSE); 1152 1153 g_object_unref (action); 1154 g_free (action_name); 1155 g_object_unref (template); 1156 } 1157 1158 camel_folder_free_uids (folder, uids); 1159 g_object_unref (folder); 1160 g_free (path); 1161 1162 folder_info = folder_info->next; 1163 } 1164 } 1165 1166 static void 1167 got_message_draft_cb (EMsgComposer *composer, 1168 GAsyncResult *result) 1169 { 1170 EShell *shell; 1171 EShellBackend *shell_backend; 1172 EMailBackend *backend; 1173 EMailSession *session; 1174 CamelMimeMessage *message; 1175 CamelMessageInfo *info; 1176 GError *error = NULL; 1177 1178 message = e_msg_composer_get_message_draft_finish ( 1179 composer, result, &error); 1180 1181 /* Ignore cancellations. */ 1182 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { 1183 g_warn_if_fail (message == NULL); 1184 g_error_free (error); 1185 return; 1186 } 1187 1188 if (error != NULL) { 1189 g_warn_if_fail (message == NULL); 1190 e_alert_run_dialog_for_args ( 1191 GTK_WINDOW (composer), 1192 "mail-composer:no-build-message", 1193 error->message, NULL); 1194 g_error_free (error); 1195 return; 1196 } 1197 1198 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); 1199 1200 shell = e_shell_get_default (); 1201 shell_backend = e_shell_get_backend_by_name (shell, "mail"); 1202 1203 backend = E_MAIL_BACKEND (shell_backend); 1204 session = e_mail_backend_get_session (backend); 1205 1206 info = camel_message_info_new (NULL); 1207 1208 /* The last argument is a bit mask which tells the function 1209 * which flags to modify. In this case, ~0 means all flags. 1210 * So it clears all the flags and then sets SEEN and DRAFT. */ 1211 camel_message_info_set_flags ( 1212 info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0); 1213 1214 /* FIXME Should submit an EActivity for this 1215 * operation, same as saving to Outbox. */ 1216 e_mail_session_append_to_local_folder ( 1217 session, E_MAIL_LOCAL_FOLDER_TEMPLATES, 1218 message, info, G_PRIORITY_DEFAULT, 1219 NULL, (GAsyncReadyCallback) NULL, NULL); 1220 1221 g_object_unref (message); 1222 } 1223 1224 static void 1225 action_template_cb (GtkAction *action, 1226 EMsgComposer *composer) 1227 { 1228 /* XXX Pass a GCancellable */ 1229 e_msg_composer_get_message_draft ( 1230 composer, G_PRIORITY_DEFAULT, NULL, 1231 (GAsyncReadyCallback) got_message_draft_cb, NULL); 1232 } 1233 1234 static GtkActionEntry composer_entries[] = { 1235 1236 { "template", 1237 GTK_STOCK_SAVE, 1238 N_("Save as _Template"), 1239 "<Shift><Control>t", 1240 N_("Save as Template"), 1241 G_CALLBACK (action_template_cb) } 1242 }; 1243 1244 static void 1245 build_menu (EShellWindow *shell_window, 1246 GtkActionGroup *action_group) 1247 { 1248 EShellView *shell_view; 1249 EShellBackend *shell_backend; 1250 EMailBackend *backend; 1251 EMailSession *session; 1252 CamelFolder *folder; 1253 CamelStore *local_store; 1254 CamelFolderInfo *folder_info; 1255 GtkUIManager *ui_manager; 1256 guint merge_id; 1257 guint action_count = 0; 1258 const gchar *full_name; 1259 1260 ui_manager = e_shell_window_get_ui_manager (shell_window); 1261 shell_view = e_shell_window_get_shell_view (shell_window, "mail"); 1262 shell_backend = e_shell_view_get_shell_backend (shell_view); 1263 1264 backend = E_MAIL_BACKEND (shell_backend); 1265 session = e_mail_backend_get_session (backend); 1266 local_store = e_mail_session_get_local_store (session); 1267 1268 merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action_group), "merge-id")); 1269 1270 /* Now recursively build template submenus in the pop-up menu. */ 1271 folder = e_mail_session_get_local_folder ( 1272 session, E_MAIL_LOCAL_FOLDER_TEMPLATES); 1273 full_name = camel_folder_get_full_name (folder); 1274 1275 /* FIXME Not passing a GCancellable or GError here. */ 1276 folder_info = camel_store_get_folder_info_sync ( 1277 local_store, full_name, 1278 CAMEL_STORE_FOLDER_INFO_RECURSIVE | 1279 CAMEL_STORE_FOLDER_INFO_FAST, NULL, NULL); 1280 1281 build_template_menus_recurse ( 1282 local_store, ui_manager, action_group, 1283 "/mail-message-popup/mail-message-templates", 1284 &action_count, merge_id, folder_info, 1285 shell_view); 1286 1287 camel_store_free_folder_info (local_store, folder_info); 1288 } 1289 1290 static void 1291 update_actions_cb (EShellView *shell_view, 1292 GtkActionGroup *action_group) 1293 { 1294 GList *list; 1295 gint length; 1296 1297 if (!plugin_enabled) 1298 return; 1299 1300 list = gtk_action_group_list_actions (action_group); 1301 length = g_list_length (list); 1302 1303 if (!length) { 1304 EShellWindow *shell_window = e_shell_view_get_shell_window (shell_view); 1305 build_menu (shell_window, action_group); 1306 } 1307 1308 gtk_action_group_set_sensitive (action_group, TRUE); 1309 gtk_action_group_set_visible (action_group, TRUE); 1310 1311 g_list_free (list); 1312 } 1313 1314 gboolean 1315 init_composer_actions (GtkUIManager *ui_manager, 1316 EMsgComposer *composer) 1317 { 1318 GtkhtmlEditor *editor; 1319 1320 editor = GTKHTML_EDITOR (composer); 1321 1322 /* Add actions to the "composer" action group. */ 1323 gtk_action_group_add_actions ( 1324 gtkhtml_editor_get_action_group (editor, "composer"), 1325 composer_entries, G_N_ELEMENTS (composer_entries), composer); 1326 1327 return TRUE; 1328 } 1329 1330 static void 1331 rebuild_template_menu (EShellWindow *shell_window) 1332 { 1333 GtkUIManager *ui_manager; 1334 GtkActionGroup *action_group; 1335 guint merge_id; 1336 1337 ui_manager = e_shell_window_get_ui_manager (shell_window); 1338 1339 action_group = e_lookup_action_group (ui_manager, "templates"); 1340 merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action_group), "merge-id")); 1341 1342 gtk_ui_manager_remove_ui (ui_manager, merge_id); 1343 e_action_group_remove_all_actions (action_group); 1344 gtk_ui_manager_ensure_update (ui_manager); 1345 1346 build_menu (shell_window, action_group); 1347 } 1348 1349 static void 1350 templates_folder_msg_changed_cb (CamelFolder *folder, 1351 CamelFolderChangeInfo *change_info, 1352 EShellWindow *shell_window) 1353 { 1354 rebuild_template_menu (shell_window); 1355 } 1356 1357 static void 1358 templates_folder_changed_cb (CamelStore *store, 1359 CamelFolderInfo *folder_info, 1360 EShellWindow *shell_window) 1361 { 1362 if (folder_info->full_name && strstr (folder_info->full_name, _("Templates")) != NULL) 1363 rebuild_template_menu (shell_window); 1364 } 1365 1366 static void 1367 templates_folder_renamed_cb (CamelStore *store, 1368 const gchar *old_name, 1369 CamelFolderInfo *folder_info, 1370 EShellWindow *shell_window) 1371 { 1372 if (folder_info->full_name && strstr (folder_info->full_name, _("Templates")) != NULL) 1373 rebuild_template_menu (shell_window); 1374 } 1375 1376 static void 1377 mail_shell_view_created_cb (EShellWindow *shell_window, 1378 EShellView *shell_view) 1379 { 1380 EMailBackend *backend; 1381 EMailSession *session; 1382 EShellBackend *shell_backend; 1383 GtkUIManager *ui_manager; 1384 GtkActionGroup *action_group; 1385 CamelFolder *folder; 1386 CamelStore *local_store; 1387 guint merge_id; 1388 1389 ui_manager = e_shell_window_get_ui_manager (shell_window); 1390 e_shell_window_add_action_group (shell_window, "templates"); 1391 action_group = e_lookup_action_group (ui_manager, "templates"); 1392 1393 merge_id = gtk_ui_manager_new_merge_id (ui_manager); 1394 1395 g_object_set_data ( 1396 G_OBJECT (action_group), "merge-id", 1397 GUINT_TO_POINTER (merge_id)); 1398 1399 shell_backend = e_shell_view_get_shell_backend (shell_view); 1400 1401 backend = E_MAIL_BACKEND (shell_backend); 1402 session = e_mail_backend_get_session (backend); 1403 local_store = e_mail_session_get_local_store (session); 1404 1405 folder = e_mail_session_get_local_folder ( 1406 session, E_MAIL_LOCAL_FOLDER_TEMPLATES); 1407 1408 g_signal_connect ( 1409 folder, "changed", 1410 G_CALLBACK (templates_folder_msg_changed_cb), shell_window); 1411 g_signal_connect ( 1412 local_store, "folder-created", 1413 G_CALLBACK (templates_folder_changed_cb), shell_window); 1414 g_signal_connect ( 1415 local_store, "folder-deleted", 1416 G_CALLBACK (templates_folder_changed_cb), shell_window); 1417 g_signal_connect ( 1418 local_store, "folder-renamed", 1419 G_CALLBACK (templates_folder_renamed_cb), shell_window); 1420 1421 g_signal_connect ( 1422 shell_view, "update-actions", 1423 G_CALLBACK (update_actions_cb), action_group); 1424 } 1425 1426 gboolean 1427 init_shell_actions (GtkUIManager *ui_manager, 1428 EShellWindow *shell_window) 1429 { 1430 EShellView *shell_view; 1431 1432 /* Be careful not to instantiate the mail view ourselves. */ 1433 shell_view = e_shell_window_peek_shell_view (shell_window, "mail"); 1434 if (shell_view != NULL) 1435 mail_shell_view_created_cb (shell_window, shell_view); 1436 else 1437 g_signal_connect ( 1438 shell_window, "shell-view-created::mail", 1439 G_CALLBACK (mail_shell_view_created_cb), NULL); 1440 1441 return TRUE; 1442 } 1443 1444 gint 1445 e_plugin_lib_enable (EPlugin *plugin, 1446 gboolean enabled) 1447 { 1448 plugin_enabled = enabled; 1449 1450 return 0; 1451 }