Statistics
| Revision:

root / src / compose.c @ 3236

History | View | Annotate | Download (220 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2012 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
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#ifndef PANGO_ENABLE_ENGINE
27
#  define PANGO_ENABLE_ENGINE
28
#endif
29

    
30
#include <glib.h>
31
#include <glib/gi18n.h>
32
#include <gdk/gdkkeysyms.h>
33
#include <gtk/gtkmain.h>
34
#include <gtk/gtkmenu.h>
35
#include <gtk/gtkmenuitem.h>
36
#include <gtk/gtkitemfactory.h>
37
#include <gtk/gtkcheckmenuitem.h>
38
#include <gtk/gtkoptionmenu.h>
39
#include <gtk/gtkwidget.h>
40
#include <gtk/gtkliststore.h>
41
#include <gtk/gtktreeview.h>
42
#include <gtk/gtktreeselection.h>
43
#include <gtk/gtkcellrenderertext.h>
44
#include <gtk/gtkvpaned.h>
45
#include <gtk/gtkentry.h>
46
#include <gtk/gtkeditable.h>
47
#include <gtk/gtktextview.h>
48
#include <gtk/gtkwindow.h>
49
#include <gtk/gtksignal.h>
50
#include <gtk/gtkvbox.h>
51
#include <gtk/gtkcontainer.h>
52
#include <gtk/gtktoolbar.h>
53
#include <gtk/gtktoolitem.h>
54
#include <gtk/gtktoolbutton.h>
55
#include <gtk/gtkseparatortoolitem.h>
56
#include <gtk/gtktable.h>
57
#include <gtk/gtkhbox.h>
58
#include <gtk/gtklabel.h>
59
#include <gtk/gtkcheckbutton.h>
60
#include <gtk/gtkscrolledwindow.h>
61
#include <gtk/gtktreeview.h>
62
#include <gtk/gtkdnd.h>
63
#include <gtk/gtkclipboard.h>
64
#include <gtk/gtkstock.h>
65
#include <gtk/gtkdialog.h>
66
#include <gtk/gtkimage.h>
67
#include <gtk/gtktooltips.h>
68
#include <pango/pango-break.h>
69

    
70
#if USE_GTKSPELL
71
#  include <gtk/gtkradiomenuitem.h>
72
#  include <gtkspell/gtkspell.h>
73
#if USE_ENCHANT
74
#  include <enchant/enchant.h>
75
#else
76
#  include <aspell.h>
77
#endif
78
#endif
79

    
80
#include <stdio.h>
81
#include <stdlib.h>
82
#include <string.h>
83
#include <ctype.h>
84
#include <sys/types.h>
85
#include <sys/stat.h>
86
#include <unistd.h>
87
#include <time.h>
88
#include <stdlib.h>
89
#if HAVE_SYS_WAIT_H
90
#  include <sys/wait.h>
91
#endif
92
#include <signal.h>
93
#include <errno.h>
94
#ifdef G_OS_WIN32
95
#  include <windows.h>
96
#endif
97

    
98
#include "main.h"
99
#include "mainwindow.h"
100
#include "compose.h"
101
#include "addressbook.h"
102
#include "folderview.h"
103
#include "procmsg.h"
104
#include "menu.h"
105
#include "stock_pixmap.h"
106
#include "send_message.h"
107
#include "imap.h"
108
#include "news.h"
109
#include "customheader.h"
110
#include "prefs_common.h"
111
#include "prefs_common_dialog.h"
112
#include "prefs_account.h"
113
#include "prefs_toolbar.h"
114
#include "action.h"
115
#include "account.h"
116
#include "account_dialog.h"
117
#include "filesel.h"
118
#include "procheader.h"
119
#include "procmime.h"
120
#include "statusbar.h"
121
#include "about.h"
122
#include "base64.h"
123
#include "quoted-printable.h"
124
#include "codeconv.h"
125
#include "utils.h"
126
#include "gtkutils.h"
127
#include "socket.h"
128
#include "alertpanel.h"
129
#include "manage_window.h"
130
#include "gtkshruler.h"
131
#include "folder.h"
132
#include "filter.h"
133
#include "addr_compl.h"
134
#include "quote_fmt.h"
135
#include "template.h"
136
#include "undo.h"
137
#include "plugin.h"
138
#include "md5.h"
139
#include "inc.h"
140

    
141
#if USE_GPGME
142
#  include "rfc2015.h"
143
#endif
144

    
145
enum
146
{
147
        COL_MIMETYPE,
148
        COL_SIZE,
149
        COL_NAME,
150
        COL_ATTACH_INFO,
151
        N_ATTACH_COLS
152
};
153

    
154
typedef enum
155
{
156
        COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE,
157
        COMPOSE_ACTION_MOVE_FORWARD_CHARACTER,
158
        COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER,
159
        COMPOSE_ACTION_MOVE_FORWARD_WORD,
160
        COMPOSE_ACTION_MOVE_BACKWARD_WORD,
161
        COMPOSE_ACTION_MOVE_END_OF_LINE,
162
        COMPOSE_ACTION_MOVE_NEXT_LINE,
163
        COMPOSE_ACTION_MOVE_PREVIOUS_LINE,
164
        COMPOSE_ACTION_DELETE_FORWARD_CHARACTER,
165
        COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER,
166
        COMPOSE_ACTION_DELETE_FORWARD_WORD,
167
        COMPOSE_ACTION_DELETE_BACKWARD_WORD,
168
        COMPOSE_ACTION_DELETE_LINE,
169
        COMPOSE_ACTION_DELETE_LINE_N,
170
        COMPOSE_ACTION_DELETE_TO_LINE_END
171
} ComposeAction;
172

    
173
#define B64_LINE_SIZE                57
174
#define B64_BUFFSIZE                77
175

    
176
#define MAX_REFERENCES_LEN        999
177

    
178
#define TEXTVIEW_MARGIN                6
179

    
180
static GdkColor quote_color = {0, 0, 0, 0xbfff};
181

    
182
static GList *compose_list = NULL;
183

    
184
static Compose *compose_create                        (PrefsAccount        *account,
185
                                                 ComposeMode         mode);
186
static Compose *compose_find_window_by_target        (MsgInfo        *msginfo);
187
static gboolean compose_window_exist                (gint                 x,
188
                                                 gint                 y);
189
static void compose_connect_changed_callbacks        (Compose        *compose);
190

    
191
static GtkWidget *compose_toolbar_create        (Compose        *compose);
192
static GtkWidget *compose_toolbar_create_from_list
193
                                                (Compose        *compose,
194
                                                 GList                *item_list);
195
static void compose_set_toolbar_button_visibility
196
                                                (Compose        *compose);
197

    
198
static GtkWidget *compose_account_option_menu_create
199
                                                (Compose        *compose,
200
                                                 GtkWidget        *hbox);
201
static void compose_set_out_encoding                (Compose        *compose);
202
static void compose_set_template_menu                (Compose        *compose);
203
static void compose_template_apply                (Compose        *compose,
204
                                                 Template        *tmpl,
205
                                                 gboolean         replace);
206
static void compose_destroy                        (Compose        *compose);
207

    
208
static void compose_entry_show                        (Compose        *compose,
209
                                                 ComposeEntryType type);
210
static GtkEntry *compose_get_entry                (Compose        *compose,
211
                                                 ComposeEntryType type);
212
static void compose_entries_set                        (Compose        *compose,
213
                                                 const gchar        *mailto);
214
static void compose_entries_set_from_item        (Compose        *compose,
215
                                                 FolderItem        *item,
216
                                                 ComposeMode         mode);
217
static gint compose_parse_header                (Compose        *compose,
218
                                                 MsgInfo        *msginfo);
219
static gchar *compose_parse_references                (const gchar        *ref,
220
                                                 const gchar        *msgid);
221
static gint compose_parse_source_msg                (Compose        *compose,
222
                                                 MsgInfo        *msginfo);
223

    
224
static gchar *compose_quote_fmt                        (Compose        *compose,
225
                                                 MsgInfo        *msginfo,
226
                                                 const gchar        *fmt,
227
                                                 const gchar        *qmark,
228
                                                 const gchar        *body);
229

    
230
static void compose_reply_set_entry                (Compose        *compose,
231
                                                 MsgInfo        *msginfo,
232
                                                 ComposeMode         mode);
233
static void compose_reedit_set_entry                (Compose        *compose,
234
                                                 MsgInfo        *msginfo);
235

    
236
static void compose_insert_sig                        (Compose        *compose,
237
                                                 gboolean         append,
238
                                                 gboolean         replace,
239
                                                 gboolean         scroll);
240
static void compose_enable_sig                        (Compose        *compose);
241
static gchar *compose_get_signature_str                (Compose        *compose);
242

    
243
static void compose_insert_file                        (Compose        *compose,
244
                                                 const gchar        *file,
245
                                                 gboolean         scroll);
246

    
247
static void compose_attach_append                (Compose        *compose,
248
                                                 const gchar        *file,
249
                                                 const gchar        *filename,
250
                                                 const gchar        *content_type);
251
static void compose_attach_parts                (Compose        *compose,
252
                                                 MsgInfo        *msginfo);
253

    
254
static void compose_wrap_paragraph                (Compose        *compose,
255
                                                 GtkTextIter        *par_iter);
256
static void compose_wrap_all                        (Compose        *compose);
257
static void compose_wrap_all_full                (Compose        *compose,
258
                                                 gboolean         autowrap);
259

    
260
static void compose_set_title                        (Compose        *compose);
261
static void compose_select_account                (Compose        *compose,
262
                                                 PrefsAccount        *account,
263
                                                 gboolean         init);
264

    
265
static gboolean compose_check_for_valid_recipient
266
                                                (Compose        *compose);
267
static gboolean compose_check_entries                (Compose        *compose);
268
static gboolean compose_check_attachments        (Compose        *compose);
269
static gboolean compose_check_recipients        (Compose        *compose);
270
static gboolean compose_check_activities        (Compose        *compose);
271

    
272
static void compose_add_new_recipients_to_addressbook
273
                                                (Compose        *compose);
274

    
275
static gint compose_send                        (Compose        *compose);
276
static gint compose_write_to_file                (Compose        *compose,
277
                                                 const gchar        *file,
278
                                                 gboolean         is_draft);
279
static gint compose_write_body_to_file                (Compose        *compose,
280
                                                 const gchar        *file);
281
static gint compose_redirect_write_to_file        (Compose        *compose,
282
                                                 const gchar        *file);
283
static gint compose_remove_reedit_target        (Compose        *compose);
284
static gint compose_queue                        (Compose        *compose,
285
                                                 const gchar        *file);
286
static gint compose_write_attach                (Compose        *compose,
287
                                                 FILE                *fp,
288
                                                 const gchar        *charset);
289
static gint compose_write_headers                (Compose        *compose,
290
                                                 FILE                *fp,
291
                                                 const gchar        *charset,
292
                                                 const gchar        *body_charset,
293
                                                 EncodingType         encoding,
294
                                                 gboolean         is_draft);
295
static gint compose_redirect_write_headers        (Compose        *compose,
296
                                                 FILE                *fp);
297

    
298
static void compose_convert_header                (Compose        *compose,
299
                                                 gchar                *dest,
300
                                                 gint                 len,
301
                                                 const gchar        *src,
302
                                                 gint                 header_len,
303
                                                 gboolean         addr_field,
304
                                                 const gchar        *encoding);
305
static gchar *compose_convert_filename                (Compose        *compose,
306
                                                 const gchar        *src,
307
                                                 const gchar        *param_name,
308
                                                 const gchar        *encoding);
309
static void compose_generate_msgid                (Compose        *compose,
310
                                                 gchar                *buf,
311
                                                 gint                 len);
312

    
313
static void compose_attach_info_free                (AttachInfo        *ainfo);
314
static void compose_attach_remove_selected        (Compose        *compose);
315

    
316
static void compose_attach_property                (Compose        *compose);
317
static void compose_attach_property_create        (gboolean        *cancelled);
318
static void attach_property_ok                        (GtkWidget        *widget,
319
                                                 gboolean        *cancelled);
320
static void attach_property_cancel                (GtkWidget        *widget,
321
                                                 gboolean        *cancelled);
322
static gint attach_property_delete_event        (GtkWidget        *widget,
323
                                                 GdkEventAny        *event,
324
                                                 gboolean        *cancelled);
325
static gboolean attach_property_key_pressed        (GtkWidget        *widget,
326
                                                 GdkEventKey        *event,
327
                                                 gboolean        *cancelled);
328

    
329
static void compose_attach_open                        (Compose        *compose);
330

    
331
static void compose_exec_ext_editor                (Compose        *compose);
332
static gboolean compose_ext_editor_kill                (Compose        *compose);
333
static void compose_ext_editor_child_exit        (GPid                 pid,
334
                                                 gint                 status,
335
                                                 gpointer         data);
336
static void compose_set_ext_editor_sensitive        (Compose        *compose,
337
                                                 gboolean         sensitive);
338

    
339
static void compose_undo_state_changed                (UndoMain        *undostruct,
340
                                                 gint                 undo_state,
341
                                                 gint                 redo_state,
342
                                                 gpointer         data);
343

    
344
static gint calc_cursor_xpos        (GtkTextView        *text,
345
                                 gint                 extra,
346
                                 gint                 char_width);
347

    
348
/* callback functions */
349

    
350
static gboolean compose_edit_size_alloc (GtkEditable        *widget,
351
                                         GtkAllocation        *allocation,
352
                                         GtkSHRuler        *shruler);
353

    
354
static void toolbar_send_cb                (GtkWidget        *widget,
355
                                         gpointer         data);
356
static void toolbar_send_later_cb        (GtkWidget        *widget,
357
                                         gpointer         data);
358
static void toolbar_draft_cb                (GtkWidget        *widget,
359
                                         gpointer         data);
360
static void toolbar_insert_cb                (GtkWidget        *widget,
361
                                         gpointer         data);
362
static void toolbar_attach_cb                (GtkWidget        *widget,
363
                                         gpointer         data);
364
static void toolbar_sig_cb                (GtkWidget        *widget,
365
                                         gpointer         data);
366
static void toolbar_ext_editor_cb        (GtkWidget        *widget,
367
                                         gpointer         data);
368
static void toolbar_linewrap_cb                (GtkWidget        *widget,
369
                                         gpointer         data);
370
static void toolbar_address_cb                (GtkWidget        *widget,
371
                                         gpointer         data);
372
static void toolbar_prefs_common_cb        (GtkWidget        *widget,
373
                                         gpointer         data);
374
static void toolbar_prefs_account_cb        (GtkWidget        *widget,
375
                                         gpointer         data);
376

    
377
static gboolean toolbar_button_pressed        (GtkWidget        *widget,
378
                                         GdkEventButton        *event,
379
                                         gpointer         data);
380

    
381
static void account_activated                (GtkMenuItem        *menuitem,
382
                                         gpointer         data);
383

    
384
static void attach_selection_changed        (GtkTreeSelection        *selection,
385
                                         gpointer                 data);
386

    
387
static gboolean attach_button_pressed        (GtkWidget        *widget,
388
                                         GdkEventButton        *event,
389
                                         gpointer         data);
390
static gboolean attach_key_pressed        (GtkWidget        *widget,
391
                                         GdkEventKey        *event,
392
                                         gpointer         data);
393

    
394
static void compose_send_cb                (gpointer         data,
395
                                         guint                 action,
396
                                         GtkWidget        *widget);
397
static void compose_send_later_cb        (gpointer         data,
398
                                         guint                 action,
399
                                         GtkWidget        *widget);
400

    
401
static void compose_draft_cb                (gpointer         data,
402
                                         guint                 action,
403
                                         GtkWidget        *widget);
404

    
405
static void compose_attach_open_cb        (gpointer         data,
406
                                         guint                 action,
407
                                         GtkWidget        *widget);
408
static void compose_attach_cb                (gpointer         data,
409
                                         guint                 action,
410
                                         GtkWidget        *widget);
411
static void compose_insert_file_cb        (gpointer         data,
412
                                         guint                 action,
413
                                         GtkWidget        *widget);
414
static void compose_insert_sig_cb        (gpointer         data,
415
                                         guint                 action,
416
                                         GtkWidget        *widget);
417

    
418
static void compose_close_cb                (gpointer         data,
419
                                         guint                 action,
420
                                         GtkWidget        *widget);
421

    
422
static void compose_set_encoding_cb        (gpointer         data,
423
                                         guint                 action,
424
                                         GtkWidget        *widget);
425

    
426
static void compose_address_cb                (gpointer         data,
427
                                         guint                 action,
428
                                         GtkWidget        *widget);
429
static void compose_template_activate_cb(GtkWidget        *widget,
430
                                         gpointer         data);
431

    
432
static void compose_ext_editor_cb        (gpointer         data,
433
                                         guint                 action,
434
                                         GtkWidget        *widget);
435

    
436
static gint compose_delete_cb                (GtkWidget        *widget,
437
                                         GdkEventAny        *event,
438
                                         gpointer         data);
439

    
440
static gint compose_window_state_cb        (GtkWidget                *widget,
441
                                         GdkEventWindowState        *event,
442
                                         gpointer                 data);
443

    
444
static void compose_undo_cb                (Compose        *compose);
445
static void compose_redo_cb                (Compose        *compose);
446
static void compose_cut_cb                (Compose        *compose);
447
static void compose_copy_cb                (Compose        *compose);
448
static void compose_paste_cb                (Compose        *compose);
449
static void compose_paste_as_quote_cb        (Compose        *compose);
450
static void compose_allsel_cb                (Compose        *compose);
451

    
452
static void compose_grab_focus_cb        (GtkWidget        *widget,
453
                                         Compose        *compose);
454

    
455
#if USE_GPGME
456
static void compose_signing_toggled        (GtkWidget        *widget,
457
                                         Compose        *compose);
458
static void compose_encrypt_toggled        (GtkWidget        *widget,
459
                                         Compose        *compose);
460
#endif
461

    
462
#if 0
463
static void compose_attach_toggled        (GtkWidget        *widget,
464
                                         Compose        *compose);
465
#endif
466

    
467
static void compose_buffer_changed_cb        (GtkTextBuffer        *textbuf,
468
                                         Compose        *compose);
469
static void compose_changed_cb                (GtkEditable        *editable,
470
                                         Compose        *compose);
471

    
472
static void compose_wrap_cb                (gpointer         data,
473
                                         guint                 action,
474
                                         GtkWidget        *widget);
475
static void compose_toggle_autowrap_cb        (gpointer         data,
476
                                         guint                 action,
477
                                         GtkWidget        *widget);
478

    
479
static void compose_toggle_to_cb        (gpointer         data,
480
                                         guint                 action,
481
                                         GtkWidget        *widget);
482
static void compose_toggle_cc_cb        (gpointer         data,
483
                                         guint                 action,
484
                                         GtkWidget        *widget);
485
static void compose_toggle_bcc_cb        (gpointer         data,
486
                                         guint                 action,
487
                                         GtkWidget        *widget);
488
static void compose_toggle_replyto_cb        (gpointer         data,
489
                                         guint                 action,
490
                                         GtkWidget        *widget);
491
static void compose_toggle_followupto_cb(gpointer         data,
492
                                         guint                 action,
493
                                         GtkWidget        *widget);
494
static void compose_toggle_ruler_cb        (gpointer         data,
495
                                         guint                 action,
496
                                         GtkWidget        *widget);
497
static void compose_toggle_attach_cb        (gpointer         data,
498
                                         guint                 action,
499
                                         GtkWidget        *widget);
500
static void compose_customize_toolbar_cb(gpointer         data,
501
                                         guint                 action,
502
                                         GtkWidget        *widget);
503

    
504
static void compose_toggle_mdn_cb        (gpointer         data,
505
                                         guint                 action,
506
                                         GtkWidget        *widget);
507

    
508
#if USE_GPGME
509
static void compose_toggle_sign_cb        (gpointer         data,
510
                                         guint                 action,
511
                                         GtkWidget        *widget);
512
static void compose_toggle_encrypt_cb        (gpointer         data,
513
                                         guint                 action,
514
                                         GtkWidget        *widget);
515
#endif
516

    
517
#if USE_GTKSPELL
518
static void compose_set_spell_lang_menu (Compose        *compose);
519
static void compose_change_spell_lang_menu
520
                                        (Compose        *compose,
521
                                         const gchar        *lang);
522
static void compose_toggle_spell_cb        (gpointer         data,
523
                                         guint                 action,
524
                                         GtkWidget        *widget);
525
static void compose_set_spell_lang_cb        (GtkWidget        *widget,
526
                                         gpointer         data);
527
#endif
528

    
529
static void compose_attach_drag_received_cb (GtkWidget                *widget,
530
                                             GdkDragContext        *drag_context,
531
                                             gint                 x,
532
                                             gint                 y,
533
                                             GtkSelectionData        *data,
534
                                             guint                 info,
535
                                             guint                 time,
536
                                             gpointer                 user_data);
537
static void compose_insert_drag_received_cb (GtkWidget                *widget,
538
                                             GdkDragContext        *drag_context,
539
                                             gint                 x,
540
                                             gint                 y,
541
                                             GtkSelectionData        *data,
542
                                             guint                 info,
543
                                             guint                 time,
544
                                             gpointer                 user_data);
545

    
546
static void to_activated                (GtkWidget        *widget,
547
                                         Compose        *compose);
548
static void newsgroups_activated        (GtkWidget        *widget,
549
                                         Compose        *compose);
550
static void cc_activated                (GtkWidget        *widget,
551
                                         Compose        *compose);
552
static void bcc_activated                (GtkWidget        *widget,
553
                                         Compose        *compose);
554
static void replyto_activated                (GtkWidget        *widget,
555
                                         Compose        *compose);
556
static void followupto_activated        (GtkWidget        *widget,
557
                                         Compose        *compose);
558
static void subject_activated                (GtkWidget        *widget,
559
                                         Compose        *compose);
560

    
561
static void text_inserted                (GtkTextBuffer        *buffer,
562
                                         GtkTextIter        *iter,
563
                                         const gchar        *text,
564
                                         gint                 len,
565
                                         Compose        *compose);
566

    
567
static gboolean autosave_timeout        (gpointer         data);
568

    
569

    
570
static GtkItemFactoryEntry compose_popup_entries[] =
571
{
572
        {N_("/_Open"),                NULL, compose_attach_open_cb, 0, NULL},
573
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
574
        {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
575
        {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
576
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
577
        {N_("/_Properties..."),        NULL, compose_attach_property, 0, NULL}
578
};
579

    
580
static GtkItemFactoryEntry compose_entries[] =
581
{
582
        {N_("/_File"),                                NULL, NULL, 0, "<Branch>"},
583
        {N_("/_File/_Send"),                        "<shift><control>E",
584
                                                compose_send_cb, 0, NULL},
585
        {N_("/_File/Send _later"),                "<shift><control>S",
586
                                                compose_send_later_cb,  0, NULL},
587
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
588
        {N_("/_File/Save to _draft folder"),
589
                                                "<shift><control>D", compose_draft_cb, 0, NULL},
590
        {N_("/_File/Save and _keep editing"),
591
                                                "<control>S", compose_draft_cb, 1, NULL},
592
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
593
        {N_("/_File/_Attach file"),                "<control>M", compose_attach_cb,      0, NULL},
594
        {N_("/_File/_Insert file"),                "<control>I", compose_insert_file_cb, 0, NULL},
595
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
596
        {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig_cb,  0, NULL},
597
        {N_("/_File/A_ppend signature"),        "<shift><control>G", compose_insert_sig_cb,  1, NULL},
598
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
599
        {N_("/_File/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
600

    
601
        {N_("/_Edit"),                        NULL, NULL, 0, "<Branch>"},
602
        {N_("/_Edit/_Undo"),                "<control>Z", compose_undo_cb, 0, NULL},
603
        {N_("/_Edit/_Redo"),                "<control>Y", compose_redo_cb, 0, NULL},
604
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
605
        {N_("/_Edit/Cu_t"),                "<control>X", compose_cut_cb,    0, NULL},
606
        {N_("/_Edit/_Copy"),                "<control>C", compose_copy_cb,   0, NULL},
607
        {N_("/_Edit/_Paste"),                "<control>V", compose_paste_cb,  0, NULL},
608
        {N_("/_Edit/Paste as _quotation"),
609
                                        NULL, compose_paste_as_quote_cb, 0, NULL},
610
        {N_("/_Edit/Select _all"),        "<control>A", compose_allsel_cb, 0, NULL},
611
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
612
        {N_("/_Edit/_Wrap current paragraph"),
613
                                        "<control>L", compose_wrap_cb, 0, NULL},
614
        {N_("/_Edit/Wrap all long _lines"),
615
                                        "<control><alt>L", compose_wrap_cb, 1, NULL},
616
        {N_("/_Edit/Aut_o wrapping"),        "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
617
        {N_("/_View"),                        NULL, NULL, 0, "<Branch>"},
618
        {N_("/_View/_To"),                NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
619
        {N_("/_View/_Cc"),                NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
620
        {N_("/_View/_Bcc"),                NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
621
        {N_("/_View/_Reply-To"),        NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
622
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
623
        {N_("/_View/_Followup-To"),        NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
624
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
625
        {N_("/_View/R_uler"),                NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
626
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
627
        {N_("/_View/_Attachment"),        NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
628
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
629
        {N_("/_View/Cu_stomize toolbar..."),
630
                                        NULL, compose_customize_toolbar_cb, 0, NULL},
631
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
632

    
633
#define ENC_ACTION(action) \
634
        NULL, compose_set_encoding_cb, action, \
635
        "/View/Character encoding/Automatic"
636

    
637
        {N_("/_View/Character _encoding"), NULL, NULL, 0, "<Branch>"},
638
        {N_("/_View/Character _encoding/_Automatic"),
639
                        NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
640
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
641

    
642
        {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"),
643
         ENC_ACTION(C_US_ASCII)},
644
        {N_("/_View/Character _encoding/Unicode (_UTF-8)"),
645
         ENC_ACTION(C_UTF_8)},
646
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
647

    
648
        {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"),
649
         ENC_ACTION(C_ISO_8859_1)},
650
        {N_("/_View/Character _encoding/Western European (ISO-8859-15)"),
651
         ENC_ACTION(C_ISO_8859_15)},
652
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
653

    
654
        {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"),
655
         ENC_ACTION(C_ISO_8859_2)},
656
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
657

    
658
        {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"),
659
         ENC_ACTION(C_ISO_8859_13)},
660
        {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"),
661
         ENC_ACTION(C_ISO_8859_4)},
662
        {N_("/_View/Character _encoding/Baltic (Windows-1257)"),
663
         ENC_ACTION(C_WINDOWS_1257)},
664
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
665

    
666
        {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"),
667
         ENC_ACTION(C_ISO_8859_7)},
668
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
669

    
670
        {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"),
671
         ENC_ACTION(C_ISO_8859_6)},
672
        {N_("/_View/Character _encoding/Arabic (Windows-1256)"),
673
         ENC_ACTION(C_WINDOWS_1256)},
674
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
675

    
676
        {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"),
677
         ENC_ACTION(C_ISO_8859_8)},
678
        {N_("/_View/Character _encoding/Hebrew (Windows-1255)"),
679
         ENC_ACTION(C_WINDOWS_1255)},
680
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
681

    
682
        {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"),
683
         ENC_ACTION(C_ISO_8859_9)},
684
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
685

    
686
        {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"),
687
         ENC_ACTION(C_ISO_8859_5)},
688
        {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"),
689
         ENC_ACTION(C_KOI8_R)},
690
        {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"),
691
         ENC_ACTION(C_KOI8_U)},
692
        {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"),
693
         ENC_ACTION(C_WINDOWS_1251)},
694
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
695

    
696
        {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"),
697
         ENC_ACTION(C_ISO_2022_JP)},
698
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
699

    
700
        {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"),
701
         ENC_ACTION(C_GB2312)},
702
        {N_("/_View/Character _encoding/Simplified Chinese (GBK)"),
703
         ENC_ACTION(C_GBK)},
704
        {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"),
705
         ENC_ACTION(C_BIG5)},
706
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
707

    
708
        {N_("/_View/Character _encoding/Korean (EUC-_KR)"),
709
         ENC_ACTION(C_EUC_KR)},
710
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
711

    
712
        {N_("/_View/Character _encoding/Thai (TIS-620)"),
713
         ENC_ACTION(C_TIS_620)},
714
        {N_("/_View/Character _encoding/Thai (Windows-874)"),
715
         ENC_ACTION(C_WINDOWS_874)},
716

    
717
        {N_("/_Tools"),                        NULL, NULL, 0, "<Branch>"},
718
        {N_("/_Tools/_Address book"),        "<shift><control>A", compose_address_cb , 0, NULL},
719
        {N_("/_Tools/_Template"),        NULL, NULL, 0, "<Branch>"},
720
#ifndef G_OS_WIN32
721
        {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
722
#endif
723
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
724
        {N_("/_Tools/Edit with e_xternal editor"),
725
                                        "<shift><control>X", compose_ext_editor_cb, 0, NULL},
726
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
727
        {N_("/_Tools/Request _disposition notification"),           NULL, compose_toggle_mdn_cb   , 0, "<ToggleItem>"},
728

    
729
#if USE_GPGME
730
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
731
        {N_("/_Tools/PGP Si_gn"),           NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
732
        {N_("/_Tools/PGP _Encrypt"),        NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
733
#endif /* USE_GPGME */
734

    
735
#if USE_GTKSPELL
736
        {N_("/_Tools/---"),                        NULL, NULL, 0, "<Separator>"},
737
        {N_("/_Tools/_Check spell"),                NULL, compose_toggle_spell_cb, 0, "<ToggleItem>"},
738
        {N_("/_Tools/_Set spell language"),        NULL, NULL, 0, "<Branch>"},
739
#endif /* USE_GTKSPELL */
740

    
741
        {N_("/_Help"),                        NULL, NULL, 0, "<Branch>"},
742
        {N_("/_Help/_About"),                NULL, about_show, 0, NULL}
743
};
744

    
745
enum
746
{
747
        DRAG_TYPE_RFC822,
748
        DRAG_TYPE_URI_LIST,
749

    
750
        N_DRAG_TYPES
751
};
752

    
753
static GtkTargetEntry compose_drag_types[] =
754
{
755
        {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
756
        {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
757
};
758

    
759

    
760
Compose *compose_new(PrefsAccount *account, FolderItem *item,
761
                     const gchar *mailto, GPtrArray *attach_files)
762
{
763
        Compose *compose;
764
        GtkTextView *text;
765
        GtkTextBuffer *buffer;
766
        GtkTextIter iter;
767

    
768
        if (!account) account = cur_account;
769
        g_return_val_if_fail(account != NULL, NULL);
770

    
771
        compose = compose_create(account, COMPOSE_NEW);
772

    
773
        undo_block(compose->undostruct);
774

    
775
        if (prefs_common.auto_sig)
776
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
777

    
778
        text = GTK_TEXT_VIEW(compose->text);
779
        buffer = gtk_text_view_get_buffer(text);
780
        gtk_text_buffer_get_start_iter(buffer, &iter);
781
        gtk_text_buffer_place_cursor(buffer, &iter);
782

    
783
        if (account->protocol != A_NNTP) {
784
                if (mailto && *mailto != '\0') {
785
                        compose_entries_set(compose, mailto);
786
                        gtk_widget_grab_focus(compose->subject_entry);
787
                } else if (item) {
788
                        compose_entries_set_from_item
789
                                (compose, item, COMPOSE_NEW);
790
                        if (item->auto_to)
791
                                gtk_widget_grab_focus(compose->subject_entry);
792
                        else
793
                                gtk_widget_grab_focus(compose->to_entry);
794
                } else
795
                        gtk_widget_grab_focus(compose->to_entry);
796
        } else {
797
                if (mailto && *mailto != '\0') {
798
                        compose_entry_append(compose, mailto,
799
                                             COMPOSE_ENTRY_NEWSGROUPS);
800
                        gtk_widget_grab_focus(compose->subject_entry);
801
                } else
802
                        gtk_widget_grab_focus(compose->newsgroups_entry);
803
        }
804

    
805
        if (attach_files) {
806
                gint i;
807
                gchar *file;
808

    
809
                for (i = 0; i < attach_files->len; i++) {
810
                        gchar *utf8file;
811

    
812
                        file = g_ptr_array_index(attach_files, i);
813
                        utf8file = conv_filename_to_utf8(file);
814
                        compose_attach_append(compose, file, utf8file, NULL);
815
                        g_free(utf8file);
816
                }
817
        }
818

    
819
        undo_unblock(compose->undostruct);
820

    
821
        compose_connect_changed_callbacks(compose);
822
        compose_set_title(compose);
823

    
824
        syl_plugin_signal_emit("compose-created", compose);
825

    
826
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
827
                compose->autosave_tag =
828
                        g_timeout_add_full(G_PRIORITY_LOW,
829
                                           prefs_common.autosave_itv * 60 * 1000,
830
                                           autosave_timeout, compose, NULL);
831
        if (prefs_common.auto_exteditor)
832
                compose_exec_ext_editor(compose);
833

    
834
        return compose;
835
}
836

    
837
void compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode,
838
                   const gchar *body)
839
{
840
        Compose *compose;
841
        PrefsAccount *account = NULL;
842
        MsgInfo *replyinfo;
843
        GtkTextBuffer *buffer;
844
        GtkTextIter iter;
845
        gboolean quote = FALSE;
846

    
847
        g_return_if_fail(msginfo != NULL);
848

    
849
        if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
850
                quote = TRUE;
851

    
852
        if (msginfo->folder)
853
                account = account_find_from_item_property(msginfo->folder);
854
        if (!account && msginfo->to && prefs_common.reply_account_autosel) {
855
                gchar *to;
856
                to = g_strdup(msginfo->to);
857
                extract_address(to);
858
                account = account_find_from_address(to);
859
                g_free(to);
860
        }
861
        if (!account && msginfo->folder && msginfo->folder->folder)
862
                account = msginfo->folder->folder->account;
863
        if (!account)
864
                account = cur_account;
865
        g_return_if_fail(account != NULL);
866

    
867
        compose = compose_create(account, COMPOSE_REPLY);
868

    
869
        replyinfo = procmsg_msginfo_get_full_info(msginfo);
870
        if (!replyinfo)
871
                replyinfo = procmsg_msginfo_copy(msginfo);
872
        if (msginfo->folder) {
873
                gchar *id;
874
                id = folder_item_get_identifier(msginfo->folder);
875
                if (id) {
876
                        compose->reply_target = g_strdup_printf
877
                                ("%s/%u", id, msginfo->msgnum);
878
                        g_free(id);
879
                }
880
        }
881

    
882
        if (compose_parse_header(compose, msginfo) < 0) return;
883

    
884
        undo_block(compose->undostruct);
885

    
886
        compose_reply_set_entry(compose, msginfo, mode);
887
        if (item)
888
                compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
889

    
890
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
891

    
892
        if (account->sig_before_quote && prefs_common.auto_sig) {
893
                GtkTextMark *mark;
894
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
895
                mark = gtk_text_buffer_get_insert(buffer);
896
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
897
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
898
        }
899

    
900
        if (quote) {
901
                compose_quote_fmt(compose, replyinfo,
902
                                  prefs_common.quotefmt,
903
                                  prefs_common.quotemark, body);
904
        }
905

    
906
        if (!account->sig_before_quote && prefs_common.auto_sig)
907
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
908

    
909
        if (quote && prefs_common.linewrap_quote)
910
                compose_wrap_all(compose);
911

    
912
        gtk_text_buffer_get_start_iter(buffer, &iter);
913
        gtk_text_buffer_place_cursor(buffer, &iter);
914

    
915
        gtk_widget_grab_focus(compose->text);
916

    
917
        undo_unblock(compose->undostruct);
918

    
919
        compose_connect_changed_callbacks(compose);
920
        compose_set_title(compose);
921

    
922
#if USE_GPGME
923
        if (rfc2015_is_available() && account->encrypt_reply &&
924
            MSG_IS_ENCRYPTED(replyinfo->flags)) {
925
                GtkItemFactory *ifactory;
926

    
927
                ifactory = gtk_item_factory_from_widget(compose->menubar);
928
                menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
929
        }
930
#endif
931

    
932
        procmsg_msginfo_free(replyinfo);
933

    
934
        syl_plugin_signal_emit("compose-created", compose);
935

    
936
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
937
                compose->autosave_tag =
938
                        g_timeout_add_full(G_PRIORITY_LOW,
939
                                           prefs_common.autosave_itv * 60 * 1000,
940
                                           autosave_timeout, compose, NULL);
941
        if (prefs_common.auto_exteditor)
942
                compose_exec_ext_editor(compose);
943
}
944

    
945
void compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach,
946
                     const gchar *body)
947
{
948
        Compose *compose;
949
        PrefsAccount *account = NULL;
950
        GtkTextView *text;
951
        GtkTextBuffer *buffer;
952
        GtkTextIter iter;
953
        GSList *cur;
954
        MsgInfo *msginfo;
955
        GString *forward_targets;
956

    
957
        g_return_if_fail(mlist != NULL);
958

    
959
        msginfo = (MsgInfo *)mlist->data;
960

    
961
        if (msginfo->folder)
962
                account = account_find_from_item(msginfo->folder);
963
        if (!account) account = cur_account;
964
        g_return_if_fail(account != NULL);
965

    
966
        compose = compose_create(account, COMPOSE_FORWARD);
967

    
968
        forward_targets = g_string_new("");
969
        for (cur = mlist; cur != NULL; cur = cur->next) {
970
                msginfo = (MsgInfo *)cur->data;
971
                if (msginfo->folder) {
972
                        gchar *id;
973
                        id = folder_item_get_identifier(msginfo->folder);
974
                        if (id) {
975
                                if (forward_targets->len > 0)
976
                                        g_string_append(forward_targets,
977
                                                        "\n ");
978
                                g_string_append_printf(forward_targets, "%s/%u",
979
                                                       id, msginfo->msgnum);
980
                                g_free(id);
981
                        }
982
                }
983
        }
984
        if (forward_targets->len > 0)
985
                compose->forward_targets = g_string_free(forward_targets,
986
                                                         FALSE);
987
        else
988
                g_string_free(forward_targets, TRUE);
989

    
990
        undo_block(compose->undostruct);
991

    
992
        compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
993
        if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
994
                compose_entry_append(compose, msginfo->subject,
995
                                     COMPOSE_ENTRY_SUBJECT);
996
        if (item)
997
                compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
998

    
999
        text = GTK_TEXT_VIEW(compose->text);
1000
        buffer = gtk_text_view_get_buffer(text);
1001

    
1002
        if (account->sig_before_quote && prefs_common.auto_sig) {
1003
                GtkTextMark *mark;
1004
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1005
                mark = gtk_text_buffer_get_insert(buffer);
1006
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1007
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1008
        }
1009

    
1010
        for (cur = mlist; cur != NULL; cur = cur->next) {
1011
                msginfo = (MsgInfo *)cur->data;
1012

    
1013
                if (as_attach) {
1014
                        gchar *msgfile;
1015

    
1016
                        msgfile = procmsg_get_message_file_path(msginfo);
1017
                        if (!is_file_exist(msgfile))
1018
                                g_warning(_("%s: file not exist\n"), msgfile);
1019
                        else
1020
                                compose_attach_append(compose, msgfile, msgfile,
1021
                                                      "message/rfc822");
1022

    
1023
                        g_free(msgfile);
1024
                } else {
1025
                        MsgInfo *full_msginfo;
1026

    
1027
                        full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1028
                        if (!full_msginfo)
1029
                                full_msginfo = procmsg_msginfo_copy(msginfo);
1030

    
1031
                        if (cur != mlist) {
1032
                                GtkTextMark *mark;
1033
                                mark = gtk_text_buffer_get_insert(buffer);
1034
                                gtk_text_buffer_get_iter_at_mark
1035
                                        (buffer, &iter, mark);
1036
                                gtk_text_buffer_insert
1037
                                        (buffer, &iter, "\n\n", 2);
1038
                        }
1039

    
1040
                        compose_quote_fmt(compose, full_msginfo,
1041
                                          prefs_common.fw_quotefmt,
1042
                                          prefs_common.fw_quotemark, body);
1043
                        compose_attach_parts(compose, msginfo);
1044

    
1045
                        procmsg_msginfo_free(full_msginfo);
1046

    
1047
                        if (body) break;
1048
                }
1049
        }
1050

    
1051
        if (!account->sig_before_quote && prefs_common.auto_sig)
1052
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1053

    
1054
        if (prefs_common.linewrap_quote)
1055
                compose_wrap_all(compose);
1056

    
1057
        gtk_text_buffer_get_start_iter(buffer, &iter);
1058
        gtk_text_buffer_place_cursor(buffer, &iter);
1059

    
1060
        undo_unblock(compose->undostruct);
1061

    
1062
        compose_connect_changed_callbacks(compose);
1063
        compose_set_title(compose);
1064

    
1065
        if (account->protocol != A_NNTP)
1066
                gtk_widget_grab_focus(compose->to_entry);
1067
        else
1068
                gtk_widget_grab_focus(compose->newsgroups_entry);
1069

    
1070
        syl_plugin_signal_emit("compose-created", compose);
1071

    
1072
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1073
                compose->autosave_tag =
1074
                        g_timeout_add_full(G_PRIORITY_LOW,
1075
                                           prefs_common.autosave_itv * 60 * 1000,
1076
                                           autosave_timeout, compose, NULL);
1077
        if (prefs_common.auto_exteditor)
1078
                compose_exec_ext_editor(compose);
1079
}
1080

    
1081
void compose_redirect(MsgInfo *msginfo, FolderItem *item)
1082
{
1083
        Compose *compose;
1084
        PrefsAccount *account;
1085
        GtkTextView *text;
1086
        GtkTextBuffer *buffer;
1087
        GtkTextMark *mark;
1088
        GtkTextIter iter;
1089
        FILE *fp;
1090
        gchar buf[BUFFSIZE];
1091

    
1092
        g_return_if_fail(msginfo != NULL);
1093
        g_return_if_fail(msginfo->folder != NULL);
1094

    
1095
        account = account_find_from_item(msginfo->folder);
1096
        if (!account) account = cur_account;
1097
        g_return_if_fail(account != NULL);
1098

    
1099
        compose = compose_create(account, COMPOSE_REDIRECT);
1100
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1101

    
1102
        if (compose_parse_header(compose, msginfo) < 0) return;
1103

    
1104
        undo_block(compose->undostruct);
1105

    
1106
        if (msginfo->subject)
1107
                compose_entry_set(compose, msginfo->subject,
1108
                                  COMPOSE_ENTRY_SUBJECT);
1109
        compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
1110

    
1111
        text = GTK_TEXT_VIEW(compose->text);
1112
        buffer = gtk_text_view_get_buffer(text);
1113
        mark = gtk_text_buffer_get_insert(buffer);
1114
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1115

    
1116
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1117
                g_warning(_("Can't get text part\n"));
1118
        else {
1119
                gboolean prev_autowrap = compose->autowrap;
1120

    
1121
                compose->autowrap = FALSE;
1122
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1123
                        strcrchomp(buf);
1124
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1125
                }
1126
                compose->autowrap = prev_autowrap;
1127
                fclose(fp);
1128
        }
1129
        compose_attach_parts(compose, msginfo);
1130

    
1131
        if (account->protocol != A_NNTP)
1132
                gtk_widget_grab_focus(compose->to_entry);
1133
        else
1134
                gtk_widget_grab_focus(compose->newsgroups_entry);
1135

    
1136
        gtk_text_view_set_editable(text, FALSE);
1137

    
1138
        undo_unblock(compose->undostruct);
1139

    
1140
        compose_connect_changed_callbacks(compose);
1141
        compose_set_title(compose);
1142

    
1143
        syl_plugin_signal_emit("compose-created", compose);
1144
}
1145

    
1146
void compose_reedit(MsgInfo *msginfo)
1147
{
1148
        Compose *compose;
1149
        PrefsAccount *account;
1150
        GtkTextView *text;
1151
        GtkTextBuffer *buffer;
1152
        GtkTextMark *mark;
1153
        GtkTextIter iter;
1154
        FILE *fp;
1155
        gchar buf[BUFFSIZE];
1156
        const gchar *str;
1157
        GtkWidget *focus_widget = NULL;
1158
        GtkItemFactory *ifactory;
1159

    
1160
        g_return_if_fail(msginfo != NULL);
1161
        g_return_if_fail(msginfo->folder != NULL);
1162

    
1163
        account = account_find_from_msginfo(msginfo);
1164
        if (!account) account = cur_account;
1165
        g_return_if_fail(account != NULL);
1166

    
1167
        if (msginfo->folder->stype == F_DRAFT ||
1168
            msginfo->folder->stype == F_QUEUE) {
1169
                compose = compose_find_window_by_target(msginfo);
1170
                if (compose) {
1171
                        debug_print
1172
                                ("compose_reedit(): existing window found.\n");
1173
                        gtk_window_present(GTK_WINDOW(compose->window));
1174
                        return;
1175
                }
1176
        }
1177

    
1178
        compose = compose_create(account, COMPOSE_REEDIT);
1179
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1180

    
1181
        if (compose_parse_header(compose, msginfo) < 0) return;
1182
        compose_parse_source_msg(compose, msginfo);
1183

    
1184
        undo_block(compose->undostruct);
1185

    
1186
        compose_reedit_set_entry(compose, msginfo);
1187

    
1188
        text = GTK_TEXT_VIEW(compose->text);
1189
        buffer = gtk_text_view_get_buffer(text);
1190
        mark = gtk_text_buffer_get_insert(buffer);
1191
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1192

    
1193
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1194
                g_warning(_("Can't get text part\n"));
1195
        else {
1196
                gboolean prev_autowrap = compose->autowrap;
1197

    
1198
                compose->autowrap = FALSE;
1199
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1200
                        strcrchomp(buf);
1201
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1202
                }
1203
                compose_enable_sig(compose);
1204
                compose->autowrap = prev_autowrap;
1205
                fclose(fp);
1206
        }
1207
        compose_attach_parts(compose, msginfo);
1208

    
1209
        gtk_text_buffer_get_start_iter(buffer, &iter);
1210
        gtk_text_buffer_place_cursor(buffer, &iter);
1211

    
1212
        if (account->protocol != A_NNTP) {
1213
                str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
1214
                if (!str || *str == '\0')
1215
                        focus_widget = compose->to_entry;
1216
        } else {
1217
                str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
1218
                if (!str || *str == '\0')
1219
                        focus_widget = compose->newsgroups_entry;
1220
        }
1221
        if (!focus_widget) {
1222
                str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
1223
                if (!str || *str == '\0')
1224
                        focus_widget = compose->subject_entry;
1225
        }
1226
        if (focus_widget)
1227
                gtk_widget_grab_focus(focus_widget);
1228
        else
1229
                gtk_widget_grab_focus(compose->text);
1230

    
1231
        undo_unblock(compose->undostruct);
1232

    
1233
        compose_connect_changed_callbacks(compose);
1234
        compose_set_title(compose);
1235

    
1236
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1237
        if (compose->use_mdn) {
1238
                menu_set_active(ifactory, "/Tools/Request disposition notification", TRUE);
1239
        }
1240
        menu_set_active(ifactory, "/Edit/Auto wrapping", compose->autowrap);
1241
#if USE_GTKSPELL
1242
        menu_set_active(ifactory, "/Tools/Check spell", compose->check_spell);
1243
        compose_change_spell_lang_menu(compose, compose->spell_lang);
1244
#endif
1245
#if USE_GPGME
1246
        menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing);
1247
        menu_set_active(ifactory, "/Tools/PGP Encrypt", compose->use_encryption);
1248
#endif
1249

    
1250
        syl_plugin_signal_emit("compose-created", compose);
1251

    
1252
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1253
                compose->autosave_tag =
1254
                        g_timeout_add_full(G_PRIORITY_LOW,
1255
                                           prefs_common.autosave_itv * 60 * 1000,
1256
                                           autosave_timeout, compose, NULL);
1257
        if (prefs_common.auto_exteditor)
1258
                compose_exec_ext_editor(compose);
1259
}
1260

    
1261
GList *compose_get_compose_list(void)
1262
{
1263
        return compose_list;
1264
}
1265

    
1266
static void compose_entry_show(Compose *compose, ComposeEntryType type)
1267
{
1268
        GtkItemFactory *ifactory;
1269

    
1270
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1271

    
1272
        switch (type) {
1273
        case COMPOSE_ENTRY_CC:
1274
                menu_set_active(ifactory, "/View/Cc", TRUE);
1275
                break;
1276
        case COMPOSE_ENTRY_BCC:
1277
                menu_set_active(ifactory, "/View/Bcc", TRUE);
1278
                break;
1279
        case COMPOSE_ENTRY_REPLY_TO:
1280
                menu_set_active(ifactory, "/View/Reply-To", TRUE);
1281
                break;
1282
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1283
                menu_set_active(ifactory, "/View/Followup-To", TRUE);
1284
                break;
1285
        case COMPOSE_ENTRY_TO:
1286
                menu_set_active(ifactory, "/View/To", TRUE);
1287
                break;
1288
        default:
1289
                break;
1290
        }
1291
}
1292

    
1293
static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
1294
{
1295
        GtkEntry *entry;
1296

    
1297
        switch (type) {
1298
        case COMPOSE_ENTRY_CC:
1299
                entry = GTK_ENTRY(compose->cc_entry);
1300
                break;
1301
        case COMPOSE_ENTRY_BCC:
1302
                entry = GTK_ENTRY(compose->bcc_entry);
1303
                break;
1304
        case COMPOSE_ENTRY_REPLY_TO:
1305
                entry = GTK_ENTRY(compose->reply_entry);
1306
                break;
1307
        case COMPOSE_ENTRY_SUBJECT:
1308
                entry = GTK_ENTRY(compose->subject_entry);
1309
                break;
1310
        case COMPOSE_ENTRY_NEWSGROUPS:
1311
                entry = GTK_ENTRY(compose->newsgroups_entry);
1312
                break;
1313
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1314
                entry = GTK_ENTRY(compose->followup_entry);
1315
                break;
1316
        case COMPOSE_ENTRY_TO:
1317
        default:
1318
                entry = GTK_ENTRY(compose->to_entry);
1319
                break;
1320
        }
1321

    
1322
        return entry;
1323
}
1324

    
1325
void compose_entry_set(Compose *compose, const gchar *text,
1326
                       ComposeEntryType type)
1327
{
1328
        GtkEntry *entry;
1329

    
1330
        if (!text) return;
1331

    
1332
        compose_entry_show(compose, type);
1333
        entry = compose_get_entry(compose, type);
1334

    
1335
        gtk_entry_set_text(entry, text);
1336
}
1337

    
1338
void compose_entry_append(Compose *compose, const gchar *text,
1339
                          ComposeEntryType type)
1340
{
1341
        GtkEntry *entry;
1342
        const gchar *str;
1343
        gint pos;
1344

    
1345
        if (!text || *text == '\0') return;
1346

    
1347
        compose_entry_show(compose, type);
1348
        entry = compose_get_entry(compose, type);
1349

    
1350
        if (type != COMPOSE_ENTRY_SUBJECT) {
1351
                str = gtk_entry_get_text(entry);
1352
                if (*str != '\0') {
1353
                        pos = entry->text_length;
1354
                        gtk_editable_insert_text(GTK_EDITABLE(entry),
1355
                                                 ", ", -1, &pos);
1356
                }
1357
        }
1358

    
1359
        pos = entry->text_length;
1360
        gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1361
}
1362

    
1363
gchar *compose_entry_get_text(Compose *compose, ComposeEntryType type)
1364
{
1365
        GtkEntry *entry;
1366

    
1367
        entry = compose_get_entry(compose, type);
1368
        return gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1369
}
1370

    
1371
static void compose_entries_set(Compose *compose, const gchar *mailto)
1372
{
1373
        gchar *to = NULL;
1374
        gchar *cc = NULL;
1375
        gchar *subject = NULL;
1376
        gchar *inreplyto = NULL;
1377
        gchar *body = NULL;
1378

    
1379
        scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body);
1380

    
1381
        if (to)
1382
                compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1383
        if (cc)
1384
                compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1385
        if (subject)
1386
                compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1387
        if (inreplyto) {
1388
                if (strchr(inreplyto, '<'))
1389
                        extract_parenthesis(inreplyto, '<', '>');
1390
                remove_space(inreplyto);
1391
                compose->inreplyto = g_strdup(inreplyto);
1392
        }
1393
        if (body) {
1394
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1395
                GtkTextBuffer *buffer;
1396
                GtkTextMark *mark;
1397
                GtkTextIter iter;
1398
                gboolean prev_autowrap = compose->autowrap;
1399

    
1400
                compose->autowrap = FALSE;
1401

    
1402
                buffer = gtk_text_view_get_buffer(text);
1403
                mark = gtk_text_buffer_get_insert(buffer);
1404
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1405

    
1406
                gtk_text_buffer_insert(buffer, &iter, body, -1);
1407
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1408

    
1409
                compose->autowrap = prev_autowrap;
1410
                if (compose->autowrap)
1411
                        compose_wrap_all(compose);
1412
        }
1413

    
1414
        g_free(to);
1415
        g_free(cc);
1416
        g_free(subject);
1417
        g_free(inreplyto);
1418
        g_free(body);
1419
}
1420

    
1421
static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1422
                                          ComposeMode mode)
1423
{
1424
        g_return_if_fail(item != NULL);
1425

    
1426
        if (item->auto_to) {
1427
                if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1428
                        compose_entry_set(compose, item->auto_to,
1429
                                          COMPOSE_ENTRY_TO);
1430
        }
1431
        if (item->auto_cc)
1432
                compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1433
        if (item->auto_bcc)
1434
                compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1435
        if (item->auto_replyto)
1436
                compose_entry_set(compose, item->auto_replyto,
1437
                                  COMPOSE_ENTRY_REPLY_TO);
1438
}
1439

    
1440
#undef ACTIVATE_MENU
1441

    
1442
static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1443
{
1444
        static HeaderEntry hentry[] = {{"Reply-To:",        NULL, TRUE},
1445
                                       {"Cc:",                NULL, TRUE},
1446
                                       {"References:",        NULL, FALSE},
1447
                                       {"Bcc:",                NULL, TRUE},
1448
                                       {"Newsgroups:",  NULL, TRUE},
1449
                                       {"Followup-To:", NULL, TRUE},
1450
                                       {"List-Post:",   NULL, FALSE},
1451
                                       {"Content-Type:",NULL, FALSE},
1452
                                       {NULL,                NULL, FALSE}};
1453

    
1454
        enum
1455
        {
1456
                H_REPLY_TO        = 0,
1457
                H_CC                = 1,
1458
                H_REFERENCES        = 2,
1459
                H_BCC                = 3,
1460
                H_NEWSGROUPS    = 4,
1461
                H_FOLLOWUP_TO        = 5,
1462
                H_LIST_POST     = 6,
1463
                H_CONTENT_TYPE  = 7
1464
        };
1465

    
1466
        FILE *fp;
1467
        gchar *charset = NULL;
1468

    
1469
        g_return_val_if_fail(msginfo != NULL, -1);
1470

    
1471
        if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1472
        procheader_get_header_fields(fp, hentry);
1473
        fclose(fp);
1474

    
1475
        if (hentry[H_CONTENT_TYPE].body != NULL) {
1476
                procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body,
1477
                                               NULL, &charset, NULL, NULL);
1478
                g_free(hentry[H_CONTENT_TYPE].body);
1479
                hentry[H_CONTENT_TYPE].body = NULL;
1480
        }
1481
        if (hentry[H_REPLY_TO].body != NULL) {
1482
                if (hentry[H_REPLY_TO].body[0] != '\0') {
1483
                        compose->replyto =
1484
                                conv_unmime_header(hentry[H_REPLY_TO].body,
1485
                                                   charset);
1486
                }
1487
                g_free(hentry[H_REPLY_TO].body);
1488
                hentry[H_REPLY_TO].body = NULL;
1489
        }
1490
        if (hentry[H_CC].body != NULL) {
1491
                compose->cc = conv_unmime_header(hentry[H_CC].body, charset);
1492
                g_free(hentry[H_CC].body);
1493
                hentry[H_CC].body = NULL;
1494
        }
1495
        if (hentry[H_REFERENCES].body != NULL) {
1496
                if (compose->mode == COMPOSE_REEDIT)
1497
                        compose->references = hentry[H_REFERENCES].body;
1498
                else {
1499
                        compose->references = compose_parse_references
1500
                                (hentry[H_REFERENCES].body, msginfo->msgid);
1501
                        g_free(hentry[H_REFERENCES].body);
1502
                }
1503
                hentry[H_REFERENCES].body = NULL;
1504
        }
1505
        if (hentry[H_BCC].body != NULL) {
1506
                if (compose->mode == COMPOSE_REEDIT)
1507
                        compose->bcc =
1508
                                conv_unmime_header(hentry[H_BCC].body, charset);
1509
                g_free(hentry[H_BCC].body);
1510
                hentry[H_BCC].body = NULL;
1511
        }
1512
        if (hentry[H_NEWSGROUPS].body != NULL) {
1513
                compose->newsgroups = hentry[H_NEWSGROUPS].body;
1514
                hentry[H_NEWSGROUPS].body = NULL;
1515
        }
1516
        if (hentry[H_FOLLOWUP_TO].body != NULL) {
1517
                if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1518
                        compose->followup_to =
1519
                                conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1520
                                                   charset);
1521
                }
1522
                g_free(hentry[H_FOLLOWUP_TO].body);
1523
                hentry[H_FOLLOWUP_TO].body = NULL;
1524
        }
1525
        if (hentry[H_LIST_POST].body != NULL) {
1526
                gchar *to = NULL;
1527

    
1528
                extract_address(hentry[H_LIST_POST].body);
1529
                if (hentry[H_LIST_POST].body[0] != '\0') {
1530
                        scan_mailto_url(hentry[H_LIST_POST].body,
1531
                                        &to, NULL, NULL, NULL, NULL, NULL);
1532
                        if (to) {
1533
                                g_free(compose->ml_post);
1534
                                compose->ml_post = to;
1535
                        }
1536
                }
1537
                g_free(hentry[H_LIST_POST].body);
1538
                hentry[H_LIST_POST].body = NULL;
1539
        }
1540

    
1541
        g_free(charset);
1542

    
1543
        if (compose->mode == COMPOSE_REEDIT) {
1544
                if (msginfo->inreplyto && *msginfo->inreplyto)
1545
                        compose->inreplyto = g_strdup(msginfo->inreplyto);
1546
                return 0;
1547
        }
1548

    
1549
        if (msginfo->msgid && *msginfo->msgid)
1550
                compose->inreplyto = g_strdup(msginfo->msgid);
1551

    
1552
        if (!compose->references) {
1553
                if (msginfo->msgid && *msginfo->msgid) {
1554
                        if (msginfo->inreplyto && *msginfo->inreplyto)
1555
                                compose->references =
1556
                                        g_strdup_printf("<%s>\n\t<%s>",
1557
                                                        msginfo->inreplyto,
1558
                                                        msginfo->msgid);
1559
                        else
1560
                                compose->references =
1561
                                        g_strconcat("<", msginfo->msgid, ">",
1562
                                                    NULL);
1563
                } else if (msginfo->inreplyto && *msginfo->inreplyto) {
1564
                        compose->references =
1565
                                g_strconcat("<", msginfo->inreplyto, ">",
1566
                                            NULL);
1567
                }
1568
        }
1569

    
1570
        return 0;
1571
}
1572

    
1573
static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1574
{
1575
        GSList *ref_id_list, *cur;
1576
        GString *new_ref;
1577
        gchar *new_ref_str;
1578

    
1579
        ref_id_list = references_list_append(NULL, ref);
1580
        if (!ref_id_list) return NULL;
1581
        if (msgid && *msgid)
1582
                ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1583

    
1584
        for (;;) {
1585
                gint len = 0;
1586

    
1587
                for (cur = ref_id_list; cur != NULL; cur = cur->next)
1588
                        /* "<" + Message-ID + ">" + CR+LF+TAB */
1589
                        len += strlen((gchar *)cur->data) + 5;
1590

    
1591
                if (len > MAX_REFERENCES_LEN) {
1592
                        /* remove second message-ID */
1593
                        if (ref_id_list && ref_id_list->next &&
1594
                            ref_id_list->next->next) {
1595
                                g_free(ref_id_list->next->data);
1596
                                ref_id_list = g_slist_remove
1597
                                        (ref_id_list, ref_id_list->next->data);
1598
                        } else {
1599
                                slist_free_strings(ref_id_list);
1600
                                g_slist_free(ref_id_list);
1601
                                return NULL;
1602
                        }
1603
                } else
1604
                        break;
1605
        }
1606

    
1607
        new_ref = g_string_new("");
1608
        for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1609
                if (new_ref->len > 0)
1610
                        g_string_append(new_ref, "\n\t");
1611
                g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1612
        }
1613

    
1614
        slist_free_strings(ref_id_list);
1615
        g_slist_free(ref_id_list);
1616

    
1617
        new_ref_str = new_ref->str;
1618
        g_string_free(new_ref, FALSE);
1619

    
1620
        return new_ref_str;
1621
}
1622

    
1623
static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo)
1624
{
1625
        static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE},
1626
                                       {"X-Sylpheed-Forward:", NULL, FALSE},
1627
                                       {"REP:", NULL, FALSE},
1628
                                       {"FWD:", NULL, FALSE},
1629
                                       {"Disposition-Notification-To:", NULL, FALSE},
1630
                                       {"X-Sylpheed-Compose-AutoWrap:", NULL, FALSE},
1631
                                       {"X-Sylpheed-Compose-CheckSpell:", NULL, FALSE},
1632
                                       {"X-Sylpheed-Compose-SpellLang:", NULL, FALSE},
1633
                                       {"X-Sylpheed-Compose-UseSigning:", NULL, FALSE},
1634
                                       {"X-Sylpheed-Compose-UseEncryption:", NULL, FALSE},
1635
                                       {NULL, NULL, FALSE}};
1636

    
1637
        enum
1638
        {
1639
                H_X_SYLPHEED_REPLY = 0,
1640
                H_X_SYLPHEED_FORWARD = 1,
1641
                H_REP = 2,
1642
                H_FWD = 3,
1643
                H_MDN = 4,
1644
                H_X_SYLPHEED_COMPOSE_AUTOWRAP = 5,
1645
                H_X_SYLPHEED_COMPOSE_CHECKSPELL = 6,
1646
                H_X_SYLPHEED_COMPOSE_SPELLLANG = 7,
1647
                H_X_SYLPHEED_COMPOSE_USESIGNING = 8,
1648
                H_X_SYLPHEED_COMPOSE_USEENCRYPTION = 9
1649
        };
1650

    
1651
        gchar *file;
1652
        FILE *fp;
1653
        gchar *str;
1654
        gchar buf[BUFFSIZE];
1655
        gint hnum;
1656

    
1657
        g_return_val_if_fail(msginfo != NULL, -1);
1658

    
1659
        file = procmsg_get_message_file(msginfo);
1660

    
1661
        if ((fp = g_fopen(file, "rb")) == NULL) {
1662
                FILE_OP_ERROR(file, "fopen");
1663
                return -1;
1664
        }
1665

    
1666
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
1667
               != -1) {
1668
                str = buf + strlen(hentry[hnum].name);
1669
                while (g_ascii_isspace(*str))
1670
                        ++str;
1671
                if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) &&
1672
                    !compose->reply_target) {
1673
                        compose->reply_target = g_strdup(str);
1674
                } else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) &&
1675
                           !compose->forward_targets) {
1676
                        compose->forward_targets = g_strdup(str);
1677
                } else if (hnum == H_MDN) {
1678
                        compose->use_mdn = TRUE;
1679
                } else if (hnum == H_X_SYLPHEED_COMPOSE_AUTOWRAP) {
1680
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1681
                                compose->autowrap = TRUE;
1682
                        else
1683
                                compose->autowrap = FALSE;
1684
#if USE_GTKSPELL
1685
                } else if (hnum == H_X_SYLPHEED_COMPOSE_CHECKSPELL) {
1686
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1687
                                compose->check_spell = TRUE;
1688
                        else
1689
                                compose->check_spell = FALSE;
1690
                } else if (hnum == H_X_SYLPHEED_COMPOSE_SPELLLANG) {
1691
                        g_free(compose->spell_lang);
1692
                        compose->spell_lang = g_strdup(str);
1693
#endif
1694
#if USE_GPGME
1695
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USESIGNING) {
1696
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1697
                                compose->use_signing = TRUE;
1698
                        else
1699
                                compose->use_signing = FALSE;
1700
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USEENCRYPTION) {
1701
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1702
                                compose->use_encryption = TRUE;
1703
                        else
1704
                                compose->use_encryption = FALSE;
1705
#endif
1706
                }
1707
        }
1708

    
1709
        fclose(fp);
1710
        g_free(file);
1711

    
1712
        return 0;
1713
}
1714

    
1715
static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1716
                                const gchar *fmt, const gchar *qmark,
