Statistics
| Revision:

root / src / mimeview.c @ 3254

History | View | Annotate | Download (38.2 KB)

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

    
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

    
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
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
604
static void open_button_clicked(GtkWidget *widget, gpointer data)
605
{
606
        MimeView *mimeview = (MimeView *)data;
607

    
608
        mimeview_launch(mimeview);
609
}
610

    
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

    
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
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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(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

    
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

    
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

    
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

    
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

    
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

    
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

    
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

    
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(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

    
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(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

    
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

    
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

    
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

    
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

    
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
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

    
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

    
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

    
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 */