1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include "defs.h"
25 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkscrolledwindow.h>
30 #include <gtk/gtktreestore.h>
31 #include <gtk/gtktreeview.h>
32 #include <gtk/gtktreeselection.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkvpaned.h>
36 #include <gtk/gtksignal.h>
37 #include <gtk/gtkmenu.h>
38 #include <gtk/gtkdnd.h>
39 #include <gtk/gtkselection.h>
40 #include <gtk/gtknotebook.h>
41 #include <gtk/gtkvbbox.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 
45 #ifdef G_OS_WIN32
46 #  include <windows.h>
47 #endif
48 
49 #include "main.h"
50 #include "mimeview.h"
51 #include "textview.h"
52 #include "imageview.h"
53 #include "procmime.h"
54 #include "procheader.h"
55 #include "summaryview.h"
56 #include "menu.h"
57 #include "compose.h"
58 #include "printing.h"
59 #include "filesel.h"
60 #include "alertpanel.h"
61 #include "inputdialog.h"
62 #include "utils.h"
63 #include "gtkutils.h"
64 #include "prefs_common.h"
65 #include "rfc2015.h"
66 
67 enum
68 {
69 	COL_MIMETYPE,
70 	COL_SIZE,
71 	COL_NAME,
72 	COL_MIME_INFO,
73 	N_COLS
74 };
75 
76 static void mimeview_set_multipart_tree		(MimeView	*mimeview,
77 						 MimeInfo	*mimeinfo,
78 						 GtkTreeIter	*parent);
79 static gboolean mimeview_append_part		(MimeView	*mimeview,
80 						 MimeInfo	*partinfo,
81 						 GtkTreeIter	*iter,
82 						 GtkTreeIter	*parent);
83 static void mimeview_show_message_part		(MimeView	*mimeview,
84 						 MimeInfo	*partinfo);
85 static void mimeview_show_image_part		(MimeView	*mimeview,
86 						 MimeInfo	*partinfo);
87 static void mimeview_show_mime_part		(MimeView	*mimeview,
88 						 MimeInfo	*partinfo);
89 #if USE_GPGME
90 static void mimeview_show_signature_part	(MimeView	*mimeview,
91 						 MimeInfo	*partinfo);
92 #endif
93 static void mimeview_change_view_type		(MimeView	*mimeview,
94 						 MimeViewType	 type);
95 
96 static void mimeview_selection_changed	(GtkTreeSelection	*selection,
97 					 MimeView		*mimeview);
98 
99 static gint mimeview_button_pressed	(GtkWidget	*widget,
100 					 GdkEventButton	*event,
101 					 MimeView	*mimeview);
102 static gint mimeview_key_pressed	(GtkWidget	*widget,
103 					 GdkEventKey	*event,
104 					 MimeView	*mimeview);
105 
106 static void mimeview_drag_begin 	(GtkWidget	  *widget,
107 					 GdkDragContext	  *drag_context,
108 					 MimeView	  *mimeview);
109 static void mimeview_drag_end	 	(GtkWidget	  *widget,
110 					 GdkDragContext	  *drag_context,
111 					 MimeView	  *mimeview);
112 static void mimeview_drag_data_get      (GtkWidget	  *widget,
113 					 GdkDragContext   *drag_context,
114 					 GtkSelectionData *selection_data,
115 					 guint		   info,
116 					 guint		   time,
117 					 MimeView	  *mimeview);
118 
119 static void mimeview_display_as_text	(MimeView	*mimeview);
120 static void mimeview_launch		(MimeView	*mimeview);
121 static void mimeview_open_with		(MimeView	*mimeview);
122 static void mimeview_view_file		(const gchar	*filename,
123 					 MimeInfo	*partinfo,
124 					 const gchar	*cmdline);
125 
126 static void mimeview_reply		(MimeView	*mimeview,
127 					 guint		 action);
128 
129 #if USE_GPGME
130 static void mimeview_check_signature	(MimeView	*mimeview);
131 #endif
132 
133 static GtkItemFactoryEntry mimeview_popup_entries[] =
134 {
135 	{N_("/_Open"),		  NULL, mimeview_launch,	  0, NULL},
136 	{N_("/Open _with..."),	  NULL, mimeview_open_with,	  0, NULL},
137 	{N_("/_Display as text"), NULL, mimeview_display_as_text, 0, NULL},
138 	{N_("/_Save as..."),	  NULL, mimeview_save_as,	  0, NULL},
139 	{N_("/Save _all..."),	  NULL, mimeview_save_all,	  0, NULL},
140 	{N_("/_Print..."),	  NULL, mimeview_print,		  0, NULL},
141 	{N_("/---"),		  NULL, NULL,			  0, "<Separator>"},
142 	{N_("/_Reply"),		  NULL, NULL,			  0, "<Branch>"},
143 	{N_("/_Reply/_Reply"),	  NULL, mimeview_reply,		  COMPOSE_REPLY, NULL},
144 	{N_("/_Reply/Reply to _all"),
145 				  NULL, mimeview_reply,		  COMPOSE_REPLY_TO_ALL, NULL},
146 	{N_("/_Reply/Reply to _sender"),
147 				  NULL, mimeview_reply,		  COMPOSE_REPLY_TO_SENDER, NULL},
148 	{N_("/_Reply/Reply to mailing _list"),
149 				  NULL, mimeview_reply,		  COMPOSE_REPLY_TO_LIST, NULL},
150 #if USE_GPGME
151 	{N_("/---"),		  NULL, NULL,			  0, "<Separator>"},
152         {N_("/_Check signature"), NULL, mimeview_check_signature, 0, NULL}
153 #endif
154 };
155 
156 static GtkTargetEntry mimeview_mime_types[] =
157 {
158 	{"text/uri-list", 0, 0}
159 };
160 
mimeview_create(void)161 MimeView *mimeview_create(void)
162 {
163 	MimeView *mimeview;
164 
165 	GtkWidget *paned;
166 	GtkWidget *scrolledwin;
167 	GtkWidget *treeview;
168 	GtkTreeStore *store;
169 	GtkTreeSelection *selection;
170 	GtkTreeViewColumn *column;
171 	GtkCellRenderer *renderer;
172 	GtkWidget *mime_vbox;
173 	GtkWidget *popupmenu;
174 	GtkItemFactory *popupfactory;
175 	gint n_entries;
176 	GtkWidget *reply_separator;
177 	GtkWidget *reply_menuitem;
178 	GList *child;
179 
180 	debug_print(_("Creating MIME view...\n"));
181 	mimeview = g_new0(MimeView, 1);
182 
183 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
184 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
185 				       GTK_POLICY_AUTOMATIC,
186 				       GTK_POLICY_ALWAYS);
187 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
188 					    GTK_SHADOW_IN);
189 	gtk_widget_set_size_request(scrolledwin, -1, 80);
190 
191 	store = gtk_tree_store_new(N_COLS, G_TYPE_STRING, G_TYPE_STRING,
192 				   G_TYPE_STRING, G_TYPE_POINTER);
193 
194 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
195 	g_object_unref(G_OBJECT(store));
196 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
197 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
198 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_NAME);
199 	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE);
200 
201 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
202 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
203 
204 	gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
205 
206 	renderer = gtk_cell_renderer_text_new();
207 	g_object_set(renderer, "ypad", 0, NULL);
208 	column = gtk_tree_view_column_new_with_attributes
209 		(_("Data type"), renderer, "text", COL_MIMETYPE, NULL);
210 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
211 	gtk_tree_view_column_set_resizable(column, TRUE);
212 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
213 
214 	renderer = gtk_cell_renderer_text_new();
215 	g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
216 	column = gtk_tree_view_column_new_with_attributes
217 		(_("Size"), renderer, "text", COL_SIZE, NULL);
218 	gtk_tree_view_column_set_alignment(column, 1.0);
219 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
220 	gtk_tree_view_column_set_resizable(column, TRUE);
221 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
222 
223 	renderer = gtk_cell_renderer_text_new();
224 	g_object_set(renderer, "ypad", 0, NULL);
225 	column = gtk_tree_view_column_new_with_attributes
226 		(_("Name"), renderer, "text", COL_NAME, NULL);
227 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
228 	gtk_tree_view_column_set_resizable(column, TRUE);
229 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
230 
231 	gtk_tree_view_enable_model_drag_source
232 		(GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK,
233 		 mimeview_mime_types, 1, GDK_ACTION_COPY);
234 
235 	g_signal_connect(G_OBJECT(selection), "changed",
236 			 G_CALLBACK(mimeview_selection_changed), mimeview);
237 	g_signal_connect(G_OBJECT(treeview), "button_press_event",
238 			 G_CALLBACK(mimeview_button_pressed), mimeview);
239 	g_signal_connect(G_OBJECT(treeview), "key_press_event",
240 			 G_CALLBACK(mimeview_key_pressed), mimeview);
241 
242 	g_signal_connect_after(G_OBJECT (treeview),"drag-begin",
243 			       G_CALLBACK (mimeview_drag_begin), mimeview);
244 	g_signal_connect(G_OBJECT (treeview),"drag-end",
245 			 G_CALLBACK (mimeview_drag_end), mimeview);
246 	g_signal_connect(G_OBJECT(treeview), "drag-data-get",
247 			 G_CALLBACK(mimeview_drag_data_get), mimeview);
248 
249 	mime_vbox = gtk_vbox_new(FALSE, 0);
250 	gtk_container_set_reallocate_redraws(GTK_CONTAINER(mime_vbox), TRUE);
251 
252 	paned = gtk_vpaned_new();
253 	gtk_paned_add1(GTK_PANED(paned), scrolledwin);
254 	gtk_paned_add2(GTK_PANED(paned), mime_vbox);
255 
256 	n_entries = sizeof(mimeview_popup_entries) /
257 		sizeof(mimeview_popup_entries[0]);
258 	popupmenu = menu_create_items(mimeview_popup_entries, n_entries,
259 				      "<MimeView>", &popupfactory, mimeview);
260 
261 	reply_menuitem = gtk_item_factory_get_item(popupfactory, "/Reply");
262 	child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
263 			    reply_menuitem);
264 	reply_separator = GTK_WIDGET(child->prev->data);
265 
266 	mimeview->paned        = paned;
267 	mimeview->scrolledwin  = scrolledwin;
268 	mimeview->treeview     = treeview;
269 	mimeview->store        = store;
270 	mimeview->selection    = selection;
271 	mimeview->mime_vbox    = mime_vbox;
272 	mimeview->popupmenu    = popupmenu;
273 	mimeview->popupfactory = popupfactory;
274 	mimeview->reply_separator = reply_separator;
275 	mimeview->reply_menuitem  = reply_menuitem;
276 	mimeview->type         = -1;
277 
278 	return mimeview;
279 }
280 
mimeview_init(MimeView * mimeview)281 void mimeview_init(MimeView *mimeview)
282 {
283 	textview_init(mimeview->textview);
284 	imageview_init(mimeview->imageview);
285 }
286 
287 /*
288  * Check whether the message is OpenPGP signed
289  */
290 #if USE_GPGME
mimeview_is_signed(MimeView * mimeview)291 static gboolean mimeview_is_signed(MimeView *mimeview)
292 {
293 	MimeInfo *partinfo;
294 
295         debug_print("mimeview_is signed of %p\n", mimeview);
296 
297         if (!mimeview) return FALSE;
298 	if (!mimeview->opened) return FALSE;
299 	if (!rfc2015_is_available()) return FALSE;
300 
301         debug_print("mimeview_is_signed: open\n" );
302 
303 	if (!mimeview->messageview->file) return FALSE;
304 
305         debug_print("mimeview_is_signed: file\n" );
306 
307 	partinfo = mimeview_get_selected_part(mimeview);
308 	g_return_val_if_fail(partinfo != NULL, FALSE);
309 
310 	/* walk the tree and see whether there is a signature somewhere */
311 	do {
312 		if (rfc2015_has_signature(partinfo))
313 			return TRUE;
314         } while ((partinfo = partinfo->parent) != NULL);
315 
316 	debug_print("mimeview_is_signed: FALSE\n" );
317 
318 	return FALSE;
319 }
320 
set_unchecked_signature(MimeInfo * mimeinfo)321 static void set_unchecked_signature(MimeInfo *mimeinfo)
322 {
323 	MimeInfo **signedinfo;
324 
325 	if (!rfc2015_is_available()) return;
326 
327 	signedinfo = rfc2015_find_signature(mimeinfo);
328 	if (signedinfo == NULL) return;
329 
330 	g_free(signedinfo[1]->sigstatus);
331 	signedinfo[1]->sigstatus =
332 		g_strdup(_("Select \"Check signature\" to check"));
333 
334 	g_free(signedinfo[1]->sigstatus_full);
335 	signedinfo[1]->sigstatus_full = NULL;
336 
337 	g_free(signedinfo);
338 }
339 #endif /* USE_GPGME */
340 
mimeview_show_message(MimeView * mimeview,MimeInfo * mimeinfo,const gchar * file)341 void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo,
342 			   const gchar *file)
343 {
344 	GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store);
345 	GtkTreeIter iter;
346 	gboolean valid;
347 
348 	mimeview_clear(mimeview);
349 	textview_clear(mimeview->messageview->textview);
350 
351 	g_return_if_fail(file != NULL);
352 	g_return_if_fail(mimeinfo != NULL);
353 
354 #if USE_GPGME
355 	if (rfc2015_is_available() && prefs_common.auto_check_signatures) {
356 		FILE *fp;
357 
358 		if ((fp = g_fopen(file, "rb")) == NULL) {
359 			FILE_OP_ERROR(file, "fopen");
360 			return;
361 		}
362 		rfc2015_check_signature(mimeinfo, fp);
363 		fclose(fp);
364 	} else
365 		set_unchecked_signature(mimeinfo);
366 #endif
367 
368 	g_signal_handlers_block_by_func
369 		(G_OBJECT(mimeview->selection),
370 		 G_CALLBACK(mimeview_selection_changed), mimeview);
371 
372 	mimeview_set_multipart_tree(mimeview, mimeinfo, NULL);
373 	gtk_tree_view_expand_all(GTK_TREE_VIEW(mimeview->treeview));
374 
375 	g_signal_handlers_unblock_by_func
376 		(G_OBJECT(mimeview->selection),
377 		 G_CALLBACK(mimeview_selection_changed), mimeview);
378 
379 	/* search first text part */
380 	for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
381 	     valid = gtkut_tree_model_next(model, &iter)) {
382 		MimeInfo *partinfo;
383 
384 		gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1);
385 		if (partinfo &&
386 		    (partinfo->mime_type == MIME_TEXT ||
387 		     partinfo->mime_type == MIME_TEXT_HTML))
388 			break;
389 	}
390 	textview_show_message(mimeview->messageview->textview, mimeinfo, file);
391 
392 	if (!valid)
393 		valid = gtk_tree_model_get_iter_first(model, &iter);
394 
395 	if (valid) {
396 		GtkTreePath *path;
397 
398 		path = gtk_tree_model_get_path(model, &iter);
399 		gtk_tree_view_set_cursor(GTK_TREE_VIEW(mimeview->treeview),
400 					 path, NULL, FALSE);
401 		gtk_tree_path_free(path);
402 		if (mimeview_get_selected_part(mimeview))
403 			gtk_widget_grab_focus(mimeview->treeview);
404 	}
405 }
406 
mimeview_clear(MimeView * mimeview)407 void mimeview_clear(MimeView *mimeview)
408 {
409 	mimeview->has_attach_file = FALSE;
410 
411 	gtk_tree_store_clear(mimeview->store);
412 	textview_clear(mimeview->textview);
413 	imageview_clear(mimeview->imageview);
414 
415 	gtk_tree_path_free(mimeview->opened);
416 	mimeview->opened = NULL;
417 
418 	g_free(mimeview->drag_file);
419 	mimeview->drag_file = NULL;
420 }
421 
mimeview_destroy(MimeView * mimeview)422 void mimeview_destroy(MimeView *mimeview)
423 {
424 	textview_destroy(mimeview->textview);
425 	imageview_destroy(mimeview->imageview);
426 	g_object_unref(mimeview->popupfactory);
427 	g_free(mimeview->drag_file);
428 	g_free(mimeview);
429 }
430 
mimeview_get_selected_part(MimeView * mimeview)431 MimeInfo *mimeview_get_selected_part(MimeView *mimeview)
432 {
433 	GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store);
434 	GtkTreeIter iter;
435 	MimeInfo *partinfo = NULL;
436 
437 	if (!mimeview->opened)
438 		return NULL;
439 	if (gtk_notebook_get_current_page
440 		(GTK_NOTEBOOK(mimeview->messageview->notebook)) == 0)
441 		return NULL;
442 
443 	if (gtk_tree_model_get_iter(model, &iter, mimeview->opened))
444 		gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1);
445 
446 	return partinfo;
447 }
448 
mimeview_step(MimeView * mimeview,GtkScrollType type)449 gboolean mimeview_step(MimeView *mimeview, GtkScrollType type)
450 {
451 	GtkTreeView *treeview = GTK_TREE_VIEW(mimeview->treeview);
452 	GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store);
453 	GtkTreeIter iter;
454 	gboolean moved;
455 
456 	if (!mimeview->opened)
457 		return FALSE;
458 	if (!gtk_tree_model_get_iter(model, &iter, mimeview->opened))
459 		return FALSE;
460 
461 	if (type == GTK_SCROLL_STEP_FORWARD) {
462 		if (gtkut_tree_model_next(model, &iter))
463 			gtkut_tree_view_expand_parent_all(treeview, &iter);
464 		else
465 			return FALSE;
466 	} else {
467 		if (!gtkut_tree_model_prev(model, &iter))
468 			return FALSE;
469 	}
470 
471 	g_signal_emit_by_name(G_OBJECT(treeview), "move-cursor",
472 			      GTK_MOVEMENT_DISPLAY_LINES,
473 			      type == GTK_SCROLL_STEP_FORWARD ? 1 : -1, &moved);
474 
475 	return TRUE;
476 }
477 
mimeview_set_multipart_tree(MimeView * mimeview,MimeInfo * mimeinfo,GtkTreeIter * parent)478 static void mimeview_set_multipart_tree(MimeView *mimeview,
479 					MimeInfo *mimeinfo,
480 					GtkTreeIter *parent)
481 {
482 	GtkTreeIter iter;
483 
484 	g_return_if_fail(mimeinfo != NULL);
485 
486 	if (mimeinfo->children)
487 		mimeinfo = mimeinfo->children;
488 
489 	while (mimeinfo != NULL) {
490 		mimeview_append_part(mimeview, mimeinfo, &iter, parent);
491 
492 		if (mimeinfo->children)
493 			mimeview_set_multipart_tree(mimeview, mimeinfo, &iter);
494 		else if (mimeinfo->sub &&
495 			 mimeinfo->sub->mime_type != MIME_TEXT &&
496 			 mimeinfo->sub->mime_type != MIME_TEXT_HTML)
497 			mimeview_set_multipart_tree(mimeview, mimeinfo->sub,
498 						    &iter);
499 		mimeinfo = mimeinfo->next;
500 	}
501 }
502 
get_part_name(MimeInfo * partinfo)503 static gchar *get_part_name(MimeInfo *partinfo)
504 {
505 #if USE_GPGME
506 	if (partinfo->sigstatus)
507 		return partinfo->sigstatus;
508 	else
509 #endif
510 	if (partinfo->name)
511 		return partinfo->name;
512 	else if (partinfo->filename)
513 		return partinfo->filename;
514 	else
515 		return "";
516 }
517 
mimeview_append_part(MimeView * mimeview,MimeInfo * partinfo,GtkTreeIter * iter,GtkTreeIter * parent)518 static gboolean mimeview_append_part(MimeView *mimeview, MimeInfo *partinfo,
519 				     GtkTreeIter *iter, GtkTreeIter *parent)
520 {
521 	gchar *mime_type;
522 	gchar *size;
523 	gchar *name;
524 
525 	mime_type = partinfo->content_type ? partinfo->content_type : "";
526 	size = to_human_readable(partinfo->content_size);
527 	name = get_part_name(partinfo);
528 	if (name && *name != '\0')
529 		mimeview->has_attach_file = TRUE;
530 
531 	gtk_tree_store_append(mimeview->store, iter, parent);
532 	gtk_tree_store_set(mimeview->store, iter,
533 			   COL_MIMETYPE, mime_type,
534 			   COL_SIZE, size,
535 			   COL_NAME, name,
536 			   COL_MIME_INFO, partinfo,
537 			   -1);
538 
539 	return TRUE;
540 }
541 
mimeview_show_message_part(MimeView * mimeview,MimeInfo * partinfo)542 static void mimeview_show_message_part(MimeView *mimeview, MimeInfo *partinfo)
543 {
544 	FILE *fp;
545 	const gchar *fname;
546 
547 	if (!partinfo) return;
548 
549 	fname = mimeview->messageview->file;
550 	if (!fname) return;
551 
552 	if ((fp = g_fopen(fname, "rb")) == NULL) {
553 		FILE_OP_ERROR(fname, "fopen");
554 		return;
555 	}
556 
557 	if (fseek(fp, partinfo->fpos, SEEK_SET) < 0) {
558 		FILE_OP_ERROR(fname, "fseek");
559 		fclose(fp);
560 		return;
561 	}
562 
563 	mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
564 	textview_show_part(mimeview->textview, partinfo, fp);
565 
566 	fclose(fp);
567 }
568 
mimeview_show_image_part(MimeView * mimeview,MimeInfo * partinfo)569 static void mimeview_show_image_part(MimeView *mimeview, MimeInfo *partinfo)
570 {
571 	gchar *filename;
572 
573 	if (!partinfo) return;
574 
575 	filename = procmime_get_tmp_file_name(partinfo);
576 
577 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0)
578 		alertpanel_error
579 			(_("Can't get the part of multipart message."));
580 	else {
581 		mimeview_change_view_type(mimeview, MIMEVIEW_IMAGE);
582 		imageview_show_image(mimeview->imageview, partinfo, filename,
583 				     prefs_common.resize_image);
584 		g_unlink(filename);
585 	}
586 
587 	g_free(filename);
588 }
589 
save_as_button_clicked(GtkWidget * widget,gpointer data)590 static void save_as_button_clicked(GtkWidget *widget, gpointer data)
591 {
592 	MimeView *mimeview = (MimeView *)data;
593 
594 	mimeview_save_as(mimeview);
595 }
596 
display_as_text_button_clicked(GtkWidget * widget,gpointer data)597 static void display_as_text_button_clicked(GtkWidget *widget, gpointer data)
598 {
599 	MimeView *mimeview = (MimeView *)data;
600 
601 	mimeview_display_as_text(mimeview);
602 }
603 
open_button_clicked(GtkWidget * widget,gpointer data)604 static void open_button_clicked(GtkWidget *widget, gpointer data)
605 {
606 	MimeView *mimeview = (MimeView *)data;
607 
608 	mimeview_launch(mimeview);
609 }
610 
open_with_button_clicked(GtkWidget * widget,gpointer data)611 static void open_with_button_clicked(GtkWidget *widget, gpointer data)
612 {
613 	MimeView *mimeview = (MimeView *)data;
614 
615 	mimeview_open_with(mimeview);
616 }
617 
mimeview_show_mime_part(MimeView * mimeview,MimeInfo * partinfo)618 static void mimeview_show_mime_part(MimeView *mimeview, MimeInfo *partinfo)
619 {
620 	TextView *textview = mimeview->textview;
621 	GtkTextBuffer *buffer;
622 	GtkTextIter iter;
623 	GtkTextChildAnchor *anchor;
624 	GtkWidget *vbbox;
625 	GtkWidget *button;
626 	gchar buf[BUFFSIZE];
627 
628 	if (!partinfo) return;
629 
630 	textview_set_font(textview, NULL);
631 	textview_clear(textview);
632 
633 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
634 	gtk_text_buffer_get_start_iter(buffer, &iter);
635 
636 	gtk_text_buffer_insert(buffer, &iter,
637 			       _("Select an action for the attached file:\n"),
638 			       -1);
639 	if (partinfo->filename || partinfo->name)
640 		g_snprintf(buf, sizeof(buf), "[%s  %s (%s)]\n\n",
641 			   partinfo->filename ? partinfo->filename :
642 			   partinfo->name,
643 			   partinfo->content_type,
644 			   to_human_readable(partinfo->content_size));
645 	else
646 		g_snprintf(buf, sizeof(buf), "[%s (%s)]\n\n",
647 			   partinfo->content_type,
648 			   to_human_readable(partinfo->content_size));
649 	gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1,
650 						 "mimepart", NULL);
651 
652 	vbbox = gtk_vbutton_box_new();
653 	gtk_box_set_spacing(GTK_BOX(vbbox), 5);
654 
655 	button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
656 	gtk_container_add(GTK_CONTAINER(vbbox), button);
657 	g_signal_connect(button, "clicked", G_CALLBACK(open_button_clicked),
658 			 mimeview);
659 	button = gtk_button_new_with_mnemonic(_("Open _with..."));
660 	gtk_container_add(GTK_CONTAINER(vbbox), button);
661 	g_signal_connect(button, "clicked",
662 			 G_CALLBACK(open_with_button_clicked), mimeview);
663 	button = gtk_button_new_with_mnemonic(_("_Display as text"));
664 	gtk_container_add(GTK_CONTAINER(vbbox), button);
665 	g_signal_connect(button, "clicked",
666 			 G_CALLBACK(display_as_text_button_clicked), mimeview);
667 	button = gtk_button_new_with_mnemonic(_("_Save as..."));
668 	gtk_container_add(GTK_CONTAINER(vbbox), button);
669 	g_signal_connect(button, "clicked", G_CALLBACK(save_as_button_clicked),
670 			 mimeview);
671 
672 	gtk_widget_show_all(vbbox);
673 
674 	anchor = gtk_text_buffer_create_child_anchor(buffer, &iter);
675 	gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text),
676 					  vbbox, anchor);
677 }
678 
679 #if USE_GPGME
check_signature_button_clicked(GtkWidget * widget,gpointer data)680 static void check_signature_button_clicked(GtkWidget *widget, gpointer data)
681 {
682 	MimeView *mimeview = (MimeView *)data;
683 
684 	mimeview_check_signature(mimeview);
685 }
686 
mimeview_show_signature_part(MimeView * mimeview,MimeInfo * partinfo)687 static void mimeview_show_signature_part(MimeView *mimeview,
688 					 MimeInfo *partinfo)
689 {
690 	TextView *textview = mimeview->textview;
691 	GtkTextBuffer *buffer;
692 	GtkTextIter iter;
693 	GtkTextChildAnchor *anchor;
694 	GtkWidget *vbbox;
695 	GtkWidget *button;
696 
697 	if (!partinfo) return;
698 
699 	textview_set_font(textview, NULL);
700 	textview_clear(textview);
701 
702 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
703 	gtk_text_buffer_get_start_iter(buffer, &iter);
704 
705 	if (partinfo->sigstatus_full) {
706 		gtk_text_buffer_insert
707 			(buffer, &iter, partinfo->sigstatus_full, -1);
708 		return;
709 	}
710 
711 	gtk_text_buffer_insert
712 		(buffer, &iter,
713 		 _("This signature has not been checked yet.\n\n"), -1);
714 
715 	vbbox = gtk_vbutton_box_new();
716 	gtk_box_set_spacing(GTK_BOX(vbbox), 5);
717 
718 	if (rfc2015_is_available()) {
719 		button = gtk_button_new_with_mnemonic(_("_Check signature"));
720 		gtk_container_add(GTK_CONTAINER(vbbox), button);
721 		g_signal_connect(button, "clicked",
722 				 G_CALLBACK(check_signature_button_clicked),
723 				 mimeview);
724 	}
725 
726 	gtk_widget_show_all(vbbox);
727 
728 	anchor = gtk_text_buffer_create_child_anchor(buffer, &iter);
729 	gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text),
730 					  vbbox, anchor);
731 }
732 #endif /* USE_GPGME */
733 
mimeview_change_view_type(MimeView * mimeview,MimeViewType type)734 static void mimeview_change_view_type(MimeView *mimeview, MimeViewType type)
735 {
736 	TextView  *textview  = mimeview->textview;
737 	ImageView *imageview = mimeview->imageview;
738 	GList *children;
739 
740 	if (mimeview->type == type) return;
741 
742 	children = gtk_container_get_children
743 		(GTK_CONTAINER(mimeview->mime_vbox));
744 	if (children) {
745 		gtkut_container_remove(GTK_CONTAINER(mimeview->mime_vbox),
746 				       GTK_WIDGET(children->data));
747 		g_list_free(children);
748 	}
749 
750 	switch (mimeview->type) {
751 	case MIMEVIEW_IMAGE:
752 		imageview_clear(mimeview->imageview);
753 		break;
754 	case MIMEVIEW_TEXT:
755 		textview_clear(mimeview->textview);
756 		break;
757 	default:
758 		break;
759 	}
760 
761 	switch (type) {
762 	case MIMEVIEW_IMAGE:
763 		gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox),
764 				  GTK_WIDGET_PTR(imageview));
765 		break;
766 	case MIMEVIEW_TEXT:
767 		gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox),
768 				  GTK_WIDGET_PTR(textview));
769 		break;
770 	default:
771 		return;
772 	}
773 
774 	mimeview->type = type;
775 }
776 
mimeview_selection_changed(GtkTreeSelection * selection,MimeView * mimeview)777 static void mimeview_selection_changed(GtkTreeSelection *selection,
778 				       MimeView *mimeview)
779 {
780 	GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store);
781 	GtkTreeIter iter;
782 	GtkTreePath *path;
783 	MimeInfo *partinfo;
784 
785 	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
786 		if (mimeview->opened) {
787 			gtk_tree_path_free(mimeview->opened);
788 			mimeview->opened = NULL;
789 		}
790 		return;
791 	}
792 
793 	path = gtk_tree_model_get_path(model, &iter);
794 
795 	if (mimeview->opened &&
796 	    gtk_tree_path_compare(mimeview->opened, path) == 0) {
797 		gtk_tree_path_free(path);
798 		return;
799 	}
800 
801 	gtk_tree_path_free(mimeview->opened);
802 	mimeview->opened = path;
803 	path = NULL;
804 
805 	gtk_tree_model_get(model, &iter, COL_MIME_INFO, &partinfo, -1);
806 	if (!partinfo)
807 		return;
808 
809 	switch (partinfo->mime_type) {
810 	case MIME_TEXT:
811 	case MIME_TEXT_HTML:
812 	case MIME_MESSAGE_RFC822:
813 	case MIME_MULTIPART:
814 		mimeview_show_message_part(mimeview, partinfo);
815 		break;
816 	case MIME_IMAGE:
817 		if (prefs_common.inline_image) {
818 			mimeview_show_image_part(mimeview, partinfo);
819 			break;
820 		}
821 		/* FALLTHROUGH */
822 	default:
823 		mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
824 #if USE_GPGME
825 		if (rfc2015_is_signature_part(partinfo))
826 			mimeview_show_signature_part(mimeview, partinfo);
827 		else
828 #endif
829 			mimeview_show_mime_part(mimeview, partinfo);
830 		break;
831 	}
832 }
833 
mimeview_button_pressed(GtkWidget * widget,GdkEventButton * event,MimeView * mimeview)834 static gint mimeview_button_pressed(GtkWidget *widget, GdkEventButton *event,
835 				    MimeView *mimeview)
836 {
837 	GtkTreeView *treeview = GTK_TREE_VIEW(widget);
838 	MimeInfo *partinfo;
839 
840 	if (!event) return FALSE;
841 
842 	if (event->button == 2 || event->button == 3) {
843 		GtkTreePath *path;
844 
845 		if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
846 						   &path, NULL, NULL, NULL))
847 			return FALSE;
848 		gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
849 		gtk_tree_path_free(path);
850 	}
851 
852 	if (event->button == 2 ||
853 	    (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) {
854 		/* call external program for image, audio or html */
855 		mimeview_launch(mimeview);
856 	} else if (event->button == 3) {
857 		partinfo = mimeview_get_selected_part(mimeview);
858 		if (partinfo && (partinfo->mime_type == MIME_TEXT ||
859 				 partinfo->mime_type == MIME_TEXT_HTML ||
860 				 partinfo->mime_type == MIME_MESSAGE_RFC822 ||
861 				 partinfo->mime_type == MIME_IMAGE ||
862 				 partinfo->mime_type == MIME_MULTIPART))
863 			menu_set_sensitive(mimeview->popupfactory,
864 					   "/Display as text", FALSE);
865 		else
866 			menu_set_sensitive(mimeview->popupfactory,
867 					   "/Display as text", TRUE);
868 		if (partinfo &&
869 		    partinfo->mime_type == MIME_APPLICATION_OCTET_STREAM)
870 			menu_set_sensitive(mimeview->popupfactory,
871 					   "/Open", FALSE);
872 		else
873 			menu_set_sensitive(mimeview->popupfactory,
874 					   "/Open", TRUE);
875 
876 		menu_set_sensitive(mimeview->popupfactory,
877 				   "/Save all...", mimeview->has_attach_file);
878 
879 		if (partinfo && (partinfo->mime_type == MIME_TEXT ||
880 				 partinfo->mime_type == MIME_TEXT_HTML ||
881 				 partinfo->mime_type == MIME_MESSAGE_RFC822))
882 			menu_set_sensitive(mimeview->popupfactory,
883 					   "/Print...", TRUE);
884 		else
885 			menu_set_sensitive(mimeview->popupfactory,
886 					   "/Print...", FALSE);
887 
888 		if (partinfo && partinfo->mime_type == MIME_MESSAGE_RFC822) {
889 			gtk_widget_show(mimeview->reply_separator);
890 			gtk_widget_show(mimeview->reply_menuitem);
891 		} else {
892 			gtk_widget_hide(mimeview->reply_separator);
893 			gtk_widget_hide(mimeview->reply_menuitem);
894 		}
895 #if USE_GPGME
896 		menu_set_sensitive(mimeview->popupfactory,
897 				   "/Check signature",
898 				   mimeview_is_signed(mimeview));
899 #endif
900 
901 		gtk_menu_popup(GTK_MENU(mimeview->popupmenu),
902 			       NULL, NULL, NULL, NULL,
903 			       event->button, event->time);
904 		return TRUE;
905 	}
906 
907 	return FALSE;
908 }
909 
mimeview_pass_key_press_event(MimeView * mimeview,GdkEventKey * event)910 void mimeview_pass_key_press_event(MimeView *mimeview, GdkEventKey *event)
911 {
912 	mimeview_key_pressed(mimeview->treeview, event, mimeview);
913 }
914 
915 #define BREAK_ON_MODIFIER_KEY() \
916 	if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
917 
918 #define KEY_PRESS_EVENT_STOP() \
919 	g_signal_stop_emission_by_name(G_OBJECT(treeview), "key_press_event");
920 
mimeview_key_pressed(GtkWidget * widget,GdkEventKey * event,MimeView * mimeview)921 static gint mimeview_key_pressed(GtkWidget *widget, GdkEventKey *event,
922 				 MimeView *mimeview)
923 {
924 	SummaryView *summaryview = NULL;
925 	GtkTreeView *treeview = GTK_TREE_VIEW(widget);
926 	GtkTreeModel *model = GTK_TREE_MODEL(mimeview->store);
927 	GtkTreeIter iter;
928 	gboolean mod_pressed;
929 
930 	if (!event) return FALSE;
931 	if (!mimeview->opened) return FALSE;
932 	if (!gtk_tree_model_get_iter(model, &iter, mimeview->opened))
933 		return FALSE;
934 
935 	if (mimeview->messageview->mainwin)
936 		summaryview = mimeview->messageview->mainwin->summaryview;
937 	mod_pressed =
938 		((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
939 
940 	switch (event->keyval) {
941 	case GDK_space:
942 	case GDK_KP_Space:
943 		if (textview_scroll_page(mimeview->textview, mod_pressed))
944 			return TRUE;
945 
946 		if (gtkut_tree_model_next(model, &iter)) {
947 			GtkTreePath *path;
948 
949 			path = gtk_tree_model_get_path(model, &iter);
950 			gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
951 			gtk_tree_path_free(path);
952 			return TRUE;
953 		}
954 		if (summaryview)
955 			summary_pass_key_press_event(summaryview, event);
956 		break;
957 	case GDK_BackSpace:
958 		textview_scroll_page(mimeview->textview, TRUE);
959 		return TRUE;
960 	case GDK_Return:
961 	case GDK_KP_Enter:
962 		textview_scroll_one_line(mimeview->textview, mod_pressed);
963 		return TRUE;
964 	case GDK_t:
965 		BREAK_ON_MODIFIER_KEY();
966 		KEY_PRESS_EVENT_STOP();
967 		mimeview_display_as_text(mimeview);
968 		return TRUE;
969 	case GDK_Escape:
970 		if (summaryview)
971 			gtk_widget_grab_focus(summaryview->treeview);
972 		break;
973 	case GDK_Left:
974 	case GDK_Delete:
975 	case GDK_KP_Left:
976 	case GDK_KP_Delete:
977 		if (summaryview)
978 			summary_pass_key_press_event(summaryview, event);
979 		break;
980 	default:
981 		break;
982 	}
983 
984 	return FALSE;
985 }
986 
mimeview_drag_begin(GtkWidget * widget,GdkDragContext * drag_context,MimeView * mimeview)987 static void mimeview_drag_begin(GtkWidget *widget, GdkDragContext *drag_context,
988 				MimeView *mimeview)
989 {
990 	gchar *filename;
991 	gchar *bname = NULL;
992 	MimeInfo *partinfo;
993 
994 	if (!mimeview->opened) return;
995 	if (!mimeview->messageview->file) return;
996 
997 	partinfo = mimeview_get_selected_part(mimeview);
998 	if (!partinfo) return;
999 
1000 	filename = partinfo->filename ? partinfo->filename : partinfo->name;
1001 	if (filename) {
1002 		const gchar *bname_;
1003 
1004 		bname_ = g_basename(filename);
1005 		bname = conv_filename_from_utf8(bname_);
1006 		subst_for_filename(bname);
1007 	}
1008 	if (!bname || *bname == '\0')
1009 		filename = procmime_get_tmp_file_name_for_user(partinfo);
1010 	else
1011 		filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
1012 				       bname, NULL);
1013 
1014 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) {
1015 		g_warning(_("Can't save the part of multipart message."));
1016 	} else
1017 		mimeview->drag_file = encode_uri(filename);
1018 
1019 	g_free(filename);
1020 
1021 	gtk_drag_set_icon_default(drag_context);
1022 }
1023 
mimeview_drag_end(GtkWidget * widget,GdkDragContext * drag_context,MimeView * mimeview)1024 static void mimeview_drag_end(GtkWidget *widget, GdkDragContext *drag_context,
1025 			      MimeView *mimeview)
1026 {
1027 	if (mimeview->drag_file) {
1028 		g_free(mimeview->drag_file);
1029 		mimeview->drag_file = NULL;
1030 	}
1031 }
1032 
mimeview_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * selection_data,guint info,guint time,MimeView * mimeview)1033 static void mimeview_drag_data_get(GtkWidget	    *widget,
1034 				   GdkDragContext   *drag_context,
1035 				   GtkSelectionData *selection_data,
1036 				   guint	     info,
1037 				   guint	     time,
1038 				   MimeView	    *mimeview)
1039 {
1040 	if (!mimeview->drag_file) return;
1041 
1042 	gtk_selection_data_set(selection_data, selection_data->target, 8,
1043 			       (guchar *)mimeview->drag_file,
1044 			       strlen(mimeview->drag_file));
1045 }
1046 
mimeview_display_as_text(MimeView * mimeview)1047 static void mimeview_display_as_text(MimeView *mimeview)
1048 {
1049 	MimeInfo *partinfo;
1050 
1051 	if (!mimeview->opened) return;
1052 
1053 	partinfo = mimeview_get_selected_part(mimeview);
1054 	g_return_if_fail(partinfo != NULL);
1055 	mimeview_show_message_part(mimeview, partinfo);
1056 }
1057 
mimeview_save_as(MimeView * mimeview)1058 void mimeview_save_as(MimeView *mimeview)
1059 {
1060 	MimeInfo *partinfo;
1061 
1062 	if (!mimeview->opened) return;
1063 	if (!mimeview->messageview->file) return;
1064 
1065 	partinfo = mimeview_get_selected_part(mimeview);
1066 	g_return_if_fail(partinfo != NULL);
1067 
1068 	mimeview_save_part_as(mimeview, partinfo);
1069 }
1070 
mimeview_save_all(MimeView * mimeview)1071 void mimeview_save_all(MimeView *mimeview)
1072 {
1073 	gchar *dir;
1074 
1075 	dir = filesel_select_dir(NULL);
1076 	if (!dir) return;
1077 
1078 	if (procmime_get_all_parts(dir, mimeview->messageview->file, mimeview->messageview->mimeinfo) < 0)
1079 		alertpanel_error(_("Can't save the attachments."));
1080 
1081 	g_free(dir);
1082 }
1083 
mimeview_print(MimeView * mimeview)1084 void mimeview_print(MimeView *mimeview)
1085 {
1086 	MimeInfo *partinfo;
1087 
1088 	if (!mimeview->opened) return;
1089 	if (!mimeview->messageview->file) return;
1090 
1091 	partinfo = mimeview_get_selected_part(mimeview);
1092 	g_return_if_fail(partinfo != NULL);
1093 
1094 	mimeview_print_part(mimeview, partinfo);
1095 }
1096 
mimeview_print_part(MimeView * mimeview,MimeInfo * partinfo)1097 void mimeview_print_part(MimeView *mimeview, MimeInfo *partinfo)
1098 {
1099 	g_return_if_fail(partinfo != NULL);
1100 
1101 	if (!mimeview->messageview->file) return;
1102 
1103 	if (partinfo->mime_type == MIME_MESSAGE_RFC822) {
1104 		gchar *filename;
1105 		MsgInfo *msginfo;
1106 		MsgFlags flags = {0, 0};
1107 
1108 		filename = procmime_get_tmp_file_name(partinfo);
1109 		if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) {
1110 			alertpanel_error
1111 				(_("Can't save the part of multipart message."));
1112 			g_free(filename);
1113 			return;
1114 		}
1115 
1116 		msginfo = procheader_parse_file(filename, flags, TRUE);
1117 		msginfo->file_path = filename;
1118 		filename = NULL;
1119 		printing_print_message
1120 			(msginfo, mimeview->textview->show_all_headers);
1121 		procmsg_msginfo_free(msginfo);
1122 	} else if (partinfo->mime_type == MIME_TEXT ||
1123 		   partinfo->mime_type == MIME_TEXT_HTML) {
1124 		printing_print_message_part(mimeview->messageview->msginfo,
1125 					    partinfo);
1126 	}
1127 }
1128 
mimeview_launch_part(MimeView * mimeview,MimeInfo * partinfo)1129 void mimeview_launch_part(MimeView *mimeview, MimeInfo *partinfo)
1130 {
1131 	gchar *filename;
1132 
1133 	g_return_if_fail(partinfo != NULL);
1134 
1135 	if (!mimeview->messageview->file) return;
1136 
1137 	filename = procmime_get_tmp_file_name_for_user(partinfo);
1138 
1139 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0)
1140 		alertpanel_error
1141 			(_("Can't save the part of multipart message."));
1142 	else
1143 		mimeview_view_file(filename, partinfo, NULL);
1144 
1145 	g_free(filename);
1146 }
1147 
mimeview_open_part_with(MimeView * mimeview,MimeInfo * partinfo)1148 void mimeview_open_part_with(MimeView *mimeview, MimeInfo *partinfo)
1149 {
1150 	gchar *filename;
1151 	gchar *cmd;
1152 
1153 	g_return_if_fail(partinfo != NULL);
1154 
1155 	if (!mimeview->messageview->file) return;
1156 
1157 	filename = procmime_get_tmp_file_name_for_user(partinfo);
1158 
1159 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) {
1160 		alertpanel_error
1161 			(_("Can't save the part of multipart message."));
1162 		g_free(filename);
1163 		return;
1164 	}
1165 
1166 	if (!prefs_common.mime_open_cmd_history)
1167 		prefs_common.mime_open_cmd_history =
1168 			add_history(NULL, prefs_common.mime_open_cmd);
1169 
1170 	cmd = input_dialog_combo
1171 		(_("Open with"),
1172 		 _("Enter the command line to open file:\n"
1173 		   "(`%s' will be replaced with file name)"),
1174 		 prefs_common.mime_open_cmd,
1175 		 prefs_common.mime_open_cmd_history,
1176 		 TRUE);
1177 	if (cmd) {
1178 		mimeview_view_file(filename, partinfo, cmd);
1179 		g_free(prefs_common.mime_open_cmd);
1180 		prefs_common.mime_open_cmd = cmd;
1181 		prefs_common.mime_open_cmd_history =
1182 			add_history(prefs_common.mime_open_cmd_history, cmd);
1183 	}
1184 
1185 	g_free(filename);
1186 }
1187 
mimeview_save_part_as(MimeView * mimeview,MimeInfo * partinfo)1188 void mimeview_save_part_as(MimeView *mimeview, MimeInfo *partinfo)
1189 {
1190 	gchar *filename = NULL;
1191 
1192 	g_return_if_fail(partinfo != NULL);
1193 
1194 	if (!mimeview->messageview->file) return;
1195 
1196 	if (partinfo->filename) {
1197 		filename = filesel_save_as(partinfo->filename);
1198 	} else if (partinfo->name) {
1199 		gchar *defname;
1200 
1201 		defname = g_strdup(partinfo->name);
1202 		subst_for_filename(defname);
1203 		filename = filesel_save_as(defname);
1204 		g_free(defname);
1205 	} else
1206 		filename = filesel_save_as(NULL);
1207 
1208 	if (!filename)
1209 		return;
1210 
1211 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0)
1212 		alertpanel_error
1213 			(_("Can't save the part of multipart message."));
1214 
1215 	g_free(filename);
1216 }
1217 
mimeview_launch(MimeView * mimeview)1218 static void mimeview_launch(MimeView *mimeview)
1219 {
1220 	MimeInfo *partinfo;
1221 
1222 	if (!mimeview->opened) return;
1223 	if (!mimeview->messageview->file) return;
1224 
1225 	partinfo = mimeview_get_selected_part(mimeview);
1226 	g_return_if_fail(partinfo != NULL);
1227 
1228 	mimeview_launch_part(mimeview, partinfo);
1229 }
1230 
mimeview_open_with(MimeView * mimeview)1231 static void mimeview_open_with(MimeView *mimeview)
1232 {
1233 	MimeInfo *partinfo;
1234 
1235 	if (!mimeview->opened) return;
1236 	if (!mimeview->messageview->file) return;
1237 
1238 	partinfo = mimeview_get_selected_part(mimeview);
1239 	g_return_if_fail(partinfo != NULL);
1240 
1241 	mimeview_open_part_with(mimeview, partinfo);
1242 }
1243 
mimeview_view_file(const gchar * filename,MimeInfo * partinfo,const gchar * cmdline)1244 static void mimeview_view_file(const gchar *filename, MimeInfo *partinfo,
1245 			       const gchar *cmdline)
1246 {
1247 	const gchar *cmd = NULL;
1248 	gchar buf[BUFFSIZE];
1249 
1250 	if (!cmdline) {
1251 #ifdef G_OS_WIN32
1252 		DWORD dwtype;
1253 
1254 		if (g_file_test(filename, G_FILE_TEST_IS_EXECUTABLE) ||
1255 		    str_has_suffix_case(filename, ".scr") ||
1256 		    str_has_suffix_case(filename, ".pif") ||
1257 		    GetBinaryType(filename, &dwtype)) {
1258 			alertpanel_full
1259 				(_("Opening executable file"),
1260 				 _("This is an executable file. Opening executable file is restricted for security.\n"
1261 				   "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."),
1262 				 ALERT_WARNING, G_ALERTDEFAULT, FALSE,
1263 				 GTK_STOCK_OK, NULL, NULL);
1264 			return;
1265 		}
1266 		execute_open_file(filename, partinfo->content_type);
1267 		return;
1268 #elif defined(__APPLE__)
1269 		if (g_file_test(filename, G_FILE_TEST_IS_EXECUTABLE) ||
1270 		    str_has_suffix_case(filename, ".py") ||
1271 		    str_has_suffix_case(filename, ".rb") ||
1272 		    str_has_suffix_case(filename, ".sh")) {
1273 			alertpanel_full
1274 				(_("Opening executable file"),
1275 				 _("This is an executable file. Opening executable file is restricted for security.\n"
1276 				   "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."),
1277 				 ALERT_WARNING, G_ALERTDEFAULT, FALSE,
1278 				 GTK_STOCK_OK, NULL, NULL);
1279 			return;
1280 		}
1281 		execute_open_file(filename, partinfo->content_type);
1282 		return;
1283 #else
1284 		if (MIME_IMAGE == partinfo->mime_type)
1285 			cmd = prefs_common.mime_image_viewer;
1286 		else if (MIME_AUDIO == partinfo->mime_type)
1287 			cmd = prefs_common.mime_audio_player;
1288 		else if (MIME_TEXT_HTML == partinfo->mime_type)
1289 			cmd = prefs_common.uri_cmd;
1290 		if (!cmd) {
1291 			if (prefs_common.mime_cmd) {
1292 				if (str_find_format_times
1293 					(prefs_common.mime_cmd, 's') == 2) {
1294 					g_snprintf(buf, sizeof(buf),
1295 						   prefs_common.mime_cmd,
1296 						   partinfo->content_type,
1297 						   "%s");
1298 					cmd = buf;
1299 				} else
1300 					cmd = prefs_common.mime_cmd;
1301 			} else {
1302 				procmime_execute_open_file
1303 					(filename, partinfo->content_type);
1304 				return;
1305 			}
1306 		}
1307 #endif
1308 	} else
1309 		cmd = cmdline;
1310 
1311 	if (cmd && str_find_format_times(cmd, 's') == 1) {
1312 		gchar *cmdbuf;
1313 		cmdbuf = g_strdup_printf(cmd, filename);
1314 		execute_command_line(cmdbuf, TRUE);
1315 		g_free(cmdbuf);
1316 	} else if (cmd)
1317 		g_warning("MIME viewer command line is invalid: '%s'", cmd);
1318 }
1319 
mimeview_reply(MimeView * mimeview,guint action)1320 static void mimeview_reply(MimeView *mimeview, guint action)
1321 {
1322 	MimeInfo *partinfo;
1323 	gchar *filename;
1324 	MsgInfo *msginfo;
1325 	MsgFlags flags = {0, 0};
1326 	ComposeMode mode = action;
1327 
1328 	if (!mimeview->opened) return;
1329 	if (!mimeview->messageview->file) return;
1330 
1331 	partinfo = mimeview_get_selected_part(mimeview);
1332 	g_return_if_fail(partinfo != NULL);
1333 
1334 	if (partinfo->mime_type != MIME_MESSAGE_RFC822)
1335 		return;
1336 
1337 	filename = procmime_get_tmp_file_name(partinfo);
1338 	if (procmime_get_part(filename, mimeview->messageview->file, partinfo) < 0) {
1339 		alertpanel_error
1340 			(_("Can't save the part of multipart message."));
1341 			g_free(filename);
1342 			return;
1343 	}
1344 
1345 	msginfo = procheader_parse_file(filename, flags, TRUE);
1346 	msginfo->file_path = filename;
1347 	filename = NULL;
1348 	if (prefs_common.reply_with_quote)
1349 		mode |= COMPOSE_WITH_QUOTE;
1350 
1351 	if (mimeview->messageview->mainwin)
1352 		compose_reply(msginfo, mimeview->messageview->mainwin->summaryview->folder_item,
1353 			      mode, NULL);
1354 	else
1355 		compose_reply(msginfo, NULL, mode, NULL);
1356 
1357 	g_unlink(msginfo->file_path);
1358 	procmsg_msginfo_free(msginfo);
1359 }
1360 
1361 #if USE_GPGME
update_node_name_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1362 static gboolean update_node_name_func(GtkTreeModel *model, GtkTreePath *path,
1363 				      GtkTreeIter *iter, gpointer data)
1364 {
1365 	MimeInfo *partinfo;
1366 	gchar *part_name;
1367 
1368 	gtk_tree_model_get(model, iter, COL_MIME_INFO, &partinfo, -1);
1369 	g_return_val_if_fail(partinfo != NULL, FALSE);
1370 
1371 	part_name = get_part_name(partinfo);
1372 	gtk_tree_store_set(GTK_TREE_STORE(model), iter, COL_NAME, part_name,
1373 			   -1);
1374 
1375 	return FALSE;
1376 }
1377 
mimeview_update_names(MimeView * mimeview)1378 static void mimeview_update_names(MimeView *mimeview)
1379 {
1380 	gtk_tree_model_foreach(GTK_TREE_MODEL(mimeview->store),
1381 			       update_node_name_func, NULL);
1382 }
1383 
mimeview_update_signature_info(MimeView * mimeview)1384 static void mimeview_update_signature_info(MimeView *mimeview)
1385 {
1386 	MimeInfo *partinfo;
1387 
1388 	if (!mimeview) return;
1389 	if (!mimeview->opened) return;
1390 
1391 	partinfo = mimeview_get_selected_part(mimeview);
1392 	if (!partinfo) return;
1393 
1394 	if (rfc2015_is_signature_part(partinfo)) {
1395 		mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
1396 		mimeview_show_signature_part(mimeview, partinfo);
1397 	}
1398 }
1399 
mimeview_check_signature(MimeView * mimeview)1400 static void mimeview_check_signature(MimeView *mimeview)
1401 {
1402 	MimeInfo *mimeinfo;
1403 	FILE *fp;
1404 
1405 	g_return_if_fail (mimeview_is_signed(mimeview));
1406 
1407 	if (!rfc2015_is_available())
1408 		return;
1409 
1410 	mimeinfo = mimeview_get_selected_part(mimeview);
1411 	g_return_if_fail(mimeinfo != NULL);
1412 	g_return_if_fail(mimeview->messageview->file != NULL);
1413 
1414 	while (mimeinfo->parent)
1415 		mimeinfo = mimeinfo->parent;
1416 
1417 	if ((fp = g_fopen(mimeview->messageview->file, "rb")) == NULL) {
1418 		FILE_OP_ERROR(mimeview->messageview->file, "fopen");
1419 		return;
1420 	}
1421 
1422 	rfc2015_check_signature(mimeinfo, fp);
1423 	fclose(fp);
1424 
1425 	mimeview_update_names(mimeview);
1426 	mimeview_update_signature_info(mimeview);
1427 
1428 	textview_show_message(mimeview->messageview->textview, mimeinfo,
1429 			      mimeview->messageview->file);
1430 }
1431 #endif /* USE_GPGME */
1432