1717
                                const gchar *body)
1718
{
1719
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1720
        GtkTextBuffer *buffer;
1721
        GtkTextMark *mark;
1722
        GtkTextIter iter;
1723
        static MsgInfo dummyinfo;
1724
        gchar *quote_str = NULL;
1725
        gchar *buf;
1726
        gchar *p, *lastp;
1727
        gint len;
1728
        gboolean prev_autowrap;
1729

    
1730
        if (!msginfo)
1731
                msginfo = &dummyinfo;
1732

    
1733
        if (qmark == NULL || *qmark == '\0')
1734
                qmark = "> ";
1735

    
1736
        quote_fmt_init(msginfo, NULL, NULL);
1737
        quote_fmt_scan_string(qmark);
1738
        quote_fmt_parse();
1739

    
1740
        buf = quote_fmt_get_buffer();
1741
        if (buf == NULL)
1742
                alertpanel_error(_("Quote mark format error."));
1743
        else
1744
                quote_str = g_strdup(buf);
1745

    
1746
        if (fmt && *fmt != '\0') {
1747
                quote_fmt_init(msginfo, quote_str, body);
1748
                quote_fmt_scan_string(fmt);
1749
                quote_fmt_parse();
1750

    
1751
                buf = quote_fmt_get_buffer();
1752
                if (buf == NULL) {
1753
                        alertpanel_error(_("Message reply/forward format error."));
1754
                        g_free(quote_str);
1755
                        return NULL;
1756
                }
1757
        } else
1758
                buf = "";
1759

    
1760
        g_free(quote_str);
1761

    
1762
        prev_autowrap = compose->autowrap;
1763
        compose->autowrap = FALSE;
1764

    
1765
        buffer = gtk_text_view_get_buffer(text);
1766
        mark = gtk_text_buffer_get_insert(buffer);
1767
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1768

    
1769
        for (p = buf; *p != '\0'; ) {
1770
                lastp = strchr(p, '\n');
1771
                len = lastp ? lastp - p + 1 : -1;
1772
                gtk_text_buffer_insert(buffer, &iter, p, len);
1773
                if (lastp)
1774
                        p = lastp + 1;
1775
                else
1776
                        break;
1777
        }
1778

    
1779
        compose->autowrap = prev_autowrap;
1780
        if (compose->autowrap)
1781
                compose_wrap_all(compose);
1782

    
1783
        return buf;
1784
}
1785

    
1786
static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1787
                                    ComposeMode mode)
