Statistics
| Revision:

root / src / mimeview.c @ 155

History | View | Annotate | Download (28.8 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/gtkctree.h>
31
#include <gtk/gtkvbox.h>
32
#include <gtk/gtkvpaned.h>
33
#include <gtk/gtksignal.h>
34
#include <gtk/gtkmenu.h>
35
#include <gtk/gtkdnd.h>
36
#include <gtk/gtkselection.h>
37
#include <stdio.h>
38
#include <unistd.h>
39
40
#include "main.h"
41
#include "mimeview.h"
42
#include "textview.h"
43
#include "imageview.h"
44
#include "procmime.h"
45
#include "summaryview.h"
46
#include "menu.h"
47
#include "filesel.h"
48
#include "alertpanel.h"
49
#include "inputdialog.h"
50
#include "utils.h"
51
#include "gtkutils.h"
52
#include "prefs_common.h"
53
#include "rfc2015.h"
54
55
typedef enum
56
{
57
        COL_MIMETYPE = 0,
58
        COL_SIZE     = 1,
59
        COL_NAME     = 2
60
} MimeViewColumnPos;
61
62
#define N_MIMEVIEW_COLS        3
63
64
static void mimeview_set_multipart_tree                (MimeView        *mimeview,
65
                                                 MimeInfo        *mimeinfo,
66
                                                 GtkCTreeNode        *parent);
67
static GtkCTreeNode *mimeview_append_part        (MimeView        *mimeview,
68
                                                 MimeInfo        *partinfo,
69
                                                 GtkCTreeNode        *parent);
70
static void mimeview_show_message_part                (MimeView        *mimeview,
71
                                                 MimeInfo        *partinfo);
72
static void mimeview_show_image_part                (MimeView        *mimeview,
73
                                                 MimeInfo        *partinfo);
74
static void mimeview_show_mime_part                (MimeView        *mimeview,
75
                                                 MimeInfo        *partinfo);
76
#if USE_GPGME
77
static void mimeview_show_signature_part        (MimeView        *mimeview,
78
                                                 MimeInfo        *partinfo);
79
#endif
80
static void mimeview_change_view_type                (MimeView        *mimeview,
81
                                                 MimeViewType         type);
82
83
static void mimeview_selected                (GtkCTree        *ctree,
84
                                         GtkCTreeNode        *node,
85
                                         gint                 column,
86
                                         MimeView        *mimeview);
87
static void mimeview_start_drag         (GtkWidget        *widget,
88
                                         gint                 button,
89
                                         GdkEvent        *event,
90
                                         MimeView        *mimeview);
91
static gint mimeview_button_pressed        (GtkWidget        *widget,
92
                                         GdkEventButton        *event,
93
                                         MimeView        *mimeview);
94
static gint mimeview_key_pressed        (GtkWidget        *widget,
95
                                         GdkEventKey        *event,
96
                                         MimeView        *mimeview);
97
98
static void mimeview_drag_data_get      (GtkWidget          *widget,
99
                                         GdkDragContext   *drag_context,
100
                                         GtkSelectionData *selection_data,
101
                                         guint                   info,
102
                                         guint                   time,
103
                                         MimeView          *mimeview);
104
105
static void mimeview_display_as_text        (MimeView        *mimeview);
106
static void mimeview_launch                (MimeView        *mimeview);
107
static void mimeview_open_with                (MimeView        *mimeview);
108
static void mimeview_view_file                (const gchar        *filename,
109
                                         MimeInfo        *partinfo,
110
                                         const gchar        *cmdline);
111
#if USE_GPGME
112
static void mimeview_check_signature        (MimeView        *mimeview);
113
#endif
114
115
static GtkItemFactoryEntry mimeview_popup_entries[] =
116
{
117
        {N_("/_Open"),                  NULL, mimeview_launch,          0, NULL},
118
        {N_("/Open _with..."),          NULL, mimeview_open_with,          0, NULL},
119
        {N_("/_Display as text"), NULL, mimeview_display_as_text, 0, NULL},
120
        {N_("/_Save as..."),          NULL, mimeview_save_as,          0, NULL}
121
#if USE_GPGME
122
        ,
123
        {N_("/_Check signature"), NULL, mimeview_check_signature, 0, NULL}
124
#endif
125
};
126
127
static GtkTargetEntry mimeview_mime_types[] =
128
{
129
        {"text/uri-list", 0, 0}
130
};
131
132
MimeView *mimeview_create(void)
133
{
134
        MimeView *mimeview;
135
136
        GtkWidget *paned;
137
        GtkWidget *scrolledwin;
138
        GtkWidget *ctree;
139
        GtkWidget *mime_vbox;
140
        GtkWidget *popupmenu;
141
        GtkItemFactory *popupfactory;
142
        gchar *titles[N_MIMEVIEW_COLS];
143
        gint n_entries;
144
        gint i;
145
146
        debug_print(_("Creating MIME view...\n"));
147
        mimeview = g_new0(MimeView, 1);
148
149
        titles[COL_MIMETYPE] = _("MIME Type");
150
        titles[COL_SIZE]     = _("Size");
151
        titles[COL_NAME]     = _("Name");
152
153
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
154
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
155
                                       GTK_POLICY_AUTOMATIC,
156
                                       GTK_POLICY_ALWAYS);
157
        gtk_widget_set_size_request(scrolledwin, -1, 80);
158
159
        ctree = gtk_sctree_new_with_titles(N_MIMEVIEW_COLS, 0, titles);
160
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
161
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
162
        gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_SIZE,
163
                                           GTK_JUSTIFY_RIGHT);
