Statistics
| Revision:

root / src / mimeview.c @ 422

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