Statistics
| Revision:

root / src / compose.c @ 2022

History | View | Annotate | Download (208 KB)

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

    
137
#if USE_GPGME
138
#  include "rfc2015.h"
139
#endif
140

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

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

    
169
#define B64_LINE_SIZE                57
170
#define B64_BUFFSIZE                77
171

    
172
#define MAX_REFERENCES_LEN        999
173

    
174
#define TEXTVIEW_MARGIN                6
175

    
176
static GdkColor quote_color = {0, 0, 0, 0xbfff};
177

    
178
static GList *compose_list = NULL;
179

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

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

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

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

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

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

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

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

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

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

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

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

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

    
290
static void compose_convert_header                (Compose        *compose,
291
                                                 gchar                *dest,
292
                                                 gint                 len,
293
                                                 const gchar        *src,
294
                                                 gint                 header_len,
295
                                                 gboolean         addr_field,
296
                                                 const gchar        *encoding);
297
static gchar *compose_convert_filename                (Compose        *compose,
298
                                                 const gchar        *src,
299
                                                 const gchar        *param_name,
300
                                                 const gchar        *encoding);
301
static void compose_generate_msgid                (Compose        *compose,
302
                                                 gchar                *buf,
303
                                                 gint                 len);
304

    
305
static void compose_attach_info_free                (AttachInfo        *ainfo);
306
static void compose_attach_remove_selected        (Compose        *compose);
307

    
308
static void compose_attach_property                (Compose        *compose);
309
static void compose_attach_property_create        (gboolean        *cancelled);
310
static void attach_property_ok                        (GtkWidget        *widget,
311
                                                 gboolean        *cancelled);
312
static void attach_property_cancel                (GtkWidget        *widget,
313
                                                 gboolean        *cancelled);
314
static gint attach_property_delete_event        (GtkWidget        *widget,
315
                                                 GdkEventAny        *event,
316
                                                 gboolean        *cancelled);
317
static gboolean attach_property_key_pressed        (GtkWidget        *widget,
318
                                                 GdkEventKey        *event,
319
                                                 gboolean        *cancelled);
320

    
321
static void compose_exec_ext_editor                (Compose        *compose);
322
static gboolean compose_ext_editor_kill                (Compose        *compose);
323
static void compose_ext_editor_child_exit        (GPid                 pid,
324
                                                 gint                 status,
325
                                                 gpointer         data);
326
static void compose_set_ext_editor_sensitive        (Compose        *compose,
327
                                                 gboolean         sensitive);
328

    
329
static void compose_undo_state_changed                (UndoMain        *undostruct,
330
                                                 gint                 undo_state,
331
                                                 gint                 redo_state,
332
                                                 gpointer         data);
333

    
334
static gint calc_cursor_xpos        (GtkTextView        *text,
335
                                 gint                 extra,
336
                                 gint                 char_width);
337

    
338
/* callback functions */
339

    
340
static gboolean compose_edit_size_alloc (GtkEditable        *widget,
341
                                         GtkAllocation        *allocation,
342
                                         GtkSHRuler        *shruler);
343

    
344
static void toolbar_send_cb                (GtkWidget        *widget,
345
                                         gpointer         data);
346
static void toolbar_send_later_cb        (GtkWidget        *widget,
347
                                         gpointer         data);
348
static void toolbar_draft_cb                (GtkWidget        *widget,
349
                                         gpointer         data);
350
static void toolbar_insert_cb                (GtkWidget        *widget,
351
                                         gpointer         data);
352
static void toolbar_attach_cb                (GtkWidget        *widget,
353
                                         gpointer         data);
354
static void toolbar_sig_cb                (GtkWidget        *widget,
355
                                         gpointer         data);
356
static void toolbar_ext_editor_cb        (GtkWidget        *widget,
357
                                         gpointer         data);
358
static void toolbar_linewrap_cb                (GtkWidget        *widget,
359
                                         gpointer         data);
360
static void toolbar_address_cb                (GtkWidget        *widget,
361
                                         gpointer         data);
362
static void toolbar_prefs_common_cb        (GtkWidget        *widget,
363
                                         gpointer         data);
364
static void toolbar_prefs_account_cb        (GtkWidget        *widget,
365
                                         gpointer         data);
366

    
367
static gboolean toolbar_button_pressed        (GtkWidget        *widget,
368
                                         GdkEventButton        *event,
369
                                         gpointer         data);
370

    
371
static void account_activated                (GtkMenuItem        *menuitem,
372
                                         gpointer         data);
373

    
374
static void attach_selection_changed        (GtkTreeSelection        *selection,
375
                                         gpointer                 data);
376

    
377
static gboolean attach_button_pressed        (GtkWidget        *widget,
378
                                         GdkEventButton        *event,
379
                                         gpointer         data);
380
static gboolean attach_key_pressed        (GtkWidget        *widget,
381
                                         GdkEventKey        *event,
382
                                         gpointer         data);
383

    
384
static void compose_send_cb                (gpointer         data,
385
                                         guint                 action,
386
                                         GtkWidget        *widget);
387
static void compose_send_later_cb        (gpointer         data,
388
                                         guint                 action,
389
                                         GtkWidget        *widget);
390

    
391
static void compose_draft_cb                (gpointer         data,
392
                                         guint                 action,
393
                                         GtkWidget        *widget);
394

    
395
static void compose_attach_cb                (gpointer         data,
396
                                         guint                 action,
397
                                         GtkWidget        *widget);
398
static void compose_insert_file_cb        (gpointer         data,
399
                                         guint                 action,
400
                                         GtkWidget        *widget);
401
static void compose_insert_sig_cb        (gpointer         data,
402
                                         guint                 action,
403
                                         GtkWidget        *widget);
404

    
405
static void compose_close_cb                (gpointer         data,
406
                                         guint                 action,
407
                                         GtkWidget        *widget);
408

    
409
static void compose_set_encoding_cb        (gpointer         data,
410
                                         guint                 action,
411
                                         GtkWidget        *widget);
412

    
413
static void compose_address_cb                (gpointer         data,
414
                                         guint                 action,
415
                                         GtkWidget        *widget);
416
static void compose_template_activate_cb(GtkWidget        *widget,
417
                                         gpointer         data);
418

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

    
423
static gint compose_delete_cb                (GtkWidget        *widget,
424
                                         GdkEventAny        *event,
425
                                         gpointer         data);
426

    
427
static gint compose_window_state_cb        (GtkWidget                *widget,
428
                                         GdkEventWindowState        *event,
429
                                         gpointer                 data);
430

    
431
static void compose_undo_cb                (Compose        *compose);
432
static void compose_redo_cb                (Compose        *compose);
433
static void compose_cut_cb                (Compose        *compose);
434
static void compose_copy_cb                (Compose        *compose);
435
static void compose_paste_cb                (Compose        *compose);
436
static void compose_paste_as_quote_cb        (Compose        *compose);
437
static void compose_allsel_cb                (Compose        *compose);
438

    
439
static void compose_grab_focus_cb        (GtkWidget        *widget,
440
                                         Compose        *compose);
441

    
442
#if USE_GPGME
443
static void compose_signing_toggled        (GtkWidget        *widget,
444
                                         Compose        *compose);
445
static void compose_encrypt_toggled        (GtkWidget        *widget,
446
                                         Compose        *compose);
447
#endif
448

    
449
#if 0
450
static void compose_attach_toggled        (GtkWidget        *widget,
451
                                         Compose        *compose);
452
#endif
453

    
454
static void compose_buffer_changed_cb        (GtkTextBuffer        *textbuf,
455
                                         Compose        *compose);
456
static void compose_changed_cb                (GtkEditable        *editable,
457
                                         Compose        *compose);
458

    
459
static void compose_wrap_cb                (gpointer         data,
460
                                         guint                 action,
461
                                         GtkWidget        *widget);
462
static void compose_toggle_autowrap_cb        (gpointer         data,
463
                                         guint                 action,
464
                                         GtkWidget        *widget);
465

    
466
static void compose_toggle_to_cb        (gpointer         data,
467
                                         guint                 action,
468
                                         GtkWidget        *widget);
469
static void compose_toggle_cc_cb        (gpointer         data,
470
                                         guint                 action,
471
                                         GtkWidget        *widget);
472
static void compose_toggle_bcc_cb        (gpointer         data,
473
                                         guint                 action,
474
                                         GtkWidget        *widget);
475
static void compose_toggle_replyto_cb        (gpointer         data,
476
                                         guint                 action,
477
                                         GtkWidget        *widget);
478
static void compose_toggle_followupto_cb(gpointer         data,
479
                                         guint                 action,
480
                                         GtkWidget        *widget);
481
static void compose_toggle_ruler_cb        (gpointer         data,
482
                                         guint                 action,
483
                                         GtkWidget        *widget);
484
static void compose_toggle_attach_cb        (gpointer         data,
485
                                         guint                 action,
486
                                         GtkWidget        *widget);
487
static void compose_customize_toolbar_cb(gpointer         data,
488
                                         guint                 action,
489
                                         GtkWidget        *widget);
490

    
491
#if USE_GPGME
492
static void compose_toggle_sign_cb        (gpointer         data,
493
                                         guint                 action,
494
                                         GtkWidget        *widget);
495
static void compose_toggle_encrypt_cb        (gpointer         data,
496
                                         guint                 action,
497
                                         GtkWidget        *widget);
498
#endif
499

    
500
#if USE_GTKSPELL
501
static void compose_set_spell_lang_menu (Compose        *compose);
502
static void compose_toggle_spell_cb        (gpointer         data,
503
                                         guint                 action,
504
                                         GtkWidget        *widget);
505
static void compose_set_spell_lang_cb        (GtkWidget        *widget,
506
                                         gpointer         data);
507
#endif
508

    
509
static void compose_attach_drag_received_cb (GtkWidget                *widget,
510
                                             GdkDragContext        *drag_context,
511
                                             gint                 x,
512
                                             gint                 y,
513
                                             GtkSelectionData        *data,
514
                                             guint                 info,
515
                                             guint                 time,
516
                                             gpointer                 user_data);
517
static void compose_insert_drag_received_cb (GtkWidget                *widget,
518
                                             GdkDragContext        *drag_context,
519
                                             gint                 x,
520
                                             gint                 y,
521
                                             GtkSelectionData        *data,
522
                                             guint                 info,
523
                                             guint                 time,
524
                                             gpointer                 user_data);
525

    
526
static void to_activated                (GtkWidget        *widget,
527
                                         Compose        *compose);
528
static void newsgroups_activated        (GtkWidget        *widget,
529
                                         Compose        *compose);
530
static void cc_activated                (GtkWidget        *widget,
531
                                         Compose        *compose);
532
static void bcc_activated                (GtkWidget        *widget,
533
                                         Compose        *compose);
534
static void replyto_activated                (GtkWidget        *widget,
535
                                         Compose        *compose);
536
static void followupto_activated        (GtkWidget        *widget,
537
                                         Compose        *compose);
538
static void subject_activated                (GtkWidget        *widget,
539
                                         Compose        *compose);
540

    
541
static void text_inserted                (GtkTextBuffer        *buffer,
542
                                         GtkTextIter        *iter,
543
                                         const gchar        *text,
544
                                         gint                 len,
545
                                         Compose        *compose);
