Statistics
| Revision:

root / src / mimeview.c @ 237

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