Statistics
| Revision:

root / src / compose.c @ 2886

History | View | Annotate | Download (219 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2011 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 <pango/pango-break.h>
68

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

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

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

    
139
#if USE_GPGME
140
#  include "rfc2015.h"
141
#endif
142

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

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

    
171
#define B64_LINE_SIZE                57
172
#define B64_BUFFSIZE                77
173

    
174
#define MAX_REFERENCES_LEN        999
175

    
176
#define TEXTVIEW_MARGIN                6
177

    
178
static GdkColor quote_color = {0, 0, 0, 0xbfff};
179

    
180
static GList *compose_list = NULL;
181

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

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

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

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

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

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

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

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

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

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

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

    
263
static gboolean compose_check_for_valid_recipient
264
                                                (Compose        *compose);
265
static gboolean compose_check_entries                (Compose        *compose);
266
static gboolean compose_check_attachments        (Compose        *compose);
267
static gboolean compose_check_recipients        (Compose        *compose);
268

    
269
static void compose_add_new_recipients_to_addressbook
270
                                                (Compose        *compose);
271

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

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

    
310
static void compose_attach_info_free                (AttachInfo        *ainfo);
311
static void compose_attach_remove_selected        (Compose        *compose);
312

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

    
326
static void compose_attach_open                        (Compose        *compose);
327

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

    
336
static void compose_undo_state_changed                (UndoMain        *undostruct,
337
                                                 gint                 undo_state,
338
                                                 gint                 redo_state,
339
                                                 gpointer         data);
340

    
341
static gint calc_cursor_xpos        (GtkTextView        *text,
342
                                 gint                 extra,
343
                                 gint                 char_width);
344

    
345
/* callback functions */
346

    
347
static gboolean compose_edit_size_alloc (GtkEditable        *widget,
348
                                         GtkAllocation        *allocation,
349
                                         GtkSHRuler        *shruler);
350

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

    
374
static gboolean toolbar_button_pressed        (GtkWidget        *widget,
375
                                         GdkEventButton        *event,
376
                                         gpointer         data);
377

    
378
static void account_activated                (GtkMenuItem        *menuitem,
379
                                         gpointer         data);
380

    
381
static void attach_selection_changed        (GtkTreeSelection        *selection,
382
                                         gpointer                 data);
383

    
384
static gboolean attach_button_pressed        (GtkWidget        *widget,
385
                                         GdkEventButton        *event,
386
                                         gpointer         data);
387
static gboolean attach_key_pressed        (GtkWidget        *widget,
388
                                         GdkEventKey        *event,
389
                                         gpointer         data);
390

    
391
static void compose_send_cb                (gpointer         data,
392
                                         guint                 action,
393
                                         GtkWidget        *widget);
394
static void compose_send_later_cb        (gpointer         data,
395
                                         guint                 action,
396
                                         GtkWidget        *widget);
397

    
398
static void compose_draft_cb                (gpointer         data,
399
                                         guint                 action,
400
                                         GtkWidget        *widget);
401

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

    
415
static void compose_close_cb                (gpointer         data,
416
                                         guint                 action,
417
                                         GtkWidget        *widget);
418

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

    
423
static void compose_address_cb                (gpointer         data,
424
                                         guint                 action,
425
                                         GtkWidget        *widget);
426
static void compose_template_activate_cb(GtkWidget        *widget,
427
                                         gpointer         data);
428

    
429
static void compose_ext_editor_cb        (gpointer         data,
430
                                         guint                 action,
431
                                         GtkWidget        *widget);
432

    
433
static gint compose_delete_cb                (GtkWidget        *widget,
434
                                         GdkEventAny        *event,
435
                                         gpointer         data);
436

    
437
static gint compose_window_state_cb        (GtkWidget                *widget,
438
                                         GdkEventWindowState        *event,
439
                                         gpointer                 data);
440

    
441
static void compose_undo_cb                (Compose        *compose);
442
static void compose_redo_cb                (Compose        *compose);
443
static void compose_cut_cb                (Compose        *compose);
444
static void compose_copy_cb                (Compose        *compose);
445
static void compose_paste_cb                (Compose        *compose);
446
static void compose_paste_as_quote_cb        (Compose        *compose);
447
static void compose_allsel_cb                (Compose        *compose);
448

    
449
static void compose_grab_focus_cb        (GtkWidget        *widget,
450
                                         Compose        *compose);
451

    
452
#if USE_GPGME
453
static void compose_signing_toggled        (GtkWidget        *widget,
454
                                         Compose        *compose);
455
static void compose_encrypt_toggled        (GtkWidget        *widget,
456
                                         Compose        *compose);
457
#endif
458

    
459
#if 0
460
static void compose_attach_toggled        (GtkWidget        *widget,
461
                                         Compose        *compose);
462
#endif
463

    
464
static void compose_buffer_changed_cb        (GtkTextBuffer        *textbuf,
465
                                         Compose        *compose);
466
static void compose_changed_cb                (GtkEditable        *editable,
467
                                         Compose        *compose);
468

    
469
static void compose_wrap_cb                (gpointer         data,
470
                                         guint                 action,
471
                                         GtkWidget        *widget);
472
static void compose_toggle_autowrap_cb        (gpointer         data,
473
                                         guint                 action,
474
                                         GtkWidget        *widget);
475

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

    
501
static void compose_toggle_mdn_cb        (gpointer         data,
502
                                         guint                 action,
503
                                         GtkWidget        *widget);
504

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

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

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

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

    
558
static void text_inserted                (GtkTextBuffer        *buffer,
559
                                         GtkTextIter        *iter,
560
                                         const gchar        *text,
561
                                         gint                 len,
562
                                         Compose        *compose);
563

    
564
static gboolean autosave_timeout        (gpointer         data);
565

    
566

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

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

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

    
630
#define ENC_ACTION(action) \
631
        NULL, compose_set_encoding_cb, action, \
632
        "/View/Character encoding/Automatic"
633

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

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

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

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

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

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

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

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

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

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

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

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

    
705
        {N_("/_View/Character _encoding/Korean (EUC-_KR)"),
706
         ENC_ACTION(C_EUC_KR)},
707
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
708

    
709
        {N_("/_View/Character _encoding/Thai (TIS-620)"),
710
         ENC_ACTION(C_TIS_620)},
711
        {N_("/_View/Character _encoding/Thai (Windows-874)"),
712
         ENC_ACTION(C_WINDOWS_874)},
713

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

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

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

    
738
        {N_("/_Help"),                        NULL, NULL, 0, "<Branch>"},
739
        {N_("/_Help/_About"),                NULL, about_show, 0, NULL}
740
};
741

    
742
enum
743
{
744
        DRAG_TYPE_RFC822,
745
        DRAG_TYPE_URI_LIST,
746

    
747
        N_DRAG_TYPES
748
};
749

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

    
756

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

    
765
        if (!account) account = cur_account;
766
        g_return_val_if_fail(account != NULL, NULL);
767

    
768
        compose = compose_create(account, COMPOSE_NEW);
769

    
770
        undo_block(compose->undostruct);
771

    
772
        if (prefs_common.auto_sig)
773
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
774

    
775
        text = GTK_TEXT_VIEW(compose->text);
776
        buffer = gtk_text_view_get_buffer(text);
777
        gtk_text_buffer_get_start_iter(buffer, &iter);
778
        gtk_text_buffer_place_cursor(buffer, &iter);
779

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

    
802
        if (attach_files) {
803
                gint i;
804
                gchar *file;
805

    
806
                for (i = 0; i < attach_files->len; i++) {
807
                        gchar *utf8file;
808

    
809
                        file = g_ptr_array_index(attach_files, i);
810
                        utf8file = conv_filename_to_utf8(file);
811
                        compose_attach_append(compose, file, utf8file, NULL);
812
                        g_free(utf8file);
813
                }
814
        }
815

    
816
        undo_unblock(compose->undostruct);
817

    
818
        compose_connect_changed_callbacks(compose);
819
        compose_set_title(compose);
820

    
821
        syl_plugin_signal_emit("compose-created", compose);
822

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

    
831
        return compose;
832
}
833

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

    
844
        g_return_if_fail(msginfo != NULL);
845

    
846
        if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
847
                quote = TRUE;
848

    
849
        if (msginfo->folder)
850
                account = account_find_from_item_property(msginfo->folder);
851
        if (!account && msginfo->to && prefs_common.reply_account_autosel) {
852
                gchar *to;
853
                Xstrdup_a(to, msginfo->to, return);
854
                extract_address(to);
855
                account = account_find_from_address(to);
856
        }
857
        if (!account && msginfo->folder && msginfo->folder->folder)
858
                account = msginfo->folder->folder->account;
859
        if (!account)
860
                account = cur_account;
861
        g_return_if_fail(account != NULL);
862

    
863
        compose = compose_create(account, COMPOSE_REPLY);
864

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

    
878
        if (compose_parse_header(compose, msginfo) < 0) return;
879

    
880
        undo_block(compose->undostruct);
881

    
882
        compose_reply_set_entry(compose, msginfo, mode);
883
        if (item)
884
                compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
885

    
886
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
887

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

    
896
        if (quote) {
897
                gchar *qmark;
898
                gchar *quote_str;
899

    
900
                if (prefs_common.quotemark && *prefs_common.quotemark)
901
                        qmark = prefs_common.quotemark;
902
                else
903
                        qmark = "> ";
904

    
905
                quote_str = compose_quote_fmt(compose, replyinfo,
906
                                              prefs_common.quotefmt,
907
                                              qmark, body);
908
        }
909

    
910
        if (!account->sig_before_quote && prefs_common.auto_sig)
911
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
912

    
913
        if (quote && prefs_common.linewrap_quote)
914
                compose_wrap_all(compose);
915

    
916
        gtk_text_buffer_get_start_iter(buffer, &iter);
917
        gtk_text_buffer_place_cursor(buffer, &iter);
918

    
919
        gtk_widget_grab_focus(compose->text);
920

    
921
        undo_unblock(compose->undostruct);
922

    
923
        compose_connect_changed_callbacks(compose);
924
        compose_set_title(compose);
925

    
926
#if USE_GPGME
927
        if (rfc2015_is_available() && account->encrypt_reply &&
928
            MSG_IS_ENCRYPTED(replyinfo->flags)) {
929
                GtkItemFactory *ifactory;
930

    
931
                ifactory = gtk_item_factory_from_widget(compose->menubar);
932
                menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
933
        }
934
#endif
935

    
936
        procmsg_msginfo_free(replyinfo);
937

    
938
        syl_plugin_signal_emit("compose-created", compose);
939

    
940
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
941
                compose->autosave_tag =
942
                        g_timeout_add_full(G_PRIORITY_LOW,
943
                                           prefs_common.autosave_itv * 60 * 1000,
944
                                           autosave_timeout, compose, NULL);
945
        if (prefs_common.auto_exteditor)
946
                compose_exec_ext_editor(compose);
947
}
948

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

    
961
        g_return_if_fail(mlist != NULL);
962

    
963
        msginfo = (MsgInfo *)mlist->data;
964

    
965
        if (msginfo->folder)
966
                account = account_find_from_item(msginfo->folder);
967
        if (!account) account = cur_account;
968
        g_return_if_fail(account != NULL);
969

    
970
        compose = compose_create(account, COMPOSE_FORWARD);
971

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

    
994
        undo_block(compose->undostruct);
995

    
996
        compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
997
        if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
998
                compose_entry_append(compose, msginfo->subject,
999
                                     COMPOSE_ENTRY_SUBJECT);
1000
        if (item)
1001
                compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
1002

    
1003
        text = GTK_TEXT_VIEW(compose->text);
1004
        buffer = gtk_text_view_get_buffer(text);
1005

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

    
1014
        for (cur = mlist; cur != NULL; cur = cur->next) {
1015
                msginfo = (MsgInfo *)cur->data;
1016

    
1017
                if (as_attach) {
1018
                        gchar *msgfile;
1019

    
1020
                        msgfile = procmsg_get_message_file_path(msginfo);
1021
                        if (!is_file_exist(msgfile))
1022
                                g_warning(_("%s: file not exist\n"), msgfile);
1023
                        else
1024
                                compose_attach_append(compose, msgfile, msgfile,
1025
                                                      "message/rfc822");
1026

    
1027
                        g_free(msgfile);
1028
                } else {
1029
                        gchar *qmark;
1030
                        gchar *quote_str;
1031
                        MsgInfo *full_msginfo;
1032

    
1033
                        full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1034
                        if (!full_msginfo)
1035
                                full_msginfo = procmsg_msginfo_copy(msginfo);
1036

    
1037
                        if (cur != mlist) {
1038
                                GtkTextMark *mark;
1039
                                mark = gtk_text_buffer_get_insert(buffer);
1040
                                gtk_text_buffer_get_iter_at_mark
1041
                                        (buffer, &iter, mark);
1042
                                gtk_text_buffer_insert
1043
                                        (buffer, &iter, "\n\n", 2);
1044
                        }
1045

    
1046
                        if (prefs_common.fw_quotemark &&
1047
                            *prefs_common.fw_quotemark)
1048
                                qmark = prefs_common.fw_quotemark;
1049
                        else
1050
                                qmark = "> ";
1051

    
1052
                        quote_str = compose_quote_fmt(compose, full_msginfo,
1053
                                                      prefs_common.fw_quotefmt,
1054
                                                      qmark, body);
1055
                        compose_attach_parts(compose, msginfo);
1056

    
1057
                        procmsg_msginfo_free(full_msginfo);
1058

    
1059
                        if (body) break;
1060
                }
1061
        }
1062

    
1063
        if (!account->sig_before_quote && prefs_common.auto_sig)
1064
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1065

    
1066
        if (prefs_common.linewrap_quote)
1067
                compose_wrap_all(compose);
1068

    
1069
        gtk_text_buffer_get_start_iter(buffer, &iter);
1070
        gtk_text_buffer_place_cursor(buffer, &iter);
1071

    
1072
        undo_unblock(compose->undostruct);
1073

    
1074
        compose_connect_changed_callbacks(compose);
1075
        compose_set_title(compose);
1076

    
1077
        if (account->protocol != A_NNTP)
1078
                gtk_widget_grab_focus(compose->to_entry);
1079
        else
1080
                gtk_widget_grab_focus(compose->newsgroups_entry);
1081

    
1082
        syl_plugin_signal_emit("compose-created", compose);
1083

    
1084
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1085
                compose->autosave_tag =
1086
                        g_timeout_add_full(G_PRIORITY_LOW,
1087
                                           prefs_common.autosave_itv * 60 * 1000,
1088
                                           autosave_timeout, compose, NULL);
1089
        if (prefs_common.auto_exteditor)
1090
                compose_exec_ext_editor(compose);
1091
}
1092

    
1093
void compose_redirect(MsgInfo *msginfo, FolderItem *item)
1094
{
1095
        Compose *compose;
1096
        PrefsAccount *account;
1097
        GtkTextView *text;
1098
        GtkTextBuffer *buffer;
1099
        GtkTextMark *mark;
1100
        GtkTextIter iter;
1101
        FILE *fp;
1102
        gchar buf[BUFFSIZE];
1103

    
1104
        g_return_if_fail(msginfo != NULL);
1105
        g_return_if_fail(msginfo->folder != NULL);
1106

    
1107
        account = account_find_from_item(msginfo->folder);
1108
        if (!account) account = cur_account;
1109
        g_return_if_fail(account != NULL);
1110

    
1111
        compose = compose_create(account, COMPOSE_REDIRECT);
1112
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1113

    
1114
        if (compose_parse_header(compose, msginfo) < 0) return;
1115

    
1116
        undo_block(compose->undostruct);
1117

    
1118
        if (msginfo->subject)
1119
                compose_entry_set(compose, msginfo->subject,
1120
                                  COMPOSE_ENTRY_SUBJECT);
1121
        compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
1122

    
1123
        text = GTK_TEXT_VIEW(compose->text);
1124
        buffer = gtk_text_view_get_buffer(text);
1125
        mark = gtk_text_buffer_get_insert(buffer);
1126
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1127

    
1128
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1129
                g_warning(_("Can't get text part\n"));
1130
        else {
1131
                gboolean prev_autowrap = compose->autowrap;
1132

    
1133
                compose->autowrap = FALSE;
1134
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1135
                        strcrchomp(buf);
1136
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1137
                }
1138
                compose->autowrap = prev_autowrap;
1139
                fclose(fp);
1140
        }