546

    
547
static gboolean autosave_timeout        (gpointer         data);
548

    
549

    
550
static GtkItemFactoryEntry compose_popup_entries[] =
551
{
552
        {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
553
        {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
554
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
555
        {N_("/_Properties..."),        NULL, compose_attach_property, 0, NULL}
556
};
557

    
558
static GtkItemFactoryEntry compose_entries[] =
559
{
560
        {N_("/_File"),                                NULL, NULL, 0, "<Branch>"},
561
        {N_("/_File/_Send"),                        "<control>Return",
562
                                                compose_send_cb, 0, NULL},
563
        {N_("/_File/Send _later"),                "<shift><control>S",
564
                                                compose_send_later_cb,  0, NULL},
565
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
566
        {N_("/_File/Save to _draft folder"),
567
                                                "<shift><control>D", compose_draft_cb, 0, NULL},
568
        {N_("/_File/Save and _keep editing"),
569
                                                "<control>S", compose_draft_cb, 1, NULL},
570
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
571
        {N_("/_File/_Attach file"),                "<control>M", compose_attach_cb,      0, NULL},
572
        {N_("/_File/_Insert file"),                "<control>I", compose_insert_file_cb, 0, NULL},
573
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
574
        {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig_cb,  0, NULL},
575
        {N_("/_File/A_ppend signature"),        "<shift><control>G", compose_insert_sig_cb,  1, NULL},
576
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
577
        {N_("/_File/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
578

    
579
        {N_("/_Edit"),                        NULL, NULL, 0, "<Branch>"},
580
        {N_("/_Edit/_Undo"),                "<control>Z", compose_undo_cb, 0, NULL},
581
        {N_("/_Edit/_Redo"),                "<control>Y", compose_redo_cb, 0, NULL},
582
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
583
        {N_("/_Edit/Cu_t"),                "<control>X", compose_cut_cb,    0, NULL},
584
        {N_("/_Edit/_Copy"),                "<control>C", compose_copy_cb,   0, NULL},
585
        {N_("/_Edit/_Paste"),                "<control>V", compose_paste_cb,  0, NULL},
586
        {N_("/_Edit/Paste as _quotation"),
587
                                        NULL, compose_paste_as_quote_cb, 0, NULL},
588
        {N_("/_Edit/Select _all"),        "<control>A", compose_allsel_cb, 0, NULL},
589
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
590
        {N_("/_Edit/_Wrap current paragraph"),
591
                                        "<control>L", compose_wrap_cb, 0, NULL},
592
        {N_("/_Edit/Wrap all long _lines"),
593
                                        "<control><alt>L", compose_wrap_cb, 1, NULL},
594
        {N_("/_Edit/Aut_o wrapping"),        "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
595
        {N_("/_View"),                        NULL, NULL, 0, "<Branch>"},
596
        {N_("/_View/_To"),                NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
597
        {N_("/_View/_Cc"),                NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
598
        {N_("/_View/_Bcc"),                NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
599
        {N_("/_View/_Reply-To"),        NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
600
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
601
        {N_("/_View/_Followup-To"),        NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
602
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
603
        {N_("/_View/R_uler"),                NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
604
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
605
        {N_("/_View/_Attachment"),        NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
606
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
607
        {N_("/_View/Cu_stomize toolbar..."),
608
                                        NULL, compose_customize_toolbar_cb, 0, NULL},
609
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
610

    
611
#define ENC_ACTION(action) \
612
        NULL, compose_set_encoding_cb, action, \
613
        "/View/Character encoding/Automatic"
614

    
615
        {N_("/_View/Character _encoding"), NULL, NULL, 0, "<Branch>"},
616
        {N_("/_View/Character _encoding/_Automatic"),
617
                        NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
618
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
619

    
620
        {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"),
621
         ENC_ACTION(C_US_ASCII)},
622
        {N_("/_View/Character _encoding/Unicode (_UTF-8)"),
623
         ENC_ACTION(C_UTF_8)},
624
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
625

    
626
        {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"),
627
         ENC_ACTION(C_ISO_8859_1)},
628
        {N_("/_View/Character _encoding/Western European (ISO-8859-15)"),
629
         ENC_ACTION(C_ISO_8859_15)},
630
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
631

    
632
        {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"),
633
         ENC_ACTION(C_ISO_8859_2)},
634
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
635

    
636
        {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"),
637
         ENC_ACTION(C_ISO_8859_13)},
638
        {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"),
639
         ENC_ACTION(C_ISO_8859_4)},
640
        {N_("/_View/Character _encoding/Baltic (Windows-1257)"),
641
         ENC_ACTION(C_WINDOWS_1257)},
642
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
643

    
644
        {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"),
645
         ENC_ACTION(C_ISO_8859_7)},
646
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
647

    
648
        {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"),
649
         ENC_ACTION(C_ISO_8859_6)},
650
        {N_("/_View/Character _encoding/Arabic (Windows-1256)"),
651
         ENC_ACTION(C_WINDOWS_1256)},
652
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
653

    
654
        {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"),
655
         ENC_ACTION(C_ISO_8859_8)},
656
        {N_("/_View/Character _encoding/Hebrew (Windows-1255)"),
657
         ENC_ACTION(C_WINDOWS_1255)},
658
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
659

    
660
        {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"),
661
         ENC_ACTION(C_ISO_8859_9)},
662
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
663

    
664
        {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"),
665
         ENC_ACTION(C_ISO_8859_5)},
666
        {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"),
667
         ENC_ACTION(C_KOI8_R)},
668
        {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"),
669
         ENC_ACTION(C_KOI8_U)},
670
        {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"),
671
         ENC_ACTION(C_WINDOWS_1251)},
672
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
673

    
674
        {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"),
675
         ENC_ACTION(C_ISO_2022_JP)},
676
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
677

    
678
        {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"),
679
         ENC_ACTION(C_GB2312)},
680
        {N_("/_View/Character _encoding/Simplified Chinese (GBK)"),
681
         ENC_ACTION(C_GBK)},
682
        {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"),
683
         ENC_ACTION(C_BIG5)},
684
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
685

    
686
        {N_("/_View/Character _encoding/Korean (EUC-_KR)"),
687
         ENC_ACTION(C_EUC_KR)},
688
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
689

    
690
        {N_("/_View/Character _encoding/Thai (TIS-620)"),
691
         ENC_ACTION(C_TIS_620)},
692
        {N_("/_View/Character _encoding/Thai (Windows-874)"),
693
         ENC_ACTION(C_WINDOWS_874)},
694

    
695
        {N_("/_Tools"),                        NULL, NULL, 0, "<Branch>"},
696
        {N_("/_Tools/_Address book"),        "<shift><control>A", compose_address_cb , 0, NULL},
697
        {N_("/_Tools/_Template"),        NULL, NULL, 0, "<Branch>"},
698
#ifndef G_OS_WIN32
699
        {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
700
#endif
701
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
702
        {N_("/_Tools/Edit with e_xternal editor"),
703
                                        "<shift><control>X", compose_ext_editor_cb, 0, NULL},
704
#if USE_GPGME
705
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
706
        {N_("/_Tools/PGP Si_gn"),           NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
707
        {N_("/_Tools/PGP _Encrypt"),        NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
708
#endif /* USE_GPGME */
709

    
710
#if USE_GTKSPELL
711
        {N_("/_Tools/---"),                        NULL, NULL, 0, "<Separator>"},
712
        {N_("/_Tools/_Check spell"),                NULL, compose_toggle_spell_cb, 0, "<ToggleItem>"},
713
        {N_("/_Tools/_Set spell language"),        NULL, NULL, 0, "<Branch>"},
714
#endif /* USE_GTKSPELL */
715

    
716
        {N_("/_Help"),                        NULL, NULL, 0, "<Branch>"},
717
        {N_("/_Help/_About"),                NULL, about_show, 0, NULL}
718
};
719

    
720
enum
721
{
722
        DRAG_TYPE_RFC822,
723
        DRAG_TYPE_URI_LIST,
724

    
725
        N_DRAG_TYPES
726
};
727

    
728
static GtkTargetEntry compose_drag_types[] =
729
{
730
        {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
731
        {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
732
};
733

    
734

    
735
Compose *compose_new(PrefsAccount *account, FolderItem *item,
736
                     const gchar *mailto, GPtrArray *attach_files)
737
{
738
        Compose *compose;
739
        GtkTextView *text;
740
        GtkTextBuffer *buffer;
741
        GtkTextIter iter;
742

    
743
        if (!account) account = cur_account;
744
        g_return_val_if_fail(account != NULL, NULL);
745

    
746
        compose = compose_create(account, COMPOSE_NEW);
747

    
748
        undo_block(compose->undostruct);
749

    
750
        if (prefs_common.auto_sig)
751
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
752

    
753
        text = GTK_TEXT_VIEW(compose->text);
754
        buffer = gtk_text_view_get_buffer(text);
755
        gtk_text_buffer_get_start_iter(buffer, &iter);
756
        gtk_text_buffer_place_cursor(buffer, &iter);
757

    
758
        if (account->protocol != A_NNTP) {
759
                if (mailto && *mailto != '\0') {
760
                        compose_entries_set(compose, mailto);
761
                        gtk_widget_grab_focus(compose->subject_entry);
762
                } else if (item) {
763
                        compose_entries_set_from_item
764
                                (compose, item, COMPOSE_NEW);
765
                        if (item->auto_to)
766
                                gtk_widget_grab_focus(compose->subject_entry);
767
                        else
768
                                gtk_widget_grab_focus(compose->to_entry);
769
                } else
770
                        gtk_widget_grab_focus(compose->to_entry);
771
        } else {
772
                if (mailto && *mailto != '\0') {
773
                        compose_entry_append(compose, mailto,
774
                                             COMPOSE_ENTRY_NEWSGROUPS);
775
                        gtk_widget_grab_focus(compose->subject_entry);
776
                } else
777
                        gtk_widget_grab_focus(compose->newsgroups_entry);
778
        }
779

    
780
        if (attach_files) {
781
                gint i;
782
                gchar *file;
783

    
784
                for (i = 0; i < attach_files->len; i++) {
785
                        gchar *utf8file;
786

    
787
                        file = g_ptr_array_index(attach_files, i);
788
                        utf8file = conv_filename_to_utf8(file);
789
                        compose_attach_append(compose, file, utf8file, NULL);
790
                        g_free(utf8file);
791
                }
792
        }
793

    
794
        undo_unblock(compose->undostruct);
795

    
796
        compose_connect_changed_callbacks(compose);
797
        compose_set_title(compose);
798

    
799
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
800
                compose->autosave_tag =
801
                        g_timeout_add(prefs_common.autosave_itv * 60 * 1000,
802
                                      autosave_timeout, compose);
803
        if (prefs_common.auto_exteditor)
804
                compose_exec_ext_editor(compose);
805

    
806
        return compose;
807
}
808

    
809
void compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode,
810
                   const gchar *body)
811
{
812
        Compose *compose;
813
        PrefsAccount *account = NULL;
814
        MsgInfo *replyinfo;
815
        GtkTextBuffer *buffer;
816
        GtkTextIter iter;
817
        gboolean quote = FALSE;
818

    
819
        g_return_if_fail(msginfo != NULL);
820

    
821
        if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
822
                quote = TRUE;
823

    
824
        if (msginfo->folder)
825
                account = account_find_from_item_property(msginfo->folder);
826
        if (!account && msginfo->to && prefs_common.reply_account_autosel) {
827
                gchar *to;
828
                Xstrdup_a(to, msginfo->to, return);
829
                extract_address(to);
830
                account = account_find_from_address(to);
831
        }
832
        if (!account && msginfo->folder && msginfo->folder->folder)
833
                account = msginfo->folder->folder->account;
834
        if (!account)
835
                account = cur_account;
836
        g_return_if_fail(account != NULL);
837

    
838
        compose = compose_create(account, COMPOSE_REPLY);
839

    
840
        replyinfo = procmsg_msginfo_get_full_info(msginfo);
841
        if (!replyinfo)
842
                replyinfo = procmsg_msginfo_copy(msginfo);
843
        if (msginfo->folder) {
844
                gchar *id;
845
                id = folder_item_get_identifier(msginfo->folder);
846
                if (id) {
847
                        compose->reply_target = g_strdup_printf
848
                                ("%s/%u", id, msginfo->msgnum);
849
                        g_free(id);
850
                }
851
        }
852

    
853
        if (compose_parse_header(compose, msginfo) < 0) return;
854

    
855
        undo_block(compose->undostruct);
856

    
857
        compose_reply_set_entry(compose, msginfo, mode);
858
        if (item)
859
                compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
860

    
861
        if (quote) {
862
                gchar *qmark;
863
                gchar *quote_str;
864

    
865
                if (prefs_common.quotemark && *prefs_common.quotemark)
866
                        qmark = prefs_common.quotemark;
867
                else
868
                        qmark = "> ";
869

    
870
                quote_str = compose_quote_fmt(compose, replyinfo,
871
                                              prefs_common.quotefmt,
872
                                              qmark, body);
873
        }
874

    
875
        if (prefs_common.auto_sig)
876
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
877

    
878
        if (quote && prefs_common.linewrap_quote)
879
                compose_wrap_all(compose);
880

    
881
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
882
        gtk_text_buffer_get_start_iter(buffer, &iter);
883
        gtk_text_buffer_place_cursor(buffer, &iter);
884

    
885
        gtk_widget_grab_focus(compose->text);
886

    
887
        undo_unblock(compose->undostruct);
888

    
889
        compose_connect_changed_callbacks(compose);
890
        compose_set_title(compose);
891

    
892
#if USE_GPGME
893
        if (rfc2015_is_available() && account->encrypt_reply &&
894
            MSG_IS_ENCRYPTED(replyinfo->flags)) {
895
                GtkItemFactory *ifactory;
896

    
897
                ifactory = gtk_item_factory_from_widget(compose->menubar);
898
                menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
899
        }
900
#endif
901

    
902
        procmsg_msginfo_free(replyinfo);
903

    
904
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
905
                compose->autosave_tag =
906
                        g_timeout_add(prefs_common.autosave_itv * 60 * 1000,
907
                                      autosave_timeout, compose);
908
        if (prefs_common.auto_exteditor)
909
                compose_exec_ext_editor(compose);
910
}
911

    
912
void compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach,
913
                     const gchar *body)
914
{
915
        Compose *compose;
916
        PrefsAccount *account = NULL;
917
        GtkTextView *text;
918
        GtkTextBuffer *buffer;
919
        GtkTextIter iter;
920
        GSList *cur;
921
        MsgInfo *msginfo;
922
        GString *forward_targets;
923

    
924
        g_return_if_fail(mlist != NULL);
925

    
926
        msginfo = (MsgInfo *)mlist->data;
927

    
928
        if (msginfo->folder)
929
                account = account_find_from_item(msginfo->folder);
930
        if (!account) account = cur_account;
931
        g_return_if_fail(account != NULL);
932

    
933
        compose = compose_create(account, COMPOSE_FORWARD);
934

    
935
        forward_targets = g_string_new("");
936
        for (cur = mlist; cur != NULL; cur = cur->next) {
937
                msginfo = (MsgInfo *)cur->data;
938
                if (msginfo->folder) {
939
                        gchar *id;
940
                        id = folder_item_get_identifier(msginfo->folder);
941
                        if (id) {
942
                                if (forward_targets->len > 0)
943
                                        g_string_append(forward_targets,
944
                                                        "\n ");
945
                                g_string_append_printf(forward_targets, "%s/%u",
946
                                                       id, msginfo->msgnum);
947
                                g_free(id);
948
                        }
949
                }
950
        }
951
        if (forward_targets->len > 0)
952
                compose->forward_targets = g_string_free(forward_targets,
953
                                                         FALSE);
954
        else
955
                g_string_free(forward_targets, TRUE);
956

    
957
        undo_block(compose->undostruct);
958

    
959
        compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
960
        if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
961
                compose_entry_append(compose, msginfo->subject,
962
                                     COMPOSE_ENTRY_SUBJECT);
963
        if (item)
964
                compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
965

    
966
        text = GTK_TEXT_VIEW(compose->text);
967
        buffer = gtk_text_view_get_buffer(text);
968

    
969
        for (cur = mlist; cur != NULL; cur = cur->next) {
970
                msginfo = (MsgInfo *)cur->data;
971

    
972
                if (as_attach) {
973
                        gchar *msgfile;
974

    
975
                        msgfile = procmsg_get_message_file_path(msginfo);
976
                        if (!is_file_exist(msgfile))
977
                                g_warning(_("%s: file not exist\n"), msgfile);
978
                        else
979
                                compose_attach_append(compose, msgfile, msgfile,
980
                                                      "message/rfc822");
981

    
982
                        g_free(msgfile);
983
                } else {
984
                        gchar *qmark;
985
                        gchar *quote_str;
986
                        MsgInfo *full_msginfo;
987

    
988
                        full_msginfo = procmsg_msginfo_get_full_info(msginfo);
989
                        if (!full_msginfo)
990
                                full_msginfo = procmsg_msginfo_copy(msginfo);
991

    
992
                        if (cur != mlist) {
993
                                GtkTextMark *mark;
994
                                mark = gtk_text_buffer_get_insert(buffer);
995
                                gtk_text_buffer_get_iter_at_mark
996
                                        (buffer, &iter, mark);
997
                                gtk_text_buffer_insert
998
                                        (buffer, &iter, "\n\n", 2);
999
                        }
1000

    
1001
                        if (prefs_common.fw_quotemark &&
1002
                            *prefs_common.fw_quotemark)
1003
                                qmark = prefs_common.fw_quotemark;
1004
                        else
1005
                                qmark = "> ";
1006

    
1007
                        quote_str = compose_quote_fmt(compose, full_msginfo,
1008
                                                      prefs_common.fw_quotefmt,
1009
                                                      qmark, body);
1010
                        compose_attach_parts(compose, msginfo);
1011

    
1012
                        procmsg_msginfo_free(full_msginfo);
1013

    
1014
                        if (body) break;
1015
                }
1016
        }
1017

    
1018
        if (prefs_common.auto_sig)
1019
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1020

    
1021
        if (prefs_common.linewrap_quote)
1022
                compose_wrap_all(compose);
1023

    
1024
        gtk_text_buffer_get_start_iter(buffer, &iter);
1025
        gtk_text_buffer_place_cursor(buffer, &iter);
1026

    
1027
        undo_unblock(compose->undostruct);
1028

    
1029
        compose_connect_changed_callbacks(compose);
1030
        compose_set_title(compose);
1031

    
1032
        if (account->protocol != A_NNTP)
1033
                gtk_widget_grab_focus(compose->to_entry);
1034
        else
1035
                gtk_widget_grab_focus(compose->newsgroups_entry);
1036

    
1037
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1038
                compose->autosave_tag =
1039
                        g_timeout_add(prefs_common.autosave_itv * 60 * 1000,
1040
                                      autosave_timeout, compose);
1041
        if (prefs_common.auto_exteditor)
1042
                compose_exec_ext_editor(compose);
1043
}
1044

    
1045
void compose_redirect(MsgInfo *msginfo, FolderItem *item)
1046
{
1047
        Compose *compose;
1048
        PrefsAccount *account;
1049
        GtkTextView *text;
1050
        GtkTextBuffer *buffer;
1051
        GtkTextMark *mark;
1052
        GtkTextIter iter;
1053
        FILE *fp;
1054
        gchar buf[BUFFSIZE];
1055

    
1056
        g_return_if_fail(msginfo != NULL);
1057
        g_return_if_fail(msginfo->folder != NULL);
1058

    
1059
        account = account_find_from_item(msginfo->folder);
1060
        if (!account) account = cur_account;
1061
        g_return_if_fail(account != NULL);
1062

    
1063
        compose = compose_create(account, COMPOSE_REDIRECT);
1064
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1065

    
1066
        if (compose_parse_header(compose, msginfo) < 0) return;
1067

    
1068
        undo_block(compose->undostruct);
1069

    
1070
        if (msginfo->subject)
1071
                compose_entry_set(compose, msginfo->subject,
1072
                                  COMPOSE_ENTRY_SUBJECT);
1073
        compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
1074

    
1075
        text = GTK_TEXT_VIEW(compose->text);
1076
        buffer = gtk_text_view_get_buffer(text);
1077
        mark = gtk_text_buffer_get_insert(buffer);
1078
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1079

    
1080
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1081
                g_warning(_("Can't get text part\n"));
1082
        else {
1083
                gboolean prev_autowrap = compose->autowrap;
1084

    
1085
                compose->autowrap = FALSE;
1086
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1087
                        strcrchomp(buf);
1088
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1089
                }
1090
                compose->autowrap = prev_autowrap;
1091
                fclose(fp);
1092
        }
1093
        compose_attach_parts(compose, msginfo);
1094

    
1095
        if (account->protocol != A_NNTP)
1096
                gtk_widget_grab_focus(compose->to_entry);
1097
        else
1098
                gtk_widget_grab_focus(compose->newsgroups_entry);
1099

    
1100
        gtk_text_view_set_editable(text, FALSE);
1101

    
1102
        undo_unblock(compose->undostruct);
1103

    
1104
        compose_connect_changed_callbacks(compose);
1105
        compose_set_title(compose);
1106
}
1107

    
1108
void compose_reedit(MsgInfo *msginfo)
1109
{
1110
        Compose *compose;
1111
        PrefsAccount *account;
1112
        GtkTextView *text;
1113
        GtkTextBuffer *buffer;
1114
        GtkTextMark *mark;
1115
        GtkTextIter iter;
1116
        FILE *fp;
1117
        gchar buf[BUFFSIZE];
1118
        const gchar *str;
1119
        GtkWidget *focus_widget = NULL;
1120

    
1121
        g_return_if_fail(msginfo != NULL);
1122
        g_return_if_fail(msginfo->folder != NULL);
1123

    
1124
        account = account_find_from_msginfo(msginfo);
1125
        if (!account) account = cur_account;
1126
        g_return_if_fail(account != NULL);
1127

    
1128
        if (msginfo->folder->stype == F_DRAFT ||
1129
            msginfo->folder->stype == F_QUEUE) {
1130
                compose = compose_find_window_by_target(msginfo);
1131
                if (compose) {
1132
                        debug_print
1133
                                ("compose_reedit(): existing window found.\n");
1134
                        gtk_window_present(GTK_WINDOW(compose->window));
1135
                        return;
1136
                }
1137
        }
1138

    
1139
        compose = compose_create(account, COMPOSE_REEDIT);
1140
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1141

    
1142
        if (compose_parse_header(compose, msginfo) < 0) return;
1143
        compose_parse_source_msg(compose, msginfo);
1144

    
1145
        undo_block(compose->undostruct);
1146

    
1147
        compose_reedit_set_entry(compose, msginfo);
1148

    
1149
        text = GTK_TEXT_VIEW(compose->text);
1150
        buffer = gtk_text_view_get_buffer(text);
1151
        mark = gtk_text_buffer_get_insert(buffer);
1152
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1153

    
1154
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1155
                g_warning(_("Can't get text part\n"));
1156
        else {
1157
                gboolean prev_autowrap = compose->autowrap;
1158

    
1159
                compose->autowrap = FALSE;
1160
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1161
                        strcrchomp(buf);
1162
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1163
                }
1164
                compose_enable_sig(compose);
1165
                compose->autowrap = prev_autowrap;
1166
                fclose(fp);
1167
        }
1168
        compose_attach_parts(compose, msginfo);
1169

    
1170
        gtk_text_buffer_get_start_iter(buffer, &iter);
1171
        gtk_text_buffer_place_cursor(buffer, &iter);
1172

    
1173
        if (account->protocol != A_NNTP) {
1174
                str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
1175
                if (!str || *str == '\0')
1176
                        focus_widget = compose->to_entry;
1177
        } else {
1178
                str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
1179
                if (!str || *str == '\0')
1180
                        focus_widget = compose->newsgroups_entry;
1181
        }
1182
        if (!focus_widget) {
1183
                str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
1184
                if (!str || *str == '\0')
1185
                        focus_widget = compose->subject_entry;
1186
        }
1187
        if (focus_widget)
1188
                gtk_widget_grab_focus(focus_widget);
1189
        else
1190
                gtk_widget_grab_focus(compose->text);
1191

    
1192
        undo_unblock(compose->undostruct);
1193

    
1194
        compose_connect_changed_callbacks(compose);
1195
        compose_set_title(compose);
1196

    
1197
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1198
                compose->autosave_tag =
1199
                        g_timeout_add(prefs_common.autosave_itv * 60 * 1000,
1200
                                      autosave_timeout, compose);
1201
        if (prefs_common.auto_exteditor)
1202
                compose_exec_ext_editor(compose);
1203
}
1204

    
1205
GList *compose_get_compose_list(void)
1206
{
1207
        return compose_list;
1208
}
1209

    
1210
static void compose_entry_show(Compose *compose, ComposeEntryType type)
1211
{
1212
        GtkItemFactory *ifactory;
1213

    
1214
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1215

    
1216
        switch (type) {
1217
        case COMPOSE_ENTRY_CC:
1218
                menu_set_active(ifactory, "/View/Cc", TRUE);
1219
                break;
1220
        case COMPOSE_ENTRY_BCC:
1221
                menu_set_active(ifactory, "/View/Bcc", TRUE);
1222
                break;
1223
        case COMPOSE_ENTRY_REPLY_TO:
1224
                menu_set_active(ifactory, "/View/Reply-To", TRUE);
1225
                break;
1226
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1227
                menu_set_active(ifactory, "/View/Followup-To", TRUE);
1228
                break;
1229
        case COMPOSE_ENTRY_TO:
1230
                menu_set_active(ifactory, "/View/To", TRUE);
1231
                break;
1232
        default:
1233
                break;
1234
        }
1235
}
1236

    
1237
static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
1238
{
1239
        GtkEntry *entry;
1240

    
1241
        switch (type) {
1242
        case COMPOSE_ENTRY_CC:
1243
                entry = GTK_ENTRY(compose->cc_entry);
1244
                break;
1245
        case COMPOSE_ENTRY_BCC:
1246
                entry = GTK_ENTRY(compose->bcc_entry);
1247
                break;
1248
        case COMPOSE_ENTRY_REPLY_TO:
1249
                entry = GTK_ENTRY(compose->reply_entry);
1250
                break;
1251
        case COMPOSE_ENTRY_SUBJECT:
1252
                entry = GTK_ENTRY(compose->subject_entry);
1253
                break;
1254
        case COMPOSE_ENTRY_NEWSGROUPS:
1255
                entry = GTK_ENTRY(compose->newsgroups_entry);
1256
                break;
1257
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1258
                entry = GTK_ENTRY(compose->followup_entry);
1259
                break;
1260
        case COMPOSE_ENTRY_TO:
1261
        default:
1262
                entry = GTK_ENTRY(compose->to_entry);
1263
                break;
1264
        }
1265

    
1266
        return entry;
1267
}
1268

    
1269
void compose_entry_set(Compose *compose, const gchar *text,
1270
                       ComposeEntryType type)
1271
{
1272
        GtkEntry *entry;
1273

    
1274
        if (!text) return;
1275

    
1276
        compose_entry_show(compose, type);
1277
        entry = compose_get_entry(compose, type);
1278

    
1279
        gtk_entry_set_text(entry, text);
1280
}
1281

    
1282
void compose_entry_append(Compose *compose, const gchar *text,
1283
                          ComposeEntryType type)
1284
{
1285
        GtkEntry *entry;
1286
        const gchar *str;
1287
        gint pos;
1288

    
1289
        if (!text || *text == '\0') return;
1290

    
1291
        compose_entry_show(compose, type);
1292
        entry = compose_get_entry(compose, type);
1293

    
1294
        if (type != COMPOSE_ENTRY_SUBJECT) {
1295
                str = gtk_entry_get_text(entry);
1296
                if (*str != '\0') {
1297
                        pos = entry->text_length;
1298
                        gtk_editable_insert_text(GTK_EDITABLE(entry),
1299
                                                 ", ", -1, &pos);
1300
                }
1301
        }
1302

    
1303
        pos = entry->text_length;
1304
        gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1305
}
1306

    
1307
static void compose_entries_set(Compose *compose, const gchar *mailto)
1308
{
1309
        gchar *to = NULL;
1310
        gchar *cc = NULL;
1311
        gchar *subject = NULL;
1312
        gchar *inreplyto = NULL;
1313
        gchar *body = NULL;
1314

    
1315
        scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body);
1316

    
1317
        if (to)
1318
                compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1319
        if (cc)
1320
                compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1321
        if (subject)
1322
                compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1323
        if (inreplyto) {
1324
                if (strchr(inreplyto, '<'))
1325
                        extract_parenthesis(inreplyto, '<', '>');
1326
                remove_space(inreplyto);
1327
                compose->inreplyto = g_strdup(inreplyto);
1328
        }
1329
        if (body) {
1330
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1331
                GtkTextBuffer *buffer;
1332
                GtkTextMark *mark;
1333
                GtkTextIter iter;
1334
                gboolean prev_autowrap = compose->autowrap;
1335

    
1336
                compose->autowrap = FALSE;
1337

    
1338
                buffer = gtk_text_view_get_buffer(text);
1339
                mark = gtk_text_buffer_get_insert(buffer);
1340
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1341

    
1342
                gtk_text_buffer_insert(buffer, &iter, body, -1);
1343
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1344

    
1345
                compose->autowrap = prev_autowrap;
1346
                if (compose->autowrap)
1347
                        compose_wrap_all(compose);
1348
        }
1349

    
1350
        g_free(to);
1351
        g_free(cc);
1352
        g_free(subject);
1353
        g_free(inreplyto);
1354
        g_free(body);
1355
}
1356

    
1357
static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1358
                                          ComposeMode mode)
1359
{
1360
        g_return_if_fail(item != NULL);
1361

    
1362
        if (item->auto_to) {
1363
                if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1364
                        compose_entry_set(compose, item->auto_to,
1365
                                          COMPOSE_ENTRY_TO);
1366
        }
1367
        if (item->auto_cc)
1368
                compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1369
        if (item->auto_bcc)
1370
                compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1371
        if (item->auto_replyto)
1372
                compose_entry_set(compose, item->auto_replyto,
1373
                                  COMPOSE_ENTRY_REPLY_TO);
1374
}
1375

    
1376
#undef ACTIVATE_MENU
1377

    
1378
static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1379
{
1380
        static HeaderEntry hentry[] = {{"Reply-To:",        NULL, TRUE},
1381
                                       {"Cc:",                NULL, TRUE},
1382
                                       {"References:",        NULL, FALSE},
1383
                                       {"Bcc:",                NULL, TRUE},
1384
                                       {"Newsgroups:",  NULL, TRUE},
1385
                                       {"Followup-To:", NULL, TRUE},
1386
                                       {"List-Post:",   NULL, FALSE},
1387
                                       {"Content-Type:",NULL, FALSE},
1388
                                       {NULL,                NULL, FALSE}};
1389

    
1390
        enum
1391
        {
1392
                H_REPLY_TO        = 0,
1393
                H_CC                = 1,
1394
                H_REFERENCES        = 2,
1395
                H_BCC                = 3,
1396
                H_NEWSGROUPS    = 4,
1397
                H_FOLLOWUP_TO        = 5,
1398
                H_LIST_POST     = 6,
1399
                H_CONTENT_TYPE  = 7
1400
        };
1401

    
1402
        FILE *fp;
1403
        gchar *charset = NULL;
1404

    
1405
        g_return_val_if_fail(msginfo != NULL, -1);
1406

    
1407
        if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1408
        procheader_get_header_fields(fp, hentry);
1409
        fclose(fp);
1410

    
1411
        if (hentry[H_CONTENT_TYPE].body != NULL) {
1412
                procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body,
1413
                                               NULL, &charset, NULL, NULL);
1414
                g_free(hentry[H_CONTENT_TYPE].body);
1415
                hentry[H_CONTENT_TYPE].body = NULL;
1416
        }
1417
        if (hentry[H_REPLY_TO].body != NULL) {
1418
                if (hentry[H_REPLY_TO].body[0] != '\0') {
1419
                        compose->replyto =
1420
                                conv_unmime_header(hentry[H_REPLY_TO].body,
1421
                                                   charset);
1422
                }
1423
                g_free(hentry[H_REPLY_TO].body);
1424
                hentry[H_REPLY_TO].body = NULL;
1425
        }
1426
        if (hentry[H_CC].body != NULL) {
1427
                compose->cc = conv_unmime_header(hentry[H_CC].body, charset);
1428
                g_free(hentry[H_CC].body);
1429
                hentry[H_CC].body = NULL;
1430
        }
1431
        if (hentry[H_REFERENCES].body != NULL) {
1432
                if (compose->mode == COMPOSE_REEDIT)
1433
                        compose->references = hentry[H_REFERENCES].body;
1434
                else {
1435
                        compose->references = compose_parse_references
1436
                                (hentry[H_REFERENCES].body, msginfo->msgid);
1437
                        g_free(hentry[H_REFERENCES].body);
1438
                }
1439
                hentry[H_REFERENCES].body = NULL;
1440
        }
1441
        if (hentry[H_BCC].body != NULL) {
1442
                if (compose->mode == COMPOSE_REEDIT)
1443
                        compose->bcc =
1444
                                conv_unmime_header(hentry[H_BCC].body, charset);
1445
                g_free(hentry[H_BCC].body);
1446
                hentry[H_BCC].body = NULL;
1447
        }
1448
        if (hentry[H_NEWSGROUPS].body != NULL) {
1449
                compose->newsgroups = hentry[H_NEWSGROUPS].body;
1450
                hentry[H_NEWSGROUPS].body = NULL;
1451
        }
1452
        if (hentry[H_FOLLOWUP_TO].body != NULL) {
1453
                if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1454
                        compose->followup_to =
1455
                                conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1456
                                                   charset);
1457
                }
1458
                g_free(hentry[H_FOLLOWUP_TO].body);
1459
                hentry[H_FOLLOWUP_TO].body = NULL;
1460
        }
1461
        if (hentry[H_LIST_POST].body != NULL) {
1462
                gchar *to = NULL;
1463

    
1464
                extract_address(hentry[H_LIST_POST].body);
1465
                if (hentry[H_LIST_POST].body[0] != '\0') {
1466
                        scan_mailto_url(hentry[H_LIST_POST].body,
1467
                                        &to, NULL, NULL, NULL, NULL, NULL);
1468
                        if (to) {
1469
                                g_free(compose->ml_post);
1470
                                compose->ml_post = to;
1471
                        }
1472
                }
1473
                g_free(hentry[H_LIST_POST].body);
1474
                hentry[H_LIST_POST].body = NULL;
1475
        }
1476

    
1477
        g_free(charset);
1478

    
1479
        if (compose->mode == COMPOSE_REEDIT) {
1480
                if (msginfo->inreplyto && *msginfo->inreplyto)
1481
                        compose->inreplyto = g_strdup(msginfo->inreplyto);
1482
                return 0;
1483
        }
1484

    
1485
        if (msginfo->msgid && *msginfo->msgid)
1486
                compose->inreplyto = g_strdup(msginfo->msgid);
1487

    
1488
        if (!compose->references) {
1489
                if (msginfo->msgid && *msginfo->msgid) {
1490
                        if (msginfo->inreplyto && *msginfo->inreplyto)
1491
                                compose->references =
1492
                                        g_strdup_printf("<%s>\n\t<%s>",
1493
                                                        msginfo->inreplyto,
1494
                                                        msginfo->msgid);
1495
                        else
1496
                                compose->references =
1497
                                        g_strconcat("<", msginfo->msgid, ">",
1498
                                                    NULL);
1499
                } else if (msginfo->inreplyto && *msginfo->inreplyto) {
1500
                        compose->references =
1501
                                g_strconcat("<", msginfo->inreplyto, ">",
1502
                                            NULL);
1503
                }
1504
        }
1505

    
1506
        return 0;
1507
}
1508

    
1509
static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1510
{
1511
        GSList *ref_id_list, *cur;
1512
        GString *new_ref;
1513
        gchar *new_ref_str;
1514

    
1515
        ref_id_list = references_list_append(NULL, ref);
1516
        if (!ref_id_list) return NULL;
1517
        if (msgid && *msgid)
1518
                ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1519

    
1520
        for (;;) {
1521
                gint len = 0;
1522

    
1523
                for (cur = ref_id_list; cur != NULL; cur = cur->next)
1524
                        /* "<" + Message-ID + ">" + CR+LF+TAB */
1525
                        len += strlen((gchar *)cur->data) + 5;
1526

    
1527
                if (len > MAX_REFERENCES_LEN) {
1528
                        /* remove second message-ID */
1529
                        if (ref_id_list && ref_id_list->next &&
1530
                            ref_id_list->next->next) {
1531
                                g_free(ref_id_list->next->data);
1532
                                ref_id_list = g_slist_remove
1533
                                        (ref_id_list, ref_id_list->next->data);
1534
                        } else {
1535
                                slist_free_strings(ref_id_list);
1536
                                g_slist_free(ref_id_list);
1537
                                return NULL;
1538
                        }
1539
                } else
1540
                        break;
1541
        }
1542

    
1543
        new_ref = g_string_new("");
1544
        for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1545
                if (new_ref->len > 0)
1546
                        g_string_append(new_ref, "\n\t");
1547
                g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1548
        }
1549

    
1550
        slist_free_strings(ref_id_list);
1551
        g_slist_free(ref_id_list);
1552

    
1553
        new_ref_str = new_ref->str;
1554
        g_string_free(new_ref, FALSE);
1555

    
1556
        return new_ref_str;
1557
}
1558

    
1559
static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo)
1560
{
1561
        static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE},
1562
                                       {"X-Sylpheed-Forward:", NULL, FALSE},
1563
                                       {"REP:", NULL, FALSE},
1564
                                       {"FWD:", NULL, FALSE},
1565
                                       {NULL, NULL, FALSE}};
1566

    
1567
        enum
1568
        {
1569
                H_X_SYLPHEED_REPLY = 0,
1570
                H_X_SYLPHEED_FORWARD = 1,
1571
                H_REP = 2,
1572
                H_FWD = 3
1573
        };
1574

    
1575
        gchar *file;
1576
        FILE *fp;
1577
        gchar *str;
1578
        gchar buf[BUFFSIZE];
1579
        gint hnum;
1580

    
1581
        g_return_val_if_fail(msginfo != NULL, -1);
1582

    
1583
        file = procmsg_get_message_file(msginfo);
1584

    
1585
        if ((fp = g_fopen(file, "rb")) == NULL) {
1586
                FILE_OP_ERROR(file, "fopen");
1587
                return -1;
1588
        }
1589

    
1590
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
1591
               != -1) {
1592
                str = buf + strlen(hentry[hnum].name);
1593
                while (g_ascii_isspace(*str))
1594
                        ++str;
1595
                if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) &&
1596
                    !compose->reply_target) {
1597
                        compose->reply_target = g_strdup(str);
1598
                } else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) &&
1599
                           !compose->forward_targets) {
1600
                        compose->forward_targets = g_strdup(str);
1601
                }
1602
        }
1603

    
1604
        fclose(fp);
1605
        g_free(file);
1606

    
1607
        return 0;
1608
}
1609

    
1610
static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1611
                                const gchar *fmt, const gchar *qmark,
