* src/send_message.c
[claws.git] / src / messageview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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 #include "defs.h"
21
22 #include <glib.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkvbox.h>
25 #include <gtk/gtkcontainer.h>
26 #include <gtk/gtkeditable.h>
27 #include <gtk/gtkwindow.h>
28 #include <gtk/gtktext.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <string.h>
32
33 #include "intl.h"
34 #include "main.h"
35 #include "messageview.h"
36 #include "headerview.h"
37 #include "summaryview.h"
38 #include "textview.h"
39 #include "mimeview.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "procmime.h"
43 #include "prefs_common.h"
44 #include "gtkutils.h"
45 #include "utils.h"
46 #include "rfc2015.h"
47 #include "about.h"
48 #include "account.h"
49 #include "alertpanel.h"
50 #include "send_message.h"
51 #include "pgptext.h"
52 #include "menu.h"
53 #include "stock_pixmap.h"
54
55
56 static void messageview_change_view_type(MessageView    *messageview,
57                                          MessageType     type);
58 static void messageview_destroy_cb      (GtkWidget      *widget,
59                                          MessageView    *messageview);
60 static void messageview_size_allocate_cb(GtkWidget      *widget,
61                                          GtkAllocation  *allocation);
62 static void key_pressed                 (GtkWidget      *widget,
63                                          GdkEventKey    *event,
64                                          MessageView    *messageview);
65
66 static void return_receipt_show         (NoticeView     *noticeview, 
67                                          MsgInfo        *msginfo);      
68 static void return_receipt_send_clicked (NoticeView     *noticeview, 
69                                          MsgInfo        *msginfo);
70
71 static PrefsAccount *select_account_from_list
72                                         (GList          *ac_list);
73
74 static void messageview_menubar_cb      (MessageView    *msgview,
75                                          guint           action, 
76                                          GtkWidget      *widget);
77 static void messageview_delete_cb       (MessageView    *msgview, 
78                                          guint           action, 
79                                          GtkWidget      *widget);                                        
80 static void messageview_close_cb        (gpointer        data,
81                                          guint           action,
82                                          GtkWidget      *widget);
83 static void messageview_update          (MessageView *msgview);
84 static void messageview_update_all      (MessageView *msgview);
85
86 static GList *msgview_list = NULL;
87
88 MessageView *messageview_create(MainWindow *mainwin)
89 {
90         MessageView *messageview;
91         GtkWidget *vbox;
92         HeaderView *headerview;
93         TextView *textview;
94         MimeView *mimeview;
95         NoticeView *noticeview;
96
97         debug_print("Creating message view...\n");
98         messageview = g_new0(MessageView, 1);
99
100         messageview->type = MVIEW_TEXT;
101
102         headerview = headerview_create();
103
104         noticeview = noticeview_create(mainwin);
105
106         textview = textview_create();
107         textview->messageview = messageview;
108
109         mimeview = mimeview_create();
110         mimeview->textview = textview_create();
111         mimeview->textview->messageview = messageview;
112         mimeview->messageview = messageview;
113
114         vbox = gtk_vbox_new(FALSE, 0);
115         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(headerview),
116                            FALSE, FALSE, 0);
117         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(noticeview),
118                            FALSE, FALSE, 0);
119         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(textview),
120                            TRUE, TRUE, 0);
121
122         /* to remove without destroyed */
123         gtk_widget_ref(GTK_WIDGET_PTR(textview));
124         gtk_widget_ref(GTK_WIDGET_PTR(mimeview));
125         gtk_widget_ref(GTK_WIDGET_PTR(mimeview->textview));
126
127         messageview->vbox       = vbox;
128         messageview->new_window = FALSE;
129         messageview->window     = NULL;
130         messageview->headerview = headerview;
131         messageview->textview   = textview;
132         messageview->mimeview   = mimeview;
133         messageview->noticeview = noticeview;
134         messageview->mainwin    = mainwin;
135
136         return messageview;
137 }
138
139 static GtkItemFactoryEntry messageview_entries[] =
140 {
141         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
142         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
143         {N_("/_File/_Close"),                   "<control>W", messageview_close_cb, 0, NULL},
144
145         {N_("/_Message"),                       NULL, NULL, 0, "<Branch>"},
146         {N_("/_Message/_Reply"),                "<control>R",   messageview_menubar_cb, COMPOSE_REPLY, NULL},
147         {N_("/_Message/Repl_y to"),             NULL, NULL, 0, "<Branch>"},
148         {N_("/_Message/Repl_y to/_all"),        "<shift><control>R", messageview_menubar_cb, COMPOSE_REPLY_TO_ALL, NULL},
149         {N_("/_Message/Repl_y to/_sender"),     NULL, messageview_menubar_cb, COMPOSE_REPLY_TO_SENDER, NULL},
150         {N_("/_Message/Repl_y to/mailing _list"),
151                                                 "<control>L", messageview_menubar_cb, COMPOSE_REPLY_TO_LIST, NULL},
152 /*      {N_("/_Message/Follow-up and reply to"),NULL, messageview_menubar_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL}, */
153         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
154         {N_("/_Message/_Forward"),              "<control><alt>F", messageview_menubar_cb, COMPOSE_FORWARD, NULL},
155         {N_("/_Message/Redirect"),              NULL, messageview_menubar_cb, COMPOSE_REDIRECT, NULL},
156         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
157         {N_("/_Message/_Delete"),               "<control>D", messageview_delete_cb,  0, NULL},
158         
159         {N_("/_Help"),                          NULL, NULL, 0, "<Branch>"},
160         {N_("/_Help/_About"),                   NULL, about_show, 0, NULL}
161 };
162
163
164 GList *messageview_get_msgview_list(void)
165 {
166         return msgview_list;
167 }
168
169 void messageview_add_toolbar(MessageView *msgview, GtkWidget *window) 
170 {
171         GtkWidget *handlebox;
172         GtkWidget *vbox;
173         GtkWidget *menubar;
174         guint n_menu_entries;
175
176         vbox = gtk_vbox_new(FALSE, 0);
177         gtk_widget_show(vbox);
178         gtk_container_add(GTK_CONTAINER(window), vbox); 
179         
180         n_menu_entries = sizeof(messageview_entries) / sizeof(messageview_entries[0]);
181         menubar = menubar_create(window, messageview_entries,
182                                  n_menu_entries, "<MessageView>", msgview);
183         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
184
185         handlebox = gtk_handle_box_new();
186         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
187         msgview->toolbar = toolbar_create(TOOLBAR_MSGVIEW, handlebox,
188                                           (gpointer)msgview);
189         msgview->handlebox = handlebox;
190         msgview->menubar   = menubar;
191
192         gtk_container_add(GTK_CONTAINER(vbox),
193                           GTK_WIDGET_PTR(msgview));
194
195         msgview_list = g_list_append(msgview_list, msgview);
196 }
197
198 MessageView *messageview_create_with_new_window(MainWindow *mainwin)
199 {
200         GtkWidget *window;
201         MessageView *msgview;
202
203         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
204         gtk_window_set_title(GTK_WINDOW(window), _("Sylpheed - Message View"));
205         gtk_window_set_wmclass(GTK_WINDOW(window), "message_view", "Sylpheed");
206         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
207         gtk_widget_set_usize(window, prefs_common.msgwin_width,
208                              prefs_common.msgwin_height);
209
210         msgview = messageview_create(mainwin);
211
212         gtk_signal_connect(GTK_OBJECT(window), "size_allocate",
213                            GTK_SIGNAL_FUNC(messageview_size_allocate_cb),
214                            msgview);
215         gtk_signal_connect(GTK_OBJECT(window), "destroy",
216                            GTK_SIGNAL_FUNC(messageview_destroy_cb), msgview);
217         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
218                            GTK_SIGNAL_FUNC(key_pressed), msgview);
219
220         messageview_add_toolbar(msgview, window);
221
222         gtk_widget_grab_focus(msgview->textview->text);
223         gtk_widget_show_all(window);
224
225         msgview->new_window = TRUE;
226         msgview->window = window;
227         msgview->visible = TRUE;
228
229         toolbar_set_style(msgview->toolbar->toolbar, msgview->handlebox, 
230                           prefs_common.toolbar_style);
231         messageview_init(msgview);
232
233         return msgview;
234 }
235
236 void messageview_init(MessageView *messageview)
237 {
238         headerview_init(messageview->headerview);
239         textview_init(messageview->textview);
240         mimeview_init(messageview->mimeview);
241         /*messageview_set_font(messageview);*/
242
243         noticeview_hide(messageview->noticeview);
244 }
245
246 static void notification_convert_header(gchar *dest, gint len, 
247                                         const gchar *src_,
248                                         gint header_len)
249 {
250         char *src;
251
252         g_return_if_fail(src_ != NULL);
253         g_return_if_fail(dest != NULL);
254
255         if (len < 1) return;
256
257         Xstrndup_a(src, src_, len, return);
258
259         remove_return(src);
260
261         if (is_ascii_str(src)) {
262                 strncpy2(dest, src, len);
263                 dest[len - 1] = '\0';
264                 return;
265         } else
266                 conv_encode_header(dest, len, src, header_len);
267 }
268
269 static gint disposition_notification_queue(PrefsAccount * account,
270                                            gchar * to, const gchar *file)
271 {
272         FolderItem *queue;
273         gchar *tmp;
274         FILE *fp, *src_fp;
275         gchar buf[BUFFSIZE];
276         gint num;
277
278         debug_print("queueing message...\n");
279         g_return_val_if_fail(account != NULL, -1);
280
281         tmp = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
282                               G_DIR_SEPARATOR, (gint)file);
283         if ((fp = fopen(tmp, "wb")) == NULL) {
284                 FILE_OP_ERROR(tmp, "fopen");
285                 g_free(tmp);
286                 return -1;
287         }
288         if ((src_fp = fopen(file, "rb")) == NULL) {
289                 FILE_OP_ERROR(file, "fopen");
290                 fclose(fp);
291                 unlink(tmp);
292                 g_free(tmp);
293                 return -1;
294         }
295         if (change_file_mode_rw(fp, tmp) < 0) {
296                 FILE_OP_ERROR(tmp, "chmod");
297                 g_warning("can't change file mode\n");
298         }
299
300         /* queueing variables */
301         fprintf(fp, "AF:\n");
302         fprintf(fp, "NF:0\n");
303         fprintf(fp, "PS:10\n");
304         fprintf(fp, "SRH:1\n");
305         fprintf(fp, "SFN:\n");
306         fprintf(fp, "DSR:\n");
307         fprintf(fp, "MID:\n");
308         fprintf(fp, "CFG:\n");
309         fprintf(fp, "PT:0\n");
310         fprintf(fp, "S:%s\n", account->address);
311         fprintf(fp, "RQ:\n");
312         if (account->smtp_server)
313                 fprintf(fp, "SSV:%s\n", account->smtp_server);
314         else
315                 fprintf(fp, "SSV:\n");
316         if (account->nntp_server)
317                 fprintf(fp, "NSV:%s\n", account->nntp_server);
318         else
319                 fprintf(fp, "NSV:\n");
320         fprintf(fp, "SSH:\n");
321         fprintf(fp, "R:<%s>", to);
322         fprintf(fp, "\n");
323         fprintf(fp, "\n");
324
325         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
326                 if (fputs(buf, fp) == EOF) {
327                         FILE_OP_ERROR(tmp, "fputs");
328                         fclose(fp);
329                         fclose(src_fp);
330                         unlink(tmp);
331                         g_free(tmp);
332                         return -1;
333                 }
334         }
335
336         fclose(src_fp);
337         if (fclose(fp) == EOF) {
338                 FILE_OP_ERROR(tmp, "fclose");
339                 unlink(tmp);
340                 g_free(tmp);
341                 return -1;
342         }
343
344         queue = folder_get_default_queue();
345         if ((num = folder_item_add_msg(queue, tmp, TRUE)) < 0) {
346                 g_warning("can't queue the message\n");
347                 unlink(tmp);
348                 g_free(tmp);
349                 return -1;
350         }
351         g_free(tmp);
352
353         return 0;
354 }
355
356 static gint disposition_notification_send(MsgInfo *msginfo)
357 {
358         gchar buf[BUFFSIZE];
359         gchar tmp[MAXPATHLEN + 1];
360         FILE *fp;
361         GSList *to_list;
362         GList *ac_list;
363         PrefsAccount *account;
364         gint ok;
365         gchar *to;
366
367         if ((!msginfo->returnreceiptto) && 
368             (!msginfo->dispositionnotificationto)) 
369                 return -1;
370
371         /* RFC2298: Test for Return-Path */
372         if (msginfo->dispositionnotificationto)
373                 to = msginfo->dispositionnotificationto;
374         else
375                 to = msginfo->returnreceiptto;
376
377         ok = get_header_from_msginfo(msginfo, buf, sizeof(buf),
378                                 "Return-Path:");
379         if (ok == 0) {
380                 gchar *to_addr = g_strdup(to);
381                 extract_address(to_addr);
382                 extract_address(buf);
383                 ok = strcmp(to_addr, buf);
384                 g_free(to_addr);
385         } else {
386                 strncpy(buf, _("<No Return-Path found>"), 
387                                 sizeof(buf));
388         }
389         
390         if (ok != 0) {
391                 AlertValue val;
392                 gchar *message;
393                 message = g_strdup_printf(
394                                  _("The notification address to which the "
395                                    "return receipt is to be sent\n"
396                                    "does not correspond to the return path:\n"
397                                    "Notification address: %s\n"
398                                    "Return path: %s\n"
399                                    "It is advised to not to send the return "
400                                    "receipt."), to, buf);
401                 val = alertpanel(_("Warning"), message, _("Send"),
402                                 _("+Don't Send"), NULL);
403                 if (val != G_ALERTDEFAULT)
404                         return -1;
405         }
406
407         ac_list = account_find_all_from_address(NULL, msginfo->to);
408         ac_list = account_find_all_from_address(ac_list, msginfo->cc);
409
410         if (ac_list == NULL) {
411                 alertpanel_error(_("This message is asking for a return "
412                                    "receipt notification\n"
413                                    "but according to its 'To:' and 'CC:' "
414                                    "headers it was not\nofficially addressed "
415                                    "to you.\n"
416                                    "Receipt notification cancelled."));
417                 return -1;
418         }
419
420         if (g_list_length(ac_list) > 1)
421                 account = select_account_from_list(ac_list);
422         else
423                 account = (PrefsAccount *) ac_list->data;
424         g_list_free(ac_list);
425
426         if (account == NULL)
427                 return -1;
428
429         /* write to temporary file */
430         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
431                    get_rc_dir(), G_DIR_SEPARATOR, (gint)msginfo);
432
433         if ((fp = fopen(tmp, "wb")) == NULL) {
434                 FILE_OP_ERROR(tmp, "fopen");
435                 return -1;
436         }
437
438         /* chmod for security */
439         if (change_file_mode_rw(fp, tmp) < 0) {
440                 FILE_OP_ERROR(tmp, "chmod");
441                 g_warning("can't change file mode\n");
442         }
443
444         /* Date */
445         get_rfc822_date(buf, sizeof(buf));
446         fprintf(fp, "Date: %s\n", buf);
447
448         /* From */
449         if (account->name && *account->name) {
450                 notification_convert_header
451                         (buf, sizeof(buf), account->name,
452                          strlen("From: "));
453                 fprintf(fp, "From: %s <%s>\n", buf, account->address);
454         } else
455                 fprintf(fp, "From: %s\n", account->address);
456
457         fprintf(fp, "To: %s\n", to);
458
459         /* Subject */
460         notification_convert_header(buf, sizeof(buf), msginfo->subject,
461                                     strlen("Subject: "));
462         fprintf(fp, "Subject: Disposition notification: %s\n", buf);
463
464         if (fclose(fp) == EOF) {
465                 FILE_OP_ERROR(tmp, "fclose");
466                 unlink(tmp);
467                 return -1;
468         }
469
470         to_list = address_list_append(NULL, to);
471         ok = send_message(tmp, account, to_list);
472         
473         if (ok < 0) {
474                 if (prefs_common.queue_msg) {
475                         AlertValue val;
476                         
477                         val = alertpanel
478                                 (_("Queueing"),
479                                  _("Error occurred while sending the notification.\n"
480                                    "Put this notification into queue folder?"),
481                                  _("OK"), _("Cancel"), NULL);
482                         if (G_ALERTDEFAULT == val) {
483                                 ok = disposition_notification_queue(account, to, tmp);
484                                 if (ok < 0)
485                                         alertpanel_error(_("Can't queue the notification."));
486                         }
487                 } else
488                         alertpanel_error_log(_("Error occurred while sending the notification."));
489         }
490
491         if (unlink(tmp) < 0) FILE_OP_ERROR(tmp, "unlink");
492
493         return ok;
494 }
495
496 void messageview_show(MessageView *messageview, MsgInfo *msginfo,
497                       gboolean all_headers)
498 {
499         FILE *fp;
500         gchar *file;
501         MimeInfo *mimeinfo;
502         MsgInfo *tmpmsginfo;
503
504         g_return_if_fail(msginfo != NULL);
505         messageview->msginfo = msginfo;
506
507 #if USE_GPGME
508         if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
509                 return;
510 #else /* !USE_GPGME */
511         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
512         mimeinfo = procmime_scan_mime_header(fp);
513 #endif /* USE_GPGME */
514         fclose(fp);
515         if (!mimeinfo) return;
516
517         file = procmsg_get_message_file_path(msginfo);
518         if (!file) {
519                 g_warning("can't get message file path.\n");
520                 procmime_mimeinfo_free_all(mimeinfo);
521                 return;
522         }
523
524         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
525
526         headerview_show(messageview->headerview, tmpmsginfo);
527         procmsg_msginfo_free(tmpmsginfo);
528
529         messageview->all_headers = all_headers;
530         textview_set_all_headers(messageview->textview, all_headers);
531         textview_set_all_headers(messageview->mimeview->textview, all_headers);
532
533         if (mimeinfo->mime_type != MIME_TEXT &&
534             mimeinfo->mime_type != MIME_TEXT_HTML) {
535                 messageview_change_view_type(messageview, MVIEW_MIME);
536                 mimeview_show_message(messageview->mimeview, mimeinfo, file);
537         } else {
538                 messageview_change_view_type(messageview, MVIEW_TEXT);
539                 textview_show_message(messageview->textview, mimeinfo, file);
540                 procmime_mimeinfo_free_all(mimeinfo);
541         }
542
543         if (MSG_IS_RETRCPT_PENDING(msginfo->flags))
544                 return_receipt_show(messageview->noticeview, msginfo);
545         else 
546                 noticeview_hide(messageview->noticeview);
547
548         g_free(file);
549 }
550
551 static void messageview_change_view_type(MessageView *messageview,
552                                          MessageType type)
553 {
554         TextView *textview = messageview->textview;
555         MimeView *mimeview = messageview->mimeview;
556
557         if (messageview->type == type) return;
558
559         if (type == MVIEW_MIME) {
560                 gtkut_container_remove
561                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
562                          GTK_WIDGET_PTR(textview));
563                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
564                                    GTK_WIDGET_PTR(mimeview), TRUE, TRUE, 0);
565                 gtk_container_add(GTK_CONTAINER(mimeview->vbox),
566                                   GTK_WIDGET_PTR(textview));
567         } else if (type == MVIEW_TEXT) {
568                 gtkut_container_remove
569                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
570                          GTK_WIDGET_PTR(mimeview));
571
572                 if (mimeview->vbox == GTK_WIDGET_PTR(textview)->parent)
573                         gtkut_container_remove(GTK_CONTAINER(mimeview->vbox),
574                                                GTK_WIDGET_PTR(textview));
575
576                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
577                                    GTK_WIDGET_PTR(textview), TRUE, TRUE, 0);
578         } else
579                 return;
580
581         messageview->type = type;
582 }
583
584 void messageview_reflect_prefs_pixmap_theme(void)
585 {
586         GList *cur;
587         MessageView *msgview;
588
589         for (cur = msgview_list; cur != NULL; cur = cur->next) {
590                 msgview = (MessageView*)cur->data;
591                 toolbar_update(TOOLBAR_MSGVIEW, msgview);
592         }
593 }
594
595 void messageview_clear(MessageView *messageview)
596 {
597         messageview_change_view_type(messageview, MVIEW_TEXT);
598         headerview_clear(messageview->headerview);
599         textview_clear(messageview->textview);
600         noticeview_hide(messageview->noticeview);
601 }
602
603 void messageview_destroy(MessageView *messageview)
604 {
605         GtkWidget *textview  = GTK_WIDGET_PTR(messageview->textview);
606         GtkWidget *mimeview  = GTK_WIDGET_PTR(messageview->mimeview);
607
608         debug_print("destroy messageview\n");
609         headerview_destroy(messageview->headerview);
610         textview_destroy(messageview->textview);
611         mimeview_destroy(messageview->mimeview);
612         noticeview_destroy(messageview->noticeview);
613
614         toolbar_clear_list(TOOLBAR_MSGVIEW);
615         if (messageview->toolbar) {
616                 toolbar_destroy(messageview->toolbar);
617                 g_free(messageview->toolbar);
618         }
619         
620         msgview_list = g_list_remove(msgview_list, messageview); 
621
622         g_free(messageview);
623
624         gtk_widget_unref(textview);
625         gtk_widget_unref(mimeview);
626 }
627
628 void messageview_delete(MessageView *msgview)
629 {
630         MsgInfo *msginfo = (MsgInfo*)msgview->msginfo;
631         SummaryView *summaryview = (SummaryView*)msgview->mainwin->summaryview;
632         FolderItem *trash = folder_get_default_trash();
633         GSList *msg_list;
634
635         g_return_if_fail(msginfo != NULL);
636         g_return_if_fail(trash   != NULL);
637         
638         msg_list = folder_item_get_msg_list(msginfo->folder);
639         
640         if (msg_list == NULL) {
641                 alertpanel_error(_("Message already removed from folder."));
642                 return;
643         }
644         
645         for (; msg_list != NULL; msg_list = msg_list->next) {
646                 MsgInfo *msginfo_list = (MsgInfo*)msg_list->data;
647                 
648                 if (msginfo->msgnum == msginfo_list->msgnum) {
649
650                         if (prefs_common.immediate_exec)
651                                 folder_item_move_msg(trash, msginfo);
652                         else {
653                                 procmsg_msginfo_set_to_folder(msginfo, trash);
654                                 procmsg_msginfo_set_flags(msginfo, MSG_DELETED, 0);
655                                 /* NOTE: does not update to next message in summaryview
656                                  */
657                         }
658                                 
659                         messageview_update_all(msgview);
660                         break;
661                 }
662         }
663 }
664
665 /*      
666  * scan List of MessageViews checking whether there are any Views holding messages 
667  * which need to be updated (another view might have deleted the one this MessagView holds)
668  */
669 static void messageview_update_all(MessageView *msgview)
670 {
671         MsgInfo *msginfo = (MsgInfo*)msgview->msginfo;
672         GList *cur;
673         
674         g_return_if_fail(msginfo != NULL);
675
676         for (cur = msgview_list; cur != NULL; cur = cur->next) {
677                 MessageView *msgview = (MessageView*)cur->data;
678                 MsgInfo *msginfo_list = (MsgInfo*)msgview->msginfo;
679                 
680                 g_return_if_fail(msginfo != NULL);
681
682                 if (msginfo->msgnum == msginfo_list->msgnum)
683                         messageview_update(msgview);
684         }
685 }
686
687 /* 
688  * \brief update messageview with currently selected message in summaryview
689  *        leave unchanged if summaryview is empty
690  * \param pointer to MessageView
691  */     
692 static void messageview_update(MessageView *msgview)
693 {
694         SummaryView *summaryview = (SummaryView*)msgview->mainwin->summaryview;
695
696         g_return_if_fail(summaryview != NULL);
697         
698         if (summaryview->selected) {
699                 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
700                 MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, 
701                                                       summaryview->selected);
702                 g_return_if_fail(msginfo != NULL);
703
704                 messageview_show(msgview, msginfo, 
705                                  msgview->all_headers);
706         } 
707 }
708
709 void messageview_quote_color_set(void)
710 {
711 }
712
713 void messageview_set_font(MessageView *messageview)
714 {
715         textview_set_font(messageview->textview, NULL);
716 }
717
718 TextView *messageview_get_current_textview(MessageView *messageview)
719 {
720         TextView *text = NULL;
721
722         if (messageview->type == MVIEW_TEXT)
723                 text = messageview->textview;
724         else if (messageview->type == MVIEW_MIME) {
725                 if (gtk_notebook_get_current_page
726                         (GTK_NOTEBOOK(messageview->mimeview->notebook)) == 0)
727                         text = messageview->textview;
728                 else if (messageview->mimeview->type == MIMEVIEW_TEXT)
729                         text = messageview->mimeview->textview;
730         }
731
732         return text;
733 }
734
735 void messageview_copy_clipboard(MessageView *messageview)
736 {
737         TextView *text;
738
739         text = messageview_get_current_textview(messageview);
740         if (text)
741                 gtk_editable_copy_clipboard(GTK_EDITABLE(text->text));
742 }
743
744 void messageview_select_all(MessageView *messageview)
745 {
746         TextView *text;
747
748         text = messageview_get_current_textview(messageview);
749         if (text)
750                 gtk_editable_select_region(GTK_EDITABLE(text->text), 0, -1);
751 }
752
753 void messageview_set_position(MessageView *messageview, gint pos)
754 {
755         textview_set_position(messageview->textview, pos);
756 }
757
758 gboolean messageview_search_string(MessageView *messageview, const gchar *str,
759                                    gboolean case_sens)
760 {
761         return textview_search_string(messageview->textview, str, case_sens);
762         return FALSE;
763 }
764
765 gboolean messageview_search_string_backward(MessageView *messageview,
766                                             const gchar *str,
767                                             gboolean case_sens)
768 {
769         return textview_search_string_backward(messageview->textview,
770                                                str, case_sens);
771         return FALSE;
772 }
773
774 gboolean messageview_is_visible(MessageView *messageview)
775 {
776         return messageview->visible;
777 }
778
779 static void messageview_destroy_cb(GtkWidget *widget, MessageView *messageview)
780 {
781         messageview_destroy(messageview);
782 }
783
784 static void messageview_size_allocate_cb(GtkWidget *widget,
785                                          GtkAllocation *allocation)
786 {
787         g_return_if_fail(allocation != NULL);
788
789         prefs_common.msgwin_width  = allocation->width;
790         prefs_common.msgwin_height = allocation->height;
791 }
792
793 static void key_pressed(GtkWidget *widget, GdkEventKey *event,
794                         MessageView *messageview)
795 {
796         if (event && event->keyval == GDK_Escape && messageview->window)
797                 gtk_widget_destroy(messageview->window);
798 }
799
800 void messageview_toggle_view_real(MessageView *messageview)
801 {
802         MainWindow *mainwin = messageview->mainwin;
803         union CompositeWin *cwin = &mainwin->win;
804         GtkWidget *vpaned = NULL;
805         GtkWidget *container = NULL;
806         GtkItemFactory *ifactory = gtk_item_factory_from_widget(mainwin->menubar);
807         
808         switch (mainwin->type) {
809         case SEPARATE_NONE:
810                 vpaned = cwin->sep_none.vpaned;
811                 container = cwin->sep_none.hpaned;
812                 break;
813         case SEPARATE_FOLDER:
814                 vpaned = cwin->sep_folder.vpaned;
815                 container = mainwin->vbox_body;
816                 break;
817         case SEPARATE_MESSAGE:
818         case SEPARATE_BOTH:
819                 return;
820         }
821
822         if (vpaned->parent != NULL) {
823                 gtk_widget_ref(vpaned);
824                 gtkut_container_remove(GTK_CONTAINER(container), vpaned);
825                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), container);
826                 menu_set_sensitive(ifactory, "/View/Expand Summary View", FALSE);
827                 gtk_widget_grab_focus(GTK_WIDGET(messageview->textview->text));
828         } else {
829                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), vpaned);
830                 gtk_container_add(GTK_CONTAINER(container), vpaned);
831                 gtk_widget_unref(vpaned);
832                 menu_set_sensitive(ifactory, "/View/Expand Summary View", TRUE);
833                 gtk_widget_grab_focus(GTK_WIDGET(mainwin->summaryview->ctree));
834         }
835 }
836
837 static void return_receipt_show(NoticeView *noticeview, MsgInfo *msginfo)
838 {
839         noticeview_set_text(noticeview, _("This messages asks for a return receipt."));
840         noticeview_set_button_text(noticeview, _("Send receipt"));
841         noticeview_set_button_press_callback(noticeview,
842                                              GTK_SIGNAL_FUNC(return_receipt_send_clicked),
843                                              (gpointer) msginfo);
844         noticeview_show(noticeview);
845 }
846
847 static void return_receipt_send_clicked(NoticeView *noticeview, MsgInfo *msginfo)
848 {
849         MsgInfo *tmpmsginfo;
850         gchar *file;
851
852         file = procmsg_get_message_file_path(msginfo);
853         if (!file) {
854                 g_warning("can't get message file path.\n");
855                 return;
856         }
857
858         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
859         tmpmsginfo->folder = msginfo->folder;
860         tmpmsginfo->msgnum = msginfo->msgnum;
861
862         if (disposition_notification_send(tmpmsginfo) >= 0) {
863                 procmsg_msginfo_unset_flags(msginfo, MSG_RETRCPT_PENDING, 0);
864                 noticeview_hide(noticeview);
865         }               
866
867         procmsg_msginfo_free(tmpmsginfo);
868         g_free(file);
869 }
870
871 static void select_account_cb(GtkWidget *w, gpointer data)
872 {
873         *(gint*)data = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(w)));
874 }
875         
876 static PrefsAccount *select_account_from_list(GList *ac_list)
877 {
878         GtkWidget *optmenu;
879         GtkWidget *menu;
880         gint account_id;
881
882         g_return_val_if_fail(ac_list != NULL, NULL);
883         g_return_val_if_fail(ac_list->data != NULL, NULL);
884         
885         optmenu = gtk_option_menu_new();
886         menu = gtkut_account_menu_new(ac_list, select_account_cb, &account_id);
887         if (!menu)
888                 return NULL;
889         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
890         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), 0);
891         account_id = ((PrefsAccount *) ac_list->data)->account_id;
892         if (alertpanel_with_widget(
893                                 _("Return Receipt Notification"),
894                                 _("The message was sent to several of your "
895                                   "accounts.\n"
896                                   "Please choose which account do you want to "
897                                   "use for sending the receipt notification:"),
898                                 _("Send Notification"), _("+Cancel"), NULL,
899                                 optmenu) != G_ALERTDEFAULT)
900                 return NULL;
901         return account_find_from_id(account_id);
902 }
903
904
905 /* 
906  * \brief return selected messageview text used by composing 
907  *        to reply to selected text only
908  *
909  * \param  pointer to Messageview 
910  *
911  * \return pointer to text (needs to be free'd by calling func)
912  */
913 gchar *messageview_get_selection(MessageView *msgview)
914 {
915         gchar *text = NULL;
916         
917         g_return_val_if_fail(msgview != NULL, NULL);
918
919         text = gtkut_editable_get_selection
920                 (GTK_EDITABLE(msgview->textview->text));
921         
922         if (!text && msgview->type == MVIEW_MIME
923             && msgview->mimeview->type == MIMEVIEW_TEXT
924             && msgview->mimeview->textview
925             && !msgview->mimeview->textview->default_text) {
926                 text = gtkut_editable_get_selection 
927                         (GTK_EDITABLE(msgview->mimeview->textview->text));   
928         }
929
930         return text;
931 }
932
933 static void messageview_delete_cb(MessageView *msgview, guint action, GtkWidget *widget)
934 {
935         messageview_delete(msgview);
936 }
937
938 static void messageview_menubar_cb(MessageView *msgview, guint action, GtkWidget *widget)
939 {
940         GSList *msginfo_list = NULL;
941         gchar *body;
942         MsgInfo *msginfo;
943
944         g_return_if_fail(msgview != NULL);
945
946         msginfo = (MsgInfo*)msgview->msginfo;
947         g_return_if_fail(msginfo != NULL);
948
949         msginfo_list = g_slist_append(msginfo_list, msginfo);
950         g_return_if_fail(msginfo_list);
951
952         body =  messageview_get_selection(msgview);
953         compose_reply_mode((ComposeMode)action, msginfo_list, body);
954         g_free(body);
955         g_slist_free(msginfo_list);
956 }
957
958 static void messageview_close_cb(gpointer data, guint action, GtkWidget *widget)
959 {
960         MessageView *messageview = (MessageView *)data;
961         
962         gtk_widget_destroy(messageview->window);
963 }