1141
        compose_attach_parts(compose, msginfo);
1142

    
1143
        if (account->protocol != A_NNTP)
1144
                gtk_widget_grab_focus(compose->to_entry);
1145
        else
1146
                gtk_widget_grab_focus(compose->newsgroups_entry);
1147

    
1148
        gtk_text_view_set_editable(text, FALSE);
1149

    
1150
        undo_unblock(compose->undostruct);
1151

    
1152
        compose_connect_changed_callbacks(compose);
1153
        compose_set_title(compose);
1154

    
1155
        syl_plugin_signal_emit("compose-created", compose);
1156
}
1157

    
1158
void compose_reedit(MsgInfo *msginfo)
1159
{
1160
        Compose *compose;
1161
        PrefsAccount *account;
1162
        GtkTextView *text;
1163
        GtkTextBuffer *buffer;
1164
        GtkTextMark *mark;
1165
        GtkTextIter iter;
1166
        FILE *fp;
1167
        gchar buf[BUFFSIZE];
1168
        const gchar *str;
1169
        GtkWidget *focus_widget = NULL;
1170
        GtkItemFactory *ifactory;
1171

    
1172
        g_return_if_fail(msginfo != NULL);
1173
        g_return_if_fail(msginfo->folder != NULL);
1174

    
1175
        account = account_find_from_msginfo(msginfo);
1176
        if (!account) account = cur_account;
1177
        g_return_if_fail(account != NULL);
1178

    
1179
        if (msginfo->folder->stype == F_DRAFT ||
1180
            msginfo->folder->stype == F_QUEUE) {
1181
                compose = compose_find_window_by_target(msginfo);
1182
                if (compose) {
1183
                        debug_print
1184
                                ("compose_reedit(): existing window found.\n");
1185
                        gtk_window_present(GTK_WINDOW(compose->window));
1186
                        return;
1187
                }
1188
        }
1189

    
1190
        compose = compose_create(account, COMPOSE_REEDIT);
1191
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1192

    
1193
        if (compose_parse_header(compose, msginfo) < 0) return;
1194
        compose_parse_source_msg(compose, msginfo);
1195

    
1196
        undo_block(compose->undostruct);
1197

    
1198
        compose_reedit_set_entry(compose, msginfo);
1199

    
1200
        text = GTK_TEXT_VIEW(compose->text);
1201
        buffer = gtk_text_view_get_buffer(text);
1202
        mark = gtk_text_buffer_get_insert(buffer);
1203
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1204

    
1205
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1206
                g_warning(_("Can't get text part\n"));
1207
        else {
1208
                gboolean prev_autowrap = compose->autowrap;
1209

    
1210
                compose->autowrap = FALSE;
1211
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1212
                        strcrchomp(buf);
1213
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1214
                }
1215
                compose_enable_sig(compose);
1216
                compose->autowrap = prev_autowrap;
1217
                fclose(fp);
1218
        }
1219
        compose_attach_parts(compose, msginfo);
1220

    
1221
        gtk_text_buffer_get_start_iter(buffer, &iter);
1222
        gtk_text_buffer_place_cursor(buffer, &iter);
1223

    
1224
        if (account->protocol != A_NNTP) {
1225
                str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
1226
                if (!str || *str == '\0')
1227
                        focus_widget = compose->to_entry;
1228
        } else {
1229
                str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
1230
                if (!str || *str == '\0')
1231
                        focus_widget = compose->newsgroups_entry;
1232
        }
1233
        if (!focus_widget) {
1234
                str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
1235
                if (!str || *str == '\0')
1236
                        focus_widget = compose->subject_entry;
1237
        }
1238
        if (focus_widget)
1239
                gtk_widget_grab_focus(focus_widget);
1240
        else
1241
                gtk_widget_grab_focus(compose->text);
1242

    
1243
        undo_unblock(compose->undostruct);
1244

    
1245
        compose_connect_changed_callbacks(compose);
1246
        compose_set_title(compose);
1247

    
1248
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1249
        if (compose->use_mdn) {
1250
                menu_set_active(ifactory, "/Tools/Request disposition notification", TRUE);
1251
        }
1252
        menu_set_active(ifactory, "/Edit/Auto wrapping", compose->autowrap);
1253
#if USE_GTKSPELL
1254
        menu_set_active(ifactory, "/Tools/Check spell", compose->check_spell);
1255
        compose_change_spell_lang_menu(compose, compose->spell_lang);
1256
#endif
1257
#if USE_GPGME
1258
        menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing);
1259
        menu_set_active(ifactory, "/Tools/PGP Encrypt", compose->use_encryption);
1260
#endif
1261

    
1262
        syl_plugin_signal_emit("compose-created", compose);
1263

    
1264
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1265
                compose->autosave_tag =
1266
                        g_timeout_add_full(G_PRIORITY_LOW,
1267
                                           prefs_common.autosave_itv * 60 * 1000,
1268
                                           autosave_timeout, compose, NULL);
1269
        if (prefs_common.auto_exteditor)
1270
                compose_exec_ext_editor(compose);
1271
}
1272

    
1273
GList *compose_get_compose_list(void)
1274
{
1275
        return compose_list;
1276
}
1277

    
1278
static void compose_entry_show(Compose *compose, ComposeEntryType type)
1279
{
1280
        GtkItemFactory *ifactory;
1281

    
1282
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1283

    
1284
        switch (type) {
1285
        case COMPOSE_ENTRY_CC:
1286
                menu_set_active(ifactory, "/View/Cc", TRUE);
1287
                break;
1288
        case COMPOSE_ENTRY_BCC:
1289
                menu_set_active(ifactory, "/View/Bcc", TRUE);
1290
                break;
1291
        case COMPOSE_ENTRY_REPLY_TO:
1292
                menu_set_active(ifactory, "/View/Reply-To", TRUE);
1293
                break;
1294
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1295
                menu_set_active(ifactory, "/View/Followup-To", TRUE);
1296
                break;
1297
        case COMPOSE_ENTRY_TO:
1298
                menu_set_active(ifactory, "/View/To", TRUE);
1299
                break;
1300
        default:
1301
                break;
1302
        }
1303
}
1304

    
1305
static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
1306
{
1307
        GtkEntry *entry;
1308

    
1309
        switch (type) {
1310
        case COMPOSE_ENTRY_CC:
1311
                entry = GTK_ENTRY(compose->cc_entry);
1312
                break;
1313
        case COMPOSE_ENTRY_BCC:
1314
                entry = GTK_ENTRY(compose->bcc_entry);
1315
                break;
1316
        case COMPOSE_ENTRY_REPLY_TO:
1317
                entry = GTK_ENTRY(compose->reply_entry);
1318
                break;
1319
        case COMPOSE_ENTRY_SUBJECT:
1320
                entry = GTK_ENTRY(compose->subject_entry);
1321
                break;
1322
        case COMPOSE_ENTRY_NEWSGROUPS:
1323
                entry = GTK_ENTRY(compose->newsgroups_entry);
1324
                break;
1325
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1326
                entry = GTK_ENTRY(compose->followup_entry);
1327
                break;
1328
        case COMPOSE_ENTRY_TO:
1329
        default:
1330
                entry = GTK_ENTRY(compose->to_entry);
1331
                break;
1332
        }
1333

    
1334
        return entry;
1335
}
1336

    
1337
void compose_entry_set(Compose *compose, const gchar *text,
1338
                       ComposeEntryType type)
1339
{
1340
        GtkEntry *entry;
1341

    
1342
        if (!text) return;
1343

    
1344
        compose_entry_show(compose, type);
1345
        entry = compose_get_entry(compose, type);
1346

    
1347
        gtk_entry_set_text(entry, text);
1348
}
1349

    
1350
void compose_entry_append(Compose *compose, const gchar *text,
1351
                          ComposeEntryType type)
1352
{
1353
        GtkEntry *entry;
1354
        const gchar *str;
1355
        gint pos;
1356

    
1357
        if (!text || *text == '\0') return;
1358

    
1359
        compose_entry_show(compose, type);
1360
        entry = compose_get_entry(compose, type);
1361

    
1362
        if (type != COMPOSE_ENTRY_SUBJECT) {
1363
                str = gtk_entry_get_text(entry);
1364
                if (*str != '\0') {
1365
                        pos = entry->text_length;
1366
                        gtk_editable_insert_text(GTK_EDITABLE(entry),
1367
                                                 ", ", -1, &pos);
1368
                }
1369
        }
1370

    
1371
        pos = entry->text_length;
1372
        gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1373
}
1374

    
1375
gchar *compose_entry_get_text(Compose *compose, ComposeEntryType type)
1376
{
1377
        GtkEntry *entry;
1378

    
1379
        entry = compose_get_entry(compose, type);
1380
        return gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1381
}
1382

    
1383
static void compose_entries_set(Compose *compose, const gchar *mailto)
1384
{
1385
        gchar *to = NULL;
1386
        gchar *cc = NULL;
1387
        gchar *subject = NULL;
1388
        gchar *inreplyto = NULL;
1389
        gchar *body = NULL;
1390

    
1391
        scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body);
1392

    
1393
        if (to)
1394
                compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1395
        if (cc)
1396
                compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1397
        if (subject)
1398
                compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1399
        if (inreplyto) {
1400
                if (strchr(inreplyto, '<'))
1401
                        extract_parenthesis(inreplyto, '<', '>');
1402
                remove_space(inreplyto);
1403
                compose->inreplyto = g_strdup(inreplyto);
1404
        }
1405
        if (body) {
1406
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1407
                GtkTextBuffer *buffer;
1408
                GtkTextMark *mark;
1409
                GtkTextIter iter;
1410
                gboolean prev_autowrap = compose->autowrap;
1411

    
1412
                compose->autowrap = FALSE;
1413

    
1414
                buffer = gtk_text_view_get_buffer(text);
1415
                mark = gtk_text_buffer_get_insert(buffer);
1416
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1417

    
1418
                gtk_text_buffer_insert(buffer, &iter, body, -1);
1419
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1420

    
1421
                compose->autowrap = prev_autowrap;
1422
                if (compose->autowrap)
1423
                        compose_wrap_all(compose);
1424
        }
1425

    
1426
        g_free(to);
1427
        g_free(cc);
1428
        g_free(subject);
1429
        g_free(inreplyto);
1430
        g_free(body);
1431
}
1432

    
1433
static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1434
                                          ComposeMode mode)
1435
{
1436
        g_return_if_fail(item != NULL);
1437

    
1438
        if (item->auto_to) {
1439
                if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1440
                        compose_entry_set(compose, item->auto_to,
1441
                                          COMPOSE_ENTRY_TO);
1442
        }
1443
        if (item->auto_cc)
1444
                compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1445
        if (item->auto_bcc)
1446
                compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1447
        if (item->auto_replyto)
1448
                compose_entry_set(compose, item->auto_replyto,
1449
                                  COMPOSE_ENTRY_REPLY_TO);
1450
}
1451

    
1452
#undef ACTIVATE_MENU
1453

    
1454
static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1455
{
1456
        static HeaderEntry hentry[] = {{"Reply-To:",        NULL, TRUE},
1457
                                       {"Cc:",                NULL, TRUE},
1458
                                       {"References:",        NULL, FALSE},
1459
                                       {"Bcc:",                NULL, TRUE},
1460
                                       {"Newsgroups:",  NULL, TRUE},
1461
                                       {"Followup-To:", NULL, TRUE},
1462
                                       {"List-Post:",   NULL, FALSE},
1463
                                       {"Content-Type:",NULL, FALSE},
1464
                                       {NULL,                NULL, FALSE}};
1465

    
1466
        enum
1467
        {
1468
                H_REPLY_TO        = 0,
1469
                H_CC                = 1,
1470
                H_REFERENCES        = 2,
1471
                H_BCC                = 3,
1472
                H_NEWSGROUPS    = 4,
1473
                H_FOLLOWUP_TO        = 5,
1474
                H_LIST_POST     = 6,
1475
                H_CONTENT_TYPE  = 7
1476
        };
1477

    
1478
        FILE *fp;
1479
        gchar *charset = NULL;
1480

    
1481
        g_return_val_if_fail(msginfo != NULL, -1);
1482

    
1483
        if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1484
        procheader_get_header_fields(fp, hentry);
1485
        fclose(fp);
1486

    
1487
        if (hentry[H_CONTENT_TYPE].body != NULL) {
1488
                procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body,
1489
                                               NULL, &charset, NULL, NULL);
1490
                g_free(hentry[H_CONTENT_TYPE].body);
1491
                hentry[H_CONTENT_TYPE].body = NULL;
1492
        }
1493
        if (hentry[H_REPLY_TO].body != NULL) {
1494
                if (hentry[H_REPLY_TO].body[0] != '\0') {
1495
                        compose->replyto =
1496
                                conv_unmime_header(hentry[H_REPLY_TO].body,
1497
                                                   charset);
1498
                }
1499
                g_free(hentry[H_REPLY_TO].body);
1500
                hentry[H_REPLY_TO].body = NULL;
1501
        }
1502
        if (hentry[H_CC].body != NULL) {
1503
                compose->cc = conv_unmime_header(hentry[H_CC].body, charset);
1504
                g_free(hentry[H_CC].body);
1505
                hentry[H_CC].body = NULL;
1506
        }
1507
        if (hentry[H_REFERENCES].body != NULL) {
1508
                if (compose->mode == COMPOSE_REEDIT)
1509
                        compose->references = hentry[H_REFERENCES].body;
1510
                else {
1511
                        compose->references = compose_parse_references
1512
                                (hentry[H_REFERENCES].body, msginfo->msgid);
1513
                        g_free(hentry[H_REFERENCES].body);
1514
                }
1515
                hentry[H_REFERENCES].body = NULL;
1516
        }
1517
        if (hentry[H_BCC].body != NULL) {
1518
                if (compose->mode == COMPOSE_REEDIT)
1519
                        compose->bcc =
1520
                                conv_unmime_header(hentry[H_BCC].body, charset);
1521
                g_free(hentry[H_BCC].body);
1522
                hentry[H_BCC].body = NULL;
1523
        }
1524
        if (hentry[H_NEWSGROUPS].body != NULL) {
1525
                compose->newsgroups = hentry[H_NEWSGROUPS].body;
1526
                hentry[H_NEWSGROUPS].body = NULL;
1527
        }
1528
        if (hentry[H_FOLLOWUP_TO].body != NULL) {
1529
                if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1530
                        compose->followup_to =
1531
                                conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1532
                                                   charset);
1533
                }
1534
                g_free(hentry[H_FOLLOWUP_TO].body);
1535
                hentry[H_FOLLOWUP_TO].body = NULL;
1536
        }
1537
        if (hentry[H_LIST_POST].body != NULL) {
1538
                gchar *to = NULL;
1539

    
1540
                extract_address(hentry[H_LIST_POST].body);
1541
                if (hentry[H_LIST_POST].body[0] != '\0') {
1542
                        scan_mailto_url(hentry[H_LIST_POST].body,
1543
                                        &to, NULL, NULL, NULL, NULL, NULL);
1544
                        if (to) {
1545
                                g_free(compose->ml_post);
1546
                                compose->ml_post = to;
1547
                        }
1548
                }
1549
                g_free(hentry[H_LIST_POST].body);
1550
                hentry[H_LIST_POST].body = NULL;
1551
        }
1552

    
1553
        g_free(charset);
1554

    
1555
        if (compose->mode == COMPOSE_REEDIT) {
1556
                if (msginfo->inreplyto && *msginfo->inreplyto)
1557
                        compose->inreplyto = g_strdup(msginfo->inreplyto);
1558
                return 0;
1559
        }
1560

    
1561
        if (msginfo->msgid && *msginfo->msgid)
1562
                compose->inreplyto = g_strdup(msginfo->msgid);