1612
                                const gchar *body)
1613
{
1614
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1615
        GtkTextBuffer *buffer;
1616
        GtkTextMark *mark;
1617
        GtkTextIter iter;
1618
        static MsgInfo dummyinfo;
1619
        gchar *quote_str = NULL;
1620
        gchar *buf;
1621
        gchar *p, *lastp;
1622
        gint len;
1623
        gboolean prev_autowrap;
1624

    
1625
        if (!msginfo)
1626
                msginfo = &dummyinfo;
1627

    
1628
        if (qmark != NULL) {
1629
                quote_fmt_init(msginfo, NULL, NULL);
1630
                quote_fmt_scan_string(qmark);
1631
                quote_fmt_parse();
1632

    
1633
                buf = quote_fmt_get_buffer();
1634
                if (buf == NULL)
1635
                        alertpanel_error(_("Quote mark format error."));
1636
                else
1637
                        Xstrdup_a(quote_str, buf, return NULL)
1638
        }
1639

    
1640
        if (fmt && *fmt != '\0') {
1641
                quote_fmt_init(msginfo, quote_str, body);
1642
                quote_fmt_scan_string(fmt);
1643
                quote_fmt_parse();
1644

    
1645
                buf = quote_fmt_get_buffer();
1646
                if (buf == NULL) {
1647
                        alertpanel_error(_("Message reply/forward format error."));
1648
                        return NULL;
1649
                }
1650
        } else
1651
                buf = "";
1652

    
1653
        prev_autowrap = compose->autowrap;
1654
        compose->autowrap = FALSE;
1655

    
1656
        buffer = gtk_text_view_get_buffer(text);
1657
        mark = gtk_text_buffer_get_insert(buffer);
1658
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1659

    
1660
        for (p = buf; *p != '\0'; ) {
1661
                lastp = strchr(p, '\n');
1662
                len = lastp ? lastp - p + 1 : -1;
1663
                gtk_text_buffer_insert(buffer, &iter, p, len);
1664
                if (lastp)
1665
                        p = lastp + 1;
1666
                else
1667
                        break;
1668
        }
1669

    
1670
        compose->autowrap = prev_autowrap;
1671
        if (compose->autowrap)
1672
                compose_wrap_all(compose);
1673

    
1674
        return buf;
1675
}
1676

    
1677
static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1678
                                    ComposeMode mode)
1679
{
1680
        GSList *cc_list = NULL;
1681
        GSList *cur;
1682
        gchar *from = NULL;
1683
        gchar *replyto = NULL;
1684
        GHashTable *to_table;
1685
        gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1686

    
1687
        g_return_if_fail(compose->account != NULL);
1688
        g_return_if_fail(msginfo != NULL);
1689

    
1690
        switch (COMPOSE_MODE(mode)) {
1691
        case COMPOSE_REPLY_TO_SENDER:
1692
                ignore_replyto = TRUE;
1693
                break;
1694
        case COMPOSE_REPLY_TO_ALL:
1695
                to_all = TRUE;
1696
                break;
1697
        case COMPOSE_REPLY_TO_LIST:
1698
                to_ml = TRUE;
1699
                break;
1700
        default:
1701
                break;
1702
        }
1703

    
1704
        if (compose->account->protocol != A_NNTP) {
1705
                if (to_ml && compose->ml_post) {
1706
                        /* don't reply to list for confirmation request etc. */
1707
                        if ((!msginfo->to ||
1708
                             !strstr_with_skip_quote(msginfo->to,
1709
                                                     compose->ml_post)) &&
1710
                            (!compose->cc ||
1711
                             !strstr_with_skip_quote(compose->cc,
1712
                                                     compose->ml_post)))
1713
                                to_ml = FALSE;
1714
                }
1715
                if (to_ml && compose->ml_post) {
1716
                        compose_entry_set(compose, compose->ml_post,
1717
                                          COMPOSE_ENTRY_TO);
1718
                        if (compose->replyto &&
1719
                            !address_equal(compose->replyto, compose->ml_post))
1720
                                compose_entry_set(compose, compose->replyto,
1721
                                                  COMPOSE_ENTRY_CC);
1722
                } else if (prefs_common.inherit_recipient_on_self_reply &&
1723
                           address_equal(compose->account->address,
1724
                                         msginfo->from)) {
1725
                        compose_entry_set(compose, msginfo->to,
1726
                                          COMPOSE_ENTRY_TO);
1727
                        if (to_all) {
1728
                                compose_entry_set(compose, compose->cc,
1729
                                                  COMPOSE_ENTRY_CC);
1730
                                to_all = FALSE;
1731
                        }
1732
                } else {
1733
                        compose_entry_set(compose, 
1734
                                          (compose->replyto && !ignore_replyto)
1735
                                          ? compose->replyto
1736
                                          : msginfo->from ? msginfo->from : "",
1737
                                          COMPOSE_ENTRY_TO);
1738
                }
1739
        } else {
1740
                if (ignore_replyto) {
1741
                        compose_entry_set(compose,
1742
                                          msginfo->from ? msginfo->from : "",
1743
                                          COMPOSE_ENTRY_TO);
1744
                } else {
1745
                        if (to_all) {
1746
                                compose_entry_set
1747
                                        (compose, 
1748
                                         (compose->replyto && !ignore_replyto)
1749
                                         ? compose->replyto
1750
                                         : msginfo->from ? msginfo->from : "",
1751
                                         COMPOSE_ENTRY_TO);
1752
                        }
1753
                        compose_entry_set(compose,
1754
                                          compose->followup_to ? compose->followup_to
1755
                                          : compose->newsgroups ? compose->newsgroups
1756
                                          : "",
1757
                                          COMPOSE_ENTRY_NEWSGROUPS);
1758
                }
1759
        }
1760

    
1761
        if (msginfo->subject && *msginfo->subject) {
1762
                gchar *buf;
1763
                gchar *p;
1764

    
1765
                buf = g_strdup(msginfo->subject);
1766

    
1767
                if (msginfo->folder && msginfo->folder->trim_compose_subject)
1768
                        trim_subject(buf);
1769

    
1770
                while (!g_ascii_strncasecmp(buf, "Re:", 3)) {
1771
                        p = buf + 3;
1772
                        while (g_ascii_isspace(*p)) p++;
1773
                        memmove(buf, p, strlen(p) + 1);
1774
                }
1775

    
1776
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1777
                compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1778

    
1779
                g_free(buf);
1780
        } else
1781
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1782

    
1783
        if (!compose->replyto && to_ml && compose->ml_post) return;
1784
        if (!to_all) return;
1785

    
1786
        if (compose->replyto && msginfo->from)
1787
                cc_list = address_list_append_orig(cc_list, msginfo->from);
1788
        cc_list = address_list_append_orig(cc_list, msginfo->to);
1789
        cc_list = address_list_append_orig(cc_list, compose->cc);
1790

    
1791
        to_table = g_hash_table_new(g_str_hash, g_str_equal);
1792
        if (compose->replyto) {
1793
                replyto = g_strdup(compose->replyto);
1794
                extract_address(replyto);
1795
                g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1796
        } else if (msginfo->from) {
1797
                from = g_strdup(msginfo->from);
1798
                extract_address(from);
1799
                g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1800
        }
1801
        if (compose->account)
1802
                g_hash_table_insert(to_table,
1803
                                    g_strdup(compose->account->address),
1804
                                    GINT_TO_POINTER(1));
1805

    
1806
        /* remove duplicate addresses */
1807
        for (cur = cc_list; cur != NULL; ) {
1808
                gchar *addr = (gchar *)cur->data;
1809
                GSList *next = cur->next;
1810
                gchar *addr_;
1811

    
1812
                addr_ = g_strdup(addr);
1813
                extract_address(addr_);
1814
                if (g_hash_table_lookup(to_table, addr_) != NULL) {
1815
                        cc_list = g_slist_remove(cc_list, addr);
1816
                        g_free(addr_);
1817
                        g_free(addr);
1818
                } else
1819
                        g_hash_table_insert(to_table, addr_, cur);
1820

    
1821
                cur = next;
1822
        }
1823
        hash_free_strings(to_table);
1824
        g_hash_table_destroy(to_table);
1825

    
1826
        if (cc_list) {
1827
                for (cur = cc_list; cur != NULL; cur = cur->next)
1828
                        compose_entry_append(compose, (gchar *)cur->data,
1829
                                             COMPOSE_ENTRY_CC);
1830
                slist_free_strings(cc_list);
1831
                g_slist_free(cc_list);
1832
        }
1833
}
1834

    
1835
static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1836
{
1837
        g_return_if_fail(msginfo != NULL);
1838
        g_return_if_fail(compose->account != NULL);
1839

    
1840
        compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1841
        compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1842
        compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1843
        compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1844
        if (compose->account->protocol == A_NNTP) {
1845
                compose_entry_set(compose, compose->newsgroups,
1846
                                  COMPOSE_ENTRY_NEWSGROUPS);
1847
                compose_entry_set(compose, compose->followup_to,
1848
                                  COMPOSE_ENTRY_FOLLOWUP_TO);
1849
        }
1850
        compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1851
}
1852

    
1853
static void compose_insert_sig(Compose *compose, gboolean append,
1854
                               gboolean replace, gboolean scroll)
1855
{
1856
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1857
        GtkTextBuffer *buffer;
1858
        GtkTextMark *mark;
1859
        GtkTextIter iter;
1860
        gchar *sig_str;
1861
        gboolean prev_autowrap;
1862

    
1863
        g_return_if_fail(compose->account != NULL);
1864

    
1865
        prev_autowrap = compose->autowrap;
1866
        compose->autowrap = FALSE;
1867

    
1868
        buffer = gtk_text_view_get_buffer(text);
1869
        mark = gtk_text_buffer_get_insert(buffer);
1870
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1871

    
1872
        if (replace) {
1873
                GtkTextIter start_iter, end_iter;
1874

    
1875
                gtk_text_buffer_get_start_iter(buffer, &start_iter);
1876
                gtk_text_buffer_get_end_iter(buffer, &iter);
1877

    
1878
                while (gtk_text_iter_begins_tag
1879
                        (&start_iter, compose->sig_tag) ||
1880
                       gtk_text_iter_forward_to_tag_toggle
1881
                        (&start_iter, compose->sig_tag)) {
1882
                        end_iter = start_iter;
1883
                        if (gtk_text_iter_forward_to_tag_toggle
1884
                                (&end_iter, compose->sig_tag)) {
1885
                                gtk_text_buffer_delete
1886
                                        (buffer, &start_iter, &end_iter);
1887
                                iter = start_iter;
1888
                        }
1889
                }
1890
        }
1891

    
1892
        if (scroll) {
1893
                if (append)
1894
                        gtk_text_buffer_get_end_iter(buffer, &iter);
1895
                else
1896
                        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1897
        }
1898

    
1899
        sig_str = compose_get_signature_str(compose);
1900
        if (sig_str) {
1901
                if (!replace)
1902
                        gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
1903
                else if (!gtk_text_iter_starts_line(&iter))
1904
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1905
                gtk_text_buffer_insert_with_tags
1906
                        (buffer, &iter, sig_str, -1, compose->sig_tag, NULL);
1907
                g_free(sig_str);
1908
                if (scroll)
1909
                        gtk_text_buffer_place_cursor(buffer, &iter);
1910
        }
1911

    
1912
        compose->autowrap = prev_autowrap;
1913
        if (compose->autowrap)
1914
                compose_wrap_all(compose);
1915

    
1916
        if (scroll)
1917
                gtk_text_view_scroll_mark_onscreen(text, mark);
1918
}
1919

    
1920
static void compose_enable_sig(Compose *compose)
1921
{
1922
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1923
        GtkTextBuffer *buffer;
1924
        GtkTextIter iter, start, end;
1925
        gchar *sig_str;
1926

    
1927
        g_return_if_fail(compose->account != NULL);
1928

    
1929
        buffer = gtk_text_view_get_buffer(text);
1930
        gtk_text_buffer_get_start_iter(buffer, &iter);
1931

    
1932
        sig_str = compose_get_signature_str(compose);
1933
        if (!sig_str)
1934
                return;
1935

    
1936
        if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) {
1937
                end = start;
1938
                gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1));
