Statistics
| Revision:

root / src / mimeview.c @ 1035

History | View | Annotate | Download (32.9 kB)

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