1563

    
1564
        if (!compose->references) {
1565
                if (msginfo->msgid && *msginfo->msgid) {
1566
                        if (msginfo->inreplyto && *msginfo->inreplyto)
1567
                                compose->references =
1568
                                        g_strdup_printf("<%s>\n\t<%s>",
1569
                                                        msginfo->inreplyto,
1570
                                                        msginfo->msgid);
1571
                        else
1572
                                compose->references =
1573
                                        g_strconcat("<", msginfo->msgid, ">",
1574
                                                    NULL);
1575
                } else if (msginfo->inreplyto && *msginfo->inreplyto) {
1576
                        compose->references =
1577
                                g_strconcat("<", msginfo->inreplyto, ">",
1578
                                            NULL);
1579
                }
1580
        }
1581

    
1582
        return 0;
1583
}
1584

    
1585
static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1586
{
1587
        GSList *ref_id_list, *cur;
1588
        GString *new_ref;
1589
        gchar *new_ref_str;
1590

    
1591
        ref_id_list = references_list_append(NULL, ref);
1592
        if (!ref_id_list) return NULL;
1593
        if (msgid && *msgid)
1594
                ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1595

    
1596
        for (;;) {
1597
                gint len = 0;
1598

    
1599
                for (cur = ref_id_list; cur != NULL; cur = cur->next)
1600
                        /* "<" + Message-ID + ">" + CR+LF+TAB */
1601
                        len += strlen((gchar *)cur->data) + 5;
1602

    
1603
                if (len > MAX_REFERENCES_LEN) {
1604
                        /* remove second message-ID */
1605
                        if (ref_id_list && ref_id_list->next &&
1606
                            ref_id_list->next->next) {
1607
                                g_free(ref_id_list->next->data);
1608
                                ref_id_list = g_slist_remove
1609
                                        (ref_id_list, ref_id_list->next->data);
1610
                        } else {
1611
                                slist_free_strings(ref_id_list);
1612
                                g_slist_free(ref_id_list);
1613
                                return NULL;
1614
                        }
1615
                } else
1616
                        break;
1617
        }
1618

    
1619
        new_ref = g_string_new("");
1620
        for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1621
                if (new_ref->len > 0)
1622
                        g_string_append(new_ref, "\n\t");
1623
                g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1624
        }
1625

    
1626
        slist_free_strings(ref_id_list);
1627
        g_slist_free(ref_id_list);
1628

    
1629
        new_ref_str = new_ref->str;
1630
        g_string_free(new_ref, FALSE);
1631

    
1632
        return new_ref_str;
1633
}
1634

    
1635
static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo)
1636
{
1637
        static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE},
1638
                                       {"X-Sylpheed-Forward:", NULL, FALSE},
1639
                                       {"REP:", NULL, FALSE},
1640
                                       {"FWD:", NULL, FALSE},
1641
                                       {"Disposition-Notification-To:", NULL, FALSE},
1642
                                       {"X-Sylpheed-Compose-AutoWrap:", NULL, FALSE},
1643
                                       {"X-Sylpheed-Compose-CheckSpell:", NULL, FALSE},
1644
                                       {"X-Sylpheed-Compose-SpellLang:", NULL, FALSE},
1645
                                       {"X-Sylpheed-Compose-UseSigning:", NULL, FALSE},
1646
                                       {"X-Sylpheed-Compose-UseEncryption:", NULL, FALSE},
1647
                                       {NULL, NULL, FALSE}};
1648

    
1649
        enum
1650
        {
1651
                H_X_SYLPHEED_REPLY = 0,
1652
                H_X_SYLPHEED_FORWARD = 1,
1653
                H_REP = 2,
1654
                H_FWD = 3,
1655
                H_MDN = 4,
1656
                H_X_SYLPHEED_COMPOSE_AUTOWRAP = 5,
1657
                H_X_SYLPHEED_COMPOSE_CHECKSPELL = 6,
1658
                H_X_SYLPHEED_COMPOSE_SPELLLANG = 7,
1659
                H_X_SYLPHEED_COMPOSE_USESIGNING = 8,
1660
                H_X_SYLPHEED_COMPOSE_USEENCRYPTION = 9
1661
        };
1662

    
1663
        gchar *file;
1664
        FILE *fp;
1665
        gchar *str;
1666
        gchar buf[BUFFSIZE];
1667
        gint hnum;
1668

    
1669
        g_return_val_if_fail(msginfo != NULL, -1);
1670

    
1671
        file = procmsg_get_message_file(msginfo);
1672

    
1673
        if ((fp = g_fopen(file, "rb")) == NULL) {
1674
                FILE_OP_ERROR(file, "fopen");
1675
                return -1;
1676
        }
1677

    
1678
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
1679
               != -1) {
1680
                str = buf + strlen(hentry[hnum].name);
1681
                while (g_ascii_isspace(*str))
1682
                        ++str;
1683
                if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) &&
1684
                    !compose->reply_target) {
1685
                        compose->reply_target = g_strdup(str);
1686
                } else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) &&
1687
                           !compose->forward_targets) {
1688
                        compose->forward_targets = g_strdup(str);
1689
                } else if (hnum == H_MDN) {
1690
                        compose->use_mdn = TRUE;
1691
                } else if (hnum == H_X_SYLPHEED_COMPOSE_AUTOWRAP) {
1692
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1693
                                compose->autowrap = TRUE;
1694
                        else
1695
                                compose->autowrap = FALSE;
1696
#if USE_GTKSPELL
1697
                } else if (hnum == H_X_SYLPHEED_COMPOSE_CHECKSPELL) {
1698
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1699
                                compose->check_spell = TRUE;
1700
                        else
1701
                                compose->check_spell = FALSE;
1702
                } else if (hnum == H_X_SYLPHEED_COMPOSE_SPELLLANG) {
1703
                        g_free(compose->spell_lang);
1704
                        compose->spell_lang = g_strdup(str);
1705
#endif
1706
#if USE_GPGME
1707
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USESIGNING) {
1708
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1709
                                compose->use_signing = TRUE;
1710
                        else
1711
                                compose->use_signing = FALSE;
1712
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USEENCRYPTION) {
1713
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1714
                                compose->use_encryption = TRUE;
1715
                        else
1716
                                compose->use_encryption = FALSE;
1717
#endif
1718
                }
1719
        }
1720

    
1721
        fclose(fp);
1722
        g_free(file);
1723

    
1724
        return 0;
1725
}
1726

    
1727
static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1728
                                const gchar *fmt, const gchar *qmark,
1729
                                const gchar *body)
1730
{
1731
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1732
        GtkTextBuffer *buffer;
1733
        GtkTextMark *mark;
1734
        GtkTextIter iter;
1735
        static MsgInfo dummyinfo;
1736
        gchar *quote_str = NULL;
1737
        gchar *buf;
1738
        gchar *p, *lastp;
1739
        gint len;
1740
        gboolean prev_autowrap;
1741

    
1742
        if (!msginfo)
1743
                msginfo = &dummyinfo;
1744

    
1745
        if (qmark != NULL) {
1746
                quote_fmt_init(msginfo, NULL, NULL);
1747
                quote_fmt_scan_string(qmark);
1748
                quote_fmt_parse();
1749

    
1750
                buf = quote_fmt_get_buffer();
1751
                if (buf == NULL)
1752
                        alertpanel_error(_("Quote mark format error."));
1753
                else
1754
                        Xstrdup_a(quote_str, buf, return NULL)
1755
        }
1756

    
1757
        if (fmt && *fmt != '\0') {
1758
                quote_fmt_init(msginfo, quote_str, body);
1759
                quote_fmt_scan_string(fmt);
1760
                quote_fmt_parse();
1761

    
1762
                buf = quote_fmt_get_buffer();
1763
                if (buf == NULL) {
1764
                        alertpanel_error(_("Message reply/forward format error."));
1765
                        return NULL;
1766
                }
1767
        } else
1768
                buf = "";
1769

    
1770
        prev_autowrap = compose->autowrap;
1771
        compose->autowrap = FALSE;
1772

    
1773
        buffer = gtk_text_view_get_buffer(text);
1774
        mark = gtk_text_buffer_get_insert(buffer);
1775
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1776

    
1777
        for (p = buf; *p != '\0'; ) {
1778
                lastp = strchr(p, '\n');
1779
                len = lastp ? lastp - p + 1 : -1;
1780
                gtk_text_buffer_insert(buffer, &iter, p, len);
1781
                if (lastp)
1782
                        p = lastp + 1;
1783
                else
1784
                        break;
1785
        }
1786

    
1787
        compose->autowrap = prev_autowrap;
1788
        if (compose->autowrap)
1789
                compose_wrap_all(compose);
1790

    
1791
        return buf;
1792
}
1793

    
1794
static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1795
                                    ComposeMode mode)
1796
{
1797
        GSList *cc_list = NULL;
1798
        GSList *cur;
1799
        GHashTable *to_table;
1800
        gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1801
        gchar *from_str = NULL, *to_str = NULL, *cc_str = NULL,
1802
              *replyto_str = NULL;
1803
        gboolean address_only = prefs_common.reply_address_only;
1804

    
1805
        g_return_if_fail(compose->account != NULL);
1806
        g_return_if_fail(msginfo != NULL);
1807

    
1808
        switch (COMPOSE_MODE(mode)) {
1809
        case COMPOSE_REPLY_TO_SENDER:
1810
                ignore_replyto = TRUE;
1811
                break;
1812
        case COMPOSE_REPLY_TO_ALL:
1813
                to_all = TRUE;
1814
                break;
1815
        case COMPOSE_REPLY_TO_LIST:
1816
                to_ml = TRUE;
1817
                break;
1818
        default:
1819
                break;
1820
        }
1821

    
1822
        if (address_only) {
1823
                from_str = extract_addresses(msginfo->from);
1824
                to_str = extract_addresses(msginfo->to);
1825
                cc_str = extract_addresses(compose->cc);
1826
                replyto_str = extract_addresses(compose->replyto);
1827
        } else {
1828
                from_str = g_strdup(msginfo->from);
1829
                to_str = g_strdup(msginfo->to);
1830
                cc_str = g_strdup(compose->cc);
1831
                replyto_str = g_strdup(compose->replyto);
1832
        }
1833

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

    
1888
        if (msginfo->subject && *msginfo->subject) {
1889
                gchar *buf;
1890
                gchar *p;
1891

    
1892
                buf = g_strdup(msginfo->subject);
1893

    
1894
                if (msginfo->folder && msginfo->folder->trim_compose_subject)
1895
                        trim_subject(buf);
1896

    
1897
                while (!g_ascii_strncasecmp(buf, "Re:", 3)) {
1898
                        p = buf + 3;
1899
                        while (g_ascii_isspace(*p)) p++;
1900
                        memmove(buf, p, strlen(p) + 1);
1901
                }
1902

    
1903
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1904
                compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1905

    
1906
                g_free(buf);
1907
        } else
1908
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1909

    
1910
        if (!compose->replyto && to_ml && compose->ml_post)
1911
                goto done;
1912
        if (!to_all)
1913
                goto done;
1914

    
1915
        if (replyto_str && from_str)
1916
                cc_list = address_list_append_orig(cc_list, from_str);
1917
        cc_list = address_list_append_orig(cc_list, to_str);
1918
        cc_list = address_list_append_orig(cc_list, cc_str);
1919

    
1920
        to_table = g_hash_table_new(g_str_hash, g_str_equal);
1921
        if (replyto_str) {
1922
                gchar *replyto;
1923

    
1924
                replyto = g_strdup(replyto_str);
1925
                extract_address(replyto);
1926
                g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1927
        } else if (from_str) {
1928
                gchar *from;
1929

    
1930
                from = g_strdup(from_str);
1931
                extract_address(from);
1932
                g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1933
        }
1934
        if (compose->account->address)
1935
                g_hash_table_insert(to_table,
1936
                                    g_strdup(compose->account->address),
1937
                                    GINT_TO_POINTER(1));
1938

    
1939
        /* remove duplicate addresses */
1940
        for (cur = cc_list; cur != NULL; ) {
1941
                gchar *addr = (gchar *)cur->data;
1942
                GSList *next = cur->next;
1943
                gchar *addr_;
1944

    
1945
                addr_ = g_strdup(addr);
1946
                extract_address(addr_);
1947
                if (g_hash_table_lookup(to_table, addr_) != NULL) {
1948
                        cc_list = g_slist_remove(cc_list, addr);
1949
                        g_free(addr_);
1950
                        g_free(addr);
1951
                } else
1952
                        g_hash_table_insert(to_table, addr_, cur);
1953

    
1954
                cur = next;
1955
        }
1956
        hash_free_strings(to_table);
1957
        g_hash_table_destroy(to_table);
1958

    
1959
        if (cc_list) {
1960
                for (cur = cc_list; cur != NULL; cur = cur->next)
1961
                        compose_entry_append(compose, (gchar *)cur->data,
1962
                                             COMPOSE_ENTRY_CC);
1963
                slist_free_strings(cc_list);
1964
                g_slist_free(cc_list);
1965
        }
1966

    
1967
done:
1968
        g_free(from_str);
1969
        g_free(to_str);
1970
        g_free(cc_str);
1971
        g_free(replyto_str);
1972
}
1973

    
1974
static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1975
{
1976
        g_return_if_fail(msginfo != NULL);
1977
        g_return_if_fail(compose->account != NULL);
1978

    
1979
        compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1980
        compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1981
        compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1982
        compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1983
        if (compose->account->protocol == A_NNTP) {
1984
                compose_entry_set(compose, compose->newsgroups,
1985
                                  COMPOSE_ENTRY_NEWSGROUPS);
1986
                compose_entry_set(compose, compose->followup_to,
1987
                                  COMPOSE_ENTRY_FOLLOWUP_TO);
1988
        }
1989
        compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1990
}
1991

    
1992
static void compose_insert_sig(Compose *compose, gboolean append,
1993
                               gboolean replace, gboolean scroll)
1994
{
1995
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1996
        GtkTextBuffer *buffer;
1997
        GtkTextMark *mark;
1998
        GtkTextIter iter;
1999
        gchar *sig_str;
2000
        gboolean prev_autowrap;
2001

    
2002
        g_return_if_fail(compose->account != NULL);
2003

    
2004
        prev_autowrap = compose->autowrap;
2005
        compose->autowrap = FALSE;
2006

    
2007
        buffer = gtk_text_view_get_buffer(text);
2008
        mark = gtk_text_buffer_get_insert(buffer);
2009
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2010

    
2011
        if (replace) {
2012
                GtkTextIter start_iter, end_iter;
2013

    
2014
                gtk_text_buffer_get_start_iter(buffer, &start_iter);
2015
                gtk_text_buffer_get_end_iter(buffer, &iter);
2016

    
2017
                while (gtk_text_iter_begins_tag
2018
                        (&start_iter, compose->sig_tag) ||
2019
                       gtk_text_iter_forward_to_tag_toggle
2020
                        (&start_iter, compose->sig_tag)) {
2021
                        end_iter = start_iter;
2022
                        if (gtk_text_iter_forward_to_tag_toggle
2023
                                (&end_iter, compose->sig_tag)) {
2024
                                gtk_text_buffer_delete
2025
                                        (buffer, &start_iter, &end_iter);
2026
                                iter = start_iter;
2027
                        }
2028
                }
2029
        }
2030

    
2031
        if (scroll) {
2032
                if (append)
2033
                        gtk_text_buffer_get_end_iter(buffer, &iter);
2034
                else
2035
                        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2036
        }
2037

    
2038
        sig_str = compose_get_signature_str(compose);
2039
        if (sig_str) {
2040
                if (!replace)
2041
                        gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
2042
                else if (!gtk_text_iter_starts_line(&iter))
2043
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2044
                gtk_text_buffer_insert_with_tags
2045
                        (buffer, &iter, sig_str, -1, compose->sig_tag, NULL);
2046
                g_free(sig_str);
2047
                if (scroll)
2048
                        gtk_text_buffer_place_cursor(buffer, &iter);
2049
        }
2050

    
2051
        compose->autowrap = prev_autowrap;
2052
        if (compose->autowrap)
2053
                compose_wrap_all(compose);
2054

    
2055
        if (scroll)
2056
                gtk_text_view_scroll_mark_onscreen(text, mark);
2057
}
2058

    
2059
static void compose_enable_sig(Compose *compose)
2060
{
2061
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2062
        GtkTextBuffer *buffer;
2063
        GtkTextIter iter, start, end;
2064
        gchar *sig_str;
2065

    
2066
        g_return_if_fail(compose->account != NULL);
2067

    
2068
        buffer = gtk_text_view_get_buffer(text);
2069
        gtk_text_buffer_get_start_iter(buffer, &iter);
2070

    
2071
        sig_str = compose_get_signature_str(compose);
2072
        if (!sig_str)
2073
                return;
2074

    
2075
        if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) {
2076
                end = start;
2077
                gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1));