1939
                gtk_text_buffer_apply_tag(buffer, compose->sig_tag,
1940
                                          &start, &end);
1941
        }
1942

    
1943
        g_free(sig_str);
1944
}
1945

    
1946
static gchar *compose_get_signature_str(Compose *compose)
1947
{
1948
        gchar *sig_path;
1949
        gchar *sig_body = NULL;
1950
        gchar *utf8_sig_body = NULL;
1951
        gchar *utf8_sig_str = NULL;
1952

    
1953
        g_return_val_if_fail(compose->account != NULL, NULL);
1954

    
1955
        if (compose->account->sig_type == SIG_DIRECT) {
1956
                gchar *p, *sp;
1957

    
1958
                if (!compose->account->sig_text)
1959
                        return NULL;
1960

    
1961
                sp = compose->account->sig_text;
1962
                p = sig_body = g_malloc(strlen(compose->account->sig_text) + 1);
1963
                while (*sp) {
1964
                        if (*sp == '\\' && *(sp + 1) == 'n') {
1965
                                *p++ = '\n';
1966
                                sp += 2;
1967
                        } else
1968
                                *p++ = *sp++;
1969
                }
1970
                *p = '\0';
1971

    
1972
                if (prefs_common.sig_sep) {
1973
                        utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
1974
                                                   sig_body, NULL);
1975
                        g_free(sig_body);
1976
                } else
1977
                        utf8_sig_str = sig_body;
1978

    
1979
                return utf8_sig_str;
1980
        }
1981

    
1982
        if (!compose->account->sig_path)
1983
                return NULL;
1984

    
1985
        if (g_path_is_absolute(compose->account->sig_path) ||
1986
            compose->account->sig_type == SIG_COMMAND)
1987
                sig_path = g_strdup(compose->account->sig_path);
1988
        else {
1989
#ifdef G_OS_WIN32
1990
                sig_path = g_strconcat(get_rc_dir(),
1991
#else
1992
                sig_path = g_strconcat(get_home_dir(),
1993
#endif
1994
                                       G_DIR_SEPARATOR_S,
1995
                                       compose->account->sig_path, NULL);
1996
        }
1997

    
1998
        if (compose->account->sig_type == SIG_FILE) {
1999
                if (!is_file_or_fifo_exist(sig_path)) {
2000
                        debug_print("can't open signature file: %s\n",
2001
                                    sig_path);
2002
                        g_free(sig_path);
2003
                        return NULL;
2004
                }
2005
        }
2006

    
2007
        if (compose->account->sig_type == SIG_COMMAND)
2008
                sig_body = get_command_output(sig_path);
2009
        else {
2010
                gchar *tmp;
2011

    
2012
                tmp = file_read_to_str(sig_path);
2013
                if (!tmp)
2014
                        return NULL;
2015
                sig_body = normalize_newlines(tmp);
2016
                g_free(tmp);
2017
        }
2018
        g_free(sig_path);
2019

    
2020
        if (sig_body) {
2021
                gint error = 0;
2022

    
2023
                utf8_sig_body = conv_codeset_strdup_full
2024
                        (sig_body, conv_get_locale_charset_str(),
2025
                         CS_INTERNAL, &error);
2026
                if (!utf8_sig_body || error != 0) {
2027
                        if (g_utf8_validate(sig_body, -1, NULL) == TRUE) {
2028
                                g_free(utf8_sig_body);
2029
                                utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2030
                        }
2031
                } else {
2032
                        g_free(sig_body);
2033
                        sig_body = utf8_sig_body;
2034
                        utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2035
                }
2036
                g_free(sig_body);
2037
        }
2038

    
2039
        if (prefs_common.sig_sep) {
2040
                utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2041
                                           utf8_sig_body, NULL);
2042
                g_free(utf8_sig_body);
2043
        } else
2044
                utf8_sig_str = utf8_sig_body;
2045

    
2046
        return utf8_sig_str;
2047
}
2048

    
2049
static void compose_insert_file(Compose *compose, const gchar *file,
2050
                                gboolean scroll)
2051
{
2052
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2053
        GtkTextBuffer *buffer;
2054
        GtkTextMark *mark;
2055
        GtkTextIter iter;
2056
        const gchar *cur_encoding;
2057
        gchar buf[BUFFSIZE];
2058
        gint len;
2059
        FILE *fp;
2060
        gboolean prev_autowrap;
2061
        CharSet enc;
2062

    
2063
        g_return_if_fail(file != NULL);
2064

    
2065
        enc = conv_check_file_encoding(file);
2066

    
2067
        if ((fp = g_fopen(file, "rb")) == NULL) {
2068
                FILE_OP_ERROR(file, "fopen");
2069
                return;
2070
        }
2071

    
2072
        prev_autowrap = compose->autowrap;
2073
        compose->autowrap = FALSE;
2074

    
2075
        buffer = gtk_text_view_get_buffer(text);
2076
        mark = gtk_text_buffer_get_insert(buffer);
2077
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2078

    
2079
        cur_encoding = conv_get_locale_charset_str();
2080

    
2081
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2082
                gchar *str;
2083
                gint error = 0;
2084

    
2085
                if (enc == C_UTF_8) {
2086
                        str = conv_utf8todisp(buf, NULL);
2087
                } else {
2088
                        str = conv_codeset_strdup_full(buf, cur_encoding,
2089
                                                       CS_INTERNAL, &error);
2090
                        if (!str || error != 0) {
2091
                                if (g_utf8_validate(buf, -1, NULL) == TRUE) {
2092
                                        g_free(str);
2093
                                        str = g_strdup(buf);
2094
                                }
2095
                        }
2096
                        if (!str) continue;
2097
                }
2098

    
2099
                /* strip <CR> if DOS/Windows file,
2100
                   replace <CR> with <LF> if Macintosh file. */
2101
                strcrchomp(str);
2102
                len = strlen(str);
2103
                if (len > 0 && str[len - 1] != '\n') {
2104
                        while (--len >= 0)
2105
                                if (str[len] == '\r') str[len] = '\n';
2106
                }
2107

    
2108
                gtk_text_buffer_insert(buffer, &iter, str, -1);
2109
                g_free(str);
2110
        }
2111

    
2112
        fclose(fp);
2113

    
2114
        compose->autowrap = prev_autowrap;
2115
        if (compose->autowrap)
2116
                compose_wrap_all(compose);
2117

    
2118
        if (scroll)
2119
                gtk_text_view_scroll_mark_onscreen(text, mark);
2120
}
2121

    
2122
static void compose_attach_append(Compose *compose, const gchar *file,
2123
                                  const gchar *filename,
2124
                                  const gchar *content_type)
2125
{
2126
        GtkTreeIter iter;
2127
        AttachInfo *ainfo;
2128
        FILE *fp;
2129
        off_t size;
2130

    
2131
        g_return_if_fail(file != NULL);
2132
        g_return_if_fail(*file != '\0');
2133

    
2134
        if (!is_file_exist(file)) {
2135
                g_warning(_("File %s doesn't exist\n"), file);
2136
                return;
2137
        }
2138
        if ((size = get_file_size(file)) < 0) {
2139
                g_warning(_("Can't get file size of %s\n"), file);
2140
                return;
2141
        }
2142
        if (size == 0) {
2143
                alertpanel_notice(_("File %s is empty."), file);
2144
                return;
2145
        }
2146
        if ((fp = g_fopen(file, "rb")) == NULL) {
2147
                alertpanel_error(_("Can't read %s."), file);
2148
                return;
2149
        }
2150
        fclose(fp);
2151

    
2152
        if (!compose->use_attach) {
2153
                GtkItemFactory *ifactory;
2154

    
2155
                ifactory = gtk_item_factory_from_widget(compose->menubar);
2156
                menu_set_active(ifactory, "/View/Attachment", TRUE);
2157
        }
2158

    
2159
        ainfo = g_new0(AttachInfo, 1);
2160
        ainfo->file = g_strdup(file);
2161

    
2162
        if (content_type) {
2163
                ainfo->content_type = g_strdup(content_type);
2164
                if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2165
                        MsgInfo *msginfo;
2166
                        MsgFlags flags = {0, 0};
2167
                        const gchar *name;
2168

    
2169
                        if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2170
                                ainfo->encoding = ENC_7BIT;
2171
                        else
2172
                                ainfo->encoding = ENC_8BIT;
2173

    
2174
                        msginfo = procheader_parse_file(file, flags, FALSE);
2175
                        if (msginfo && msginfo->subject)
2176
                                name = msginfo->subject;
2177
                        else
2178
                                name = g_basename(filename ? filename : file);
2179

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

    
2182
                        procmsg_msginfo_free(msginfo);
2183
                } else {
2184
                        if (!g_ascii_strncasecmp(content_type, "text", 4))
2185
                                ainfo->encoding = procmime_get_encoding_for_text_file(file);
2186
                        else
2187
                                ainfo->encoding = ENC_BASE64;
2188
                        ainfo->name = g_strdup
2189
                                (g_basename(filename ? filename : file));
2190
                }
2191
        } else {
2192
                ainfo->content_type = procmime_get_mime_type(file);
2193
                if (!ainfo->content_type) {
2194
                        ainfo->content_type =
2195
                                g_strdup("application/octet-stream");
2196
                        ainfo->encoding = ENC_BASE64;
2197
                } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
2198
                        ainfo->encoding =
2199
                                procmime_get_encoding_for_text_file(file);
2200
                else
2201
                        ainfo->encoding = ENC_BASE64;
2202
                ainfo->name = g_strdup(g_basename(filename ? filename : file));        
2203
        }
2204
        ainfo->size = size;
2205

    
2206
        gtk_list_store_append(compose->attach_store, &iter);
2207
        gtk_list_store_set(compose->attach_store, &iter,
2208
                           COL_MIMETYPE, ainfo->content_type,
2209
                           COL_SIZE, to_human_readable(ainfo->size),
2210
                           COL_NAME, ainfo->name,
2211
                           COL_ATTACH_INFO, ainfo,
2212
                           -1);
2213
}
2214

    
2215
static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2216
{
2217
        MimeInfo *mimeinfo;
2218
        MimeInfo *child;
2219
        gchar *infile;
2220
        gchar *outfile;
2221

    
2222
        mimeinfo = procmime_scan_message(msginfo);
2223
        if (!mimeinfo) return;
2224

    
2225
        infile = procmsg_get_message_file_path(msginfo);
2226

    
2227
        child = mimeinfo;
2228
        while (child != NULL) {
2229
                if (child->children || child->mime_type == MIME_MULTIPART)
2230
                        goto next;
2231
                if (child->mime_type != MIME_MESSAGE_RFC822 &&
2232
                    child->mime_type != MIME_IMAGE &&
2233
                    child->mime_type != MIME_AUDIO &&
2234
                    child->mime_type != MIME_VIDEO &&
2235
                    !child->filename && !child->name)
2236
                        goto next;
2237

    
2238
                outfile = procmime_get_tmp_file_name(child);
2239
                if (procmime_get_part(outfile, infile, child) < 0) {
2240
                        g_warning(_("Can't get the part of multipart message."));
2241
                        g_free(outfile);
2242
                        goto next;
2243
                }
2244

    
2245
                compose_attach_append
2246
                        (compose, outfile,
2247
                         child->filename ? child->filename : child->name,
2248
                         child->content_type);
2249

    
2250
                g_free(outfile);
2251

    
2252
next:
2253
                if (child->mime_type == MIME_MESSAGE_RFC822)
2254
                        child = child->next;
2255
                else
2256
                        child = procmime_mimeinfo_next(child);
2257
        }
2258

    
2259
        g_free(infile);
2260
        procmime_mimeinfo_free_all(mimeinfo);
2261
}
2262

    
2263
#define INDENT_CHARS        ">|#"
2264

    
2265
typedef enum {
2266
        WAIT_FOR_INDENT_CHAR,
2267
        WAIT_FOR_INDENT_CHAR_OR_SPACE,
2268
} IndentState;
2269

    
2270
/* return indent length, we allow:
2271
   indent characters followed by indent characters or spaces/tabs,
2272
   alphabets and numbers immediately followed by indent characters,
2273
   and the repeating sequences of the above
2274
   If quote ends with multiple spaces, only the first one is included. */
2275
static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
2276
                                    const GtkTextIter *start, gint *len)
2277
{
2278
        GtkTextIter iter = *start;
2279
        gunichar wc;
2280
        gchar ch[6];
2281
        gint clen;
2282
        IndentState state = WAIT_FOR_INDENT_CHAR;
2283
        gboolean is_space;
2284
        gboolean is_indent;
2285
        gint alnum_count = 0;
2286
        gint space_count = 0;
2287
        gint quote_len = 0;
2288

    
2289
        while (!gtk_text_iter_ends_line(&iter)) {
2290
                wc = gtk_text_iter_get_char(&iter);
2291
                if (g_unichar_iswide(wc))
2292
                        break;
2293
                clen = g_unichar_to_utf8(wc, ch);
2294
                if (clen != 1)
2295
                        break;
2296

    
2297
                is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE;
2298
                is_space = g_unichar_isspace(wc);
2299

    
2300
                if (state == WAIT_FOR_INDENT_CHAR) {
2301
                        if (!is_indent && !g_unichar_isalnum(wc))
2302
                                break;
2303
                        if (is_indent) {
2304
                                quote_len += alnum_count + space_count + 1;
2305
                                alnum_count = space_count = 0;
2306
                                state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2307
                        } else
2308
                                alnum_count++;
2309
                } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2310
                        if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2311
                                break;
2312
                        if (is_space)
2313
                                space_count++;
2314
                        else if (is_indent) {
2315
                                quote_len += alnum_count + space_count + 1;
2316
                                alnum_count = space_count = 0;
2317
                        } else {
2318
                                alnum_count++;
2319
                                state = WAIT_FOR_INDENT_CHAR;
2320
                        }
2321
                }
2322

    
2323
                gtk_text_iter_forward_char(&iter);
2324
        }
2325

    
2326
        if (quote_len > 0 && space_count > 0)
2327
                quote_len++;
2328

    
2329
        if (len)
2330
                *len = quote_len;
2331

    
2332
        if (quote_len > 0) {
2333
                iter = *start;
2334
                gtk_text_iter_forward_chars(&iter, quote_len);
2335
                return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2336
        }
2337

    
2338
        return NULL;
2339
}
2340

    
2341
/* return TRUE if the line is itemized */
2342
static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2343
                                    const GtkTextIter *start)
2344
{
2345
        GtkTextIter iter = *start;
2346
        gunichar wc;
2347
        gchar ch[6];
2348
        gint clen;
2349

    
2350
        if (gtk_text_iter_ends_line(&iter))
2351
                return FALSE;
2352

    
2353
        while (1) {
2354
                wc = gtk_text_iter_get_char(&iter);
2355
                if (!g_unichar_isspace(wc))
2356
                        break;
2357
                gtk_text_iter_forward_char(&iter);
2358
                if (gtk_text_iter_ends_line(&iter))
2359
                        return FALSE;
2360
        }
2361

    
2362
        clen = g_unichar_to_utf8(wc, ch);
2363

    
2364
        /* (1), 2), 3. etc. */
2365
        if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) {
2366
                gboolean digit_appeared = FALSE;
2367

    
2368
                if (ch[0] == '(')
2369
                        gtk_text_iter_forward_char(&iter);
2370

    
2371
                while (1) {
2372
                        wc = gtk_text_iter_get_char(&iter);
2373
                        clen = g_unichar_to_utf8(wc, ch);
2374
                        if (g_unichar_isdigit(wc)) {
2375
                                digit_appeared = TRUE;
2376
                                gtk_text_iter_forward_char(&iter);
2377
                                if (gtk_text_iter_ends_line(&iter))
2378
                                        return FALSE;
2379
                        } else if (clen == 1 &&
2380
                                   (ch[0] == ')' || ch[0] == '.')) {
2381
                                if (!digit_appeared)
2382
                                        return FALSE;
2383
                                gtk_text_iter_forward_char(&iter);
2384
                                if (gtk_text_iter_ends_line(&iter))
2385
                                        return TRUE;
2386
                                wc = gtk_text_iter_get_char(&iter);
2387
                                if (g_unichar_isspace(wc))
2388
                                        return TRUE;
2389
                                else
2390
                                        return FALSE;
2391
                        } else
2392
                                return FALSE;
2393
                }
2394
        }
2395

    
2396
        if (clen != 1)
2397
                return FALSE;
2398
        if (!strchr("*-+", ch[0]))
2399
                return FALSE;
2400

    
2401
        gtk_text_iter_forward_char(&iter);
2402
        if (gtk_text_iter_ends_line(&iter))
2403
                return FALSE;
2404
        wc = gtk_text_iter_get_char(&iter);
2405
        if (g_unichar_isspace(wc))
2406
                return TRUE;
2407
        else if (ch[0] == '-') {
2408
                /* -- */
2409
                clen = g_unichar_to_utf8(wc, ch);
2410
                if (clen == 1 && ch[0] == '-')
2411
                        return TRUE;
2412
        }
2413

    
2414
        return FALSE;
2415
}
2416

    
2417
static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2418
                                           const GtkTextIter *start,
2419
                                           GtkTextIter *break_pos,
2420
                                           gint max_col,
2421
                                           gint quote_len)
2422
{
2423
        GtkTextIter iter = *start, line_end = *start;
2424
        PangoLogAttr *attrs;
2425
        gchar *str;
2426
        gchar *p;
2427
        gint len;
2428
        gint i;
2429
        gint col = 0;
2430
        gint pos = 0;
2431
        gboolean can_break = FALSE;
2432
        gboolean do_break = FALSE;
2433
        gboolean prev_dont_break = FALSE;
2434

    
2435
        gtk_text_iter_forward_to_line_end(&line_end);
2436
        str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2437
        len = g_utf8_strlen(str, -1);
2438
        /* g_print("breaking line: %d: %s (len = %d)\n",
2439
                gtk_text_iter_get_line(&iter), str, len); */
2440
        attrs = g_new(PangoLogAttr, len + 1);
2441

    
2442
        pango_default_break(str, -1, NULL, attrs, len + 1);
2443

    
2444
        p = str;
2445

    
2446
        /* skip quote and leading spaces */
2447
        for (i = 0; *p != '\0' && i < len; i++) {
2448
                gunichar wc;
2449

    
2450
                wc = g_utf8_get_char(p);
2451
                if (i >= quote_len && !g_unichar_isspace(wc))
2452
                        break;
2453
                if (g_unichar_iswide(wc))
2454
                        col += 2;
2455
                else if (*p == '\t')
2456
                        col += 8;
2457
                else
2458
                        col++;
2459
                p = g_utf8_next_char(p);
2460
        }
2461

    
2462
        for (; *p != '\0' && i < len; i++) {
2463
                PangoLogAttr *attr = attrs + i;
2464
                gunichar wc;
2465
                gint uri_len;
2466

    
2467
                if (attr->is_line_break && can_break && !prev_dont_break)
2468
                        pos = i;
2469

    
2470
                /* don't wrap URI */
2471
                if ((uri_len = get_uri_len(p)) > 0) {
2472
                        col += uri_len;
2473
                        if (pos > 0 && col > max_col) {
2474
                                do_break = TRUE;
2475
                                break;
2476
                        }
2477
                        i += uri_len - 1;
2478
                        p += uri_len;
2479
                        can_break = TRUE;
2480
                        continue;
2481
                }
2482

    
2483
                wc = g_utf8_get_char(p);
2484
                if (g_unichar_iswide(wc)) {
2485
                        col += 2;
2486
                        if (prev_dont_break && can_break && attr->is_line_break)
2487
                                pos = i;
2488
                } else if (*p == '\t')
2489
                        col += 8;
2490
                else
2491
                        col++;
2492
                if (pos > 0 && col > max_col) {
2493
                        do_break = TRUE;
2494
                        break;
2495
                }
2496

    
2497
                if (*p == '-' || *p == '/')
2498
                        prev_dont_break = TRUE;
2499
                else
2500
                        prev_dont_break = FALSE;
2501

    
2502
                p = g_utf8_next_char(p);
2503
                can_break = TRUE;
2504
        }
2505

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

    
2508
        g_free(attrs);
2509
        g_free(str);
2510

    
2511
        *break_pos = *start;
2512
        gtk_text_iter_set_line_offset(break_pos, pos);
2513

    
2514
        return do_break;
2515
}
2516

    
2517
static gboolean compose_join_next_line(GtkTextBuffer *buffer,
2518
                                       GtkTextIter *iter,
2519
                                       const gchar *quote_str)