1788
{
1789
        GSList *cc_list = NULL;
1790
        GSList *cur;
1791
        GHashTable *to_table;
1792
        gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1793
        gchar *from_str = NULL, *to_str = NULL, *cc_str = NULL,
1794
              *replyto_str = NULL;
1795
        gboolean address_only = prefs_common.reply_address_only;
1796

    
1797
        g_return_if_fail(compose->account != NULL);
1798
        g_return_if_fail(msginfo != NULL);
1799

    
1800
        switch (COMPOSE_MODE(mode)) {
1801
        case COMPOSE_REPLY_TO_SENDER:
1802
                ignore_replyto = TRUE;
1803
                break;
1804
        case COMPOSE_REPLY_TO_ALL:
1805
                to_all = TRUE;
1806
                break;
1807
        case COMPOSE_REPLY_TO_LIST:
1808
                to_ml = TRUE;
1809
                break;
1810
        default:
1811
                break;
1812
        }
1813

    
1814
        if (address_only) {
1815
                from_str = extract_addresses(msginfo->from);
1816
                to_str = extract_addresses(msginfo->to);
1817
                cc_str = extract_addresses(compose->cc);
1818
                replyto_str = extract_addresses(compose->replyto);
1819
        } else {
1820
                from_str = g_strdup(msginfo->from);
1821
                to_str = g_strdup(msginfo->to);
1822
                cc_str = g_strdup(compose->cc);
1823
                replyto_str = g_strdup(compose->replyto);
1824
        }
1825

    
1826
        if (compose->account->protocol != A_NNTP) {
1827
                if (to_ml && compose->ml_post) {
1828
                        /* don't reply to list for confirmation request etc. */
1829
                        if ((!to_str ||
1830
                             !strcasestr_with_skip_quote(to_str,
1831
                                                         compose->ml_post)) &&
1832
                            (!cc_str ||
1833
                             !strcasestr_with_skip_quote(cc_str,
1834
                                                         compose->ml_post)))
1835
                                to_ml = FALSE;
1836
                }
1837
                if (to_ml && compose->ml_post) {
1838
                        compose_entry_set(compose, compose->ml_post,
1839
                                          COMPOSE_ENTRY_TO);
1840
                        if (replyto_str &&
1841
                            !address_equal(replyto_str, compose->ml_post))
1842
                                compose_entry_set(compose, replyto_str,
1843
                                                  COMPOSE_ENTRY_CC);
1844
                } else if (prefs_common.inherit_recipient_on_self_reply &&
1845
                           address_equal(compose->account->address, from_str)) {
1846
                        compose_entry_set(compose, to_str, COMPOSE_ENTRY_TO);
1847
                        if (to_all) {
1848
                                compose_entry_set(compose, cc_str,
1849
                                                  COMPOSE_ENTRY_CC);
1850
                                to_all = FALSE;
1851
                        }
1852
                } else {
1853
                        compose_entry_set(compose, 
1854
                                          (replyto_str && !ignore_replyto)
1855
                                          ? replyto_str
1856
                                          : from_str ? from_str : "",
1857
                                          COMPOSE_ENTRY_TO);
1858
                }
1859
        } else {
1860
                if (ignore_replyto) {
1861
                        compose_entry_set(compose, from_str ? from_str : "",
1862
                                          COMPOSE_ENTRY_TO);
1863
                } else {
1864
                        if (to_all) {
1865
                                compose_entry_set
1866
                                        (compose, 
1867
                                         (replyto_str && !ignore_replyto)
1868
                                         ? replyto_str
1869
                                         : from_str ? from_str : "",
1870
                                         COMPOSE_ENTRY_TO);
1871
                        }
1872
                        compose_entry_set(compose,
1873
                                          compose->followup_to ? compose->followup_to
1874
                                          : compose->newsgroups ? compose->newsgroups
1875
                                          : "",
1876
                                          COMPOSE_ENTRY_NEWSGROUPS);
1877
                }
1878
        }
1879

    
1880
        if (msginfo->subject && *msginfo->subject) {
1881
                gchar *buf;
1882
                gchar *p;
1883

    
1884
                buf = g_strdup(msginfo->subject);
1885

    
1886
                if (msginfo->folder && msginfo->folder->trim_compose_subject)
1887
                        trim_subject(buf);
1888

    
1889
                while (!g_ascii_strncasecmp(buf, "Re:", 3)) {
1890
                        p = buf + 3;
1891
                        while (g_ascii_isspace(*p)) p++;
1892
                        memmove(buf, p, strlen(p) + 1);
1893
                }
1894

    
1895
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1896
                compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1897

    
1898
                g_free(buf);
1899
        } else
1900
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1901

    
1902
        if (!compose->replyto && to_ml && compose->ml_post)
1903
                goto done;
1904
        if (!to_all)
1905
                goto done;
1906

    
1907
        if (replyto_str && from_str)
1908
                cc_list = address_list_append_orig(cc_list, from_str);
1909
        cc_list = address_list_append_orig(cc_list, to_str);
1910
        cc_list = address_list_append_orig(cc_list, cc_str);
1911

    
1912
        to_table = g_hash_table_new(g_str_hash, g_str_equal);
1913
        if (replyto_str) {
1914
                gchar *replyto;
1915

    
1916
                replyto = g_strdup(replyto_str);
1917
                extract_address(replyto);
1918
                g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1919
        } else if (from_str) {
1920
                gchar *from;
1921

    
1922
                from = g_strdup(from_str);
1923
                extract_address(from);
1924
                g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1925
        }
1926
        if (compose->account->address)
1927
                g_hash_table_insert(to_table,
1928
                                    g_strdup(compose->account->address),
1929
                                    GINT_TO_POINTER(1));
1930

    
1931
        /* remove duplicate addresses */
1932
        for (cur = cc_list; cur != NULL; ) {
1933
                gchar *addr = (gchar *)cur->data;
1934
                GSList *next = cur->next;
1935
                gchar *addr_;
1936

    
1937
                addr_ = g_strdup(addr);
1938
                extract_address(addr_);
1939
                if (g_hash_table_lookup(to_table, addr_) != NULL) {
1940
                        cc_list = g_slist_remove(cc_list, addr);
1941
                        g_free(addr_);
1942
                        g_free(addr);
1943
                } else
1944
                        g_hash_table_insert(to_table, addr_, cur);
1945

    
1946
                cur = next;
1947
        }
1948
        hash_free_strings(to_table);
1949
        g_hash_table_destroy(to_table);
1950

    
1951
        if (cc_list) {
1952
                for (cur = cc_list; cur != NULL; cur = cur->next)
1953
                        compose_entry_append(compose, (gchar *)cur->data,
1954
                                             COMPOSE_ENTRY_CC);
1955
                slist_free_strings(cc_list);
1956
                g_slist_free(cc_list);
1957
        }
1958

    
1959
done:
1960
        g_free(from_str);
1961
        g_free(to_str);
1962
        g_free(cc_str);
1963
        g_free(replyto_str);
1964
}
1965

    
1966
static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1967
{
1968
        g_return_if_fail(msginfo != NULL);
1969
        g_return_if_fail(compose->account != NULL);
1970

    
1971
        compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1972
        compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1973
        compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1974
        compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1975
        if (compose->account->protocol == A_NNTP) {
1976
                compose_entry_set(compose, compose->newsgroups,
1977
                                  COMPOSE_ENTRY_NEWSGROUPS);
1978
                compose_entry_set(compose, compose->followup_to,
1979
                                  COMPOSE_ENTRY_FOLLOWUP_TO);
1980
        }
1981
        compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1982
}
1983

    
1984
static void compose_insert_sig(Compose *compose, gboolean append,
1985
                               gboolean replace, gboolean scroll)