2078
                gtk_text_buffer_apply_tag(buffer, compose->sig_tag,
2079
                                          &start, &end);
2080
        }
2081

    
2082
        g_free(sig_str);
2083
}
2084

    
2085
static gchar *compose_get_signature_str(Compose *compose)
2086
{
2087
        gchar *sig_path;
2088
        gchar *sig_body = NULL;
2089
        gchar *utf8_sig_body = NULL;
2090
        gchar *utf8_sig_str = NULL;
2091

    
2092
        g_return_val_if_fail(compose->account != NULL, NULL);
2093

    
2094
        if (compose->account->sig_type == SIG_DIRECT) {
2095
                gchar *p, *sp;
2096

    
2097
                if (!compose->account->sig_text)
2098
                        return NULL;
2099

    
2100
                sp = compose->account->sig_text;
2101
                p = sig_body = g_malloc(strlen(compose->account->sig_text) + 1);
2102
                while (*sp) {
2103
                        if (*sp == '\\' && *(sp + 1) == 'n') {
2104
                                *p++ = '\n';
2105
                                sp += 2;
2106
                        } else
2107
                                *p++ = *sp++;
2108
                }
2109
                *p = '\0';
2110

    
2111
                if (prefs_common.sig_sep) {
2112
                        utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2113
                                                   sig_body, NULL);
2114
                        g_free(sig_body);
2115
                } else
2116
                        utf8_sig_str = sig_body;
2117

    
2118
                return utf8_sig_str;
2119
        }
2120

    
2121
        if (!compose->account->sig_path)
2122
                return NULL;
2123

    
2124
        if (g_path_is_absolute(compose->account->sig_path) ||
2125
            compose->account->sig_type == SIG_COMMAND)
2126
                sig_path = g_strdup(compose->account->sig_path);
2127
        else {
2128
#ifdef G_OS_WIN32
2129
                sig_path = g_strconcat(get_rc_dir(),
2130
#else
2131
                sig_path = g_strconcat(get_home_dir(),
2132
#endif
2133
                                       G_DIR_SEPARATOR_S,
2134
                                       compose->account->sig_path, NULL);
2135
        }
2136

    
2137
        if (compose->account->sig_type == SIG_FILE) {
2138
                if (!is_file_or_fifo_exist(sig_path)) {
2139
                        debug_print("can't open signature file: %s\n",
2140
                                    sig_path);
2141
                        g_free(sig_path);
2142
                        return NULL;
2143
                }
2144
        }
2145

    
2146
        if (compose->account->sig_type == SIG_COMMAND)
2147
                sig_body = get_command_output(sig_path);
2148
        else {
2149
                gchar *tmp;
2150

    
2151
                tmp = file_read_to_str(sig_path);
2152
                if (!tmp)
2153
                        return NULL;
2154
                sig_body = normalize_newlines(tmp);
2155
                g_free(tmp);
2156
        }
2157
        g_free(sig_path);
2158

    
2159
        if (sig_body) {
2160
                gint error = 0;
2161

    
2162
                utf8_sig_body = conv_codeset_strdup_full
2163
                        (sig_body, conv_get_locale_charset_str(),
2164
                         CS_INTERNAL, &error);
2165
                if (!utf8_sig_body || error != 0) {
2166
                        if (g_utf8_validate(sig_body, -1, NULL) == TRUE) {
2167
                                g_free(utf8_sig_body);
2168
                                utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2169
                        }
2170
                } else {
2171
                        g_free(sig_body);
2172
                        sig_body = utf8_sig_body;
2173
                        utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2174
                }
2175
                g_free(sig_body);
2176
        }
2177

    
2178
        if (prefs_common.sig_sep) {
2179
                utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2180
                                           utf8_sig_body, NULL);
2181
                g_free(utf8_sig_body);
2182
        } else
2183
                utf8_sig_str = utf8_sig_body;
2184

    
2185
        return utf8_sig_str;
2186
}
2187

    
2188
static void compose_insert_file(Compose *compose, const gchar *file,
2189
                                gboolean scroll)
2190
{
2191
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2192
        GtkTextBuffer *buffer;
2193
        GtkTextMark *mark;
2194
        GtkTextIter iter;
2195
        const gchar *cur_encoding;
2196
        gchar buf[BUFFSIZE];
2197
        gint len;
2198
        FILE *fp;
2199
        gboolean prev_autowrap;
2200
        CharSet enc;
2201

    
2202
        g_return_if_fail(file != NULL);
2203

    
2204
        enc = conv_check_file_encoding(file);
2205

    
2206
        if ((fp = g_fopen(file, "rb")) == NULL) {
2207
                FILE_OP_ERROR(file, "fopen");
2208
                return;
2209
        }
2210

    
2211
        prev_autowrap = compose->autowrap;
2212
        compose->autowrap = FALSE;
2213

    
2214
        buffer = gtk_text_view_get_buffer(text);
2215
        mark = gtk_text_buffer_get_insert(buffer);
2216
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2217

    
2218
        cur_encoding = conv_get_locale_charset_str();
2219

    
2220
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2221
                gchar *str;
2222
                gint error = 0;
2223

    
2224
                if (enc == C_UTF_8) {
2225
                        str = conv_utf8todisp(buf, NULL);
2226
                } else {
2227
                        str = conv_codeset_strdup_full(buf, cur_encoding,
2228
                                                       CS_INTERNAL, &error);
2229
                        if (!str || error != 0) {
2230
                                if (g_utf8_validate(buf, -1, NULL) == TRUE) {
2231
                                        g_free(str);
2232
                                        str = g_strdup(buf);
2233
                                }
2234
                        }
2235
                        if (!str) continue;
2236
                }
2237

    
2238
                /* strip <CR> if DOS/Windows file,
2239
                   replace <CR> with <LF> if Macintosh file. */
2240
                strcrchomp(str);
2241
                len = strlen(str);
2242
                if (len > 0 && str[len - 1] != '\n') {
2243
                        while (--len >= 0)
2244
                                if (str[len] == '\r') str[len] = '\n';
2245
                }
2246

    
2247
                gtk_text_buffer_insert(buffer, &iter, str, -1);
2248
                g_free(str);
2249
        }
2250

    
2251
        fclose(fp);
2252

    
2253
        compose->autowrap = prev_autowrap;
2254
        if (compose->autowrap)
2255
                compose_wrap_all(compose);
2256

    
2257
        if (scroll)
2258
                gtk_text_view_scroll_mark_onscreen(text, mark);
2259
}
2260

    
2261
static void compose_attach_append(Compose *compose, const gchar *file,
2262
                                  const gchar *filename,
2263
                                  const gchar *content_type)
2264
{
2265
        GtkTreeIter iter;
2266
        AttachInfo *ainfo;
2267
        FILE *fp;
2268
        off_t size;
2269

    
2270
        g_return_if_fail(file != NULL);
2271
        g_return_if_fail(*file != '\0');
2272

    
2273
        if (!is_file_exist(file)) {
2274
                g_warning(_("File %s doesn't exist\n"), file);
2275
                return;
2276
        }
2277
        if ((size = get_file_size(file)) < 0) {
2278
                g_warning(_("Can't get file size of %s\n"), file);
2279
                return;
2280
        }
2281
        if (size == 0) {
2282
                alertpanel_notice(_("File %s is empty."), file);
2283
                return;
2284
        }
2285
        if ((fp = g_fopen(file, "rb")) == NULL) {
2286
                alertpanel_error(_("Can't read %s."), file);
2287
                return;
2288
        }
2289
        fclose(fp);
2290

    
2291
        if (!compose->use_attach) {
2292
                GtkItemFactory *ifactory;
2293

    
2294
                ifactory = gtk_item_factory_from_widget(compose->menubar);
2295
                menu_set_active(ifactory, "/View/Attachment", TRUE);
2296
        }
2297

    
2298
        ainfo = g_new0(AttachInfo, 1);
2299
        ainfo->file = g_strdup(file);
2300

    
2301
        if (content_type) {
2302
                ainfo->content_type = g_strdup(content_type);
2303
                if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2304
                        MsgInfo *msginfo;
2305
                        MsgFlags flags = {0, 0};
2306
                        const gchar *name;
2307

    
2308
                        if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2309
                                ainfo->encoding = ENC_7BIT;
2310
                        else
2311
                                ainfo->encoding = ENC_8BIT;
2312

    
2313
                        msginfo = procheader_parse_file(file, flags, FALSE);
2314
                        if (msginfo && msginfo->subject)
2315
                                name = msginfo->subject;
2316
                        else
2317
                                name = g_basename(filename ? filename : file);
2318

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

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

    
2345
        gtk_list_store_append(compose->attach_store, &iter);
2346
        gtk_list_store_set(compose->attach_store, &iter,
2347
                           COL_MIMETYPE, ainfo->content_type,
2348
                           COL_SIZE, to_human_readable(ainfo->size),
2349
                           COL_NAME, ainfo->name,
2350
                           COL_ATTACH_INFO, ainfo,
2351
                           -1);
2352
}
2353

    
2354
static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2355
{
2356
        MimeInfo *mimeinfo;
2357
        MimeInfo *child;
2358
        gchar *infile;
2359
        gchar *outfile;
2360

    
2361
        mimeinfo = procmime_scan_message(msginfo);
2362
        if (!mimeinfo) return;
2363

    
2364
        infile = procmsg_get_message_file_path(msginfo);
2365

    
2366
        child = mimeinfo;
2367
        while (child != NULL) {
2368
                if (child->children || child->mime_type == MIME_MULTIPART)
2369
                        goto next;
2370
                if (child->mime_type != MIME_MESSAGE_RFC822 &&
2371
                    child->mime_type != MIME_IMAGE &&
2372
                    child->mime_type != MIME_AUDIO &&
2373
                    child->mime_type != MIME_VIDEO &&
2374
                    !child->filename && !child->name)
2375
                        goto next;
2376

    
2377
                outfile = procmime_get_tmp_file_name(child);
2378
                if (procmime_get_part(outfile, infile, child) < 0) {
2379
                        g_warning(_("Can't get the part of multipart message."));
2380
                        g_free(outfile);
2381
                        goto next;
2382
                }
2383

    
2384
                compose_attach_append
2385
                        (compose, outfile,
2386
                         child->filename ? child->filename : child->name,
2387
                         child->content_type);
2388

    
2389
                g_free(outfile);
2390

    
2391
next:
2392
                if (child->mime_type == MIME_MESSAGE_RFC822)
2393
                        child = child->next;
2394
                else
2395
                        child = procmime_mimeinfo_next(child);
2396
        }
2397

    
2398
        g_free(infile);
2399
        procmime_mimeinfo_free_all(mimeinfo);
2400
}
2401

    
2402
#define INDENT_CHARS        ">|#"
2403

    
2404
typedef enum {
2405
        WAIT_FOR_INDENT_CHAR,
2406
        WAIT_FOR_INDENT_CHAR_OR_SPACE,
2407
} IndentState;
2408

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

    
2428
        while (!gtk_text_iter_ends_line(&iter)) {
2429
                wc = gtk_text_iter_get_char(&iter);
2430
                if (g_unichar_iswide(wc))
2431
                        break;
2432
                clen = g_unichar_to_utf8(wc, ch);
2433
                if (clen != 1)
2434
                        break;
2435

    
2436
                is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE;
2437
                is_space = g_unichar_isspace(wc);
2438

    
2439
                if (state == WAIT_FOR_INDENT_CHAR) {
2440
                        if (!is_indent && !g_unichar_isalnum(wc))
2441
                                break;
2442
                        if (is_indent) {
2443
                                quote_len += alnum_count + space_count + 1;
2444
                                alnum_count = space_count = 0;
2445
                                state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2446
                        } else
2447
                                alnum_count++;
2448
                } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2449
                        if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2450
                                break;
2451
                        if (is_space)
2452
                                space_count++;
2453
                        else if (is_indent) {
2454
                                quote_len += alnum_count + space_count + 1;
2455
                                alnum_count = space_count = 0;
2456
                        } else {
2457
                                alnum_count++;
2458
                                state = WAIT_FOR_INDENT_CHAR;
2459
                        }
2460
                }
2461

    
2462
                gtk_text_iter_forward_char(&iter);
2463
        }
2464

    
2465
        if (quote_len > 0 && space_count > 0)
2466
                quote_len++;
2467

    
2468
        if (len)
2469
                *len = quote_len;
2470

    
2471
        if (quote_len > 0) {
2472
                iter = *start;
2473
                gtk_text_iter_forward_chars(&iter, quote_len);
2474
                return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2475
        }
2476

    
2477
        return NULL;
2478
}
2479

    
2480
/* return TRUE if the line is itemized */
2481
static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2482
                                    const GtkTextIter *start)
2483
{
2484
        GtkTextIter iter = *start;
2485
        gunichar wc;
2486
        gchar ch[6];
2487
        gint clen;
2488

    
2489
        if (gtk_text_iter_ends_line(&iter))
2490
                return FALSE;
2491

    
2492
        while (1) {
2493
                wc = gtk_text_iter_get_char(&iter);
2494
                if (!g_unichar_isspace(wc))
2495
                        break;
2496
                gtk_text_iter_forward_char(&iter);
2497
                if (gtk_text_iter_ends_line(&iter))
2498
                        return FALSE;
2499
        }
2500

    
2501
        clen = g_unichar_to_utf8(wc, ch);
2502

    
2503
        /* (1), 2), 3. etc. */
2504
        if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) {
2505
                gboolean digit_appeared = FALSE;
2506

    
2507
                if (ch[0] == '(')
2508
                        gtk_text_iter_forward_char(&iter);
2509

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

    
2535
        if (clen != 1)
2536
                return FALSE;
2537
        if (!strchr("*-+", ch[0]))
2538
                return FALSE;
2539

    
2540
        gtk_text_iter_forward_char(&iter);
2541
        if (gtk_text_iter_ends_line(&iter))
2542
                return FALSE;
2543
        wc = gtk_text_iter_get_char(&iter);
2544
        if (g_unichar_isspace(wc))
2545
                return TRUE;
2546
        else if (ch[0] == '-') {
2547
                /* -- */
2548
                clen = g_unichar_to_utf8(wc, ch);
2549
                if (clen == 1 && ch[0] == '-')
2550
                        return TRUE;
2551
        }
2552

    
2553
        return FALSE;
2554
}
2555

    
2556
static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2557
                                           const GtkTextIter *start,
2558
                                           GtkTextIter *break_pos,
2559
                                           gint max_col,
2560
                                           gint quote_len)