2520
{
2521
        GtkTextIter iter_ = *iter, cur, prev, next, end;
2522
        PangoLogAttr attrs[3];
2523
        gchar *str;
2524
        gchar *next_quote_str;
2525
        gunichar wc1, wc2;
2526
        gint quote_len;
2527
        gboolean keep_cursor = FALSE;
2528

    
2529
        if (!gtk_text_iter_forward_line(&iter_) ||
2530
            gtk_text_iter_ends_line(&iter_))
2531
                return FALSE;
2532

    
2533
        next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
2534

    
2535
        if ((quote_str || next_quote_str) &&
2536
            strcmp2(quote_str, next_quote_str) != 0) {
2537
                g_free(next_quote_str);
2538
                return FALSE;
2539
        }
2540
        g_free(next_quote_str);
2541

    
2542
        end = iter_;
2543
        if (quote_len > 0) {
2544
                gtk_text_iter_forward_chars(&end, quote_len);
2545
                if (gtk_text_iter_ends_line(&end))
2546
                        return FALSE;
2547
        }
2548

    
2549
        /* don't join itemized lines */
2550
        if (compose_is_itemized(buffer, &end))
2551
                return FALSE;
2552

    
2553
        /* delete quote str */
2554
        if (quote_len > 0)
2555
                gtk_text_buffer_delete(buffer, &iter_, &end);
2556

    
2557
        /* delete linebreak and extra spaces */
2558
        prev = cur = iter_;
2559
        while (gtk_text_iter_backward_char(&cur)) {
2560
                wc1 = gtk_text_iter_get_char(&cur);
2561
                if (!g_unichar_isspace(wc1))
2562
                        break;
2563
                prev = cur;
2564
        }
2565
        next = cur = iter_;
2566
        while (!gtk_text_iter_ends_line(&cur)) {
2567
                wc1 = gtk_text_iter_get_char(&cur);
2568
                if (!g_unichar_isspace(wc1))
2569
                        break;
2570
                gtk_text_iter_forward_char(&cur);
2571
                next = cur;
2572
        }
2573
        if (!gtk_text_iter_equal(&prev, &next)) {
2574
                GtkTextMark *mark;
2575

    
2576
                mark = gtk_text_buffer_get_insert(buffer);
2577
                gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
2578
                if (gtk_text_iter_equal(&prev, &cur))
2579
                        keep_cursor = TRUE;
2580
                gtk_text_buffer_delete(buffer, &prev, &next);
2581
        }
2582
        iter_ = prev;
2583

    
2584
        /* insert space if required */
2585
        gtk_text_iter_backward_char(&prev);
2586
        wc1 = gtk_text_iter_get_char(&prev);
2587
        wc2 = gtk_text_iter_get_char(&next);
2588
        gtk_text_iter_forward_char(&next);
2589
        str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
2590
        pango_default_break(str, -1, NULL, attrs, 3);
2591
        if (!attrs[1].is_line_break ||
2592
            (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
2593
                gtk_text_buffer_insert(buffer, &iter_, " ", 1);
2594
                if (keep_cursor) {
2595
                        gtk_text_iter_backward_char(&iter_);
2596
                        gtk_text_buffer_place_cursor(buffer, &iter_);
2597
                }
2598
        }
2599
        g_free(str);
2600

    
2601
        *iter = iter_;
2602
        return TRUE;
2603
}
2604

    
2605
static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
2606
{
2607
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2608
        GtkTextBuffer *buffer;
2609
        GtkTextIter iter, break_pos;
2610
        gchar *quote_str = NULL;
2611
        gint quote_len;
2612
        gboolean wrap_quote = prefs_common.linewrap_quote;
2613
        gboolean prev_autowrap = compose->autowrap;
2614

    
2615
        compose->autowrap = FALSE;
2616

    
2617
        buffer = gtk_text_view_get_buffer(text);
2618

    
2619
        if (par_iter) {
2620
                iter = *par_iter;
2621
        } else {
2622
                GtkTextMark *mark;
2623
                mark = gtk_text_buffer_get_insert(buffer);
2624
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2625
        }
2626

    
2627
        /* move to paragraph start */
2628
        gtk_text_iter_set_line_offset(&iter, 0);
2629
        if (gtk_text_iter_ends_line(&iter)) {
2630
                while (gtk_text_iter_ends_line(&iter) &&
2631
                       gtk_text_iter_forward_line(&iter))
2632
                        ;
2633
        } else {
2634
                while (gtk_text_iter_backward_line(&iter)) {
2635
                        if (gtk_text_iter_ends_line(&iter)) {
2636
                                gtk_text_iter_forward_line(&iter);
2637
                                break;
2638
                        }
2639
                }
2640
        }
2641

    
2642
        /* go until paragraph end (empty line) */
2643
        while (!gtk_text_iter_ends_line(&iter)) {
2644
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
2645
                if (quote_str) {
2646
                        if (!wrap_quote) {
2647
                                gtk_text_iter_forward_line(&iter);
2648
                                g_free(quote_str);
2649
                                continue;
2650
                        }
2651
                        debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
2652
                }
2653

    
2654
                if (compose_get_line_break_pos(buffer, &iter, &break_pos,
2655
                                               prefs_common.linewrap_len,
2656
                                               quote_len)) {
2657
                        GtkTextIter prev, next, cur;
2658

    
2659
                        gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
2660

    
2661
                        /* remove trailing spaces */
2662
                        cur = break_pos;
2663
                        gtk_text_iter_backward_char(&cur);
2664
                        prev = next = cur;
2665
                        while (!gtk_text_iter_starts_line(&cur)) {
2666
                                gunichar wc;
2667

    
2668
                                gtk_text_iter_backward_char(&cur);
2669
                                wc = gtk_text_iter_get_char(&cur);
2670
                                if (!g_unichar_isspace(wc))
2671
                                        break;
2672
                                prev = cur;
2673
                        }
2674
                        if (!gtk_text_iter_equal(&prev, &next)) {
2675
                                gtk_text_buffer_delete(buffer, &prev, &next);
2676
                                break_pos = next;
2677
                                gtk_text_iter_forward_char(&break_pos);
2678
                        }
2679

    
2680
                        if (quote_str)
2681
                                gtk_text_buffer_insert(buffer, &break_pos,
2682
                                                       quote_str, -1);
2683

    
2684
                        iter = break_pos;
2685
                        compose_join_next_line(buffer, &iter, quote_str);
2686

    
2687
                        /* move iter to current line start */
2688
                        gtk_text_iter_set_line_offset(&iter, 0);
2689
                } else {
2690
                        /* move iter to next line start */
2691
                        iter = break_pos;
2692
                        gtk_text_iter_forward_line(&iter);
2693
                }
2694

    
2695
                g_free(quote_str);
2696
        }
2697

    
2698
        if (par_iter)
2699
                *par_iter = iter;
2700

    
2701
        compose->autowrap = prev_autowrap;
2702
}
2703

    
2704
static void compose_wrap_all(Compose *compose)
2705
{
2706
        compose_wrap_all_full(compose, FALSE);
2707
}
2708

    
2709
static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
2710
{
2711
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2712
        GtkTextBuffer *buffer;
2713
        GtkTextIter iter;
2714

    
2715
        buffer = gtk_text_view_get_buffer(text);
2716

    
2717
        gtk_text_buffer_get_start_iter(buffer, &iter);
2718
        while (!gtk_text_iter_is_end(&iter))
2719
                compose_wrap_paragraph(compose, &iter);
2720
}
2721

    
2722
static void compose_set_title(Compose *compose)
2723
{
2724
        gchar *str;
2725
        gchar *edited;
2726
        const gchar *subject;
2727

    
2728
        subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2729
        if (!subject || subject[0] == '\0')
2730
                subject = _("(No Subject)");
2731

    
2732
        edited = compose->modified ? " *" : "";
2733
        str = g_strdup_printf(_("%s - Compose%s"), subject, edited);
2734
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
2735
        g_free(str);
2736
}
2737

    
2738
static void compose_select_account(Compose *compose, PrefsAccount *account,
2739
                                   gboolean init)
2740
{
2741
        GtkItemFactory *ifactory;
2742
        PrefsAccount *prev_account;
2743

    
2744
        g_return_if_fail(account != NULL);
2745

    
2746
        prev_account = compose->account;
2747
        compose->account = account;
2748

    
2749
        compose_set_title(compose);
2750

    
2751
        ifactory = gtk_item_factory_from_widget(compose->menubar);
2752

    
2753
        if (account->protocol == A_NNTP &&
2754
            (init || prev_account->protocol != A_NNTP)) {
2755
                gtk_widget_show(compose->newsgroups_hbox);
2756
                gtk_widget_show(compose->newsgroups_entry);
2757
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2758
                compose->use_newsgroups = TRUE;
2759

    
2760
                menu_set_active(ifactory, "/View/To", FALSE);
2761
                menu_set_sensitive(ifactory, "/View/To", TRUE);
2762
                menu_set_active(ifactory, "/View/Cc", FALSE);
2763
                menu_set_sensitive(ifactory, "/View/Followup-To", TRUE);
2764
        } else if (account->protocol != A_NNTP &&
2765
                   (init || prev_account->protocol == A_NNTP)) {
2766
                gtk_widget_hide(compose->newsgroups_hbox);
2767
                gtk_widget_hide(compose->newsgroups_entry);
2768
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0);
2769
                gtk_widget_queue_resize(compose->table_vbox);
2770
                compose->use_newsgroups = FALSE;
2771

    
2772
                menu_set_active(ifactory, "/View/To", TRUE);
2773
                menu_set_sensitive(ifactory, "/View/To", FALSE);
2774
                menu_set_active(ifactory, "/View/Cc", TRUE);
2775
                menu_set_active(ifactory, "/View/Followup-To", FALSE);
2776
                menu_set_sensitive(ifactory, "/View/Followup-To", FALSE);
2777
        }
2778

    
2779
        if (account->set_autocc) {
2780
                compose_entry_show(compose, COMPOSE_ENTRY_CC);
2781
                if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2782
                        compose_entry_set(compose, account->auto_cc,
2783
                                          COMPOSE_ENTRY_CC);
2784
        }
2785
        if (account->set_autobcc) {
2786
                compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2787
                if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2788
                        compose_entry_set(compose, account->auto_bcc,
2789
                                          COMPOSE_ENTRY_BCC);
2790
        }
2791
        if (account->set_autoreplyto) {
2792
                compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2793
                if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2794
                        compose_entry_set(compose, account->auto_replyto,
2795
                                          COMPOSE_ENTRY_REPLY_TO);
2796
        }
2797

    
2798
#if USE_GPGME
2799
        if (rfc2015_is_available()) {
2800
                if (account->default_sign)
2801
                        menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2802
                if (account->default_encrypt)
2803
                        menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2804
        }
2805
#endif /* USE_GPGME */
2806

    
2807
        if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2808
                compose_insert_sig(compose, TRUE, TRUE, FALSE);
2809
}
2810

    
2811
static gboolean compose_check_for_valid_recipient(Compose *compose)
2812
{
2813
        const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
2814
        const gchar *newsgroups_raw = "";
2815
        gchar *to, *cc, *bcc;
2816
        gchar *newsgroups;
2817

    
2818
        if (compose->use_to)
2819
                to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2820
        if (compose->use_cc)
2821
                cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2822
        if (compose->use_bcc)
2823
                bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2824
        if (compose->use_newsgroups)
2825
                newsgroups_raw = gtk_entry_get_text
2826
                        (GTK_ENTRY(compose->newsgroups_entry));
2827

    
2828
        Xstrdup_a(to, to_raw, return FALSE);
2829
        Xstrdup_a(cc, cc_raw, return FALSE);
2830
        Xstrdup_a(bcc, bcc_raw, return FALSE);
2831
        Xstrdup_a(newsgroups, newsgroups_raw, return FALSE);
2832
        g_strstrip(to);
2833
        g_strstrip(cc);
2834
        g_strstrip(bcc);
2835
        g_strstrip(newsgroups);
2836

    
2837
        if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
2838
                return FALSE;
2839
        else
2840
                return TRUE;
2841
}
2842

    
2843
static gboolean compose_check_entries(Compose *compose)
2844
{
2845
        const gchar *str;
2846

    
2847
        if (compose_check_for_valid_recipient(compose) == FALSE) {
2848
                alertpanel_error(_("Recipient is not specified."));
2849
                return FALSE;
2850
        }
2851

    
2852
        str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2853
        if (*str == '\0') {
2854
                AlertValue aval;
2855

    
2856
                aval = alertpanel(_("Empty subject"),
2857
                                  _("Subject is empty. Send it anyway?"),
2858
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
2859
                if (aval != G_ALERTDEFAULT)
2860
                        return FALSE;
2861
        }
2862

    
2863
        return TRUE;
2864
}
2865

    
2866
static gboolean compose_check_attachments(Compose *compose)
2867
{
2868
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2869
        GtkTextBuffer *buffer;
2870
        GtkTextIter iter, line_end;
2871
        gchar *line;
2872
        gchar **strv;
2873
        gint i;
2874
        gboolean attach_found = FALSE;
2875
        gboolean valid = TRUE;
2876

    
2877
        if (!prefs_common.check_attach)
2878
                return TRUE;
2879
        if (!prefs_common.check_attach_str)
2880
                return TRUE;
2881

    
2882
        if (compose->use_attach &&
2883
            gtk_tree_model_iter_n_children
2884
                (GTK_TREE_MODEL(compose->attach_store), NULL) > 0)
2885
                return TRUE;
2886

    
2887
        buffer = gtk_text_view_get_buffer(text);
2888
        gtk_text_buffer_get_start_iter(buffer, &iter);
2889
        line_end = iter;
2890

    
2891
        strv = g_strsplit(prefs_common.check_attach_str, ",", -1);
2892
        for (i = 0; strv[i] != NULL; i++)
2893
                g_strstrip(strv[i]);
2894

    
2895
        while (valid) {
2896
                valid = gtk_text_iter_forward_to_line_end(&line_end);
2897
                line = gtk_text_buffer_get_text(buffer, &iter, &line_end,
2898
                                                FALSE);
2899
                iter = line_end;
2900
                if (get_quote_level(line) != -1)
2901
                        continue;
2902

    
2903
                for (i = 0; strv[i] != NULL; i++) {
2904
                        if (strv[i][0] == '\0')
2905
                                continue;
2906
                        if (strcasestr(line, strv[i])) {
2907
                                attach_found = TRUE;
2908
                                valid = FALSE;
2909
                                break;
2910
                        }
2911
                }
2912

    
2913
                g_free(line);
2914
        }
2915

    
2916
        g_strfreev(strv);
2917

    
2918
        if (attach_found) {
2919
                AlertValue aval;
2920

    
2921
                aval = alertpanel(_("Attachment is missing"),
2922
                                  _("There is no attachment. Send it without attachments?"),
2923
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
2924
                if (aval != G_ALERTDEFAULT)
2925
                        return FALSE;
2926
        }
2927

    
2928
        return TRUE;
2929
}
2930

    
2931
static gint check_recp_delete_event(GtkWidget *widget, GdkEventAny *event,
2932
                                    gint *state)
2933
{
2934
        *state = GTK_RESPONSE_CANCEL;
2935
        return TRUE;
2936
}
2937

    
2938
static gboolean check_recp_key_pressed(GtkWidget *widget, GdkEventKey *event,
2939
                                       gint *state)
2940
{
2941
        if (event && event->keyval == GDK_Escape) {
2942
                *state = GTK_RESPONSE_CANCEL;
2943
                return TRUE;
2944
        }
2945
        return FALSE;
2946
}
2947

    
2948
static void check_recp_ok(GtkWidget *widget, gint *state)
2949
{
2950
        *state = GTK_RESPONSE_OK;
2951
}
2952

    
2953
static void check_recp_cancel(GtkWidget *widget, gint *state)
2954
{
2955
        *state = GTK_RESPONSE_CANCEL;
2956
}
2957

    
2958
static gboolean compose_check_recipients(Compose *compose)
2959
{
2960
        GtkWidget *window;
2961
        GtkWidget *vbox;
2962
        GtkWidget *hbox;
2963
        GtkWidget *image;
2964
        GtkWidget *vbox2;
2965
        GtkWidget *label;
2966
        GtkWidget *table;
2967
        GtkWidget *entry;
2968
        gchar buf[1024];
2969
        const gchar *text;
2970
        GtkWidget *scrwin;
2971
        GtkWidget *treeview;
2972
        GtkTreeStore *store;
2973
        GtkTreeViewColumn *column;
2974
        GtkCellRenderer *renderer;
2975
        GtkTreeIter iter, parent;
2976
        GtkWidget *hbbox;
2977
        GtkWidget *ok_btn;
2978
        GtkWidget *cancel_btn;
2979
        static PangoFontDescription *font_desc;
2980
        GtkStyle *style;
2981

    
2982
        GSList *cur, *to_list = NULL;
2983
        gboolean check_recp = FALSE;
2984
        gint state = 0;
2985
 
2986
        g_return_val_if_fail(compose->account != NULL, FALSE);
2987
        g_return_val_if_fail(compose->account->address != NULL, FALSE);
2988

    
2989
        if (!prefs_common.check_recipients)
2990
                return TRUE;
2991

    
2992
        if (prefs_common.check_recp_exclude) {
2993
                gchar **strv;
2994
                gint i;
2995

    
2996
                strv = g_strsplit(prefs_common.check_recp_exclude, ",", -1);
2997
                for (i = 0; strv[i] != NULL; i++)
2998
                        g_strstrip(strv[i]);
2999

    
3000
                if (compose->use_to) {
3001
                        text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3002
                        to_list = address_list_append_orig(NULL, text);
3003
                }
3004
                if (compose->use_cc) {
3005
                        text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3006
                        to_list = address_list_append_orig(to_list, text);
3007
                }
3008
                if (compose->use_bcc) {
3009
                        text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3010
                        to_list = address_list_append_orig(to_list, text);
3011
                }
3012

    
3013
                for (cur = to_list; cur != NULL; cur = cur->next) {
3014
                        for (i = 0; strv[i] != NULL; i++) {
3015
                                if (strv[i][0] == '\0')
3016
                                        continue;
3017
                                if (strcasestr((gchar *)cur->data, strv[i]))
3018
                                        break;
3019
                        }
3020
                        if (!strv[i]) {
3021
                                /* not found in exclude list */
3022
                                check_recp = TRUE;
3023
                                break;
3024
                        }
3025
                }
3026

    
3027
                slist_free_strings(to_list);
3028
                g_slist_free(to_list);
3029
                to_list = NULL;
3030
                g_strfreev(strv);
3031
        } else
3032
                check_recp = TRUE;
3033

    
3034
        if (!check_recp)
3035
                return TRUE;
3036

    
3037
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3038
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
3039
        gtk_window_set_title(GTK_WINDOW(window), _("Check recipients"));
3040
        gtk_window_set_position(GTK_WINDOW(window),
3041
                                GTK_WIN_POS_CENTER_ON_PARENT);
3042
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
3043
        gtk_widget_set_size_request(window, 480, -1);
3044
        gtk_widget_realize(window);
3045
        g_signal_connect(G_OBJECT(window), "delete_event",
3046
                         G_CALLBACK(check_recp_delete_event), &state);
3047
        g_signal_connect(G_OBJECT(window), "key_press_event",
3048
                         G_CALLBACK(check_recp_key_pressed), &state);
3049

    
3050
        vbox = gtk_vbox_new(FALSE, 8);
3051
        gtk_container_add(GTK_CONTAINER(window), vbox);
3052

    
3053
        hbox = gtk_hbox_new(FALSE, 12);
3054
        gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
3055
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3056

    
3057
        image = gtk_image_new_from_stock
3058
                (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
3059
        gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0);
3060
        gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
3061

    
3062
        vbox2 = gtk_vbox_new(FALSE, 12);
3063
        gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
3064

    
3065
        label = gtk_label_new(_("Check recipients"));
3066
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3067
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3068
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3069

    
3070
        if (!font_desc) {
3071
                gint size;
3072

    
3073
                size = pango_font_description_get_size
3074
                        (label->style->font_desc);
3075
                font_desc = pango_font_description_new();
3076
                pango_font_description_set_weight
3077
                        (font_desc, PANGO_WEIGHT_BOLD);
3078
                pango_font_description_set_size
3079
                        (font_desc, size * PANGO_SCALE_LARGE);
3080
        }
3081
        if (font_desc)
3082
                gtk_widget_modify_font(label, font_desc);
3083

    
3084
        label = gtk_label_new
3085
                (_("Really send this mail to the following addresses?"));
3086
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3087
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3088
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3089
        gtk_label_set_selectable(GTK_LABEL(label), TRUE);
3090
        GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS);
3091

    
3092
        table = gtk_table_new(2, 2, FALSE);
3093
        gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
3094
        gtk_table_set_row_spacings(GTK_TABLE(table), 4);
3095
        gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3096

    
3097
        hbox = gtk_hbox_new(FALSE, 0);
3098
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:")
3099
                              : "From:");
3100
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3101
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1,
3102
                         GTK_FILL, 0, 2, 0);