1986
{
1987
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1988
        GtkTextBuffer *buffer;
1989
        GtkTextMark *mark;
1990
        GtkTextIter iter;
1991
        gchar *sig_str;
1992
        gboolean prev_autowrap;
1993

    
1994
        g_return_if_fail(compose->account != NULL);
1995

    
1996
        prev_autowrap = compose->autowrap;
1997
        compose->autowrap = FALSE;
1998

    
1999
        buffer = gtk_text_view_get_buffer(text);
2000
        mark = gtk_text_buffer_get_insert(buffer);
2001
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2002

    
2003
        if (replace) {
2004
                GtkTextIter start_iter, end_iter;
2005

    
2006
                gtk_text_buffer_get_start_iter(buffer, &start_iter);
2007
                gtk_text_buffer_get_end_iter(buffer, &iter);
2008

    
2009
                while (gtk_text_iter_begins_tag
2010
                        (&start_iter, compose->sig_tag) ||
2011
                       gtk_text_iter_forward_to_tag_toggle
2012
                        (&start_iter, compose->sig_tag)) {
2013
                        end_iter = start_iter;
2014
                        if (gtk_text_iter_forward_to_tag_toggle
2015
                                (&end_iter, compose->sig_tag)) {
2016
                                gtk_text_buffer_delete
2017
                                        (buffer, &start_iter, &end_iter);
2018
                                iter = start_iter;
2019
                        }
2020
                }
2021
        }
2022

    
2023
        if (scroll) {
2024
                if (append)
2025
                        gtk_text_buffer_get_end_iter(buffer, &iter);
2026
                else
2027
                        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2028
        }
2029

    
2030
        sig_str = compose_get_signature_str(compose);
2031
        if (sig_str) {
2032
                if (!replace)
2033
                        gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
2034
                else if (!gtk_text_iter_starts_line(&iter))
2035
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2036
                gtk_text_buffer_insert_with_tags
2037
                        (buffer, &iter, sig_str, -1, compose->sig_tag, NULL);
2038
                g_free(sig_str);
2039
                if (scroll)
2040
                        gtk_text_buffer_place_cursor(buffer, &iter);
2041
        }
2042

    
2043
        compose->autowrap = prev_autowrap;
2044
        if (compose->autowrap)
2045
                compose_wrap_all(compose);
2046

    
2047
        if (scroll)
2048
                gtk_text_view_scroll_mark_onscreen(text, mark);
2049
}
2050

    
2051
static void compose_enable_sig(Compose *compose)
2052
{
2053
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2054
        GtkTextBuffer *buffer;
2055
        GtkTextIter iter, start, end;
2056
        gchar *sig_str;
2057

    
2058
        g_return_if_fail(compose->account != NULL);
2059

    
2060
        buffer = gtk_text_view_get_buffer(text);
2061
        gtk_text_buffer_get_start_iter(buffer, &iter);
2062

    
2063
        sig_str = compose_get_signature_str(compose);
2064
        if (!sig_str)
2065
                return;
2066

    
2067
        if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) {
2068
                end = start;
2069
                gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1));
2070
                gtk_text_buffer_apply_tag(buffer, compose->sig_tag,
2071
                                          &start, &end);
2072
        }
2073

    
2074
        g_free(sig_str);
2075
}
2076

    
2077
static gchar *compose_get_signature_str(Compose *compose)
2078
{
2079
        gchar *sig_path;
2080
        gchar *sig_body = NULL;
2081
        gchar *utf8_sig_body = NULL;
2082
        gchar *utf8_sig_str = NULL;
2083

    
2084
        g_return_val_if_fail(compose->account != NULL, NULL);
2085

    
2086
        if (compose->account->sig_type == SIG_DIRECT) {
2087
                gchar *p, *sp;
2088

    
2089
                if (!compose->account->sig_text)
2090
                        return NULL;
2091

    
2092
                sp = compose->account->sig_text;
2093
                p = sig_body = g_malloc(strlen(compose->account->sig_text) + 1);
2094
                while (*sp) {
2095
                        if (*sp == '\\' && *(sp + 1) == 'n') {
2096
                                *p++ = '\n';
2097
                                sp += 2;
2098
                        } else
2099
                                *p++ = *sp++;
2100
                }
2101
                *p = '\0';
2102

    
2103
                if (prefs_common.sig_sep) {
2104
                        utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2105
                                                   sig_body, NULL);
2106
                        g_free(sig_body);
2107
                } else
2108
                        utf8_sig_str = sig_body;
2109

    
2110
                return utf8_sig_str;
2111
        }
2112

    
2113
        if (!compose->account->sig_path)
2114
                return NULL;
2115

    
2116
        if (g_path_is_absolute(compose->account->sig_path) ||
2117
            compose->account->sig_type == SIG_COMMAND)
2118
                sig_path = g_strdup(compose->account->sig_path);
2119
        else {
2120
#ifdef G_OS_WIN32
2121
                sig_path = g_strconcat(get_rc_dir(),
2122
#else
2123
                sig_path = g_strconcat(get_home_dir(),
2124
#endif
2125
                                       G_DIR_SEPARATOR_S,
2126
                                       compose->account->sig_path, NULL);
2127
        }
2128

    
2129
        if (compose->account->sig_type == SIG_FILE) {
2130
                if (!is_file_or_fifo_exist(sig_path)) {
2131
                        debug_print("can't open signature file: %s\n",
2132
                                    sig_path);
2133
                        g_free(sig_path);
2134
                        return NULL;
2135
                }
2136
        }
2137

    
2138
        if (compose->account->sig_type == SIG_COMMAND)
2139
                sig_body = get_command_output(sig_path);
2140
        else {
2141
                gchar *tmp;
2142

    
2143
                tmp = file_read_to_str(sig_path);
2144
                if (!tmp)
2145
                        return NULL;
2146
                sig_body = normalize_newlines(tmp);
2147
                g_free(tmp);
2148
        }
2149
        g_free(sig_path);
2150

    
2151
        if (sig_body) {
2152
                gint error = 0;
2153

    
2154
                utf8_sig_body = conv_codeset_strdup_full
2155
                        (sig_body, conv_get_locale_charset_str(),
2156
                         CS_INTERNAL, &error);
2157
                if (!utf8_sig_body || error != 0) {
2158
                        if (g_utf8_validate(sig_body, -1, NULL) == TRUE) {
2159
                                g_free(utf8_sig_body);
2160
                                utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2161
                        }
2162
                } else {
2163
                        g_free(sig_body);
2164
                        sig_body = utf8_sig_body;
2165
                        utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2166
                }
2167
                g_free(sig_body);
2168
        }
2169

    
2170
        if (prefs_common.sig_sep) {
2171
                utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2172
                                           utf8_sig_body, NULL);
2173
                g_free(utf8_sig_body);
2174
        } else
2175
                utf8_sig_str = utf8_sig_body;
2176

    
2177
        return utf8_sig_str;
2178
}
2179

    
2180
static void compose_insert_file(Compose *compose, const gchar *file,
2181
                                gboolean scroll)
2182
{
2183
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2184
        GtkTextBuffer *buffer;
2185
        GtkTextMark *mark;
2186
        GtkTextIter iter;
2187
        const gchar *cur_encoding;
2188
        gchar buf[BUFFSIZE];
2189
        gint len;
2190
        FILE *fp;
2191
        gboolean prev_autowrap;
2192
        CharSet enc;
2193

    
2194
        g_return_if_fail(file != NULL);
2195

    
2196
        enc = conv_check_file_encoding(file);
2197

    
2198
        if ((fp = g_fopen(file, "rb")) == NULL) {
2199
                FILE_OP_ERROR(file, "fopen");
2200
                return;
2201
        }
2202

    
2203
        prev_autowrap = compose->autowrap;
2204
        compose->autowrap = FALSE;
2205

    
2206
        buffer = gtk_text_view_get_buffer(text);
2207
        mark = gtk_text_buffer_get_insert(buffer);
2208
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2209

    
2210
        cur_encoding = conv_get_locale_charset_str();
2211

    
2212
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2213
                gchar *str;
2214
                gint error = 0;
2215

    
2216
                if (enc == C_UTF_8) {
2217
                        str = conv_utf8todisp(buf, NULL);
2218
                } else {
2219
                        str = conv_codeset_strdup_full(buf, cur_encoding,
2220
                                                       CS_INTERNAL, &error);
2221
                        if (!str || error != 0) {
2222
                                if (g_utf8_validate(buf, -1, NULL) == TRUE) {
2223
                                        g_free(str);
2224
                                        str = g_strdup(buf);
2225
                                }
2226
                        }
2227
                        if (!str) continue;
2228
                }
2229

    
2230
                /* strip <CR> if DOS/Windows file,
2231
                   replace <CR> with <LF> if Macintosh file. */
2232
                strcrchomp(str);
2233
                len = strlen(str);
2234
                if (len > 0 && str[len - 1] != '\n') {
2235
                        while (--len >= 0)
2236
                                if (str[len] == '\r') str[len] = '\n';
2237
                }
2238

    
2239
                gtk_text_buffer_insert(buffer, &iter, str, -1);
2240
                g_free(str);
2241
        }
2242

    
2243
        fclose(fp);
2244

    
2245
        compose->autowrap = prev_autowrap;
2246
        if (compose->autowrap)
2247
                compose_wrap_all(compose);
2248

    
2249
        if (scroll)
2250
                gtk_text_view_scroll_mark_onscreen(text, mark);
2251
}
2252

    
2253
static void compose_attach_append(Compose *compose, const gchar *file,
2254
                                  const gchar *filename,
2255
                                  const gchar *content_type)
2256
{
2257
        GtkTreeIter iter;
2258
        AttachInfo *ainfo;
2259
        FILE *fp;
2260
        off_t size;
2261

    
2262
        g_return_if_fail(file != NULL);
2263
        g_return_if_fail(*file != '\0');
2264

    
2265
        if (!is_file_exist(file)) {
2266
                g_warning(_("File %s doesn't exist\n"), file);
2267
                return;
2268
        }
2269
        if ((size = get_file_size(file)) < 0) {
2270
                g_warning(_("Can't get file size of %s\n"), file);
2271
                return;
2272
        }
2273
        if (size == 0) {
2274
                manage_window_focus_in(compose->window, NULL, NULL);
2275
                alertpanel_notice(_("File %s is empty."), file);
2276
                return;
2277
        }
2278
        if ((fp = g_fopen(file, "rb")) == NULL) {
2279
                manage_window_focus_in(compose->window, NULL, NULL);
2280
                alertpanel_error(_("Can't read %s."), file);
2281
                return;
2282
        }
2283
        fclose(fp);
2284

    
2285
        if (!compose->use_attach) {
2286
                GtkItemFactory *ifactory;
2287

    
2288
                ifactory = gtk_item_factory_from_widget(compose->menubar);
2289
                menu_set_active(ifactory, "/View/Attachment", TRUE);
2290
        }
2291

    
2292
        ainfo = g_new0(AttachInfo, 1);
2293
        ainfo->file = g_strdup(file);
2294

    
2295
        if (content_type) {
2296
                ainfo->content_type = g_strdup(content_type);
2297
                if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2298
                        MsgInfo *msginfo;
2299
                        MsgFlags flags = {0, 0};
2300
                        const gchar *name;
2301

    
2302
                        if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2303
                                ainfo->encoding = ENC_7BIT;
2304
                        else
2305
                                ainfo->encoding = ENC_8BIT;
2306

    
2307
                        msginfo = procheader_parse_file(file, flags, FALSE);
2308
                        if (msginfo && msginfo->subject)
2309
                                name = msginfo->subject;
2310
                        else
2311
                                name = g_basename(filename ? filename : file);
2312

    
2313
                        ainfo->name = g_strdup_printf(_("Message: %s"), name);
2314

    
2315
                        procmsg_msginfo_free(msginfo);
2316
                } else {
2317
                        if (!g_ascii_strncasecmp(content_type, "text", 4))
2318
                                ainfo->encoding = procmime_get_encoding_for_text_file(file);
2319
                        else
2320
                                ainfo->encoding = ENC_BASE64;
2321
                        ainfo->name = g_strdup
2322
                                (g_basename(filename ? filename : file));
2323
                }
2324
        } else {
2325
                ainfo->content_type = procmime_get_mime_type(file);
2326
                if (!ainfo->content_type) {
2327
                        ainfo->content_type =
2328
                                g_strdup("application/octet-stream");
2329
                        ainfo->encoding = ENC_BASE64;
2330
                } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
2331
                        ainfo->encoding =
2332
                                procmime_get_encoding_for_text_file(file);
2333
                else
2334
                        ainfo->encoding = ENC_BASE64;
2335
                ainfo->name = g_strdup(g_basename(filename ? filename : file));        
2336
        }