2561
{
2562
        GtkTextIter iter = *start, line_end = *start;
2563
        PangoLogAttr *attrs;
2564
        gchar *str;
2565
        gchar *p;
2566
        gint len;
2567
        gint i;
2568
        gint col = 0;
2569
        gint pos = 0;
2570
        gboolean can_break = FALSE;
2571
        gboolean do_break = FALSE;
2572
        gboolean prev_dont_break = FALSE;
2573

    
2574
        gtk_text_iter_forward_to_line_end(&line_end);
2575
        str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2576
        len = g_utf8_strlen(str, -1);
2577
        /* g_print("breaking line: %d: %s (len = %d)\n",
2578
                gtk_text_iter_get_line(&iter), str, len); */
2579
        attrs = g_new(PangoLogAttr, len + 1);
2580

    
2581
        pango_default_break(str, -1, NULL, attrs, len + 1);
2582

    
2583
        p = str;
2584

    
2585
        /* skip quote and leading spaces */
2586
        for (i = 0; *p != '\0' && i < len; i++) {
2587
                gunichar wc;
2588

    
2589
                wc = g_utf8_get_char(p);
2590
                if (i >= quote_len && !g_unichar_isspace(wc))
2591
                        break;
2592
                if (g_unichar_iswide(wc))
2593
                        col += 2;
2594
                else if (*p == '\t')
2595
                        col += 8;
2596
                else
2597
                        col++;
2598
                p = g_utf8_next_char(p);
2599
        }
2600

    
2601
        for (; *p != '\0' && i < len; i++) {
2602
                PangoLogAttr *attr = attrs + i;
2603
                gunichar wc;
2604
                gint uri_len;
2605

    
2606
                if (attr->is_line_break && can_break && !prev_dont_break)
2607
                        pos = i;
2608

    
2609
                /* don't wrap URI */
2610
                if ((uri_len = get_uri_len(p)) > 0) {
2611
                        col += uri_len;
2612
                        if (pos > 0 && col > max_col) {
2613
                                do_break = TRUE;
2614
                                break;
2615
                        }
2616
                        i += uri_len - 1;
2617
                        p += uri_len;
2618
                        can_break = TRUE;
2619
                        continue;
2620
                }
2621

    
2622
                wc = g_utf8_get_char(p);
2623
                if (g_unichar_iswide(wc)) {
2624
                        col += 2;
2625
                        if (prev_dont_break && can_break && attr->is_line_break)
2626
                                pos = i;
2627
                } else if (*p == '\t')
2628
                        col += 8;
2629
                else
2630
                        col++;
2631
                if (pos > 0 && col > max_col) {
2632
                        do_break = TRUE;
2633
                        break;
2634
                }
2635

    
2636
                if (*p == '-' || *p == '/')
2637
                        prev_dont_break = TRUE;
2638
                else
2639
                        prev_dont_break = FALSE;
2640

    
2641
                p = g_utf8_next_char(p);
2642
                can_break = TRUE;
2643
        }
2644

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

    
2647
        g_free(attrs);
2648
        g_free(str);
2649

    
2650
        *break_pos = *start;
2651
        gtk_text_iter_set_line_offset(break_pos, pos);
2652

    
2653
        return do_break;
2654
}
2655

    
2656
static gboolean compose_join_next_line(GtkTextBuffer *buffer,
2657
                                       GtkTextIter *iter,
2658
                                       const gchar *quote_str)
2659
{
2660
        GtkTextIter iter_ = *iter, cur, prev, next, end;
2661
        PangoLogAttr attrs[3];
2662
        gchar *str;
2663
        gchar *next_quote_str;
2664
        gunichar wc1, wc2;
2665
        gint quote_len;
2666
        gboolean keep_cursor = FALSE;
2667

    
2668
        if (!gtk_text_iter_forward_line(&iter_) ||
2669
            gtk_text_iter_ends_line(&iter_))
2670
                return FALSE;
2671

    
2672
        next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
2673

    
2674
        if ((quote_str || next_quote_str) &&
2675
            strcmp2(quote_str, next_quote_str) != 0) {
2676
                g_free(next_quote_str);
2677
                return FALSE;
2678
        }
2679
        g_free(next_quote_str);
2680

    
2681
        end = iter_;
2682
        if (quote_len > 0) {
2683
                gtk_text_iter_forward_chars(&end, quote_len);
2684
                if (gtk_text_iter_ends_line(&end))
2685
                        return FALSE;
2686
        }
2687

    
2688
        /* don't join itemized lines */
2689
        if (compose_is_itemized(buffer, &end))
2690
                return FALSE;
2691

    
2692
        /* delete quote str */
2693
        if (quote_len > 0)
2694
                gtk_text_buffer_delete(buffer, &iter_, &end);
2695

    
2696
        /* delete linebreak and extra spaces */
2697
        prev = cur = iter_;
2698
        while (gtk_text_iter_backward_char(&cur)) {
2699
                wc1 = gtk_text_iter_get_char(&cur);
2700
                if (!g_unichar_isspace(wc1))
2701
                        break;
2702
                prev = cur;
2703
        }
2704
        next = cur = iter_;
2705
        while (!gtk_text_iter_ends_line(&cur)) {
2706
                wc1 = gtk_text_iter_get_char(&cur);
2707
                if (!g_unichar_isspace(wc1))
2708
                        break;
2709
                gtk_text_iter_forward_char(&cur);
2710
                next = cur;
2711
        }
2712
        if (!gtk_text_iter_equal(&prev, &next)) {
2713
                GtkTextMark *mark;
2714

    
2715
                mark = gtk_text_buffer_get_insert(buffer);
2716
                gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
2717
                if (gtk_text_iter_equal(&prev, &cur))
2718
                        keep_cursor = TRUE;
2719
                gtk_text_buffer_delete(buffer, &prev, &next);
2720
        }
2721
        iter_ = prev;
2722

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

    
2740
        *iter = iter_;
2741
        return TRUE;
2742
}
2743

    
2744
static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
2745
{
2746
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2747
        GtkTextBuffer *buffer;
2748
        GtkTextIter iter, break_pos;
2749
        gchar *quote_str = NULL;
2750
        gint quote_len;
2751
        gboolean wrap_quote = prefs_common.linewrap_quote;
2752
        gboolean prev_autowrap = compose->autowrap;
2753

    
2754
        compose->autowrap = FALSE;
2755

    
2756
        buffer = gtk_text_view_get_buffer(text);
2757

    
2758
        if (par_iter) {
2759
                iter = *par_iter;
2760
        } else {
2761
                GtkTextMark *mark;
2762
                mark = gtk_text_buffer_get_insert(buffer);
2763
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2764
        }
2765

    
2766
        /* move to paragraph start */
2767
        gtk_text_iter_set_line_offset(&iter, 0);
2768
        if (gtk_text_iter_ends_line(&iter)) {
2769
                while (gtk_text_iter_ends_line(&iter) &&
2770
                       gtk_text_iter_forward_line(&iter))
2771
                        ;
2772
        } else {
2773
                while (gtk_text_iter_backward_line(&iter)) {
2774
                        if (gtk_text_iter_ends_line(&iter)) {
2775
                                gtk_text_iter_forward_line(&iter);
2776
                                break;
2777
                        }
2778
                }
2779
        }
2780

    
2781
        /* go until paragraph end (empty line) */
2782
        while (!gtk_text_iter_ends_line(&iter)) {
2783
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
2784
                if (quote_str) {
2785
                        if (!wrap_quote) {
2786
                                gtk_text_iter_forward_line(&iter);
2787
                                g_free(quote_str);
2788
                                continue;
2789
                        }
2790
                        debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
2791
                }
2792

    
2793
                if (compose_get_line_break_pos(buffer, &iter, &break_pos,
2794
                                               prefs_common.linewrap_len,
2795
                                               quote_len)) {
2796
                        GtkTextIter prev, next, cur;
2797

    
2798
                        gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
2799

    
2800
                        /* remove trailing spaces */
2801
                        cur = break_pos;
2802
                        gtk_text_iter_backward_char(&cur);
2803
                        prev = next = cur;
2804
                        while (!gtk_text_iter_starts_line(&cur)) {
2805
                                gunichar wc;
2806

    
2807
                                gtk_text_iter_backward_char(&cur);
2808
                                wc = gtk_text_iter_get_char(&cur);
2809
                                if (!g_unichar_isspace(wc))
2810
                                        break;
2811
                                prev = cur;
2812
                        }
2813
                        if (!gtk_text_iter_equal(&prev, &next)) {
2814
                                gtk_text_buffer_delete(buffer, &prev, &next);
2815
                                break_pos = next;
2816
                                gtk_text_iter_forward_char(&break_pos);
2817
                        }
2818

    
2819
                        if (quote_str)
2820
                                gtk_text_buffer_insert(buffer, &break_pos,
2821
                                                       quote_str, -1);
2822

    
2823
                        iter = break_pos;
2824
                        compose_join_next_line(buffer, &iter, quote_str);
2825

    
2826
                        /* move iter to current line start */
2827
                        gtk_text_iter_set_line_offset(&iter, 0);
2828
                } else {
2829
                        /* move iter to next line start */
2830
                        iter = break_pos;
2831
                        gtk_text_iter_forward_line(&iter);
2832
                }
2833

    
2834
                g_free(quote_str);
2835
        }
2836

    
2837
        if (par_iter)
2838
                *par_iter = iter;
2839

    
2840
        compose->autowrap = prev_autowrap;
2841
}
2842

    
2843
static void compose_wrap_all(Compose *compose)
2844
{
2845
        compose_wrap_all_full(compose, FALSE);
2846
}
2847

    
2848
static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
2849
{
2850
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2851
        GtkTextBuffer *buffer;
2852
        GtkTextIter iter;
2853

    
2854
        buffer = gtk_text_view_get_buffer(text);
2855

    
2856
        gtk_text_buffer_get_start_iter(buffer, &iter);
2857
        while (!gtk_text_iter_is_end(&iter))
2858
                compose_wrap_paragraph(compose, &iter);
2859
}
2860

    
2861
static void compose_set_title(Compose *compose)
2862
{
2863
        gchar *str;
2864
        gchar *edited;
2865
        const gchar *subject;
2866

    
2867
        subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2868
        if (!subject || subject[0] == '\0')
2869
                subject = _("(No Subject)");
2870

    
2871
        edited = compose->modified ? " *" : "";
2872
        str = g_strdup_printf(_("%s - Compose%s"), subject, edited);
2873
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
2874
        g_free(str);
2875
}
2876

    
2877
static void compose_select_account(Compose *compose, PrefsAccount *account,
2878
                                   gboolean init)
2879
{
2880
        GtkItemFactory *ifactory;
2881
        PrefsAccount *prev_account;
2882

    
2883
        g_return_if_fail(account != NULL);
2884

    
2885
        prev_account = compose->account;
2886
        compose->account = account;
2887

    
2888
        compose_set_title(compose);
2889

    
2890
        ifactory = gtk_item_factory_from_widget(compose->menubar);
2891

    
2892
        if (account->protocol == A_NNTP &&
2893
            (init || prev_account->protocol != A_NNTP)) {
2894
                gtk_widget_show(compose->newsgroups_hbox);
2895
                gtk_widget_show(compose->newsgroups_entry);
2896
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2897
                compose->use_newsgroups = TRUE;
2898

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

    
2911
                menu_set_active(ifactory, "/View/To", TRUE);
2912
                menu_set_sensitive(ifactory, "/View/To", FALSE);
2913
                menu_set_active(ifactory, "/View/Cc", TRUE);
2914
                menu_set_active(ifactory, "/View/Followup-To", FALSE);
2915
                menu_set_sensitive(ifactory, "/View/Followup-To", FALSE);
2916
        }
2917

    
2918
        if (account->set_autocc) {
2919
                compose_entry_show(compose, COMPOSE_ENTRY_CC);
2920
                if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2921
                        compose_entry_set(compose, account->auto_cc,
2922
                                          COMPOSE_ENTRY_CC);
2923
        }
2924
        if (account->set_autobcc) {
2925
                compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2926
                if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2927
                        compose_entry_set(compose, account->auto_bcc,
2928
                                          COMPOSE_ENTRY_BCC);
2929
        }
2930
        if (account->set_autoreplyto) {
2931
                compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2932
                if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2933
                        compose_entry_set(compose, account->auto_replyto,
2934
                                          COMPOSE_ENTRY_REPLY_TO);
2935
        }
2936

    
2937
#if USE_GPGME
2938
        if (rfc2015_is_available()) {
2939
                if (account->default_sign)
2940
                        menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2941
                if (account->default_encrypt)
2942
                        menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2943
        }
2944
#endif /* USE_GPGME */
2945

    
2946
        if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2947
                compose_insert_sig(compose, TRUE, TRUE, FALSE);
2948
}
2949

    
2950
static gboolean compose_check_for_valid_recipient(Compose *compose)
2951
{
2952
        const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
2953
        const gchar *newsgroups_raw = "";
2954
        gchar *to, *cc, *bcc;
2955
        gchar *newsgroups;
2956

    
2957
        if (compose->use_to)
2958
                to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2959
        if (compose->use_cc)
2960
                cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2961
        if (compose->use_bcc)
2962
                bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2963
        if (compose->use_newsgroups)
2964
                newsgroups_raw = gtk_entry_get_text
2965
                        (GTK_ENTRY(compose->newsgroups_entry));
2966

    
2967
        Xstrdup_a(to, to_raw, return FALSE);
2968
        Xstrdup_a(cc, cc_raw, return FALSE);
2969
        Xstrdup_a(bcc, bcc_raw, return FALSE);
2970
        Xstrdup_a(newsgroups, newsgroups_raw, return FALSE);
2971
        g_strstrip(to);
2972
        g_strstrip(cc);
2973
        g_strstrip(bcc);
2974
        g_strstrip(newsgroups);
2975

    
2976
        if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
2977
                return FALSE;
2978
        else
2979
                return TRUE;
2980
}
2981

    
2982
static gboolean compose_check_entries(Compose *compose)
2983
{
2984
        const gchar *str;
2985

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

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

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

    
3002
        return TRUE;
3003
}
3004

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

    
3016
        if (!prefs_common.check_attach)
3017
                return TRUE;
3018
        if (!prefs_common.check_attach_str)
3019
                return TRUE;
3020

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

    
3026
        buffer = gtk_text_view_get_buffer(text);
3027
        gtk_text_buffer_get_start_iter(buffer, &iter);
3028
        line_end = iter;
3029

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

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

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

    
3052
                g_free(line);
3053
        }
3054

    
3055
        g_strfreev(strv);
3056

    
3057
        if (attach_found) {
3058
                AlertValue aval;
3059

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

    
3067
        return TRUE;
3068
}
3069

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

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

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

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

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

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

    
3128
        if (!prefs_common.check_recipients)
3129
                return TRUE;