164
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_MIMETYPE, 240);
165
        gtk_clist_set_column_width(GTK_CLIST(ctree), COL_SIZE, 64);
166
        for (i = 0; i < N_MIMEVIEW_COLS; i++)
167
                GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
168
                                       GTK_CAN_FOCUS);
169
        gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
170
171
        g_signal_connect(G_OBJECT(ctree), "tree_select_row",
172
                         G_CALLBACK(mimeview_selected), mimeview);
173
        g_signal_connect(G_OBJECT(ctree), "button_press_event",
174
                         G_CALLBACK(mimeview_button_pressed), mimeview);
175
        g_signal_connect(G_OBJECT(ctree), "key_press_event",
176
                         G_CALLBACK(mimeview_key_pressed), mimeview);
177
        g_signal_connect(G_OBJECT (ctree),"start_drag",
178
                         G_CALLBACK (mimeview_start_drag), mimeview);
179
        g_signal_connect(G_OBJECT(ctree), "drag_data_get",
180
                         G_CALLBACK(mimeview_drag_data_get), mimeview);
181
    
182
        mime_vbox = gtk_vbox_new(FALSE, 0);
183
        gtk_container_set_reallocate_redraws(GTK_CONTAINER(mime_vbox), TRUE);
184
185
        paned = gtk_vpaned_new();
186
        gtk_paned_add1(GTK_PANED(paned), scrolledwin);
187
        gtk_paned_add2(GTK_PANED(paned), mime_vbox);
188
189
        n_entries = sizeof(mimeview_popup_entries) /
190
                sizeof(mimeview_popup_entries[0]);
191
        popupmenu = menu_create_items(mimeview_popup_entries, n_entries,
192
                                      "<MimeView>", &popupfactory, mimeview);
193
194
        mimeview->paned        = paned;
195
        mimeview->scrolledwin  = scrolledwin;
196
        mimeview->ctree        = ctree;
197
        mimeview->mime_vbox    = mime_vbox;
198
        mimeview->popupmenu    = popupmenu;
199
        mimeview->popupfactory = popupfactory;
200
        mimeview->type         = -1;
201
202
        return mimeview;
203
}
204
205
void mimeview_init(MimeView *mimeview)
206
{
207
        textview_init(mimeview->textview);
208
        imageview_init(mimeview->imageview);
209
}
210
211
/* 
212
 * Check whether the message is OpenPGP signed
213
 */
214
#if USE_GPGME
215
static gboolean mimeview_is_signed(MimeView *mimeview)
216
{
217
        MimeInfo *partinfo;
218
219
        debug_print("mimeview_is signed of %p\n", mimeview);
220
221
        if (!mimeview) return FALSE;
222
        if (!mimeview->opened) return FALSE;
223
224
        debug_print("mimeview_is_signed: open\n" );
225
226
        if (!mimeview->file) return FALSE;
227
228
        debug_print("mimeview_is_signed: file\n" );
229
230
        partinfo = mimeview_get_selected_part(mimeview);
231
        g_return_val_if_fail(partinfo != NULL, FALSE);
232
233
        /* walk the tree and see whether there is a signature somewhere */
234
        do {
235
                if (rfc2015_has_signature(partinfo))
236
                        return TRUE;
237
        } while ((partinfo = partinfo->parent) != NULL);
238
239
        debug_print("mimeview_is_signed: FALSE\n" );
240
241
        return FALSE;
242
}
243
244
static void set_unchecked_signature(MimeInfo *mimeinfo)
245
{
246
        MimeInfo **signedinfo;
247
248
        signedinfo = rfc2015_find_signature(mimeinfo);
249
        if (signedinfo == NULL) return;
250
251
        g_free(signedinfo[1]->sigstatus);
252
        signedinfo[1]->sigstatus =
253
                g_strdup(_("Select \"Check signature\" to check"));
254
255
        g_free(signedinfo[1]->sigstatus_full);
256
        signedinfo[1]->sigstatus_full = NULL;
257
258
    g_free(signedinfo);
259
}
260
#endif /* USE_GPGME */
261
262
void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo,
263
                           const gchar *file)