2337
        ainfo->size = size;
2338

    
2339
        gtk_list_store_append(compose->attach_store, &iter);
2340
        gtk_list_store_set(compose->attach_store, &iter,
2341
                           COL_MIMETYPE, ainfo->content_type,
2342
                           COL_SIZE, to_human_readable(ainfo->size),
2343
                           COL_NAME, ainfo->name,
2344
                           COL_ATTACH_INFO, ainfo,
2345
                           -1);
2346
}
2347

    
2348
static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2349
{
2350
        MimeInfo *mimeinfo;
2351
        MimeInfo *child;
2352
        gchar *infile;
2353
        gchar *outfile;
2354

    
2355
        mimeinfo = procmime_scan_message(msginfo);
2356
        if (!mimeinfo) return;
2357

    
2358
        infile = procmsg_get_message_file_path(msginfo);
2359

    
2360
        child = mimeinfo;
2361
        while (child != NULL) {
2362
                if (child->children || child->mime_type == MIME_MULTIPART)
2363
                        goto next;
2364
                if (child->mime_type != MIME_MESSAGE_RFC822 &&
2365
                    child->mime_type != MIME_IMAGE &&
2366
                    child->mime_type != MIME_AUDIO &&
2367
                    child->mime_type != MIME_VIDEO &&
2368
                    !child->filename && !child->name)
2369
                        goto next;
2370

    
2371
                outfile = procmime_get_tmp_file_name(child);
2372
                if (procmime_get_part(outfile, infile, child) < 0) {
2373
                        g_warning(_("Can't get the part of multipart message."));
2374
                        g_free(outfile);
2375
                        goto next;
2376
                }
2377

    
2378
                compose_attach_append
2379
                        (compose, outfile,
2380
                         child->filename ? child->filename : child->name,
2381
                         child->content_type);
2382

    
2383
                g_free(outfile);
2384

    
2385
next:
2386
                if (child->mime_type == MIME_MESSAGE_RFC822)
2387
                        child = child->next;
2388
                else
2389
                        child = procmime_mimeinfo_next(child);
2390
        }
2391

    
2392
        g_free(infile);
2393
        procmime_mimeinfo_free_all(mimeinfo);
2394
}
2395

    
2396
#define INDENT_CHARS        ">|#"
2397

    
2398
typedef enum {
2399
        WAIT_FOR_INDENT_CHAR,
2400
        WAIT_FOR_INDENT_CHAR_OR_SPACE,
2401
} IndentState;
2402

    
2403
/* return indent length, we allow:
2404
   indent characters followed by indent characters or spaces/tabs,
2405
   alphabets and numbers immediately followed by indent characters,
2406
   and the repeating sequences of the above
2407
   If quote ends with multiple spaces, only the first one is included. */
2408
static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
2409
                                    const GtkTextIter *start, gint *len)
2410
{
2411
        GtkTextIter iter = *start;
2412
        gunichar wc;
2413
        gchar ch[6];
2414
        gint clen;
2415
        IndentState state = WAIT_FOR_INDENT_CHAR;
2416
        gboolean is_space;
2417
        gboolean is_indent;
2418
        gint alnum_count = 0;
2419
        gint space_count = 0;
2420
        gint quote_len = 0;
2421

    
2422
        while (!gtk_text_iter_ends_line(&iter)) {
2423
                wc = gtk_text_iter_get_char(&iter);
2424
                if (g_unichar_iswide(wc))
2425
                        break;
2426
                clen = g_unichar_to_utf8(wc, ch);
2427
                if (clen != 1)
2428
                        break;
2429

    
2430
                is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE;
2431
                is_space = g_unichar_isspace(wc);
2432

    
2433
                if (state == WAIT_FOR_INDENT_CHAR) {
2434
                        if (!is_indent && !g_unichar_isalnum(wc))
2435
                                break;
2436
                        if (is_indent) {
2437
                                quote_len += alnum_count + space_count + 1;
2438
                                alnum_count = space_count = 0;
2439
                                state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2440
                        } else
2441
                                alnum_count++;
2442
                } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2443
                        if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2444
                                break;
2445
                        if (is_space)
2446
                                space_count++;
2447
                        else if (is_indent) {
2448
                                quote_len += alnum_count + space_count + 1;
2449
                                alnum_count = space_count = 0;
2450
                        } else {
2451
                                alnum_count++;
2452
                                state = WAIT_FOR_INDENT_CHAR;
2453
                        }
2454
                }
2455

    
2456
                gtk_text_iter_forward_char(&iter);
2457
        }
2458

    
2459
        if (quote_len > 0 && space_count > 0)
2460
                quote_len++;
2461

    
2462
        if (len)
2463
                *len = quote_len;
2464

    
2465
        if (quote_len > 0) {
2466
                iter = *start;
2467
                gtk_text_iter_forward_chars(&iter, quote_len);
2468
                return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2469
        }
2470

    
2471
        return NULL;
2472
}
2473

    
2474
/* return TRUE if the line is itemized */
2475
static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2476
                                    const GtkTextIter *start)
2477
{
2478
        GtkTextIter iter = *start;
2479
        gunichar wc;
2480
        gchar ch[6];
2481
        gint clen;
2482

    
2483
        if (gtk_text_iter_ends_line(&iter))
2484
                return FALSE;
2485

    
2486
        while (1) {
2487
                wc = gtk_text_iter_get_char(&iter);
2488
                if (!g_unichar_isspace(wc))
2489
                        break;
2490
                gtk_text_iter_forward_char(&iter);
2491
                if (gtk_text_iter_ends_line(&iter))
2492
                        return FALSE;
2493
        }
2494

    
2495
        clen = g_unichar_to_utf8(wc, ch);
2496

    
2497
        /* (1), 2), 3. etc. */
2498
        if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) {
2499
                gboolean digit_appeared = FALSE;
2500

    
2501
                if (ch[0] == '(')
2502
                        gtk_text_iter_forward_char(&iter);
2503

    
2504
                while (1) {
2505
                        wc = gtk_text_iter_get_char(&iter);
2506
                        clen = g_unichar_to_utf8(wc, ch);
2507
                        if (g_unichar_isdigit(wc)) {
2508
                                digit_appeared = TRUE;
2509
                                gtk_text_iter_forward_char(&iter);
2510
                                if (gtk_text_iter_ends_line(&iter))
2511
                                        return FALSE;
2512
                        } else if (clen == 1 &&
2513
                                   (ch[0] == ')' || ch[0] == '.')) {
2514
                                if (!digit_appeared)
2515
                                        return FALSE;
2516
                                gtk_text_iter_forward_char(&iter);
2517
                                if (gtk_text_iter_ends_line(&iter))
2518
                                        return TRUE;
2519
                                wc = gtk_text_iter_get_char(&iter);
2520
                                if (g_unichar_isspace(wc))
2521
                                        return TRUE;
2522
                                else
2523
                                        return FALSE;
2524
                        } else
2525
                                return FALSE;
2526
                }
2527
        }
2528

    
2529
        if (clen != 1)
2530
                return FALSE;
2531
        if (!strchr("*-+", ch[0]))
2532
                return FALSE;
2533

    
2534
        gtk_text_iter_forward_char(&iter);
2535
        if (gtk_text_iter_ends_line(&iter))
2536
                return FALSE;
2537
        wc = gtk_text_iter_get_char(&iter);
2538
        if (g_unichar_isspace(wc))
2539
                return TRUE;
2540
        else if (ch[0] == '-') {
2541
                /* -- */
2542
                clen = g_unichar_to_utf8(wc, ch);
2543
                if (clen == 1 && ch[0] == '-')
2544
                        return TRUE;
2545
        }
2546

    
2547
        return FALSE;
2548
}
2549

    
2550
static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2551
                                           const GtkTextIter *start,
2552
                                           GtkTextIter *break_pos,
2553
                                           gint max_col,
2554
                                           gint quote_len)
2555
{
2556
        GtkTextIter iter = *start, line_end = *start;
2557
        PangoLogAttr *attrs;
2558
        gchar *str;
2559
        gchar *p;
2560
        gint len;
2561
        gint i;
2562
        gint col = 0;
2563
        gint pos = 0;
2564
        gboolean can_break = FALSE;
2565
        gboolean do_break = FALSE;
2566
        gboolean prev_dont_break = FALSE;
2567

    
2568
        gtk_text_iter_forward_to_line_end(&line_end);
2569
        str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2570
        len = g_utf8_strlen(str, -1);
2571
        /* g_print("breaking line: %d: %s (len = %d)\n",
2572
                gtk_text_iter_get_line(&iter), str, len); */
2573
        attrs = g_new(PangoLogAttr, len + 1);
2574

    
2575
        pango_default_break(str, -1, NULL, attrs, len + 1);
2576

    
2577
        p = str;
2578

    
2579
        /* skip quote and leading spaces */
2580
        for (i = 0; *p != '\0' && i < len; i++) {
2581
                gunichar wc;
2582

    
2583
                wc = g_utf8_get_char(p);
2584
                if (i >= quote_len && !g_unichar_isspace(wc))
2585
                        break;
2586
                if (g_unichar_iswide(wc))
2587
                        col += 2;
2588
                else if (*p == '\t')
2589
                        col += 8;
2590
                else
2591
                        col++;
2592
                p = g_utf8_next_char(p);
2593
        }
2594

    
2595
        for (; *p != '\0' && i < len; i++) {
2596
                PangoLogAttr *attr = attrs + i;
2597
                gunichar wc;
2598
                gint uri_len;
2599

    
2600
                if (attr->is_line_break && can_break && !prev_dont_break)
2601
                        pos = i;
2602

    
2603
                /* don't wrap URI */
2604
                if ((uri_len = get_uri_len(p)) > 0) {
2605
                        col += uri_len;
2606
                        if (pos > 0 && col > max_col) {
2607
                                do_break = TRUE;
2608
                                break;
2609
                        }
2610
                        i += uri_len - 1;
2611
                        p += uri_len;
2612
                        can_break = TRUE;
2613
                        continue;
2614
                }
2615

    
2616
                wc = g_utf8_get_char(p);
2617
                if (g_unichar_iswide(wc)) {
2618
                        col += 2;
2619
                        if (prev_dont_break && can_break && attr->is_line_break)
2620
                                pos = i;
2621
                } else if (*p == '\t')
2622
                        col += 8;
2623
                else
2624
                        col++;
2625
                if (pos > 0 && col > max_col) {
2626
                        do_break = TRUE;
2627
                        break;
2628
                }
2629

    
2630
                if (*p == '-' || *p == '/')
2631
                        prev_dont_break = TRUE;
2632
                else
2633
                        prev_dont_break = FALSE;
2634

    
2635
                p = g_utf8_next_char(p);
2636
                can_break = TRUE;
2637
        }
2638

    
2639
        debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
2640

    
2641
        g_free(attrs);
2642
        g_free(str);
2643

    
2644
        *break_pos = *start;
2645
        gtk_text_iter_set_line_offset(break_pos, pos);
2646

    
2647
        return do_break;
2648
}
2649

    
2650
static gboolean compose_join_next_line(GtkTextBuffer *buffer,
2651
                                       GtkTextIter *iter,
2652
                                       const gchar *quote_str)