3130

    
3131
        if (prefs_common.check_recp_exclude) {
3132
                gchar **strv;
3133
                gint i;
3134

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

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

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

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

    
3173
        if (!check_recp)
3174
                return TRUE;
3175

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

    
3189
        vbox = gtk_vbox_new(FALSE, 8);
3190
        gtk_container_add(GTK_CONTAINER(window), vbox);
3191

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

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

    
3201
        vbox2 = gtk_vbox_new(FALSE, 12);
3202
        gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
3203

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

    
3209
        if (!font_desc) {
3210
                gint size;
3211

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

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

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

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

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

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

    
3273
        text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3274
        gtk_entry_set_text(GTK_ENTRY(entry), text);
3275

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

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

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

    
3342
        gtk_container_add(GTK_CONTAINER(scrwin), treeview);
3343

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

    
3350
        gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
3351

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

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

    
3364
        manage_window_set_transient(GTK_WINDOW(window));
3365

    
3366
        gtk_widget_show_all(window);
3367

    
3368
        while (state == 0)
3369
                gtk_main_iteration();
3370

    
3371
        gtk_widget_destroy(window);
3372

    
3373
        if (state == GTK_RESPONSE_OK)
3374
                return TRUE;
3375

    
3376
        return FALSE;
3377
}
3378

    
3379
static void compose_add_new_recipients_to_addressbook(Compose *compose)
3380
{
3381
        GSList *to_list = NULL, *cur;
3382
        const gchar *text;
3383

    
3384
        if (compose->use_to) {
3385
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3386
                to_list = address_list_append_orig(NULL, text);
3387
        }
3388
        if (compose->use_cc) {
3389
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3390
                to_list = address_list_append_orig(to_list, text);
3391
        }
3392
        if (compose->use_bcc) {
3393
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3394
                to_list = address_list_append_orig(to_list, text);
3395
        }
3396

    
3397
        for (cur = to_list; cur != NULL; cur = cur->next) {
3398
                gchar *orig_addr = cur->data;
3399
                gchar *name, *addr;
3400

    
3401
                name = procheader_get_fromname(orig_addr);
3402
                addr = g_strdup(orig_addr);
3403
                extract_address(addr);
3404
                if (!g_ascii_strcasecmp(name, addr)) {
3405
                        g_free(name);
3406
                        name = NULL;
3407
                }
3408

    
3409
                if (addressbook_has_address(addr))
3410
                        debug_print("compose_add_new_recipients_to_addressbook: address <%s> already registered.\n", addr);
3411
                else
3412
                        addressbook_add_contact_autoreg(name, addr, NULL);
3413

    
3414
                g_free(addr);
3415
                g_free(name);
3416
        }
3417

    
3418
        slist_free_strings(to_list);
3419
        g_slist_free(to_list);
3420
}
3421

    
3422
void compose_lock(Compose *compose)
3423
{
3424
        compose->lock_count++;
3425
}
3426

    
3427
void compose_unlock(Compose *compose)
3428
{
3429
        if (compose->lock_count > 0)
3430
                compose->lock_count--;
3431
}
3432

    
3433
void compose_block_modified(Compose *compose)
3434
{
3435
        compose->block_modified = TRUE;
3436
}
3437

    
3438
void compose_unblock_modified(Compose *compose)
3439
{
3440
        compose->block_modified = FALSE;
3441
}
3442

    
3443
static gint compose_send(Compose *compose)
3444
{
3445
        gchar tmp[MAXPATHLEN + 1];
3446
        gint ok = 0;
3447
        gboolean ack = TRUE;
3448

    
3449
        if (compose->lock_count > 0)
3450
                return 1;
3451

    
3452
        g_return_val_if_fail(compose->account != NULL, -1);
3453

    
3454
        compose_lock(compose);
3455

    
3456
        if (compose_check_entries(compose) == FALSE) {
3457
                compose_unlock(compose);
3458
                return 1;
3459
        }
3460
        if (compose_check_attachments(compose) == FALSE) {
3461
                compose_unlock(compose);
3462
                return 1;
3463
        }
3464
        if (compose_check_recipients(compose) == FALSE) {
3465
                compose_unlock(compose);
3466
                return 1;
3467
        }
3468

    
3469
        if (!main_window_toggle_online_if_offline(main_window_get())) {
3470
                compose_unlock(compose);
3471
                return 1;
3472
        }
3473

    
3474
        /* write to temporary file */
3475
        g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
3476
                   get_tmp_dir(), G_DIR_SEPARATOR, compose);
3477

    
3478
        if (compose->mode == COMPOSE_REDIRECT) {
3479
                if (compose_redirect_write_to_file(compose, tmp) < 0) {
3480
                        compose_unlock(compose);
3481
                        return -1;
3482
                }
3483
        } else {
3484
                if (compose_write_to_file(compose, tmp, FALSE) < 0) {
3485
                        compose_unlock(compose);
3486
                        return -1;
3487
                }
3488
        }
3489

    
3490
        if (!compose->to_list && !compose->newsgroup_list) {
3491
                g_warning(_("can't get recipient list."));
3492
                g_unlink(tmp);
3493
                compose_unlock(compose);
3494
                return 1;
3495
        }
3496

    
3497
        syl_plugin_signal_emit("compose-send", compose, compose->mode, 0,
3498
                               tmp, compose->to_list, &ack);
3499
        if (ack == FALSE) {
3500
                g_unlink(tmp);
3501
                compose_unlock(compose);
3502
                return -1;
3503
        }
3504

    
3505
        if (compose->to_list) {
3506
                PrefsAccount *ac;
3507

    
3508
                if (compose->account->protocol != A_NNTP)
3509
                        ac = compose->account;
3510
                else {
3511
                        ac = account_find_from_address(compose->account->address);
3512
                        if (!ac) {
3513
                                if (cur_account && cur_account->protocol != A_NNTP)
3514
                                        ac = cur_account;
3515
                                else
3516
                                        ac = account_get_default();
3517
                        }
3518
                        if (!ac || ac->protocol == A_NNTP) {
3519
                                alertpanel_error(_("Account for sending mail is not specified.\n"
3520
                                                   "Please select a mail account before sending."));
3521
                                g_unlink(tmp);
3522
                                compose_unlock(compose);
3523
                                return -1;
3524
                        }
3525
                }
3526
                ok = send_message(tmp, ac, compose->to_list);
3527
                statusbar_pop_all();
3528
        }
3529

    
3530
        if (ok == 0 && compose->newsgroup_list) {
3531
                ok = news_post(FOLDER(compose->account->folder), tmp);
3532
                if (ok < 0) {
3533
                        alertpanel_error(_("Error occurred while posting the message to %s ."),
3534
                                         compose->account->nntp_server);
3535
                        g_unlink(tmp);
3536
                        compose_unlock(compose);
3537
                        return -1;
3538
                }
3539
        }
3540

    
3541
        if (ok == 0) {
3542
                if (compose->mode == COMPOSE_REEDIT) {
3543
                        compose_remove_reedit_target(compose);
3544
                        if (compose->targetinfo)
3545
                                folderview_update_item
3546
                                        (compose->targetinfo->folder, TRUE);
3547
                }
3548

    
3549
                if (compose->reply_target)
3550
                        send_message_set_reply_flag(compose->reply_target,
3551
                                                    compose->inreplyto);
3552
                else if (compose->forward_targets)
3553
                        send_message_set_forward_flags
3554
                                (compose->forward_targets);
3555

    
3556
                /* save message to outbox */
3557
                if (prefs_common.savemsg) {
3558
                        FolderItem *outbox;
3559
                        gboolean drop_done = FALSE;
3560

    
3561
                        /* filter sent message */
3562
                        if (prefs_common.filter_sent) {
3563
                                FilterInfo *fltinfo;
3564

    
3565
                                fltinfo = filter_info_new();
3566
                                fltinfo->account = compose->account;
3567
                                fltinfo->flags.perm_flags = 0;
3568
                                fltinfo->flags.tmp_flags = MSG_RECEIVED;
3569

    
3570
                                filter_apply(prefs_common.fltlist, tmp,
3571
                                             fltinfo);
3572

    
3573
                                drop_done = fltinfo->drop_done;
3574
                                folderview_update_all_updated(TRUE);
3575
                                filter_info_free(fltinfo);
3576
                        }
3577

    
3578
                        if (!drop_done) {
3579
                                outbox = account_get_special_folder
3580
                                        (compose->account, F_OUTBOX);
3581
                                if (procmsg_save_to_outbox(outbox, tmp) < 0) {
3582
                                        alertpanel_error
3583
                                                (_("Sending of message was completed, but the message could not be saved to outbox."));
3584
                                        ok = -2;
3585
                                } else
3586
                                        folderview_update_item(outbox, TRUE);
3587
                        }
3588
                }
3589

    
3590
                /* Add recipients to addressbook automatically */
3591
                if (prefs_common.recipients_autoreg) {
3592
                        compose_add_new_recipients_to_addressbook(compose);
3593
                }
3594
        }
3595

    
3596
        g_unlink(tmp);
3597
        compose_unlock(compose);
3598

    
3599
        return ok;
3600
}
3601

    
3602
#if USE_GPGME
3603
/* interfaces to rfc2015 to keep out the prefs stuff there.
3604
 * returns 0 on success and -1 on error. */
3605
static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
3606
{
3607
        const gchar *key_id = NULL;
3608
        GSList *key_list;
3609

    
3610
        switch (compose->account->sign_key) {
3611
        case SIGN_KEY_DEFAULT:
3612
                *pkey_list = NULL;
3613
                return 0;
3614
        case SIGN_KEY_BY_FROM:
3615
                key_id = compose->account->address;
3616
                break;
3617
        case SIGN_KEY_CUSTOM:
3618
                key_id = compose->account->sign_key_id;
3619
                break;
3620
        default:
3621
                break;
3622
        }
3623

    
3624
        key_list = rfc2015_create_signers_list(key_id);
3625

    
3626
        if (!key_list) {
3627
                alertpanel_error(_("Could not find any key associated with "
3628
                                   "currently selected key id `%s'."), key_id);
3629
                return -1;
3630
        }
3631

    
3632
        *pkey_list = key_list;
3633
        return 0;
3634
}
3635

    
3636
/* clearsign message body text */
3637
static gint compose_clearsign_text(Compose *compose, gchar **text)
3638
{
3639
        GSList *key_list;
3640
        gchar *tmp_file;
3641

    
3642
        tmp_file = get_tmp_file();
3643
        if (str_write_to_file(*text, tmp_file) < 0) {
3644
                g_free(tmp_file);
3645
                return -1;
3646
        }
3647

    
3648
        if (compose_create_signers_list(compose, &key_list) < 0) {
3649
                g_unlink(tmp_file);
3650
                g_free(tmp_file);
3651
                return -1;
3652
        }
3653
        if (rfc2015_clearsign(tmp_file, key_list) < 0) {
3654
                alertpanel_error(_("Can't sign the message."));
3655
                g_unlink(tmp_file);
3656
                g_free(tmp_file);
3657
                return -1;
3658
        }
3659

    
3660
        g_free(*text);
3661
        *text = file_read_to_str(tmp_file);
3662
        g_unlink(tmp_file);
3663
        g_free(tmp_file);
3664
        if (*text == NULL)
3665
                return -1;
3666

    
3667
        return 0;
3668
}
3669

    
3670
static gint compose_encrypt_armored(Compose *compose, gchar **text)
3671
{
3672
        gchar *tmp_file;
3673

    
3674
        tmp_file = get_tmp_file();
3675
        if (str_write_to_file(*text, tmp_file) < 0) {
3676
                g_free(tmp_file);
3677
                return -1;
3678
        }
3679

    
3680
        if (rfc2015_encrypt_armored(tmp_file, compose->to_list) < 0) {
3681
                alertpanel_error(_("Can't encrypt the message."));
3682
                g_unlink(tmp_file);
3683
                g_free(tmp_file);
3684
                return -1;
3685
        }
3686

    
3687
        g_free(*text);
3688
        *text = file_read_to_str(tmp_file);
3689
        g_unlink(tmp_file);
3690
        g_free(tmp_file);
3691
        if (*text == NULL)
3692
                return -1;
3693

    
3694
        return 0;
3695
}
3696

    
3697
static gint compose_encrypt_sign_armored(Compose *compose, gchar **text)
3698
{
3699
        GSList *key_list;
3700
        gchar *tmp_file;
3701

    
3702
        tmp_file = get_tmp_file();
3703
        if (str_write_to_file(*text, tmp_file) < 0) {
3704
                g_free(tmp_file);
3705
                return -1;
3706
        }
3707

    
3708
        if (compose_create_signers_list(compose, &key_list) < 0) {
3709
                g_unlink(tmp_file);
3710
                g_free(tmp_file);
3711
                return -1;
3712
        }
3713

    
3714
        if (rfc2015_encrypt_sign_armored
3715
                (tmp_file, compose->to_list, key_list) < 0) {
3716
                alertpanel_error(_("Can't encrypt or sign 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
#endif /* USE_GPGME */
3732

    
3733
static gint compose_write_to_file(Compose *compose, const gchar *file,
3734
                                  gboolean is_draft)
3735
{
3736
        GtkTextBuffer *buffer;
3737
        GtkTextIter start, end;
3738
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
3739
        FILE *fp;
3740
        size_t len;
3741
        gchar *chars;
3742
        gchar *buf;
3743
        gchar *canon_buf;
3744
        const gchar *out_charset;
3745
        const gchar *body_charset;
3746
        const gchar *src_charset = CS_INTERNAL;
3747
        EncodingType encoding;
3748
        gint line;
3749
#if USE_GPGME
3750
        gboolean use_pgpmime_encryption = FALSE;
3751
        gboolean use_pgpmime_signing = FALSE;
3752
#endif
3753

    
3754
        if ((fp = g_fopen(file, "wb")) == NULL) {
3755
                FILE_OP_ERROR(file, "fopen");
3756
                return -1;
3757
        }
3758

    
3759
        /* chmod for security */
3760
        if (change_file_mode_rw(fp, file) < 0) {
3761
                FILE_OP_ERROR(file, "chmod");
3762
                g_warning(_("can't change file mode\n"));
3763
        }
3764

    
3765
        /* get outgoing charset */
3766
        out_charset = conv_get_charset_str(compose->out_encoding);
3767
        if (!out_charset)
3768
                out_charset = conv_get_outgoing_charset_str();
3769
        if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII))
3770
                out_charset = CS_ISO_8859_1;
3771
        body_charset = out_charset;
3772

    
3773
        /* get all composed text */
3774
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3775
        gtk_text_buffer_get_start_iter(buffer, &start);
3776
        gtk_text_buffer_get_end_iter(buffer, &end);
3777
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3778
        if (is_ascii_str(chars)) {
3779
                buf = chars;
3780
                chars = NULL;
3781
                body_charset = CS_US_ASCII;
3782
                encoding = ENC_7BIT;
3783
        } else {
3784
                gint error = 0;
3785

    
3786
                buf = conv_codeset_strdup_full
3787
                        (chars, src_charset, body_charset, &error);
3788
                if (!buf || error != 0) {
3789
                        AlertValue aval = G_ALERTDEFAULT;
3790
                        gchar *msg;
3791

    
3792
                        g_free(buf);
3793

    
3794
                        if (!is_draft) {
3795
                                msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n"
3796
                                                        "\n"
3797
                                                        "Send it as %s anyway?"),
3798
                                                      src_charset, body_charset,
3799
                                                      src_charset);
3800
                                aval = alertpanel_full
3801
                                        (_("Code conversion error"), msg, ALERT_ERROR,
3802
                                         G_ALERTALTERNATE,
3803
                                         FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3804
                                g_free(msg);
3805
                        }
3806

    
3807
                        if (aval != G_ALERTDEFAULT) {
3808
                                g_free(chars);
3809
                                fclose(fp);
3810
                                g_unlink(file);
3811
                                return -1;
3812
                        } else {
3813
                                buf = chars;
3814
                                out_charset = body_charset = src_charset;
3815
                                chars = NULL;
3816
                        }
3817
                }
3818

    
3819
                if (prefs_common.encoding_method == CTE_BASE64)
3820
                        encoding = ENC_BASE64;
3821
                else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3822
                        encoding = ENC_QUOTED_PRINTABLE;
3823
                else if (prefs_common.encoding_method == CTE_8BIT)
3824
                        encoding = ENC_8BIT;
3825
                else
3826
                        encoding = procmime_get_encoding_for_charset
3827
                                (body_charset);
3828
        }
3829
        g_free(chars);
3830

    
3831
        canon_buf = canonicalize_str(buf);
3832
        g_free(buf);
3833
        buf = canon_buf;
3834

    
3835
#if USE_GPGME
3836
        if (compose->use_signing && !compose->account->clearsign)
3837
                use_pgpmime_signing = TRUE;
3838
        if (compose->use_encryption && compose->account->ascii_armored) {
3839
                use_pgpmime_encryption = FALSE;
3840
                use_pgpmime_signing = FALSE;
3841
        }
3842
        if (compose->use_encryption && !compose->account->ascii_armored)
3843
                use_pgpmime_encryption = TRUE;
3844

    
3845
        /* protect trailing spaces */
3846
        if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) {
3847
                if (encoding == ENC_7BIT) {
3848
                        if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) {
3849
                                gchar *tmp;
3850
                                tmp = strchomp_all(buf);
3851
                                g_free(buf);
3852
                                buf = tmp;
3853
                        } else
3854
                                encoding = ENC_QUOTED_PRINTABLE;
3855
                } else if (encoding == ENC_8BIT) {
3856
                        encoding = procmime_get_encoding_for_str(buf);
3857
                        if (encoding == ENC_7BIT)
3858
                                encoding = ENC_QUOTED_PRINTABLE;
3859
                }
3860
        }
3861

    
3862
        if (rfc2015_is_available() && !is_draft) {
3863
                if ((compose->use_encryption &&
3864
                     compose->account->ascii_armored) ||
3865
                    (compose->use_signing && compose->account->clearsign)) {
3866
                        /* MIME encoding doesn't fit with cleartext signature */
3867
                        if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64)
3868
                                encoding = ENC_8BIT;
3869

    
3870
                }
3871
        }