264
{
265
        GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
266
        GtkCTreeNode *node;
267
268
        mimeview_clear(mimeview);
269
        textview_clear(mimeview->messageview->textview);
270
271
        g_return_if_fail(file != NULL);
272
        g_return_if_fail(mimeinfo != NULL);
273
274
        mimeview->mimeinfo = mimeinfo;
275
276
        mimeview->file = g_strdup(file);
277
278
#if USE_GPGME
279
        if (prefs_common.auto_check_signatures) {
280
                FILE *fp;
281
282
                if ((fp = fopen(file, "rb")) == NULL) {
283
                        FILE_OP_ERROR(file, "fopen");
284
                        return;
285
                }
286
                rfc2015_check_signature(mimeinfo, fp);
287
                fclose(fp);
288
        } else
289
                set_unchecked_signature(mimeinfo);
290
#endif
291
292
        g_signal_handlers_block_by_func
293
                (G_OBJECT(ctree), G_CALLBACK(mimeview_selected), mimeview);
294
295
        mimeview_set_multipart_tree(mimeview, mimeinfo, NULL);
296
297
        g_signal_handlers_unblock_by_func
298
                (G_OBJECT(ctree), G_CALLBACK(mimeview_selected), mimeview);
299
300
        /* search first text part */
301
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
302
             node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
303
                MimeInfo *partinfo;
304
305
                partinfo = gtk_ctree_node_get_row_data(ctree, node);
306
                if (partinfo &&
307
                    (partinfo->mime_type == MIME_TEXT ||
308
                     partinfo->mime_type == MIME_TEXT_HTML))
309
                        break;
310
        }
311
        textview_show_message(mimeview->messageview->textview, mimeinfo, file);
312
313
        if (!node)
314
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
315
316
        if (node) {
317
                gtk_ctree_select(ctree, node);
318
                gtkut_ctree_set_focus_row(ctree, node);
319
                if (mimeview_get_selected_part(mimeview))
320
                        gtk_widget_grab_focus(mimeview->ctree);
321
        }
322
}
323
324
void mimeview_clear(MimeView *mimeview)
325
{
326
        GtkCList *clist = GTK_CLIST(mimeview->ctree);
327
328
        procmime_mimeinfo_free_all(mimeview->mimeinfo);
329
        mimeview->mimeinfo = NULL;
330
331
        gtk_clist_clear(clist);
332
        textview_clear(mimeview->textview);
333
        imageview_clear(mimeview->imageview);
334
335
        mimeview->opened = NULL;
336
337
        g_free(mimeview->file);
338
        mimeview->file = NULL;
339
}
340
341
void mimeview_destroy(MimeView *mimeview)
342
{
343
        textview_destroy(mimeview->textview);
344
        imageview_destroy(mimeview->imageview);
345
        procmime_mimeinfo_free_all(mimeview->mimeinfo);
346
        g_free(mimeview->file);
347
        g_free(mimeview);
348
}
349
350
MimeInfo *mimeview_get_selected_part(MimeView *mimeview)
351
{
352
        if (gtk_notebook_get_current_page
353
                (GTK_NOTEBOOK(mimeview->messageview->notebook)) == 0)
354
                return NULL;
355
356
        return gtk_ctree_node_get_row_data
357
                (GTK_CTREE(mimeview->ctree), mimeview->opened);
358
}
359
360
gboolean mimeview_step(MimeView *mimeview, GtkScrollType type)
361
{
362
        GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
363
        GtkCTreeNode *node;
364
365
        if (type == GTK_SCROLL_STEP_FORWARD) {
366
                node = gtkut_ctree_node_next(ctree, mimeview->opened);
367
                if (node)
368
                        gtkut_ctree_expand_parent_all(ctree, node);
369
                else
370
                        return FALSE;
371
        } else {
372
                if (mimeview->opened) {
373
                        node = GTK_CTREE_NODE_PREV(mimeview->opened);
374
                        if (!node) return FALSE;
375
                } else
376
                        return FALSE;
377
        }
378
379
        g_signal_emit_by_name(G_OBJECT(ctree), "scroll_vertical", type, 0.0);
380
381
        return TRUE;
382
}
383
384
static void mimeview_set_multipart_tree(MimeView *mimeview,
385
                                        MimeInfo *mimeinfo,
386
                                        GtkCTreeNode *parent)
387
{
388
        GtkCTreeNode *node;
389
390
        g_return_if_fail(mimeinfo != NULL);
391
392
        if (mimeinfo->children)
393
                mimeinfo = mimeinfo->children;
394
395
        while (mimeinfo != NULL) {
396
                node = mimeview_append_part(mimeview, mimeinfo, parent);
397
398
                if (mimeinfo->children)
399
                        mimeview_set_multipart_tree(mimeview, mimeinfo, node);
400
                else if (mimeinfo->sub &&
401
                         mimeinfo->sub->mime_type != MIME_TEXT &&
402
                         mimeinfo->sub->mime_type != MIME_TEXT_HTML)
403
                        mimeview_set_multipart_tree(mimeview, mimeinfo->sub,
404
                                                    node);
405
                mimeinfo = mimeinfo->next;
406
        }
407
}
408
409
static gchar *get_part_name(MimeInfo *partinfo)
410
{
411
#if USE_GPGME
412
        if (partinfo->sigstatus)
413
                return partinfo->sigstatus;
414
        else
415
#endif
416
        if (partinfo->name)
417
                return partinfo->name;
418
        else if (partinfo->filename)
419
                return partinfo->filename;
420
        else
421
                return "";
422
}
423
424
static GtkCTreeNode *mimeview_append_part(MimeView *mimeview,
425
                                          MimeInfo *partinfo,
426
                                          GtkCTreeNode *parent)