2653
{
2654
        GtkTextIter iter_ = *iter, cur, prev, next, end;
2655
        PangoLogAttr attrs[3];
2656
        gchar *str;
2657
        gchar *next_quote_str;
2658
        gunichar wc1, wc2;
2659
        gint quote_len;
2660
        gboolean keep_cursor = FALSE;
2661

    
2662
        if (!gtk_text_iter_forward_line(&iter_) ||
2663
            gtk_text_iter_ends_line(&iter_))
2664
                return FALSE;
2665

    
2666
        next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
2667

    
2668
        if ((quote_str || next_quote_str) &&
2669
            strcmp2(quote_str, next_quote_str) != 0) {
2670
                g_free(next_quote_str);
2671
                return FALSE;
2672
        }
2673
        g_free(next_quote_str);
2674

    
2675
        end = iter_;
2676
        if (quote_len > 0) {
2677
                gtk_text_iter_forward_chars(&end, quote_len);
2678
                if (gtk_text_iter_ends_line(&end))
2679
                        return FALSE;
2680
        }
2681

    
2682
        /* don't join itemized lines */
2683
        if (compose_is_itemized(buffer, &end))
2684
                return FALSE;
2685

    
2686
        /* delete quote str */
2687
        if (quote_len > 0)
2688
                gtk_text_buffer_delete(buffer, &iter_, &end);
2689

    
2690
        /* delete linebreak and extra spaces */
2691
        prev = cur = iter_;
2692
        while (gtk_text_iter_backward_char(&cur)) {
2693
                wc1 = gtk_text_iter_get_char(&cur);
2694
                if (!g_unichar_isspace(wc1))
2695
                        break;
2696
                prev = cur;
2697
        }
2698
        next = cur = iter_;
2699
        while (!gtk_text_iter_ends_line(&cur)) {
2700
                wc1 = gtk_text_iter_get_char(&cur);
2701
                if (!g_unichar_isspace(wc1))
2702
                        break;
2703
                gtk_text_iter_forward_char(&cur);
2704
                next = cur;
2705
        }
2706
        if (!gtk_text_iter_equal(&prev, &next)) {
2707
                GtkTextMark *mark;
2708

    
2709
                mark = gtk_text_buffer_get_insert(buffer);
2710
                gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
2711
                if (gtk_text_iter_equal(&prev, &cur))
2712
                        keep_cursor = TRUE;
2713
                gtk_text_buffer_delete(buffer, &prev, &next);
2714
        }
2715
        iter_ = prev;
2716

    
2717
        /* insert space if required */
2718
        gtk_text_iter_backward_char(&prev);
2719
        wc1 = gtk_text_iter_get_char(&prev);
2720
        wc2 = gtk_text_iter_get_char(&next);
2721
        gtk_text_iter_forward_char(&next);
2722
        str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
2723
        pango_default_break(str, -1, NULL, attrs, 3);
2724
        if (!attrs[1].is_line_break ||
2725
            (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
2726
                gtk_text_buffer_insert(buffer, &iter_, " ", 1);
2727
                if (keep_cursor) {
2728
                        gtk_text_iter_backward_char(&iter_);
2729
                        gtk_text_buffer_place_cursor(buffer, &iter_);
2730
                }
2731
        }
2732
        g_free(str);
2733

    
2734
        *iter = iter_;
2735
        return TRUE;
2736
}
2737

    
2738
static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
2739
{
2740
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2741
        GtkTextBuffer *buffer;
2742
        GtkTextIter iter, break_pos;
2743
        gchar *quote_str = NULL;
2744
        gint quote_len;
2745
        gboolean wrap_quote = prefs_common.linewrap_quote;
2746
        gboolean prev_autowrap = compose->autowrap;
2747

    
2748
        compose->autowrap = FALSE;
2749

    
2750
        buffer = gtk_text_view_get_buffer(text);
2751

    
2752
        if (par_iter) {
2753
                iter = *par_iter;
2754
        } else {
2755
                GtkTextMark *mark;
2756
                mark = gtk_text_buffer_get_insert(buffer);
2757
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2758
        }
2759

    
2760
        /* move to paragraph start */
2761
        gtk_text_iter_set_line_offset(&iter, 0);
2762
        if (gtk_text_iter_ends_line(&iter)) {
2763
                while (gtk_text_iter_ends_line(&iter) &&
2764
                       gtk_text_iter_forward_line(&iter))
2765
                        ;
2766
        } else {
2767
                while (gtk_text_iter_backward_line(&iter)) {
2768
                        if (gtk_text_iter_ends_line(&iter)) {
2769
                                gtk_text_iter_forward_line(&iter);
2770
                                break;
2771
                        }
2772
                }
2773
        }
2774

    
2775
        /* go until paragraph end (empty line) */
2776
        while (!gtk_text_iter_ends_line(&iter)) {
2777
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
2778
                if (quote_str) {
2779
                        if (!wrap_quote) {
2780
                                gtk_text_iter_forward_line(&iter);
2781
                                g_free(quote_str);
2782
                                continue;
2783
                        }
2784
                        debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
2785
                }
2786

    
2787
                if (compose_get_line_break_pos(buffer, &iter, &break_pos,
2788
                                               prefs_common.linewrap_len,
2789
                                               quote_len)) {
2790
                        GtkTextIter prev, next, cur;
2791

    
2792
                        gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
2793

    
2794
                        /* remove trailing spaces */
2795
                        cur = break_pos;
2796
                        gtk_text_iter_backward_char(&cur);
2797
                        prev = next = cur;
2798
                        while (!gtk_text_iter_starts_line(&cur)) {
2799
                                gunichar wc;
2800

    
2801
                                gtk_text_iter_backward_char(&cur);
2802
                                wc = gtk_text_iter_get_char(&cur);
2803
                                if (!g_unichar_isspace(wc))
2804
                                        break;
2805
                                prev = cur;
2806
                        }
2807
                        if (!gtk_text_iter_equal(&prev, &next)) {
2808
                                gtk_text_buffer_delete(buffer, &prev, &next);
2809
                                break_pos = next;
2810
                                gtk_text_iter_forward_char(&break_pos);
2811
                        }
2812

    
2813
                        if (quote_str)
2814
                                gtk_text_buffer_insert(buffer, &break_pos,
2815
                                                       quote_str, -1);
2816

    
2817
                        iter = break_pos;
2818
                        compose_join_next_line(buffer, &iter, quote_str);
2819

    
2820
                        /* move iter to current line start */
2821
                        gtk_text_iter_set_line_offset(&iter, 0);
2822
                } else {
2823
                        /* move iter to next line start */
2824
                        iter = break_pos;
2825
                        gtk_text_iter_forward_line(&iter);
2826
                }
2827

    
2828
                g_free(quote_str);
2829
        }
2830

    
2831
        if (par_iter)
2832
                *par_iter = iter;
2833

    
2834
        compose->autowrap = prev_autowrap;
2835
}
2836

    
2837
static void compose_wrap_all(Compose *compose)
2838
{
2839
        compose_wrap_all_full(compose, FALSE);
2840
}
2841

    
2842
static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
2843
{
2844
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2845
        GtkTextBuffer *buffer;
2846
        GtkTextIter iter;
2847

    
2848
        buffer = gtk_text_view_get_buffer(text);
2849

    
2850
        gtk_text_buffer_get_start_iter(buffer, &iter);
2851
        while (!gtk_text_iter_is_end(&iter))
2852
                compose_wrap_paragraph(compose, &iter);
2853
}
2854

    
2855
static void compose_set_title(Compose *compose)
2856
{
2857
        gchar *str;
2858
        gchar *edited;
2859
        const gchar *subject;
2860

    
2861
        subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2862
        if (!subject || subject[0] == '\0')
2863
                subject = _("(No Subject)");
2864

    
2865
        edited = compose->modified ? " *" : "";
2866
        str = g_strdup_printf(_("%s - Compose%s"), subject, edited);
2867
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
2868
        g_free(str);
2869
}
2870

    
2871
static void compose_select_account(Compose *compose, PrefsAccount *account,
2872
                                   gboolean init)
2873
{
2874
        GtkItemFactory *ifactory;
2875
        PrefsAccount *prev_account;
2876

    
2877
        g_return_if_fail(account != NULL);
2878

    
2879
        prev_account = compose->account;
2880
        compose->account = account;
2881

    
2882
        compose_set_title(compose);
2883

    
2884
        ifactory = gtk_item_factory_from_widget(compose->menubar);
2885

    
2886
        if (account->protocol == A_NNTP &&
2887
            (init || prev_account->protocol != A_NNTP)) {
2888
                gtk_widget_show(compose->newsgroups_hbox);
2889
                gtk_widget_show(compose->newsgroups_entry);
2890
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2891
                compose->use_newsgroups = TRUE;
2892

    
2893
                menu_set_active(ifactory, "/View/To", FALSE);
2894
                menu_set_sensitive(ifactory, "/View/To", TRUE);
2895
                menu_set_active(ifactory, "/View/Cc", FALSE);
2896
                menu_set_sensitive(ifactory, "/View/Followup-To", TRUE);
2897
        } else if (account->protocol != A_NNTP &&
2898
                   (init || prev_account->protocol == A_NNTP)) {
2899
                gtk_widget_hide(compose->newsgroups_hbox);
2900
                gtk_widget_hide(compose->newsgroups_entry);
2901
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0);
2902
                gtk_widget_queue_resize(compose->table_vbox);
2903
                compose->use_newsgroups = FALSE;
2904

    
2905
                menu_set_active(ifactory, "/View/To", TRUE);
2906
                menu_set_sensitive(ifactory, "/View/To", FALSE);
2907
                menu_set_active(ifactory, "/View/Cc", TRUE);
2908
                menu_set_active(ifactory, "/View/Followup-To", FALSE);
2909
                menu_set_sensitive(ifactory, "/View/Followup-To", FALSE);
2910
        }
2911

    
2912
        if (account->set_autocc) {
2913
                compose_entry_show(compose, COMPOSE_ENTRY_CC);
2914
                if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2915
                        compose_entry_set(compose, account->auto_cc,
2916
                                          COMPOSE_ENTRY_CC);
2917
        }
2918
        if (account->set_autobcc) {
2919
                compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2920
                if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2921
                        compose_entry_set(compose, account->auto_bcc,
2922
                                          COMPOSE_ENTRY_BCC);
2923
        }
2924
        if (account->set_autoreplyto) {
2925
                compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2926
                if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2927
                        compose_entry_set(compose, account->auto_replyto,
2928
                                          COMPOSE_ENTRY_REPLY_TO);
2929
        }
2930

    
2931
#if USE_GPGME
2932
        if (rfc2015_is_available()) {
2933
                if (account->default_sign)
2934
                        menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2935
                if (account->default_encrypt)
2936
                        menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2937
        }
2938
#endif /* USE_GPGME */
2939

    
2940
        if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2941
                compose_insert_sig(compose, TRUE, TRUE, FALSE);
2942
}
2943

    
2944
static gboolean compose_check_for_valid_recipient(Compose *compose)
2945
{
2946
        const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
2947
        const gchar *newsgroups_raw = "";
2948
        gchar *to, *cc, *bcc;
2949
        gchar *newsgroups;
2950
        gboolean valid;
2951

    
2952
        if (compose->use_to)
2953
                to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2954
        if (compose->use_cc)
2955
                cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2956
        if (compose->use_bcc)
2957
                bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2958
        if (compose->use_newsgroups)
2959
                newsgroups_raw = gtk_entry_get_text
2960
                        (GTK_ENTRY(compose->newsgroups_entry));
2961

    
2962
        if (*to_raw == '\0' && *cc_raw == '\0' && *bcc_raw == '\0' &&
2963
            *newsgroups_raw == '\0')
2964
                return FALSE;
2965

    
2966
        to = g_strstrip(g_strdup(to_raw));
2967
        cc = g_strstrip(g_strdup(cc_raw));
2968
        bcc = g_strstrip(g_strdup(bcc_raw));
2969
        newsgroups = g_strstrip(g_strdup(newsgroups_raw));
2970

    
2971
        if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
2972
                valid = FALSE;
2973
        else
2974
                valid = TRUE;
2975

    
2976
        g_free(newsgroups);
2977
        g_free(bcc);
2978
        g_free(cc);
2979
        g_free(to);
2980

    
2981
        return valid;
2982
}
2983

    
2984
static gboolean compose_check_entries(Compose *compose)
2985
{
2986
        const gchar *str;
2987

    
2988
        if (compose_check_for_valid_recipient(compose) == FALSE) {
2989
                alertpanel_error(_("Recipient is not specified."));
2990
                return FALSE;
2991
        }
2992

    
2993
        str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2994
        if (*str == '\0') {
2995
                AlertValue aval;
2996

    
2997
                aval = alertpanel(_("Empty subject"),
2998
                                  _("Subject is empty. Send it anyway?"),
2999
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3000
                if (aval != G_ALERTDEFAULT)
3001
                        return FALSE;
3002
        }
3003

    
3004
        return TRUE;
3005
}
3006

    
3007
static gboolean compose_check_attachments(Compose *compose)
3008
{
3009
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3010
        GtkTextBuffer *buffer;
3011
        GtkTextIter iter, line_end;
3012
        gchar *line;
3013
        gchar **strv;
3014
        gint i;
3015
        gboolean attach_found = FALSE;
3016
        gboolean valid = TRUE;
3017

    
3018
        if (!prefs_common.check_attach)
3019
                return TRUE;
3020
        if (!prefs_common.check_attach_str)
3021
                return TRUE;
3022

    
3023
        if (compose->use_attach &&
3024
            gtk_tree_model_iter_n_children
3025
                (GTK_TREE_MODEL(compose->attach_store), NULL) > 0)
3026
                return TRUE;
3027

    
3028
        buffer = gtk_text_view_get_buffer(text);
3029
        gtk_text_buffer_get_start_iter(buffer, &iter);
3030
        line_end = iter;
3031

    
3032
        strv = g_strsplit(prefs_common.check_attach_str, ",", -1);
3033
        for (i = 0; strv[i] != NULL; i++)
3034
                g_strstrip(strv[i]);
3035

    
3036
        while (valid) {
3037
                valid = gtk_text_iter_forward_to_line_end(&line_end);
3038
                line = gtk_text_buffer_get_text(buffer, &iter, &line_end,
3039
                                                FALSE);
3040
                iter = line_end;
3041
                if (get_quote_level(line) != -1)
3042
                        continue;
3043

    
3044
                for (i = 0; strv[i] != NULL; i++) {
3045
                        if (strv[i][0] == '\0')
3046
                                continue;
3047
                        if (strcasestr(line, strv[i])) {
3048
                                attach_found = TRUE;
3049
                                valid = FALSE;
3050
                                break;
3051
                        }
3052
                }
3053

    
3054
                g_free(line);
3055
        }
3056

    
3057
        g_strfreev(strv);
3058

    
3059
        if (attach_found) {
3060
                AlertValue aval;
3061

    
3062
                aval = alertpanel(_("Attachment is missing"),
3063
                                  _("There is no attachment. Send it without attachments?"),
3064
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3065
                if (aval != G_ALERTDEFAULT)
3066
                        return FALSE;
3067
        }
3068

    
3069
        return TRUE;
3070
}
3071

    
3072
static gint check_recp_delete_event(GtkWidget *widget, GdkEventAny *event,
3073
                                    gint *state)
3074
{
3075
        *state = GTK_RESPONSE_CANCEL;
3076
        return TRUE;
3077
}
3078

    
3079
static gboolean check_recp_key_pressed(GtkWidget *widget, GdkEventKey *event,
3080
                                       gint *state)
3081
{
3082
        if (event && event->keyval == GDK_Escape) {
3083
                *state = GTK_RESPONSE_CANCEL;
3084
                return TRUE;
3085
        }
3086
        return FALSE;
3087
}
3088

    
3089
static void check_recp_ok(GtkWidget *widget, gint *state)
3090
{
3091
        *state = GTK_RESPONSE_OK;
3092
}
3093

    
3094
static void check_recp_cancel(GtkWidget *widget, gint *state)
3095
{
3096
        *state = GTK_RESPONSE_CANCEL;
3097
}
3098

    
3099
static gboolean compose_check_recipients(Compose *compose)
3100
{
3101
        GtkWidget *window;
3102
        GtkWidget *vbox;
3103
        GtkWidget *hbox;
3104
        GtkWidget *image;
3105
        GtkWidget *vbox2;
3106
        GtkWidget *label;
3107
        GtkWidget *table;
3108
        GtkWidget *entry;
3109
        gchar buf[1024];
3110
        const gchar *text;
3111
        GtkWidget *scrwin;
3112
        GtkWidget *treeview;
3113
        GtkTreeStore *store;
3114
        GtkTreeViewColumn *column;
3115
        GtkCellRenderer *renderer;
3116
        GtkTreeIter iter, parent;
3117
        GtkWidget *hbbox;
3118
        GtkWidget *ok_btn;
3119
        GtkWidget *cancel_btn;
3120
        static PangoFontDescription *font_desc;
3121
        GtkStyle *style;
3122

    
3123
        GSList *cur, *to_list = NULL;
3124
        gboolean check_recp = FALSE;
3125
        gint state = 0;
3126
 
3127
        g_return_val_if_fail(compose->account != NULL, FALSE);
3128
        g_return_val_if_fail(compose->account->address != NULL, FALSE);
3129

    
3130
        if (!prefs_common.check_recipients)
3131
                return TRUE;
3132

    
3133
        if (prefs_common.check_recp_exclude) {
3134
                gchar **strv;
3135
                gint i;
3136

    
3137
                strv = g_strsplit(prefs_common.check_recp_exclude, ",", -1);
3138
                for (i = 0; strv[i] != NULL; i++)
3139
                        g_strstrip(strv[i]);
3140

    
3141
                if (compose->use_to) {
3142
                        text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3143
                        to_list = address_list_append_orig(NULL, text);
3144
                }
3145
                if (compose->use_cc) {
3146
                        text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3147
                        to_list = address_list_append_orig(to_list, text);
3148
                }
3149
                if (compose->use_bcc) {
3150
                        text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3151
                        to_list = address_list_append_orig(to_list, text);
3152
                }
3153

    
3154
                for (cur = to_list; cur != NULL; cur = cur->next) {
3155
                        for (i = 0; strv[i] != NULL; i++) {
3156
                                if (strv[i][0] == '\0')
3157
                                        continue;
3158
                                if (strcasestr((gchar *)cur->data, strv[i]))
3159
                                        break;
3160
                        }
3161
                        if (!strv[i]) {
3162
                                /* not found in exclude list */
3163
                                check_recp = TRUE;
3164
                                break;
3165
                        }
3166
                }
3167

    
3168
                slist_free_strings(to_list);
3169
                g_slist_free(to_list);
3170
                to_list = NULL;
3171
                g_strfreev(strv);
3172
        } else
3173
                check_recp = TRUE;
3174

    
3175
        if (!check_recp)
3176
                return TRUE;
3177

    
3178
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3179
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
3180
        gtk_window_set_title(GTK_WINDOW(window), _("Check recipients"));
3181
        gtk_window_set_position(GTK_WINDOW(window),
3182
                                GTK_WIN_POS_CENTER_ON_PARENT);
3183
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
3184
        gtk_widget_set_size_request(window, 480, -1);
3185
        gtk_widget_realize(window);
3186
        g_signal_connect(G_OBJECT(window), "delete_event",
3187
                         G_CALLBACK(check_recp_delete_event), &state);
3188
        g_signal_connect(G_OBJECT(window), "key_press_event",
3189
                         G_CALLBACK(check_recp_key_pressed), &state);
3190

    
3191
        vbox = gtk_vbox_new(FALSE, 8);
3192
        gtk_container_add(GTK_CONTAINER(window), vbox);
3193

    
3194
        hbox = gtk_hbox_new(FALSE, 12);
3195
        gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
3196
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3197

    
3198
        image = gtk_image_new_from_stock
3199
                (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
3200
        gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0);
3201
        gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
3202

    
3203
        vbox2 = gtk_vbox_new(FALSE, 12);
3204
        gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
3205

    
3206
        label = gtk_label_new(_("Check recipients"));
3207
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3208
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3209
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3210

    
3211
        if (!font_desc) {
3212
                gint size;
3213

    
3214
                size = pango_font_description_get_size
3215
                        (label->style->font_desc);
3216
                font_desc = pango_font_description_new();
3217
                pango_font_description_set_weight
3218
                        (font_desc, PANGO_WEIGHT_BOLD);
3219
                pango_font_description_set_size
3220
                        (font_desc, size * PANGO_SCALE_LARGE);
3221
        }
3222
        if (font_desc)
3223
                gtk_widget_modify_font(label, font_desc);
3224

    
3225
        label = gtk_label_new
3226
                (_("Really send this mail to the following addresses?"));
3227
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3228
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3229
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3230
        gtk_label_set_selectable(GTK_LABEL(label), TRUE);
3231
        GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS);
3232

    
3233
        table = gtk_table_new(2, 2, FALSE);
3234
        gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
3235
        gtk_table_set_row_spacings(GTK_TABLE(table), 4);
3236
        gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3237

    
3238
        hbox = gtk_hbox_new(FALSE, 0);
3239
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:")
3240
                              : "From:");
3241
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3242
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1,
3243
                         GTK_FILL, 0, 2, 0);
3244
        entry = gtk_entry_new();
3245
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3246
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3247
        style = gtk_widget_get_style(window);
3248
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3249
                               &style->bg[GTK_STATE_NORMAL]);
3250
        gtk_table_attach_defaults
3251
                (GTK_TABLE(table), entry, 1, 2, 0, 1);
3252

    
3253
        if (compose->account->name && *compose->account->name) {
3254
                g_snprintf(buf, sizeof(buf), "%s <%s>",
3255
                           compose->account->name, compose->account->address);
3256
                gtk_entry_set_text(GTK_ENTRY(entry), buf);
3257
        } else
3258
                gtk_entry_set_text(GTK_ENTRY(entry), compose->account->address);
3259

    
3260
        hbox = gtk_hbox_new(FALSE, 0);
3261
        label = gtk_label_new(prefs_common.trans_hdr ? _("Subject:")
3262
                              : "Subject:");
3263
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3264
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2,
3265
                         GTK_FILL, 0, 2, 0);
3266
        entry = gtk_entry_new();
3267
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3268
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3269
        style = gtk_widget_get_style(window);
3270
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3271
                               &style->bg[GTK_STATE_NORMAL]);
3272
        gtk_table_attach_defaults
3273
                (GTK_TABLE(table), entry, 1, 2, 1, 2);
3274

    
3275
        text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3276
        gtk_entry_set_text(GTK_ENTRY(entry), text);
3277

    
3278
        scrwin = gtk_scrolled_window_new(NULL, NULL);
3279
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
3280
                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3281
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
3282
                                            GTK_SHADOW_IN);
3283
        gtk_widget_set_size_request(scrwin, -1, 180);
3284
        gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
3285

    
3286
        store = gtk_tree_store_new(1, G_TYPE_STRING);
3287
        if (compose->use_to) {
3288
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3289
                to_list = address_list_append_orig(NULL, text);
3290
                if (to_list) {
3291
                        gtk_tree_store_append(store, &parent, NULL);
3292
                        gtk_tree_store_set(store, &parent, 0,
3293
                                           prefs_common.trans_hdr ?
3294
                                           _("To:") : "To:", -1);
3295
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3296
                                gtk_tree_store_append(store, &iter, &parent);
3297
                                gtk_tree_store_set(store, &iter, 0,
3298
                                                   (gchar *)cur->data, -1);
3299
                        }
3300
                        slist_free_strings(to_list);
3301
                        g_slist_free(to_list);
3302
                }
3303
        }
3304
        if (compose->use_cc) {
3305
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3306
                to_list = address_list_append_orig(NULL, text);
3307
                if (to_list) {
3308
                        gtk_tree_store_append(store, &parent, NULL);
3309
                        gtk_tree_store_set(store, &parent, 0,
3310
                                           prefs_common.trans_hdr ?
3311
                                           _("Cc:") : "Cc:", -1);
3312
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3313
                                gtk_tree_store_append(store, &iter, &parent);
3314
                                gtk_tree_store_set(store, &iter, 0,
3315
                                                   (gchar *)cur->data, -1);
3316
                        }
3317
                        slist_free_strings(to_list);
3318
                        g_slist_free(to_list);
3319
                }
3320
        }
3321
        if (compose->use_bcc) {
3322
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3323
                to_list = address_list_append_orig(NULL, text);
3324
                if (to_list) {
3325
                        gtk_tree_store_append(store, &parent, NULL);
3326
                        gtk_tree_store_set(store, &parent, 0,
3327
                                           prefs_common.trans_hdr ?
3328
                                           _("Bcc:") : "Bcc:", -1);
3329
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3330
                                gtk_tree_store_append(store, &iter, &parent);
3331
                                gtk_tree_store_set(store, &iter, 0,
3332
                                                   (gchar *)cur->data, -1);
3333
                        }
3334
                        slist_free_strings(to_list);
3335
                        g_slist_free(to_list);
3336
                }
3337
        }
3338

    
3339
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3340
        g_object_unref(G_OBJECT(store));
3341
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
3342
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
3343

    
3344
        gtk_container_add(GTK_CONTAINER(scrwin), treeview);
3345

    
3346
        renderer = gtk_cell_renderer_text_new();
3347
        g_object_set(renderer, "ypad", 0, NULL);
3348
        column = gtk_tree_view_column_new_with_attributes
3349
                (_("Address"), renderer, "text", 0, NULL);
3350
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
3351

    
3352
        gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
3353

    
3354
        gtkut_stock_button_set_create(&hbbox, &ok_btn, _("_Send"),
3355
                                      &cancel_btn, GTK_STOCK_CANCEL,
3356
                                      NULL, NULL);
3357
        gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
3358
        gtk_widget_grab_default(ok_btn);
3359
        gtk_widget_grab_focus(ok_btn);
3360

    
3361
        g_signal_connect(G_OBJECT(ok_btn), "clicked",
3362
                         G_CALLBACK(check_recp_ok), &state);
3363
        g_signal_connect(G_OBJECT(cancel_btn), "clicked",
3364
                         G_CALLBACK(check_recp_cancel), &state);
3365

    
3366
        manage_window_set_transient(GTK_WINDOW(window));
3367

    
3368
        gtk_widget_show_all(window);
3369

    
3370
        while (state == 0)
3371
                gtk_main_iteration();
3372

    
3373
        gtk_widget_destroy(window);
3374

    
3375
        if (state == GTK_RESPONSE_OK)
3376
                return TRUE;
3377

    
3378
        return FALSE;
3379
}
3380

    
3381
static gboolean compose_check_activities(Compose *compose)
3382
{
3383
        if (inc_is_active()) {
3384
                alertpanel_notice(_("Checking for new messages is currently running.\n"
3385
                                    "Please try again later."));
3386
                return FALSE;
3387
        }
3388

    
3389
        return TRUE;
3390
}
3391

    
3392
static void compose_add_new_recipients_to_addressbook(Compose *compose)
3393
{
3394
        GSList *to_list = NULL, *cur;
3395
        const gchar *text;
3396

    
3397
        if (compose->use_to) {
3398
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3399
                to_list = address_list_append_orig(NULL, text);
3400
        }
3401
        if (compose->use_cc) {
3402
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3403
                to_list = address_list_append_orig(to_list, text);
3404
        }
3405
        if (compose->use_bcc) {
3406
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3407
                to_list = address_list_append_orig(to_list, text);
3408
        }
3409

    
3410
        for (cur = to_list; cur != NULL; cur = cur->next) {
3411
                gchar *orig_addr = cur->data;
3412
                gchar *name, *addr;
3413

    
3414
                name = procheader_get_fromname(orig_addr);
3415
                addr = g_strdup(orig_addr);
3416
                extract_address(addr);
3417
                if (!g_ascii_strcasecmp(name, addr)) {
3418
                        g_free(name);
3419
                        name = NULL;
3420
                }
3421

    
3422
                if (addressbook_has_address(addr))
3423
                        debug_print("compose_add_new_recipients_to_addressbook: address <%s> already registered.\n", addr);
3424
                else
3425
                        addressbook_add_contact_autoreg(name, addr, NULL);
3426

    
3427
                g_free(addr);
3428
                g_free(name);
3429
        }
3430

    
3431
        slist_free_strings(to_list);
3432
        g_slist_free(to_list);
3433
}
3434

    
3435
void compose_lock(Compose *compose)
3436
{
3437
        compose->lock_count++;
3438
}
3439

    
3440
void compose_unlock(Compose *compose)
3441
{
3442
        if (compose->lock_count > 0)
3443
                compose->lock_count--;
3444
}
3445

    
3446
void compose_block_modified(Compose *compose)
3447
{
3448
        compose->block_modified = TRUE;
3449
}
3450

    
3451
void compose_unblock_modified(Compose *compose)
3452
{
3453
        compose->block_modified = FALSE;
3454
}
3455

    
3456
#define C_LOCK()                        \
3457
{                                        \
3458
        inc_lock();                        \
3459
        compose_lock(compose);                \
3460
}
3461

    
3462
#define C_UNLOCK()                        \
3463
{                                        \
3464
        compose_unlock(compose);        \
3465
        inc_unlock();                        \
3466
}
3467

    
3468
static gint compose_send(Compose *compose)
3469
{
3470
        gchar tmp[MAXPATHLEN + 1];
3471
        gint ok = 0;
3472
        gboolean cancel = FALSE;
3473

    
3474
        if (compose->lock_count > 0)
3475
                return 1;
3476

    
3477
        g_return_val_if_fail(compose->account != NULL, -1);
3478

    
3479
        C_LOCK();
3480

    
3481
        if (compose_check_entries(compose) == FALSE) {
3482
                C_UNLOCK();
3483
                return 1;
3484
        }
3485
        if (compose_check_attachments(compose) == FALSE) {
3486
                C_UNLOCK();
3487
                return 1;
3488
        }
3489
        if (compose_check_recipients(compose) == FALSE) {
3490
                C_UNLOCK();
3491
                return 1;
3492
        }
3493
        if (compose_check_activities(compose) == FALSE) {
3494
                C_UNLOCK();
3495
                return 1;
3496
        }
3497

    
3498
        if (!main_window_toggle_online_if_offline(main_window_get())) {
3499
                C_UNLOCK();
3500
                return 1;
3501
        }
3502

    
3503
        /* write to temporary file */
3504
        g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
3505
                   get_tmp_dir(), G_DIR_SEPARATOR, compose);
3506

    
3507
        if (compose->mode == COMPOSE_REDIRECT) {
3508
                if (compose_redirect_write_to_file(compose, tmp) < 0) {
3509
                        C_UNLOCK();
3510
                        return -1;
3511
                }
3512
        } else {
3513
                if (compose_write_to_file(compose, tmp, FALSE) < 0) {
3514
                        C_UNLOCK();
3515
                        return -1;
3516
                }
3517
        }
3518

    
3519
        if (!compose->to_list && !compose->newsgroup_list) {
3520
                g_warning(_("can't get recipient list."));
3521
                g_unlink(tmp);
3522
                C_UNLOCK();
3523
                return 1;
3524
        }
3525

    
3526
        syl_plugin_signal_emit("compose-send", compose, compose->mode, 0,
3527
                               tmp, compose->to_list, &cancel);
3528
        if (cancel) {
3529
                g_unlink(tmp);
3530
                C_UNLOCK();
3531
                return -1;
3532
        }
3533

    
3534
        if (compose->to_list) {
3535
                PrefsAccount *ac;
3536

    
3537
                if (compose->account->protocol != A_NNTP)
3538
                        ac = compose->account;
3539
                else {
3540
                        ac = account_find_from_address(compose->account->address);
3541
                        if (!ac) {
3542
                                if (cur_account && cur_account->protocol != A_NNTP)
3543
                                        ac = cur_account;
3544
                                else
3545
                                        ac = account_get_default();
3546
                        }
3547
                        if (!ac || ac->protocol == A_NNTP) {
3548
                                alertpanel_error(_("Account for sending mail is not specified.\n"
3549
                                                   "Please select a mail account before sending."));
3550
                                g_unlink(tmp);
3551
                                C_UNLOCK();
3552
                                return -1;
3553
                        }
3554
                }
3555

    
3556
                /* POP before SMTP requires inc to be unlocked.
3557
                   send_message() also locks inc internally. */
3558
                inc_unlock();
3559
                ok = send_message(tmp, ac, compose->to_list);
3560
                inc_lock();
3561

    
3562
                statusbar_pop_all();
3563
        }