3872
#endif
3873

    
3874
        debug_print("src encoding = %s, out encoding = %s, "
3875
                    "body encoding = %s, transfer encoding = %s\n",
3876
                    src_charset, out_charset, body_charset,
3877
                    procmime_get_encoding_str(encoding));
3878

    
3879
        /* check for line length limit */
3880
        if (!is_draft &&
3881
            encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
3882
            check_line_length(buf, 1000, &line) < 0) {
3883
                AlertValue aval;
3884
                gchar *msg;
3885

    
3886
                msg = g_strdup_printf
3887
                        (_("Line %d exceeds the line length limit (998 bytes).\n"
3888
                           "The contents of the message might be broken on the way to the delivery.\n"
3889
                           "\n"
3890
                           "Send it anyway?"), line + 1);
3891
                aval = alertpanel_full(_("Line length limit"),
3892
                                       msg, ALERT_WARNING,
3893
                                       G_ALERTALTERNATE, FALSE,
3894
                                       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3895
                if (aval != G_ALERTDEFAULT) {
3896
                        g_free(msg);
3897
                        fclose(fp);
3898
                        g_unlink(file);
3899
                        g_free(buf);
3900
                        return -1;
3901
                }
3902
        }
3903

    
3904
        /* write headers */
3905
        if (compose_write_headers(compose, fp, out_charset,
3906
                                  body_charset, encoding, is_draft) < 0) {
3907
                g_warning("can't write headers\n");
3908
                fclose(fp);
3909
                g_unlink(file);
3910
                g_free(buf);
3911
                return -1;
3912
        }
3913

    
3914
#if USE_GPGME
3915
        /* do ascii-armor encryption and/or clearsign */
3916
        if (rfc2015_is_available() && !is_draft) {
3917
                gint ret;
3918

    
3919
                if (compose->use_encryption && compose->account->ascii_armored) {
3920
                        if (compose->use_signing)
3921
                                ret = compose_encrypt_sign_armored(compose, &buf);
3922
                        else
3923
                                ret = compose_encrypt_armored(compose, &buf);
3924
                        if (ret < 0) {
3925
                                g_warning("ascii-armored encryption failed\n");
3926
                                fclose(fp);
3927
                                g_unlink(file);
3928
                                g_free(buf);
3929
                                return -1;
3930
                        }
3931
                } else if (compose->use_signing && compose->account->clearsign) {
3932
                        if (compose_clearsign_text(compose, &buf) < 0) {
3933
                                g_warning("clearsign failed\n");
3934
                                fclose(fp);
3935
                                g_unlink(file);
3936
                                g_free(buf);
3937
                                return -1;
3938
                        }
3939
                }
3940
        }
3941
#endif
3942

    
3943
        if (compose->use_attach &&
3944
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3945
#if USE_GPGME
3946
            /* This prolog message is ignored by mime software and
3947
             * because it would make our signing/encryption task
3948
             * tougher, we don't emit it in that case */
3949
            if (!rfc2015_is_available() ||
3950
                (!compose->use_signing && !compose->use_encryption))
3951
#endif
3952
                fputs("This is a multi-part message in MIME format.\n", fp);
3953

    
3954
                fprintf(fp, "\n--%s\n", compose->boundary);
3955
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
3956
                        body_charset);
3957
#if USE_GPGME
3958
                if (rfc2015_is_available() && use_pgpmime_signing)
3959
                        fprintf(fp, "Content-Disposition: inline\n");
3960
#endif
3961
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
3962
                        procmime_get_encoding_str(encoding));
3963
                fputc('\n', fp);
3964
        }
3965

    
3966
        /* write body */
3967
        len = strlen(buf);
3968
        if (encoding == ENC_BASE64) {
3969
                gchar outbuf[B64_BUFFSIZE];
3970
                gint i, l;
3971

    
3972
                for (i = 0; i < len; i += B64_LINE_SIZE) {
3973
                        l = MIN(B64_LINE_SIZE, len - i);
3974
                        base64_encode(outbuf, (guchar *)buf + i, l);
3975
                        fputs(outbuf, fp);
3976
                        fputc('\n', fp);
3977
                }
3978
        } else if (encoding == ENC_QUOTED_PRINTABLE) {
3979
                gchar *outbuf;
3980
                size_t outlen;
3981

    
3982
                outbuf = g_malloc(len * 4);
3983
                qp_encode_line(outbuf, (guchar *)buf);
3984
                outlen = strlen(outbuf);
3985
                if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
3986
                        FILE_OP_ERROR(file, "fwrite");
3987
                        fclose(fp);
3988
                        g_unlink(file);
3989
                        g_free(outbuf);
3990
                        g_free(buf);
3991
                        return -1;
3992
                }
3993
                g_free(outbuf);
3994
        } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
3995
                FILE_OP_ERROR(file, "fwrite");
3996
                fclose(fp);
3997
                g_unlink(file);
3998
                g_free(buf);
3999
                return -1;
4000
        }
4001
        g_free(buf);
4002

    
4003
        if (compose->use_attach &&
4004
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
4005
                if (compose_write_attach(compose, fp, out_charset) < 0) {
4006
                        fclose(fp);
4007
                        g_unlink(file);
4008
                        return -1;
4009
                }
4010
        }
4011

    
4012
        if (fclose(fp) == EOF) {
4013
                FILE_OP_ERROR(file, "fclose");
4014
                g_unlink(file);
4015
                return -1;
4016
        }
4017

    
4018
#if USE_GPGME
4019
        if (!rfc2015_is_available() || is_draft) {
4020
                uncanonicalize_file_replace(file);
4021
                return 0;
4022
        }
4023

    
4024
        if (use_pgpmime_signing || use_pgpmime_encryption) {
4025
                if (canonicalize_file_replace(file) < 0) {
4026
                        g_unlink(file);
4027
                        return -1;
4028
                }
4029
        }
4030

    
4031
        if (use_pgpmime_signing && !use_pgpmime_encryption) {
4032
                GSList *key_list;
4033

    
4034
                if (compose_create_signers_list(compose, &key_list) < 0) {
4035
                        g_unlink(file);
4036
                        return -1;
4037
                }
4038
                if (rfc2015_sign(file, key_list) < 0) {
4039
                        alertpanel_error(_("Can't sign the message."));
4040
                        g_unlink(file);
4041
                        return -1;
4042
                }
4043
        } else if (use_pgpmime_encryption) {
4044
                GSList *key_list;
4045

    
4046
                if (compose->use_bcc) {
4047
                        const gchar *text;
4048
                        gchar *bcc;
4049
                        AlertValue aval;
4050

    
4051
                        text = gtk_entry_get_text
4052
                                (GTK_ENTRY(compose->bcc_entry));
4053
                        Xstrdup_a(bcc, text, { g_unlink(file); return -1; });
4054
                        g_strstrip(bcc);
4055
                        if (*bcc != '\0') {
4056
                                aval = alertpanel_full
4057
                                        (_("Encrypting with Bcc"),
4058
                                         _("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"
4059
                                           "\n"
4060
                                           "Send it anyway?"),
4061
                                         ALERT_WARNING, G_ALERTDEFAULT, FALSE,
4062
                                         GTK_STOCK_YES, GTK_STOCK_NO, NULL);
4063
                                if (aval != G_ALERTDEFAULT) {
4064
                                        g_unlink(file);
4065
                                        return -1;
4066
                                }
4067
                        }
4068
                }
4069
                if (use_pgpmime_signing) {
4070
                        if (compose_create_signers_list
4071
                                (compose, &key_list) < 0) {
4072
                                g_unlink(file);
4073
                                return -1;
4074
                        }
4075
                        if (rfc2015_encrypt_sign(file, compose->to_list,
4076
                                                 key_list) < 0) {
4077
                                alertpanel_error(_("Can't encrypt or sign the message."));
4078
                                g_unlink(file);
4079
                                return -1;
4080
                        }
4081
                } else if (rfc2015_encrypt(file, compose->to_list) < 0) {
4082
                        alertpanel_error(_("Can't encrypt the message."));
4083
                        g_unlink(file);
4084
                        return -1;
4085
                }
4086
        }
4087
#endif /* USE_GPGME */
4088

    
4089
        uncanonicalize_file_replace(file);
4090

    
4091
        return 0;
4092
}
4093

    
4094
static gint compose_write_body_to_file(Compose *compose, const gchar *file)
4095
{
4096
        GtkTextBuffer *buffer;
4097
        GtkTextIter start, end;
4098
        FILE *fp;
4099
        size_t len;
4100
        gchar *chars, *tmp;
4101

    
4102
        if ((fp = g_fopen(file, "wb")) == NULL) {
4103
                FILE_OP_ERROR(file, "fopen");
4104
                return -1;
4105
        }
4106

    
4107
        /* chmod for security */
4108
        if (change_file_mode_rw(fp, file) < 0) {
4109
                FILE_OP_ERROR(file, "chmod");
4110
                g_warning(_("can't change file mode\n"));
4111
        }
4112

    
4113
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4114
        gtk_text_buffer_get_start_iter(buffer, &start);
4115
        gtk_text_buffer_get_end_iter(buffer, &end);
4116
        tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4117

    
4118
        chars = conv_codeset_strdup
4119
                (tmp, CS_INTERNAL, conv_get_locale_charset_str());
4120

    
4121
        g_free(tmp);
4122

    
4123
        if (!chars) {
4124
                fclose(fp);
4125
                g_unlink(file);
4126
                return -1;
4127
        }
4128

    
4129
        /* write body */
4130
        len = strlen(chars);
4131
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
4132
                FILE_OP_ERROR(file, "fwrite");
4133
                g_free(chars);
4134
                fclose(fp);
4135
                g_unlink(file);
4136
                return -1;
4137
        }
4138

    
4139
        g_free(chars);
4140

    
4141
        if (fclose(fp) == EOF) {
4142
                FILE_OP_ERROR(file, "fclose");
4143
                g_unlink(file);
4144
                return -1;
4145
        }
4146
        return 0;
4147
}
4148

    
4149
static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
4150
{
4151
        FILE *fp;
4152
        FILE *fdest;
4153
        size_t len;
4154
        gchar buf[BUFFSIZE];
4155

    
4156
        g_return_val_if_fail(file != NULL, -1);
4157
        g_return_val_if_fail(compose->account != NULL, -1);
4158
        g_return_val_if_fail(compose->account->address != NULL, -1);
4159
        g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
4160
        g_return_val_if_fail(compose->targetinfo != NULL, -1);
4161

    
4162
        if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
4163
                return -1;
4164

    
4165
        if ((fdest = g_fopen(file, "wb")) == NULL) {
4166
                FILE_OP_ERROR(file, "fopen");
4167
                fclose(fp);
4168
                return -1;
4169
        }
4170

    
4171
        if (change_file_mode_rw(fdest, file) < 0) {
4172
                FILE_OP_ERROR(file, "chmod");
4173
                g_warning(_("can't change file mode\n"));
4174
        }
4175

    
4176
        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
4177
                if (g_ascii_strncasecmp(buf, "Return-Path:",
4178
                                        strlen("Return-Path:")) == 0 ||
4179
                    g_ascii_strncasecmp(buf, "Delivered-To:",
4180
                                        strlen("Delivered-To:")) == 0 ||
4181
                    g_ascii_strncasecmp(buf, "Received:",
4182
                                        strlen("Received:")) == 0 ||
4183
                    g_ascii_strncasecmp(buf, "Subject:",
4184
                                        strlen("Subject:")) == 0 ||
4185
                    g_ascii_strncasecmp(buf, "X-UIDL:",
4186
                                        strlen("X-UIDL:")) == 0)
4187
                        continue;
4188

    
4189
                if (fputs(buf, fdest) == EOF)
4190
                        goto error;
4191

    
4192
#if 0
4193
                if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) {
4194
                        fputs("\n (by way of ", fdest);
4195
                        if (compose->account->name) {
4196
                                compose_convert_header(compose,
4197
                                                       buf, sizeof(buf),
4198
                                                       compose->account->name,
4199
                                                       strlen(" (by way of "),
4200
                                                       FALSE, NULL);
4201
                                fprintf(fdest, "%s <%s>", buf,
4202
                                        compose->account->address);
4203
                        } else
4204
                                fputs(compose->account->address, fdest);
4205
                        fputs(")", fdest);
4206
                }
4207
#endif
4208

    
4209
                if (fputs("\n", fdest) == EOF)
4210
                        goto error;
4211
        }
4212

    
4213
        compose_redirect_write_headers(compose, fdest);
4214

    
4215
        while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4216
                if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
4217
                        FILE_OP_ERROR(file, "fwrite");
4218
                        goto error;
4219
                }
4220
        }
4221

    
4222
        fclose(fp);
4223
        if (fclose(fdest) == EOF) {
4224
                FILE_OP_ERROR(file, "fclose");
4225
                g_unlink(file);
4226
                return -1;
4227
        }
4228

    
4229
        return 0;
4230
error:
4231
        fclose(fp);
4232
        fclose(fdest);
4233
        g_unlink(file);
4234

    
4235
        return -1;
4236
}
4237

    
4238
static gint compose_remove_reedit_target(Compose *compose)
4239
{
4240
        FolderItem *item;
4241
        MsgInfo *msginfo = compose->targetinfo;
4242

    
4243
        g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4244
        if (!msginfo) return -1;
4245

    
4246
        item = msginfo->folder;
4247
        g_return_val_if_fail(item != NULL, -1);
4248

    
4249
        folder_item_scan(item);
4250
        if (procmsg_msg_exist(msginfo) &&
4251
            (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
4252
                if (folder_item_remove_msg(item, msginfo) < 0) {
4253
                        g_warning(_("can't remove the old message\n"));
4254
                        return -1;
4255
                }
4256
        }
4257

    
4258
        return 0;
4259
}
4260

    
4261
static gint compose_queue(Compose *compose, const gchar *file)
4262
{
4263
        FolderItem *queue;
4264
        gchar *tmp;
4265
        FILE *fp, *src_fp;
4266
        GSList *cur;
4267
        gchar buf[BUFFSIZE];
4268
        gint num;
4269
        MsgFlags flag = {0, MSG_QUEUED};
4270

    
4271
        debug_print(_("queueing message...\n"));
4272
        g_return_val_if_fail(compose->to_list != NULL ||
4273
                             compose->newsgroup_list != NULL,
4274
                             -1);
4275
        g_return_val_if_fail(compose->account != NULL, -1);
4276

    
4277
        tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4278
                              G_DIR_SEPARATOR, compose);
4279
        if ((fp = g_fopen(tmp, "wb")) == NULL) {
4280
                FILE_OP_ERROR(tmp, "fopen");
4281
                g_free(tmp);
4282
                return -1;
4283
        }
4284
        if ((src_fp = g_fopen(file, "rb")) == NULL) {
4285
                FILE_OP_ERROR(file, "fopen");
4286
                fclose(fp);
4287
                g_unlink(tmp);
4288
                g_free(tmp);
4289
                return -1;
4290
        }
4291
        if (change_file_mode_rw(fp, tmp) < 0) {
4292
                FILE_OP_ERROR(tmp, "chmod");
4293
                g_warning(_("can't change file mode\n"));
4294
        }
4295

    
4296
        /* queueing variables */
4297
        fprintf(fp, "AF:\n");
4298
        fprintf(fp, "NF:0\n");
4299
        fprintf(fp, "PS:10\n");
4300
        fprintf(fp, "SRH:1\n");
4301
        fprintf(fp, "SFN:\n");
4302
        fprintf(fp, "DSR:\n");
4303
        if (compose->msgid)
4304
                fprintf(fp, "MID:<%s>\n", compose->msgid);
4305
        else
4306
                fprintf(fp, "MID:\n");
4307
        fprintf(fp, "CFG:\n");
4308
        fprintf(fp, "PT:0\n");
4309
        fprintf(fp, "S:%s\n", compose->account->address);
4310
        fprintf(fp, "RQ:\n");
4311
        if (compose->account->smtp_server)
4312
                fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
4313
        else
4314
                fprintf(fp, "SSV:\n");
4315
        if (compose->account->nntp_server)
4316
                fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
4317
        else
4318
                fprintf(fp, "NSV:\n");
4319
        fprintf(fp, "SSH:\n");
4320
        if (compose->to_list) {
4321
                fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4322
                for (cur = compose->to_list->next; cur != NULL;
4323
                     cur = cur->next)
4324
                        fprintf(fp, ",<%s>", (gchar *)cur->data);
4325
                fprintf(fp, "\n");
4326
        } else
4327
                fprintf(fp, "R:\n");
4328
        /* Sylpheed account ID */
4329
        fprintf(fp, "AID:%d\n", compose->account->account_id);
4330
        /* Reply target */
4331
        if (compose->reply_target)
4332
                fprintf(fp, "REP:%s\n", compose->reply_target);
4333
        /* Forward target */
4334
        if (compose->forward_targets)
4335
                fprintf(fp, "FWD:%s\n", compose->forward_targets);
4336
        fprintf(fp, "\n");
4337

    
4338
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4339
                if (fputs(buf, fp) == EOF) {
4340
                        FILE_OP_ERROR(tmp, "fputs");
4341
                        fclose(fp);
4342
                        fclose(src_fp);
4343
                        g_unlink(tmp);
4344
                        g_free(tmp);
4345
                        return -1;
4346
                }
4347
        }