427
{
428
        GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
429
        GtkCTreeNode *node;
430
        gchar *str[N_MIMEVIEW_COLS];
431
432
        str[COL_MIMETYPE] =
433
                partinfo->content_type ? partinfo->content_type : "";
434
        str[COL_SIZE] = to_human_readable(partinfo->size);
435
        str[COL_NAME] = get_part_name(partinfo);
436
437
        node = gtk_ctree_insert_node(ctree, parent, NULL, str, 0,
438
                                     NULL, NULL, NULL, NULL,
439
                                     FALSE, TRUE);
440
        gtk_ctree_node_set_row_data(ctree, node, partinfo);
441
442
        return node;
443
}
444
445
static void mimeview_show_message_part(MimeView *mimeview, MimeInfo *partinfo)
446
{
447
        FILE *fp;
448
        const gchar *fname;
449
#if USE_GPGME
450
        MimeInfo *pi;
451
#endif
452
453
        if (!partinfo) return;
454
455
#if USE_GPGME
456
        for (pi = partinfo; pi && !pi->plaintextfile ; pi = pi->parent)
457
                ;
458
        fname = pi ? pi->plaintextfile : mimeview->file;
459
#else
460
        fname = mimeview->file;
461
#endif /* USE_GPGME */
462
        if (!fname) return;
463
464
        if ((fp = fopen(fname, "rb")) == NULL) {
465
                FILE_OP_ERROR(fname, "fopen");
466
                return;
467
        }
468
469
        if (fseek(fp, partinfo->fpos, SEEK_SET) < 0) {
470
                FILE_OP_ERROR(mimeview->file, "fseek");
471
                fclose(fp);
472
                return;
473
        }
474
475
        mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
476
        textview_show_part(mimeview->textview, partinfo, fp);
477
478
        fclose(fp);
479
}
480
481
static void mimeview_show_image_part(MimeView *mimeview, MimeInfo *partinfo)
482
{
483
        gchar *filename;
484
485
        if (!partinfo) return;
486
487
        filename = procmime_get_tmp_file_name(partinfo);
488
489
        if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
490
                alertpanel_error
491
                        (_("Can't get the part of multipart message."));
492
        else {
493
                mimeview_change_view_type(mimeview, MIMEVIEW_IMAGE);
494
                imageview_show_image(mimeview->imageview, partinfo, filename,
495
                                     prefs_common.resize_image);
496
                unlink(filename);
497
        }
498
499
        g_free(filename);
500
}
501
502
static void save_as_button_clicked(GtkWidget *widget, gpointer data)
503
{
504
        MimeView *mimeview = (MimeView *)data;
505
506
        mimeview_save_as(mimeview);
507
}
508
509
static void display_as_text_button_clicked(GtkWidget *widget, gpointer data)
510
{
511
        MimeView *mimeview = (MimeView *)data;
512
513
        mimeview_display_as_text(mimeview);
514
}
515
516
static void open_button_clicked(GtkWidget *widget, gpointer data)
517
{
518
        MimeView *mimeview = (MimeView *)data;
519
520
        mimeview_launch(mimeview);
521
}
522
523
static void open_with_button_clicked(GtkWidget *widget, gpointer data)
524
{
525
        MimeView *mimeview = (MimeView *)data;
526
527
        mimeview_open_with(mimeview);
528
}
529
530
static void mimeview_show_mime_part(MimeView *mimeview, MimeInfo *partinfo)
531
{
532
        TextView *textview = mimeview->textview;
533
        GtkTextBuffer *buffer;
534
        GtkTextIter iter;
535
        GtkTextChildAnchor *anchor;
536
        GtkWidget *vbbox;
537
        GtkWidget *button;
538
        gchar buf[BUFFSIZE];
539
540
        if (!partinfo) return;
541
542
        textview_set_font(textview, NULL);
543
        textview_clear(textview);
544
545
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
546
        gtk_text_buffer_get_start_iter(buffer, &iter);
547
548
        gtk_text_buffer_insert(buffer, &iter,
549
                               _("Select an action for the attached file:\n"),
550
                               -1);
551
        if (partinfo->filename || partinfo->name)
552
                g_snprintf(buf, sizeof(buf), "[%s  %s (%d bytes)]\n\n",
553
                           partinfo->filename ? partinfo->filename :
554
                           partinfo->name,
555
                           partinfo->content_type, partinfo->size);
556
        else
557
                g_snprintf(buf, sizeof(buf), "[%s (%d bytes)]\n\n",
558
                           partinfo->content_type, partinfo->size);
559
        gtk_text_buffer_insert(buffer, &iter, buf, -1);
560
561
        vbbox = gtk_vbutton_box_new();
562
        gtk_box_set_spacing(GTK_BOX(vbbox), 5);
563
564
        button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
565
        gtk_container_add(GTK_CONTAINER(vbbox), button);
566
        g_signal_connect(button, "clicked", G_CALLBACK(open_button_clicked),
567
                         mimeview);
568
        button = gtk_button_new_with_mnemonic(_("Open _with..."));
569
        gtk_container_add(GTK_CONTAINER(vbbox), button);
570
        g_signal_connect(button, "clicked",
571
                         G_CALLBACK(open_with_button_clicked), mimeview);
572
        button = gtk_button_new_with_mnemonic(_("_Display as text"));
573
        gtk_container_add(GTK_CONTAINER(vbbox), button);
574
        g_signal_connect(button, "clicked",
575
                         G_CALLBACK(display_as_text_button_clicked), mimeview);
576
        button = gtk_button_new_with_mnemonic(_("_Save as..."));
577
        gtk_container_add(GTK_CONTAINER(vbbox), button);
578
        g_signal_connect(button, "clicked", G_CALLBACK(save_as_button_clicked),
579
                         mimeview);
580
581
        gtk_widget_show_all(vbbox);
582
583
        anchor = gtk_text_buffer_create_child_anchor(buffer, &iter);
584
        gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text),