3103
        entry = gtk_entry_new();
3104
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3105
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3106
        style = gtk_widget_get_style(window);
3107
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3108
                               &style->bg[GTK_STATE_NORMAL]);
3109
        gtk_table_attach_defaults
3110
                (GTK_TABLE(table), entry, 1, 2, 0, 1);
3111

    
3112
        if (compose->account->name && *compose->account->name) {
3113
                g_snprintf(buf, sizeof(buf), "%s <%s>",
3114
                           compose->account->name, compose->account->address);
3115
                gtk_entry_set_text(GTK_ENTRY(entry), buf);
3116
        } else
3117
                gtk_entry_set_text(GTK_ENTRY(entry), compose->account->address);
3118

    
3119
        hbox = gtk_hbox_new(FALSE, 0);
3120
        label = gtk_label_new(prefs_common.trans_hdr ? _("Subject:")
3121
                              : "Subject:");
3122
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3123
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2,
3124
                         GTK_FILL, 0, 2, 0);
3125
        entry = gtk_entry_new();
3126
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3127
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3128
        style = gtk_widget_get_style(window);
3129
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3130
                               &style->bg[GTK_STATE_NORMAL]);
3131
        gtk_table_attach_defaults
3132
                (GTK_TABLE(table), entry, 1, 2, 1, 2);
3133

    
3134
        text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3135
        gtk_entry_set_text(GTK_ENTRY(entry), text);
3136

    
3137
        scrwin = gtk_scrolled_window_new(NULL, NULL);
3138
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
3139
                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3140
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
3141
                                            GTK_SHADOW_IN);
3142
        gtk_widget_set_size_request(scrwin, -1, 180);
3143
        gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
3144

    
3145
        store = gtk_tree_store_new(1, G_TYPE_STRING);
3146
        if (compose->use_to) {
3147
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3148
                to_list = address_list_append_orig(NULL, text);
3149
                if (to_list) {
3150
                        gtk_tree_store_append(store, &parent, NULL);
3151
                        gtk_tree_store_set(store, &parent, 0,
3152
                                           prefs_common.trans_hdr ?
3153
                                           _("To:") : "To:", -1);
3154
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3155
                                gtk_tree_store_append(store, &iter, &parent);
3156
                                gtk_tree_store_set(store, &iter, 0,
3157
                                                   (gchar *)cur->data, -1);
3158
                        }
3159
                        slist_free_strings(to_list);
3160
                        g_slist_free(to_list);
3161
                }
3162
        }
3163
        if (compose->use_cc) {
3164
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3165
                to_list = address_list_append_orig(NULL, text);
3166
                if (to_list) {
3167
                        gtk_tree_store_append(store, &parent, NULL);
3168
                        gtk_tree_store_set(store, &parent, 0,
3169
                                           prefs_common.trans_hdr ?
3170
                                           _("Cc:") : "Cc:", -1);
3171
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3172
                                gtk_tree_store_append(store, &iter, &parent);
3173
                                gtk_tree_store_set(store, &iter, 0,
3174
                                                   (gchar *)cur->data, -1);
3175
                        }
3176
                        slist_free_strings(to_list);
3177
                        g_slist_free(to_list);
3178
                }
3179
        }
3180
        if (compose->use_bcc) {
3181
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3182
                to_list = address_list_append_orig(NULL, text);
3183
                if (to_list) {
3184
                        gtk_tree_store_append(store, &parent, NULL);
3185
                        gtk_tree_store_set(store, &parent, 0,
3186
                                           prefs_common.trans_hdr ?
3187
                                           _("Bcc:") : "Bcc:", -1);
3188
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3189
                                gtk_tree_store_append(store, &iter, &parent);
3190
                                gtk_tree_store_set(store, &iter, 0,
3191
                                                   (gchar *)cur->data, -1);
3192
                        }
3193
                        slist_free_strings(to_list);
3194
                        g_slist_free(to_list);
3195
                }
3196
        }
3197

    
3198
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3199
        g_object_unref(G_OBJECT(store));
3200
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
3201
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
3202

    
3203
        gtk_container_add(GTK_CONTAINER(scrwin), treeview);
3204

    
3205
        renderer = gtk_cell_renderer_text_new();
3206
        g_object_set(renderer, "ypad", 0, NULL);
3207
        column = gtk_tree_view_column_new_with_attributes
3208
                (_("Address"), renderer, "text", 0, NULL);
3209
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
3210

    
3211
        gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
3212

    
3213
        gtkut_stock_button_set_create(&hbbox, &ok_btn, _("_Send"),
3214
                                      &cancel_btn, GTK_STOCK_CANCEL,
3215
                                      NULL, NULL);
3216
        gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
3217
        gtk_widget_grab_default(ok_btn);
3218
        gtk_widget_grab_focus(ok_btn);
3219

    
3220
        g_signal_connect(G_OBJECT(ok_btn), "clicked",
3221
                         G_CALLBACK(check_recp_ok), &state);
3222
        g_signal_connect(G_OBJECT(cancel_btn), "clicked",
3223
                         G_CALLBACK(check_recp_cancel), &state);
3224

    
3225
        manage_window_set_transient(GTK_WINDOW(window));
3226

    
3227
        gtk_widget_show_all(window);
3228

    
3229
        while (state == 0)
3230
                gtk_main_iteration();
3231

    
3232
        gtk_widget_destroy(window);
3233

    
3234
        if (state == GTK_RESPONSE_OK)
3235
                return TRUE;
3236

    
3237
        return FALSE;
3238
}
3239

    
3240
void compose_lock(Compose *compose)
3241
{
3242
        compose->lock_count++;
3243
}
3244

    
3245
void compose_unlock(Compose *compose)
3246
{
3247
        if (compose->lock_count > 0)
3248
                compose->lock_count--;
3249
}
3250

    
3251
static gint compose_send(Compose *compose)
3252
{
3253
        gchar tmp[MAXPATHLEN + 1];
3254
        gint ok = 0;
3255

    
3256
        if (compose->lock_count > 0)
3257
                return 1;
3258

    
3259
        g_return_val_if_fail(compose->account != NULL, -1);
3260

    
3261
        compose_lock(compose);
3262

    
3263
        if (compose_check_entries(compose) == FALSE) {
3264
                compose_unlock(compose);
3265
                return 1;
3266
        }
3267
        if (compose_check_attachments(compose) == FALSE) {
3268
                compose_unlock(compose);
3269
                return 1;
3270
        }
3271
        if (compose_check_recipients(compose) == FALSE) {
3272
                compose_unlock(compose);
3273
                return 1;
3274
        }
3275

    
3276
        if (!main_window_toggle_online_if_offline(main_window_get())) {
3277
                compose_unlock(compose);
3278
                return 1;
3279
        }
3280

    
3281
        /* write to temporary file */
3282
        g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
3283
                   get_tmp_dir(), G_DIR_SEPARATOR, compose);
3284

    
3285
        if (compose->mode == COMPOSE_REDIRECT) {
3286
                if (compose_redirect_write_to_file(compose, tmp) < 0) {
3287
                        compose_unlock(compose);
3288
                        return -1;
3289
                }
3290
        } else {
3291
                if (compose_write_to_file(compose, tmp, FALSE) < 0) {
3292
                        compose_unlock(compose);
3293
                        return -1;
3294
                }
3295
        }
3296

    
3297
        if (!compose->to_list && !compose->newsgroup_list) {
3298
                g_warning(_("can't get recipient list."));
3299
                g_unlink(tmp);
3300
                compose_unlock(compose);
3301
                return -1;
3302
        }
3303

    
3304
        if (compose->to_list) {
3305
                PrefsAccount *ac;
3306

    
3307
                if (compose->account->protocol != A_NNTP)
3308
                        ac = compose->account;
3309
                else {
3310
                        ac = account_find_from_address(compose->account->address);
3311
                        if (!ac) {
3312
                                if (cur_account && cur_account->protocol != A_NNTP)
3313
                                        ac = cur_account;
3314
                                else
3315
                                        ac = account_get_default();
3316
                        }
3317
                        if (!ac || ac->protocol == A_NNTP) {
3318
                                alertpanel_error(_("Account for sending mail is not specified.\n"
3319
                                                   "Please select a mail account before sending."));
3320
                                g_unlink(tmp);
3321
                                compose_unlock(compose);
3322
                                return -1;
3323
                        }
3324
                }
3325
                ok = send_message(tmp, ac, compose->to_list);
3326
                statusbar_pop_all();
3327
        }
3328

    
3329
        if (ok == 0 && compose->newsgroup_list) {
3330
                ok = news_post(FOLDER(compose->account->folder), tmp);
3331
                if (ok < 0) {
3332
                        alertpanel_error(_("Error occurred while posting the message to %s ."),
3333
                                         compose->account->nntp_server);
3334
                        g_unlink(tmp);
3335
                        compose_unlock(compose);
3336
                        return -1;
3337
                }
3338
        }
3339

    
3340
        if (ok == 0) {
3341
                if (compose->mode == COMPOSE_REEDIT) {
3342
                        compose_remove_reedit_target(compose);
3343
                        if (compose->targetinfo)
3344
                                folderview_update_item
3345
                                        (compose->targetinfo->folder, TRUE);
3346
                }
3347

    
3348
                if (compose->reply_target)
3349
                        send_message_set_reply_flag(compose->reply_target,
3350
                                                    compose->inreplyto);
3351
                else if (compose->forward_targets)
3352
                        send_message_set_forward_flags
3353
                                (compose->forward_targets);
3354

    
3355
                /* save message to outbox */
3356
                if (prefs_common.savemsg) {
3357
                        FolderItem *outbox;
3358
                        gboolean drop_done = FALSE;
3359

    
3360
                        /* filter sent message */
3361
                        if (prefs_common.filter_sent) {
3362
                                FilterInfo *fltinfo;
3363

    
3364
                                fltinfo = filter_info_new();
3365
                                fltinfo->account = compose->account;
3366
                                fltinfo->flags.perm_flags = 0;
3367
                                fltinfo->flags.tmp_flags = MSG_RECEIVED;
3368

    
3369
                                filter_apply(prefs_common.fltlist, tmp,
3370
                                             fltinfo);
3371

    
3372
                                drop_done = fltinfo->drop_done;
3373
                                folderview_update_all_updated(TRUE);
3374
                                filter_info_free(fltinfo);
3375
                        }
3376

    
3377
                        if (!drop_done) {
3378
                                outbox = account_get_special_folder
3379
                                        (compose->account, F_OUTBOX);
3380
                                if (procmsg_save_to_outbox(outbox, tmp) < 0)
3381
                                        alertpanel_error
3382
                                                (_("Can't save the message to outbox."));
3383
                                else
3384
                                        folderview_update_item(outbox, TRUE);
3385
                        }
3386
                }
3387
        }
3388

    
3389
        g_unlink(tmp);
3390
        compose_unlock(compose);
3391

    
3392
        return ok;
3393
}
3394

    
3395
#if USE_GPGME
3396
/* interfaces to rfc2015 to keep out the prefs stuff there.
3397
 * returns 0 on success and -1 on error. */
3398
static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
3399
{
3400
        const gchar *key_id = NULL;
3401
        GSList *key_list;
3402

    
3403
        switch (compose->account->sign_key) {
3404
        case SIGN_KEY_DEFAULT:
3405
                *pkey_list = NULL;
3406
                return 0;
3407
        case SIGN_KEY_BY_FROM:
3408
                key_id = compose->account->address;
3409
                break;
3410
        case SIGN_KEY_CUSTOM:
3411
                key_id = compose->account->sign_key_id;
3412
                break;
3413
        default:
3414
                break;
3415
        }
3416

    
3417
        key_list = rfc2015_create_signers_list(key_id);
3418

    
3419
        if (!key_list) {
3420
                alertpanel_error(_("Could not find any key associated with "
3421
                                   "currently selected key id `%s'."), key_id);
3422
                return -1;
3423
        }
3424

    
3425
        *pkey_list = key_list;
3426
        return 0;
3427
}
3428

    
3429
/* clearsign message body text */
3430
static gint compose_clearsign_text(Compose *compose, gchar **text)
3431
{
3432
        GSList *key_list;
3433
        gchar *tmp_file;
3434

    
3435
        tmp_file = get_tmp_file();
3436
        if (str_write_to_file(*text, tmp_file) < 0) {
3437
                g_free(tmp_file);
3438
                return -1;
3439
        }
3440

    
3441
        if (compose_create_signers_list(compose, &key_list) < 0) {
3442
                g_unlink(tmp_file);
3443
                g_free(tmp_file);
3444
                return -1;
3445
        }
3446
        if (rfc2015_clearsign(tmp_file, key_list) < 0) {
3447
                alertpanel_error(_("Can't sign the message."));
3448
                g_unlink(tmp_file);
3449
                g_free(tmp_file);
3450
                return -1;
3451
        }
3452

    
3453
        g_free(*text);
3454
        *text = file_read_to_str(tmp_file);
3455
        g_unlink(tmp_file);
3456
        g_free(tmp_file);
3457
        if (*text == NULL)
3458
                return -1;
3459

    
3460
        return 0;
3461
}
3462

    
3463
static gint compose_encrypt_armored(Compose *compose, gchar **text)
3464
{
3465
        gchar *tmp_file;
3466

    
3467
        tmp_file = get_tmp_file();
3468
        if (str_write_to_file(*text, tmp_file) < 0) {
3469
                g_free(tmp_file);
3470
                return -1;
3471
        }
3472

    
3473
        if (rfc2015_encrypt_armored(tmp_file, compose->to_list) < 0) {
3474
                alertpanel_error(_("Can't encrypt the message."));
3475
                g_unlink(tmp_file);
3476
                g_free(tmp_file);
3477
                return -1;
3478
        }
3479

    
3480
        g_free(*text);
3481
        *text = file_read_to_str(tmp_file);
3482
        g_unlink(tmp_file);
3483
        g_free(tmp_file);
3484
        if (*text == NULL)
3485
                return -1;
3486

    
3487
        return 0;
3488
}
3489

    
3490
static gint compose_encrypt_sign_armored(Compose *compose, gchar **text)
3491
{
3492
        GSList *key_list;
3493
        gchar *tmp_file;
3494

    
3495
        tmp_file = get_tmp_file();
3496
        if (str_write_to_file(*text, tmp_file) < 0) {
3497
                g_free(tmp_file);
3498
                return -1;
3499
        }
3500

    
3501
        if (compose_create_signers_list(compose, &key_list) < 0) {
3502
                g_unlink(tmp_file);
3503
                g_free(tmp_file);
3504
                return -1;
3505
        }
3506

    
3507
        if (rfc2015_encrypt_sign_armored
3508
                (tmp_file, compose->to_list, key_list) < 0) {
3509
                alertpanel_error(_("Can't encrypt or sign the message."));
3510
                g_unlink(tmp_file);
3511
                g_free(tmp_file);
3512
                return -1;
3513
        }
3514

    
3515
        g_free(*text);
3516
        *text = file_read_to_str(tmp_file);
3517
        g_unlink(tmp_file);
3518
        g_free(tmp_file);
3519
        if (*text == NULL)
3520
                return -1;
3521

    
3522
        return 0;
3523
}
3524
#endif /* USE_GPGME */
3525

    
3526
static gint compose_write_to_file(Compose *compose, const gchar *file,
3527
                                  gboolean is_draft)
3528
{
3529
        GtkTextBuffer *buffer;
3530
        GtkTextIter start, end;
3531
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
3532
        FILE *fp;
3533
        size_t len;
3534
        gchar *chars;
3535
        gchar *buf;
3536
        gchar *canon_buf;
3537
        const gchar *out_charset;
3538
        const gchar *body_charset;
3539
        const gchar *src_charset = CS_INTERNAL;
3540
        EncodingType encoding;
3541
        gint line;
3542
#if USE_GPGME
3543
        gboolean use_pgpmime_encryption = FALSE;
3544
        gboolean use_pgpmime_signing = FALSE;
3545
#endif
3546

    
3547
        if ((fp = g_fopen(file, "wb")) == NULL) {
3548
                FILE_OP_ERROR(file, "fopen");
3549
                return -1;
3550
        }
3551

    
3552
        /* chmod for security */
3553
        if (change_file_mode_rw(fp, file) < 0) {
3554
                FILE_OP_ERROR(file, "chmod");
3555
                g_warning(_("can't change file mode\n"));
3556
        }
3557

    
3558
        /* get outgoing charset */
3559
        out_charset = conv_get_charset_str(compose->out_encoding);
3560
        if (!out_charset)
3561
                out_charset = conv_get_outgoing_charset_str();
3562
        if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII))
3563
                out_charset = CS_ISO_8859_1;
3564
        body_charset = out_charset;
3565

    
3566
        /* get all composed text */
3567
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3568
        gtk_text_buffer_get_start_iter(buffer, &start);
3569
        gtk_text_buffer_get_end_iter(buffer, &end);
3570
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3571
        if (is_ascii_str(chars)) {
3572
                buf = chars;
3573
                chars = NULL;
3574
                body_charset = CS_US_ASCII;
3575
                encoding = ENC_7BIT;
3576
        } else {
3577
                gint error = 0;
3578

    
3579
                buf = conv_codeset_strdup_full
3580
                        (chars, src_charset, body_charset, &error);
3581
                if (!buf || error != 0) {
3582
                        AlertValue aval = G_ALERTDEFAULT;
3583
                        gchar *msg;
3584

    
3585
                        g_free(buf);
3586

    
3587
                        if (!is_draft) {
3588
                                msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n"
3589
                                                        "\n"
3590
                                                        "Send it as %s anyway?"),
3591
                                                      src_charset, body_charset,
3592
                                                      src_charset);
3593
                                aval = alertpanel_full
3594
                                        (_("Code conversion error"), msg, ALERT_ERROR,
3595
                                         G_ALERTALTERNATE,
3596
                                         FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3597
                                g_free(msg);
3598
                        }
3599

    
3600
                        if (aval != G_ALERTDEFAULT) {
3601
                                g_free(chars);
3602
                                fclose(fp);
3603
                                g_unlink(file);
3604
                                return -1;
3605
                        } else {
3606
                                buf = chars;
3607
                                out_charset = body_charset = src_charset;
3608
                                chars = NULL;
3609
                        }
3610
                }
3611

    
3612
                if (prefs_common.encoding_method == CTE_BASE64)
3613
                        encoding = ENC_BASE64;
3614
                else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3615
                        encoding = ENC_QUOTED_PRINTABLE;
3616
                else if (prefs_common.encoding_method == CTE_8BIT)
3617
                        encoding = ENC_8BIT;
3618
                else
3619
                        encoding = procmime_get_encoding_for_charset
3620
                                (body_charset);
3621
        }
3622
        g_free(chars);
3623

    
3624
        canon_buf = canonicalize_str(buf);
3625
        g_free(buf);
3626
        buf = canon_buf;
3627

    
3628
#if USE_GPGME
3629
        if (compose->use_signing && !compose->account->clearsign)
3630
                use_pgpmime_signing = TRUE;
3631
        if (compose->use_encryption && compose->account->ascii_armored) {
3632
                use_pgpmime_encryption = FALSE;
3633
                use_pgpmime_signing = FALSE;
3634
        }
3635
        if (compose->use_encryption && !compose->account->ascii_armored)
3636
                use_pgpmime_encryption = TRUE;
3637

    
3638
        /* protect trailing spaces */