4348

    
4349
        fclose(src_fp);
4350
        if (fclose(fp) == EOF) {
4351
                FILE_OP_ERROR(tmp, "fclose");
4352
                g_unlink(tmp);
4353
                g_free(tmp);
4354
                return -1;
4355
        }
4356

    
4357
        queue = account_get_special_folder(compose->account, F_QUEUE);
4358
        if (!queue) {
4359
                g_warning(_("can't find queue folder\n"));
4360
                g_unlink(tmp);
4361
                g_free(tmp);
4362
                return -1;
4363
        }
4364
        folder_item_scan(queue);
4365
        if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
4366
                g_warning(_("can't queue the message\n"));
4367
                g_unlink(tmp);
4368
                g_free(tmp);
4369
                return -1;
4370
        }
4371
        g_free(tmp);
4372

    
4373
        if (compose->mode == COMPOSE_REEDIT) {
4374
                compose_remove_reedit_target(compose);
4375
                if (compose->targetinfo &&
4376
                    compose->targetinfo->folder != queue)
4377
                        folderview_update_item
4378
                                (compose->targetinfo->folder, TRUE);
4379
        }
4380

    
4381
        folder_item_scan(queue);
4382
        folderview_update_item(queue, TRUE);
4383

    
4384
        /* Add recipients to addressbook automatically */
4385
        if (prefs_common.recipients_autoreg) {
4386
                compose_add_new_recipients_to_addressbook(compose);
4387
        }
4388

    
4389
        main_window_set_menu_sensitive(main_window_get());
4390
        main_window_set_toolbar_sensitive(main_window_get());
4391

    
4392
        return 0;
4393
}
4394

    
4395
static gint compose_write_attach(Compose *compose, FILE *fp,
4396
                                 const gchar *charset)
4397
{
4398
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
4399
        GtkTreeIter iter;
4400
        gboolean valid;
4401
        AttachInfo *ainfo;
4402
        FILE *attach_fp;
4403
        gint len;
4404
        EncodingType encoding;
4405

    
4406
        for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
4407
             valid = gtk_tree_model_iter_next(model, &iter)) {
4408
                gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
4409

    
4410
                if (!is_file_exist(ainfo->file)) {
4411
                        alertpanel_error(_("File %s doesn't exist."),
4412
                                         ainfo->file);
4413
                        return -1;
4414
                }
4415
                if (get_file_size(ainfo->file) <= 0) {
4416
                        alertpanel_error(_("File %s is empty."), ainfo->file);
4417
                        return -1;
4418
                }
4419
                if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) {
4420
                        alertpanel_error(_("Can't open file %s."), ainfo->file);
4421
                        return -1;
4422
                }
4423

    
4424
                fprintf(fp, "\n--%s\n", compose->boundary);
4425

    
4426
                encoding = ainfo->encoding;
4427

    
4428
                if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) {
4429
                        fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
4430
                        fprintf(fp, "Content-Disposition: inline\n");
4431

    
4432
                        /* message/... shouldn't be encoded */
4433
                        if (encoding == ENC_QUOTED_PRINTABLE ||
4434
                            encoding == ENC_BASE64)
4435
                                encoding = ENC_8BIT;
4436
                } else {
4437
                        if (prefs_common.mime_fencoding_method ==
4438
                            FENC_RFC2231) {
4439
                                gchar *param;
4440

    
4441
                                param = compose_convert_filename
4442
                                        (compose, ainfo->name, "name", charset);
4443
                                fprintf(fp, "Content-Type: %s;\n"
4444
                                            "%s\n",
4445
                                        ainfo->content_type, param);
4446
                                g_free(param);
4447
                                param = compose_convert_filename
4448
                                        (compose, ainfo->name, "filename",
4449
                                         charset);
4450
                                fprintf(fp, "Content-Disposition: attachment;\n"
4451
                                            "%s\n", param);
4452
                                g_free(param);
4453
                        } else {
4454
                                gchar filename[BUFFSIZE];
4455

    
4456
                                compose_convert_header(compose, filename,
4457
                                                       sizeof(filename),
4458
                                                       ainfo->name, 12, FALSE,
4459
                                                       charset);
4460
                                fprintf(fp, "Content-Type: %s;\n"
4461
                                            " name=\"%s\"\n",
4462
                                        ainfo->content_type, filename);
4463
                                fprintf(fp, "Content-Disposition: attachment;\n"
4464
                                            " filename=\"%s\"\n", filename);
4465
                        }
4466

    
4467
#if USE_GPGME
4468
                        /* force encoding to protect trailing spaces */
4469
                        if (rfc2015_is_available() && compose->use_signing &&
4470
                            !compose->account->clearsign) {
4471
                                if (encoding == ENC_7BIT)
4472
                                        encoding = ENC_QUOTED_PRINTABLE;
4473
                                else if (encoding == ENC_8BIT)
4474
                                        encoding = ENC_BASE64;
4475
                        }
4476
#endif
4477
                }
4478

    
4479
                fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
4480
                        procmime_get_encoding_str(encoding));
4481

    
4482
                if (encoding == ENC_BASE64) {
4483
                        gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
4484
                        FILE *tmp_fp = attach_fp;
4485
                        gchar *tmp_file = NULL;
4486
                        ContentType content_type;
4487

    
4488
                        content_type =
4489
                                procmime_scan_mime_type(ainfo->content_type);
4490
                        if (content_type == MIME_TEXT ||
4491
                            content_type == MIME_TEXT_HTML ||
4492
                            content_type == MIME_MESSAGE_RFC822) {
4493
                                tmp_file = get_tmp_file();
4494
                                if (canonicalize_file(ainfo->file, tmp_file) < 0) {
4495
                                        g_free(tmp_file);
4496
                                        fclose(attach_fp);
4497
                                        return -1;
4498
                                }
4499
                                if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
4500
                                        FILE_OP_ERROR(tmp_file, "fopen");
4501
                                        g_unlink(tmp_file);
4502
                                        g_free(tmp_file);
4503
                                        fclose(attach_fp);
4504
                                        return -1;
4505
                                }
4506
                        }
4507

    
4508
                        while ((len = fread(inbuf, sizeof(gchar),
4509
                                            B64_LINE_SIZE, tmp_fp))
4510
                               == B64_LINE_SIZE) {
4511
                                base64_encode(outbuf, (guchar *)inbuf,
4512
                                              B64_LINE_SIZE);
4513
                                fputs(outbuf, fp);
4514
                                fputc('\n', fp);
4515
                        }
4516
                        if (len > 0 && feof(tmp_fp)) {
4517
                                base64_encode(outbuf, (guchar *)inbuf, len);
4518
                                fputs(outbuf, fp);
4519
                                fputc('\n', fp);
4520
                        }
4521

    
4522
                        if (tmp_file) {
4523
                                fclose(tmp_fp);
4524
                                g_unlink(tmp_file);
4525
                                g_free(tmp_file);
4526
                        }
4527
                } else if (encoding == ENC_QUOTED_PRINTABLE) {
4528
                        gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
4529

    
4530
                        while (fgets(inbuf, sizeof(inbuf), attach_fp) != NULL) {
4531
                                qp_encode_line(outbuf, (guchar *)inbuf);
4532
                                fputs(outbuf, fp);
4533
                        }
4534
                } else {
4535
                        gchar buf[BUFFSIZE];
4536

    
4537
                        while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
4538
                                strcrchomp(buf);
4539
                                fputs(buf, fp);
4540
                        }
4541
                }
4542

    
4543
                fclose(attach_fp);
4544
        }
4545

    
4546
        fprintf(fp, "\n--%s--\n", compose->boundary);
4547
        return 0;
4548
}
4549

    
4550
#define QUOTE_REQUIRED(str) \
4551
        (*str != '"' && strpbrk(str, ",.[]<>") != NULL)
4552

    
4553
#define PUT_RECIPIENT_HEADER(header, str)                                     \
4554
{                                                                             \
4555
        if (*str != '\0') {                                                     \
4556
                gchar *dest;                                                     \
4557
                                                                             \
4558
                dest = g_strdup(str);                                             \
4559
                g_strstrip(dest);                                             \
4560
                if (*dest != '\0') {                                             \
4561
                        compose->to_list = address_list_append                     \
4562
                                (compose->to_list, dest);                     \
4563
                        compose_convert_header                                     \
4564
                                (compose, buf, sizeof(buf), dest,             \
4565
                                 strlen(header) + 2, TRUE, charset);             \
4566
                        fprintf(fp, "%s: %s\n", header, buf);                     \
4567
                }                                                             \
4568
                g_free(dest);                                                     \
4569
        }                                                                     \
4570
}
4571

    
4572
#define IS_IN_CUSTOM_HEADER(header) \
4573
        (compose->account->add_customhdr && \
4574
         custom_header_find(compose->account->customhdr_list, header) != NULL)
4575

    
4576
static gint compose_write_headers(Compose *compose, FILE *fp,
4577
                                  const gchar *charset,
4578
                                  const gchar *body_charset,
4579
                                  EncodingType encoding, gboolean is_draft)
4580
{
4581
        gchar buf[BUFFSIZE];
4582
        const gchar *entry_str;
4583
        gchar *str;
4584

    
4585
        g_return_val_if_fail(fp != NULL, -1);
4586
        g_return_val_if_fail(charset != NULL, -1);
4587
        g_return_val_if_fail(compose->account != NULL, -1);
4588
        g_return_val_if_fail(compose->account->address != NULL, -1);
4589

    
4590
        /* Date */
4591
        if (compose->account->add_date) {
4592
                get_rfc822_date(buf, sizeof(buf));
4593
                fprintf(fp, "Date: %s\n", buf);
4594
        }
4595

    
4596
        /* From */
4597
        if (compose->account->name && *compose->account->name) {
4598
                compose_convert_header
4599
                        (compose, buf, sizeof(buf), compose->account->name,
4600
                         strlen("From: "), TRUE, charset);
4601
                if (QUOTE_REQUIRED(buf))
4602
                        fprintf(fp, "From: \"%s\" <%s>\n",
4603
                                buf, compose->account->address);
4604
                else
4605
                        fprintf(fp, "From: %s <%s>\n",
4606
                                buf, compose->account->address);
4607
        } else
4608
                fprintf(fp, "From: %s\n", compose->account->address);
4609

    
4610
        slist_free_strings(compose->to_list);
4611
        g_slist_free(compose->to_list);
4612
        compose->to_list = NULL;
4613

    
4614
        /* To */
4615
        if (compose->use_to) {
4616
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4617
                PUT_RECIPIENT_HEADER("To", entry_str);
4618
        }
4619

    
4620
        slist_free_strings(compose->newsgroup_list);
4621
        g_slist_free(compose->newsgroup_list);
4622
        compose->newsgroup_list = NULL;
4623

    
4624
        /* Newsgroups */
4625
        if (compose->use_newsgroups) {
4626
                entry_str = gtk_entry_get_text
4627
                        (GTK_ENTRY(compose->newsgroups_entry));
4628
                if (*entry_str != '\0') {
4629
                        Xstrdup_a(str, entry_str, return -1);
4630
                        g_strstrip(str);
4631
                        remove_space(str);
4632
                        if (*str != '\0') {
4633
                                compose->newsgroup_list =
4634
                                        newsgroup_list_append
4635
                                                (compose->newsgroup_list, str);
4636
                                compose_convert_header(compose,
4637
                                                       buf, sizeof(buf), str,
4638
                                                       strlen("Newsgroups: "),
4639
                                                       FALSE, charset);
4640
                                fprintf(fp, "Newsgroups: %s\n", buf);
4641
                        }
4642
                }
4643
        }
4644

    
4645
        /* Cc */
4646
        if (compose->use_cc) {
4647
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4648
                PUT_RECIPIENT_HEADER("Cc", entry_str);
4649
        }
4650

    
4651
        /* Bcc */
4652
        if (compose->use_bcc) {
4653
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4654
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4655
        }
4656

    
4657
        if (!is_draft && !compose->to_list && !compose->newsgroup_list)
4658
                return -1;
4659

    
4660
        /* Subject */
4661
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4662
        if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4663
                Xstrdup_a(str, entry_str, return -1);
4664
                g_strstrip(str);
4665
                if (*str != '\0') {
4666
                        compose_convert_header(compose, buf, sizeof(buf), str,
4667
                                               strlen("Subject: "), FALSE,
4668
                                               charset);
4669
                        fprintf(fp, "Subject: %s\n", buf);
4670
                }
4671
        }
4672

    
4673
        /* Message-ID */
4674
        if (compose->account->gen_msgid) {
4675
                compose_generate_msgid(compose, buf, sizeof(buf));
4676
                fprintf(fp, "Message-Id: <%s>\n", buf);
4677
                compose->msgid = g_strdup(buf);
4678
        }
4679

    
4680
        /* In-Reply-To */
4681
        if (compose->inreplyto && compose->to_list)
4682
                fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
4683

    
4684
        /* References */
4685
        if (compose->references)
4686
                fprintf(fp, "References: %s\n", compose->references);
4687

    
4688
        /* Followup-To */
4689
        if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
4690
                entry_str = gtk_entry_get_text
4691
                        (GTK_ENTRY(compose->followup_entry));
4692
                if (*entry_str != '\0') {
4693
                        Xstrdup_a(str, entry_str, return -1);
4694
                        g_strstrip(str);
4695
                        remove_space(str);
4696
                        if (*str != '\0') {
4697
                                compose_convert_header(compose,
4698
                                                       buf, sizeof(buf), str,
4699
                                                       strlen("Followup-To: "),
4700
                                                       FALSE, charset);
4701
                                fprintf(fp, "Followup-To: %s\n", buf);
4702
                        }
4703
                }
4704
        }
4705

    
4706
        /* Reply-To */
4707
        if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
4708
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4709
                if (*entry_str != '\0') {
4710
                        Xstrdup_a(str, entry_str, return -1);
4711
                        g_strstrip(str);
4712
                        if (*str != '\0') {
4713
                                compose_convert_header(compose,
4714
                                                       buf, sizeof(buf), str,
4715
                                                       strlen("Reply-To: "),
4716
                                                       TRUE, charset);
4717
                                fprintf(fp, "Reply-To: %s\n", buf);
4718
                        }
4719
                }
4720
        }
4721

    
4722
        /* Disposition-Notification-To */
4723
        if (compose->use_mdn &&
4724
            !IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
4725
                fprintf(fp, "Disposition-Notification-To: %s\n",
4726
                        compose->account->address);
4727
        }
4728

    
4729
        /* Organization */
4730
        if (compose->account->organization &&
4731
            !IS_IN_CUSTOM_HEADER("Organization")) {
4732
                compose_convert_header(compose, buf, sizeof(buf),
4733
                                       compose->account->organization,
4734
                                       strlen("Organization: "), FALSE,
4735
                                       charset);
4736
                fprintf(fp, "Organization: %s\n", buf);
4737
        }
4738

    
4739
        /* Program version and system info */