3564

    
3565
        if (ok == 0 && compose->newsgroup_list) {
3566
                ok = news_post(FOLDER(compose->account->folder), tmp);
3567
                if (ok < 0) {
3568
                        alertpanel_error(_("Error occurred while posting the message to %s ."),
3569
                                         compose->account->nntp_server);
3570
                        g_unlink(tmp);
3571
                        C_UNLOCK();
3572
                        return -1;
3573
                }
3574
        }
3575

    
3576
        if (ok == 0) {
3577
                if (compose->mode == COMPOSE_REEDIT) {
3578
                        compose_remove_reedit_target(compose);
3579
                        if (compose->targetinfo)
3580
                                folderview_update_item
3581
                                        (compose->targetinfo->folder, TRUE);
3582
                }
3583

    
3584
                if (compose->reply_target)
3585
                        send_message_set_reply_flag(compose->reply_target,
3586
                                                    compose->inreplyto);
3587
                else if (compose->forward_targets)
3588
                        send_message_set_forward_flags
3589
                                (compose->forward_targets);
3590

    
3591
                /* save message to outbox */
3592
                if (prefs_common.savemsg) {
3593
                        FolderItem *outbox;
3594
                        gboolean drop_done = FALSE;
3595

    
3596
                        /* filter sent message */
3597
                        if (prefs_common.filter_sent) {
3598
                                FilterInfo *fltinfo;
3599

    
3600
                                fltinfo = filter_info_new();
3601
                                fltinfo->account = compose->account;
3602
                                fltinfo->flags.perm_flags = 0;
3603
                                fltinfo->flags.tmp_flags = MSG_RECEIVED;
3604

    
3605
                                filter_apply(prefs_common.fltlist, tmp,
3606
                                             fltinfo);
3607

    
3608
                                drop_done = fltinfo->drop_done;
3609
                                folderview_update_all_updated(TRUE);
3610
                                filter_info_free(fltinfo);
3611
                        }
3612

    
3613
                        if (!drop_done) {
3614
                                outbox = account_get_special_folder
3615
                                        (compose->account, F_OUTBOX);
3616
                                if (procmsg_save_to_outbox(outbox, tmp) < 0) {
3617
                                        alertpanel_error
3618
                                                (_("Sending of message was completed, but the message could not be saved to outbox."));
3619
                                        ok = -2;
3620
                                } else
3621
                                        folderview_update_item(outbox, TRUE);
3622
                        }
3623
                }
3624

    
3625
                /* Add recipients to addressbook automatically */
3626
                if (prefs_common.recipients_autoreg) {
3627
                        compose_add_new_recipients_to_addressbook(compose);
3628
                }
3629
        }
3630

    
3631
        g_unlink(tmp);
3632
        C_UNLOCK();
3633

    
3634
        return ok;
3635
}
3636

    
3637
#if USE_GPGME
3638
/* interfaces to rfc2015 to keep out the prefs stuff there.
3639
 * returns 0 on success and -1 on error. */
3640
static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
3641
{
3642
        const gchar *key_id = NULL;
3643
        GSList *key_list;
3644

    
3645
        switch (compose->account->sign_key) {
3646
        case SIGN_KEY_DEFAULT:
3647
                *pkey_list = NULL;
3648
                return 0;
3649
        case SIGN_KEY_BY_FROM:
3650
                key_id = compose->account->address;
3651
                break;
3652
        case SIGN_KEY_CUSTOM:
3653
                key_id = compose->account->sign_key_id;
3654
                break;
3655
        default:
3656
                break;
3657
        }
3658

    
3659
        key_list = rfc2015_create_signers_list(key_id);
3660

    
3661
        if (!key_list) {
3662
                alertpanel_error(_("Could not find any key associated with "
3663
                                   "currently selected key id `%s'."), key_id);
3664
                return -1;
3665
        }
3666

    
3667
        *pkey_list = key_list;
3668
        return 0;
3669
}
3670

    
3671
/* clearsign message body text */
3672
static gint compose_clearsign_text(Compose *compose, gchar **text)
3673
{
3674
        GSList *key_list;
3675
        gchar *tmp_file;
3676

    
3677
        tmp_file = get_tmp_file();
3678
        if (str_write_to_file(*text, tmp_file) < 0) {
3679
                g_free(tmp_file);
3680
                return -1;
3681
        }
3682

    
3683
        if (compose_create_signers_list(compose, &key_list) < 0) {
3684
                g_unlink(tmp_file);
3685
                g_free(tmp_file);
3686
                return -1;
3687
        }
3688
        if (rfc2015_clearsign(tmp_file, key_list) < 0) {
3689
                alertpanel_error(_("Can't sign the message."));
3690
                g_unlink(tmp_file);
3691
                g_free(tmp_file);
3692
                return -1;
3693
        }
3694

    
3695
        g_free(*text);
3696
        *text = file_read_to_str(tmp_file);
3697
        g_unlink(tmp_file);
3698
        g_free(tmp_file);
3699
        if (*text == NULL)
3700
                return -1;
3701

    
3702
        return 0;
3703
}
3704

    
3705
static gint compose_encrypt_armored(Compose *compose, gchar **text)
3706
{
3707
        gchar *tmp_file;
3708

    
3709
        tmp_file = get_tmp_file();
3710
        if (str_write_to_file(*text, tmp_file) < 0) {
3711
                g_free(tmp_file);
3712
                return -1;
3713
        }
3714

    
3715
        if (rfc2015_encrypt_armored(tmp_file, compose->to_list) < 0) {
3716
                alertpanel_error(_("Can't encrypt the message."));
3717
                g_unlink(tmp_file);
3718
                g_free(tmp_file);
3719
                return -1;
3720
        }
3721

    
3722
        g_free(*text);
3723
        *text = file_read_to_str(tmp_file);
3724
        g_unlink(tmp_file);
3725
        g_free(tmp_file);
3726
        if (*text == NULL)
3727
                return -1;
3728

    
3729
        return 0;
3730
}
3731

    
3732
static gint compose_encrypt_sign_armored(Compose *compose, gchar **text)
3733
{
3734
        GSList *key_list;
3735
        gchar *tmp_file;
3736

    
3737
        tmp_file = get_tmp_file();
3738
        if (str_write_to_file(*text, tmp_file) < 0) {
3739
                g_free(tmp_file);
3740
                return -1;
3741
        }
3742

    
3743
        if (compose_create_signers_list(compose, &key_list) < 0) {
3744
                g_unlink(tmp_file);
3745
                g_free(tmp_file);
3746
                return -1;
3747
        }
3748

    
3749
        if (rfc2015_encrypt_sign_armored
3750
                (tmp_file, compose->to_list, key_list) < 0) {
3751
                alertpanel_error(_("Can't encrypt or sign the message."));
3752
                g_unlink(tmp_file);
3753
                g_free(tmp_file);
3754
                return -1;
3755
        }
3756

    
3757
        g_free(*text);
3758
        *text = file_read_to_str(tmp_file);
3759
        g_unlink(tmp_file);
3760
        g_free(tmp_file);
3761
        if (*text == NULL)
3762
                return -1;
3763

    
3764
        return 0;
3765
}
3766
#endif /* USE_GPGME */
3767

    
3768
static gint compose_write_to_file(Compose *compose, const gchar *file,
3769
                                  gboolean is_draft)
3770
{
3771
        GtkTextBuffer *buffer;
3772
        GtkTextIter start, end;
3773
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
3774
        FILE *fp;
3775
        size_t len;
3776
        gchar *chars;
3777
        gchar *buf;
3778
        gchar *canon_buf;
3779
        const gchar *out_charset;
3780
        const gchar *body_charset;
3781
        const gchar *src_charset = CS_INTERNAL;
3782
        EncodingType encoding;
3783
        gint line;
3784
#if USE_GPGME
3785
        gboolean use_pgpmime_encryption = FALSE;
3786
        gboolean use_pgpmime_signing = FALSE;
3787
#endif
3788

    
3789
        if ((fp = g_fopen(file, "wb")) == NULL) {
3790
                FILE_OP_ERROR(file, "fopen");
3791
                return -1;
3792
        }
3793

    
3794
        /* chmod for security */
3795
        if (change_file_mode_rw(fp, file) < 0) {
3796
                FILE_OP_ERROR(file, "chmod");
3797
                g_warning(_("can't change file mode\n"));
3798
        }
3799

    
3800
        /* get outgoing charset */
3801
        out_charset = conv_get_charset_str(compose->out_encoding);
3802
        if (!out_charset)
3803
                out_charset = conv_get_outgoing_charset_str();
3804
        if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII))
3805
                out_charset = CS_ISO_8859_1;
3806
        body_charset = out_charset;
3807

    
3808
        /* get all composed text */
3809
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3810
        gtk_text_buffer_get_start_iter(buffer, &start);
3811
        gtk_text_buffer_get_end_iter(buffer, &end);
3812
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3813
        if (is_ascii_str(chars)) {
3814
                buf = chars;
3815
                chars = NULL;
3816
                body_charset = CS_US_ASCII;
3817
                encoding = ENC_7BIT;
3818
        } else {
3819
                gint error = 0;
3820

    
3821
                buf = conv_codeset_strdup_full
3822
                        (chars, src_charset, body_charset, &error);
3823
                if (!buf || error != 0) {
3824
                        AlertValue aval = G_ALERTDEFAULT;
3825
                        gchar *msg;
3826

    
3827
                        g_free(buf);
3828

    
3829
                        if (!is_draft) {
3830
                                msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n"
3831
                                                        "\n"
3832
                                                        "Send it as %s anyway?"),
3833
                                                      src_charset, body_charset,
3834
                                                      src_charset);
3835
                                aval = alertpanel_full
3836
                                        (_("Code conversion error"), msg, ALERT_ERROR,
3837
                                         G_ALERTALTERNATE,
3838
                                         FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3839
                                g_free(msg);
3840
                        }
3841

    
3842
                        if (aval != G_ALERTDEFAULT) {
3843
                                g_free(chars);
3844
                                fclose(fp);
3845
                                g_unlink(file);
3846
                                return -1;
3847
                        } else {
3848
                                buf = chars;
3849
                                out_charset = body_charset = src_charset;
3850
                                chars = NULL;
3851
                        }
3852
                }
3853

    
3854
                if (prefs_common.encoding_method == CTE_BASE64)
3855
                        encoding = ENC_BASE64;
3856
                else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3857
                        encoding = ENC_QUOTED_PRINTABLE;
3858
                else if (prefs_common.encoding_method == CTE_8BIT)
3859
                        encoding = ENC_8BIT;
3860
                else
3861
                        encoding = procmime_get_encoding_for_charset
3862
                                (body_charset);
3863
        }
3864
        g_free(chars);
3865

    
3866
        canon_buf = canonicalize_str(buf);
3867
        g_free(buf);
3868
        buf = canon_buf;
3869

    
3870
#if USE_GPGME
3871
        if (compose->use_signing && !compose->account->clearsign)
3872
                use_pgpmime_signing = TRUE;
3873
        if (compose->use_encryption && compose->account->ascii_armored) {
3874
                use_pgpmime_encryption = FALSE;
3875
                use_pgpmime_signing = FALSE;
3876
        }
3877
        if (compose->use_encryption && !compose->account->ascii_armored)
3878
                use_pgpmime_encryption = TRUE;
3879

    
3880
        /* protect trailing spaces */
3881
        if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) {
3882
                if (encoding == ENC_7BIT) {
3883
                        if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) {
3884
                                gchar *tmp;
3885
                                tmp = strchomp_all(buf);
3886
                                g_free(buf);
3887
                                buf = tmp;
3888
                        } else
3889
                                encoding = ENC_QUOTED_PRINTABLE;
3890
                } else if (encoding == ENC_8BIT) {
3891
                        encoding = procmime_get_encoding_for_str(buf);
3892
                        if (encoding == ENC_7BIT)
3893
                                encoding = ENC_QUOTED_PRINTABLE;
3894
                }
3895
        }
3896

    
3897
        if (rfc2015_is_available() && !is_draft) {
3898
                if ((compose->use_encryption &&
3899
                     compose->account->ascii_armored) ||
3900
                    (compose->use_signing && compose->account->clearsign)) {
3901
                        /* MIME encoding doesn't fit with cleartext signature */
3902
                        if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64)
3903
                                encoding = ENC_8BIT;
3904

    
3905
                }
3906
        }
3907
#endif
3908

    
3909
        debug_print("src encoding = %s, out encoding = %s, "
3910
                    "body encoding = %s, transfer encoding = %s\n",
3911
                    src_charset, out_charset, body_charset,
3912
                    procmime_get_encoding_str(encoding));
3913

    
3914
        /* check for line length limit */
3915
        if (!is_draft &&
3916
            encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
3917
            check_line_length(buf, 1000, &line) < 0) {
3918
                AlertValue aval;
3919
                gchar *msg;
3920

    
3921
                msg = g_strdup_printf
3922
                        (_("Line %d exceeds the line length limit (998 bytes).\n"
3923
                           "The contents of the message might be broken on the way to the delivery.\n"
3924
                           "\n"
3925
                           "Send it anyway?"), line + 1);
3926
                aval = alertpanel_full(_("Line length limit"),
3927
                                       msg, ALERT_WARNING,
3928
                                       G_ALERTALTERNATE, FALSE,
3929
                                       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3930
                if (aval != G_ALERTDEFAULT) {
3931
                        g_free(msg);
3932
                        fclose(fp);
3933
                        g_unlink(file);
3934
                        g_free(buf);
3935
                        return -1;
3936
                }
3937
        }
3938

    
3939
        /* write headers */
3940
        if (compose_write_headers(compose, fp, out_charset,
3941
                                  body_charset, encoding, is_draft) < 0) {
3942
                g_warning("can't write headers\n");
3943
                fclose(fp);
3944
                g_unlink(file);
3945
                g_free(buf);
3946
                return -1;
3947
        }
3948

    
3949
#if USE_GPGME
3950
        /* do ascii-armor encryption and/or clearsign */
3951
        if (rfc2015_is_available() && !is_draft) {
3952
                gint ret;
3953

    
3954
                if (compose->use_encryption && compose->account->ascii_armored) {
3955
                        if (compose->use_signing)
3956
                                ret = compose_encrypt_sign_armored(compose, &buf);
3957
                        else
3958
                                ret = compose_encrypt_armored(compose, &buf);
3959
                        if (ret < 0) {
3960
                                g_warning("ascii-armored encryption failed\n");
3961
                                fclose(fp);
3962
                                g_unlink(file);
3963
                                g_free(buf);
3964
                                return -1;
3965
                        }
3966
                } else if (compose->use_signing && compose->account->clearsign) {
3967
                        if (compose_clearsign_text(compose, &buf) < 0) {
3968
                                g_warning("clearsign failed\n");
3969
                                fclose(fp);
3970
                                g_unlink(file);
3971
                                g_free(buf);
3972
                                return -1;
3973
                        }
3974
                }
3975
        }
3976
#endif
3977

    
3978
        if (compose->use_attach &&
3979
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3980
#if USE_GPGME
3981
            /* This prolog message is ignored by mime software and
3982
             * because it would make our signing/encryption task
3983
             * tougher, we don't emit it in that case */
3984
            if (!rfc2015_is_available() ||
3985
                (!compose->use_signing && !compose->use_encryption))
3986
#endif
3987
                fputs("This is a multi-part message in MIME format.\n", fp);
3988

    
3989
                fprintf(fp, "\n--%s\n", compose->boundary);
3990
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
3991
                        body_charset);
3992
#if USE_GPGME
3993
                if (rfc2015_is_available() && use_pgpmime_signing)
3994
                        fprintf(fp, "Content-Disposition: inline\n");
3995
#endif
3996
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
3997
                        procmime_get_encoding_str(encoding));
3998
                fputc('\n', fp);
3999
        }
4000

    
4001
        /* write body */
4002
        len = strlen(buf);
4003
        if (encoding == ENC_BASE64) {
4004
                gchar outbuf[B64_BUFFSIZE];
4005
                gint i, l;
4006

    
4007
                for (i = 0; i < len; i += B64_LINE_SIZE) {
4008
                        l = MIN(B64_LINE_SIZE, len - i);
4009
                        base64_encode(outbuf, (guchar *)buf + i, l);
4010
                        fputs(outbuf, fp);
4011
                        fputc('\n', fp);
4012
                }
4013
        } else if (encoding == ENC_QUOTED_PRINTABLE) {
4014
                gchar *outbuf;
4015
                size_t outlen;
4016

    
4017
                outbuf = g_malloc(len * 4);
4018
                qp_encode_line(outbuf, (guchar *)buf);
4019
                outlen = strlen(outbuf);
4020
                if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
4021
                        FILE_OP_ERROR(file, "fwrite");
4022
                        fclose(fp);
4023
                        g_unlink(file);
4024
                        g_free(outbuf);
4025
                        g_free(buf);
4026
                        return -1;
4027
                }
4028
                g_free(outbuf);
4029
        } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
4030
                FILE_OP_ERROR(file, "fwrite");
4031
                fclose(fp);
4032
                g_unlink(file);
4033
                g_free(buf);
4034
                return -1;
4035
        }
4036
        g_free(buf);
4037

    
4038
        if (compose->use_attach &&
4039
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
4040
                if (compose_write_attach(compose, fp, out_charset) < 0) {
4041
                        fclose(fp);
4042
                        g_unlink(file);
4043
                        return -1;
4044
                }
4045
        }
4046

    
4047
        if (fclose(fp) == EOF) {
4048
                FILE_OP_ERROR(file, "fclose");
4049
                g_unlink(file);
4050
                return -1;
4051
        }
4052

    
4053
#if USE_GPGME
4054
        if (!rfc2015_is_available() || is_draft) {
4055
                uncanonicalize_file_replace(file);
4056
                return 0;
4057
        }
4058

    
4059
        if (use_pgpmime_signing || use_pgpmime_encryption) {
4060
                if (canonicalize_file_replace(file) < 0) {
4061
                        g_unlink(file);
4062
                        return -1;
4063
                }
4064
        }
4065

    
4066
        if (use_pgpmime_signing && !use_pgpmime_encryption) {
4067
                GSList *key_list;
4068

    
4069
                if (compose_create_signers_list(compose, &key_list) < 0) {
4070
                        g_unlink(file);
4071
                        return -1;
4072
                }
4073
                if (rfc2015_sign(file, key_list) < 0) {
4074
                        alertpanel_error(_("Can't sign the message."));
4075
                        g_unlink(file);
4076
                        return -1;
4077
                }
4078
        } else if (use_pgpmime_encryption) {
4079
                GSList *key_list;
4080

    
4081
                if (compose->use_bcc) {
4082
                        const gchar *text;
4083
                        gchar *bcc;
4084
                        AlertValue aval;
4085

    
4086
                        text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4087
                        if (*text != '\0') {
4088
                                bcc = g_strdup(text);
4089
                                g_strstrip(bcc);
4090
                                if (*bcc != '\0') {
4091
                                        aval = alertpanel_full
4092
                                                (_("Encrypting with Bcc"),
4093
                                                 _("This message has Bcc recipients. If this message is encrypted, all Bcc recipients will be visible by examing the encryption key list, leading to loss of confidentiality.\n"
4094
                                                   "\n"
4095
                                                   "Send it anyway?"),
4096
                                                 ALERT_WARNING, G_ALERTDEFAULT, FALSE,
4097
                                                 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
4098
                                        if (aval != G_ALERTDEFAULT) {
4099
                                                g_free(bcc);
4100
                                                g_unlink(file);
4101
                                                return -1;
4102
                                        }
4103
                                }
4104
                                g_free(bcc);
4105
                        }
4106
                }
4107
                if (use_pgpmime_signing) {
4108
                        if (compose_create_signers_list
4109
                                (compose, &key_list) < 0) {
4110
                                g_unlink(file);
4111
                                return -1;
4112
                        }
4113
                        if (rfc2015_encrypt_sign(file, compose->to_list,
4114
                                                 key_list) < 0) {
4115
                                alertpanel_error(_("Can't encrypt or sign the message."));
4116
                                g_unlink(file);
4117
                                return -1;
4118
                        }
4119
                } else if (rfc2015_encrypt(file, compose->to_list) < 0) {
4120
                        alertpanel_error(_("Can't encrypt the message."));
4121
                        g_unlink(file);
4122
                        return -1;
4123
                }
4124
        }
4125
#endif /* USE_GPGME */
4126

    
4127
        uncanonicalize_file_replace(file);
4128

    
4129
        return 0;
4130
}
4131

    
4132
static gint compose_write_body_to_file(Compose *compose, const gchar *file)
4133
{
4134
        GtkTextBuffer *buffer;
4135
        GtkTextIter start, end;
4136
        FILE *fp;
4137
        size_t len;
4138
        gchar *chars, *tmp;
4139

    
4140
        if ((fp = g_fopen(file, "wb")) == NULL) {
4141
                FILE_OP_ERROR(file, "fopen");
4142
                return -1;
4143
        }
4144

    
4145
        /* chmod for security */
4146
        if (change_file_mode_rw(fp, file) < 0) {
4147
                FILE_OP_ERROR(file, "chmod");
4148
                g_warning(_("can't change file mode\n"));
4149
        }
4150

    
4151
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4152
        gtk_text_buffer_get_start_iter(buffer, &start);
4153
        gtk_text_buffer_get_end_iter(buffer, &end);
4154
        tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4155

    
4156
        chars = conv_codeset_strdup
4157
                (tmp, CS_INTERNAL, conv_get_locale_charset_str());
4158

    
4159
        g_free(tmp);
4160

    
4161
        if (!chars) {
4162
                fclose(fp);
4163
                g_unlink(file);
4164
                return -1;
4165
        }
4166

    
4167
        /* write body */
4168
        len = strlen(chars);
4169
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
4170
                FILE_OP_ERROR(file, "fwrite");
4171
                g_free(chars);
4172
                fclose(fp);
4173
                g_unlink(file);
4174
                return -1;
4175
        }