3639
        if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) {
3640
                if (encoding == ENC_7BIT) {
3641
                        if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) {
3642
                                gchar *tmp;
3643
                                tmp = strchomp_all(buf);
3644
                                g_free(buf);
3645
                                buf = tmp;
3646
                        } else
3647
                                encoding = ENC_QUOTED_PRINTABLE;
3648
                } else if (encoding == ENC_8BIT) {
3649
                        encoding = procmime_get_encoding_for_str(buf);
3650
                        if (encoding == ENC_7BIT)
3651
                                encoding = ENC_QUOTED_PRINTABLE;
3652
                }
3653
        }
3654

    
3655
        if (rfc2015_is_available() && !is_draft) {
3656
                if ((compose->use_encryption &&
3657
                     compose->account->ascii_armored) ||
3658
                    (compose->use_signing && compose->account->clearsign)) {
3659
                        /* MIME encoding doesn't fit with cleartext signature */
3660
                        if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64)
3661
                                encoding = ENC_8BIT;
3662

    
3663
                }
3664
        }
3665
#endif
3666

    
3667
        debug_print("src encoding = %s, out encoding = %s, "
3668
                    "body encoding = %s, transfer encoding = %s\n",
3669
                    src_charset, out_charset, body_charset,
3670
                    procmime_get_encoding_str(encoding));
3671

    
3672
        /* check for line length limit */
3673
        if (!is_draft &&
3674
            encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
3675
            check_line_length(buf, 1000, &line) < 0) {
3676
                AlertValue aval;
3677
                gchar *msg;
3678

    
3679
                msg = g_strdup_printf
3680
                        (_("Line %d exceeds the line length limit (998 bytes).\n"
3681
                           "The contents of the message might be broken on the way to the delivery.\n"
3682
                           "\n"
3683
                           "Send it anyway?"), line + 1);
3684
                aval = alertpanel_full(_("Line length limit"),
3685
                                       msg, ALERT_WARNING,
3686
                                       G_ALERTALTERNATE, FALSE,
3687
                                       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3688
                if (aval != G_ALERTDEFAULT) {
3689
                        g_free(msg);
3690
                        fclose(fp);
3691
                        g_unlink(file);
3692
                        g_free(buf);
3693
                        return -1;
3694
                }
3695
        }
3696

    
3697
        /* write headers */
3698
        if (compose_write_headers(compose, fp, out_charset,
3699
                                  body_charset, encoding, is_draft) < 0) {
3700
                g_warning("can't write headers\n");
3701
                fclose(fp);
3702
                g_unlink(file);
3703
                g_free(buf);
3704
                return -1;
3705
        }
3706

    
3707
#if USE_GPGME
3708
        /* do ascii-armor encryption and/or clearsign */
3709
        if (rfc2015_is_available() && !is_draft) {
3710
                gint ret;
3711

    
3712
                if (compose->use_encryption && compose->account->ascii_armored) {
3713
                        if (compose->use_signing)
3714
                                ret = compose_encrypt_sign_armored(compose, &buf);
3715
                        else
3716
                                ret = compose_encrypt_armored(compose, &buf);
3717
                        if (ret < 0) {
3718
                                g_warning("ascii-armored encryption failed\n");
3719
                                fclose(fp);
3720
                                g_unlink(file);
3721
                                g_free(buf);
3722
                                return -1;
3723
                        }
3724
                } else if (compose->use_signing && compose->account->clearsign) {
3725
                        if (compose_clearsign_text(compose, &buf) < 0) {
3726
                                g_warning("clearsign failed\n");
3727
                                fclose(fp);
3728
                                g_unlink(file);
3729
                                g_free(buf);
3730
                                return -1;
3731
                        }
3732
                }
3733
        }
3734
#endif
3735

    
3736
        if (compose->use_attach &&
3737
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3738
#if USE_GPGME
3739
            /* This prolog message is ignored by mime software and
3740
             * because it would make our signing/encryption task
3741
             * tougher, we don't emit it in that case */
3742
            if (!rfc2015_is_available() ||
3743
                (!compose->use_signing && !compose->use_encryption))
3744
#endif
3745
                fputs("This is a multi-part message in MIME format.\n", fp);
3746

    
3747
                fprintf(fp, "\n--%s\n", compose->boundary);
3748
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
3749
                        body_charset);
3750
#if USE_GPGME
3751
                if (rfc2015_is_available() && use_pgpmime_signing)
3752
                        fprintf(fp, "Content-Disposition: inline\n");
3753
#endif
3754
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
3755
                        procmime_get_encoding_str(encoding));
3756
                fputc('\n', fp);
3757
        }
3758

    
3759
        /* write body */
3760
        len = strlen(buf);
3761
        if (encoding == ENC_BASE64) {
3762
                gchar outbuf[B64_BUFFSIZE];
3763
                gint i, l;
3764

    
3765
                for (i = 0; i < len; i += B64_LINE_SIZE) {
3766
                        l = MIN(B64_LINE_SIZE, len - i);
3767
                        base64_encode(outbuf, (guchar *)buf + i, l);
3768
                        fputs(outbuf, fp);
3769
                        fputc('\n', fp);
3770
                }
3771
        } else if (encoding == ENC_QUOTED_PRINTABLE) {
3772
                gchar *outbuf;
3773
                size_t outlen;
3774

    
3775
                outbuf = g_malloc(len * 4);
3776
                qp_encode_line(outbuf, (guchar *)buf);
3777
                outlen = strlen(outbuf);
3778
                if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
3779
                        FILE_OP_ERROR(file, "fwrite");
3780
                        fclose(fp);
3781
                        g_unlink(file);
3782
                        g_free(outbuf);
3783
                        g_free(buf);
3784
                        return -1;
3785
                }
3786
                g_free(outbuf);
3787
        } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
3788
                FILE_OP_ERROR(file, "fwrite");
3789
                fclose(fp);
3790
                g_unlink(file);
3791
                g_free(buf);
3792
                return -1;
3793
        }
3794
        g_free(buf);
3795

    
3796
        if (compose->use_attach &&
3797
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3798
                if (compose_write_attach(compose, fp, out_charset) < 0) {
3799
                        fclose(fp);
3800
                        g_unlink(file);
3801
                        return -1;
3802
                }
3803
        }
3804

    
3805
        if (fclose(fp) == EOF) {
3806
                FILE_OP_ERROR(file, "fclose");
3807
                g_unlink(file);
3808
                return -1;
3809
        }
3810

    
3811
#if USE_GPGME
3812
        if (!rfc2015_is_available() || is_draft) {
3813
                uncanonicalize_file_replace(file);
3814
                return 0;
3815
        }
3816

    
3817
        if (use_pgpmime_signing || use_pgpmime_encryption) {
3818
                if (canonicalize_file_replace(file) < 0) {
3819
                        g_unlink(file);
3820
                        return -1;
3821
                }
3822
        }
3823

    
3824
        if (use_pgpmime_signing && !use_pgpmime_encryption) {
3825
                GSList *key_list;
3826

    
3827
                if (compose_create_signers_list(compose, &key_list) < 0) {
3828
                        g_unlink(file);
3829
                        return -1;
3830
                }
3831
                if (rfc2015_sign(file, key_list) < 0) {
3832
                        alertpanel_error(_("Can't sign the message."));
3833
                        g_unlink(file);
3834
                        return -1;
3835
                }
3836
        } else if (use_pgpmime_encryption) {
3837
                GSList *key_list;
3838

    
3839
                if (compose->use_bcc) {
3840
                        const gchar *text;
3841
                        gchar *bcc;
3842
                        AlertValue aval;
3843

    
3844
                        text = gtk_entry_get_text
3845
                                (GTK_ENTRY(compose->bcc_entry));
3846
                        Xstrdup_a(bcc, text, { g_unlink(file); return -1; });
3847
                        g_strstrip(bcc);
3848
                        if (*bcc != '\0') {
3849
                                aval = alertpanel_full
3850
                                        (_("Encrypting with Bcc"),
3851
                                         _("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"
3852
                                           "\n"
3853
                                           "Send it anyway?"),
3854
                                         ALERT_WARNING, G_ALERTDEFAULT, FALSE,
3855
                                         GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3856
                                if (aval != G_ALERTDEFAULT) {
3857
                                        g_unlink(file);
3858
                                        return -1;
3859
                                }
3860
                        }
3861
                }
3862
                if (use_pgpmime_signing) {
3863
                        if (compose_create_signers_list
3864
                                (compose, &key_list) < 0) {
3865
                                g_unlink(file);
3866
                                return -1;
3867
                        }
3868
                        if (rfc2015_encrypt_sign(file, compose->to_list,
3869
                                                 key_list) < 0) {
3870
                                alertpanel_error(_("Can't encrypt or sign the message."));
3871
                                g_unlink(file);
3872
                                return -1;
3873
                        }
3874
                } else if (rfc2015_encrypt(file, compose->to_list) < 0) {
3875
                        alertpanel_error(_("Can't encrypt the message."));
3876
                        g_unlink(file);
3877
                        return -1;
3878
                }
3879
        }
3880
#endif /* USE_GPGME */
3881

    
3882
        uncanonicalize_file_replace(file);
3883

    
3884
        return 0;
3885
}
3886

    
3887
static gint compose_write_body_to_file(Compose *compose, const gchar *file)
3888
{
3889
        GtkTextBuffer *buffer;
3890
        GtkTextIter start, end;
3891
        FILE *fp;
3892
        size_t len;
3893
        gchar *chars, *tmp;
3894

    
3895
        if ((fp = g_fopen(file, "wb")) == NULL) {
3896
                FILE_OP_ERROR(file, "fopen");
3897
                return -1;
3898
        }
3899

    
3900
        /* chmod for security */
3901
        if (change_file_mode_rw(fp, file) < 0) {
3902
                FILE_OP_ERROR(file, "chmod");
3903
                g_warning(_("can't change file mode\n"));
3904
        }
3905

    
3906
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3907
        gtk_text_buffer_get_start_iter(buffer, &start);
3908
        gtk_text_buffer_get_end_iter(buffer, &end);
3909
        tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3910

    
3911
        chars = conv_codeset_strdup
3912
                (tmp, CS_INTERNAL, conv_get_locale_charset_str());
3913

    
3914
        g_free(tmp);
3915

    
3916
        if (!chars) {
3917
                fclose(fp);
3918
                g_unlink(file);
3919
                return -1;
3920
        }
3921

    
3922
        /* write body */
3923
        len = strlen(chars);
3924
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
3925
                FILE_OP_ERROR(file, "fwrite");
3926
                g_free(chars);
3927
                fclose(fp);
3928
                g_unlink(file);
3929
                return -1;
3930
        }
3931

    
3932
        g_free(chars);
3933

    
3934
        if (fclose(fp) == EOF) {
3935
                FILE_OP_ERROR(file, "fclose");
3936
                g_unlink(file);
3937
                return -1;
3938
        }
3939
        return 0;
3940
}
3941

    
3942
static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
3943
{
3944
        FILE *fp;
3945
        FILE *fdest;
3946
        size_t len;
3947
        gchar buf[BUFFSIZE];
3948

    
3949
        g_return_val_if_fail(file != NULL, -1);
3950
        g_return_val_if_fail(compose->account != NULL, -1);
3951
        g_return_val_if_fail(compose->account->address != NULL, -1);
3952
        g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
3953
        g_return_val_if_fail(compose->targetinfo != NULL, -1);
3954

    
3955
        if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
3956
                return -1;
3957

    
3958
        if ((fdest = g_fopen(file, "wb")) == NULL) {
3959
                FILE_OP_ERROR(file, "fopen");
3960
                fclose(fp);
3961
                return -1;
3962
        }
3963

    
3964
        if (change_file_mode_rw(fdest, file) < 0) {
3965
                FILE_OP_ERROR(file, "chmod");
3966
                g_warning(_("can't change file mode\n"));
3967
        }
3968

    
3969
        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
3970
                if (g_ascii_strncasecmp(buf, "Return-Path:",
3971
                                        strlen("Return-Path:")) == 0 ||
3972
                    g_ascii_strncasecmp(buf, "Delivered-To:",
3973
                                        strlen("Delivered-To:")) == 0 ||
3974
                    g_ascii_strncasecmp(buf, "Received:",
3975
                                        strlen("Received:")) == 0 ||
3976
                    g_ascii_strncasecmp(buf, "Subject:",
3977
                                        strlen("Subject:")) == 0 ||
3978
                    g_ascii_strncasecmp(buf, "X-UIDL:",
3979
                                        strlen("X-UIDL:")) == 0)
3980
                        continue;
3981

    
3982
                if (fputs(buf, fdest) == EOF)
3983
                        goto error;
3984

    
3985
#if 0
3986
                if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) {
3987
                        fputs("\n (by way of ", fdest);
3988
                        if (compose->account->name) {
3989
                                compose_convert_header(compose,
3990
                                                       buf, sizeof(buf),
3991
                                                       compose->account->name,
3992
                                                       strlen(" (by way of "),
3993
                                                       FALSE, NULL);
3994
                                fprintf(fdest, "%s <%s>", buf,
3995
                                        compose->account->address);
3996
                        } else
3997
                                fputs(compose->account->address, fdest);
3998
                        fputs(")", fdest);
3999
                }
4000
#endif
4001

    
4002
                if (fputs("\n", fdest) == EOF)
4003
                        goto error;
4004
        }
4005

    
4006
        compose_redirect_write_headers(compose, fdest);
4007

    
4008
        while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4009
                if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
4010
                        FILE_OP_ERROR(file, "fwrite");
4011
                        goto error;
4012
                }
4013
        }
4014

    
4015
        fclose(fp);
4016
        if (fclose(fdest) == EOF) {
4017
                FILE_OP_ERROR(file, "fclose");
4018
                g_unlink(file);
4019
                return -1;
4020
        }
4021

    
4022
        return 0;
4023
error:
4024
        fclose(fp);
4025
        fclose(fdest);
4026
        g_unlink(file);
4027

    
4028
        return -1;
4029
}
4030

    
4031
static gint compose_remove_reedit_target(Compose *compose)
4032
{
4033
        FolderItem *item;
4034
        MsgInfo *msginfo = compose->targetinfo;
4035

    
4036
        g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4037
        if (!msginfo) return -1;
4038

    
4039
        item = msginfo->folder;
4040
        g_return_val_if_fail(item != NULL, -1);
4041

    
4042
        folder_item_scan(item);
4043
        if (procmsg_msg_exist(msginfo) &&
4044
            (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
4045
                if (folder_item_remove_msg(item, msginfo) < 0) {
4046
                        g_warning(_("can't remove the old message\n"));
4047
                        return -1;
4048
                }
4049
        }
4050

    
4051
        return 0;
4052
}
4053

    
4054
static gint compose_queue(Compose *compose, const gchar *file)
4055
{
4056
        FolderItem *queue;
4057
        gchar *tmp;
4058
        FILE *fp, *src_fp;
4059
        GSList *cur;
4060
        gchar buf[BUFFSIZE];
4061
        gint num;
4062
        MsgFlags flag = {0, 0};
4063

    
4064
        debug_print(_("queueing message...\n"));
4065
        g_return_val_if_fail(compose->to_list != NULL ||
4066
                             compose->newsgroup_list != NULL,
4067
                             -1);
4068
        g_return_val_if_fail(compose->account != NULL, -1);
4069

    
4070
        tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4071
                              G_DIR_SEPARATOR, compose);
4072
        if ((fp = g_fopen(tmp, "wb")) == NULL) {
4073
                FILE_OP_ERROR(tmp, "fopen");
4074
                g_free(tmp);
4075
                return -1;
4076
        }
4077
        if ((src_fp = g_fopen(file, "rb")) == NULL) {
4078
                FILE_OP_ERROR(file, "fopen");
4079
                fclose(fp);
4080
                g_unlink(tmp);
4081
                g_free(tmp);
4082
                return -1;
4083
        }
4084
        if (change_file_mode_rw(fp, tmp) < 0) {
4085
                FILE_OP_ERROR(tmp, "chmod");
4086
                g_warning(_("can't change file mode\n"));
4087
        }
4088

    
4089
        /* queueing variables */
4090
        fprintf(fp, "AF:\n");
4091
        fprintf(fp, "NF:0\n");
4092
        fprintf(fp, "PS:10\n");
4093
        fprintf(fp, "SRH:1\n");
4094
        fprintf(fp, "SFN:\n");
4095
        fprintf(fp, "DSR:\n");
4096
        if (compose->msgid)
4097
                fprintf(fp, "MID:<%s>\n", compose->msgid);
4098
        else
4099
                fprintf(fp, "MID:\n");
4100
        fprintf(fp, "CFG:\n");
4101
        fprintf(fp, "PT:0\n");
4102
        fprintf(fp, "S:%s\n", compose->account->address);
4103
        fprintf(fp, "RQ:\n");
4104
        if (compose->account->smtp_server)
4105
                fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
4106
        else
4107
                fprintf(fp, "SSV:\n");
4108
        if (compose->account->nntp_server)
4109
                fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
4110
        else
4111
                fprintf(fp, "NSV:\n");
4112
        fprintf(fp, "SSH:\n");
4113
        if (compose->to_list) {
4114
                fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4115
                for (cur = compose->to_list->next; cur != NULL;
4116
                     cur = cur->next)
4117
                        fprintf(fp, ",<%s>", (gchar *)cur->data);
4118
                fprintf(fp, "\n");
4119
        } else
4120
                fprintf(fp, "R:\n");
4121
        /* Sylpheed account ID */
4122
        fprintf(fp, "AID:%d\n", compose->account->account_id);
4123
        /* Reply target */
4124
        if (compose->reply_target)
4125
                fprintf(fp, "REP:%s\n", compose->reply_target);
4126
        /* Forward target */
4127
        if (compose->forward_targets)
4128
                fprintf(fp, "FWD:%s\n", compose->forward_targets);
4129
        fprintf(fp, "\n");
4130

    
4131
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4132
                if (fputs(buf, fp) == EOF) {
4133
                        FILE_OP_ERROR(tmp, "fputs");
4134
                        fclose(fp);
4135
                        fclose(src_fp);
4136
                        g_unlink(tmp);
4137
                        g_free(tmp);
4138
                        return -1;
4139
                }
4140
        }
4141

    
4142
        fclose(src_fp);
4143
        if (fclose(fp) == EOF) {
4144
                FILE_OP_ERROR(tmp, "fclose");
4145
                g_unlink(tmp);
4146
                g_free(tmp);
4147
                return -1;
4148
        }
4149

    
4150
        queue = account_get_special_folder(compose->account, F_QUEUE);
4151
        if (!queue) {
4152
                g_warning(_("can't find queue folder\n"));
4153
                g_unlink(tmp);
4154
                g_free(tmp);
4155
                return -1;
4156
        }
4157
        folder_item_scan(queue);
4158
        if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
4159
                g_warning(_("can't queue the message\n"));
4160
                g_unlink(tmp);
4161
                g_free(tmp);
4162
                return -1;
4163
        }
4164
        g_free(tmp);
4165

    
4166
        if (compose->mode == COMPOSE_REEDIT) {
4167
                compose_remove_reedit_target(compose);
4168
                if (compose->targetinfo &&
4169
                    compose->targetinfo->folder != queue)
4170
                        folderview_update_item
4171
                                (compose->targetinfo->folder, TRUE);
4172
        }
4173

    
4174
        folder_item_scan(queue);
4175
        folderview_update_item(queue, TRUE);
4176

    
4177
        main_window_set_menu_sensitive(main_window_get());
4178
        main_window_set_toolbar_sensitive(main_window_get());
4179

    
4180
        return 0;
4181
}
4182

    
4183
static gint compose_write_attach(Compose *compose, FILE *fp,
4184
                                 const gchar *charset)
4185
{
4186
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
4187
        GtkTreeIter iter;
4188
        gboolean valid;
4189
        AttachInfo *ainfo;
4190
        FILE *attach_fp;
4191
        gint len;
4192
        EncodingType encoding;
4193

    
4194
        for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
4195
             valid = gtk_tree_model_iter_next(model, &iter)) {
4196
                gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
4197

    
4198
                if (!is_file_exist(ainfo->file)) {
4199
                        alertpanel_error(_("File %s doesn't exist."),
4200
                                         ainfo->file);
4201
                        return -1;
4202
                }
4203
                if (get_file_size(ainfo->file) <= 0) {
4204
                        alertpanel_error(_("File %s is empty."), ainfo->file);
4205
                        return -1;
4206
                }
4207
                if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) {
4208
                        alertpanel_error(_("Can't open file %s."), ainfo->file);
4209
                        return -1;
4210
                }
4211

    
4212
                fprintf(fp, "\n--%s\n", compose->boundary);
