/*
* grn_mime.c - MIME handling routines
*
* Some code borrowed from Balsa
*
* $Id: grn_mime.c,v 1.7 2000/07/19 06:26:21 sc Exp $
*/
/* Copyright (C) 1999-2000 Sergey Chernikov (sc@ivvs.ul.ru)
*
* Authors: Sergey Chernikov <sc@ivvs.ul.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*/
#include <errno.h>
#include "grn_consts.h"
#include "mutt.h"
#include "mime.h"
#include "rfc2047.h"
#include "grn_vars.h"
#include "grn_types.h"
#include "grn_util.h"
#include "grn_misc.h"
#include "grn_msgpost.h"
#include "grn_config.h"
static void decode_hdrs(gpointer key, gpointer value, GHashTable *ht)
{
char s[8192];
gchar *new_key = g_strdup(key);
rfc2047_decode(s, value, 8191);
g_hash_table_insert(ht, new_key, g_strdup(s));
}
void grn_decode_headers(t_msgheaders *mh)
{
char s[8192];
g_return_if_fail(mh != NULL);
if (str_check(mh->subject))
{
rfc2047_decode(s, mh->subject, 8191);
str_free(&(mh->subject));
mh->subject = g_strdup(s);
}
if (str_check(mh->from))
{
rfc2047_decode(s, mh->from, 8191);
str_free(&(mh->from));
mh->from = g_strdup(s);
}
if (str_check(mh->to))
{
rfc2047_decode(s, mh->to, 8191);
str_free(&(mh->to));
mh->to = g_strdup(s);
}
if (mh->extra_hdrs)
{
GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_foreach(mh->extra_hdrs, (GHFunc) decode_hdrs, ht);
t_msgheaders_rm_extra_hdrs(mh);
mh->extra_hdrs = ht;
}
}
static void postparse_mime_parts(BODY *b, STATE *s, t_message *m)
{
gboolean need_decode = FALSE;
if (! b) return;
if ((b->type == TYPEMULTIPART) || (b->type == TYPEMESSAGE)) ;
else {
if (b->disposition == DISPINLINE)
{
gchar sh[8192], *p;
if (! b->hdr) b->hdr = mutt_new_header();
if (! b->hdr->env) b->hdr->env = mutt_new_envelope();
fseek(s->fpin, b->hdr_offset, 0);
do {
fgets(sh, 8191, s->fpin);
p = strrchr(sh, '\n');
if (p) *p = '\0';
if (strstr(sh, ": "))
{
if (! mutt_strncasecmp("content-id", sh, 10))
b->hdr->env->userhdrs = mutt_add_list(b->hdr->env->userhdrs, g_strdup(sh));
}
} while (strlen(sh) > 0);
need_decode = TRUE;
}
if (str_check(b->filename)) need_decode = TRUE;
if (b->encoding >= 3) need_decode = TRUE;
if (b->type != TYPETEXT) need_decode = TRUE;
}
if (need_decode)
{
gchar *fn = NULL;
if (! str_check(b->filename))
{
char tempfn[64] = "/tmp/grn.XXXXXXX";
int fd = mkstemp(tempfn);
if (fd != -1)
{
fn = g_strdup(tempfn);
close(fd);
}
b->filename = g_strdup(fn);
}
else fn = g_strdup(b->filename);
if (fn)
{
s->fpout = fopen(fn, "w");
m->temp_files = g_slist_append(m->temp_files, fn);
}
}
if (s->fpout)
{
fseek(s->fpin, b->offset, 0);
mutt_decode_attachment(b, s);
fclose(s->fpout);
}
postparse_mime_parts(b->parts, s, m);
postparse_mime_parts(b->next, s, m);
}
void grn_mime_parse_message(t_message *m)
{
char tempfn[64] = "/tmp/grn.XXXXX";
int fd;
gchar *s;
FILE *f;
BODY *b, *body;
fd = mkstemp(tempfn);
if (fd == -1)
{
perror("Grn"); return;
}
s = msg_to_text(m);
if (write(fd, s, strlen(s)) == -1 )
{
str_free(&s);
perror("Grn"); return;
}
str_free(&s);
lseek(fd, 0, 0);
f = fdopen(fd, "r");
m->fp = f;
body = (BODY *) m->data;
if (body) mutt_free_body(&body);
b = mutt_read_mime_header(f, 0);
fseek(f, 0, SEEK_END);
b->length = ftell(f) - b->offset;
fseek(f, b->offset, 0);
if ((b->type == TYPETEXT) && (b->encoding < 3) && (! strcmp(b->subtype, "plain")))
{
m->data = b;
unlink(tempfn);
}
else {
STATE state;
memset(&state, 0, sizeof(state));
mutt_parse_part(f, b);
state.fpin = f; state.fpout = NULL;
postparse_mime_parts(b, &state, m);
m->data = b;
m->temp_files = g_slist_append(m->temp_files, g_strdup(tempfn));
m->filename = g_strdup(tempfn);
}
}
grn_mime_part *grn_mime_part_alloc()
{
grn_mime_part *mp = g_new(grn_mime_part, 1);
mp->size = 0;
mp->type = NULL; mp->subtype = NULL;
mp->enc = NULL; mp->filename = NULL;
mp->data = NULL;
return mp;
}
void grn_mime_part_free(grn_mime_part **mp)
{
g_return_if_fail(mp != NULL);
g_return_if_fail(*mp != NULL);
str_free(&((*mp)->type));
str_free(&((*mp)->subtype));
str_free(&((*mp)->enc));
str_free(&((*mp)->filename));
str_free(&((*mp)->data));
g_free(*mp); *mp = NULL;
}
static gchar *mtype2str(BODY *b)
{
gchar *ret = NULL;
switch (b->type)
{
case TYPEOTHER:
ret = g_strdup(b->xtype);
break;
case TYPEAUDIO:
ret = g_strdup("audio");
break;
case TYPEAPPLICATION:
ret = g_strdup("application");
break;
case TYPEIMAGE:
ret = g_strdup("image");
break;
case TYPEMESSAGE:
ret = g_strdup("message");
break;
case TYPEMODEL:
ret = g_strdup("model");
break;
case TYPEMULTIPART:
ret = g_strdup("multipart");
break;
case TYPETEXT:
ret = g_strdup("text");
break;
case TYPEVIDEO:
ret = g_strdup("video");
break;
}
return ret;
}
gchar *body_mtype(BODY *b)
{
gchar *s;
gchar *ret;
g_return_val_if_fail(b != NULL, NULL);
s = mtype2str(b);
ret = g_strdup_printf("%s/%s", s, b->subtype);
g_free(s);
return ret;
}
gchar *menc2str(BODY *b)
{
gchar *ret = NULL;
if (! b) return NULL;
switch (b->encoding)
{
case ENCOTHER:
ret = g_strdup("other");
break;
case ENC7BIT:
ret = g_strdup("7bit");
break;
case ENC8BIT:
ret = g_strdup("8bit");
break;
case ENCQUOTEDPRINTABLE:
ret = g_strdup("quoted-printable");
break;
case ENCBASE64:
ret = g_strdup("base64");
break;
case ENCBINARY:
ret = g_strdup("binary");
break;
case ENCUUENCODED:
ret = g_strdup("uuencoded");
break;
}
return ret;
}
static gchar *mdisp2str(BODY *b)
{
gchar *ret = NULL;
switch (b->disposition)
{
case DISPINLINE:
ret = g_strdup("inline");
break;
case DISPATTACH:
ret = g_strdup("attach");
break;
case DISPFORMDATA:
ret = g_strdup("form data");
break;
}
return ret;
}
GSList *body_to_mime_parts(BODY *b, GSList *l, FILE *fp)
{
grn_mime_part *mp = NULL;
GSList *ret = l;
size_t nread;
if (! b) return l;
mp = grn_mime_part_alloc();
mp->size = b->length;
mp->type = mtype2str(b);
mp->subtype = g_strdup(b->subtype);
mp->enc = menc2str(b);
mp->filename = str_copy(b->filename);
fseek(fp, b->offset, 0);
if (b->encoding < 3)
{
mp->data = g_malloc(mp->size + 1);
nread = fread(mp->data, 1, mp->size, fp);
mp->data[nread] = '\0';
}
else {
STATE s;
char tempfn[64] = "/tmp/grn.XXXXXXX";
int fd = mkstemp(tempfn);
glong sz;
close(fd);
memset(&s, 0, sizeof(s));
s.fpin = fp;
s.fpout = fopen(tempfn, "w+");
mutt_decode_attachment(b, &s);
fseek(s.fpout, 0, SEEK_END); sz = ftell(s.fpout);
fseek(s.fpout, 0, 0);
mp->data = g_malloc(sz + 1);
nread = fread(mp->data, 1, sz, s.fpout);
mp->data[nread] = '\0';
fclose(s.fpout); unlink(tempfn);
}
ret = g_slist_append(l, mp);
ret = body_to_mime_parts(b->parts, ret, fp);
ret = body_to_mime_parts(b->next, ret, fp);
return ret;
}
gboolean is_8bit(gchar *s)
{
gchar *p = s;
while (*p)
{
if ((guchar) *p > 127) return TRUE;
p++;
}
return FALSE;
}
void add_mime_to_plaintext(t_message *msg)
{
gchar *s1 = get_charset(), *s2;
t_msgheaders_add_extra_hdr(msg->mh, "Mime-Version", "1.0");
s2 = g_strdup_printf("text/plain; charset=%s", s1);
t_msgheaders_add_extra_hdr(msg->mh, "Content-Type", s2);
str_free(&s1); str_free(&s2);
if (is_8bit(msg->body))
t_msgheaders_add_extra_hdr(msg->mh, "Content-Transfer-Encoding", "8bit");
else t_msgheaders_add_extra_hdr(msg->mh, "Content-Transfer-Encoding", "7bit");
}
void mutt_message(const char *fmt, ...)
{
va_list arglist;
gchar *s;
va_start(arglist, fmt);
s = g_strdup_vprintf(fmt, arglist);
va_end(arglist);
grn_appbar_set_status(GRN->appbar, _("Mutt message: %s"), s);
str_free(&s);
}
void grn_mutt_error(const char *fmt, ...)
{
va_list arglist;
gchar *s;
va_start(arglist, fmt);
s = g_strdup_vprintf(fmt, arglist);
va_end(arglist);
grn_appbar_set_status(GRN->appbar, _("Mutt error: %s"), s);
str_free(&s);
}
void mutt_exit(int code)
{
g_print("mutt_exit() called, code = %d\n", code);
}
int mutt_yesorno(const char *msg, int def)
{
g_print("mutt_yesorno() called, msg = %s, def = %d\n", msg, def);
return 1;
}
void mutt_clear_error(void)
{
g_print("mutt_clear_error() called\n");
}
void grn_mime_init(void)
{
mutt_error = grn_mutt_error;
}
static void mp_sel(GtkCList *clist, gint row, gint col, GdkEventButton *ev,
GtkWidget *vbox)
{
BODY *b = (BODY *) gtk_clist_get_row_data(clist, row);
GtkWidget *btn = gtk_object_get_data(GTK_OBJECT(vbox), KEY_BTN_SAVE);
GtkWidget *btn1 = gtk_object_get_data(GTK_OBJECT(vbox), KEY_BTN_OPEN);
GtkWidget *btn2 = gtk_object_get_data(GTK_OBJECT(vbox), KEY_BTN_VIEW);
GtkWidget *btn3 = gtk_object_get_data(GTK_OBJECT(vbox), KEY_BTN_EXEC);
gchar *mime_type;
const gchar *s1;
if ((! b) || (! btn)) return;
if ((b->type == TYPEMULTIPART) || (b->type == TYPEMESSAGE))
gtk_widget_set_sensitive(btn, FALSE);
else gtk_widget_set_sensitive(btn, TRUE);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_DATA, b);
if (btn3) gtk_widget_set_sensitive(btn3, TRUE);
if ((! btn1) || (! btn2)) return;
mime_type = body_mtype(b);
s1 = gnome_mime_program(mime_type);
gtk_widget_set_sensitive(btn1, (s1 != NULL));
s1 = gnome_mime_get_value(mime_type, "view");
gtk_widget_set_sensitive(btn2, (s1 != NULL));
g_free(mime_type);
}
static void mp_save_ok(GtkWidget *w, GtkFileSelection *fsel)
{
gchar *fn = gtk_file_selection_get_filename(fsel);
GtkWidget *cb = gtk_object_get_data(GTK_OBJECT(fsel), KEY_BTN);
t_message *m = gtk_object_get_data(GTK_OBJECT(fsel), KEY_MSG);
BODY *b = gtk_object_get_data(GTK_OBJECT(fsel), KEY_DATA);
FILE *fpout;
if (g_file_exists(fn))
{
grn_error(_("Already exists"), fn);
return;
}
fpout = fopen(fn, "w");
if (cb && m && b && m->fp && fpout)
{
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb)))
{
STATE s;
memset(&s, 0, sizeof(s));
s.fpin = m->fp; s.fpout = fpout;
fseek(s.fpin, b->offset, 0);
mutt_decode_attachment(b, &s);
}
else {
gchar *buf = g_malloc(8192);
glong len = b->length;
fseek(m->fp, b->offset, 0);
while (! feof(m->fp))
{
size_t nwritten, nread = fread(buf, 1, MIN(8192, len), m->fp);
nwritten = fwrite(buf, 1, nread, fpout);
len -= nwritten;
if (nread != nwritten) grn_error(_("I/O problem"), _("Read and written lengths mismatch"));
if (len <= 0) break;
}
g_free(buf);
}
fclose(fpout);
}
else {
if (! fpout) grn_error(_("The following file can't be opened for writing"), fn);
}
gtk_widget_destroy(GTK_WIDGET(fsel));
}
static void mp_save_cancel(GtkWidget *w, GtkWidget *fsel)
{
gtk_widget_destroy(fsel);
}
static void mp_save(GtkWidget *w, GtkWidget *vbox)
{
BODY *b = (BODY *) gtk_object_get_data(GTK_OBJECT(vbox), KEY_DATA);
t_message *m = gtk_object_get_data(GTK_OBJECT(vbox), KEY_MSG);
GtkFileSelection *fsel;
GtkWidget *cb;
if ((! b) || (! m)) return;
fsel = GTK_FILE_SELECTION(gtk_file_selection_new(_("Save MIME part")));
if (str_check(b->filename)) gtk_file_selection_set_filename(fsel, b->filename);
cb = gtk_check_button_new_with_label(_("Decode before saving"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
gtk_box_pack_start(GTK_BOX(fsel->main_vbox), cb, FALSE, FALSE, 2);
gtk_object_set_data(GTK_OBJECT(fsel), KEY_BTN, cb);
gtk_signal_connect(GTK_OBJECT(fsel->ok_button), "clicked",
GTK_SIGNAL_FUNC(mp_save_ok), fsel);
gtk_signal_connect(GTK_OBJECT(fsel->cancel_button), "clicked",
GTK_SIGNAL_FUNC(mp_save_cancel), fsel);
gtk_object_set_data(GTK_OBJECT(fsel), KEY_MSG, m);
gtk_object_set_data(GTK_OBJECT(fsel), KEY_DATA, b);
gtk_widget_show_all(GTK_WIDGET(fsel));
}
static gchar *mp_temp(BODY *b, t_message *m)
{
gchar tempfn[64] = "/tmp/grn.XXXXXXX";
gchar *filename = NULL;
gint fd = mkstemp(tempfn);
STATE s;
filename = g_strdup(tempfn);
memset(&s, 0, sizeof(s));
s.fpin = m->fp;
s.fpout = fdopen(fd, "w+");
fseek(s.fpin, b->offset, 0);
mutt_decode_attachment(b, &s);
fclose(s.fpout);
m->temp_files = g_slist_append(m->temp_files, g_strdup(filename));
if (! b->filename) b->filename = g_strdup(filename);
return filename;
}
static void mp_open(GtkWidget *w, GtkWidget *vbox)
{
BODY *b = (BODY *) gtk_object_get_data(GTK_OBJECT(vbox), KEY_DATA);
t_message *m = gtk_object_get_data(GTK_OBJECT(vbox), KEY_MSG);
gchar *mime_type, *s;
const gchar *s1;
if (! b) return;
mime_type = body_mtype(b);
s1 = gnome_mime_program(mime_type);
if (s1)
{
gchar *fn = mp_temp(b, m);
s = str_subst(s1, "%f", fn);
if (_mutt_system(s, M_DETACH_PROCESS) == -1)
grn_error(s, strerror(errno));
g_free(s); g_free(fn);
}
g_free(mime_type);
}
static void mp_view(GtkWidget *w, GtkWidget *vbox)
{
BODY *b = (BODY *) gtk_object_get_data(GTK_OBJECT(vbox), KEY_DATA);
t_message *m = gtk_object_get_data(GTK_OBJECT(vbox), KEY_MSG);
gchar *mime_type, *s;
const gchar *s1;
if (! b) return;
mime_type = body_mtype(b);
s1 = gnome_mime_get_value(mime_type, "view");
if (s1)
{
gchar *fn = mp_temp(b, m);
s = str_subst(s1, "%f", fn);
if (_mutt_system(s, M_DETACH_PROCESS) == -1)
grn_error(s, strerror(errno));
g_free(s); g_free(fn);
}
g_free(mime_type);
}
static void mp_exec_ok(GtkWidget *w, GtkWidget *dlg)
{
GtkWidget *vbox = gtk_object_get_data(GTK_OBJECT(dlg), KEY_WND);
BODY *b = (BODY *) gtk_object_get_data(GTK_OBJECT(vbox), KEY_DATA);
t_message *m = gtk_object_get_data(GTK_OBJECT(vbox), KEY_MSG);
GtkWidget *en;
gchar *s, *s1, *fn;
if (! dlg) return;
en = gtk_object_get_data(GTK_OBJECT(dlg), KEY_ENTRY);
if (! en) return;
s1 = gtk_entry_get_text(GTK_ENTRY(en));
fn = mp_temp(b, m);
s = str_subst(s1, "%f", fn);
if (_mutt_system(s, M_DETACH_PROCESS) == -1) grn_error(s, strerror(errno));
g_free(s); g_free(fn);
gtk_widget_destroy(dlg);
}
static void mp_exec(GtkWidget *w, GtkWidget *vbox)
{
BODY *b = (BODY *) gtk_object_get_data(GTK_OBJECT(vbox), KEY_DATA);
GtkWidget *dlg;
GtkWidget *box, *en, *l;
if (! b) return;
grn_lock();
box = gtk_vbox_new(FALSE, 0);
l = gtk_label_new(_("Enter command (%f may be used):"));
gtk_box_pack_start(GTK_BOX(box), l, FALSE, FALSE, 3);
en = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(box), en, TRUE, TRUE, 3);
dlg = gnome_dialog_new(_("Execute command"), GNOME_STOCK_BUTTON_CANCEL,
GNOME_STOCK_BUTTON_OK, NULL);
gnome_dialog_set_close(GNOME_DIALOG(dlg), TRUE);
gnome_dialog_editable_enters(GNOME_DIALOG(dlg), GTK_EDITABLE(en));
gnome_dialog_button_connect(GNOME_DIALOG(dlg), 1,
GTK_SIGNAL_FUNC(mp_exec_ok), dlg);
gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dlg)->vbox), box, TRUE, TRUE, 0);
gtk_object_set_data(GTK_OBJECT(dlg), KEY_ENTRY, en);
gtk_object_set_data(GTK_OBJECT(dlg), KEY_WND, vbox);
gtk_widget_show_all(dlg);
gtk_widget_grab_focus(en);
grn_unlock();
}
#define NCOLS 5
static GtkWidget *mime_tree_create()
{
gchar *ttl[NCOLS] = {_("Type"), _("Description"), _("Size"), _("Encoding"), _("Disposition")};
GtkWidget *vbox, *scrwin, *bbox, *btn;
GtkCList *clist;
grn_lock();
vbox = gtk_vbox_new(FALSE, 0);
clist = GTK_CLIST(gtk_ctree_new_with_titles(NCOLS, 0, ttl));
gtk_clist_set_column_justification(clist, 2, GTK_JUSTIFY_RIGHT);
gtk_clist_set_selection_mode(clist, GTK_SELECTION_BROWSE);
gtk_clist_column_titles_passive(clist);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_CTREE, clist);
gtk_signal_connect(GTK_OBJECT(clist), "select_row",
GTK_SIGNAL_FUNC(mp_sel), vbox);
scrwin = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(scrwin), GTK_WIDGET(clist));
gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
bbox = gtk_hbutton_box_new();
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), gnome_preferences_get_button_layout());
gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), GNOME_PAD);
gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
gtk_widget_set_sensitive(bbox, TRUE);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_BTN, bbox);
btn = gnome_pixmap_button(gnome_stock_pixmap_widget(NULL, GNOME_STOCK_PIXMAP_SAVE),
_("Save..."));
gtk_box_pack_start(GTK_BOX(bbox), btn, TRUE, TRUE, 0);
gtk_signal_connect(GTK_OBJECT(btn), "clicked",
GTK_SIGNAL_FUNC(mp_save), vbox);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_BTN_SAVE, btn);
gtk_widget_set_sensitive(btn, FALSE);
btn = gtk_button_new_with_label(_("Open"));
gtk_box_pack_start(GTK_BOX(bbox), btn, TRUE, TRUE, 0);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_BTN_OPEN, btn);
gtk_signal_connect(GTK_OBJECT(btn), "clicked",
GTK_SIGNAL_FUNC(mp_open), vbox);
gtk_widget_set_sensitive(btn, FALSE);
btn = gtk_button_new_with_label(_("View"));
gtk_box_pack_start(GTK_BOX(bbox), btn, TRUE, TRUE, 0);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_BTN_VIEW, btn);
gtk_signal_connect(GTK_OBJECT(btn), "clicked",
GTK_SIGNAL_FUNC(mp_view), vbox);
gtk_widget_set_sensitive(btn, FALSE);
btn = gtk_button_new_with_label(_("Execute..."));
gtk_box_pack_start(GTK_BOX(bbox), btn, TRUE, TRUE, 0);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_BTN_EXEC, btn);
gtk_signal_connect(GTK_OBJECT(btn), "clicked",
GTK_SIGNAL_FUNC(mp_exec), vbox);
gtk_widget_set_sensitive(btn, FALSE);
grn_unlock();
return vbox;
}
static gboolean mime_tree_dlg_delete(GtkWidget *w)
{
grn_save_ws(7, w);
return FALSE;
}
static void btn_event(GtkWidget *btn, GtkWidget *w)
{
mime_tree_dlg_delete(w);
}
static GtkWidget *mime_tree_dlg_create(t_message *m)
{
GtkWidget *dlg, *vbox;
grn_lock();
dlg = gnome_dialog_new(_("MIME structure"), GNOME_STOCK_BUTTON_CLOSE, NULL);
gnome_dialog_set_close(GNOME_DIALOG(dlg), TRUE);
gtk_window_set_policy(GTK_WINDOW(dlg), FALSE, TRUE, FALSE);
gtk_window_set_default_size(GTK_WINDOW(dlg), grn_prefs.ws[7].width, grn_prefs.ws[7].height);
gtk_widget_set_uposition(dlg, grn_prefs.ws[7].x, grn_prefs.ws[7].y);
gtk_signal_connect(GTK_OBJECT(dlg), "delete_event",
GTK_SIGNAL_FUNC(mime_tree_dlg_delete), NULL);
gnome_dialog_button_connect(GNOME_DIALOG(dlg), 0,
GTK_SIGNAL_FUNC(btn_event), dlg);
grn_unlock();
vbox = mime_tree_create();
grn_lock();
gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dlg)->vbox), vbox, TRUE, TRUE, 0);
gtk_object_set_data(GTK_OBJECT(dlg), KEY_WND, vbox);
gtk_object_set_data(GTK_OBJECT(vbox), KEY_MSG, m);
gtk_widget_show_all(dlg);
grn_unlock();
return dlg;
}
static void show_mime_part(BODY *b, GtkCTree *ctree, GtkCTreeNode *parent)
{
gchar *s;
gchar *row[NCOLS];
GtkCTreeNode *node;
gint i;
if (! b) return;
if (! ctree) return;
s = mtype2str(b);
row[0] = g_strdup_printf("%s/%s", s, b->subtype);
str_free(&s);
row[1] = str_copy(b->description);
row[2] = g_strdup_printf("%ld", b->length);
row[3] = menc2str(b);
row[4] = mdisp2str(b);
node = gtk_ctree_insert_node(ctree, parent, NULL, row, 0,
NULL, NULL, NULL, NULL, FALSE, TRUE);
gtk_ctree_node_set_row_data(ctree, node, b);
for (i=0; i<NCOLS; i++) str_free(&row[i]);
show_mime_part(b->parts, ctree, node);
show_mime_part(b->next, ctree, parent);
}
#undef NCOLS
typedef struct mt_data
{
GtkWidget **dlg;
GtkWidget *btn;
t_message *m;
} mt_data;
static void mt_destroy(GtkWidget *w, mt_data *mtd)
{
*(mtd->dlg) = NULL;
gtk_widget_set_sensitive(mtd->btn, TRUE);
t_message_destroy(mtd->m);
g_free(mtd);
}
static gboolean show_mime_tree(GtkWidget *w, t_message *m)
{
static GtkWidget *dlg = NULL, *mtw = NULL;
mt_data *mtd;
GtkCTree *ctree = NULL;
if (dlg) return FALSE;
dlg = mime_tree_dlg_create(m);
mtw = gtk_object_get_data(GTK_OBJECT(dlg), KEY_WND);
if (mtw) ctree = gtk_object_get_data(GTK_OBJECT(mtw), KEY_CTREE);
if (! ctree) return FALSE;
gtk_widget_set_sensitive(w, FALSE);
mtd = g_new(mt_data, 1);
mtd->dlg = &dlg; mtd->btn = w; mtd->m = m;
gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
GTK_SIGNAL_FUNC(mt_destroy), mtd);
show_mime_part((BODY *) m->data, ctree, NULL);
gtk_clist_columns_autosize(GTK_CLIST(ctree));
gtk_widget_grab_focus(GTK_WIDGET(ctree));
return TRUE;
}
void evt_mime_view(GtkWidget *w)
{
t_message *m;
static t_message *m1;
if (! GRN->msgwin) return;
m = gtk_object_get_data(GTK_OBJECT(GRN->msgwin), KEY_MSG);
if (! m) return;
m1 = t_message_copy(m);
grn_mime_parse_message(m1);
if (! show_mime_tree(w, m1)) t_message_free(&m1);
}
syntax highlighted by Code2HTML, v. 0.9.1