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