585
                                          vbbox, anchor);
586
}
587
588
#if USE_GPGME
589
static void check_signature_button_clicked(GtkWidget *widget, gpointer data)
590
{
591
        MimeView *mimeview = (MimeView *)data;
592
593
        mimeview_check_signature(mimeview);
594
}
595
596
static void mimeview_show_signature_part(MimeView *mimeview,
597
                                         MimeInfo *partinfo)
598
{
599
        TextView *textview = mimeview->textview;
600
        GtkTextBuffer *buffer;
601
        GtkTextIter iter;
602
        GtkTextChildAnchor *anchor;
603
        GtkWidget *vbbox;
604
        GtkWidget *button;
605
606
        if (!partinfo) return;
607
608
        textview_set_font(textview, NULL);
609
        textview_clear(textview);
610
611
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
612
        gtk_text_buffer_get_start_iter(buffer, &iter);
613
614
        if (partinfo->sigstatus_full) {
615
                gtk_text_buffer_insert
616
                        (buffer, &iter, partinfo->sigstatus_full, -1);
617
                return;
618
        }
619
620
        gtk_text_buffer_insert
621
                (buffer, &iter,
622
                 _("This signature has not been checked yet.\n\n"), -1);
623
624
        vbbox = gtk_vbutton_box_new();
625
        gtk_box_set_spacing(GTK_BOX(vbbox), 5);
626
627
        button = gtk_button_new_with_mnemonic(_("_Check signature"));
628
        gtk_container_add(GTK_CONTAINER(vbbox), button);
629
        g_signal_connect(button, "clicked",
630
                         G_CALLBACK(check_signature_button_clicked), mimeview);
631
632
        gtk_widget_show_all(vbbox);
633
634
        anchor = gtk_text_buffer_create_child_anchor(buffer, &iter);
635
        gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(textview->text),
636
                                          vbbox, anchor);
637
}
638
#endif /* USE_GPGME */
639
640
static void mimeview_change_view_type(MimeView *mimeview, MimeViewType type)
641
{
642
        TextView  *textview  = mimeview->textview;
643
        ImageView *imageview = mimeview->imageview;
644
        GList *children;
645
646
        if (mimeview->type == type) return;
647
648
        children = gtk_container_get_children
649
                (GTK_CONTAINER(mimeview->mime_vbox));
650
        if (children) {
651
                gtkut_container_remove(GTK_CONTAINER(mimeview->mime_vbox),
652
                                       GTK_WIDGET(children->data));
653
                g_list_free(children);
654
        }
655
656
        switch (type) {
657
        case MIMEVIEW_IMAGE:
658
                gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox),
659
                                  GTK_WIDGET_PTR(imageview));
660
                break;
661
        case MIMEVIEW_TEXT:
662
                gtk_container_add(GTK_CONTAINER(mimeview->mime_vbox),
663
                                  GTK_WIDGET_PTR(textview));
664
                break;
665
        default:
666
                return;
667
        }
668
669
        mimeview->type = type;
670
}
671
672
static void mimeview_selected(GtkCTree *ctree, GtkCTreeNode *node, gint column,
673
                              MimeView *mimeview)
674
{
675
        MimeInfo *partinfo;
676
677
        if (mimeview->opened == node) return;
678
        mimeview->opened = node;
679
        gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
680
681
        partinfo = gtk_ctree_node_get_row_data(ctree, node);
682
        if (!partinfo) return;
683
684
        /* ungrab the mouse event */
685
        if (GTK_WIDGET_HAS_GRAB(ctree)) {
686
                gtk_grab_remove(GTK_WIDGET(ctree));
687
                if (gdk_pointer_is_grabbed())
688
                        gdk_pointer_ungrab(GDK_CURRENT_TIME);
689
        }
690
691
        switch (partinfo->mime_type) {
692
        case MIME_TEXT:
693
        case MIME_TEXT_HTML:
694
        case MIME_MESSAGE_RFC822:
695
        case MIME_MULTIPART:
696
                mimeview_show_message_part(mimeview, partinfo);
697
                break;
698
        case MIME_IMAGE:
699
                mimeview_show_image_part(mimeview, partinfo);
700
                break;
701
        default:
702
                mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
703
#if USE_GPGME
704
                if (g_strcasecmp(partinfo->content_type,
705
                                 "application/pgp-signature") == 0)
706
                        mimeview_show_signature_part(mimeview, partinfo);
707
                else
708
#endif
709
                        mimeview_show_mime_part(mimeview, partinfo);
710
                break;
711
        }
712
}
713
714
static void mimeview_start_drag(GtkWidget *widget, gint button,
715
                                GdkEvent *event, MimeView *mimeview)