4176

    
4177
        g_free(chars);
4178

    
4179
        if (fclose(fp) == EOF) {
4180
                FILE_OP_ERROR(file, "fclose");
4181
                g_unlink(file);
4182
                return -1;
4183
        }
4184
        return 0;
4185
}
4186

    
4187
static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
4188
{
4189
        FILE *fp;
4190
        FILE *fdest;
4191
        size_t len;
4192
        gchar buf[BUFFSIZE];
4193

    
4194
        g_return_val_if_fail(file != NULL, -1);
4195
        g_return_val_if_fail(compose->account != NULL, -1);
4196
        g_return_val_if_fail(compose->account->address != NULL, -1);
4197
        g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
4198
        g_return_val_if_fail(compose->targetinfo != NULL, -1);
4199

    
4200
        if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
4201
                return -1;
4202

    
4203
        if ((fdest = g_fopen(file, "wb")) == NULL) {
4204
                FILE_OP_ERROR(file, "fopen");
4205
                fclose(fp);
4206
                return -1;
4207
        }
4208

    
4209
        if (change_file_mode_rw(fdest, file) < 0) {
4210
                FILE_OP_ERROR(file, "chmod");
4211
                g_warning(_("can't change file mode\n"));
4212
        }
4213

    
4214
        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
4215
                if (g_ascii_strncasecmp(buf, "Return-Path:",
4216
                                        strlen("Return-Path:")) == 0 ||
4217
                    g_ascii_strncasecmp(buf, "Delivered-To:",
4218
                                        strlen("Delivered-To:")) == 0 ||
4219
                    g_ascii_strncasecmp(buf, "Received:",
4220
                                        strlen("Received:")) == 0 ||
4221
                    g_ascii_strncasecmp(buf, "Subject:",
4222
                                        strlen("Subject:")) == 0 ||
4223
                    g_ascii_strncasecmp(buf, "X-UIDL:",
4224
                                        strlen("X-UIDL:")) == 0)
4225
                        continue;
4226

    
4227
                if (fputs(buf, fdest) == EOF)
4228
                        goto error;
4229

    
4230
#if 0
4231
                if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) {
4232
                        fputs("\n (by way of ", fdest);
4233
                        if (compose->account->name) {
4234
                                compose_convert_header(compose,
4235
                                                       buf, sizeof(buf),
4236
                                                       compose->account->name,
4237
                                                       strlen(" (by way of "),
4238
                                                       FALSE, NULL);
4239
                                fprintf(fdest, "%s <%s>", buf,
4240
                                        compose->account->address);
4241
                        } else
4242
                                fputs(compose->account->address, fdest);
4243
                        fputs(")", fdest);
4244
                }
4245
#endif
4246

    
4247
                if (fputs("\n", fdest) == EOF)
4248
                        goto error;
4249
        }
4250

    
4251
        compose_redirect_write_headers(compose, fdest);
4252

    
4253
        while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4254
                if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
4255
                        FILE_OP_ERROR(file, "fwrite");
4256
                        goto error;
4257
                }
4258
        }
4259

    
4260
        fclose(fp);
4261
        if (fclose(fdest) == EOF) {
4262
                FILE_OP_ERROR(file, "fclose");
4263
                g_unlink(file);
4264
                return -1;
4265
        }
4266

    
4267
        return 0;
4268
error:
4269
        fclose(fp);
4270
        fclose(fdest);
4271
        g_unlink(file);
4272

    
4273
        return -1;
4274
}
4275

    
4276
static gint compose_remove_reedit_target(Compose *compose)
4277
{
4278
        FolderItem *item;
4279
        MsgInfo *msginfo = compose->targetinfo;
4280

    
4281
        g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4282
        if (!msginfo) return -1;
4283

    
4284
        item = msginfo->folder;
4285
        g_return_val_if_fail(item != NULL, -1);
4286

    
4287
        folder_item_scan(item);
4288
        if (procmsg_msg_exist(msginfo) &&
4289
            (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
4290
                if (folder_item_remove_msg(item, msginfo) < 0) {
4291
                        g_warning(_("can't remove the old message\n"));
4292
                        return -1;
4293
                }
4294
        }
4295

    
4296
        return 0;
4297
}
4298

    
4299
static gint compose_queue(Compose *compose, const gchar *file)
4300
{
4301
        FolderItem *queue;
4302
        gchar *tmp;
4303
        FILE *fp, *src_fp;
4304
        GSList *cur;
4305
        gchar buf[BUFFSIZE];
4306
        gint num;
4307
        MsgFlags flag = {0, MSG_QUEUED};
4308

    
4309
        debug_print(_("queueing message...\n"));
4310
        g_return_val_if_fail(compose->to_list != NULL ||
4311
                             compose->newsgroup_list != NULL,
4312
                             -1);
4313
        g_return_val_if_fail(compose->account != NULL, -1);
4314

    
4315
        tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4316
                              G_DIR_SEPARATOR, compose);
4317
        if ((fp = g_fopen(tmp, "wb")) == NULL) {
4318
                FILE_OP_ERROR(tmp, "fopen");
4319
                g_free(tmp);
4320
                return -1;
4321
        }
4322
        if ((src_fp = g_fopen(file, "rb")) == NULL) {
4323
                FILE_OP_ERROR(file, "fopen");
4324
                fclose(fp);
4325
                g_unlink(tmp);
4326
                g_free(tmp);
4327
                return -1;
4328
        }
4329
        if (change_file_mode_rw(fp, tmp) < 0) {
4330
                FILE_OP_ERROR(tmp, "chmod");
4331
                g_warning(_("can't change file mode\n"));
4332
        }
4333

    
4334
        /* queueing variables */
4335
        fprintf(fp, "AF:\n");
4336
        fprintf(fp, "NF:0\n");
4337
        fprintf(fp, "PS:10\n");
4338
        fprintf(fp, "SRH:1\n");
4339
        fprintf(fp, "SFN:\n");
4340
        fprintf(fp, "DSR:\n");
4341
        if (compose->msgid)
4342
                fprintf(fp, "MID:<%s>\n", compose->msgid);
4343
        else
4344
                fprintf(fp, "MID:\n");
4345
        fprintf(fp, "CFG:\n");
4346
        fprintf(fp, "PT:0\n");
4347
        fprintf(fp, "S:%s\n", compose->account->address);
4348
        fprintf(fp, "RQ:\n");
4349
        if (compose->account->smtp_server)
4350
                fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
4351
        else
4352
                fprintf(fp, "SSV:\n");
4353
        if (compose->account->nntp_server)
4354
                fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
4355
        else
4356
                fprintf(fp, "NSV:\n");
4357
        fprintf(fp, "SSH:\n");
4358
        if (compose->to_list) {
4359
                fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4360
                for (cur = compose->to_list->next; cur != NULL;
4361
                     cur = cur->next)
4362
                        fprintf(fp, ",<%s>", (gchar *)cur->data);
4363
                fprintf(fp, "\n");
4364
        } else
4365
                fprintf(fp, "R:\n");
4366
        /* Sylpheed account ID */
4367
        fprintf(fp, "AID:%d\n", compose->account->account_id);
4368
        /* Reply target */
4369
        if (compose->reply_target)
4370
                fprintf(fp, "REP:%s\n", compose->reply_target);
4371
        /* Forward target */
4372
        if (compose->forward_targets)
4373
                fprintf(fp, "FWD:%s\n", compose->forward_targets);
4374
        fprintf(fp, "\n");
4375

    
4376
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4377
                if (fputs(buf, fp) == EOF) {
4378
                        FILE_OP_ERROR(tmp, "fputs");
4379
                        fclose(fp);
4380
                        fclose(src_fp);
4381
                        g_unlink(tmp);
4382
                        g_free(tmp);
4383
                        return -1;
4384
                }
4385
        }
4386

    
4387
        fclose(src_fp);
4388
        if (fclose(fp) == EOF) {
4389
                FILE_OP_ERROR(tmp, "fclose");
4390
                g_unlink(tmp);
4391
                g_free(tmp);
4392
                return -1;
4393
        }
4394

    
4395
        queue = account_get_special_folder(compose->account, F_QUEUE);
4396
        if (!queue) {
4397
                g_warning(_("can't find queue folder\n"));
4398
                g_unlink(tmp);
4399
                g_free(tmp);
4400
                return -1;
4401
        }
4402
        folder_item_scan(queue);
4403
        if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
4404
                g_warning(_("can't queue the message\n"));
4405
                g_unlink(tmp);
4406
                g_free(tmp);
4407
                return -1;
4408
        }
4409
        g_free(tmp);
4410

    
4411
        if (compose->mode == COMPOSE_REEDIT) {
4412
                compose_remove_reedit_target(compose);
4413
                if (compose->targetinfo &&
4414
                    compose->targetinfo->folder != queue)
4415
                        folderview_update_item
4416
                                (compose->targetinfo->folder, TRUE);
4417
        }
4418

    
4419
        folder_item_scan(queue);
4420
        folderview_update_item(queue, TRUE);
4421

    
4422
        /* Add recipients to addressbook automatically */
4423
        if (prefs_common.recipients_autoreg) {
4424
                compose_add_new_recipients_to_addressbook(compose);
4425
        }
4426

    
4427
        main_window_set_menu_sensitive(main_window_get());
4428
        main_window_set_toolbar_sensitive(main_window_get());
4429

    
4430
        return 0;
4431
}
4432

    
4433
static gint compose_write_attach(Compose *compose, FILE *fp,
4434
                                 const gchar *charset)
4435
{
4436
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
4437
        GtkTreeIter iter;
4438
        gboolean valid;
4439
        AttachInfo *ainfo;
4440
        FILE *attach_fp;
4441
        gint len;
4442
        EncodingType encoding;
4443

    
4444
        for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
4445
             valid = gtk_tree_model_iter_next(model, &iter)) {
4446
                gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
4447

    
4448
                if (!is_file_exist(ainfo->file)) {
4449
                        alertpanel_error(_("File %s doesn't exist."),
4450
                                         ainfo->file);
4451
                        return -1;
4452
                }
4453
                if (get_file_size(ainfo->file) <= 0) {
4454
                        alertpanel_error(_("File %s is empty."), ainfo->file);
4455
                        return -1;
4456
                }
4457
                if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) {
4458
                        alertpanel_error(_("Can't open file %s."), ainfo->file);
4459
                        return -1;
4460
                }
4461

    
4462
                fprintf(fp, "\n--%s\n", compose->boundary);
4463

    
4464
                encoding = ainfo->encoding;
4465

    
4466
                if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) {
4467
                        fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
4468
                        fprintf(fp, "Content-Disposition: inline\n");
4469

    
4470
                        /* message/... shouldn't be encoded */
4471
                        if (encoding == ENC_QUOTED_PRINTABLE ||
4472
                            encoding == ENC_BASE64)
4473
                                encoding = ENC_8BIT;
4474
                } else {
4475
                        if (prefs_common.mime_fencoding_method ==
4476
                            FENC_RFC2231) {
4477
                                gchar *param;
4478

    
4479
                                param = compose_convert_filename
4480
                                        (compose, ainfo->name, "name", charset);
4481
                                fprintf(fp, "Content-Type: %s;\n"
4482
                                            "%s\n",
4483
                                        ainfo->content_type, param);
4484
                                g_free(param);
4485
                                param = compose_convert_filename
4486
                                        (compose, ainfo->name, "filename",
4487
                                         charset);
4488
                                fprintf(fp, "Content-Disposition: attachment;\n"
4489
                                            "%s\n", param);
4490
                                g_free(param);
4491
                        } else {
4492
                                gchar filename[BUFFSIZE];
4493

    
4494
                                compose_convert_header(compose, filename,
4495
                                                       sizeof(filename),
4496
                                                       ainfo->name, 12, FALSE,
4497
                                                       charset);
4498
                                fprintf(fp, "Content-Type: %s;\n"
4499
                                            " name=\"%s\"\n",
4500
                                        ainfo->content_type, filename);
4501
                                fprintf(fp, "Content-Disposition: attachment;\n"
4502
                                            " filename=\"%s\"\n", filename);
4503
                        }
4504

    
4505
#if USE_GPGME
4506
                        /* force encoding to protect trailing spaces */
4507
                        if (rfc2015_is_available() && compose->use_signing &&
4508
                            !compose->account->clearsign) {
4509
                                if (encoding == ENC_7BIT)
4510
                                        encoding = ENC_QUOTED_PRINTABLE;
4511
                                else if (encoding == ENC_8BIT)
4512
                                        encoding = ENC_BASE64;
4513
                        }
4514
#endif
4515
                }
4516

    
4517
                fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
4518
                        procmime_get_encoding_str(encoding));
4519

    
4520
                if (encoding == ENC_BASE64) {
4521
                        gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
4522
                        FILE *tmp_fp = attach_fp;
4523
                        gchar *tmp_file = NULL;
4524
                        ContentType content_type;
4525

    
4526
                        content_type =
4527
                                procmime_scan_mime_type(ainfo->content_type);
4528
                        if (content_type == MIME_TEXT ||
4529
                            content_type == MIME_TEXT_HTML ||
4530
                            content_type == MIME_MESSAGE_RFC822) {
4531
                                tmp_file = get_tmp_file();
4532
                                if (canonicalize_file(ainfo->file, tmp_file) < 0) {
4533
                                        g_free(tmp_file);
4534
                                        fclose(attach_fp);
4535
                                        return -1;
4536
                                }
4537
                                if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
4538
                                        FILE_OP_ERROR(tmp_file, "fopen");
4539
                                        g_unlink(tmp_file);
4540
                                        g_free(tmp_file);
4541
                                        fclose(attach_fp);
4542
                                        return -1;
4543
                                }
4544
                        }
4545

    
4546
                        while ((len = fread(inbuf, sizeof(gchar),
4547
                                            B64_LINE_SIZE, tmp_fp))
4548
                               == B64_LINE_SIZE) {
4549
                                base64_encode(outbuf, (guchar *)inbuf,
4550
                                              B64_LINE_SIZE);
4551
                                fputs(outbuf, fp);
4552
                                fputc('\n', fp);
4553
                        }
4554
                        if (len > 0 && feof(tmp_fp)) {
4555
                                base64_encode(outbuf, (guchar *)inbuf, len);
4556
                                fputs(outbuf, fp);
4557
                                fputc('\n', fp);
4558
                        }
4559

    
4560
                        if (tmp_file) {
4561
                                fclose(tmp_fp);
4562
                                g_unlink(tmp_file);
4563
                                g_free(tmp_file);
4564
                        }
4565
                } else if (encoding == ENC_QUOTED_PRINTABLE) {
4566
                        gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
4567

    
4568
                        while (fgets(inbuf, sizeof(inbuf), attach_fp) != NULL) {
4569
                                qp_encode_line(outbuf, (guchar *)inbuf);
4570
                                fputs(outbuf, fp);
4571
                        }
4572
                } else {
4573
                        gchar buf[BUFFSIZE];
4574

    
4575
                        while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
4576
                                strcrchomp(buf);
4577
                                fputs(buf, fp);
4578
                        }
4579
                }
4580

    
4581
                fclose(attach_fp);
4582
        }
4583

    
4584
        fprintf(fp, "\n--%s--\n", compose->boundary);
4585
        return 0;
4586
}
4587

    
4588
#define QUOTE_REQUIRED(str) \
4589
        (*str != '"' && strpbrk(str, ",.[]<>") != NULL)
4590

    
4591
#define PUT_RECIPIENT_HEADER(header, str)                                     \
4592
{                                                                             \
4593
        if (*str != '\0') {                                                     \
4594
                gchar *dest;                                                     \
4595
                                                                             \
4596
                dest = g_strdup(str);                                             \
4597
                g_strstrip(dest);                                             \
4598
                if (*dest != '\0') {                                             \
4599
                        compose->to_list = address_list_append                     \
4600
                                (compose->to_list, dest);                     \
4601
                        compose_convert_header                                     \
4602
                                (compose, buf, sizeof(buf), dest,             \
4603
                                 strlen(header) + 2, TRUE, charset);             \
4604
                        fprintf(fp, "%s: %s\n", header, buf);                     \
4605
                }                                                             \
4606
                g_free(dest);                                                     \
4607
        }                                                                     \
4608
}
4609

    
4610
#define IS_IN_CUSTOM_HEADER(header) \
4611
        (compose->account->add_customhdr && \
4612
         custom_header_find(compose->account->customhdr_list, header) != NULL)
4613

    
4614
static gint compose_write_headers(Compose *compose, FILE *fp,
4615
                                  const gchar *charset,
4616
                                  const gchar *body_charset,
4617
                                  EncodingType encoding, gboolean is_draft)
4618
{
4619
        gchar buf[BUFFSIZE];
4620
        const gchar *entry_str;
4621
        gchar *str;
4622

    
4623
        g_return_val_if_fail(fp != NULL, -1);
4624
        g_return_val_if_fail(charset != NULL, -1);
4625
        g_return_val_if_fail(compose->account != NULL, -1);
4626
        g_return_val_if_fail(compose->account->address != NULL, -1);
4627

    
4628
        /* Date */
4629
        if (compose->account->add_date) {
4630
                get_rfc822_date(buf, sizeof(buf));
4631
                fprintf(fp, "Date: %s\n", buf);
4632
        }
4633

    
4634
        /* From */
4635
        if (compose->account->name && *compose->account->name) {
4636
                compose_convert_header
4637
                        (compose, buf, sizeof(buf), compose->account->name,
4638
                         strlen("From: "), TRUE, charset);
4639
                if (QUOTE_REQUIRED(buf))
4640
                        fprintf(fp, "From: \"%s\" <%s>\n",
4641
                                buf, compose->account->address);
4642
                else
4643
                        fprintf(fp, "From: %s <%s>\n",
4644
                                buf, compose->account->address);
4645
        } else
4646
                fprintf(fp, "From: %s\n", compose->account->address);
4647

    
4648
        slist_free_strings(compose->to_list);
4649
        g_slist_free(compose->to_list);
4650
        compose->to_list = NULL;
4651

    
4652
        /* To */
4653
        if (compose->use_to) {
4654
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4655
                PUT_RECIPIENT_HEADER("To", entry_str);
4656
        }
4657

    
4658
        slist_free_strings(compose->newsgroup_list);
4659
        g_slist_free(compose->newsgroup_list);
4660
        compose->newsgroup_list = NULL;
4661

    
4662
        /* Newsgroups */
4663
        if (compose->use_newsgroups) {
4664
                entry_str = gtk_entry_get_text
4665
                        (GTK_ENTRY(compose->newsgroups_entry));
4666
                if (*entry_str != '\0') {
4667
                        str = g_strdup(entry_str);
4668
                        g_strstrip(str);
4669
                        remove_space(str);
4670
                        if (*str != '\0') {
4671
                                compose->newsgroup_list =
4672
                                        newsgroup_list_append
4673
                                                (compose->newsgroup_list, str);
4674
                                compose_convert_header(compose,
4675
                                                       buf, sizeof(buf), str,
4676
                                                       strlen("Newsgroups: "),
4677
                                                       FALSE, charset);
4678
                                fprintf(fp, "Newsgroups: %s\n", buf);
4679
                        }
4680
                        g_free(str);
4681
                }
4682
        }
4683

    
4684
        /* Cc */
4685
        if (compose->use_cc) {
4686
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4687
                PUT_RECIPIENT_HEADER("Cc", entry_str);
4688
        }
4689

    
4690
        /* Bcc */
4691
        if (compose->use_bcc) {
4692
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4693
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4694
        }
4695

    
4696
        if (!is_draft && !compose->to_list && !compose->newsgroup_list)
4697
                return -1;
4698

    
4699
        /* Subject */
4700
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4701
        if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4702
                str = g_strdup(entry_str);
4703
                g_strstrip(str);
4704
                if (*str != '\0') {
4705
                        compose_convert_header(compose, buf, sizeof(buf), str,
4706
                                               strlen("Subject: "), FALSE,
4707
                                               charset);
4708
                        fprintf(fp, "Subject: %s\n", buf);
4709
                }
4710
                g_free(str);
4711
        }
4712

    
4713
        /* Message-ID */
4714
        if (compose->account->gen_msgid) {
4715
                compose_generate_msgid(compose, buf, sizeof(buf));
4716
                fprintf(fp, "Message-Id: <%s>\n", buf);
4717
                compose->msgid = g_strdup(buf);
4718
        }
4719

    
4720
        /* In-Reply-To */
4721
        if (compose->inreplyto && compose->to_list)
4722
                fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
4723

    
4724
        /* References */
4725
        if (compose->references)
4726
                fprintf(fp, "References: %s\n", compose->references);
4727

    
4728
        /* Followup-To */
4729
        if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
4730
                entry_str = gtk_entry_get_text
4731
                        (GTK_ENTRY(compose->followup_entry));
4732
                if (*entry_str != '\0') {
4733
                        str = g_strdup(entry_str);
4734
                        g_strstrip(str);
4735
                        remove_space(str);
4736
                        if (*str != '\0') {
4737
                                compose_convert_header(compose,
4738
                                                       buf, sizeof(buf), str,
4739
                                                       strlen("Followup-To: "),
4740
                                                       FALSE, charset);
4741
                                fprintf(fp, "Followup-To: %s\n", buf);
4742
                        }
4743
                        g_free(str);
4744
                }
4745
        }
4746

    
4747
        /* Reply-To */
4748
        if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
4749
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4750
                if (*entry_str != '\0') {
4751
                        str = g_strdup(entry_str);
4752
                        g_strstrip(str);
4753
                        if (*str != '\0') {
4754
                                compose_convert_header(compose,
4755