keep attachement when forwarding or reediting mails
[claws.git] / src / messageview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 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 "textview.h"
38 #include "imageview.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 "account.h"
48 #include "alertpanel.h"
49 #include "send.h"
50
51 static void messageview_change_view_type(MessageView    *messageview,
52                                          MessageType     type);
53 static void messageview_destroy_cb      (GtkWidget      *widget,
54                                          MessageView    *messageview);
55 static void messageview_size_allocate_cb(GtkWidget      *widget,
56                                          GtkAllocation  *allocation);
57 static void key_pressed                 (GtkWidget      *widget,
58                                          GdkEventKey    *event,
59                                          MessageView    *messageview);
60
61 MessageView *messageview_create(void)
62 {
63         MessageView *messageview;
64         GtkWidget *vbox;
65         HeaderView *headerview;
66         TextView *textview;
67         ImageView *imageview;
68         MimeView *mimeview;
69
70         debug_print(_("Creating message view...\n"));
71         messageview = g_new0(MessageView, 1);
72
73         messageview->type = MVIEW_TEXT;
74
75         headerview = headerview_create();
76
77         textview = textview_create();
78         textview->messageview = messageview;
79
80         imageview = imageview_create();
81         imageview->messageview = messageview;
82
83         mimeview = mimeview_create();
84         mimeview->textview = textview;
85         mimeview->imageview = imageview;
86         mimeview->messageview = messageview;
87
88         vbox = gtk_vbox_new(FALSE, 0);
89         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(headerview),
90                            FALSE, FALSE, 0);
91         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(textview),
92                            TRUE, TRUE, 0);
93
94         /* to remove without destroyed */
95         gtk_widget_ref(GTK_WIDGET_PTR(textview));
96         gtk_widget_ref(GTK_WIDGET_PTR(imageview));
97         gtk_widget_ref(GTK_WIDGET_PTR(mimeview));
98
99         messageview->vbox       = vbox;
100         messageview->new_window = FALSE;
101         messageview->window     = NULL;
102         messageview->headerview = headerview;
103         messageview->textview   = textview;
104         messageview->imageview  = imageview;
105         messageview->mimeview   = mimeview;
106
107         return messageview;
108 }
109
110 MessageView *messageview_create_with_new_window(void)
111 {
112         GtkWidget *window;
113         MessageView *msgview;
114
115         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
116         gtk_window_set_title(GTK_WINDOW(window), "Message");
117         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
118         gtk_widget_set_usize(window, prefs_common.msgwin_width,
119                              prefs_common.msgwin_height);
120
121         msgview = messageview_create();
122
123         gtk_signal_connect(GTK_OBJECT(window), "size_allocate",
124                            GTK_SIGNAL_FUNC(messageview_size_allocate_cb),
125                            msgview);
126         gtk_signal_connect(GTK_OBJECT(window), "destroy",
127                            GTK_SIGNAL_FUNC(messageview_destroy_cb), msgview);
128         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
129                            GTK_SIGNAL_FUNC(key_pressed), msgview);
130
131         gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET_PTR(msgview));
132         gtk_widget_grab_focus(msgview->textview->text);
133         gtk_widget_show_all(window);
134
135         msgview->new_window = TRUE;
136         msgview->window = window;
137
138         messageview_init(msgview);
139
140         return msgview;
141 }
142
143 void messageview_init(MessageView *messageview)
144 {
145         headerview_init(messageview->headerview);
146         textview_init(messageview->textview);
147         imageview_init(messageview->imageview);
148         mimeview_init(messageview->mimeview);
149         //messageview_set_font(messageview);
150 }
151
152 static void notification_convert_header(gchar *dest, gint len, gchar *src,
153                                         gint header_len)
154 {
155         g_return_if_fail(src != NULL);
156         g_return_if_fail(dest != NULL);
157
158         if (len < 1) return;
159
160         remove_return(src);
161
162         if (is_ascii_str(src)) {
163                 strncpy2(dest, src, len);
164                 dest[len - 1] = '\0';
165                 return;
166         } else
167                 conv_encode_header(dest, len, src, header_len);
168 }
169
170 static gint dispotition_notification_send(MsgInfo * msginfo)
171 {
172         gchar buf[BUFFSIZE];
173         gchar tmp[MAXPATHLEN + 1];
174         FILE *fp;
175         GSList * to_list;
176         gint ok;
177
178         if ((!msginfo->returnreceiptto) && 
179             (!msginfo->dispositionnotificationto))
180                 return -1;
181
182         /* write to temporary file */
183         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
184                    get_rc_dir(), G_DIR_SEPARATOR, (gint)msginfo);
185
186         if ((fp = fopen(tmp, "w")) == NULL) {
187                 FILE_OP_ERROR(tmp, "fopen");
188                 return -1;
189         }
190
191         /* chmod for security */
192         if (change_file_mode_rw(fp, tmp) < 0) {
193                 FILE_OP_ERROR(tmp, "chmod");
194                 g_warning(_("can't change file mode\n"));
195         }
196
197         /* Date */
198         get_rfc822_date(buf, sizeof(buf));
199         fprintf(fp, "Date: %s\n", buf);
200
201         /* From */
202         if (cur_account->name && *cur_account->name) {
203                 notification_convert_header
204                         (buf, sizeof(buf), cur_account->name,
205                          strlen("From: "));
206                 fprintf(fp, "From: %s <%s>\n", buf, cur_account->address);
207         } else
208                 fprintf(fp, "From: %s\n", cur_account->address);
209
210         /* To */
211         if (msginfo->dispositionnotificationto)
212                 fprintf(fp, "To: %s\n", msginfo->dispositionnotificationto);
213         else
214                 fprintf(fp, "To: %s\n", msginfo->returnreceiptto);
215
216         /* Subject */
217         notification_convert_header(buf, sizeof(buf), msginfo->subject,
218                                     strlen("Subject: "));
219         fprintf(fp, "Subject: Disposition notification: %s\n", buf);
220
221         if (fclose(fp) == EOF) {
222                 FILE_OP_ERROR(tmp, "fclose");
223                 unlink(tmp);
224                 return -1;
225         }
226
227         to_list = address_list_append(NULL, msginfo->dispositionnotificationto);
228         ok = send_message(tmp, cur_account, to_list);
229
230         if (unlink(tmp) < 0) FILE_OP_ERROR(tmp, "unlink");
231
232         return ok;
233 }
234
235 void messageview_show(MessageView *messageview, MsgInfo *msginfo)
236 {
237         FILE *fp;
238         gchar *file;
239         MimeInfo *mimeinfo;
240         MsgInfo *tmpmsginfo;
241
242         g_return_if_fail(msginfo != NULL);
243
244 #if USE_GPGME
245         for (;;) {
246                 if ((fp = procmsg_open_message(msginfo)) == NULL) return;
247                 mimeinfo = procmime_scan_mime_header(fp);
248                 if (!mimeinfo) break;
249
250                 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
251                     rfc2015_is_encrypted(mimeinfo)) {
252                         MSG_SET_FLAGS(msginfo->flags, MSG_ENCRYPTED);
253                 }
254                 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
255                     !msginfo->plaintext_file  &&
256                     !msginfo->decryption_failed) {
257                         /* This is an encrypted message but it has not yet
258                          * been decrypted and there was no unsuccessful
259                          * decryption attempt */
260                         rfc2015_decrypt_message(msginfo, mimeinfo, fp);
261                         if (msginfo->plaintext_file &&
262                             !msginfo->decryption_failed) {
263                                 fclose(fp);
264                                 continue;
265                         }
266                 }
267
268                 break;
269         }
270 #else /* !USE_GPGME */
271         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
272         mimeinfo = procmime_scan_mime_header(fp);
273 #endif /* USE_GPGME */
274         fclose(fp);
275         if (!mimeinfo) return;
276
277         file = procmsg_get_message_file_path(msginfo);
278         g_return_if_fail(file != NULL);
279
280         tmpmsginfo = procheader_parse(file, msginfo->flags, TRUE);
281
282         if (prefs_common.return_receipt
283             && (tmpmsginfo->dispositionnotificationto
284                 || tmpmsginfo->returnreceiptto)
285             && (MSG_IS_UNREAD(msginfo->flags))) {
286                 gint ok;
287                 
288                 if (alertpanel(_("Return Receipt"), _("Send return receipt ?"),
289                                _("Yes"), _("No"), NULL) == G_ALERTDEFAULT) {
290                         ok = dispotition_notification_send(tmpmsginfo);
291                         if (ok < 0)
292                                 alertpanel_error(_("Error occurred while sending notification."));
293                 }
294         }
295
296         headerview_show(messageview->headerview, tmpmsginfo);
297         procmsg_msginfo_free(tmpmsginfo);
298
299         if (mimeinfo->mime_type != MIME_TEXT) {
300                 messageview_change_view_type(messageview, MVIEW_MIME);
301                 mimeview_show_message(messageview->mimeview, mimeinfo, file);
302         } else {
303                 messageview_change_view_type(messageview, MVIEW_TEXT);
304                 textview_show_message(messageview->textview, mimeinfo, file);
305                 procmime_mimeinfo_free(mimeinfo);
306         }
307
308         g_free(file);
309 }
310
311 static void messageview_change_view_type(MessageView *messageview,
312                                          MessageType type)
313 {
314         TextView *textview = messageview->textview;
315         ImageView *imageview = messageview->imageview;
316         MimeView *mimeview = messageview->mimeview;
317
318         if (messageview->type == type) return;
319
320         if (type == MVIEW_MIME) {
321                 gtk_container_remove
322                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
323                          GTK_WIDGET_PTR(textview));
324                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
325                                    GTK_WIDGET_PTR(mimeview), TRUE, TRUE, 0);
326                 gtk_container_add(GTK_CONTAINER(mimeview->vbox),
327                                   GTK_WIDGET_PTR(textview));
328                 mimeview->type = MIMEVIEW_TEXT;
329         } else if (type == MVIEW_TEXT) {
330                 gtk_container_remove
331                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
332                          GTK_WIDGET_PTR(mimeview));
333
334                 if (mimeview->vbox == GTK_WIDGET_PTR(textview)->parent) {
335                         gtk_container_remove(GTK_CONTAINER(mimeview->vbox),
336                                              GTK_WIDGET_PTR(textview));
337                 } else {
338                         gtk_container_remove(GTK_CONTAINER(mimeview->vbox),
339                                              GTK_WIDGET_PTR(imageview));
340                 }
341
342                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
343                                    GTK_WIDGET_PTR(textview), TRUE, TRUE, 0);
344         } else
345                 return;
346
347         messageview->type = type;
348 }
349
350 void messageview_clear(MessageView *messageview)
351 {
352         messageview_change_view_type(messageview, MVIEW_TEXT);
353         headerview_clear(messageview->headerview);
354         textview_clear(messageview->textview);
355 }
356
357 void messageview_destroy(MessageView *messageview)
358 {
359         GtkWidget *textview  = GTK_WIDGET_PTR(messageview->textview);
360         GtkWidget *imageview = GTK_WIDGET_PTR(messageview->imageview);
361         GtkWidget *mimeview  = GTK_WIDGET_PTR(messageview->mimeview);
362
363         headerview_destroy(messageview->headerview);
364         textview_destroy(messageview->textview);
365         imageview_destroy(messageview->imageview);
366         mimeview_destroy(messageview->mimeview);
367
368         g_free(messageview);
369
370         gtk_widget_unref(textview);
371         gtk_widget_unref(imageview);
372         gtk_widget_unref(mimeview);
373 }
374
375 void messageview_quote_color_set(void)
376 {
377 }
378
379 void messageview_set_font(MessageView *messageview)
380 {
381         textview_set_font(messageview->textview, NULL);
382 }
383
384 void messageview_copy_clipboard(MessageView *messageview)
385 {
386         if (messageview->type == MVIEW_TEXT)
387                 gtk_editable_copy_clipboard
388                         (GTK_EDITABLE(messageview->textview->text));
389 }
390
391 void messageview_select_all(MessageView *messageview)
392 {
393         if (messageview->type == MVIEW_TEXT)
394                 gtk_editable_select_region
395                         (GTK_EDITABLE(messageview->textview->text), 0, -1);
396 }
397
398 GtkWidget *messageview_get_text_widget(MessageView *messageview)
399 {
400         return messageview->textview->text;
401 }
402
403 static void messageview_destroy_cb(GtkWidget *widget, MessageView *messageview)
404 {
405         messageview_destroy(messageview);
406 }
407
408 static void messageview_size_allocate_cb(GtkWidget *widget,
409                                          GtkAllocation *allocation)
410 {
411         g_return_if_fail(allocation != NULL);
412
413         prefs_common.msgwin_width  = allocation->width;
414         prefs_common.msgwin_height = allocation->height;
415 }
416
417 static void key_pressed(GtkWidget *widget, GdkEventKey *event,
418                         MessageView *messageview)
419 {
420         if (event && event->keyval == GDK_Escape && messageview->window)
421                 gtk_widget_destroy(messageview->window);
422 }