716
{
717
        GtkTargetList *list;
718
        GdkDragContext *context;
719
        MimeInfo *partinfo;
720
721
        g_return_if_fail(mimeview != NULL);
722
723
        partinfo = mimeview_get_selected_part(mimeview);
724
        if (partinfo->filename == NULL && partinfo->name == NULL) return;
725
726
        list = gtk_target_list_new(mimeview_mime_types, 1);
727
        context = gtk_drag_begin(widget, list,
728
                                 GDK_ACTION_COPY, button, event);
729
        gtk_drag_set_icon_default(context);
730
}
731
732
static gint mimeview_button_pressed(GtkWidget *widget, GdkEventButton *event,
733
                                    MimeView *mimeview)
734
{
735
        GtkCList *clist = GTK_CLIST(widget);
736
        MimeInfo *partinfo;
737
        gint row, column;
738
739
        if (!event) return FALSE;
740
741
        if (event->button == 2 || event->button == 3) {
742
                if (!gtk_clist_get_selection_info(clist, event->x, event->y,
743
                                                  &row, &column))
744
                        return FALSE;
745
                gtk_clist_unselect_all(clist);
746
                gtk_clist_select_row(clist, row, column);
747
                gtkut_clist_set_focus_row(clist, row);
748
        }
749
750
        if (event->button == 2 ||
751
            (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) {
752
                /* call external program for image, audio or html */
753
                mimeview_launch(mimeview);
754
        } else if (event->button == 3) {
755
                partinfo = mimeview_get_selected_part(mimeview);
756
                if (partinfo && (partinfo->mime_type == MIME_TEXT ||
757
                                 partinfo->mime_type == MIME_TEXT_HTML ||
758
                                 partinfo->mime_type == MIME_MESSAGE_RFC822 ||
759
                                 partinfo->mime_type == MIME_IMAGE ||
760
                                 partinfo->mime_type == MIME_MULTIPART))
761
                        menu_set_sensitive(mimeview->popupfactory,
762
                                           "/Display as text", FALSE);
763
                else
764
                        menu_set_sensitive(mimeview->popupfactory,
765
                                           "/Display as text", TRUE);
766
                if (partinfo &&
767
                    partinfo->mime_type == MIME_APPLICATION_OCTET_STREAM)
768
                        menu_set_sensitive(mimeview->popupfactory,
769
                                           "/Open", FALSE);
770
                else
771
                        menu_set_sensitive(mimeview->popupfactory,
772
                                           "/Open", TRUE);
773
#if USE_GPGME
774
                menu_set_sensitive(mimeview->popupfactory,
775
                                   "/Check signature",
776
                                   mimeview_is_signed(mimeview));
777
#endif
778
779
                gtk_menu_popup(GTK_MENU(mimeview->popupmenu),
780
                               NULL, NULL, NULL, NULL,
781
                               event->button, event->time);
782
                return TRUE;
783
        }
784
785
        return FALSE;
786
}
787
788
void mimeview_pass_key_press_event(MimeView *mimeview, GdkEventKey *event)
789
{
790
        mimeview_key_pressed(mimeview->ctree, event, mimeview);
791
}
792
793
#define BREAK_ON_MODIFIER_KEY() \
794
        if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
795
796
#define KEY_PRESS_EVENT_STOP() \
797
        g_signal_stop_emission_by_name(G_OBJECT(ctree), "key_press_event");
798
799
static gint mimeview_key_pressed(GtkWidget *widget, GdkEventKey *event,
800
                                 MimeView *mimeview)
801
{
802
        SummaryView *summaryview = NULL;
803
        GtkCTree *ctree = GTK_CTREE(widget);
804
        GtkCTreeNode *node;
805
        gboolean mod_pressed;
806
807
        if (!event) return FALSE;
808
        if (!mimeview->opened) return FALSE;
809
810
        if (mimeview->messageview->mainwin)
811
                summaryview = mimeview->messageview->mainwin->summaryview;
812
        mod_pressed =
813
                ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
814
815
        switch (event->keyval) {
816
        case GDK_space:
817
                if (textview_scroll_page(mimeview->textview, mod_pressed))
818
                        return TRUE;
819
820
                node = GTK_CTREE_NODE_NEXT(mimeview->opened);
821
                if (node) {
822
                        gtk_sctree_unselect_all(GTK_SCTREE(ctree));
823
                        gtk_sctree_select(GTK_SCTREE(ctree), node);
824
                        return TRUE;
825
                }
826
                if (summaryview)
827
                        summary_pass_key_press_event(summaryview, event);
828
                break;
829
        case GDK_BackSpace:
830
                textview_scroll_page(mimeview->textview, TRUE);
831
                return TRUE;
832
        case GDK_Return:
833
                textview_scroll_one_line(mimeview->textview, mod_pressed);
834
                return TRUE;
835
        case GDK_t:
836
                BREAK_ON_MODIFIER_KEY();
837
                KEY_PRESS_EVENT_STOP();
838
                mimeview_display_as_text(mimeview);
839
                return TRUE;
840
        case GDK_Left:
841
        case GDK_Escape:
842
        case GDK_Delete:
843
                if (summaryview)
844
                        summary_pass_key_press_event(summaryview, event);
845
                break;
846
        default:
847
                break;
848
        }
849
850
        return FALSE;
851
}
852
853
static void mimeview_drag_data_get(GtkWidget            *widget,
854
                                   GdkDragContext   *drag_context,
855
                                   GtkSelectionData *selection_data,
856
                                   guint             info,
857
                                   guint             time,
858
                                   MimeView            *mimeview)
859
{
860
        gchar *filename, *uriname;
861
        const gchar *bname;
862
        MimeInfo *partinfo;
863
864
        if (!mimeview->opened) return;
865
        if (!mimeview->file) return;
866
867
        partinfo = mimeview_get_selected_part(mimeview);
868
        if (!partinfo) return;
869
        if (!partinfo->filename && !partinfo->name) return;
870
871
        filename = partinfo->filename ? partinfo->filename : partinfo->name;
872
        bname = g_basename(filename);
873
        if (*bname == '\0') return;
874
875
        filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
876
                               bname, NULL);
877
878
        if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
879
                alertpanel_error
880
                        (_("Can't save the part of multipart message."));
881
882
        uriname = g_strconcat("file://", filename, NULL);
883
        gtk_selection_data_set(selection_data, selection_data->target, 8,
884
                               uriname, strlen(uriname));
885
886
        g_free(uriname);
887
        g_free(filename);
888
}
889
890
static void mimeview_display_as_text(MimeView *mimeview)
891
{
892
        MimeInfo *partinfo;
893
894
        if (!mimeview->opened) return;
895
896
        partinfo = mimeview_get_selected_part(mimeview);
897
        g_return_if_fail(partinfo != NULL);
898
        mimeview_show_message_part(mimeview, partinfo);
899
}
900
901
void mimeview_save_as(MimeView *mimeview)
902
{
903
        gchar *filename;
904
        gchar *defname = NULL;
905
        MimeInfo *partinfo;
906
907
        if (!mimeview->opened) return;
908
        if (!mimeview->file) return;
909
910
        partinfo = mimeview_get_selected_part(mimeview);
911
        g_return_if_fail(partinfo != NULL);
912
913
        if (partinfo->filename)
914
                defname = partinfo->filename;
915
        else if (partinfo->name) {
916
                Xstrdup_a(defname, partinfo->name, return);
917
                subst_for_filename(defname);
918
        }
919
920
        filename = filesel_save_as(defname);
921
        if (!filename) return;
922
923
        if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
924
                alertpanel_error
925
                        (_("Can't save the part of multipart message."));
926
927
        g_free(filename);
928
}
929
930
static void mimeview_launch(MimeView *mimeview)
931
{
932
        MimeInfo *partinfo;
933
        gchar *filename;
934
935
        if (!mimeview->opened) return;
936
        if (!mimeview->file) return;
937
938
        partinfo = mimeview_get_selected_part(mimeview);
939
        g_return_if_fail(partinfo != NULL);
940
941
        filename = procmime_get_tmp_file_name(partinfo);
942
943
        if (procmime_get_part(filename, mimeview->file, partinfo) < 0)
944
                alertpanel_error
945
                        (_("Can't save the part of multipart message."));
946
        else
947
                mimeview_view_file(filename, partinfo, NULL);
948
949
        g_free(filename);
950
}
951
952
static void mimeview_open_with(MimeView *mimeview)
953
{
954
        MimeInfo *partinfo;
955
        gchar *filename;
956
        gchar *cmd;
957
958
        if (!mimeview->opened) return;
959
        if (!mimeview->file) return;
960
961
        partinfo = mimeview_get_selected_part(mimeview);
962
        g_return_if_fail(partinfo != NULL);
963
964
        filename = procmime_get_tmp_file_name(partinfo);
965
966
        if (procmime_get_part(filename, mimeview->file, partinfo) < 0) {
967
                alertpanel_error
968
                        (_("Can't save the part of multipart message."));
969
                g_free(filename);
970
                return;
971
        }
972
973
        if (!prefs_common.mime_open_cmd_history)
974
                prefs_common.mime_open_cmd_history =
975
                        add_history(NULL, prefs_common.mime_open_cmd);
976
977
        cmd = input_dialog_combo
978
                (_("Open with"),
979
                 _("Enter the command line to open file:\n"
980
                   "(`%s' will be replaced with file name)"),
981
                 prefs_common.mime_open_cmd,
982
                 prefs_common.mime_open_cmd_history,
983
                 TRUE);
984
        if (cmd) {
985
                mimeview_view_file(filename, partinfo, cmd);
986
                g_free(prefs_common.mime_open_cmd);
987
                prefs_common.mime_open_cmd = cmd;
988
                prefs_common.mime_open_cmd_history =
989
                        add_history(prefs_common.mime_open_cmd_history, cmd);
990
        }
991
992
        g_free(filename);
993
}
994
995
static void mimeview_view_file(const gchar *filename, MimeInfo *partinfo,
996
                               const gchar *cmdline)
