1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25 #include "balsa-mime-widget-message.h"
26
27 #include <string.h>
28 #include <gtk/gtk.h>
29
30 #include "balsa-app.h"
31 #include "balsa-icons.h"
32 #include "send.h"
33 #include "rfc3156.h"
34 #include <glib/gi18n.h>
35 #include "balsa-mime-widget.h"
36 #include "balsa-mime-widget-callbacks.h"
37 #include "sendmsg-window.h"
38
39 typedef enum _rfc_extbody_t {
40 RFC2046_EXTBODY_FTP,
41 RFC2046_EXTBODY_ANONFTP,
42 RFC2046_EXTBODY_TFTP,
43 RFC2046_EXTBODY_LOCALFILE,
44 RFC2046_EXTBODY_MAILSERVER,
45 RFC2017_EXTBODY_URL,
46 RFC2046_EXTBODY_UNKNOWN
47 } rfc_extbody_t;
48
49
50 typedef struct _rfc_extbody_id {
51 gchar *id_string;
52 rfc_extbody_t action;
53 } rfc_extbody_id;
54
55
56 static rfc_extbody_id rfc_extbodys[] = {
57 {"ftp", RFC2046_EXTBODY_FTP},
58 {"anon-ftp", RFC2046_EXTBODY_ANONFTP},
59 {"tftp", RFC2046_EXTBODY_TFTP},
60 {"local-file", RFC2046_EXTBODY_LOCALFILE},
61 {"mail-server", RFC2046_EXTBODY_MAILSERVER},
62 {"URL", RFC2017_EXTBODY_URL},
63 {NULL, RFC2046_EXTBODY_UNKNOWN}
64 };
65
66
67 /* message/external-body related stuff */
68 static BalsaMimeWidget *bmw_message_extbody_url(LibBalsaMessageBody *
69 mime_body,
70 rfc_extbody_t url_type);
71 static BalsaMimeWidget *bmw_message_extbody_mail(LibBalsaMessageBody *
72 mime_body);
73 static void extbody_call_url(GtkWidget * button, gpointer data);
74 static void extbody_send_mail(GtkWidget * button,
75 LibBalsaMessageBody * mime_body);
76
77 /* message/rfc822 related stuff */
78 static GtkWidget *bm_header_widget_new(BalsaMessage * bm,
79 GtkWidget * const * buttons);
80 #ifdef HAVE_GPGME
81 static void add_header_sigstate(GtkGrid * grid,
82 GMimeGpgmeSigstat * siginfo);
83 #endif
84
85 BalsaMimeWidget *
balsa_mime_widget_new_message(BalsaMessage * bm,LibBalsaMessageBody * mime_body,const gchar * content_type,gpointer data)86 balsa_mime_widget_new_message(BalsaMessage * bm,
87 LibBalsaMessageBody * mime_body,
88 const gchar * content_type, gpointer data)
89 {
90 BalsaMimeWidget *mw = NULL;
91
92 g_return_val_if_fail(mime_body != NULL, NULL);
93 g_return_val_if_fail(content_type != NULL, NULL);
94
95 if (!g_ascii_strcasecmp("message/external-body", content_type)) {
96 gchar *access_type;
97 rfc_extbody_id *extbody_type = rfc_extbodys;
98
99 access_type =
100 libbalsa_message_body_get_parameter(mime_body, "access-type");
101 while (extbody_type->id_string &&
102 g_ascii_strcasecmp(extbody_type->id_string, access_type))
103 extbody_type++;
104 switch (extbody_type->action) {
105 case RFC2046_EXTBODY_FTP:
106 case RFC2046_EXTBODY_ANONFTP:
107 case RFC2046_EXTBODY_TFTP:
108 case RFC2046_EXTBODY_LOCALFILE:
109 case RFC2017_EXTBODY_URL:
110 mw = bmw_message_extbody_url(mime_body, extbody_type->action);
111 break;
112 case RFC2046_EXTBODY_MAILSERVER:
113 mw = bmw_message_extbody_mail(mime_body);
114 break;
115 case RFC2046_EXTBODY_UNKNOWN:
116 break;
117 default:
118 g_error("Undefined external body action %d!", extbody_type->action);
119 break;
120 }
121 g_free(access_type);
122 } else if (!g_ascii_strcasecmp("message/rfc822", content_type)) {
123 GtkWidget *emb_hdrs;
124
125 mw = g_object_new(BALSA_TYPE_MIME_WIDGET, NULL);
126
127 mw->widget = gtk_frame_new(NULL);
128
129 mw->container = gtk_box_new(GTK_ORIENTATION_VERTICAL, BMW_MESSAGE_PADDING);
130 gtk_container_set_border_width(GTK_CONTAINER(mw->container),
131 BMW_MESSAGE_PADDING);
132 gtk_container_add(GTK_CONTAINER(mw->widget), mw->container);
133
134 mw->header_widget = emb_hdrs = bm_header_widget_new(bm, NULL);
135 gtk_box_pack_start(GTK_BOX(mw->container), emb_hdrs, FALSE, FALSE, 0);
136
137 balsa_mime_widget_message_set_headers(bm, mw, mime_body);
138 }
139
140 /* return the created widget (may be NULL) */
141 return mw;
142 }
143
144
145 /* ----- message/external-body related stuff ----- */
146 static BalsaMimeWidget *
bmw_message_extbody_url(LibBalsaMessageBody * mime_body,rfc_extbody_t url_type)147 bmw_message_extbody_url(LibBalsaMessageBody * mime_body,
148 rfc_extbody_t url_type)
149 {
150 GtkWidget *button;
151 GString *msg = NULL;
152 gchar *url;
153 BalsaMimeWidget *mw;
154
155 if (url_type == RFC2046_EXTBODY_LOCALFILE) {
156 url = libbalsa_message_body_get_parameter(mime_body, "name");
157
158 if (!url)
159 return NULL;
160
161 msg = g_string_new(_("Content Type: external-body\n"));
162 g_string_append_printf(msg, _("Access type: local-file\n"));
163 g_string_append_printf(msg, _("File name: %s"), url);
164 } else if (url_type == RFC2017_EXTBODY_URL) {
165 gchar *local_name;
166
167 local_name = libbalsa_message_body_get_parameter(mime_body, "URL");
168
169 if (!local_name)
170 return NULL;
171
172 url = g_strdup(local_name);
173 msg = g_string_new(_("Content Type: external-body\n"));
174 g_string_append_printf(msg, _("Access type: URL\n"));
175 g_string_append_printf(msg, _("URL: %s"), url);
176 g_free(local_name);
177 } else { /* *FTP* */
178 gchar *ftp_dir, *ftp_name, *ftp_site;
179
180 ftp_dir =
181 libbalsa_message_body_get_parameter(mime_body, "directory");
182 ftp_name = libbalsa_message_body_get_parameter(mime_body, "name");
183 ftp_site = libbalsa_message_body_get_parameter(mime_body, "site");
184
185 if (!ftp_name || !ftp_site) {
186 g_free(ftp_dir);
187 g_free(ftp_name);
188 g_free(ftp_site);
189 return NULL;
190 }
191
192 if (ftp_dir)
193 url = g_strdup_printf("%s://%s/%s/%s",
194 url_type == RFC2046_EXTBODY_TFTP
195 ? "tftp" : "ftp",
196 ftp_site, ftp_dir, ftp_name);
197 else
198 url = g_strdup_printf("%s://%s/%s",
199 url_type == RFC2046_EXTBODY_TFTP
200 ? "tftp" : "ftp", ftp_site, ftp_name);
201 msg = g_string_new(_("Content Type: external-body\n"));
202 g_string_append_printf(msg, _("Access type: %s\n"),
203 url_type == RFC2046_EXTBODY_TFTP ? "tftp" :
204 url_type ==
205 RFC2046_EXTBODY_FTP ? "ftp" : "anon-ftp");
206 g_string_append_printf(msg, _("FTP site: %s\n"), ftp_site);
207 if (ftp_dir)
208 g_string_append_printf(msg, _("Directory: %s\n"), ftp_dir);
209 g_string_append_printf(msg, _("File name: %s"), ftp_name);
210 g_free(ftp_dir);
211 g_free(ftp_name);
212 g_free(ftp_site);
213 }
214
215 /* now create & return the widget... */
216 mw = g_object_new(BALSA_TYPE_MIME_WIDGET, NULL);
217
218 mw->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, BMW_VBOX_SPACE);
219 gtk_container_set_border_width(GTK_CONTAINER(mw->widget),
220 BMW_CONTAINER_BORDER);
221
222 gtk_box_pack_start(GTK_BOX(mw->widget), gtk_label_new(msg->str), FALSE,
223 FALSE, 0);
224 g_string_free(msg, TRUE);
225
226 button = gtk_button_new_with_label(url);
227 gtk_box_pack_start(GTK_BOX(mw->widget), button, FALSE, FALSE,
228 BMW_BUTTON_PACK_SPACE);
229 g_object_set_data_full(G_OBJECT(button), "call_url", url,
230 (GDestroyNotify) g_free);
231 g_signal_connect(G_OBJECT(button), "clicked",
232 G_CALLBACK(extbody_call_url), NULL);
233
234 return mw;
235 }
236
237
238 static BalsaMimeWidget *
bmw_message_extbody_mail(LibBalsaMessageBody * mime_body)239 bmw_message_extbody_mail(LibBalsaMessageBody * mime_body)
240 {
241 GtkWidget *button;
242 GString *msg = NULL;
243 gchar *mail_subject, *mail_site;
244 BalsaMimeWidget *mw;
245
246 mail_site = libbalsa_message_body_get_parameter(mime_body, "server");
247
248 if (!mail_site)
249 return NULL;
250
251 mail_subject =
252 libbalsa_message_body_get_parameter(mime_body, "subject");
253
254 msg = g_string_new(_("Content Type: external-body\n"));
255 g_string_append(msg, _("Access type: mail-server\n"));
256 g_string_append_printf(msg, _("Mail server: %s\n"), mail_site);
257 if (mail_subject)
258 g_string_append_printf(msg, _("Subject: %s\n"), mail_subject);
259 g_free(mail_subject);
260 g_free(mail_site);
261
262 /* now create & return the widget... */
263 mw = g_object_new(BALSA_TYPE_MIME_WIDGET, NULL);
264
265 mw->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, BMW_VBOX_SPACE);
266 gtk_container_set_border_width(GTK_CONTAINER(mw->widget),
267 BMW_CONTAINER_BORDER);
268
269 gtk_box_pack_start(GTK_BOX(mw->widget), gtk_label_new(msg->str), FALSE,
270 FALSE, 0);
271 g_string_free(msg, TRUE);
272
273 button =
274 gtk_button_new_with_mnemonic(_
275 ("Se_nd message to obtain this part"));
276 gtk_box_pack_start(GTK_BOX(mw->widget), button, FALSE, FALSE,
277 BMW_BUTTON_PACK_SPACE);
278 g_signal_connect(G_OBJECT(button), "clicked",
279 G_CALLBACK(extbody_send_mail), (gpointer) mime_body);
280
281
282 return mw;
283 }
284
285
286 static void
extbody_call_url(GtkWidget * button,gpointer data)287 extbody_call_url(GtkWidget * button, gpointer data)
288 {
289 gchar *url = g_object_get_data(G_OBJECT(button), "call_url");
290 GdkScreen *screen;
291 GError *err = NULL;
292
293 g_return_if_fail(url);
294 screen = gtk_widget_get_screen(button);
295 gtk_show_uri(screen, url, gtk_get_current_event_time(), &err);
296 if (err) {
297 balsa_information(LIBBALSA_INFORMATION_WARNING,
298 _("Error showing %s: %s\n"), url, err->message);
299 g_error_free(err);
300 }
301 }
302
303 static void
extbody_send_mail(GtkWidget * button,LibBalsaMessageBody * mime_body)304 extbody_send_mail(GtkWidget * button, LibBalsaMessageBody * mime_body)
305 {
306 LibBalsaMessage *message;
307 LibBalsaMessageBody *body;
308 gchar *data;
309 GError *err = NULL;
310 LibBalsaMsgCreateResult result;
311
312 /* create a message */
313 message = libbalsa_message_new();
314 message->headers->from = internet_address_list_new();
315 internet_address_list_add(message->headers->from,
316 balsa_app.current_ident->ia);
317
318 data = libbalsa_message_body_get_parameter(mime_body, "subject");
319 if (data) {
320 libbalsa_message_set_subject(message, data);
321 g_free(data);
322 }
323
324 data = libbalsa_message_body_get_parameter(mime_body, "server");
325 message->headers->to_list = internet_address_list_parse_string(data);
326 g_free(data);
327
328 /* the original body my have some data to be returned as commands... */
329 body = libbalsa_message_body_new(message);
330
331 if(libbalsa_message_body_get_content(mime_body, &data, &err)<0) {
332 balsa_information(LIBBALSA_INFORMATION_ERROR,
333 _("Could not get a part: %s"),
334 err ? err->message : "Unknown error");
335 g_clear_error(&err);
336 }
337
338 if (data) {
339 gchar *p;
340
341 /* ignore everything before the first two newlines */
342 if ((p = strstr(data, "\n\n")))
343 body->buffer = g_strdup(p + 2);
344 else
345 body->buffer = g_strdup(data);
346 g_free(data);
347 }
348 if (mime_body->charset)
349 body->charset = g_strdup(mime_body->charset);
350 else
351 body->charset = g_strdup("US-ASCII");
352 libbalsa_message_append_part(message, body);
353 #if ENABLE_ESMTP
354 result = libbalsa_message_send(message, balsa_app.outbox, NULL,
355 balsa_find_sentbox_by_url,
356 balsa_app.current_ident->smtp_server,
357 FALSE, balsa_app.debug, &err);
358 #else
359 result = libbalsa_message_send(message, balsa_app.outbox, NULL,
360 balsa_find_sentbox_by_url,
361 FALSE, balsa_app.debug, &err);
362 #endif
363 if (result != LIBBALSA_MESSAGE_CREATE_OK)
364 libbalsa_information(LIBBALSA_INFORMATION_ERROR,
365 _("Sending the external body request failed: %s"),
366 err ? err->message : "?");
367 g_error_free(err);
368 g_object_unref(G_OBJECT(message));
369 }
370
371
372 /* ----- message/rfc822 related stuff ----- */
373
374 BalsaMimeWidget *
balsa_mime_widget_new_message_tl(BalsaMessage * bm,GtkWidget * const * tl_buttons)375 balsa_mime_widget_new_message_tl(BalsaMessage * bm,
376 GtkWidget * const * tl_buttons)
377 {
378 GtkWidget *headers;
379 BalsaMimeWidget *mw;
380
381 mw = g_object_new(BALSA_TYPE_MIME_WIDGET, NULL);
382
383 mw->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, BMW_MESSAGE_PADDING);
384 gtk_container_set_border_width(GTK_CONTAINER(mw->widget), BMW_MESSAGE_PADDING);
385
386 mw->header_widget = headers = bm_header_widget_new(bm, tl_buttons);
387 gtk_box_pack_start(GTK_BOX(mw->widget), headers, FALSE, FALSE, 0);
388
389 mw->container = gtk_box_new(GTK_ORIENTATION_VERTICAL, BMW_MESSAGE_PADDING);
390 gtk_box_pack_start(GTK_BOX(mw->widget), mw->container, TRUE, TRUE,
391 BMW_CONTAINER_BORDER - BMW_MESSAGE_PADDING);
392
393 return mw;
394 }
395
396
397 /* Callback for the "realized" signal; set header frame and text base
398 * color when first realized. */
399 #define BALSA_MESSAGE_GRID "balsa-message-grid"
400 #define bm_header_widget_get_grid(header_widget) \
401 g_object_get_data(G_OBJECT(header_widget), BALSA_MESSAGE_GRID)
402
403 static void
bm_header_ctx_menu_reply(GtkWidget * menu_item,LibBalsaMessageBody * part)404 bm_header_ctx_menu_reply(GtkWidget * menu_item,
405 LibBalsaMessageBody *part)
406 {
407 sendmsg_window_reply_embedded(part, SEND_REPLY);
408 }
409
410 static void
bm_header_extend_popup(GtkWidget * widget,GtkMenu * menu,gpointer arg)411 bm_header_extend_popup(GtkWidget * widget, GtkMenu * menu, gpointer arg)
412 {
413 GtkWidget *menu_item, *submenu;
414 GtkWidget *separator = gtk_separator_menu_item_new();
415
416 gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
417 gtk_widget_show(separator);
418 menu_item = gtk_menu_item_new_with_label(_("Reply..."));
419 g_signal_connect(G_OBJECT(menu_item), "activate",
420 G_CALLBACK(bm_header_ctx_menu_reply),
421 arg);
422 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
423 gtk_widget_show(menu_item);
424
425
426 menu_item = gtk_menu_item_new_with_mnemonic(_("_Copy to folder..."));
427 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
428 gtk_widget_show(menu_item);
429
430 submenu =
431 balsa_mblist_mru_menu(GTK_WINDOW
432 (gtk_widget_get_toplevel(widget)),
433 &balsa_app.folder_mru,
434 G_CALLBACK(balsa_message_copy_part), arg);
435 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
436 submenu);
437 gtk_widget_show_all(submenu);
438 }
439
440 static GtkWidget *
bm_header_widget_new(BalsaMessage * bm,GtkWidget * const * buttons)441 bm_header_widget_new(BalsaMessage * bm, GtkWidget * const * buttons)
442 {
443 GtkWidget *grid;
444 GtkWidget *info_bar_widget;
445 GtkInfoBar *info_bar;
446 GtkWidget *content_area;
447 GtkWidget *action_area;
448 GtkWidget *widget;
449
450 grid = gtk_grid_new();
451 gtk_grid_set_column_spacing(GTK_GRID(grid), 12);
452 gtk_widget_show(grid);
453
454 g_signal_connect(grid, "focus_in_event",
455 G_CALLBACK(balsa_mime_widget_limit_focus), bm);
456 g_signal_connect(grid, "focus_out_event",
457 G_CALLBACK(balsa_mime_widget_unlimit_focus), bm);
458 g_signal_connect(grid, "key_press_event",
459 G_CALLBACK(balsa_mime_widget_key_press_event), bm);
460
461 info_bar_widget = gtk_info_bar_new();
462 info_bar = GTK_INFO_BAR(info_bar_widget);
463 content_area = gtk_info_bar_get_content_area(info_bar);
464 gtk_container_add(GTK_CONTAINER(content_area), grid);
465
466 action_area = gtk_info_bar_get_action_area(info_bar);
467 if (!bm->face_box) {
468 bm->face_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
469 gtk_container_add(GTK_CONTAINER(action_area), bm->face_box);
470 gtk_button_box_set_child_non_homogeneous(GTK_BUTTON_BOX(action_area),
471 bm->face_box, TRUE);
472 }
473
474 if (buttons) {
475 while (*buttons) {
476 gtk_container_add(GTK_CONTAINER(action_area), *buttons++);
477 }
478 }
479
480 widget = gtk_frame_new(NULL);
481 gtk_frame_set_shadow_type(GTK_FRAME(widget), GTK_SHADOW_IN);
482 gtk_container_add(GTK_CONTAINER(widget), info_bar_widget);
483
484 g_object_set_data(G_OBJECT(widget), BALSA_MESSAGE_GRID, grid);
485
486 return widget;
487 }
488
489 static gboolean
label_size_allocate_cb(GtkLabel * label,GdkRectangle * rectangle,GtkWidget * expander)490 label_size_allocate_cb(GtkLabel * label, GdkRectangle * rectangle,
491 GtkWidget * expander)
492 {
493 PangoLayout *layout;
494
495 layout = gtk_label_get_layout(label);
496
497 if (pango_layout_is_wrapped(layout)
498 || pango_layout_is_ellipsized(layout))
499 gtk_widget_show(expander);
500 else
501 gtk_widget_hide(expander);
502
503 return FALSE;
504 }
505
506 static void
expanded_cb(GtkExpander * expander,GParamSpec * arg1,GtkLabel * label)507 expanded_cb(GtkExpander * expander, GParamSpec * arg1, GtkLabel * label)
508 {
509 if (gtk_expander_get_expanded(expander)) {
510 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
511 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
512 } else {
513 gtk_label_set_line_wrap(GTK_LABEL(label), FALSE);
514 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
515 }
516 }
517
518 static void
add_header_gchar(BalsaMessage * bm,GtkGrid * grid,const gchar * header,const gchar * label,const gchar * value)519 add_header_gchar(BalsaMessage * bm, GtkGrid * grid,
520 const gchar * header, const gchar * label,
521 const gchar * value)
522 {
523 gint row;
524 GtkWidget *lab;
525 PangoAttrList *attr_list;
526 PangoFontDescription *font_desc;
527 PangoAttribute *attr;
528
529 if (!(bm->shown_headers == HEADERS_ALL ||
530 libbalsa_find_word(header, balsa_app.selected_headers)))
531 return;
532
533 row = 0;
534 while (gtk_grid_get_child_at(grid, 0, row))
535 row++;
536
537 attr_list = pango_attr_list_new();
538 font_desc =
539 pango_font_description_from_string(strcmp(header, "subject") ?
540 balsa_app.message_font :
541 balsa_app.subject_font);
542 attr = pango_attr_font_desc_new(font_desc);
543 pango_font_description_free(font_desc);
544 pango_attr_list_insert(attr_list, attr);
545
546 lab = gtk_label_new(label);
547 gtk_label_set_attributes(GTK_LABEL(lab), attr_list);
548 gtk_grid_attach(grid, lab, 0, row, 1, 1);
549 gtk_label_set_selectable(GTK_LABEL(lab), TRUE);
550 gtk_misc_set_alignment(GTK_MISC(lab), 0, 0);
551 gtk_widget_show(lab);
552
553 if (value && *value != '\0') {
554 gchar *sanitized;
555 GtkWidget *expander;
556 GtkWidget *hbox;
557
558 sanitized = g_strdup(value);
559 libbalsa_utf8_sanitize(&sanitized,
560 balsa_app.convert_unknown_8bit, NULL);
561 lab = gtk_label_new(sanitized);
562 g_free(sanitized);
563
564 gtk_label_set_attributes(GTK_LABEL(lab), attr_list);
565 gtk_label_set_line_wrap_mode(GTK_LABEL(lab), PANGO_WRAP_WORD_CHAR);
566 gtk_label_set_selectable(GTK_LABEL(lab), TRUE);
567 gtk_misc_set_alignment(GTK_MISC(lab), 0, 0);
568 gtk_widget_set_hexpand(lab, TRUE);
569
570 expander = gtk_expander_new(NULL);
571 g_signal_connect(expander, "notify::expanded",
572 G_CALLBACK(expanded_cb), lab);
573
574 if(bm->shown_headers == HEADERS_ALL) {
575 gtk_label_set_line_wrap(GTK_LABEL(lab), TRUE);
576 gtk_expander_set_expanded(GTK_EXPANDER(expander), TRUE);
577 } else {
578 gtk_label_set_ellipsize(GTK_LABEL(lab), PANGO_ELLIPSIZE_END);
579 gtk_expander_set_expanded(GTK_EXPANDER(expander), FALSE);
580 }
581 g_signal_connect(lab, "size-allocate",
582 G_CALLBACK(label_size_allocate_cb), expander);
583
584 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
585 gtk_container_add(GTK_CONTAINER(hbox), lab);
586 gtk_container_add(GTK_CONTAINER(hbox), expander);
587 gtk_widget_show_all(hbox);
588 gtk_grid_attach(grid, hbox, 1, row, 1, 1);
589 }
590
591 pango_attr_list_unref(attr_list);
592 }
593
594 static void
add_header_address_list(BalsaMessage * bm,GtkGrid * grid,gchar * header,gchar * label,InternetAddressList * list)595 add_header_address_list(BalsaMessage * bm, GtkGrid * grid,
596 gchar * header, gchar * label,
597 InternetAddressList * list)
598 {
599 gchar *value;
600
601 if (list == NULL || internet_address_list_length(list) == 0)
602 return;
603
604 if (!(bm->shown_headers == HEADERS_ALL ||
605 libbalsa_find_word(header, balsa_app.selected_headers)))
606 return;
607
608 value = internet_address_list_to_string(list, FALSE);
609
610 add_header_gchar(bm, grid, header, label, value);
611
612 g_free(value);
613 }
614
615 static void
foreach_label(GtkWidget * widget,LibBalsaMessageBody * part)616 foreach_label(GtkWidget * widget, LibBalsaMessageBody * part)
617 {
618 if (GTK_IS_CONTAINER(widget))
619 gtk_container_foreach(GTK_CONTAINER(widget),
620 (GtkCallback) foreach_label, part);
621 else if (g_signal_lookup("populate-popup",
622 G_TYPE_FROM_INSTANCE(widget)))
623 g_signal_connect(widget, "populate-popup",
624 G_CALLBACK(bm_header_extend_popup), part);
625 }
626
627 void
balsa_mime_widget_message_set_headers(BalsaMessage * bm,BalsaMimeWidget * mw,LibBalsaMessageBody * part)628 balsa_mime_widget_message_set_headers(BalsaMessage * bm, BalsaMimeWidget *mw,
629 LibBalsaMessageBody * part)
630 {
631 GtkWidget *widget;
632 GtkGrid *grid;
633
634 balsa_mime_widget_message_set_headers_d(bm, mw, part->embhdrs, part->parts,
635 part->embhdrs
636 ? part->embhdrs->subject
637 : NULL );
638 if (!(widget = mw->header_widget))
639 return;
640 grid = bm_header_widget_get_grid(widget);
641 gtk_container_foreach(GTK_CONTAINER(grid), (GtkCallback) foreach_label,
642 part);
643 }
644
645 void
balsa_mime_widget_message_set_headers_d(BalsaMessage * bm,BalsaMimeWidget * mw,LibBalsaMessageHeaders * headers,LibBalsaMessageBody * part,const gchar * subject)646 balsa_mime_widget_message_set_headers_d(BalsaMessage * bm,
647 BalsaMimeWidget *mw,
648 LibBalsaMessageHeaders *headers,
649 LibBalsaMessageBody *part,
650 const gchar *subject)
651 {
652 GtkGrid *grid;
653 GList *p;
654 gchar *date;
655 GtkWidget * widget;
656
657 if (!(widget = mw->header_widget))
658 return;
659
660 grid = bm_header_widget_get_grid(widget);
661 gtk_container_foreach(GTK_CONTAINER(grid),
662 (GtkCallback) gtk_widget_destroy, NULL);
663
664 if (!headers) {
665 /* Gmail sometimes fails to do that. */
666 add_header_gchar(bm, grid, "subject", _("Error:"),
667 _("IMAP server did not report message structure"));
668 return;
669 }
670
671 if (bm->shown_headers == HEADERS_NONE) {
672 g_signal_connect(G_OBJECT(widget), "realize",
673 G_CALLBACK(gtk_widget_hide), NULL);
674 return;
675 }
676
677 bm->tab_position = 0;
678
679 add_header_gchar(bm, grid, "subject", _("Subject:"), subject);
680
681 date = libbalsa_message_headers_date_to_utf8(headers,
682 balsa_app.date_string);
683 add_header_gchar(bm, grid, "date", _("Date:"), date);
684 g_free(date);
685
686 if (headers->from) {
687 gchar *from =
688 internet_address_list_to_string(headers->from, FALSE);
689 add_header_gchar(bm, grid, "from", _("From:"), from);
690 g_free(from);
691 }
692
693 if (headers->reply_to) {
694 gchar *reply_to =
695 internet_address_list_to_string(headers->reply_to, FALSE);
696 add_header_gchar(bm, grid, "reply-to", _("Reply-To:"), reply_to);
697 g_free(reply_to);
698 }
699 add_header_address_list(bm, grid, "to", _("To:"), headers->to_list);
700 add_header_address_list(bm, grid, "cc", _("Cc:"), headers->cc_list);
701 add_header_address_list(bm, grid, "bcc", _("Bcc:"), headers->bcc_list);
702
703 #if BALSA_SHOW_FCC_AS_WELL_AS_X_BALSA_FCC
704 if (headers->fcc_url)
705 add_header_gchar(bm, grid, "fcc", _("Fcc:"), headers->fcc_url);
706 #endif
707
708 if (headers->dispnotify_to) {
709 gchar *mdn_to =
710 internet_address_list_to_string(headers->dispnotify_to, FALSE);
711 add_header_gchar(bm, grid, "disposition-notification-to",
712 _("Disposition-Notification-To:"), mdn_to);
713 g_free(mdn_to);
714 }
715
716 /* remaining headers */
717 for (p = g_list_first(headers->user_hdrs); p; p = g_list_next(p)) {
718 gchar **pair = p->data;
719 gchar *hdr;
720
721 hdr = g_strconcat(pair[0], ":", NULL);
722 add_header_gchar(bm, grid, pair[0], hdr, pair[1]);
723 g_free(hdr);
724 }
725
726 #ifdef HAVE_GPGME
727 if (part) {
728 if (part->parts
729 && part->parts->next
730 && part->parts->next->sig_info
731 && part->parts->next->sig_info->status !=
732 GPG_ERR_NOT_SIGNED)
733 /* top-level part is RFC 3156 or RFC 2633 signed */
734 add_header_sigstate(grid, part->parts->next->sig_info);
735 else if (part->sig_info
736 && part->sig_info->status != GPG_ERR_NOT_SIGNED)
737 /* top-level is OpenPGP (RFC 2440) signed */
738 add_header_sigstate(grid, part->sig_info);
739 }
740 #endif
741 }
742
743
744 #ifdef HAVE_GPGME
745 /*
746 * Add the short status of a signature info siginfo to the message headers in
747 * view
748 */
749 static void
add_header_sigstate(GtkGrid * grid,GMimeGpgmeSigstat * siginfo)750 add_header_sigstate(GtkGrid * grid, GMimeGpgmeSigstat * siginfo)
751 {
752 gchar *format;
753 gchar *msg;
754 GtkWidget *label;
755 gint row;
756
757 format = siginfo->status ==
758 GPG_ERR_NO_ERROR ? "<i>%s%s</i>" : "<b><i>%s%s</i><b>";
759 msg = g_markup_printf_escaped
760 (format,
761 libbalsa_gpgme_sig_protocol_name(siginfo->protocol),
762 libbalsa_gpgme_sig_stat_to_gchar(siginfo->status));
763
764 label = gtk_label_new(NULL);
765 gtk_label_set_markup(GTK_LABEL(label), msg);
766 g_free(msg);
767 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
768 gtk_widget_show(label);
769
770 row = 0;
771 while (gtk_grid_get_child_at(grid, 0, row))
772 row++;
773 gtk_grid_attach(grid, label, 0, row, 2, 1);
774 }
775 #endif
776