4213

    
4214
                encoding = ainfo->encoding;
4215

    
4216
                if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) {
4217
                        fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
4218
                        fprintf(fp, "Content-Disposition: inline\n");
4219

    
4220
                        /* message/... shouldn't be encoded */
4221
                        if (encoding == ENC_QUOTED_PRINTABLE ||
4222
                            encoding == ENC_BASE64)
4223
                                encoding = ENC_8BIT;
4224
                } else {
4225
                        if (prefs_common.mime_fencoding_method ==
4226
                            FENC_RFC2231) {
4227
                                gchar *param;
4228

    
4229
                                param = compose_convert_filename
4230
                                        (compose, ainfo->name, "name", charset);
4231
                                fprintf(fp, "Content-Type: %s;\n"
4232
                                            "%s\n",
4233
                                        ainfo->content_type, param);
4234
                                g_free(param);
4235
                                param = compose_convert_filename
4236
                                        (compose, ainfo->name, "filename",
4237
                                         charset);
4238
                                fprintf(fp, "Content-Disposition: attachment;\n"
4239
                                            "%s\n", param);
4240
                                g_free(param);
4241
                        } else {
4242
                                gchar filename[BUFFSIZE];
4243

    
4244
                                compose_convert_header(compose, filename,
4245
                                                       sizeof(filename),
4246
                                                       ainfo->name, 12, FALSE,
4247
                                                       charset);
4248
                                fprintf(fp, "Content-Type: %s;\n"
4249
                                            " name=\"%s\"\n",
4250
                                        ainfo->content_type, filename);
4251
                                fprintf(fp, "Content-Disposition: attachment;\n"
4252
                                            " filename=\"%s\"\n", filename);
4253
                        }
4254

    
4255
#if USE_GPGME
4256
                        /* force encoding to protect trailing spaces */
4257
                        if (rfc2015_is_available() && compose->use_signing &&
4258
                            !compose->account->clearsign) {
4259
                                if (encoding == ENC_7BIT)
4260
                                        encoding = ENC_QUOTED_PRINTABLE;
4261
                                else if (encoding == ENC_8BIT)
4262
                                        encoding = ENC_BASE64;
4263
                        }
4264
#endif
4265
                }
4266

    
4267
                fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
4268
                        procmime_get_encoding_str(encoding));
4269

    
4270
                if (encoding == ENC_BASE64) {
4271
                        gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
4272
                        FILE *tmp_fp = attach_fp;
4273
                        gchar *tmp_file = NULL;
4274
                        ContentType content_type;
4275

    
4276
                        content_type =
4277
                                procmime_scan_mime_type(ainfo->content_type);
4278
                        if (content_type == MIME_TEXT ||
4279
                            content_type == MIME_TEXT_HTML ||
4280
                            content_type == MIME_MESSAGE_RFC822) {
4281
                                tmp_file = get_tmp_file();
4282
                                if (canonicalize_file(ainfo->file, tmp_file) < 0) {
4283
                                        g_free(tmp_file);
4284
                                        fclose(attach_fp);
4285
                                        return -1;
4286
                                }
4287
                                if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
4288
                                        FILE_OP_ERROR(tmp_file, "fopen");
4289
                                        g_unlink(tmp_file);
4290
                                        g_free(tmp_file);
4291
                                        fclose(attach_fp);
4292
                                        return -1;
4293
                                }
4294
                        }
4295

    
4296
                        while ((len = fread(inbuf, sizeof(gchar),
4297
                                            B64_LINE_SIZE, tmp_fp))
4298
                               == B64_LINE_SIZE) {
4299
                                base64_encode(outbuf, (guchar *)inbuf,
4300
                                              B64_LINE_SIZE);
4301
                                fputs(outbuf, fp);
4302
                                fputc('\n', fp);
4303
                        }
4304
                        if (len > 0 && feof(tmp_fp)) {
4305
                                base64_encode(outbuf, (guchar *)inbuf, len);
4306
                                fputs(outbuf, fp);
4307
                                fputc('\n', fp);
4308
                        }
4309

    
4310
                        if (tmp_file) {
4311
                                fclose(tmp_fp);
4312
                                g_unlink(tmp_file);
4313
                                g_free(tmp_file);
4314
                        }
4315
                } else if (encoding == ENC_QUOTED_PRINTABLE) {
4316
                        gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
4317

    
4318
                        while (fgets(inbuf, sizeof(inbuf), attach_fp) != NULL) {
4319
                                qp_encode_line(outbuf, (guchar *)inbuf);
4320
                                fputs(outbuf, fp);
4321
                        }
4322
                } else {
4323
                        gchar buf[BUFFSIZE];
4324

    
4325
                        while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
4326
                                strcrchomp(buf);
4327
                                fputs(buf, fp);
4328
                        }
4329
                }
4330

    
4331
                fclose(attach_fp);
4332
        }
4333

    
4334
        fprintf(fp, "\n--%s--\n", compose->boundary);
4335
        return 0;
4336
}
4337

    
4338
#define QUOTE_IF_REQUIRED(out, str)                        \
4339
{                                                        \
4340
        if (*str != '"' && strpbrk(str, ",.[]<>")) {        \
4341
                gchar *__tmp;                                \
4342
                gint len;                                \
4343
                                                        \
4344
                len = strlen(str) + 3;                        \
4345
                Xalloca(__tmp, len, return -1);                \
4346
                g_snprintf(__tmp, len, "\"%s\"", str);        \
4347
                out = __tmp;                                \
4348
        } else {                                        \
4349
                Xstrdup_a(out, str, return -1);                \
4350
        }                                                \
4351
}
4352

    
4353
#define PUT_RECIPIENT_HEADER(header, str)                                     \
4354
{                                                                             \
4355
        if (*str != '\0') {                                                     \
4356
                gchar *dest;                                                     \
4357
                                                                             \
4358
                Xstrdup_a(dest, str, return -1);                             \
4359
                g_strstrip(dest);                                             \
4360
                if (*dest != '\0') {                                             \
4361
                        compose->to_list = address_list_append                     \
4362
                                (compose->to_list, dest);                     \
4363
                        compose_convert_header                                     \
4364
                                (compose, buf, sizeof(buf), dest,             \
4365
                                 strlen(header) + 2, TRUE, charset);             \
4366
                        fprintf(fp, "%s: %s\n", header, buf);                     \
4367
                }                                                             \
4368
        }                                                                     \
4369
}
4370

    
4371
#define IS_IN_CUSTOM_HEADER(header) \
4372
        (compose->account->add_customhdr && \
4373
         custom_header_find(compose->account->customhdr_list, header) != NULL)
4374

    
4375
static gint compose_write_headers(Compose *compose, FILE *fp,
4376
                                  const gchar *charset,
4377
                                  const gchar *body_charset,
4378
                                  EncodingType encoding, gboolean is_draft)
4379
{
4380
        gchar buf[BUFFSIZE];
4381
        const gchar *entry_str;
4382
        gchar *str;
4383
        gchar *name;
4384

    
4385
        g_return_val_if_fail(fp != NULL, -1);
4386
        g_return_val_if_fail(charset != NULL, -1);
4387
        g_return_val_if_fail(compose->account != NULL, -1);
4388
        g_return_val_if_fail(compose->account->address != NULL, -1);
4389

    
4390
        /* Date */
4391
        if (compose->account->add_date) {
4392
                get_rfc822_date(buf, sizeof(buf));
4393
                fprintf(fp, "Date: %s\n", buf);
4394
        }
4395

    
4396
        /* From */
4397
        if (compose->account->name && *compose->account->name) {
4398
                compose_convert_header
4399
                        (compose, buf, sizeof(buf), compose->account->name,
4400
                         strlen("From: "), TRUE, charset);
4401
                QUOTE_IF_REQUIRED(name, buf);
4402
                fprintf(fp, "From: %s <%s>\n",
4403
                        name, compose->account->address);
4404
        } else
4405
                fprintf(fp, "From: %s\n", compose->account->address);
4406

    
4407
        slist_free_strings(compose->to_list);
4408
        g_slist_free(compose->to_list);
4409
        compose->to_list = NULL;
4410

    
4411
        /* To */
4412
        if (compose->use_to) {
4413
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4414
                PUT_RECIPIENT_HEADER("To", entry_str);
4415
        }
4416

    
4417
        slist_free_strings(compose->newsgroup_list);
4418
        g_slist_free(compose->newsgroup_list);
4419
        compose->newsgroup_list = NULL;
4420

    
4421
        /* Newsgroups */
4422
        if (compose->use_newsgroups) {
4423
                entry_str = gtk_entry_get_text
4424
                        (GTK_ENTRY(compose->newsgroups_entry));
4425
                if (*entry_str != '\0') {
4426
                        Xstrdup_a(str, entry_str, return -1);
4427
                        g_strstrip(str);
4428
                        remove_space(str);
4429
                        if (*str != '\0') {
4430
                                compose->newsgroup_list =
4431
                                        newsgroup_list_append
4432
                                                (compose->newsgroup_list, str);
4433
                                compose_convert_header(compose,
4434
                                                       buf, sizeof(buf), str,
4435
                                                       strlen("Newsgroups: "),
4436
                                                       FALSE, charset);
4437
                                fprintf(fp, "Newsgroups: %s\n", buf);
4438
                        }
4439
                }
4440
        }
4441

    
4442
        /* Cc */
4443
        if (compose->use_cc) {
4444
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4445
                PUT_RECIPIENT_HEADER("Cc", entry_str);
4446
        }
4447

    
4448
        /* Bcc */
4449
        if (compose->use_bcc) {
4450
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4451
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4452
        }
4453

    
4454
        if (!is_draft && !compose->to_list && !compose->newsgroup_list)
4455
                return -1;
4456

    
4457
        /* Subject */
4458
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4459
        if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4460
                Xstrdup_a(str, entry_str, return -1);
4461
                g_strstrip(str);
4462
                if (*str != '\0') {
4463
                        compose_convert_header(compose, buf, sizeof(buf), str,
4464
                                               strlen("Subject: "), FALSE,
4465
                                               charset);
4466
                        fprintf(fp, "Subject: %s\n", buf);
4467
                }
4468
        }
4469

    
4470
        /* Message-ID */
4471
        if (compose->account->gen_msgid) {
4472
                compose_generate_msgid(compose, buf, sizeof(buf));
4473
                fprintf(fp, "Message-Id: <%s>\n", buf);
4474
                compose->msgid = g_strdup(buf);
4475
        }
4476

    
4477
        /* In-Reply-To */
4478
        if (compose->inreplyto && compose->to_list)
4479
                fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
4480

    
4481
        /* References */
4482
        if (compose->references)
4483
                fprintf(fp, "References: %s\n", compose->references);
4484

    
4485
        /* Followup-To */
4486
        if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
4487
                entry_str = gtk_entry_get_text
4488
                        (GTK_ENTRY(compose->followup_entry));
4489
                if (*entry_str != '\0') {
4490
                        Xstrdup_a(str, entry_str, return -1);
4491
                        g_strstrip(str);
4492
                        remove_space(str);
4493
                        if (*str != '\0') {
4494
                                compose_convert_header(compose,
4495
                                                       buf, sizeof(buf), str,
4496
                                                       strlen("Followup-To: "),
4497
                                                       FALSE, charset);
4498
                                fprintf(fp, "Followup-To: %s\n", buf);
4499
                        }
4500
                }
4501
        }
4502

    
4503
        /* Reply-To */
4504
        if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
4505
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4506
                if (*entry_str != '\0') {
4507
                        Xstrdup_a(str, entry_str, return -1);
4508
                        g_strstrip(str);
4509
                        if (*str != '\0') {
4510
                                compose_convert_header(compose,
4511
                                                       buf, sizeof(buf), str,
4512
                                                       strlen("Reply-To: "),
4513
                                                       TRUE, charset);
4514
                                fprintf(fp, "Reply-To: %s\n", buf);
4515
                        }
4516
                }
4517
        }
4518

    
4519
        /* Organization */
4520
        if (compose->account->organization &&
4521
            !IS_IN_CUSTOM_HEADER("Organization")) {
4522
                compose_convert_header(compose, buf, sizeof(buf),
4523
                                       compose->account->organization,
4524
                                       strlen("Organization: "), FALSE,
4525
                                       charset);
4526
                fprintf(fp, "Organization: %s\n", buf);
4527
        }
4528

    
4529
        /* Program version and system info */
4530
        if (compose->to_list && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
4531
                fprintf(fp, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
4532
                        prog_version,
4533
                        gtk_major_version, gtk_minor_version, gtk_micro_version,
4534
                        TARGET_ALIAS);
4535
        }
4536
        if (compose->newsgroup_list && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
4537
                fprintf(fp, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
4538
                        prog_version,
4539
                        gtk_major_version, gtk_minor_version, gtk_micro_version,
4540
                        TARGET_ALIAS);
4541
        }
4542

    
4543
        /* custom headers */
4544
        if (compose->account->add_customhdr) {
4545
                GSList *cur;
4546

    
4547
                for (cur = compose->account->customhdr_list; cur != NULL;
4548
                     cur = cur->next) {
4549
                        CustomHeader *chdr = (CustomHeader *)cur->data;
4550

    
4551
                        if (g_ascii_strcasecmp(chdr->name, "Date") != 0 &&
4552
                            g_ascii_strcasecmp(chdr->name, "From") != 0 &&
4553
                            g_ascii_strcasecmp(chdr->name, "To") != 0 &&
4554
                         /* g_ascii_strcasecmp(chdr->name, "Sender") != 0 && */
4555
                            g_ascii_strcasecmp(chdr->name, "Message-Id") != 0 &&
4556
                            g_ascii_strcasecmp(chdr->name, "In-Reply-To") != 0 &&
4557
                            g_ascii_strcasecmp(chdr->name, "References") != 0 &&
4558
                            g_ascii_strcasecmp(chdr->name, "Mime-Version") != 0 &&
4559
                            g_ascii_strcasecmp(chdr->name, "Content-Type") != 0 &&
4560
                            g_ascii_strcasecmp(chdr->name, "Content-Transfer-Encoding") != 0) {
4561
                                compose_convert_header
4562
                                        (compose, buf, sizeof(buf),
4563
                                         chdr->value ? chdr->value : "",
4564
                                         strlen(chdr->name) + 2, FALSE,
4565
                                         charset);
4566
                                fprintf(fp, "%s: %s\n", chdr->name, buf);
4567
                        }
4568
                }
4569
        }
4570

    
4571
        /* MIME */
4572
        fprintf(fp, "Mime-Version: 1.0\n");
4573
        if (compose->use_attach &&
4574
            gtk_tree_model_iter_n_children
4575
                (GTK_TREE_MODEL(compose->attach_store), NULL) > 0) {
4576
                compose->boundary = generate_mime_boundary(NULL);
4577
                fprintf(fp,
4578
                        "Content-Type: multipart/mixed;\n"
4579
                        " boundary=\"%s\"\n", compose->boundary);
4580
        } else {
4581
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
4582
                        body_charset);
4583
#if USE_GPGME
4584
                if (rfc2015_is_available() &&
4585
                    compose->use_signing && !compose->account->clearsign)
4586
                        fprintf(fp, "Content-Disposition: inline\n");
4587
#endif
4588
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
4589
                        procmime_get_encoding_str(encoding));
4590
        }
4591

    
4592
        /* X-Sylpheed headers */
4593
        if (is_draft) {
4594
                fprintf(fp, "X-Sylpheed-Account-Id: %d\n",
4595
                        compose->account->account_id);
4596
                if (compose->reply_target)
4597
                        fprintf(fp, "X-Sylpheed-Reply: %s\n",
4598
                                compose->reply_target);
4599
                else if (compose->forward_targets)
4600
                        fprintf(fp, "X-Sylpheed-Forward: %s\n",
4601
                                compose->forward_targets);
4602
        }
4603

    
4604
        /* separator between header and body */
4605
        fputs("\n", fp);
4606

    
4607
        return 0;
4608
}
4609

    
4610
static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4611
{
4612
        gchar buf[BUFFSIZE];
4613
        const gchar *entry_str;
4614
        gchar *str;
4615
        const gchar *charset = NULL;
4616

    
4617
        g_return_val_if_fail(fp != NULL, -1);
4618
        g_return_val_if_fail(compose->account != NULL, -1);
4619
        g_return_val_if_fail(compose->account->address != NULL, -1);
4620

    
4621
        /* Resent-Date */
4622
        get_rfc822_date(buf, sizeof(buf));
4623
        fprintf(fp, "Resent-Date: %s\n", buf);
4624

    
4625
        /* Resent-From */
4626
        if (compose->account->name) {
4627
                compose_convert_header
4628
                        (compose, buf, sizeof(buf), compose->account->name,
4629
                         strlen("Resent-From: "), TRUE, NULL);
4630
                fprintf(fp, "Resent-From: %s <%s>\n",
4631
                        buf, compose->account->address);
4632
        } else
4633
                fprintf(fp, "Resent-From: %s\n", compose->account->address);
4634

    
4635
        slist_free_strings(compose->to_list);
4636
        g_slist_free(compose->to_list);
4637
        compose->to_list = NULL;
4638

    
4639
        /* Resent-To */
4640
        if (compose->use_to) {
4641
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4642
                PUT_RECIPIENT_HEADER("Resent-To", entry_str);
4643
        }
4644
        if (compose->use_cc) {
4645
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4646
                PUT_RECIPIENT_HEADER("Resent-Cc", entry_str);
4647
        }
4648
        if (compose->use_bcc) {
4649
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4650
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4651
        }
4652

    
4653
        slist_free_strings(compose->newsgroup_list);
4654
        g_slist_free(compose->newsgroup_list);
4655
        compose->newsgroup_list = NULL;
4656

    
4657
        /* Newsgroups */
4658
        if (compose->use_newsgroups) {
4659
                entry_str = gtk_entry_get_text
4660
                        (GTK_ENTRY(compose->newsgroups_entry));
4661
                if (*entry_str != '\0') {
4662
                        Xstrdup_a(str, entry_str, return -1);
4663
                        g_strstrip(str);
4664
                        remove_space(str);
4665
                        if (*str != '\0') {
4666
                                compose->newsgroup_list =
4667
                                        newsgroup_list_append
4668
                                                (compose->newsgroup_list, str);
4669
                                compose_convert_header(compose,
4670
                                                       buf, sizeof(buf), str,
4671
                                                       strlen("Newsgroups: "),
4672
                                                       FALSE, NULL);
4673
                                fprintf(fp, "Newsgroups: %s\n", buf);
4674
                        }
4675
                }
4676
        }
4677

    
4678
        if (!compose->to_list && !compose->newsgroup_list)
4679
                return -1;
4680

    
4681
        /* Subject */
4682
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4683
        if (*entry_str != '\0') {
4684
                Xstrdup_a(str, entry_str, return -1);
4685
                g_strstrip(str);
4686
                if (*str != '\0') {
4687
                        compose_convert_header(compose, buf, sizeof(buf), str,
4688
                                               strlen("Subject: "), FALSE,
4689
                                               NULL);
4690
                        fprintf(fp, "Subject: %s\n", buf);
4691
                }
4692
        }
4693

    
4694
        /* Resent-Message-Id */
4695
        if (compose->account->gen_msgid) {
4696
                compose_generate_msgid(compose, buf, sizeof(buf));
4697
                fprintf(fp, "Resent-Message-Id: <%s>\n", buf);
4698
                compose->msgid = g_strdup(buf);
4699
        }
4700

    
4701
        /* Followup-To */
4702
        if (compose->use_followupto) {
4703
                entry_str = gtk_entry_get_text
4704
                        (GTK_ENTRY(compose->followup_entry));
4705
                if (*entry_str != '\0') {
4706
                        Xstrdup_a(str, entry_str, return -1);
4707
                        g_strstrip(str);
4708
                        remove_space(str);
4709
                        if (*str != '\0') {
4710
                                compose_convert_header(compose,
4711
                                                       buf, sizeof(buf), str,
4712
                                                       strlen("Followup-To: "),
4713
                                                       FALSE, NULL);
4714
                                fprintf(fp, "Followup-To: %s\n", buf);
4715
                        }
4716
                }
4717
        }
4718

    
4719
        /* Resent-Reply-To */
4720
        if (compose->use_replyto) {
4721
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4722
                if (*entry_str != '\0') {
4723
                        Xstrdup_a(str, entry_str, return -1);
4724
                        g_strstrip(str);
4725
                        if (*str != '\0') {
4726
                                compose_convert_header
4727
                                        (compose, buf, sizeof(buf), str,
4728
                                         strlen("Resent-Reply-To: "), TRUE,
4729
                                         NULL);
4730
                                fprintf(fp,