997
{
998
        static gchar *default_image_cmdline = "display '%s'";
999
        static gchar *default_audio_cmdline = "play '%s'";
1000
        static gchar *default_html_cmdline = DEFAULT_BROWSER_CMD;
1001
        static gchar *mime_cmdline = "metamail -d -b -x -c %s '%s'";
1002
        gchar buf[1024];
1003
        gchar m_buf[1024];
1004
        const gchar *cmd;
1005
        const gchar *def_cmd;
1006
        const gchar *p;
1007
1008
        if (cmdline) {
1009
                cmd = cmdline;
1010
                def_cmd = NULL;
1011
        } else if (MIME_APPLICATION_OCTET_STREAM == partinfo->mime_type) {
1012
                return;
1013
        } else if (MIME_IMAGE == partinfo->mime_type) {
1014
                cmd = prefs_common.mime_image_viewer;
1015
                def_cmd = default_image_cmdline;
1016
        } else if (MIME_AUDIO == partinfo->mime_type) {
1017
                cmd = prefs_common.mime_audio_player;
1018
                def_cmd = default_audio_cmdline;
1019
        } else if (MIME_TEXT_HTML == partinfo->mime_type) {
1020
                cmd = prefs_common.uri_cmd;
1021
                def_cmd = default_html_cmdline;
1022
        } else {
1023
                g_snprintf(m_buf, sizeof(m_buf), mime_cmdline,
1024
                           partinfo->content_type, "%s");
1025
                cmd = m_buf;
1026
                def_cmd = NULL;
1027
        }
1028
1029
        if (cmd && (p = strchr(cmd, '%')) && *(p + 1) == 's' &&
1030
            !strchr(p + 2, '%'))
1031
                g_snprintf(buf, sizeof(buf), cmd, filename);
1032
        else {
1033
                if (cmd)
1034
                        g_warning(_("MIME viewer command line is invalid: `%s'"), cmd);
1035
                if (def_cmd)
1036
                        g_snprintf(buf, sizeof(buf), def_cmd, filename);
1037
                else
1038
                        return;
1039
        }
1040
1041
        execute_command_line(buf, TRUE);
1042
}
1043
1044
#if USE_GPGME
1045
static void update_node_name(GtkCTree *ctree, GtkCTreeNode *node,
1046
                             gpointer data)
1047
{
1048
        MimeInfo *partinfo;
1049
        gchar *part_name;
1050
1051
        partinfo = gtk_ctree_node_get_row_data(ctree, node);
1052
        g_return_if_fail(partinfo != NULL);
1053
1054
        part_name = get_part_name(partinfo);
1055
        gtk_ctree_node_set_text(ctree, node, COL_NAME, part_name);
1056
}
1057
1058
static void mimeview_update_names(MimeView *mimeview)
1059
{
1060
        GtkCTree *ctree = GTK_CTREE(mimeview->ctree);
1061
1062
        gtk_ctree_pre_recursive(ctree, NULL, update_node_name, NULL);
1063
}
1064
1065
static void mimeview_update_signature_info(MimeView *mimeview)
1066
{
1067
        MimeInfo *partinfo;
1068
1069
        if (!mimeview) return;
1070
        if (!mimeview->opened) return;
1071
1072
        partinfo = mimeview_get_selected_part(mimeview);
1073
        if (!partinfo) return;
1074
1075
        if (g_strcasecmp(partinfo->content_type,
1076
                         "application/pgp-signature") == 0) {
1077
                mimeview_change_view_type(mimeview, MIMEVIEW_TEXT);
1078
                mimeview_show_signature_part(mimeview, partinfo);
1079
        }
1080
}
1081
1082
static void mimeview_check_signature(MimeView *mimeview)
1083
{
1084
        MimeInfo *mimeinfo;
1085
        FILE *fp;
1086
1087
        g_return_if_fail (mimeview_is_signed(mimeview));
1088
1089
        mimeinfo = mimeview_get_selected_part(mimeview);
1090
        g_return_if_fail(mimeinfo != NULL);
1091
        g_return_if_fail(mimeview->file != NULL);
1092
1093
        while (mimeinfo->parent)
1094
                mimeinfo = mimeinfo->parent;
1095
1096
        if ((fp = fopen(mimeview->file, "rb")) == NULL) {
1097
                FILE_OP_ERROR(mimeview->file, "fopen");
1098
                return;
1099
        }
1100
1101
        rfc2015_check_signature(mimeinfo, fp);
1102
        fclose(fp);
1103
1104
        mimeview_update_names(mimeview);
1105
        mimeview_update_signature_info(mimeview);
1106
1107
        textview_show_message(mimeview->messageview->textview, mimeinfo,
1108
                              mimeview->file);
1109
}
1110
#endif /* USE_GPGME */