Statistics
| Revision:

root / src / compose.c @ 1

History | View | Annotate | Download (163 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 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
#include <glib.h>
27
#include <gdk/gdkkeysyms.h>
28
#include <gtk/gtkmain.h>
29
#include <gtk/gtkmenu.h>
30
#include <gtk/gtkmenuitem.h>
31
#include <gtk/gtkitemfactory.h>
32
#include <gtk/gtkcheckmenuitem.h>
33
#include <gtk/gtkoptionmenu.h>
34
#include <gtk/gtkwidget.h>
35
#include <gtk/gtkclist.h>
36
#include <gtk/gtkctree.h>
37
#include <gtk/gtkvpaned.h>
38
#include <gtk/gtkentry.h>
39
#include <gtk/gtkeditable.h>
40
#include <gtk/gtkwindow.h>
41
#include <gtk/gtksignal.h>
42
#include <gtk/gtkvbox.h>
43
#include <gtk/gtkcontainer.h>
44
#include <gtk/gtkhandlebox.h>
45
#include <gtk/gtktoolbar.h>
46
#include <gtk/gtktable.h>
47
#include <gtk/gtkhbox.h>
48
#include <gtk/gtklabel.h>
49
#include <gtk/gtkscrolledwindow.h>
50
#include <gtk/gtktreeview.h>
51
#include <gtk/gtkdnd.h>
52
#include <stdio.h>
53
#include <stdlib.h>
54
#include <string.h>
55
#include <ctype.h>
56
#include <sys/types.h>
57
#include <sys/stat.h>
58
#include <unistd.h>
59
#include <time.h>
60
/* #include <sys/utsname.h> */
61
#include <stdlib.h>
62
#include <sys/wait.h>
63
#include <signal.h>
64
#include <errno.h>
65

    
66
#if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
67
#  include <wchar.h>
68
#  include <wctype.h>
69
#endif
70

    
71
#include "intl.h"
72
#include "main.h"
73
#include "mainwindow.h"
74
#include "compose.h"
75
#include "addressbook.h"
76
#include "folderview.h"
77
#include "procmsg.h"
78
#include "menu.h"
79
#include "stock_pixmap.h"
80
#include "send_message.h"
81
#include "imap.h"
82
#include "news.h"
83
#include "customheader.h"
84
#include "prefs_common.h"
85
#include "prefs_account.h"
86
#include "action.h"
87
#include "account.h"
88
#include "filesel.h"
89
#include "procheader.h"
90
#include "procmime.h"
91
#include "statusbar.h"
92
#include "about.h"
93
#include "base64.h"
94
#include "quoted-printable.h"
95
#include "codeconv.h"
96
#include "utils.h"
97
#include "gtkutils.h"
98
#include "socket.h"
99
#include "alertpanel.h"
100
#include "manage_window.h"
101
#include "gtkshruler.h"
102
#include "folder.h"
103
#include "addr_compl.h"
104
#include "quote_fmt.h"
105
#include "template.h"
106
#include "undo.h"
107

    
108
#if USE_GPGME
109
#  include "rfc2015.h"
110
#endif
111

    
112
typedef enum
113
{
114
        COL_MIMETYPE = 0,
115
        COL_SIZE     = 1,
116
        COL_NAME     = 2
117
} AttachColumnPos;
118

    
119
#define N_ATTACH_COLS                3
120

    
121
typedef enum
122
{
123
        COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE,
124
        COMPOSE_ACTION_MOVE_FORWARD_CHARACTER,
125
        COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER,
126
        COMPOSE_ACTION_MOVE_FORWARD_WORD,
127
        COMPOSE_ACTION_MOVE_BACKWARD_WORD,
128
        COMPOSE_ACTION_MOVE_END_OF_LINE,
129
        COMPOSE_ACTION_MOVE_NEXT_LINE,
130
        COMPOSE_ACTION_MOVE_PREVIOUS_LINE,
131
        COMPOSE_ACTION_DELETE_FORWARD_CHARACTER,
132
        COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER,
133
        COMPOSE_ACTION_DELETE_FORWARD_WORD,
134
        COMPOSE_ACTION_DELETE_BACKWARD_WORD,
135
        COMPOSE_ACTION_DELETE_LINE,
136
        COMPOSE_ACTION_DELETE_LINE_N,
137
        COMPOSE_ACTION_DELETE_TO_LINE_END
138
} ComposeAction;
139

    
140
#define B64_LINE_SIZE                57
141
#define B64_BUFFSIZE                77
142

    
143
#define MAX_REFERENCES_LEN        999
144

    
145
static GdkColor quote_color = {0, 0, 0, 0xbfff};
146

    
147
static GList *compose_list = NULL;
148

    
149
static Compose *compose_create                        (PrefsAccount        *account,
150
                                                 ComposeMode         mode);
151
static void compose_connect_changed_callbacks        (Compose        *compose);
152
static void compose_toolbar_create                (Compose        *compose,
153
                                                 GtkWidget        *container);
154
static GtkWidget *compose_account_option_menu_create
155
                                                (Compose        *compose);
156
static void compose_set_template_menu                (Compose        *compose);
157
static void compose_template_apply                (Compose        *compose,
158
                                                 Template        *tmpl,
159
                                                 gboolean         replace);
160
static void compose_destroy                        (Compose        *compose);
161

    
162
static void compose_entry_show                        (Compose        *compose,
163
                                                 ComposeEntryType type);
164
static GtkEntry *compose_get_entry                (Compose        *compose,
165
                                                 ComposeEntryType type);
166
static void compose_entries_set                        (Compose        *compose,
167
                                                 const gchar        *mailto);
168
static void compose_entries_set_from_item        (Compose        *compose,
169
                                                 FolderItem        *item,
170
                                                 ComposeMode         mode);
171
static gint compose_parse_header                (Compose        *compose,
172
                                                 MsgInfo        *msginfo);
173
static gchar *compose_parse_references                (const gchar        *ref,
174
                                                 const gchar        *msgid);
175

    
176
static gchar *compose_quote_fmt                        (Compose        *compose,
177
                                                 MsgInfo        *msginfo,
178
                                                 const gchar        *fmt,
179
                                                 const gchar        *qmark,
180
                                                 const gchar        *body);
181

    
182
static void compose_reply_set_entry                (Compose        *compose,
183
                                                 MsgInfo        *msginfo,
184
                                                 ComposeMode         mode);
185
static void compose_reedit_set_entry                (Compose        *compose,
186
                                                 MsgInfo        *msginfo);
187
static void compose_insert_sig                        (Compose        *compose,
188
                                                 gboolean         replace);
189
static gchar *compose_get_signature_str                (Compose        *compose);
190
static void compose_insert_file                        (Compose        *compose,
191
                                                 const gchar        *file);
192
static void compose_attach_append                (Compose        *compose,
193
                                                 const gchar        *file,
194
                                                 const gchar        *filename,
195
                                                 const gchar        *content_type);
196
static void compose_attach_parts                (Compose        *compose,
197
                                                 MsgInfo        *msginfo);
198
static void compose_wrap_line                        (Compose        *compose);
199
static void compose_wrap_line_all                (Compose        *compose);
200
static void compose_wrap_line_all_full                (Compose        *compose,
201
                                                 gboolean         autowrap);
202
static void compose_set_title                        (Compose        *compose);
203
static void compose_select_account                (Compose        *compose,
204
                                                 PrefsAccount        *account,
205
                                                 gboolean         init);
206

    
207
static gboolean compose_check_for_valid_recipient
208
                                                (Compose        *compose);
209
static gboolean compose_check_entries                (Compose        *compose);
210

    
211
static gint compose_send                        (Compose        *compose);
212
static gint compose_write_to_file                (Compose        *compose,
213
                                                 const gchar        *file,
214
                                                 gboolean         is_draft);
215
static gint compose_write_body_to_file                (Compose        *compose,
216
                                                 const gchar        *file);
217
static gint compose_redirect_write_to_file        (Compose        *compose,
218
                                                 const gchar        *file);
219
static gint compose_remove_reedit_target        (Compose        *compose);
220
static gint compose_queue                        (Compose        *compose,
221
                                                 const gchar        *file);
222
static void compose_write_attach                (Compose        *compose,
223
                                                 FILE                *fp);
224
static gint compose_write_headers                (Compose        *compose,
225
                                                 FILE                *fp,
226
                                                 const gchar        *charset,
227
                                                 EncodingType         encoding,
228
                                                 gboolean         is_draft);
229
static gint compose_redirect_write_headers        (Compose        *compose,
230
                                                 FILE                *fp);
231

    
232
static void compose_convert_header                (gchar                *dest,
233
                                                 gint                 len,
234
                                                 gchar                *src,
235
                                                 gint                 header_len,
236
                                                 gboolean         addr_field);
237
static void compose_generate_msgid                (Compose        *compose,
238
                                                 gchar                *buf,
239
                                                 gint                 len);
240

    
241
static void compose_attach_info_free                (AttachInfo        *ainfo);
242
static void compose_attach_remove_selected        (Compose        *compose);
243

    
244
static void compose_attach_property                (Compose        *compose);
245
static void compose_attach_property_create        (gboolean        *cancelled);
246
static void attach_property_ok                        (GtkWidget        *widget,
247
                                                 gboolean        *cancelled);
248
static void attach_property_cancel                (GtkWidget        *widget,
249
                                                 gboolean        *cancelled);
250
static gint attach_property_delete_event        (GtkWidget        *widget,
251
                                                 GdkEventAny        *event,
252
                                                 gboolean        *cancelled);
253
static gboolean attach_property_key_pressed        (GtkWidget        *widget,
254
                                                 GdkEventKey        *event,
255
                                                 gboolean        *cancelled);
256

    
257
static void compose_exec_ext_editor                (Compose           *compose);
258
static gint compose_exec_ext_editor_real        (const gchar           *file);
259
static gboolean compose_ext_editor_kill                (Compose           *compose);
260
static void compose_input_cb                        (gpointer            data,
261
                                                 gint                    source,
262
                                                 GdkInputCondition  condition);
263
static void compose_set_ext_editor_sensitive        (Compose           *compose,
264
                                                 gboolean            sensitive);
265

    
266
static void compose_undo_state_changed                (UndoMain        *undostruct,
267
                                                 gint                 undo_state,
268
                                                 gint                 redo_state,
269
                                                 gpointer         data);
270

    
271
static gint calc_cursor_xpos        (GtkTextView        *text,
272
                                 gint                 extra,
273
                                 gint                 char_width);
274

    
275
/* callback functions */
276

    
277
static gboolean compose_edit_size_alloc (GtkEditable        *widget,
278
                                         GtkAllocation        *allocation,
279
                                         GtkSHRuler        *shruler);
280

    
281
static void toolbar_send_cb                (GtkWidget        *widget,
282
                                         gpointer         data);
283
static void toolbar_send_later_cb        (GtkWidget        *widget,
284
                                         gpointer         data);
285
static void toolbar_draft_cb                (GtkWidget        *widget,
286
                                         gpointer         data);
287
static void toolbar_insert_cb                (GtkWidget        *widget,
288
                                         gpointer         data);
289
static void toolbar_attach_cb                (GtkWidget        *widget,
290
                                         gpointer         data);
291
static void toolbar_sig_cb                (GtkWidget        *widget,
292
                                         gpointer         data);
293
static void toolbar_ext_editor_cb        (GtkWidget        *widget,
294
                                         gpointer         data);
295
static void toolbar_linewrap_cb                (GtkWidget        *widget,
296
                                         gpointer         data);
297
static void toolbar_address_cb                (GtkWidget        *widget,
298
                                         gpointer         data);
299

    
300
static void account_activated                (GtkMenuItem        *menuitem,
301
                                         gpointer         data);
302

    
303
static void attach_selected                (GtkCList        *clist,
304
                                         gint                 row,
305
                                         gint                 column,
306
                                         GdkEvent        *event,
307
                                         gpointer         data);
308
static gboolean attach_button_pressed        (GtkWidget        *widget,
309
                                         GdkEventButton        *event,
310
                                         gpointer         data);
311
static gboolean attach_key_pressed        (GtkWidget        *widget,
312
                                         GdkEventKey        *event,
313
                                         gpointer         data);
314

    
315
static void compose_send_cb                (gpointer         data,
316
                                         guint                 action,
317
                                         GtkWidget        *widget);
318
static void compose_send_later_cb        (gpointer         data,
319
                                         guint                 action,
320
                                         GtkWidget        *widget);
321

    
322
static void compose_draft_cb                (gpointer         data,
323
                                         guint                 action,
324
                                         GtkWidget        *widget);
325

    
326
static void compose_attach_cb                (gpointer         data,
327
                                         guint                 action,
328
                                         GtkWidget        *widget);
329
static void compose_insert_file_cb        (gpointer         data,
330
                                         guint                 action,
331
                                         GtkWidget        *widget);
332
static void compose_insert_sig_cb        (gpointer         data,
333
                                         guint                 action,
334
                                         GtkWidget        *widget);
335

    
336
static void compose_close_cb                (gpointer         data,
337
                                         guint                 action,
338
                                         GtkWidget        *widget);
339

    
340
static void compose_address_cb                (gpointer         data,
341
                                         guint                 action,
342
                                         GtkWidget        *widget);
343
static void compose_template_activate_cb(GtkWidget        *widget,
344
                                         gpointer         data);
345

    
346
static void compose_ext_editor_cb        (gpointer         data,
347
                                         guint                 action,
348
                                         GtkWidget        *widget);
349

    
350
static gint compose_delete_cb                (GtkWidget        *widget,
351
                                         GdkEventAny        *event,
352
                                         gpointer         data);
353
static void compose_destroy_cb                (GtkWidget        *widget,
354
                                         Compose        *compose);
355

    
356
static void compose_undo_cb                (Compose        *compose);
357
static void compose_redo_cb                (Compose        *compose);
358
static void compose_cut_cb                (Compose        *compose);
359
static void compose_copy_cb                (Compose        *compose);
360
static void compose_paste_cb                (Compose        *compose);
361
static void compose_paste_as_quote_cb        (Compose        *compose);
362
static void compose_allsel_cb                (Compose        *compose);
363

    
364
static void compose_action_cb                (Compose        *compose,
365
                                         ComposeAction         action);
366

    
367
static void compose_grab_focus_cb        (GtkWidget        *widget,
368
                                         Compose        *compose);
369

    
370
static void compose_changed_cb                (GtkTextBuffer        *textbuf,
371
                                         Compose        *compose);
372

    
373
static void compose_toggle_autowrap_cb        (gpointer         data,
374
                                         guint                 action,
375
                                         GtkWidget        *widget);
376

    
377
static void compose_toggle_to_cb        (gpointer         data,
378
                                         guint                 action,
379
                                         GtkWidget        *widget);
380
static void compose_toggle_cc_cb        (gpointer         data,
381
                                         guint                 action,
382
                                         GtkWidget        *widget);
383
static void compose_toggle_bcc_cb        (gpointer         data,
384
                                         guint                 action,
385
                                         GtkWidget        *widget);
386
static void compose_toggle_replyto_cb        (gpointer         data,
387
                                         guint                 action,
388
                                         GtkWidget        *widget);
389
static void compose_toggle_followupto_cb(gpointer         data,
390
                                         guint                 action,
391
                                         GtkWidget        *widget);
392
static void compose_toggle_attach_cb        (gpointer         data,
393
                                         guint                 action,
394
                                         GtkWidget        *widget);
395
static void compose_toggle_ruler_cb        (gpointer         data,
396
                                         guint                 action,
397
                                         GtkWidget        *widget);
398
#if USE_GPGME
399
static void compose_toggle_sign_cb        (gpointer         data,
400
                                         guint                 action,
401
                                         GtkWidget        *widget);
402
static void compose_toggle_encrypt_cb        (gpointer         data,
403
                                         guint                 action,
404
                                         GtkWidget        *widget);
405
#endif
406

    
407
static void compose_attach_drag_received_cb (GtkWidget                *widget,
408
                                             GdkDragContext        *drag_context,
409
                                             gint                 x,
410
                                             gint                 y,
411
                                             GtkSelectionData        *data,
412
                                             guint                 info,
413
                                             guint                 time,
414
                                             gpointer                 user_data);
415
static void compose_insert_drag_received_cb (GtkWidget                *widget,
416
                                             GdkDragContext        *drag_context,
417
                                             gint                 x,
418
                                             gint                 y,
419
                                             GtkSelectionData        *data,
420
                                             guint                 info,
421
                                             guint                 time,
422
                                             gpointer                 user_data);
423

    
424
static void to_activated                (GtkWidget        *widget,
425
                                         Compose        *compose);
426
static void newsgroups_activated        (GtkWidget        *widget,
427
                                         Compose        *compose);
428
static void cc_activated                (GtkWidget        *widget,
429
                                         Compose        *compose);
430
static void bcc_activated                (GtkWidget        *widget,
431
                                         Compose        *compose);
432
static void replyto_activated                (GtkWidget        *widget,
433
                                         Compose        *compose);
434
static void followupto_activated        (GtkWidget        *widget,
435
                                         Compose        *compose);
436
static void subject_activated                (GtkWidget        *widget,
437
                                         Compose        *compose);
438

    
439
static void text_activated                (GtkWidget        *widget,
440
                                         Compose        *compose);
441
static void text_inserted                (GtkTextBuffer        *buffer,
442
                                         GtkTextIter        *iter,
443
                                         const gchar        *text,
444
                                         gint                 len,
445
                                         Compose        *compose);
446

    
447
static gboolean compose_send_control_enter        (Compose        *compose);
448

    
449
static GtkItemFactoryEntry compose_popup_entries[] =
450
{
451
        {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
452
        {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
453
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
454
        {N_("/_Properties..."),        NULL, compose_attach_property, 0, NULL}
455
};
456

    
457
static GtkItemFactoryEntry compose_entries[] =
458
{
459
        {N_("/_File"),                                NULL, NULL, 0, "<Branch>"},
460
        {N_("/_File/_Send"),                        "<control>Return",
461
                                                compose_send_cb, 0, NULL},
462
        {N_("/_File/Send _later"),                "<shift><control>S",
463
                                                compose_send_later_cb,  0, NULL},
464
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
465
        {N_("/_File/Save to _draft folder"),
466
                                                "<shift><control>D", compose_draft_cb, 0, NULL},
467
        {N_("/_File/Save and _keep editing"),
468
                                                "<control>S", compose_draft_cb, 1, NULL},
469
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
470
        {N_("/_File/_Attach file"),                "<control>M", compose_attach_cb,      0, NULL},
471
        {N_("/_File/_Insert file"),                "<control>I", compose_insert_file_cb, 0, NULL},
472
        {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig_cb,  0, NULL},
473
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
474
        {N_("/_File/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
475

    
476
        {N_("/_Edit"),                        NULL, NULL, 0, "<Branch>"},
477
        {N_("/_Edit/_Undo"),                "<control>Z", compose_undo_cb, 0, NULL},
478
        {N_("/_Edit/_Redo"),                "<control>Y", compose_redo_cb, 0, NULL},
479
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
480
        {N_("/_Edit/Cu_t"),                "<control>X", compose_cut_cb,    0, NULL},
481
        {N_("/_Edit/_Copy"),                "<control>C", compose_copy_cb,   0, NULL},
482
        {N_("/_Edit/_Paste"),                "<control>V", compose_paste_cb,  0, NULL},
483
        {N_("/_Edit/Paste as _quotation"),
484
                                        NULL, compose_paste_as_quote_cb, 0, NULL},
485
        {N_("/_Edit/Select _all"),        "<control>A", compose_allsel_cb, 0, NULL},
486
        {N_("/_Edit/A_dvanced"),        NULL, NULL, 0, "<Branch>"},
487
        {N_("/_Edit/A_dvanced/Move a character backward"),
488
                                        "<control>B",
489
                                        compose_action_cb,
490
                                        COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER,
491
                                        NULL},
492
        {N_("/_Edit/A_dvanced/Move a character forward"),
493
                                        "<control>F",
494
                                        compose_action_cb,
495
                                        COMPOSE_ACTION_MOVE_FORWARD_CHARACTER,
496
                                        NULL},
497
        {N_("/_Edit/A_dvanced/Move a word backward"),
498
                                        NULL, /* "<alt>B" */
499
                                        compose_action_cb,
500
                                        COMPOSE_ACTION_MOVE_BACKWARD_WORD,
501
                                        NULL},
502
        {N_("/_Edit/A_dvanced/Move a word forward"),
503
                                        NULL, /* "<alt>F" */
504
                                        compose_action_cb,
505
                                        COMPOSE_ACTION_MOVE_FORWARD_WORD,
506
                                        NULL},
507
        {N_("/_Edit/A_dvanced/Move to beginning of line"),
508
                                        NULL, /* "<control>A" */
509
                                        compose_action_cb,
510
                                        COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE,
511
                                        NULL},
512
        {N_("/_Edit/A_dvanced/Move to end of line"),
513
                                        "<control>E",
514
                                        compose_action_cb,
515
                                        COMPOSE_ACTION_MOVE_END_OF_LINE,
516
                                        NULL},
517
        {N_("/_Edit/A_dvanced/Move to previous line"),
518
                                        "<control>P",
519
                                        compose_action_cb,
520
                                        COMPOSE_ACTION_MOVE_PREVIOUS_LINE,
521
                                        NULL},
522
        {N_("/_Edit/A_dvanced/Move to next line"),
523
                                        "<control>N",
524
                                        compose_action_cb,
525
                                        COMPOSE_ACTION_MOVE_NEXT_LINE,
526
                                        NULL},
527
        {N_("/_Edit/A_dvanced/Delete a character backward"),
528
                                        "<control>H",
529
                                        compose_action_cb,
530
                                        COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER,
531
                                        NULL},
532
        {N_("/_Edit/A_dvanced/Delete a character forward"),
533
                                        "<control>D",
534
                                        compose_action_cb,
535
                                        COMPOSE_ACTION_DELETE_FORWARD_CHARACTER,
536
                                        NULL},
537
        {N_("/_Edit/A_dvanced/Delete a word backward"),
538
                                        NULL, /* "<control>W" */
539
                                        compose_action_cb,
540
                                        COMPOSE_ACTION_DELETE_BACKWARD_WORD,
541
                                        NULL},
542
        {N_("/_Edit/A_dvanced/Delete a word forward"),
543
                                        NULL, /* "<alt>D", */
544
                                        compose_action_cb,
545
                                        COMPOSE_ACTION_DELETE_FORWARD_WORD,
546
                                        NULL},
547
        {N_("/_Edit/A_dvanced/Delete line"),
548
                                        "<control>U",
549
                                        compose_action_cb,
550
                                        COMPOSE_ACTION_DELETE_LINE,
551
                                        NULL},
552
        {N_("/_Edit/A_dvanced/Delete to end of line"),
553
                                        "<control>K",
554
                                        compose_action_cb,
555
                                        COMPOSE_ACTION_DELETE_TO_LINE_END,
556
                                        NULL},
557
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
558
        {N_("/_Edit/_Wrap current paragraph"),
559
                                        "<control>L", compose_wrap_line, 0, NULL},
560
        {N_("/_Edit/Wrap all long _lines"),
561
                                        "<control><alt>L", compose_wrap_line_all, 0, NULL},
562
        {N_("/_Edit/Aut_o wrapping"),        "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
563
        {N_("/_View"),                        NULL, NULL, 0, "<Branch>"},
564
        {N_("/_View/_To"),                NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
565
        {N_("/_View/_Cc"),                NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
566
        {N_("/_View/_Bcc"),                NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
567
        {N_("/_View/_Reply to"),        NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
568
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
569
        {N_("/_View/_Followup to"),        NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
570
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
571
        {N_("/_View/R_uler"),                NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
572
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
573
        {N_("/_View/_Attachment"),        NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
574

    
575
        {N_("/_Tools"),                        NULL, NULL, 0, "<Branch>"},
576
        {N_("/_Tools/_Address book"),        "<shift><control>A", compose_address_cb , 0, NULL},
577
        {N_("/_Tools/_Template"),        NULL, NULL, 0, "<Branch>"},
578
        {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
579
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
580
        {N_("/_Tools/Edit with e_xternal editor"),
581
                                        "<shift><control>X", compose_ext_editor_cb, 0, NULL},
582
#if USE_GPGME
583
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
584
        {N_("/_Tools/PGP Si_gn"),           NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
585
        {N_("/_Tools/PGP _Encrypt"),        NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
586
#endif /* USE_GPGME */
587

    
588
        {N_("/_Help"),                        NULL, NULL, 0, "<Branch>"},
589
        {N_("/_Help/_About"),                NULL, about_show, 0, NULL}
590
};
591

    
592
static GtkTargetEntry compose_mime_types[] =
593
{
594
        {"text/uri-list", 0, 0}
595
};
596

    
597

    
598
void compose_new(PrefsAccount *account, FolderItem *item, const gchar *mailto,
599
                 GPtrArray *attach_files)
600
{
601
        Compose *compose;
602
        GtkTextView *text;
603
        GtkTextBuffer *buffer;
604
        GtkTextIter iter;
605

    
606
        if (!account) account = cur_account;
607
        g_return_if_fail(account != NULL);
608

    
609
        compose = compose_create(account, COMPOSE_NEW);
610

    
611
        undo_block(compose->undostruct);
612

    
613
        if (prefs_common.auto_sig)
614
                compose_insert_sig(compose, FALSE);
615

    
616
        text = GTK_TEXT_VIEW(compose->text);
617
        buffer = gtk_text_view_get_buffer(text);
618
        gtk_text_buffer_get_start_iter(buffer, &iter);
619
        gtk_text_buffer_place_cursor(buffer, &iter);
620

    
621
        if (account->protocol != A_NNTP) {
622
                if (mailto && *mailto != '\0') {
623
                        compose_entries_set(compose, mailto);
624
                        gtk_widget_grab_focus(compose->subject_entry);
625
                } else if (item) {
626
                        compose_entries_set_from_item
627
                                (compose, item, COMPOSE_NEW);
628
                        if (item->auto_to)
629
                                gtk_widget_grab_focus(compose->subject_entry);
630
                        else
631
                                gtk_widget_grab_focus(compose->to_entry);
632
                } else
633
                        gtk_widget_grab_focus(compose->to_entry);
634
        } else {
635
                if (mailto && *mailto != '\0') {
636
                        compose_entry_append(compose, mailto,
637
                                             COMPOSE_ENTRY_NEWSGROUPS);
638
                        gtk_widget_grab_focus(compose->subject_entry);
639
                } else
640
                        gtk_widget_grab_focus(compose->newsgroups_entry);
641
        }
642

    
643
        if (attach_files) {
644
                gint i;
645
                gchar *file;
646

    
647
                for (i = 0; i < attach_files->len; i++) {
648
                        file = g_ptr_array_index(attach_files, i);
649
                        compose_attach_append(compose, file, file, NULL);
650
                }
651
        }
652

    
653
        undo_unblock(compose->undostruct);
654

    
655
        compose_connect_changed_callbacks(compose);
656

    
657
        if (prefs_common.auto_exteditor)
658
                compose_exec_ext_editor(compose);
659
}
660

    
661
void compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode,
662
                   const gchar *body)
663
{
664
        Compose *compose;
665
        PrefsAccount *account;
666
        GtkTextBuffer *buffer;
667
        GtkTextIter iter;
668
        gboolean quote = FALSE;
669

    
670
        g_return_if_fail(msginfo != NULL);
671
        g_return_if_fail(msginfo->folder != NULL);
672

    
673
        if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
674
                quote = TRUE;
675

    
676
        account = account_find_from_item(msginfo->folder);
677
        if (!account && msginfo->to && prefs_common.reply_account_autosel) {
678
                gchar *to;
679
                Xstrdup_a(to, msginfo->to, return);
680
                extract_address(to);
681
                account = account_find_from_address(to);
682
        }
683
        if (!account) account = cur_account;
684
        g_return_if_fail(account != NULL);
685

    
686
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
687
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
688
        if (MSG_IS_IMAP(msginfo->flags))
689
                imap_msg_set_perm_flags(msginfo, MSG_REPLIED);
690

    
691
        compose = compose_create(account, COMPOSE_REPLY);
692

    
693
        compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
694
        if (!compose->replyinfo)
695
                compose->replyinfo = procmsg_msginfo_copy(msginfo);
696

    
697
        if (compose_parse_header(compose, msginfo) < 0) return;
698

    
699
        undo_block(compose->undostruct);
700

    
701
        compose_reply_set_entry(compose, msginfo, mode);
702
        if (item)
703
                compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
704

    
705
        if (quote) {
706
                gchar *qmark;
707
                gchar *quote_str;
708

    
709
                if (prefs_common.quotemark && *prefs_common.quotemark)
710
                        qmark = prefs_common.quotemark;
711
                else
712
                        qmark = "> ";
713

    
714
                quote_str = compose_quote_fmt(compose, compose->replyinfo,
715
                                              prefs_common.quotefmt,
716
                                              qmark, body);
717
        }
718

    
719
        if (prefs_common.auto_sig)
720
                compose_insert_sig(compose, FALSE);
721

    
722
        if (quote && prefs_common.linewrap_quote)
723
                compose_wrap_line_all(compose);
724

    
725
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
726
        gtk_text_buffer_get_start_iter(buffer, &iter);
727
        gtk_text_buffer_place_cursor(buffer, &iter);
728

    
729
        gtk_widget_grab_focus(compose->text);
730

    
731
        undo_unblock(compose->undostruct);
732

    
733
        compose_connect_changed_callbacks(compose);
734

    
735
        if (prefs_common.auto_exteditor)
736
                compose_exec_ext_editor(compose);
737
}
738

    
739
void compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach,
740
                     const gchar *body)
741
{
742
        Compose *compose;
743
        PrefsAccount *account;
744
        GtkTextView *text;
745
        GtkTextBuffer *buffer;
746
        GtkTextIter iter;
747
        GSList *cur;
748
        MsgInfo *msginfo;
749

    
750
        g_return_if_fail(mlist != NULL);
751

    
752
        msginfo = (MsgInfo *)mlist->data;
753
        g_return_if_fail(msginfo->folder != NULL);
754

    
755
        account = account_find_from_item(msginfo->folder);
756
        if (!account) account = cur_account;
757
        g_return_if_fail(account != NULL);
758

    
759
        for (cur = mlist; cur != NULL; cur = cur->next) {
760
                msginfo = (MsgInfo *)cur->data;
761
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
762
                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
763
        }
764
        msginfo = (MsgInfo *)mlist->data;
765
        if (MSG_IS_IMAP(msginfo->flags))
766
                imap_msg_list_unset_perm_flags(mlist, MSG_REPLIED);
767

    
768
        compose = compose_create(account, COMPOSE_FORWARD);
769

    
770
        undo_block(compose->undostruct);
771

    
772
        compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
773
        if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
774
                compose_entry_append(compose, msginfo->subject,
775
                                     COMPOSE_ENTRY_SUBJECT);
776
        if (item)
777
                compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
778

    
779
        text = GTK_TEXT_VIEW(compose->text);
780
        buffer = gtk_text_view_get_buffer(text);
781

    
782
        for (cur = mlist; cur != NULL; cur = cur->next) {
783
                msginfo = (MsgInfo *)cur->data;
784

    
785
                if (as_attach) {
786
                        gchar *msgfile;
787

    
788
                        msgfile = procmsg_get_message_file_path(msginfo);
789
                        if (!is_file_exist(msgfile))
790
                                g_warning(_("%s: file not exist\n"), msgfile);
791
                        else
792
                                compose_attach_append(compose, msgfile, msgfile,
793
                                                      "message/rfc822");
794

    
795
                        g_free(msgfile);
796
                } else {
797
                        gchar *qmark;
798
                        gchar *quote_str;
799
                        MsgInfo *full_msginfo;
800

    
801
                        full_msginfo = procmsg_msginfo_get_full_info(msginfo);
802
                        if (!full_msginfo)
803
                                full_msginfo = procmsg_msginfo_copy(msginfo);
804

    
805
                        if (cur != mlist) {
806
                                GtkTextMark *mark;
807
                                mark = gtk_text_buffer_get_insert(buffer);
808
                                gtk_text_buffer_get_iter_at_mark
809
                                        (buffer, &iter, mark);
810
                                gtk_text_buffer_insert
811
                                        (buffer, &iter, "\n\n", 2);
812
                        }
813

    
814
                        if (prefs_common.fw_quotemark &&
815
                            *prefs_common.fw_quotemark)
816
                                qmark = prefs_common.fw_quotemark;
817
                        else
818
                                qmark = "> ";
819

    
820
                        quote_str = compose_quote_fmt(compose, full_msginfo,
821
                                                      prefs_common.fw_quotefmt,
822
                                                      qmark, body);
823
                        compose_attach_parts(compose, msginfo);
824

    
825
                        procmsg_msginfo_free(full_msginfo);
826

    
827
                        if (body) break;
828
                }
829
        }
830

    
831
        if (prefs_common.auto_sig)
832
                compose_insert_sig(compose, FALSE);
833

    
834
        if (prefs_common.linewrap_quote)
835
                compose_wrap_line_all(compose);
836

    
837
        gtk_text_buffer_get_start_iter(buffer, &iter);
838
        gtk_text_buffer_place_cursor(buffer, &iter);
839

    
840
        undo_unblock(compose->undostruct);
841

    
842
        compose_connect_changed_callbacks(compose);
843

    
844
        if (account->protocol != A_NNTP)
845
                gtk_widget_grab_focus(compose->to_entry);
846
        else
847
                gtk_widget_grab_focus(compose->newsgroups_entry);
848

    
849
        if (prefs_common.auto_exteditor)
850
                compose_exec_ext_editor(compose);
851
}
852

    
853
void compose_redirect(MsgInfo *msginfo, FolderItem *item)
854
{
855
        Compose *compose;
856
        PrefsAccount *account;
857
        GtkTextView *text;
858
        GtkTextBuffer *buffer;
859
        GtkTextMark *mark;
860
        GtkTextIter iter;
861
        FILE *fp;
862
        gchar buf[BUFFSIZE];
863

    
864
        g_return_if_fail(msginfo != NULL);
865
        g_return_if_fail(msginfo->folder != NULL);
866

    
867
        account = account_find_from_item(msginfo->folder);
868
        if (!account) account = cur_account;
869
        g_return_if_fail(account != NULL);
870

    
871
        compose = compose_create(account, COMPOSE_REDIRECT);
872
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
873

    
874
        if (compose_parse_header(compose, msginfo) < 0) return;
875

    
876
        undo_block(compose->undostruct);
877

    
878
        if (msginfo->subject)
879
                compose_entry_set(compose, msginfo->subject,
880
                                  COMPOSE_ENTRY_SUBJECT);
881
        compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
882

    
883
        text = GTK_TEXT_VIEW(compose->text);
884
        buffer = gtk_text_view_get_buffer(text);
885
        mark = gtk_text_buffer_get_insert(buffer);
886
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
887

    
888
        if ((fp = procmime_get_first_text_content(msginfo)) == NULL)
889
                g_warning(_("Can't get text part\n"));
890
        else {
891
                while (fgets(buf, sizeof(buf), fp) != NULL) {
892
                        strcrchomp(buf);
893
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
894
                }
895
                fclose(fp);
896
        }
897
        compose_attach_parts(compose, msginfo);
898

    
899
        if (account->protocol != A_NNTP)
900
                gtk_widget_grab_focus(compose->to_entry);
901
        else
902
                gtk_widget_grab_focus(compose->newsgroups_entry);
903

    
904
        gtk_text_view_set_editable(text, FALSE);
905

    
906
        undo_unblock(compose->undostruct);
907

    
908
        compose_connect_changed_callbacks(compose);
909
}
910

    
911
void compose_reedit(MsgInfo *msginfo)
912
{
913
        Compose *compose;
914
        PrefsAccount *account;
915
        GtkTextView *text;
916
        GtkTextBuffer *buffer;
917
        GtkTextMark *mark;
918
        GtkTextIter iter;
919
        FILE *fp;
920
        gchar buf[BUFFSIZE];
921

    
922
        g_return_if_fail(msginfo != NULL);
923
        g_return_if_fail(msginfo->folder != NULL);
924

    
925
        account = account_find_from_msginfo(msginfo);
926
        if (!account) account = cur_account;
927
        g_return_if_fail(account != NULL);
928

    
929
        compose = compose_create(account, COMPOSE_REEDIT);
930
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
931

    
932
        if (compose_parse_header(compose, msginfo) < 0) return;
933

    
934
        undo_block(compose->undostruct);
935

    
936
        compose_reedit_set_entry(compose, msginfo);
937

    
938
        text = GTK_TEXT_VIEW(compose->text);
939
        buffer = gtk_text_view_get_buffer(text);
940
        mark = gtk_text_buffer_get_insert(buffer);
941
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
942

    
943
        if ((fp = procmime_get_first_text_content(msginfo)) == NULL)
944
                g_warning(_("Can't get text part\n"));
945
        else {
946
                while (fgets(buf, sizeof(buf), fp) != NULL) {
947
                        strcrchomp(buf);
948
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
949
                }
950
                fclose(fp);
951
        }
952
        compose_attach_parts(compose, msginfo);
953

    
954
        gtk_widget_grab_focus(compose->text);
955

    
956
        undo_unblock(compose->undostruct);
957

    
958
        compose_connect_changed_callbacks(compose);
959

    
960
        if (prefs_common.auto_exteditor)
961
                compose_exec_ext_editor(compose);
962
}
963

    
964
GList *compose_get_compose_list(void)
965
{
966
        return compose_list;
967
}
968

    
969
static void compose_entry_show(Compose *compose, ComposeEntryType type)
970
{
971
        GtkItemFactory *ifactory;
972

    
973
        ifactory = gtk_item_factory_from_widget(compose->menubar);
974

    
975
        switch (type) {
976
        case COMPOSE_ENTRY_CC:
977
                menu_set_active(ifactory, "/View/Cc", TRUE);
978
                break;
979
        case COMPOSE_ENTRY_BCC:
980
                menu_set_active(ifactory, "/View/Bcc", TRUE);
981
                break;
982
        case COMPOSE_ENTRY_REPLY_TO:
983
                menu_set_active(ifactory, "/View/Reply to", TRUE);
984
                break;
985
        case COMPOSE_ENTRY_FOLLOWUP_TO:
986
                menu_set_active(ifactory, "/View/Followup to", TRUE);
987
                break;
988
        case COMPOSE_ENTRY_TO:
989
                menu_set_active(ifactory, "/View/To", TRUE);
990
                break;
991
        default:
992
                break;
993
        }
994
}
995

    
996
static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
997
{
998
        GtkEntry *entry;
999

    
1000
        switch (type) {
1001
        case COMPOSE_ENTRY_CC:
1002
                entry = GTK_ENTRY(compose->cc_entry);
1003
                break;
1004
        case COMPOSE_ENTRY_BCC:
1005
                entry = GTK_ENTRY(compose->bcc_entry);
1006
                break;
1007
        case COMPOSE_ENTRY_REPLY_TO:
1008
                entry = GTK_ENTRY(compose->reply_entry);
1009
                break;
1010
        case COMPOSE_ENTRY_SUBJECT:
1011
                entry = GTK_ENTRY(compose->subject_entry);
1012
                break;
1013
        case COMPOSE_ENTRY_NEWSGROUPS:
1014
                entry = GTK_ENTRY(compose->newsgroups_entry);
1015
                break;
1016
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1017
                entry = GTK_ENTRY(compose->followup_entry);
1018
                break;
1019
        case COMPOSE_ENTRY_TO:
1020
        default:
1021
                entry = GTK_ENTRY(compose->to_entry);
1022
                break;
1023
        }
1024

    
1025
        return entry;
1026
}
1027

    
1028
void compose_entry_set(Compose *compose, const gchar *text,
1029
                       ComposeEntryType type)
1030
{
1031
        GtkEntry *entry;
1032

    
1033
        if (!text) return;
1034

    
1035
        compose_entry_show(compose, type);
1036
        entry = compose_get_entry(compose, type);
1037

    
1038
        gtk_entry_set_text(entry, text);
1039
}
1040

    
1041
void compose_entry_append(Compose *compose, const gchar *text,
1042
                          ComposeEntryType type)
1043
{
1044
        GtkEntry *entry;
1045
        const gchar *str;
1046
        gint pos;
1047

    
1048
        if (!text || *text == '\0') return;
1049

    
1050
        compose_entry_show(compose, type);
1051
        entry = compose_get_entry(compose, type);
1052

    
1053
        if (type != COMPOSE_ENTRY_SUBJECT) {
1054
                str = gtk_entry_get_text(entry);
1055
                if (*str != '\0') {
1056
                        pos = entry->text_length;
1057
                        gtk_editable_insert_text(GTK_EDITABLE(entry),
1058
                                                 ", ", -1, &pos);
1059
                }
1060
        }
1061

    
1062
        pos = entry->text_length;
1063
        gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1064
}
1065

    
1066
static void compose_entries_set(Compose *compose, const gchar *mailto)
1067
{
1068
        gchar *to = NULL;
1069
        gchar *cc = NULL;
1070
        gchar *bcc = NULL;
1071
        gchar *subject = NULL;
1072
        gchar *body = NULL;
1073

    
1074
        scan_mailto_url(mailto, &to, &cc, &bcc, &subject, &body);
1075

    
1076
        if (to)
1077
                compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1078
        if (cc)
1079
                compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1080
        if (bcc)
1081
                compose_entry_set(compose, bcc, COMPOSE_ENTRY_BCC);
1082
        if (subject)
1083
                compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1084
        if (body) {
1085
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1086
                GtkTextBuffer *buffer;
1087
                GtkTextMark *mark;
1088
                GtkTextIter iter;
1089

    
1090
                buffer = gtk_text_view_get_buffer(text);
1091
                mark = gtk_text_buffer_get_insert(buffer);
1092
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1093

    
1094
                gtk_text_buffer_insert(buffer, &iter, body, -1);
1095
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1096
        }
1097

    
1098
        g_free(to);
1099
        g_free(cc);
1100
        g_free(bcc);
1101
        g_free(subject);
1102
        g_free(body);
1103
}
1104

    
1105
static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1106
                                          ComposeMode mode)
1107
{
1108
        g_return_if_fail(item != NULL);
1109

    
1110
        if (item->auto_to) {
1111
                if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1112
                        compose_entry_set(compose, item->auto_to,
1113
                                          COMPOSE_ENTRY_TO);
1114
        }
1115
        if (item->auto_cc)
1116
                compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1117
        if (item->auto_bcc)
1118
                compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1119
        if (item->auto_replyto)
1120
                compose_entry_set(compose, item->auto_replyto,
1121
                                  COMPOSE_ENTRY_REPLY_TO);
1122
}
1123

    
1124
#undef ACTIVATE_MENU
1125

    
1126
static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1127
{
1128
        static HeaderEntry hentry[] = {{"Reply-To:",        NULL, TRUE},
1129
                                       {"Cc:",                NULL, TRUE},
1130
                                       {"References:",        NULL, FALSE},
1131
                                       {"Bcc:",                NULL, TRUE},
1132
                                       {"Newsgroups:",  NULL, TRUE},
1133
                                       {"Followup-To:", NULL, TRUE},
1134
                                       {"List-Post:",   NULL, FALSE},
1135
                                       {NULL,                NULL, FALSE}};
1136

    
1137
        enum
1138
        {
1139
                H_REPLY_TO        = 0,
1140
                H_CC                = 1,
1141
                H_REFERENCES        = 2,
1142
                H_BCC                = 3,
1143
                H_NEWSGROUPS    = 4,
1144
                H_FOLLOWUP_TO        = 5,
1145
                H_LIST_POST     = 6
1146
        };
1147

    
1148
        FILE *fp;
1149

    
1150
        g_return_val_if_fail(msginfo != NULL, -1);
1151

    
1152
        if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1153
        procheader_get_header_fields(fp, hentry);
1154
        fclose(fp);
1155

    
1156
        if (hentry[H_REPLY_TO].body != NULL) {
1157
                conv_unmime_header_overwrite(hentry[H_REPLY_TO].body);
1158
                compose->replyto = hentry[H_REPLY_TO].body;
1159
                hentry[H_REPLY_TO].body = NULL;
1160
        }
1161
        if (hentry[H_CC].body != NULL) {
1162
                conv_unmime_header_overwrite(hentry[H_CC].body);
1163
                compose->cc = hentry[H_CC].body;
1164
                hentry[H_CC].body = NULL;
1165
        }
1166
        if (hentry[H_REFERENCES].body != NULL) {
1167
                if (compose->mode == COMPOSE_REEDIT)
1168
                        compose->references = hentry[H_REFERENCES].body;
1169
                else {
1170
                        compose->references = compose_parse_references
1171
                                (hentry[H_REFERENCES].body, msginfo->msgid);
1172
                        g_free(hentry[H_REFERENCES].body);
1173
                }
1174
                hentry[H_REFERENCES].body = NULL;
1175
        }
1176
        if (hentry[H_BCC].body != NULL) {
1177
                if (compose->mode == COMPOSE_REEDIT) {
1178
                        conv_unmime_header_overwrite(hentry[H_BCC].body);
1179
                        compose->bcc = hentry[H_BCC].body;
1180
                } else
1181
                        g_free(hentry[H_BCC].body);
1182
                hentry[H_BCC].body = NULL;
1183
        }
1184
        if (hentry[H_NEWSGROUPS].body != NULL) {
1185
                compose->newsgroups = hentry[H_NEWSGROUPS].body;
1186
                hentry[H_NEWSGROUPS].body = NULL;
1187
        }
1188
        if (hentry[H_FOLLOWUP_TO].body != NULL) {
1189
                conv_unmime_header_overwrite(hentry[H_FOLLOWUP_TO].body);
1190
                compose->followup_to = hentry[H_FOLLOWUP_TO].body;
1191
                hentry[H_FOLLOWUP_TO].body = NULL;
1192
        }
1193
        if (hentry[H_LIST_POST].body != NULL) {
1194
                gchar *to = NULL;
1195

    
1196
                extract_address(hentry[H_LIST_POST].body);
1197
                if (hentry[H_LIST_POST].body[0] != '\0') {
1198
                        scan_mailto_url(hentry[H_LIST_POST].body,
1199
                                        &to, NULL, NULL, NULL, NULL);
1200
                        if (to) {
1201
                                g_free(compose->ml_post);
1202
                                compose->ml_post = to;
1203
                        }
1204
                }
1205
                g_free(hentry[H_LIST_POST].body);
1206
                hentry[H_LIST_POST].body = NULL;
1207
        }
1208

    
1209
        if (compose->mode == COMPOSE_REEDIT && msginfo->inreplyto)
1210
                compose->inreplyto = g_strdup(msginfo->inreplyto);
1211
        else if (compose->mode != COMPOSE_REEDIT &&
1212
                 msginfo->msgid && *msginfo->msgid) {
1213
                compose->inreplyto = g_strdup(msginfo->msgid);
1214

    
1215
                if (!compose->references) {
1216
                        if (msginfo->inreplyto && *msginfo->inreplyto)
1217
                                compose->references =
1218
                                        g_strdup_printf("<%s>\n\t<%s>",
1219
                                                        msginfo->inreplyto,
1220
                                                        msginfo->msgid);
1221
                        else
1222
                                compose->references =
1223
                                        g_strconcat("<", msginfo->msgid, ">",
1224
                                                    NULL);
1225
                }
1226
        }
1227

    
1228
        return 0;
1229
}
1230

    
1231
static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1232
{
1233
        GSList *ref_id_list, *cur;
1234
        GString *new_ref;
1235
        gchar *new_ref_str;
1236

    
1237
        ref_id_list = references_list_append(NULL, ref);
1238
        if (!ref_id_list) return NULL;
1239
        if (msgid && *msgid)
1240
                ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1241

    
1242
        for (;;) {
1243
                gint len = 0;
1244

    
1245
                for (cur = ref_id_list; cur != NULL; cur = cur->next)
1246
                        /* "<" + Message-ID + ">" + CR+LF+TAB */
1247
                        len += strlen((gchar *)cur->data) + 5;
1248

    
1249
                if (len > MAX_REFERENCES_LEN) {
1250
                        /* remove second message-ID */
1251
                        if (ref_id_list && ref_id_list->next &&
1252
                            ref_id_list->next->next) {
1253
                                g_free(ref_id_list->next->data);
1254
                                ref_id_list = g_slist_remove
1255
                                        (ref_id_list, ref_id_list->next->data);
1256
                        } else {
1257
                                slist_free_strings(ref_id_list);
1258
                                g_slist_free(ref_id_list);
1259
                                return NULL;
1260
                        }
1261
                } else
1262
                        break;
1263
        }
1264

    
1265
        new_ref = g_string_new("");
1266
        for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1267
                if (new_ref->len > 0)
1268
                        g_string_append(new_ref, "\n\t");
1269
                g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1270
        }
1271

    
1272
        slist_free_strings(ref_id_list);
1273
        g_slist_free(ref_id_list);
1274

    
1275
        new_ref_str = new_ref->str;
1276
        g_string_free(new_ref, FALSE);
1277

    
1278
        return new_ref_str;
1279
}
1280

    
1281
static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1282
                                const gchar *fmt, const gchar *qmark,
1283
                                const gchar *body)
1284
{
1285
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1286
        GtkTextBuffer *buffer;
1287
        GtkTextMark *mark;
1288
        GtkTextIter iter;
1289
        static MsgInfo dummyinfo;
1290
        gchar *quote_str = NULL;
1291
        gchar *buf;
1292
        gchar *p, *lastp;
1293
        gint len;
1294

    
1295
        if (!msginfo)
1296
                msginfo = &dummyinfo;
1297

    
1298
        if (qmark != NULL) {
1299
                quote_fmt_init(msginfo, NULL, NULL);
1300
                quote_fmt_scan_string(qmark);
1301
                quote_fmt_parse();
1302

    
1303
                buf = quote_fmt_get_buffer();
1304
                if (buf == NULL)
1305
                        alertpanel_error(_("Quote mark format error."));
1306
                else
1307
                        Xstrdup_a(quote_str, buf, return NULL)
1308
        }
1309

    
1310
        if (fmt && *fmt != '\0') {
1311
                quote_fmt_init(msginfo, quote_str, body);
1312
                quote_fmt_scan_string(fmt);
1313
                quote_fmt_parse();
1314

    
1315
                buf = quote_fmt_get_buffer();
1316
                if (buf == NULL) {
1317
                        alertpanel_error(_("Message reply/forward format error."));
1318
                        return NULL;
1319
                }
1320
        } else
1321
                buf = "";
1322

    
1323
        buffer = gtk_text_view_get_buffer(text);
1324
        mark = gtk_text_buffer_get_insert(buffer);
1325
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1326

    
1327
        for (p = buf; *p != '\0'; ) {
1328
                lastp = strchr(p, '\n');
1329
                len = lastp ? lastp - p + 1 : -1;
1330
                gtk_text_buffer_insert(buffer, &iter, p, len);
1331
                if (lastp)
1332
                        p = lastp + 1;
1333
                else
1334
                        break;
1335
        }
1336

    
1337
        return buf;
1338
}
1339

    
1340
static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1341
                                    ComposeMode mode)
1342
{
1343
        GSList *cc_list = NULL;
1344
        GSList *cur;
1345
        gchar *from = NULL;
1346
        gchar *replyto = NULL;
1347
        GHashTable *to_table;
1348
        gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1349

    
1350
        g_return_if_fail(compose->account != NULL);
1351
        g_return_if_fail(msginfo != NULL);
1352

    
1353
        switch (COMPOSE_MODE(mode)) {
1354
        case COMPOSE_REPLY_TO_SENDER:
1355
                ignore_replyto = TRUE;
1356
                break;
1357
        case COMPOSE_REPLY_TO_ALL:
1358
                to_all = TRUE;
1359
                break;
1360
        case COMPOSE_REPLY_TO_LIST:
1361
                to_ml = TRUE;
1362
                break;
1363
        default:
1364
                break;
1365
        }
1366

    
1367
        if (compose->account->protocol != A_NNTP) {
1368
                if (!compose->replyto && to_ml && compose->ml_post)
1369
                        compose_entry_set(compose, compose->ml_post,
1370
                                          COMPOSE_ENTRY_TO);
1371
                else
1372
                        compose_entry_set(compose, 
1373
                                 (compose->replyto && !ignore_replyto)
1374
                                 ? compose->replyto
1375
                                 : msginfo->from ? msginfo->from : "",
1376
                                 COMPOSE_ENTRY_TO);
1377
        } else {
1378
                if (ignore_replyto) {
1379
                        compose_entry_set(compose,
1380
                                          msginfo->from ? msginfo->from : "",
1381
                                          COMPOSE_ENTRY_TO);
1382
                } else {
1383
                        compose_entry_set(compose,
1384
                                          compose->followup_to ? compose->followup_to
1385
                                          : compose->newsgroups ? compose->newsgroups
1386
                                          : "",
1387
                                          COMPOSE_ENTRY_NEWSGROUPS);
1388
                }
1389
        }
1390

    
1391
        if (msginfo->subject && *msginfo->subject) {
1392
                gchar *buf;
1393
                guchar *p;
1394

    
1395
                buf = g_strdup(msginfo->subject);
1396

    
1397
                if (msginfo->folder && msginfo->folder->trim_compose_subject)
1398
                        trim_subject(buf);
1399

    
1400
                while (!strncasecmp(buf, "Re:", 3)) {
1401
                        p = buf + 3;
1402
                        while (isspace(*p)) p++;
1403
                        memmove(buf, p, strlen(p) + 1);
1404
                }
1405

    
1406
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1407
                compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1408

    
1409
                g_free(buf);
1410
        } else
1411
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1412

    
1413
        if (!compose->replyto && to_ml && compose->ml_post) return;
1414
        if (!to_all || compose->account->protocol == A_NNTP) return;
1415

    
1416
        if (compose->replyto) {
1417
                Xstrdup_a(replyto, compose->replyto, return);
1418
                extract_address(replyto);
1419
        }
1420
        if (msginfo->from) {
1421
                Xstrdup_a(from, msginfo->from, return);
1422
                extract_address(from);
1423
        }
1424

    
1425
        if (replyto && from)
1426
                cc_list = address_list_append(cc_list, from);
1427
        cc_list = address_list_append(cc_list, msginfo->to);
1428
        cc_list = address_list_append(cc_list, compose->cc);
1429

    
1430
        to_table = g_hash_table_new(g_str_hash, g_str_equal);
1431
        if (replyto)
1432
                g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1433
        if (compose->account)
1434
                g_hash_table_insert(to_table, compose->account->address,
1435
                                    GINT_TO_POINTER(1));
1436

    
1437
        /* remove address on To: and that of current account */
1438
        for (cur = cc_list; cur != NULL; ) {
1439
                GSList *next = cur->next;
1440

    
1441
                if (g_hash_table_lookup(to_table, cur->data) != NULL)
1442
                        cc_list = g_slist_remove(cc_list, cur->data);
1443
                else
1444
                        g_hash_table_insert(to_table, cur->data, cur);
1445

    
1446
                cur = next;
1447
        }
1448
        g_hash_table_destroy(to_table);
1449

    
1450
        if (cc_list) {
1451
                for (cur = cc_list; cur != NULL; cur = cur->next)
1452
                        compose_entry_append(compose, (gchar *)cur->data,
1453
                                             COMPOSE_ENTRY_CC);
1454
                slist_free_strings(cc_list);
1455
                g_slist_free(cc_list);
1456
        }
1457
}
1458

    
1459
static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1460
{
1461
        g_return_if_fail(msginfo != NULL);
1462

    
1463
        compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1464
        compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1465
        compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1466
        compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1467
        compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1468
}
1469

    
1470
static void compose_insert_sig(Compose *compose, gboolean replace)
1471
{
1472
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1473
        GtkTextBuffer *buffer;
1474
        GtkTextMark *mark;
1475
        GtkTextIter iter;
1476
        gint cur_pos;
1477

    
1478
        g_return_if_fail(compose->account != NULL);
1479

    
1480
        buffer = gtk_text_view_get_buffer(text);
1481
        mark = gtk_text_buffer_get_insert(buffer);
1482
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1483
        cur_pos = gtk_text_iter_get_offset(&iter);
1484

    
1485
        if (replace)
1486
                gtk_text_buffer_get_end_iter(buffer, &iter);
1487

    
1488
        if (replace && compose->sig_str) {
1489
                GtkTextIter first_iter, start_iter, end_iter;
1490
                gboolean found;
1491

    
1492
                gtk_text_buffer_get_start_iter(buffer, &first_iter);
1493

    
1494
                if (compose->sig_str[0] == '\0')
1495
                        found = FALSE;
1496
                else
1497
                        found = gtk_text_iter_forward_search
1498
                                (&first_iter, compose->sig_str,
1499
                                 GTK_TEXT_SEARCH_TEXT_ONLY,
1500
                                 &start_iter, &end_iter, NULL);
1501

    
1502
                if (found)
1503
                        gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
1504
        }
1505

    
1506
        g_free(compose->sig_str);
1507
        compose->sig_str = compose_get_signature_str(compose);
1508
        if (compose->sig_str) {
1509
                if (!replace)
1510
                        gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
1511
                gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
1512
        } else
1513
                compose->sig_str = g_strdup("");
1514

    
1515
        if (cur_pos > gtk_text_buffer_get_char_count(buffer))
1516
                cur_pos = gtk_text_buffer_get_char_count(buffer);
1517

    
1518
        gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
1519
        gtk_text_buffer_move_mark(buffer, mark, &iter);
1520
}
1521

    
1522
static gchar *compose_get_signature_str(Compose *compose)
1523
{
1524
        gchar *sig_body = NULL;
1525
        gchar *sig_str = NULL;
1526
        gchar *utf8_sig_str = NULL;
1527

    
1528
        g_return_val_if_fail(compose->account != NULL, NULL);
1529

    
1530
        if (!compose->account->sig_path)
1531
                return NULL;
1532

    
1533
        if (compose->account->sig_type == SIG_FILE) {
1534
                if (!is_file_or_fifo_exist(compose->account->sig_path)) {
1535
                        g_warning("can't open signature file: %s\n",
1536
                                  compose->account->sig_path);
1537
                        return NULL;
1538
                }
1539
        }
1540

    
1541
        if (compose->account->sig_type == SIG_COMMAND)
1542
                sig_body = get_command_output(compose->account->sig_path);
1543
        else {
1544
                gchar *tmp;
1545

    
1546
                tmp = file_read_to_str(compose->account->sig_path);
1547
                if (!tmp)
1548
                        return NULL;
1549
                sig_body = normalize_newlines(tmp);
1550
                g_free(tmp);
1551
        }
1552

    
1553
        if (prefs_common.sig_sep) {
1554
                sig_str = g_strconcat(prefs_common.sig_sep, "\n", sig_body,
1555
                                      NULL);
1556
                g_free(sig_body);
1557
        } else
1558
                sig_str = sig_body;
1559

    
1560
        if (sig_str) {
1561
                utf8_sig_str = conv_codeset_strdup
1562
                        (sig_str, conv_get_locale_charset_str(), CS_UTF_8);
1563
                g_free(sig_str);
1564
        }
1565

    
1566
        return utf8_sig_str;
1567
}
1568

    
1569
static void compose_insert_file(Compose *compose, const gchar *file)
1570
{
1571
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1572
        GtkTextBuffer *buffer;
1573
        GtkTextMark *mark;
1574
        GtkTextIter iter;
1575
        const gchar *cur_encoding;
1576
        gchar buf[BUFFSIZE];
1577
        gint len;
1578
        FILE *fp;
1579

    
1580
        g_return_if_fail(file != NULL);
1581

    
1582
        if ((fp = fopen(file, "rb")) == NULL) {
1583
                FILE_OP_ERROR(file, "fopen");
1584
                return;
1585
        }
1586

    
1587
        buffer = gtk_text_view_get_buffer(text);
1588
        mark = gtk_text_buffer_get_insert(buffer);
1589
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1590

    
1591
        cur_encoding = conv_get_locale_charset_str();
1592

    
1593
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1594
                gchar *str;
1595

    
1596
                str = conv_codeset_strdup(buf, cur_encoding, CS_UTF_8);
1597
                if (!str) continue;
1598

    
1599
                /* strip <CR> if DOS/Windows file,
1600
                   replace <CR> with <LF> if Macintosh file. */
1601
                strcrchomp(str);
1602
                len = strlen(str);
1603
                if (len > 0 && str[len - 1] != '\n') {
1604
                        while (--len >= 0)
1605
                                if (str[len] == '\r') str[len] = '\n';
1606
                }
1607

    
1608
                gtk_text_buffer_insert(buffer, &iter, str, -1);
1609
                g_free(str);
1610
        }
1611

    
1612
        fclose(fp);
1613
}
1614

    
1615
static void compose_attach_append(Compose *compose, const gchar *file,
1616
                                  const gchar *filename,
1617
                                  const gchar *content_type)
1618
{
1619
        AttachInfo *ainfo;
1620
        gchar *text[N_ATTACH_COLS];
1621
        FILE *fp;
1622
        off_t size;
1623
        gint row;
1624

    
1625
        g_return_if_fail(file != NULL);
1626
        g_return_if_fail(*file != '\0');
1627

    
1628
        if (!is_file_exist(file)) {
1629
                g_warning(_("File %s doesn't exist\n"), file);
1630
                return;
1631
        }
1632
        if ((size = get_file_size(file)) < 0) {
1633
                g_warning(_("Can't get file size of %s\n"), file);
1634
                return;
1635
        }
1636
        if (size == 0) {
1637
                alertpanel_notice(_("File %s is empty."), file);
1638
                return;
1639
        }
1640
        if ((fp = fopen(file, "rb")) == NULL) {
1641
                alertpanel_error(_("Can't read %s."), file);
1642
                return;
1643
        }
1644
        fclose(fp);
1645

    
1646
        compose_changed_cb(NULL, compose);
1647

    
1648
        if (!compose->use_attach) {
1649
                GtkItemFactory *ifactory;
1650

    
1651
                ifactory = gtk_item_factory_from_widget(compose->menubar);
1652
                menu_set_active(ifactory, "/View/Attachment", TRUE);
1653
        }
1654

    
1655
        ainfo = g_new0(AttachInfo, 1);
1656
        ainfo->file = g_strdup(file);
1657

    
1658
        if (content_type) {
1659
                ainfo->content_type = g_strdup(content_type);
1660
                if (!strcasecmp(content_type, "message/rfc822")) {
1661
                        MsgInfo *msginfo;
1662
                        MsgFlags flags = {0, 0};
1663
                        const gchar *name;
1664

    
1665
                        if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
1666
                                ainfo->encoding = ENC_7BIT;
1667
                        else
1668
                                ainfo->encoding = ENC_8BIT;
1669

    
1670
                        msginfo = procheader_parse_file(file, flags, FALSE);
1671
                        if (msginfo && msginfo->subject)
1672
                                name = msginfo->subject;
1673
                        else
1674
                                name = g_basename(filename ? filename : file);
1675

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

    
1678
                        procmsg_msginfo_free(msginfo);
1679
                } else {
1680
                        if (!g_strncasecmp(content_type, "text", 4))
1681
                                ainfo->encoding = procmime_get_encoding_for_text_file(file);
1682
                        else
1683
                                ainfo->encoding = ENC_BASE64;
1684
                        ainfo->name = g_strdup
1685
                                (g_basename(filename ? filename : file));
1686
                }
1687
        } else {
1688
                ainfo->content_type = procmime_get_mime_type(file);
1689
                if (!ainfo->content_type) {
1690
                        ainfo->content_type =
1691
                                g_strdup("application/octet-stream");
1692
                        ainfo->encoding = ENC_BASE64;
1693
                } else if (!g_strncasecmp(ainfo->content_type, "text", 4))
1694
                        ainfo->encoding =
1695
                                procmime_get_encoding_for_text_file(file);
1696
                else
1697
                        ainfo->encoding = ENC_BASE64;
1698
                ainfo->name = g_strdup(g_basename(filename ? filename : file));        
1699
        }
1700
        ainfo->size = size;
1701

    
1702
        text[COL_MIMETYPE] = ainfo->content_type;
1703
        text[COL_SIZE] = to_human_readable(size);
1704
        text[COL_NAME] = ainfo->name;
1705

    
1706
        row = gtk_clist_append(GTK_CLIST(compose->attach_clist), text);
1707
        gtk_clist_set_row_data(GTK_CLIST(compose->attach_clist), row, ainfo);
1708
}
1709

    
1710
#define IS_FIRST_PART_TEXT(info) \
1711
        ((info->mime_type == MIME_TEXT || info->mime_type == MIME_TEXT_HTML) || \
1712
         (info->mime_type == MIME_MULTIPART && info->content_type && \
1713
          !strcasecmp(info->content_type, "multipart/alternative") && \
1714
          (info->children && \
1715
           (info->children->mime_type == MIME_TEXT || \
1716
            info->children->mime_type == MIME_TEXT_HTML))))
1717

    
1718
static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
1719
{
1720
        MimeInfo *mimeinfo;
1721
        MimeInfo *child;
1722
        gchar *infile;
1723
        gchar *outfile;
1724

    
1725
        mimeinfo = procmime_scan_message(msginfo);
1726
        if (!mimeinfo) return;
1727

    
1728
        /* skip first text (presumably message body) */
1729
        child = mimeinfo->children;
1730
        if (!child || IS_FIRST_PART_TEXT(mimeinfo)) {
1731
                procmime_mimeinfo_free_all(mimeinfo);
1732
                return;
1733
        }
1734
        if (IS_FIRST_PART_TEXT(child))
1735
                child = child->next;
1736

    
1737
        infile = procmsg_get_message_file_path(msginfo);
1738

    
1739
        while (child != NULL) {
1740
                if (child->children || child->mime_type == MIME_MULTIPART) {
1741
                        child = procmime_mimeinfo_next(child);
1742
                        continue;
1743
                }
1744

    
1745
                outfile = procmime_get_tmp_file_name(child);
1746
                if (procmime_get_part(outfile, infile, child) < 0)
1747
                        g_warning(_("Can't get the part of multipart message."));
1748
                else
1749
                        compose_attach_append
1750
                                (compose, outfile,
1751
                                 child->filename ? child->filename : child->name,
1752
                                 child->content_type);
1753

    
1754
                child = child->next;
1755
        }
1756

    
1757
        g_free(infile);
1758
        procmime_mimeinfo_free_all(mimeinfo);
1759
}
1760

    
1761
#undef IS_FIRST_PART_TEXT
1762

    
1763
#define CHAR_BUF_SIZE        8
1764

    
1765
#define GET_CHAR(iter_p, buf, len)                                             \
1766
{                                                                             \
1767
        GtkTextIter end_iter;                                                     \
1768
        gchar *tmp;                                                             \
1769
        end_iter = *iter_p;                                                     \
1770
                                                                             \
1771
        gtk_text_iter_forward_char(&end_iter);                                     \
1772
        tmp = gtk_text_buffer_get_text(textbuf, iter_p, &end_iter, FALSE);   \
1773
        if (tmp) {                                                             \
1774
                glong items_read, items_witten;                                     \
1775
                GError *error = NULL;                                             \
1776
                gunichar *wide_char;                                             \
1777
                                                                             \
1778
                strncpy2(buf, tmp, CHAR_BUF_SIZE);                             \
1779
                wide_char = g_utf8_to_ucs4(tmp, -1,                             \
1780
                                           &items_read, &items_witten,             \
1781
                                           &error);                             \
1782
                if (error != NULL) {                                             \
1783
                        g_warning("%s\n", error->message);                     \
1784
                        g_error_free(error);                                     \
1785
                }                                                             \
1786
                len = wide_char && g_unichar_iswide(*wide_char) ? 2 : 1;     \
1787
                g_free(wide_char);                                             \
1788
        } else {                                                             \
1789
                buf[0] = '\0';                                                     \
1790
                len = 1;                                                     \
1791
        }                                                                     \
1792
        g_free(tmp);                                                             \
1793
}
1794

    
1795
#define INDENT_CHARS        ">|#"
1796
#define SPACE_CHARS        " \t"
1797

    
1798
#warning FIXME_GTK2
1799
static void compose_wrap_line(Compose *compose)
1800
{
1801
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1802
        GtkTextBuffer *textbuf;
1803
        GtkTextMark *mark;
1804
        GtkTextIter insert_iter, iter;
1805
        gint ch_len, last_ch_len;
1806
        gchar cbuf[CHAR_BUF_SIZE], last_ch;
1807
        guint text_len;
1808
        gint p_start, p_end;
1809
        gint line_pos, cur_pos;
1810
        gint line_len, cur_len;
1811
        gboolean line_end;
1812
        gboolean quoted;
1813

    
1814
        textbuf = gtk_text_view_get_buffer(text);
1815
        text_len = gtk_text_buffer_get_char_count(textbuf);
1816
        mark = gtk_text_buffer_get_insert(textbuf);
1817
        gtk_text_buffer_get_iter_at_mark(textbuf, &insert_iter, mark);
1818
        cur_pos = gtk_text_iter_get_offset(&insert_iter);
1819
        GET_CHAR(&insert_iter, cbuf, ch_len);
1820

    
1821
        if ((ch_len == 1 && *cbuf == '\n') || cur_pos == text_len) {
1822
                GtkTextIter prev_iter;
1823

    
1824
                if (cur_pos == 0)
1825
                        return; /* on the paragraph mark */
1826
                prev_iter = insert_iter;
1827
                gtk_text_iter_backward_char(&prev_iter);
1828
                GET_CHAR(&prev_iter, cbuf, ch_len);
1829
                if (ch_len == 1 && *cbuf == '\n')
1830
                        return; /* on the paragraph mark */
1831
        }
1832

    
1833
        /* find paragraph start. */
1834
        line_end = quoted = FALSE;
1835
        for (iter = insert_iter; gtk_text_iter_backward_char(&iter); ) {
1836
                GET_CHAR(&iter, cbuf, ch_len);
1837
                if (ch_len == 1 && *cbuf == '\n') {
1838
                        if (quoted)
1839
                                return; /* quoted part */
1840
                        if (line_end) {
1841
                                gtk_text_iter_forward_chars(&iter, 2);
1842
                                break;
1843
                        }
1844
                        line_end = TRUE;
1845
                } else {
1846
                        if (ch_len == 1 && strchr(INDENT_CHARS, *cbuf))
1847
                                quoted = TRUE;
1848
                        else if (ch_len != 1 || !isspace(*(guchar *)cbuf))
1849
                                quoted = FALSE;
1850

    
1851
                        line_end = FALSE;
1852
                }
1853
        }
1854
        p_start = gtk_text_iter_get_offset(&iter);
1855

    
1856
        /* find paragraph end. */
1857
        line_end = FALSE;
1858
        for (iter = insert_iter; gtk_text_iter_forward_char(&iter); ) {
1859
                GET_CHAR(&iter, cbuf, ch_len);
1860
                if (ch_len == 1 && *cbuf == '\n') {
1861
                        if (line_end) {
1862
                                gtk_text_iter_backward_char(&iter);
1863
                                break;
1864
                        }
1865
                        line_end = TRUE;
1866
                } else {
1867
                        if (line_end && ch_len == 1 &&
1868
                            strchr(INDENT_CHARS, *cbuf))
1869
                                return; /* quoted part */
1870

    
1871
                        line_end = FALSE;
1872
                }
1873
        }
1874
        p_end = gtk_text_iter_get_offset(&iter);
1875

    
1876
        if (p_end >= text_len)
1877
                p_end = text_len;
1878

    
1879
        if (p_start >= p_end)
1880
                return;
1881

    
1882
        line_len = cur_len = 0;
1883
        last_ch_len = 0;
1884
        last_ch = '\0';
1885
        line_pos = p_start;
1886
        for (cur_pos = p_start; cur_pos < p_end; cur_pos++) {
1887
                gboolean space = FALSE;
1888

    
1889
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter, cur_pos);
1890
                GET_CHAR(&iter, cbuf, ch_len);
1891

    
1892
                if (ch_len < 0) {
1893
                        cbuf[0] = '\0';
1894
                        ch_len = 1;
1895
                }
1896

    
1897
                if (ch_len == 1 && isspace(*(guchar *)cbuf))
1898
                        space = TRUE;
1899

    
1900
                if (ch_len == 1 && *cbuf == '\n') {
1901
                        gboolean replace = FALSE;
1902
                        GtkTextIter next_iter = iter;
1903

    
1904
                        gtk_text_iter_forward_char(&next_iter);
1905

    
1906
                        if (last_ch_len == 1 && !isspace((guchar)last_ch)) {
1907
                                if (cur_pos + 1 < p_end) {
1908
                                        GET_CHAR(&next_iter, cbuf, ch_len);
1909
                                        if (ch_len == 1 &&
1910
                                            !isspace(*(guchar *)cbuf))
1911
                                                replace = TRUE;
1912
                                }
1913
                        }
1914
                        gtk_text_buffer_delete(textbuf, &iter, &next_iter);
1915
                        if (replace) {
1916
                                gtk_text_buffer_insert(textbuf, &iter, " ", 1);
1917
                                space = TRUE;
1918
                        } else {
1919
                                p_end--;
1920
                                cur_pos--;
1921
                                gtk_text_buffer_get_iter_at_offset
1922
                                        (textbuf, &iter, cur_pos);
1923
                                continue;
1924
                        }
1925
                }
1926

    
1927
                last_ch_len = ch_len;
1928
                last_ch = *cbuf;
1929

    
1930
                if (space) {
1931
                        line_pos = cur_pos + 1;
1932
                        line_len = cur_len + ch_len;
1933
                }
1934

    
1935
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter, line_pos);
1936

    
1937
                if (cur_len + ch_len > prefs_common.linewrap_len &&
1938
                    line_len > 0) {
1939
                        gint tlen = ch_len;
1940
                        GtkTextIter prev_iter = iter;
1941

    
1942
                        gtk_text_iter_backward_char(&prev_iter);
1943

    
1944
                        if (ch_len == 1 && isspace(*(guchar *)cbuf)) {
1945
                                gtk_text_buffer_delete
1946
                                        (textbuf, &prev_iter, &iter);
1947
                                iter = prev_iter;
1948
                                p_end--;
1949
                                cur_pos--;
1950
                                line_pos--;
1951
                                cur_len--;
1952
                                line_len--;
1953
                        }
1954
                        ch_len = tlen;
1955

    
1956
                        gtk_text_buffer_insert(textbuf, &iter, "\n", 1);
1957
                        p_end++;
1958
                        cur_pos++;
1959
                        line_pos++;
1960
                        cur_len = cur_len - line_len + ch_len;
1961
                        line_len = 0;
1962
                        continue;
1963
                }
1964

    
1965
                if (ch_len > 1) {
1966
                        line_pos = cur_pos + 1;
1967
                        line_len = cur_len + ch_len;
1968
                }
1969
                cur_len += ch_len;
1970
        }
1971
}
1972

    
1973
#undef WRAP_DEBUG
1974
#ifdef WRAP_DEBUG
1975
/* Darko: used when I debug wrapping */
1976
void dump_text(GtkTextBuffer *textbuf, int pos, int tlen, int breakoncr)
1977
{
1978
        gint clen;
1979
        gchar cbuf[CHAR_BUF_SIZE];
1980

    
1981
        printf("%d [", pos);
1982
        gtk_text_buffer_get_iter_at_offset(textbuf, &iter, pos);
1983
        gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter, pos + tlen);
1984
        for (; gtk_text_iter_forward_char(&iter) &&
1985
             gtk_text_iter_compare(&iter, &end_iter) < 0; ) {
1986
                GET_CHAR(&iter, cbuf, clen);
1987
                if (clen < 0) break;
1988
                if (breakoncr && clen == 1 && cbuf[0] == '\n')
1989
                        break;
1990
                fwrite(cbuf, clen, 1, stdout);
1991
        }
1992
        printf("]\n");
1993
}
1994
#endif
1995

    
1996
typedef enum {
1997
        WAIT_FOR_SPACE,
1998
        WAIT_FOR_INDENT_CHAR,
1999
        WAIT_FOR_INDENT_CHAR_OR_SPACE
2000
} IndentState;
2001

    
2002
/* return indent length, we allow:
2003
   > followed by spaces/tabs
2004
   | followed by spaces/tabs
2005
   uppercase characters immediately followed by >,
2006
   and the repeating sequences of the above */
2007
/* return indent length */
2008
static guint get_indent_length(GtkTextBuffer *textbuf, guint start_pos,
2009
                               guint text_len)
2010
{
2011
        guint i_len = 0;
2012
        guint i, ch_len, alnum_cnt = 0;
2013
        IndentState state = WAIT_FOR_INDENT_CHAR;
2014
        gchar cbuf[CHAR_BUF_SIZE];
2015
        gboolean is_space;
2016
        gboolean is_indent;
2017

    
2018
#warning FIXME_GTK2 use GtkTextIter
2019
        for (i = start_pos; i < text_len; i++) {
2020
                GtkTextIter iter;
2021

    
2022
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter, i);
2023
                GET_CHAR(&iter, cbuf, ch_len);
2024
                if (ch_len > 1)
2025
                        break;
2026

    
2027
                if (cbuf[0] == '\n')
2028
                        break;
2029

    
2030
                is_indent = strchr(INDENT_CHARS, cbuf[0]) ? TRUE : FALSE;
2031
                is_space = strchr(SPACE_CHARS, cbuf[0]) ? TRUE : FALSE;
2032

    
2033
                switch (state) {
2034
                case WAIT_FOR_SPACE:
2035
                        if (is_space == FALSE)
2036
                                goto out;
2037
                        state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2038
                        break;
2039
                case WAIT_FOR_INDENT_CHAR_OR_SPACE:
2040
                        if (is_indent == FALSE && is_space == FALSE &&
2041
                            !isupper((guchar)cbuf[0]))
2042
                                goto out;
2043
                        if (is_space == TRUE) {
2044
                                alnum_cnt = 0;
2045
                                state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2046
                        } else if (is_indent == TRUE) {
2047
                                alnum_cnt = 0;
2048
                                state = WAIT_FOR_SPACE;
2049
                        } else {
2050
                                alnum_cnt++;
2051
                                state = WAIT_FOR_INDENT_CHAR;
2052
                        }
2053
                        break;
2054
                case WAIT_FOR_INDENT_CHAR:
2055
                        if (is_indent == FALSE && !isupper((guchar)cbuf[0]))
2056
                                goto out;
2057
                        if (is_indent == TRUE) {
2058
                                alnum_cnt = 0;
2059
                                state = WAIT_FOR_SPACE;
2060
                        } else {
2061
                                alnum_cnt++;
2062
                        }
2063
                        break;
2064
                }
2065

    
2066
                i_len++;
2067
        }
2068

    
2069
out:
2070
        if ((i_len > 0) && (state == WAIT_FOR_INDENT_CHAR))
2071
                i_len -= alnum_cnt;
2072

    
2073
        return i_len;
2074
}
2075

    
2076
/* insert quotation string when line was wrapped */
2077
static guint ins_quote(GtkTextBuffer *textbuf, GtkTextIter *iter,
2078
                       guint indent_len,
2079
                       guint prev_line_pos, guint text_len,
2080
                       gchar *quote_fmt)
2081
{
2082
        guint ins_len = 0;
2083

    
2084
        if (indent_len) {
2085
                GtkTextIter iter1, iter2;
2086
                gchar *text;
2087

    
2088
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter1,
2089
                                                   prev_line_pos);
2090
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter2,
2091
                                                   prev_line_pos + indent_len);
2092
                text = gtk_text_buffer_get_text(textbuf, &iter1, &iter2, FALSE);
2093
                if (!text) return 0;
2094

    
2095
                gtk_text_buffer_insert(textbuf, iter, text, -1);
2096
                ins_len = g_utf8_strlen(text, -1);
2097

    
2098
                g_free(text);
2099
        }
2100

    
2101
        return ins_len;
2102
}
2103

    
2104
/* check if we should join the next line */
2105
static gboolean join_next_line_is_needed(GtkTextBuffer *textbuf,
2106
                                         guint start_pos, guint tlen,
2107
                                         guint prev_ilen, gboolean autowrap)
2108
{
2109
        guint indent_len, ch_len;
2110
        gboolean do_join = FALSE;
2111
        gchar cbuf[CHAR_BUF_SIZE];
2112

    
2113
        indent_len = get_indent_length(textbuf, start_pos, tlen);
2114

    
2115
        if ((autowrap || indent_len > 0) && indent_len == prev_ilen) {
2116
                GtkTextIter iter;
2117

    
2118
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter,
2119
                                                   start_pos + indent_len);
2120
                GET_CHAR(&iter, cbuf, ch_len);
2121
                if (ch_len > 0 && (cbuf[0] != '\n'))
2122
                        do_join = TRUE;
2123
        }
2124

    
2125
        return do_join;
2126
}
2127

    
2128
static void compose_wrap_line_all(Compose *compose)
2129
{
2130
        compose_wrap_line_all_full(compose, FALSE);
2131
}
2132

    
2133
static void compose_wrap_line_all_full(Compose *compose, gboolean autowrap)
2134
{
2135
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2136
        GtkTextBuffer *textbuf;
2137
        GtkTextIter iter, end_iter;
2138
        guint tlen;
2139
        guint line_pos = 0, cur_pos = 0, p_pos = 0;
2140
        gint line_len = 0, cur_len = 0;
2141
        gint ch_len;
2142
        gboolean is_new_line = TRUE, do_delete = FALSE;
2143
        guint i_len = 0;
2144
        gboolean linewrap_quote = TRUE;
2145
        gboolean set_editable_pos = FALSE;
2146
        gint editable_pos = 0;
2147
        guint linewrap_len = prefs_common.linewrap_len;
2148
        gchar *qfmt = prefs_common.quotemark;
2149
        gchar cbuf[CHAR_BUF_SIZE];
2150

    
2151
        textbuf = gtk_text_view_get_buffer(text);
2152
        tlen = gtk_text_buffer_get_char_count(textbuf);
2153

    
2154
        for (; cur_pos < tlen; cur_pos++) {
2155
                /* mark position of new line - needed for quotation wrap */
2156
                if (is_new_line) {
2157
                        if (linewrap_quote)
2158
                                i_len = get_indent_length
2159
                                        (textbuf, cur_pos, tlen);
2160

    
2161
                        is_new_line = FALSE;
2162
                        p_pos = cur_pos;
2163
                }
2164

    
2165
                gtk_text_buffer_get_iter_at_offset(textbuf, &iter, cur_pos);
2166
                GET_CHAR(&iter, cbuf, ch_len);
2167

    
2168
                /* fix line length for tabs */
2169
                if (ch_len == 1 && *cbuf == '\t') {
2170
                        guint tab_width = 8;
2171
                        guint tab_offset = line_len % tab_width;
2172

    
2173
                        if (tab_offset) {
2174
                                line_len += tab_width - tab_offset - 1;
2175
                                cur_len = line_len;
2176
                        }
2177
                }
2178

    
2179
                /* we have encountered line break */
2180
                if (ch_len == 1 && *cbuf == '\n') {
2181
                        gint clen;
2182
                        gchar cb[CHAR_BUF_SIZE];
2183

    
2184
                        /* should we join the next line */
2185
                        if ((autowrap || i_len != cur_len) && do_delete &&
2186
                            join_next_line_is_needed
2187
                                (textbuf, cur_pos + 1, tlen, i_len, autowrap))
2188
                                do_delete = TRUE;
2189
                        else
2190
                                do_delete = FALSE;
2191

    
2192
                        /* skip delete if it is continuous URL */
2193
                        if (do_delete && (line_pos - p_pos <= i_len) &&
2194
                            gtkut_text_buffer_is_uri_string(textbuf, line_pos,
2195
                                                            tlen))
2196
                                do_delete = FALSE;
2197

    
2198
                        /* should we delete to perform smart wrapping */
2199
                        if (line_len < linewrap_len && do_delete) {
2200
                                /* get rid of newline */
2201
                                gtk_text_buffer_get_iter_at_offset
2202
                                        (textbuf, &iter, cur_pos);
2203
                                end_iter = iter;
2204
                                gtk_text_iter_forward_char(&end_iter);
2205
                                gtk_text_buffer_delete
2206
                                        (textbuf, &iter, &end_iter);
2207
                                tlen--;
2208

    
2209
                                /* if text starts with quote fmt or with
2210
                                   indent string, delete them */
2211
                                if (i_len) {
2212
                                        guint ilen;
2213
                                        ilen =  gtkut_text_buffer_str_compare_n
2214
                                                (textbuf, cur_pos, p_pos, i_len,
2215
                                                 tlen);
2216
                                        if (ilen) {
2217
                                                end_iter = iter;
2218
                                                gtk_text_iter_forward_chars
2219
                                                        (&end_iter, ilen);
2220
                                                gtk_text_buffer_delete
2221
                                                        (textbuf,
2222
                                                         &iter, &end_iter);
2223
                                                tlen -= ilen;
2224
                                        }
2225
                                }
2226

    
2227
                                gtk_text_buffer_get_iter_at_offset
2228
                                        (textbuf, &iter, cur_pos);
2229
                                GET_CHAR(&iter, cb, clen);
2230
                                /* insert space between the next line */
2231
                                if (cur_pos > 0) {
2232
                                        gint clen_prev;
2233
                                        gchar cb_prev[MB_LEN_MAX];
2234
                                        GtkTextIter iter_prev = iter;
2235

    
2236
                                        gtk_text_iter_backward_char(&iter_prev);
2237
                                        GET_CHAR(&iter_prev, cb_prev,
2238
                                                 clen_prev);
2239
                                        if ((clen_prev != clen && clen > 1) ||
2240
                                            (clen == 1 &&
2241
                                             !isspace((guchar)cb[0]))) {
2242
                                                gtk_text_buffer_insert
2243
                                                        (textbuf, &iter,
2244
                                                         " ", 1);
2245
                                                tlen++;
2246
                                        }
2247
                                }
2248

    
2249
                                /* and start over with current line */
2250
                                cur_pos = p_pos - 1;
2251
                                line_pos = cur_pos;
2252
                                line_len = cur_len = 0;
2253
                                do_delete = FALSE;
2254
                                is_new_line = TRUE;
2255
                                /* move beginning of line if we are on
2256
                                   linebreak */
2257
#warning FIXME_GTK2
2258
                                gtk_text_buffer_get_iter_at_offset
2259
                                        (textbuf, &iter, line_pos);
2260
                                GET_CHAR(&iter, cb, clen);
2261
                                if (clen == 1 && *cb == '\n')
2262
                                        line_pos++;
2263
                                continue;
2264
                        }
2265

    
2266
                        /* mark new line beginning */
2267
                        line_pos = cur_pos + 1;
2268
                        line_len = cur_len = 0;
2269
                        do_delete = FALSE;
2270
                        is_new_line = TRUE;
2271
                        continue;
2272
                }
2273

    
2274
                if (ch_len < 0) {
2275
                        cbuf[0] = '\0';
2276
                        ch_len = 1;
2277
                }
2278

    
2279
                /* possible line break */
2280
                if (ch_len == 1 && isspace(*(guchar *)cbuf)) {
2281
                        line_pos = cur_pos + 1;
2282
                        line_len = cur_len + ch_len;
2283
                }
2284

    
2285
                /* are we over wrapping length set in preferences ? */
2286
                if (cur_len + ch_len > linewrap_len) {
2287
                        gint clen;
2288

    
2289
                        /* force wrapping if it is one long word but not URL */
2290
                        if (line_pos - p_pos <= i_len)
2291
                                if (!gtkut_text_buffer_is_uri_string
2292
                                    (textbuf, line_pos, tlen))
2293
                                        line_pos = cur_pos - 1;
2294

    
2295
                        gtk_text_buffer_get_iter_at_offset
2296
                                (textbuf, &iter, line_pos);
2297
                        GET_CHAR(&iter, cbuf, clen);
2298

    
2299
                        /* if next character is space delete it */
2300
                        if (clen == 1 && isspace(*(guchar *)cbuf)) {
2301
                                if (p_pos + i_len != line_pos ||
2302
                                        !gtkut_text_buffer_is_uri_string
2303
                                        (textbuf, line_pos, tlen)) {
2304
                                        /* workaround for correct cursor
2305
                                           position */
2306
                                        if (set_editable_pos == FALSE) {
2307
                                                GtkTextMark *mark;
2308

    
2309
                                                mark = gtk_text_buffer_get_insert(textbuf);
2310
                                                gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2311
                                                editable_pos = gtk_text_iter_get_offset(&iter);
2312
                                                if (editable_pos == line_pos)
2313
                                                        set_editable_pos = TRUE;
2314
                                        }
2315
                                        gtk_text_buffer_get_iter_at_offset
2316
                                                (textbuf, &iter, line_pos);
2317
                                        end_iter = iter;
2318
                                        gtk_text_iter_backward_char(&end_iter);
2319
                                        gtk_text_buffer_delete
2320
                                                (textbuf, &iter, &end_iter);
2321
                                        tlen--;
2322
                                        cur_pos--;
2323
                                        line_pos--;
2324
                                        cur_len--;
2325
                                        line_len--;
2326
                                }
2327
                        }
2328

    
2329
                        /* if it is URL at beginning of line don't wrap */
2330
                        if (p_pos + i_len == line_pos &&
2331
                            gtkut_text_buffer_is_uri_string(textbuf,
2332
                                                            line_pos, tlen)) {
2333
                                continue;
2334
                        }
2335

    
2336
                        /* insert CR */
2337
                        gtk_text_buffer_get_iter_at_offset
2338
                                (textbuf, &iter, line_pos);
2339
                        gtk_text_buffer_insert(textbuf, &iter, "\n", 1);
2340
                        tlen++;
2341
                        line_pos++;
2342
                        /* for loop will increase it */
2343
                        cur_pos = line_pos - 1;
2344
                        /* start over with current line */
2345
                        is_new_line = TRUE;
2346
                        line_len = cur_len = 0;
2347
                        if (autowrap || i_len > 0)
2348
                                do_delete = TRUE;
2349
                        else
2350
                                do_delete = FALSE;
2351

    
2352
                        /* should we insert quotation ? */
2353
                        if (linewrap_quote && i_len) {
2354
                                /* only if line is not already quoted  */
2355
                                if (!gtkut_text_buffer_str_compare
2356
                                        (textbuf, line_pos, tlen, qfmt)) {
2357
                                        guint ins_len;
2358

    
2359
                                        if (line_pos - p_pos > i_len) {
2360
                                                ins_len = ins_quote
2361
                                                        (textbuf, &iter, i_len,
2362
                                                         p_pos, tlen, qfmt);
2363
                                                tlen += ins_len;
2364
                                        }
2365
                                }
2366
                        }
2367
                        continue;
2368
                }
2369

    
2370
                if (ch_len > 1) {
2371
                        line_pos = cur_pos + 1;
2372
                        line_len = cur_len + ch_len;
2373
                }
2374
                /* advance to next character in buffer */
2375
                cur_len += ch_len;
2376
        }
2377

    
2378
        if (set_editable_pos && editable_pos <= tlen) {
2379
                gtk_text_buffer_get_iter_at_offset
2380
                        (textbuf, &iter, editable_pos);
2381
                gtk_text_buffer_place_cursor(textbuf, &iter);
2382
        }
2383
}
2384

    
2385
#undef GET_CHAR
2386
#undef CHAR_BUF_SIZE
2387

    
2388
static void compose_set_title(Compose *compose)
2389
{
2390
        gchar *str;
2391
        gchar *edited;
2392

    
2393
        edited = compose->modified ? _(" [Edited]") : "";
2394
        if (compose->account && compose->account->address)
2395
                str = g_strdup_printf(_("%s - Compose message%s"),
2396
                                      compose->account->address, edited);
2397
        else
2398
                str = g_strdup_printf(_("Compose message%s"), edited);
2399
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
2400
        g_free(str);
2401
}
2402

    
2403
static void compose_select_account(Compose *compose, PrefsAccount *account,
2404
                                   gboolean init)
2405
{
2406
        GtkItemFactory *ifactory;
2407

    
2408
        g_return_if_fail(account != NULL);
2409

    
2410
        compose->account = account;
2411

    
2412
        compose_set_title(compose);
2413

    
2414
        ifactory = gtk_item_factory_from_widget(compose->menubar);
2415

    
2416
        if (account->protocol == A_NNTP) {
2417
                gtk_widget_show(compose->newsgroups_hbox);
2418
                gtk_widget_show(compose->newsgroups_entry);
2419
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2420
                compose->use_newsgroups = TRUE;
2421

    
2422
                menu_set_active(ifactory, "/View/To", FALSE);
2423
                menu_set_sensitive(ifactory, "/View/To", TRUE);
2424
                menu_set_active(ifactory, "/View/Cc", FALSE);
2425
                menu_set_sensitive(ifactory, "/View/Cc", TRUE);
2426
                menu_set_sensitive(ifactory, "/View/Followup to", TRUE);
2427
        } else {
2428
                gtk_widget_hide(compose->newsgroups_hbox);
2429
                gtk_widget_hide(compose->newsgroups_entry);
2430
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0);
2431
                gtk_widget_queue_resize(compose->table_vbox);
2432
                compose->use_newsgroups = FALSE;
2433

    
2434
                menu_set_active(ifactory, "/View/To", TRUE);
2435
                menu_set_sensitive(ifactory, "/View/To", FALSE);
2436
                menu_set_active(ifactory, "/View/Cc", TRUE);
2437
                menu_set_sensitive(ifactory, "/View/Cc", FALSE);
2438
                menu_set_active(ifactory, "/View/Followup to", FALSE);
2439
                menu_set_sensitive(ifactory, "/View/Followup to", FALSE);
2440
        }
2441

    
2442
        if (account->set_autocc) {
2443
                compose_entry_show(compose, COMPOSE_ENTRY_CC);
2444
                if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2445
                        compose_entry_set(compose, account->auto_cc,
2446
                                          COMPOSE_ENTRY_CC);
2447
        }
2448
        if (account->set_autobcc) {
2449
                compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2450
                if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2451
                        compose_entry_set(compose, account->auto_bcc,
2452
                                          COMPOSE_ENTRY_BCC);
2453
        }
2454
        if (account->set_autoreplyto) {
2455
                compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2456
                if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2457
                        compose_entry_set(compose, account->auto_replyto,
2458
                                          COMPOSE_ENTRY_REPLY_TO);
2459
        }
2460

    
2461
#if USE_GPGME
2462
        if (account->default_sign)
2463
                menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2464
        if (account->default_encrypt)
2465
                menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2466
#endif /* USE_GPGME */
2467

    
2468
        if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2469
                compose_insert_sig(compose, TRUE);
2470
}
2471

    
2472
static gboolean compose_check_for_valid_recipient(Compose *compose)
2473
{
2474
        const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
2475
        const gchar *newsgroups_raw = "";
2476
        gchar *to, *cc, *bcc;
2477
        gchar *newsgroups;
2478

    
2479
        if (compose->use_to)
2480
                to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2481
        if (compose->use_cc)
2482
                cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2483
        if (compose->use_bcc)
2484
                bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2485
        if (compose->use_newsgroups)
2486
                newsgroups_raw = gtk_entry_get_text
2487
                        (GTK_ENTRY(compose->newsgroups_entry));
2488

    
2489
        Xstrdup_a(to, to_raw, return FALSE);
2490
        Xstrdup_a(cc, cc_raw, return FALSE);
2491
        Xstrdup_a(bcc, bcc_raw, return FALSE);
2492
        Xstrdup_a(newsgroups, newsgroups_raw, return FALSE);
2493
        g_strstrip(to);
2494
        g_strstrip(cc);
2495
        g_strstrip(bcc);
2496
        g_strstrip(newsgroups);
2497

    
2498
        if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
2499
                return FALSE;
2500
        else
2501
                return TRUE;
2502
}
2503

    
2504
static gboolean compose_check_entries(Compose *compose)
2505
{
2506
        const gchar *str;
2507

    
2508
        if (compose_check_for_valid_recipient(compose) == FALSE) {
2509
                alertpanel_error(_("Recipient is not specified."));
2510
                return FALSE;
2511
        }
2512

    
2513
        str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2514
        if (*str == '\0') {
2515
                AlertValue aval;
2516

    
2517
                aval = alertpanel(_("Send"),
2518
                                  _("Subject is empty. Send it anyway?"),
2519
                                  _("Yes"), _("No"), NULL);
2520
                if (aval != G_ALERTDEFAULT)
2521
                        return FALSE;
2522
        }
2523

    
2524
        return TRUE;
2525
}
2526

    
2527
static gint compose_send(Compose *compose)
2528
{
2529
        gchar tmp[MAXPATHLEN + 1];
2530
        gint ok = 0;
2531
        static gboolean lock = FALSE;
2532

    
2533
        if (lock) return 1;
2534

    
2535
        g_return_val_if_fail(compose->account != NULL, -1);
2536

    
2537
        lock = TRUE;
2538

    
2539
        if (compose_check_entries(compose) == FALSE) {
2540
                lock = FALSE;
2541
                return 1;
2542
        }
2543

    
2544
        if (!main_window_toggle_online_if_offline(main_window_get())) {
2545
                lock = FALSE;
2546
                return 1;
2547
        }
2548

    
2549
        /* write to temporary file */
2550
        g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
2551
                   get_tmp_dir(), G_DIR_SEPARATOR, compose);
2552

    
2553
        if (compose->mode == COMPOSE_REDIRECT) {
2554
                if (compose_redirect_write_to_file(compose, tmp) < 0) {
2555
                        lock = FALSE;
2556
                        return -1;
2557
                }
2558
        } else {
2559
                if (prefs_common.linewrap_at_send)
2560
                        compose_wrap_line_all(compose);
2561

    
2562
                if (compose_write_to_file(compose, tmp, FALSE) < 0) {
2563
                        lock = FALSE;
2564
                        return -1;
2565
                }
2566
        }
2567

    
2568
        if (!compose->to_list && !compose->newsgroup_list) {
2569
                g_warning(_("can't get recipient list."));
2570
                unlink(tmp);
2571
                lock = FALSE;
2572
                return -1;
2573
        }
2574

    
2575
        if (compose->to_list) {
2576
                PrefsAccount *ac;
2577

    
2578
                if (compose->account->protocol != A_NNTP)
2579
                        ac = compose->account;
2580
                else {
2581
                        ac = account_find_from_address(compose->account->address);
2582
                        if (!ac) {
2583
                                if (cur_account && cur_account->protocol != A_NNTP)
2584
                                        ac = cur_account;
2585
                                else
2586
                                        ac = account_get_default();
2587
                        }
2588
                        if (!ac || ac->protocol == A_NNTP) {
2589
                                alertpanel_error(_("Account for sending mail is not specified.\n"
2590
                                                   "Please select a mail account before sending."));
2591
                                unlink(tmp);
2592
                                lock = FALSE;
2593
                                return -1;
2594
                        }
2595
                }
2596
                ok = send_message(tmp, ac, compose->to_list);
2597
                statusbar_pop_all();
2598
        }
2599

    
2600
        if (ok == 0 && compose->newsgroup_list) {
2601
                ok = news_post(FOLDER(compose->account->folder), tmp);
2602
                if (ok < 0) {
2603
                        alertpanel_error(_("Error occurred while posting the message to %s ."),
2604
                                         compose->account->nntp_server);
2605
                        unlink(tmp);
2606
                        lock = FALSE;
2607
                        return -1;
2608
                }
2609
        }
2610

    
2611
        if (ok == 0) {
2612
                if (compose->mode == COMPOSE_REEDIT) {
2613
                        compose_remove_reedit_target(compose);
2614
                        if (compose->targetinfo)
2615
                                folderview_update_item
2616
                                        (compose->targetinfo->folder, TRUE);
2617
                }
2618
                /* save message to outbox */
2619
                if (prefs_common.savemsg) {
2620
                        FolderItem *outbox;
2621

    
2622
                        outbox = account_get_special_folder
2623
                                (compose->account, F_OUTBOX);
2624
                        if (procmsg_save_to_outbox(outbox, tmp, FALSE) < 0)
2625
                                alertpanel_error
2626
                                        (_("Can't save the message to outbox."));
2627
                        else
2628
                                folderview_update_item(outbox, TRUE);
2629
                }
2630
        }
2631

    
2632
        unlink(tmp);
2633
        lock = FALSE;
2634
        return ok;
2635
}
2636

    
2637
#if USE_GPGME
2638
/* interfaces to rfc2015 to keep out the prefs stuff there.
2639
 * returns 0 on success and -1 on error. */
2640
static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
2641
{
2642
        const gchar *key_id = NULL;
2643
        GSList *key_list;
2644

    
2645
        switch (compose->account->sign_key) {
2646
        case SIGN_KEY_DEFAULT:
2647
                *pkey_list = NULL;
2648
                return 0;
2649
        case SIGN_KEY_BY_FROM:
2650
                key_id = compose->account->address;
2651
                break;
2652
        case SIGN_KEY_CUSTOM:
2653
                key_id = compose->account->sign_key_id;
2654
                break;
2655
        default:
2656
                break;
2657
        }
2658

    
2659
        key_list = rfc2015_create_signers_list(key_id);
2660

    
2661
        if (!key_list) {
2662
                alertpanel_error(_("Could not find any key associated with "
2663
                                   "currently selected key id `%s'."), key_id);
2664
                return -1;
2665
        }
2666

    
2667
        *pkey_list = key_list;
2668
        return 0;
2669
}
2670

    
2671
/* clearsign message body text */
2672
static gint compose_clearsign_text(Compose *compose, gchar **text)
2673
{
2674
        GSList *key_list;
2675
        gchar *tmp_file;
2676

    
2677
        tmp_file = get_tmp_file();
2678
        if (str_write_to_file(*text, tmp_file) < 0) {
2679
                g_free(tmp_file);
2680
                return -1;
2681
        }
2682

    
2683
        if (compose_create_signers_list(compose, &key_list) < 0 ||
2684
            rfc2015_clearsign(tmp_file, key_list) < 0) {
2685
                unlink(tmp_file);
2686
                g_free(tmp_file);
2687
                return -1;
2688
        }
2689

    
2690
        g_free(*text);
2691
        *text = file_read_to_str(tmp_file);
2692
        unlink(tmp_file);
2693
        g_free(tmp_file);
2694
        if (*text == NULL)
2695
                return -1;
2696

    
2697
        return 0;
2698
}
2699
#endif /* USE_GPGME */
2700

    
2701
static gint compose_write_to_file(Compose *compose, const gchar *file,
2702
                                  gboolean is_draft)
2703
{
2704
        GtkTextBuffer *buffer;
2705
        GtkTextIter start, end;
2706
        FILE *fp;
2707
        size_t len;
2708
        gchar *chars;
2709
        gchar *buf;
2710
        gchar *canon_buf;
2711
        const gchar *out_codeset;
2712
        EncodingType encoding;
2713

    
2714
        if ((fp = fopen(file, "wb")) == NULL) {
2715
                FILE_OP_ERROR(file, "fopen");
2716
                return -1;
2717
        }
2718

    
2719
        /* chmod for security */
2720
        if (change_file_mode_rw(fp, file) < 0) {
2721
                FILE_OP_ERROR(file, "chmod");
2722
                g_warning(_("can't change file mode\n"));
2723
        }
2724

    
2725
        /* get all composed text */
2726
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2727
        gtk_text_buffer_get_start_iter(buffer, &start);
2728
        gtk_text_buffer_get_end_iter(buffer, &end);
2729
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2730
        if (is_ascii_str(chars)) {
2731
                buf = chars;
2732
                chars = NULL;
2733
                out_codeset = CS_US_ASCII;
2734
                encoding = ENC_7BIT;
2735
        } else {
2736
                const gchar *src_codeset;
2737

    
2738
                out_codeset = conv_get_outgoing_charset_str();
2739
                if (!strcasecmp(out_codeset, CS_US_ASCII))
2740
                        out_codeset = CS_ISO_8859_1;
2741

    
2742
                if (prefs_common.encoding_method == CTE_BASE64)
2743
                        encoding = ENC_BASE64;
2744
                else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
2745
                        encoding = ENC_QUOTED_PRINTABLE;
2746
                else if (prefs_common.encoding_method == CTE_8BIT)
2747
                        encoding = ENC_8BIT;
2748
                else
2749
                        encoding = procmime_get_encoding_for_charset(out_codeset);
2750

    
2751
#if USE_GPGME
2752
                if (!is_draft &&
2753
                    compose->use_signing && !compose->account->clearsign &&
2754
                    encoding == ENC_8BIT)
2755
                        encoding = ENC_BASE64;
2756
#endif
2757

    
2758
                src_codeset = CS_UTF_8;
2759

    
2760
                debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
2761
                            src_codeset, out_codeset, procmime_get_encoding_str(encoding));
2762

    
2763
                buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
2764
                if (!buf) {
2765
                        AlertValue aval;
2766
                        gchar *msg;
2767

    
2768
                        msg = g_strdup_printf(_("Can't convert the character encoding of the message from\n"
2769
                                                "%s to %s.\n"
2770
                                                "Send it anyway?"), src_codeset, out_codeset);
2771
                        aval = alertpanel
2772
                                (_("Error"), msg, _("Yes"), _("+No"), NULL);
2773
                        g_free(msg);
2774

    
2775
                        if (aval != G_ALERTDEFAULT) {
2776
                                g_free(chars);
2777
                                fclose(fp);
2778
                                unlink(file);
2779
                                return -1;
2780
                        } else {
2781
                                buf = chars;
2782
                                out_codeset = src_codeset;
2783
                                chars = NULL;
2784
                        }
2785
                }
2786
        }
2787
        g_free(chars);
2788

    
2789
        canon_buf = canonicalize_str(buf);
2790
        g_free(buf);
2791
        buf = canon_buf;
2792

    
2793
#if USE_GPGME
2794
        if (!is_draft && compose->use_signing && compose->account->clearsign) {
2795
                if (compose_clearsign_text(compose, &buf) < 0) {
2796
                        g_warning("clearsign failed\n");
2797
                        fclose(fp);
2798
                        unlink(file);
2799
                        g_free(buf);
2800
                        return -1;
2801
                }
2802
        }
2803
#endif
2804

    
2805
        /* write headers */
2806
        if (compose_write_headers
2807
                (compose, fp, out_codeset, encoding, is_draft) < 0) {
2808
                g_warning(_("can't write headers\n"));
2809
                fclose(fp);
2810
                unlink(file);
2811
                g_free(buf);
2812
                return -1;
2813
        }
2814

    
2815
        if (compose->use_attach &&
2816
            GTK_CLIST(compose->attach_clist)->row_list) {
2817
#if USE_GPGME
2818
            /* This prolog message is ignored by mime software and
2819
             * because it would make our signing/encryption task
2820
             * tougher, we don't emit it in that case */
2821
            if (!compose->use_signing && !compose->use_encryption)
2822
#endif
2823
                fputs("This is a multi-part message in MIME format.\n", fp);
2824

    
2825
                fprintf(fp, "\n--%s\n", compose->boundary);
2826
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
2827
                        out_codeset);
2828
#if USE_GPGME
2829
                if (compose->use_signing && !compose->account->clearsign)
2830
                        fprintf(fp, "Content-Disposition: inline\n");
2831
#endif
2832
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
2833
                        procmime_get_encoding_str(encoding));
2834
                fputc('\n', fp);
2835
        }
2836

    
2837
        /* write body */
2838
        len = strlen(buf);
2839
        if (encoding == ENC_BASE64) {
2840
                gchar outbuf[B64_BUFFSIZE];
2841
                gint i, l;
2842

    
2843
                for (i = 0; i < len; i += B64_LINE_SIZE) {
2844
                        l = MIN(B64_LINE_SIZE, len - i);
2845
                        base64_encode(outbuf, buf + i, l);
2846
                        fputs(outbuf, fp);
2847
                        fputc('\n', fp);
2848
                }
2849
        } else if (encoding == ENC_QUOTED_PRINTABLE) {
2850
                gchar *outbuf;
2851
                size_t outlen;
2852

    
2853
                outbuf = g_malloc(len * 4);
2854
                qp_encode_line(outbuf, buf);
2855
                outlen = strlen(outbuf);
2856
                if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
2857
                        FILE_OP_ERROR(file, "fwrite");
2858
                        fclose(fp);
2859
                        unlink(file);
2860
                        g_free(outbuf);
2861
                        g_free(buf);
2862
                        return -1;
2863
                }
2864
                g_free(outbuf);
2865
        } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
2866
                FILE_OP_ERROR(file, "fwrite");
2867
                fclose(fp);
2868
                unlink(file);
2869
                g_free(buf);
2870
                return -1;
2871
        }
2872
        g_free(buf);
2873

    
2874
        if (compose->use_attach &&
2875
            GTK_CLIST(compose->attach_clist)->row_list)
2876
                compose_write_attach(compose, fp);
2877

    
2878
        if (fclose(fp) == EOF) {
2879
                FILE_OP_ERROR(file, "fclose");
2880
                unlink(file);
2881
                return -1;
2882
        }
2883

    
2884
#if USE_GPGME
2885
        if (is_draft) {
2886
                uncanonicalize_file_replace(file);
2887
                return 0;
2888
        }
2889

    
2890
        if ((compose->use_signing && !compose->account->clearsign) ||
2891
            compose->use_encryption) {
2892
                if (canonicalize_file_replace(file) < 0) {
2893
                        unlink(file);
2894
                        return -1;
2895
                }
2896
        }
2897

    
2898
        if (compose->use_signing && !compose->account->clearsign) {
2899
                GSList *key_list;
2900

    
2901
                if (compose_create_signers_list(compose, &key_list) < 0 ||
2902
                    rfc2015_sign(file, key_list) < 0) {
2903
                        unlink(file);
2904
                        return -1;
2905
                }
2906
        }
2907
        if (compose->use_encryption) {
2908
                if (rfc2015_encrypt(file, compose->to_list,
2909
                                    compose->account->ascii_armored) < 0) {
2910
                        unlink(file);
2911
                        return -1;
2912
                }
2913
        }
2914
#endif /* USE_GPGME */
2915

    
2916
        uncanonicalize_file_replace(file);
2917

    
2918
        return 0;
2919
}
2920

    
2921
static gint compose_write_body_to_file(Compose *compose, const gchar *file)
2922
{
2923
        GtkTextBuffer *buffer;
2924
        GtkTextIter start, end;
2925
        FILE *fp;
2926
        size_t len;
2927
        gchar *chars, *tmp;
2928

    
2929
        if ((fp = fopen(file, "wb")) == NULL) {
2930
                FILE_OP_ERROR(file, "fopen");
2931
                return -1;
2932
        }
2933

    
2934
        /* chmod for security */
2935
        if (change_file_mode_rw(fp, file) < 0) {
2936
                FILE_OP_ERROR(file, "chmod");
2937
                g_warning(_("can't change file mode\n"));
2938
        }
2939

    
2940
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2941
        gtk_text_buffer_get_start_iter(buffer, &start);
2942
        gtk_text_buffer_get_end_iter(buffer, &end);
2943
        tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2944

    
2945
        chars = conv_codeset_strdup
2946
                (tmp, CS_UTF_8, conv_get_locale_charset_str());
2947

    
2948
        g_free(tmp);
2949

    
2950
        if (!chars) {
2951
                fclose(fp);
2952
                unlink(file);
2953
                return -1;
2954
        }
2955

    
2956
        /* write body */
2957
        len = strlen(chars);
2958
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
2959
                FILE_OP_ERROR(file, "fwrite");
2960
                g_free(chars);
2961
                fclose(fp);
2962
                unlink(file);
2963
                return -1;
2964
        }
2965

    
2966
        g_free(chars);
2967

    
2968
        if (fclose(fp) == EOF) {
2969
                FILE_OP_ERROR(file, "fclose");
2970
                unlink(file);
2971
                return -1;
2972
        }
2973
        return 0;
2974
}
2975

    
2976
static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
2977
{
2978
        FILE *fp;
2979
        FILE *fdest;
2980
        size_t len;
2981
        gchar buf[BUFFSIZE];
2982

    
2983
        g_return_val_if_fail(file != NULL, -1);
2984
        g_return_val_if_fail(compose->account != NULL, -1);
2985
        g_return_val_if_fail(compose->account->address != NULL, -1);
2986
        g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
2987
        g_return_val_if_fail(compose->targetinfo != NULL, -1);
2988

    
2989
        if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
2990
                return -1;
2991

    
2992
        if ((fdest = fopen(file, "wb")) == NULL) {
2993
                FILE_OP_ERROR(file, "fopen");
2994
                fclose(fp);
2995
                return -1;
2996
        }
2997

    
2998
        if (change_file_mode_rw(fdest, file) < 0) {
2999
                FILE_OP_ERROR(file, "chmod");
3000
                g_warning(_("can't change file mode\n"));
3001
        }
3002

    
3003
        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
3004
                if (g_strncasecmp(buf, "Return-Path:",
3005
                                   strlen("Return-Path:")) == 0 ||
3006
                    g_strncasecmp(buf, "Delivered-To:",
3007
                                  strlen("Delivered-To:")) == 0 ||
3008
                    g_strncasecmp(buf, "Received:",
3009
                                  strlen("Received:")) == 0 ||
3010
                    g_strncasecmp(buf, "Subject:",
3011
                                  strlen("Subject:")) == 0 ||
3012
                    g_strncasecmp(buf, "X-UIDL:",
3013
                                  strlen("X-UIDL:")) == 0)
3014
                        continue;
3015

    
3016
                if (fputs(buf, fdest) == EOF)
3017
                        goto error;
3018

    
3019
#if 0
3020
                if (g_strncasecmp(buf, "From:", strlen("From:")) == 0) {
3021
                        fputs("\n (by way of ", fdest);
3022
                        if (compose->account->name) {
3023
                                compose_convert_header(buf, sizeof(buf),
3024
                                                       compose->account->name,
3025
                                                       strlen(" (by way of "),
3026
                                                       FALSE);
3027
                                fprintf(fdest, "%s <%s>", buf,
3028
                                        compose->account->address);
3029
                        } else
3030
                                fputs(compose->account->address, fdest);
3031
                        fputs(")", fdest);
3032
                }
3033
#endif
3034

    
3035
                if (fputs("\n", fdest) == EOF)
3036
                        goto error;
3037
        }
3038

    
3039
        compose_redirect_write_headers(compose, fdest);
3040

    
3041
        while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3042
                if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
3043
                        FILE_OP_ERROR(file, "fwrite");
3044
                        goto error;
3045
                }
3046
        }
3047

    
3048
        fclose(fp);
3049
        if (fclose(fdest) == EOF) {
3050
                FILE_OP_ERROR(file, "fclose");
3051
                unlink(file);
3052
                return -1;
3053
        }
3054

    
3055
        return 0;
3056
error:
3057
        fclose(fp);
3058
        fclose(fdest);
3059
        unlink(file);
3060

    
3061
        return -1;
3062
}
3063

    
3064
static gint compose_remove_reedit_target(Compose *compose)
3065
{
3066
        FolderItem *item;
3067
        MsgInfo *msginfo = compose->targetinfo;
3068

    
3069
        g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
3070
        if (!msginfo) return -1;
3071

    
3072
        item = msginfo->folder;
3073
        g_return_val_if_fail(item != NULL, -1);
3074

    
3075
        folder_item_scan(item);
3076
        if (procmsg_msg_exist(msginfo) &&
3077
            (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
3078
                if (folder_item_remove_msg(item, msginfo) < 0) {
3079
                        g_warning(_("can't remove the old message\n"));
3080
                        return -1;
3081
                }
3082
        }
3083

    
3084
        return 0;
3085
}
3086

    
3087
static gint compose_queue(Compose *compose, const gchar *file)
3088
{
3089
        FolderItem *queue;
3090
        gchar *tmp;
3091
        FILE *fp, *src_fp;
3092
        GSList *cur;
3093
        gchar buf[BUFFSIZE];
3094
        gint num;
3095
        MsgFlags flag = {0, 0};
3096

    
3097
        debug_print(_("queueing message...\n"));
3098
        g_return_val_if_fail(compose->to_list != NULL ||
3099
                             compose->newsgroup_list != NULL,
3100
                             -1);
3101
        g_return_val_if_fail(compose->account != NULL, -1);
3102

    
3103
        tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
3104
                              G_DIR_SEPARATOR, compose);
3105
        if ((fp = fopen(tmp, "wb")) == NULL) {
3106
                FILE_OP_ERROR(tmp, "fopen");
3107
                g_free(tmp);
3108
                return -1;
3109
        }
3110
        if ((src_fp = fopen(file, "rb")) == NULL) {
3111
                FILE_OP_ERROR(file, "fopen");
3112
                fclose(fp);
3113
                unlink(tmp);
3114
                g_free(tmp);
3115
                return -1;
3116
        }
3117
        if (change_file_mode_rw(fp, tmp) < 0) {
3118
                FILE_OP_ERROR(tmp, "chmod");
3119
                g_warning(_("can't change file mode\n"));
3120
        }
3121

    
3122
        /* queueing variables */
3123
        fprintf(fp, "AF:\n");
3124
        fprintf(fp, "NF:0\n");
3125
        fprintf(fp, "PS:10\n");
3126
        fprintf(fp, "SRH:1\n");
3127
        fprintf(fp, "SFN:\n");
3128
        fprintf(fp, "DSR:\n");
3129
        if (compose->msgid)
3130
                fprintf(fp, "MID:<%s>\n", compose->msgid);
3131
        else
3132
                fprintf(fp, "MID:\n");
3133
        fprintf(fp, "CFG:\n");
3134
        fprintf(fp, "PT:0\n");
3135
        fprintf(fp, "S:%s\n", compose->account->address);
3136
        fprintf(fp, "RQ:\n");
3137
        if (compose->account->smtp_server)
3138
                fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
3139
        else
3140
                fprintf(fp, "SSV:\n");
3141
        if (compose->account->nntp_server)
3142
                fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
3143
        else
3144
                fprintf(fp, "NSV:\n");
3145
        fprintf(fp, "SSH:\n");
3146
        if (compose->to_list) {
3147
                fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
3148
                for (cur = compose->to_list->next; cur != NULL;
3149
                     cur = cur->next)
3150
                        fprintf(fp, ",<%s>", (gchar *)cur->data);
3151
                fprintf(fp, "\n");
3152
        } else
3153
                fprintf(fp, "R:\n");
3154
        /* Sylpheed account ID */
3155
        fprintf(fp, "AID:%d\n", compose->account->account_id);
3156
        fprintf(fp, "\n");
3157

    
3158
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3159
                if (fputs(buf, fp) == EOF) {
3160
                        FILE_OP_ERROR(tmp, "fputs");
3161
                        fclose(fp);
3162
                        fclose(src_fp);
3163
                        unlink(tmp);
3164
                        g_free(tmp);
3165
                        return -1;
3166
                }
3167
        }
3168

    
3169
        fclose(src_fp);
3170
        if (fclose(fp) == EOF) {
3171
                FILE_OP_ERROR(tmp, "fclose");
3172
                unlink(tmp);
3173
                g_free(tmp);
3174
                return -1;
3175
        }
3176

    
3177
        queue = account_get_special_folder(compose->account, F_QUEUE);
3178
        if (!queue) {
3179
                g_warning(_("can't find queue folder\n"));
3180
                unlink(tmp);
3181
                g_free(tmp);
3182
                return -1;
3183
        }
3184
        folder_item_scan(queue);
3185
        if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
3186
                g_warning(_("can't queue the message\n"));
3187
                unlink(tmp);
3188
                g_free(tmp);
3189
                return -1;
3190
        }
3191
        g_free(tmp);
3192

    
3193
        if (compose->mode == COMPOSE_REEDIT) {
3194
                compose_remove_reedit_target(compose);
3195
                if (compose->targetinfo &&
3196
                    compose->targetinfo->folder != queue)
3197
                        folderview_update_item
3198
                                (compose->targetinfo->folder, TRUE);
3199
        }
3200

    
3201
        folder_item_scan(queue);
3202
        folderview_update_item(queue, TRUE);
3203

    
3204
        return 0;
3205
}
3206

    
3207
static void compose_write_attach(Compose *compose, FILE *fp)
3208
{
3209
        AttachInfo *ainfo;
3210
        GtkCList *clist = GTK_CLIST(compose->attach_clist);
3211
        gint row;
3212
        FILE *attach_fp;
3213
        gchar filename[BUFFSIZE];
3214
        gint len;
3215

    
3216
        for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
3217
             row++) {
3218
                if ((attach_fp = fopen(ainfo->file, "rb")) == NULL) {
3219
                        g_warning("Can't open file %s\n", ainfo->file);
3220
                        continue;
3221
                }
3222

    
3223
                fprintf(fp, "\n--%s\n", compose->boundary);
3224

    
3225
                if (!g_strcasecmp(ainfo->content_type, "message/rfc822")) {
3226
                        fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
3227
                        fprintf(fp, "Content-Disposition: inline\n");
3228
                } else {
3229
                        compose_convert_header(filename, sizeof(filename),
3230
                                               ainfo->name, 12, FALSE);
3231
                        fprintf(fp, "Content-Type: %s;\n"
3232
                                    " name=\"%s\"\n",
3233
                                ainfo->content_type, filename);
3234
                        fprintf(fp, "Content-Disposition: attachment;\n"
3235
                                    " filename=\"%s\"\n", filename);
3236
                }
3237

    
3238
                fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
3239
                        procmime_get_encoding_str(ainfo->encoding));
3240

    
3241
                if (ainfo->encoding == ENC_BASE64) {
3242
                        gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
3243
                        FILE *tmp_fp = attach_fp;
3244
                        gchar *tmp_file = NULL;
3245
                        ContentType content_type;
3246

    
3247
                        content_type =
3248
                                procmime_scan_mime_type(ainfo->content_type);
3249
                        if (content_type == MIME_TEXT ||
3250
                            content_type == MIME_TEXT_HTML ||
3251
                            content_type == MIME_MESSAGE_RFC822) {
3252
                                tmp_file = get_tmp_file();
3253
                                if (canonicalize_file(ainfo->file, tmp_file) < 0) {
3254
                                        g_free(tmp_file);
3255
                                        fclose(attach_fp);
3256
                                        continue;
3257
                                }
3258
                                if ((tmp_fp = fopen(tmp_file, "rb")) == NULL) {
3259
                                        FILE_OP_ERROR(tmp_file, "fopen");
3260
                                        unlink(tmp_file);
3261
                                        g_free(tmp_file);
3262
                                        fclose(attach_fp);
3263
                                        continue;
3264
                                }
3265
                        }
3266

    
3267
                        while ((len = fread(inbuf, sizeof(gchar),
3268
                                            B64_LINE_SIZE, tmp_fp))
3269
                               == B64_LINE_SIZE) {
3270
                                base64_encode(outbuf, inbuf, B64_LINE_SIZE);
3271
                                fputs(outbuf, fp);
3272
                                fputc('\n', fp);
3273
                        }
3274
                        if (len > 0 && feof(tmp_fp)) {
3275
                                base64_encode(outbuf, inbuf, len);
3276
                                fputs(outbuf, fp);
3277
                                fputc('\n', fp);
3278
                        }
3279

    
3280
                        if (tmp_file) {
3281
                                fclose(tmp_fp);
3282
                                unlink(tmp_file);
3283
                                g_free(tmp_file);
3284
                        }
3285
                } else if (ainfo->encoding == ENC_QUOTED_PRINTABLE) {
3286
                        gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
3287

    
3288
                        while (fgets(inbuf, sizeof(inbuf), attach_fp) != NULL) {
3289
                                qp_encode_line(outbuf, inbuf);
3290
                                fputs(outbuf, fp);
3291
                        }
3292
                } else {
3293
                        gchar buf[BUFFSIZE];
3294

    
3295
                        while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
3296
                                strcrchomp(buf);
3297
                                fputs(buf, fp);
3298
                        }
3299
                }
3300

    
3301
                fclose(attach_fp);
3302
        }
3303

    
3304
        fprintf(fp, "\n--%s--\n", compose->boundary);
3305
}
3306

    
3307
#define QUOTE_IF_REQUIRED(out, str)                        \
3308
{                                                        \
3309
        if (*str != '"' && strpbrk(str, ",.[]<>")) {        \
3310
                gchar *__tmp;                                \
3311
                gint len;                                \
3312
                                                        \
3313
                len = strlen(str) + 3;                        \
3314
                Xalloca(__tmp, len, return -1);                \
3315
                g_snprintf(__tmp, len, "\"%s\"", str);        \
3316
                out = __tmp;                                \
3317
        } else {                                        \
3318
                Xstrdup_a(out, str, return -1);                \
3319
        }                                                \
3320
}
3321

    
3322
#define PUT_RECIPIENT_HEADER(header, str)                                     \
3323
{                                                                             \
3324
        if (*str != '\0') {                                                     \
3325
                gchar *dest;                                                     \
3326
                                                                             \
3327
                Xstrdup_a(dest, str, return -1);                             \
3328
                g_strstrip(dest);                                             \
3329
                if (*dest != '\0') {                                             \
3330
                        compose->to_list = address_list_append                     \
3331
                                (compose->to_list, dest);                     \
3332
                        compose_convert_header                                     \
3333
                                (buf, sizeof(buf), dest, strlen(header) + 2, \
3334
                                 TRUE);                                             \
3335
                        fprintf(fp, "%s: %s\n", header, buf);                     \
3336
                }                                                             \
3337
        }                                                                     \
3338
}
3339

    
3340
#define IS_IN_CUSTOM_HEADER(header) \
3341
        (compose->account->add_customhdr && \
3342
         custom_header_find(compose->account->customhdr_list, header) != NULL)
3343

    
3344
static gint compose_write_headers(Compose *compose, FILE *fp,
3345
                                  const gchar *charset, EncodingType encoding,
3346
                                  gboolean is_draft)
3347
{
3348
        gchar buf[BUFFSIZE];
3349
        const gchar *entry_str;
3350
        gchar *str;
3351
        gchar *name;
3352
        /* struct utsname utsbuf; */
3353

    
3354
        g_return_val_if_fail(fp != NULL, -1);
3355
        g_return_val_if_fail(charset != NULL, -1);
3356
        g_return_val_if_fail(compose->account != NULL, -1);
3357
        g_return_val_if_fail(compose->account->address != NULL, -1);
3358

    
3359
        /* Date */
3360
        if (compose->account->add_date) {
3361
                get_rfc822_date(buf, sizeof(buf));
3362
                fprintf(fp, "Date: %s\n", buf);
3363
        }
3364

    
3365
        /* From */
3366
        if (compose->account->name && *compose->account->name) {
3367
                compose_convert_header
3368
                        (buf, sizeof(buf), compose->account->name,
3369
                         strlen("From: "), TRUE);
3370
                QUOTE_IF_REQUIRED(name, buf);
3371
                fprintf(fp, "From: %s <%s>\n",
3372
                        name, compose->account->address);
3373
        } else
3374
                fprintf(fp, "From: %s\n", compose->account->address);
3375

    
3376
        slist_free_strings(compose->to_list);
3377
        g_slist_free(compose->to_list);
3378
        compose->to_list = NULL;
3379

    
3380
        /* To */
3381
        if (compose->use_to) {
3382
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3383
                PUT_RECIPIENT_HEADER("To", entry_str);
3384
        }
3385

    
3386
        slist_free_strings(compose->newsgroup_list);
3387
        g_slist_free(compose->newsgroup_list);
3388
        compose->newsgroup_list = NULL;
3389

    
3390
        /* Newsgroups */
3391
        if (compose->use_newsgroups) {
3392
                entry_str = gtk_entry_get_text
3393
                        (GTK_ENTRY(compose->newsgroups_entry));
3394
                if (*entry_str != '\0') {
3395
                        Xstrdup_a(str, entry_str, return -1);
3396
                        g_strstrip(str);
3397
                        remove_space(str);
3398
                        if (*str != '\0') {
3399
                                compose->newsgroup_list =
3400
                                        newsgroup_list_append
3401
                                                (compose->newsgroup_list, str);
3402
                                compose_convert_header(buf, sizeof(buf), str,
3403
                                                       strlen("Newsgroups: "),
3404
                                                       TRUE);
3405
                                fprintf(fp, "Newsgroups: %s\n", buf);
3406
                        }
3407
                }
3408
        }
3409

    
3410
        /* Cc */
3411
        if (compose->use_cc) {
3412
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3413
                PUT_RECIPIENT_HEADER("Cc", entry_str);
3414
        }
3415

    
3416
        /* Bcc */
3417
        if (compose->use_bcc) {
3418
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3419
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
3420
        }
3421

    
3422
        if (!is_draft && !compose->to_list && !compose->newsgroup_list)
3423
                return -1;
3424

    
3425
        /* Subject */
3426
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3427
        if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
3428
                Xstrdup_a(str, entry_str, return -1);
3429
                g_strstrip(str);
3430
                if (*str != '\0') {
3431
                        compose_convert_header(buf, sizeof(buf), str,
3432
                                               strlen("Subject: "), FALSE);
3433
                        fprintf(fp, "Subject: %s\n", buf);
3434
                }
3435
        }
3436

    
3437
        /* Message-ID */
3438
        if (compose->account->gen_msgid) {
3439
                compose_generate_msgid(compose, buf, sizeof(buf));
3440
                fprintf(fp, "Message-Id: <%s>\n", buf);
3441
                compose->msgid = g_strdup(buf);
3442
        }
3443

    
3444
        /* In-Reply-To */
3445
        if (compose->inreplyto && compose->to_list)
3446
                fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
3447

    
3448
        /* References */
3449
        if (compose->references)
3450
                fprintf(fp, "References: %s\n", compose->references);
3451

    
3452
        /* Followup-To */
3453
        if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
3454
                entry_str = gtk_entry_get_text
3455
                        (GTK_ENTRY(compose->followup_entry));
3456
                if (*entry_str != '\0') {
3457
                        Xstrdup_a(str, entry_str, return -1);
3458
                        g_strstrip(str);
3459
                        remove_space(str);
3460
                        if (*str != '\0') {
3461
                                compose_convert_header(buf, sizeof(buf), str,
3462
                                                       strlen("Followup-To: "),
3463
                                                       TRUE);
3464
                                fprintf(fp, "Followup-To: %s\n", buf);
3465
                        }
3466
                }
3467
        }
3468

    
3469
        /* Reply-To */
3470
        if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
3471
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
3472
                if (*entry_str != '\0') {
3473
                        Xstrdup_a(str, entry_str, return -1);
3474
                        g_strstrip(str);
3475
                        if (*str != '\0') {
3476
                                compose_convert_header(buf, sizeof(buf), str,
3477
                                                       strlen("Reply-To: "),
3478
                                                       TRUE);
3479
                                fprintf(fp, "Reply-To: %s\n", buf);
3480
                        }
3481
                }
3482
        }
3483

    
3484
        /* Organization */
3485
        if (compose->account->organization &&
3486
            !IS_IN_CUSTOM_HEADER("Organization")) {
3487
                compose_convert_header(buf, sizeof(buf),
3488
                                       compose->account->organization,
3489
                                       strlen("Organization: "), FALSE);
3490
                fprintf(fp, "Organization: %s\n", buf);
3491
        }
3492

    
3493
        /* Program version and system info */
3494
        /* uname(&utsbuf); */
3495
        if (compose->to_list && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
3496
                fprintf(fp, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
3497
                        prog_version,
3498
                        gtk_major_version, gtk_minor_version, gtk_micro_version,
3499
                        TARGET_ALIAS);
3500
                        /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
3501
        }
3502
        if (compose->newsgroup_list && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
3503
                fprintf(fp, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
3504
                        prog_version,
3505
                        gtk_major_version, gtk_minor_version, gtk_micro_version,
3506
                        TARGET_ALIAS);
3507
                        /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
3508
        }
3509

    
3510
        /* custom headers */
3511
        if (compose->account->add_customhdr) {
3512
                GSList *cur;
3513

    
3514
                for (cur = compose->account->customhdr_list; cur != NULL;
3515
                     cur = cur->next) {
3516
                        CustomHeader *chdr = (CustomHeader *)cur->data;
3517

    
3518
                        if (strcasecmp(chdr->name, "Date")         != 0 &&
3519
                            strcasecmp(chdr->name, "From")         != 0 &&
3520
                            strcasecmp(chdr->name, "To")           != 0 &&
3521
                         /* strcasecmp(chdr->name, "Sender")       != 0 && */
3522
                            strcasecmp(chdr->name, "Message-Id")   != 0 &&
3523
                            strcasecmp(chdr->name, "In-Reply-To")  != 0 &&
3524
                            strcasecmp(chdr->name, "References")   != 0 &&
3525
                            strcasecmp(chdr->name, "Mime-Version") != 0 &&
3526
                            strcasecmp(chdr->name, "Content-Type") != 0 &&
3527
                            strcasecmp(chdr->name, "Content-Transfer-Encoding")
3528
                            != 0) {
3529
                                compose_convert_header
3530
                                        (buf, sizeof(buf),
3531
                                         chdr->value ? chdr->value : "",
3532
                                         strlen(chdr->name) + 2, FALSE);
3533
                                fprintf(fp, "%s: %s\n", chdr->name, buf);
3534
                        }
3535
                }
3536
        }
3537

    
3538
        /* MIME */
3539
        fprintf(fp, "Mime-Version: 1.0\n");
3540
        if (compose->use_attach &&
3541
            GTK_CLIST(compose->attach_clist)->row_list) {
3542
                compose->boundary = generate_mime_boundary(NULL);
3543
                fprintf(fp,
3544
                        "Content-Type: multipart/mixed;\n"
3545
                        " boundary=\"%s\"\n", compose->boundary);
3546
        } else {
3547
                fprintf(fp, "Content-Type: text/plain; charset=%s\n", charset);
3548
#if USE_GPGME
3549
                if (compose->use_signing && !compose->account->clearsign)
3550
                        fprintf(fp, "Content-Disposition: inline\n");
3551
#endif
3552
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
3553
                        procmime_get_encoding_str(encoding));
3554
        }
3555

    
3556
        /* X-Sylpheed header */
3557
        if (is_draft)
3558
                fprintf(fp, "X-Sylpheed-Account-Id: %d\n",
3559
                        compose->account->account_id);
3560

    
3561
        /* separator between header and body */
3562
        fputs("\n", fp);
3563

    
3564
        return 0;
3565
}
3566

    
3567
static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
3568
{
3569
        gchar buf[BUFFSIZE];
3570
        const gchar *entry_str;
3571
        gchar *str;
3572

    
3573
        g_return_val_if_fail(fp != NULL, -1);
3574
        g_return_val_if_fail(compose->account != NULL, -1);
3575
        g_return_val_if_fail(compose->account->address != NULL, -1);
3576

    
3577
        /* Resent-Date */
3578
        get_rfc822_date(buf, sizeof(buf));
3579
        fprintf(fp, "Resent-Date: %s\n", buf);
3580

    
3581
        /* Resent-From */
3582
        if (compose->account->name) {
3583
                compose_convert_header
3584
                        (buf, sizeof(buf), compose->account->name,
3585
                         strlen("Resent-From: "), TRUE);
3586
                fprintf(fp, "Resent-From: %s <%s>\n",
3587
                        buf, compose->account->address);
3588
        } else
3589
                fprintf(fp, "Resent-From: %s\n", compose->account->address);
3590

    
3591
        slist_free_strings(compose->to_list);
3592
        g_slist_free(compose->to_list);
3593
        compose->to_list = NULL;
3594

    
3595
        /* Resent-To */
3596
        if (compose->use_to) {
3597
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3598
                PUT_RECIPIENT_HEADER("Resent-To", entry_str);
3599
        }
3600
        if (compose->use_cc) {
3601
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3602
                PUT_RECIPIENT_HEADER("Resent-Cc", entry_str);
3603
        }
3604
        if (compose->use_bcc) {
3605
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3606
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
3607
        }
3608

    
3609
        slist_free_strings(compose->newsgroup_list);
3610
        g_slist_free(compose->newsgroup_list);
3611
        compose->newsgroup_list = NULL;
3612

    
3613
        /* Newsgroups */
3614
        if (compose->use_newsgroups) {
3615
                entry_str = gtk_entry_get_text
3616
                        (GTK_ENTRY(compose->newsgroups_entry));
3617
                if (*entry_str != '\0') {
3618
                        Xstrdup_a(str, entry_str, return -1);
3619
                        g_strstrip(str);
3620
                        remove_space(str);
3621
                        if (*str != '\0') {
3622
                                compose->newsgroup_list =
3623
                                        newsgroup_list_append
3624
                                                (compose->newsgroup_list, str);
3625
                                compose_convert_header(buf, sizeof(buf), str,
3626
                                                       strlen("Newsgroups: "),
3627
                                                       TRUE);
3628
                                fprintf(fp, "Newsgroups: %s\n", buf);
3629
                        }
3630
                }
3631
        }
3632

    
3633
        if (!compose->to_list && !compose->newsgroup_list)
3634
                return -1;
3635

    
3636
        /* Subject */
3637
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3638
        if (*entry_str != '\0') {
3639
                Xstrdup_a(str, entry_str, return -1);
3640
                g_strstrip(str);
3641
                if (*str != '\0') {
3642
                        compose_convert_header(buf, sizeof(buf), str,
3643
                                               strlen("Subject: "), FALSE);
3644
                        fprintf(fp, "Subject: %s\n", buf);
3645
                }
3646
        }
3647

    
3648
        /* Resent-Message-Id */
3649
        if (compose->account->gen_msgid) {
3650
                compose_generate_msgid(compose, buf, sizeof(buf));
3651
                fprintf(fp, "Resent-Message-Id: <%s>\n", buf);
3652
                compose->msgid = g_strdup(buf);
3653
        }
3654

    
3655
        /* Followup-To */
3656
        if (compose->use_followupto) {
3657
                entry_str = gtk_entry_get_text
3658
                        (GTK_ENTRY(compose->followup_entry));
3659
                if (*entry_str != '\0') {
3660
                        Xstrdup_a(str, entry_str, return -1);
3661
                        g_strstrip(str);
3662
                        remove_space(str);
3663
                        if (*str != '\0') {
3664
                                compose_convert_header(buf, sizeof(buf), str,
3665
                                                       strlen("Followup-To: "),
3666
                                                       TRUE);
3667
                                fprintf(fp, "Followup-To: %s\n", buf);
3668
                        }
3669
                }
3670
        }
3671

    
3672
        /* Resent-Reply-To */
3673
        if (compose->use_replyto) {
3674
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
3675
                if (*entry_str != '\0') {
3676
                        Xstrdup_a(str, entry_str, return -1);
3677
                        g_strstrip(str);
3678
                        if (*str != '\0') {
3679
                                compose_convert_header
3680
                                        (buf, sizeof(buf), str,
3681
                                         strlen("Resent-Reply-To: "), TRUE);
3682
                                fprintf(fp, "Resent-Reply-To: %s\n", buf);
3683
                        }
3684
                }
3685
        }
3686

    
3687
        fputs("\n", fp);
3688

    
3689
        return 0;
3690
}
3691

    
3692
#undef IS_IN_CUSTOM_HEADER
3693

    
3694
static void compose_convert_header(gchar *dest, gint len, gchar *src,
3695
                                   gint header_len, gboolean addr_field)
3696
{
3697
        gchar *str;
3698
        const gchar *cur_encoding;
3699

    
3700
        g_return_if_fail(src != NULL);
3701
        g_return_if_fail(dest != NULL);
3702

    
3703
        if (len < 1) return;
3704

    
3705
        g_strchomp(src);
3706

    
3707
#warning FIXME_GTK2 redundant code conversion
3708
        cur_encoding = conv_get_locale_charset_str();
3709
        if (!strcmp(cur_encoding, CS_US_ASCII))
3710
                cur_encoding = CS_ISO_8859_1;
3711
        str = conv_codeset_strdup(src, CS_UTF_8, cur_encoding);
3712
        if (str)
3713
                conv_encode_header(dest, len, str, header_len, addr_field);
3714
        g_free(str);
3715
}
3716

    
3717
static void compose_generate_msgid(Compose *compose, gchar *buf, gint len)
3718
{
3719
        struct tm *lt;
3720
        time_t t;
3721
        gchar *addr;
3722

    
3723
        t = time(NULL);
3724
        lt = localtime(&t);
3725

    
3726
        if (compose->account && compose->account->address &&
3727
            *compose->account->address) {
3728
                if (strchr(compose->account->address, '@'))
3729
                        addr = g_strdup(compose->account->address);
3730
                else
3731
                        addr = g_strconcat(compose->account->address, "@",
3732
                                           get_domain_name(), NULL);
3733
        } else
3734
                addr = g_strconcat(g_get_user_name(), "@", get_domain_name(),
3735
                                   NULL);
3736

    
3737
        g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x.%s",
3738
                   lt->tm_year + 1900, lt->tm_mon + 1,
3739
                   lt->tm_mday, lt->tm_hour,
3740
                   lt->tm_min, lt->tm_sec,
3741
                   (guint)random(), addr);
3742

    
3743
        debug_print(_("generated Message-ID: %s\n"), buf);
3744

    
3745
        g_free(addr);
3746
}
3747

    
3748
static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox,
3749
                                    GtkWidget **entry, gint *count,
3750
                                    const gchar *label_str,
3751
                                    gboolean is_addr_entry)
3752
{
3753
        GtkWidget *label;
3754

    
3755
        if (GTK_TABLE(table)->nrows < (*count) + 1)
3756
                gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2);
3757

    
3758
        *hbox = gtk_hbox_new(FALSE, 0);
3759
        label = gtk_label_new
3760
                (prefs_common.trans_hdr ? gettext(label_str) : label_str);
3761
        gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0);
3762
        gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1,
3763
                         GTK_FILL, 0, 2, 0);
3764
        *entry = gtk_entry_new();
3765
        gtk_entry_set_max_length(GTK_ENTRY(*entry), MAX_ENTRY_LENGTH);
3766
        gtk_table_attach_defaults
3767
                (GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1);
3768
        if (GTK_TABLE(table)->nrows > (*count) + 1)
3769
                gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4);
3770

    
3771
        if (is_addr_entry)
3772
                address_completion_register_entry(GTK_ENTRY(*entry));
3773

    
3774
        (*count)++;
3775
}
3776

    
3777
static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
3778
{
3779
        Compose   *compose;
3780
        GtkWidget *window;
3781
        GtkWidget *vbox;
3782
        GtkWidget *menubar;
3783
        GtkWidget *handlebox;
3784

    
3785
        GtkWidget *vbox2;
3786

    
3787
        GtkWidget *table_vbox;
3788
        GtkWidget *label;
3789
        GtkWidget *from_optmenu_hbox;
3790
        GtkWidget *to_entry;
3791
        GtkWidget *to_hbox;
3792
        GtkWidget *newsgroups_entry;
3793
        GtkWidget *newsgroups_hbox;
3794
        GtkWidget *subject_entry;
3795
        GtkWidget *cc_entry;
3796
        GtkWidget *cc_hbox;
3797
        GtkWidget *bcc_entry;
3798
        GtkWidget *bcc_hbox;
3799
        GtkWidget *reply_entry;
3800
        GtkWidget *reply_hbox;
3801
        GtkWidget *followup_entry;
3802
        GtkWidget *followup_hbox;
3803

    
3804
        GtkWidget *paned;
3805

    
3806
        GtkWidget *attach_scrwin;
3807
        GtkWidget *attach_clist;
3808

    
3809
        GtkWidget *edit_vbox;
3810
        GtkWidget *ruler_hbox;
3811
        GtkWidget *ruler;
3812
        GtkWidget *scrolledwin;
3813
        GtkWidget *text;
3814

    
3815
        GtkTextBuffer *buffer;
3816
        GtkClipboard *clipboard;
3817

    
3818
        GtkWidget *table;
3819
        GtkWidget *hbox;
3820

    
3821
        UndoMain *undostruct;
3822

    
3823
        gchar *titles[N_ATTACH_COLS];
3824
        guint n_menu_entries;
3825
        GtkStyle  *style, *new_style;
3826
        GdkColormap *cmap;
3827
        GdkColor color[1];
3828
        gboolean success[1];
3829
        GtkWidget *popupmenu;
3830
        GtkItemFactory *popupfactory;
3831
        GtkItemFactory *ifactory;
3832
        GtkWidget *tmpl_menu;
3833
        gint n_entries;
3834
        gint count = 0;
3835
        gint i;
3836

    
3837
        static GdkGeometry geometry;
3838

    
3839
        g_return_val_if_fail(account != NULL, NULL);
3840

    
3841
        debug_print(_("Creating compose window...\n"));
3842
        compose = g_new0(Compose, 1);
3843

    
3844
        titles[COL_MIMETYPE] = _("MIME type");
3845
        titles[COL_SIZE]     = _("Size");
3846
        titles[COL_NAME]     = _("Name");
3847

    
3848
        compose->account = account;
3849

    
3850
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3851
        gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
3852
        gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
3853
        gtk_window_set_wmclass(GTK_WINDOW(window), "compose", "Sylpheed");
3854

    
3855
        if (!geometry.max_width) {
3856
                geometry.max_width = gdk_screen_width();
3857
                geometry.max_height = gdk_screen_height();
3858
        }
3859
        gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
3860
                                      &geometry, GDK_HINT_MAX_SIZE);
3861

    
3862
        g_signal_connect(G_OBJECT(window), "delete_event",
3863
                         G_CALLBACK(compose_delete_cb), compose);
3864
        g_signal_connect(G_OBJECT(window), "destroy",
3865
                         G_CALLBACK(compose_destroy_cb), compose);
3866
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
3867
        gtk_widget_realize(window);
3868

    
3869
        vbox = gtk_vbox_new(FALSE, 0);
3870
        gtk_container_add(GTK_CONTAINER(window), vbox);
3871

    
3872
        n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
3873
        menubar = menubar_create(window, compose_entries,
3874
                                 n_menu_entries, "<Compose>", compose);
3875
        gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
3876

    
3877
        handlebox = gtk_handle_box_new();
3878
        gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
3879

    
3880
        compose_toolbar_create(compose, handlebox);
3881

    
3882
        vbox2 = gtk_vbox_new(FALSE, 2);
3883
        gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
3884
        gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
3885

    
3886
        table_vbox = gtk_vbox_new(FALSE, 0);
3887
        gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0);
3888
        gtk_container_set_border_width(GTK_CONTAINER(table_vbox),
3889
                                       BORDER_WIDTH * 2);
3890

    
3891
        table = gtk_table_new(8, 2, FALSE);
3892
        gtk_box_pack_start(GTK_BOX(table_vbox), table, FALSE, TRUE, 0);
3893

    
3894
        /* option menu for selecting accounts */
3895
        hbox = gtk_hbox_new(FALSE, 0);
3896
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
3897
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3898
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, count, count + 1,
3899
                         GTK_FILL, 0, 2, 0);
3900
        from_optmenu_hbox = compose_account_option_menu_create(compose);
3901
        gtk_table_attach_defaults(GTK_TABLE(table), from_optmenu_hbox,
3902
                                  1, 2, count, count + 1);
3903
        gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
3904
        count++;
3905

    
3906
        /* header labels and entries */
3907
        compose_add_entry_field(table, &to_hbox, &to_entry, &count,
3908
                                "To:", TRUE); 
3909
        compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry,
3910
                                &count, "Newsgroups:", FALSE);
3911
        compose_add_entry_field(table, &cc_hbox, &cc_entry, &count,
3912
                                "Cc:", TRUE);
3913
        compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count,
3914
                                "Bcc:", TRUE);
3915
        compose_add_entry_field(table, &reply_hbox, &reply_entry, &count,
3916
                                "Reply-To:", TRUE);
3917
        compose_add_entry_field(table, &followup_hbox, &followup_entry, &count,
3918
                                "Followup-To:", FALSE);
3919
        compose_add_entry_field(table, &hbox, &subject_entry, &count,
3920
                                "Subject:", FALSE);
3921

    
3922
        gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3923

    
3924
        g_signal_connect(G_OBJECT(to_entry), "activate",
3925
                         G_CALLBACK(to_activated), compose);
3926
        g_signal_connect(G_OBJECT(newsgroups_entry), "activate",
3927
                         G_CALLBACK(newsgroups_activated), compose);
3928
        g_signal_connect(G_OBJECT(cc_entry), "activate",
3929
                         G_CALLBACK(cc_activated), compose);
3930
        g_signal_connect(G_OBJECT(bcc_entry), "activate",
3931
                         G_CALLBACK(bcc_activated), compose);
3932
        g_signal_connect(G_OBJECT(reply_entry), "activate",
3933
                         G_CALLBACK(replyto_activated), compose);
3934
        g_signal_connect(G_OBJECT(followup_entry), "activate",
3935
                         G_CALLBACK(followupto_activated), compose);
3936
        g_signal_connect(G_OBJECT(subject_entry), "activate",
3937
                         G_CALLBACK(subject_activated), compose);
3938

    
3939
        g_signal_connect(G_OBJECT(to_entry), "grab_focus",
3940
                         G_CALLBACK(compose_grab_focus_cb), compose);
3941
        g_signal_connect(G_OBJECT(newsgroups_entry), "grab_focus",
3942
                         G_CALLBACK(compose_grab_focus_cb), compose);
3943
        g_signal_connect(G_OBJECT(cc_entry), "grab_focus",
3944
                         G_CALLBACK(compose_grab_focus_cb), compose);
3945
        g_signal_connect(G_OBJECT(bcc_entry), "grab_focus",
3946
                         G_CALLBACK(compose_grab_focus_cb), compose);
3947
        g_signal_connect(G_OBJECT(reply_entry), "grab_focus",
3948
                         G_CALLBACK(compose_grab_focus_cb), compose);
3949
        g_signal_connect(G_OBJECT(followup_entry), "grab_focus",
3950
                         G_CALLBACK(compose_grab_focus_cb), compose);
3951
        g_signal_connect(G_OBJECT(subject_entry), "grab_focus",
3952
                         G_CALLBACK(compose_grab_focus_cb), compose);
3953

    
3954
        /* attachment list */
3955
        attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
3956
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
3957
                                       GTK_POLICY_AUTOMATIC,
3958
                                       GTK_POLICY_ALWAYS);
3959
        gtk_widget_set_size_request(attach_scrwin, -1, 80);
3960

    
3961
        attach_clist = gtk_clist_new_with_titles(N_ATTACH_COLS, titles);
3962
        gtk_clist_set_column_justification(GTK_CLIST(attach_clist), COL_SIZE,
3963
                                           GTK_JUSTIFY_RIGHT);
3964
        gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_MIMETYPE, 240);
3965
        gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_SIZE, 64);
3966
        gtk_clist_set_selection_mode(GTK_CLIST(attach_clist),
3967
                                     GTK_SELECTION_EXTENDED);
3968
        for (i = 0; i < N_ATTACH_COLS; i++)
3969
                GTK_WIDGET_UNSET_FLAGS
3970
                        (GTK_CLIST(attach_clist)->column[i].button,
3971
                         GTK_CAN_FOCUS);
3972
        gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
3973

    
3974
        g_signal_connect(G_OBJECT(attach_clist), "select_row",
3975
                         G_CALLBACK(attach_selected), compose);
3976
        g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
3977
                         G_CALLBACK(attach_button_pressed), compose);
3978
        g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
3979
                         G_CALLBACK(attach_key_pressed), compose);
3980

    
3981
        /* drag and drop */
3982
        gtk_drag_dest_set(attach_clist,
3983
                          GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
3984
                          GDK_ACTION_COPY | GDK_ACTION_MOVE);
3985
        g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
3986
                         G_CALLBACK(compose_attach_drag_received_cb),
3987
                         compose);
3988

    
3989
        /* pane between attach clist and text */
3990
        paned = gtk_vpaned_new();
3991
        gtk_paned_add1(GTK_PANED(paned), attach_scrwin);
3992
        gtk_widget_ref(paned);
3993
        gtk_widget_show_all(paned);
3994

    
3995
        edit_vbox = gtk_vbox_new(FALSE, 0);
3996
        gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0);
3997

    
3998
        /* ruler */
3999
        ruler_hbox = gtk_hbox_new(FALSE, 0);
4000
        gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
4001

    
4002
        ruler = gtk_shruler_new();
4003
        gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
4004
        gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
4005
                           BORDER_WIDTH + 1);
4006
        gtk_widget_set_size_request(ruler_hbox, 1, -1);
4007

    
4008
        /* text widget */
4009
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
4010
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
4011
                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4012
        gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
4013
        gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width,
4014
                                    -1);
4015

    
4016
        text = gtk_text_view_new();
4017
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
4018
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
4019
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
4020
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
4021
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
4022
        /* GTK_STEXT(text)->default_tab_width = 8; */
4023
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
4024

    
4025
        g_signal_connect(G_OBJECT(text), "grab_focus",
4026
                         G_CALLBACK(compose_grab_focus_cb), compose);
4027
#warning FIXME_GTK2
4028
#if 0
4029
        g_signal_connect(G_OBJECT(text), "activate",
4030
                         G_CALLBACK(text_activated), compose);
4031
#endif
4032
        g_signal_connect(G_OBJECT(text), "insert_text",
4033
                         G_CALLBACK(text_inserted), compose);
4034
        g_signal_connect_after(G_OBJECT(text), "size_allocate",
4035
                               G_CALLBACK(compose_edit_size_alloc),
4036
                               ruler);
4037

    
4038
        /* drag and drop */
4039
        gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
4040
                          GDK_ACTION_COPY | GDK_ACTION_MOVE);
4041
        g_signal_connect(G_OBJECT(text), "drag_data_received",
4042
                         G_CALLBACK(compose_insert_drag_received_cb),
4043
                         compose);
4044

    
4045
        gtk_widget_show_all(vbox);
4046

    
4047
        style = gtk_widget_get_style(text);
4048
        new_style = gtk_style_copy(style);
4049

    
4050
#warning FIXME_GTK2 use normal API for setting font
4051
        if (prefs_common.textfont) {
4052
                PangoFontDescription *font_desc;
4053

    
4054
                font_desc = pango_font_description_from_string
4055
                        (prefs_common.textfont);
4056
                if (font_desc) {
4057
                        if (new_style->font_desc)
4058
                                pango_font_description_free
4059
                                        (new_style->font_desc);
4060
                        new_style->font_desc = font_desc;
4061
                }
4062
        }
4063

    
4064
        gtk_widget_set_style(text, new_style);
4065

    
4066
        color[0] = quote_color;
4067
        cmap = gdk_window_get_colormap(window->window);
4068
        gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
4069
        if (success[0] == FALSE) {
4070
                g_warning("Compose: color allocation failed.\n");
4071
                style = gtk_widget_get_style(text);
4072
                quote_color = style->black;
4073
        }
4074

    
4075
        n_entries = sizeof(compose_popup_entries) /
4076
                sizeof(compose_popup_entries[0]);
4077
        popupmenu = menu_create_items(compose_popup_entries, n_entries,
4078
                                      "<Compose>", &popupfactory,
4079
                                      compose);
4080

    
4081
        ifactory = gtk_item_factory_from_widget(menubar);
4082
        menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
4083
        menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
4084

    
4085
        tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
4086

    
4087
        gtk_widget_hide(bcc_hbox);
4088
        gtk_widget_hide(bcc_entry);
4089
        gtk_widget_hide(reply_hbox);
4090
        gtk_widget_hide(reply_entry);
4091
        gtk_widget_hide(followup_hbox);
4092
        gtk_widget_hide(followup_entry);
4093
        gtk_widget_hide(ruler_hbox);
4094
        gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0);
4095
        gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0);
4096
        gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0);
4097

    
4098
        if (account->protocol == A_NNTP) {
4099
                gtk_widget_hide(to_hbox);
4100
                gtk_widget_hide(to_entry);
4101
                gtk_widget_hide(cc_hbox);
4102
                gtk_widget_hide(cc_entry);
4103
                gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
4104
                gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
4105
        } else {
4106
                gtk_widget_hide(newsgroups_hbox);
4107
                gtk_widget_hide(newsgroups_entry);
4108
                gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
4109
        }
4110

    
4111
        switch (prefs_common.toolbar_style) {
4112
        case TOOLBAR_NONE:
4113
                gtk_widget_hide(handlebox);
4114
                break;
4115
        case TOOLBAR_ICON:
4116
                gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4117
                                      GTK_TOOLBAR_ICONS);
4118
                break;
4119
        case TOOLBAR_TEXT:
4120
                gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4121
                                      GTK_TOOLBAR_TEXT);
4122
                break;
4123
        case TOOLBAR_BOTH:
4124
                gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4125
                                      GTK_TOOLBAR_BOTH);
4126
                break;
4127
        }
4128

    
4129
        undostruct = undo_init(text);
4130
        undo_set_change_state_func(undostruct, &compose_undo_state_changed,
4131
                                   menubar);
4132

    
4133
        address_completion_start(window);
4134

    
4135
        compose->window        = window;
4136
        compose->vbox               = vbox;
4137
        compose->menubar       = menubar;
4138
        compose->handlebox     = handlebox;
4139

    
4140
        compose->vbox2               = vbox2;
4141

    
4142
        compose->table_vbox       = table_vbox;
4143
        compose->table                  = table;
4144
        compose->to_hbox          = to_hbox;
4145
        compose->to_entry         = to_entry;
4146
        compose->newsgroups_hbox  = newsgroups_hbox;
4147
        compose->newsgroups_entry = newsgroups_entry;
4148
        compose->subject_entry    = subject_entry;
4149
        compose->cc_hbox          = cc_hbox;
4150
        compose->cc_entry         = cc_entry;
4151
        compose->bcc_hbox         = bcc_hbox;
4152
        compose->bcc_entry        = bcc_entry;
4153
        compose->reply_hbox       = reply_hbox;
4154
        compose->reply_entry      = reply_entry;
4155
        compose->followup_hbox    = followup_hbox;
4156
        compose->followup_entry   = followup_entry;
4157

    
4158
        compose->paned = paned;
4159

    
4160
        compose->attach_scrwin = attach_scrwin;
4161
        compose->attach_clist  = attach_clist;
4162

    
4163
        compose->edit_vbox     = edit_vbox;
4164
        compose->ruler_hbox    = ruler_hbox;
4165
        compose->ruler         = ruler;
4166
        compose->scrolledwin   = scrolledwin;
4167
        compose->text               = text;
4168

    
4169
        compose->focused_editable = NULL;
4170

    
4171
        compose->popupmenu    = popupmenu;
4172
        compose->popupfactory = popupfactory;
4173

    
4174
        compose->tmpl_menu = tmpl_menu;
4175

    
4176
        compose->mode = mode;
4177

    
4178
        compose->targetinfo = NULL;
4179
        compose->replyinfo  = NULL;
4180

    
4181
        compose->replyto     = NULL;
4182
        compose->cc             = NULL;
4183
        compose->bcc             = NULL;
4184
        compose->followup_to = NULL;
4185

    
4186
        compose->ml_post     = NULL;
4187

    
4188
        compose->inreplyto   = NULL;
4189
        compose->references  = NULL;
4190
        compose->msgid       = NULL;
4191
        compose->boundary    = NULL;
4192

    
4193
        compose->autowrap       = prefs_common.autowrap;
4194

    
4195
        compose->use_to         = FALSE;
4196
        compose->use_cc         = FALSE;
4197
        compose->use_bcc        = FALSE;
4198
        compose->use_replyto    = FALSE;
4199
        compose->use_newsgroups = FALSE;
4200
        compose->use_followupto = FALSE;
4201
        compose->use_attach     = FALSE;
4202

    
4203
#if USE_GPGME
4204
        compose->use_signing    = FALSE;
4205
        compose->use_encryption = FALSE;
4206
#endif /* USE_GPGME */
4207

    
4208
        compose->modified = FALSE;
4209

    
4210
        compose->paste_as_quotation = FALSE;
4211

    
4212
        compose->to_list        = NULL;
4213
        compose->newsgroup_list = NULL;
4214

    
4215
        compose->undostruct = undostruct;
4216

    
4217
        compose->sig_str = NULL;
4218

    
4219
        compose->exteditor_file    = NULL;
4220
        compose->exteditor_pid     = -1;
4221
        compose->exteditor_readdes = -1;
4222
        compose->exteditor_tag     = -1;
4223

    
4224
        compose_select_account(compose, account, TRUE);
4225

    
4226
        menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
4227
        menu_set_active(ifactory, "/View/Ruler", prefs_common.show_ruler);
4228

    
4229
        if (mode == COMPOSE_REDIRECT) {
4230
                menu_set_sensitive(ifactory, "/File/Save to draft folder", FALSE);
4231
                menu_set_sensitive(ifactory, "/File/Save and keep editing", FALSE);
4232
                menu_set_sensitive(ifactory, "/File/Attach file", FALSE);
4233
                menu_set_sensitive(ifactory, "/File/Insert file", FALSE);
4234
                menu_set_sensitive(ifactory, "/File/Insert signature", FALSE);
4235
                menu_set_sensitive(ifactory, "/Edit/Cut", FALSE);
4236
                menu_set_sensitive(ifactory, "/Edit/Paste", FALSE);
4237
                menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE);
4238
                menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE);
4239
                menu_set_sensitive(ifactory, "/Edit/Auto wrapping", FALSE);
4240
                menu_set_sensitive(ifactory, "/Edit/Advanced", FALSE);
4241
                menu_set_sensitive(ifactory, "/View/Attachment", FALSE);
4242
                menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
4243
                menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
4244
                menu_set_sensitive(ifactory, "/Tools/Edit with external editor", FALSE);
4245
#if USE_GPGME
4246
                menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE);
4247
                menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE);
4248
#endif /* USE_GPGME */
4249

    
4250
                gtk_widget_set_sensitive(compose->insert_btn, FALSE);
4251
                gtk_widget_set_sensitive(compose->attach_btn, FALSE);
4252
                gtk_widget_set_sensitive(compose->sig_btn, FALSE);
4253
                gtk_widget_set_sensitive(compose->exteditor_btn, FALSE);
4254
                gtk_widget_set_sensitive(compose->linewrap_btn, FALSE);
4255

    
4256
                menu_set_sensitive_all(GTK_MENU_SHELL(compose->popupmenu),
4257
                                       FALSE);
4258
        }
4259

    
4260
        addressbook_set_target_compose(compose);
4261
        action_update_compose_menu(ifactory, compose);
4262
        compose_set_template_menu(compose);
4263

    
4264
        compose_list = g_list_append(compose_list, compose);
4265

    
4266
        gtk_widget_show(window);
4267

    
4268
        return compose;
4269
}
4270

    
4271
static void compose_connect_changed_callbacks(Compose *compose)
4272
{
4273
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);;
4274
        GtkTextBuffer *buffer;
4275

    
4276
        buffer = gtk_text_view_get_buffer(text);
4277

    
4278
        g_signal_connect(G_OBJECT(buffer), "changed",
4279
                         G_CALLBACK(compose_changed_cb), compose);
4280
        g_signal_connect(G_OBJECT(compose->to_entry), "changed",
4281
                         G_CALLBACK(compose_changed_cb), compose);
4282
        g_signal_connect(G_OBJECT(compose->newsgroups_entry), "changed",
4283
                         G_CALLBACK(compose_changed_cb), compose);
4284
        g_signal_connect(G_OBJECT(compose->cc_entry), "changed",
4285
                         G_CALLBACK(compose_changed_cb), compose);
4286
        g_signal_connect(G_OBJECT(compose->bcc_entry), "changed",
4287
                         G_CALLBACK(compose_changed_cb), compose);
4288
        g_signal_connect(G_OBJECT(compose->reply_entry), "changed",
4289
                         G_CALLBACK(compose_changed_cb), compose);
4290
        g_signal_connect(G_OBJECT(compose->followup_entry), "changed",
4291
                         G_CALLBACK(compose_changed_cb), compose);
4292
        g_signal_connect(G_OBJECT(compose->subject_entry), "changed",
4293
                         G_CALLBACK(compose_changed_cb), compose);
4294
}
4295

    
4296
static void compose_toolbar_create(Compose *compose, GtkWidget *container)
4297
{
4298
        GtkWidget *toolbar;
4299
        GtkWidget *icon_wid;
4300
        GtkWidget *send_btn;
4301
        GtkWidget *sendl_btn;
4302
        GtkWidget *draft_btn;
4303
        GtkWidget *insert_btn;
4304
        GtkWidget *attach_btn;
4305
        GtkWidget *sig_btn;
4306
        GtkWidget *exteditor_btn;
4307
        GtkWidget *linewrap_btn;
4308
        GtkWidget *addrbook_btn;
4309

    
4310
        toolbar = gtk_toolbar_new();
4311
        gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
4312
                                    GTK_ORIENTATION_HORIZONTAL);
4313
        gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
4314
        gtk_container_add(GTK_CONTAINER(container), toolbar);
4315
        gtk_container_set_border_width(GTK_CONTAINER(container), 2);
4316

    
4317
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL_SEND);
4318
        send_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4319
                                           _("Send"),
4320
                                           _("Send message"),
4321
                                           "Send",
4322
                                           icon_wid,
4323
                                           G_CALLBACK(toolbar_send_cb),
4324
                                           compose);
4325

    
4326
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL_SEND_QUEUE);
4327
        sendl_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4328
                                           _("Send later"),
4329
                                           _("Put into queue folder and send later"),
4330
                                           "Send later",
4331
                                           icon_wid,
4332
                                           G_CALLBACK(toolbar_send_later_cb),
4333
                                           compose);
4334

    
4335
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL);
4336
        draft_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4337
                                            _("Draft"),
4338
                                            _("Save to draft folder"),
4339
                                            "Draft",
4340
                                            icon_wid,
4341
                                            G_CALLBACK(toolbar_draft_cb),
4342
                                            compose);
4343

    
4344
        gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4345

    
4346
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_PASTE);
4347
        insert_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4348
                                             _("Insert"),
4349
                                             _("Insert file"),
4350
                                             "Insert",
4351
                                             icon_wid,
4352
                                             G_CALLBACK(toolbar_insert_cb),
4353
                                             compose);
4354

    
4355
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL_ATTACH);
4356
        attach_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4357
                                             _("Attach"),
4358
                                             _("Attach file"),
4359
                                             "Attach",
4360
                                             icon_wid,
4361
                                             G_CALLBACK(toolbar_attach_cb),
4362
                                             compose);
4363

    
4364
        gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4365

    
4366
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL);
4367
        sig_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4368
                                          _("Signature"),
4369
                                          _("Insert signature"),
4370
                                          "Signature",
4371
                                          icon_wid,
4372
                                          G_CALLBACK(toolbar_sig_cb), compose);
4373

    
4374
        gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4375

    
4376
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_MAIL_COMPOSE);
4377
        exteditor_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4378
                                                _("Editor"),
4379
                                                _("Edit with external editor"),
4380
                                                "Editor",
4381
                                                icon_wid,
4382
                                                G_CALLBACK(toolbar_ext_editor_cb),
4383
                                                compose);
4384

    
4385
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_LINEWRAP);
4386
        linewrap_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4387
                                               _("Linewrap"),
4388
                                               _("Wrap all long lines"),
4389
                                               "Linewrap",
4390
                                               icon_wid,
4391
                                               G_CALLBACK(toolbar_linewrap_cb),
4392
                                               compose);
4393

    
4394
        gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4395

    
4396
        icon_wid = stock_pixmap_widget(container, STOCK_PIXMAP_ADDRESS_BOOK);
4397
        addrbook_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4398
                                               _("Address"),
4399
                                               _("Address book"),
4400
                                               "Address",
4401
                                               icon_wid,
4402
                                               G_CALLBACK(toolbar_address_cb),
4403
                                               compose);
4404

    
4405
        compose->toolbar       = toolbar;
4406
        compose->send_btn      = send_btn;
4407
        compose->sendl_btn     = sendl_btn;
4408
        compose->draft_btn     = draft_btn;
4409
        compose->insert_btn    = insert_btn;
4410
        compose->attach_btn    = attach_btn;
4411
        compose->sig_btn       = sig_btn;
4412
        compose->exteditor_btn = exteditor_btn;
4413
        compose->linewrap_btn  = linewrap_btn;
4414
        compose->addrbook_btn  = addrbook_btn;
4415

    
4416
        gtk_widget_show_all(toolbar);
4417
}
4418

    
4419
static GtkWidget *compose_account_option_menu_create(Compose *compose)
4420
{
4421
        GList *accounts;
4422
        GtkWidget *hbox;
4423
        GtkWidget *optmenu;
4424
        GtkWidget *menu;
4425
        gint num = 0, def_menu = 0;
4426

    
4427
        accounts = account_get_list();
4428
        g_return_val_if_fail(accounts != NULL, NULL);
4429

    
4430
        hbox = gtk_hbox_new(FALSE, 0);
4431
        optmenu = gtk_option_menu_new();
4432
        gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
4433
        menu = gtk_menu_new();
4434

    
4435
        for (; accounts != NULL; accounts = accounts->next, num++) {
4436
                PrefsAccount *ac = (PrefsAccount *)accounts->data;
4437
                GtkWidget *menuitem;
4438
                gchar *name;
4439

    
4440
                if (ac == compose->account) def_menu = num;
4441

    
4442
                if (ac->name)
4443
                        name = g_strdup_printf("%s: %s <%s>",
4444
                                               ac->account_name,
4445
                                               ac->name, ac->address);
4446
                else
4447
                        name = g_strdup_printf("%s: %s",
4448
                                               ac->account_name, ac->address);
4449
                MENUITEM_ADD(menu, menuitem, name, ac);
4450
                g_free(name);
4451
                g_signal_connect(G_OBJECT(menuitem), "activate",
4452
                                 G_CALLBACK(account_activated),
4453
                                 compose);
4454
        }
4455

    
4456
        gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
4457
        gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
4458

    
4459
        return hbox;
4460
}
4461

    
4462
static void compose_set_template_menu(Compose *compose)
4463
{
4464
        GSList *tmpl_list, *cur;
4465
        GtkWidget *menu;
4466
        GtkWidget *item;
4467

    
4468
        tmpl_list = template_get_config();
4469

    
4470
        menu = gtk_menu_new();
4471

    
4472
        for (cur = tmpl_list; cur != NULL; cur = cur->next) {
4473
                Template *tmpl = (Template *)cur->data;
4474

    
4475
                item = gtk_menu_item_new_with_label(tmpl->name);
4476
                gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
4477
                g_signal_connect(G_OBJECT(item), "activate",
4478
                                 G_CALLBACK(compose_template_activate_cb),
4479
                                 compose);
4480
                g_object_set_data(G_OBJECT(item), "template", tmpl);
4481
                gtk_widget_show(item);
4482
        }
4483

    
4484
        gtk_widget_show(menu);
4485
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
4486
}
4487

    
4488
void compose_reflect_prefs_all(void)
4489
{
4490
        GList *cur;
4491
        Compose *compose;
4492

    
4493
        for (cur = compose_list; cur != NULL; cur = cur->next) {
4494
                compose = (Compose *)cur->data;
4495
                compose_set_template_menu(compose);
4496
        }
4497
}
4498

    
4499
static void compose_template_apply(Compose *compose, Template *tmpl,
4500
                                   gboolean replace)
4501
{
4502
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4503
        GtkTextBuffer *buffer;
4504
        GtkTextMark *mark;
4505
        GtkTextIter iter;
4506
        gchar *qmark;
4507
        gchar *parsed_str;
4508

    
4509
        if (!tmpl || !tmpl->value) return;
4510

    
4511
        buffer = gtk_text_view_get_buffer(text);
4512

    
4513
        if (tmpl->to && *tmpl->to != '\0')
4514
                compose_entry_set(compose, tmpl->to, COMPOSE_ENTRY_TO);
4515
        if (tmpl->cc && *tmpl->cc != '\0')
4516
                compose_entry_set(compose, tmpl->cc, COMPOSE_ENTRY_CC);
4517
        if (tmpl->subject && *tmpl->subject != '\0')
4518
                compose_entry_set(compose, tmpl->subject, COMPOSE_ENTRY_SUBJECT);
4519

    
4520
        if (replace)
4521
                gtk_text_buffer_set_text(buffer, "", 0);
4522

    
4523
        mark = gtk_text_buffer_get_insert(buffer);
4524
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4525

    
4526
        if (compose->replyinfo == NULL) {
4527
                parsed_str = compose_quote_fmt(compose, NULL, tmpl->value,
4528
                                               NULL, NULL);
4529
        } else {
4530
                if (prefs_common.quotemark && *prefs_common.quotemark)
4531
                        qmark = prefs_common.quotemark;
4532
                else
4533
                        qmark = "> ";
4534

    
4535
                parsed_str = compose_quote_fmt(compose, compose->replyinfo,
4536
                                               tmpl->value, qmark, NULL);
4537
        }
4538

    
4539
        if (replace && parsed_str && prefs_common.auto_sig)
4540
                compose_insert_sig(compose, FALSE);
4541

    
4542
        if (replace && parsed_str) {
4543
                gtk_text_buffer_get_start_iter(buffer, &iter);
4544
                gtk_text_buffer_place_cursor(buffer, &iter);
4545
        }
4546

    
4547
        if (parsed_str)
4548
                compose_changed_cb(NULL, compose);
4549
}
4550

    
4551
static void compose_destroy(Compose *compose)
4552
{
4553
        gint row;
4554
        GtkCList *clist = GTK_CLIST(compose->attach_clist);
4555
        AttachInfo *ainfo;
4556

    
4557
        /* NOTE: address_completion_end() does nothing with the window
4558
         * however this may change. */
4559
        address_completion_end(compose->window);
4560

    
4561
        slist_free_strings(compose->to_list);
4562
        g_slist_free(compose->to_list);
4563
        slist_free_strings(compose->newsgroup_list);
4564
        g_slist_free(compose->newsgroup_list);
4565

    
4566
        procmsg_msginfo_free(compose->targetinfo);
4567
        procmsg_msginfo_free(compose->replyinfo);
4568

    
4569
        g_free(compose->replyto);
4570
        g_free(compose->cc);
4571
        g_free(compose->bcc);
4572
        g_free(compose->newsgroups);
4573
        g_free(compose->followup_to);
4574

    
4575
        g_free(compose->ml_post);
4576

    
4577
        g_free(compose->inreplyto);
4578
        g_free(compose->references);
4579
        g_free(compose->msgid);
4580
        g_free(compose->boundary);
4581

    
4582
        if (compose->undostruct)
4583
                undo_destroy(compose->undostruct);
4584

    
4585
        g_free(compose->sig_str);
4586

    
4587
        g_free(compose->exteditor_file);
4588

    
4589
        for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
4590
             row++)
4591
                compose_attach_info_free(ainfo);
4592

    
4593
        if (addressbook_get_target_compose() == compose)
4594
                addressbook_set_target_compose(NULL);
4595

    
4596
        prefs_common.compose_width = compose->scrolledwin->allocation.width;
4597
        prefs_common.compose_height = compose->window->allocation.height;
4598

    
4599
        gtk_widget_destroy(compose->paned);
4600

    
4601
        g_free(compose);
4602

    
4603
        compose_list = g_list_remove(compose_list, compose);
4604
}
4605

    
4606
static void compose_attach_info_free(AttachInfo *ainfo)
4607
{
4608
        g_free(ainfo->file);
4609
        g_free(ainfo->content_type);
4610
        g_free(ainfo->name);
4611
        g_free(ainfo);
4612
}
4613

    
4614
static void compose_attach_remove_selected(Compose *compose)
4615
{
4616
        GtkCList *clist = GTK_CLIST(compose->attach_clist);
4617
        AttachInfo *ainfo;
4618
        gint row;
4619

    
4620
        while (clist->selection != NULL) {
4621
                row = GPOINTER_TO_INT(clist->selection->data);
4622
                ainfo = gtk_clist_get_row_data(clist, row);
4623
                compose_attach_info_free(ainfo);
4624
                gtk_clist_remove(clist, row);
4625
        }
4626
}
4627

    
4628
static struct _AttachProperty
4629
{
4630
        GtkWidget *window;
4631
        GtkWidget *mimetype_entry;
4632
        GtkWidget *encoding_optmenu;
4633
        GtkWidget *path_entry;
4634
        GtkWidget *filename_entry;
4635
        GtkWidget *ok_btn;
4636
        GtkWidget *cancel_btn;
4637
} attach_prop;
4638

    
4639
static void compose_attach_property(Compose *compose)
4640
{
4641
        GtkCList *clist = GTK_CLIST(compose->attach_clist);
4642
        AttachInfo *ainfo;
4643
        gint row;
4644
        GtkOptionMenu *optmenu;
4645
        static gboolean cancelled;
4646

    
4647
        if (!clist->selection) return;
4648
        row = GPOINTER_TO_INT(clist->selection->data);
4649

    
4650
        ainfo = gtk_clist_get_row_data(clist, row);
4651
        if (!ainfo) return;
4652

    
4653
        if (!attach_prop.window)
4654
                compose_attach_property_create(&cancelled);
4655
        gtk_widget_grab_focus(attach_prop.ok_btn);
4656
        gtk_widget_show(attach_prop.window);
4657
        manage_window_set_transient(GTK_WINDOW(attach_prop.window));
4658

    
4659
        optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu);
4660
        if (ainfo->encoding == ENC_UNKNOWN)
4661
                gtk_option_menu_set_history(optmenu, ENC_BASE64);
4662
        else
4663
                gtk_option_menu_set_history(optmenu, ainfo->encoding);
4664

    
4665
        gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
4666
                           ainfo->content_type ? ainfo->content_type : "");
4667
        gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
4668
                           ainfo->file ? ainfo->file : "");
4669
        gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
4670
                           ainfo->name ? ainfo->name : "");
4671

    
4672
        for (;;) {
4673
                const gchar *entry_text;
4674
                gchar *text;
4675
                gchar *cnttype = NULL;
4676
                gchar *file = NULL;
4677
                off_t size = 0;
4678
                GtkWidget *menu;
4679
                GtkWidget *menuitem;
4680

    
4681
                cancelled = FALSE;
4682
                gtk_main();
4683

    
4684
                if (cancelled == TRUE) {
4685
                        gtk_widget_hide(attach_prop.window);
4686
                        break;
4687
                }
4688

    
4689
                entry_text = gtk_entry_get_text
4690
                        (GTK_ENTRY(attach_prop.mimetype_entry));
4691
                if (*entry_text != '\0') {
4692
                        gchar *p;
4693

    
4694
                        text = g_strstrip(g_strdup(entry_text));
4695
                        if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
4696
                                cnttype = g_strdup(text);
4697
                                g_free(text);
4698
                        } else {
4699
                                alertpanel_error(_("Invalid MIME type."));
4700
                                g_free(text);
4701
                                continue;
4702
                        }
4703
                }
4704

    
4705
                menu = gtk_option_menu_get_menu(optmenu);
4706
                menuitem = gtk_menu_get_active(GTK_MENU(menu));
4707
                ainfo->encoding = GPOINTER_TO_INT
4708
                        (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID));
4709

    
4710
                entry_text = gtk_entry_get_text
4711
                        (GTK_ENTRY(attach_prop.path_entry));
4712
                if (*entry_text != '\0') {
4713
                        if (is_file_exist(entry_text) &&
4714
                            (size = get_file_size(entry_text)) > 0)
4715
                                file = g_strdup(entry_text);
4716
                        else {
4717
                                alertpanel_error
4718
                                        (_("File doesn't exist or is empty."));
4719
                                g_free(cnttype);
4720
                                continue;
4721
                        }
4722
                }
4723

    
4724
                entry_text = gtk_entry_get_text
4725
                        (GTK_ENTRY(attach_prop.filename_entry));
4726
                if (*entry_text != '\0') {
4727
                        g_free(ainfo->name);
4728
                        ainfo->name = g_strdup(entry_text);
4729
                }
4730

    
4731
                if (cnttype) {
4732
                        g_free(ainfo->content_type);
4733
                        ainfo->content_type = cnttype;
4734
                }
4735
                if (file) {
4736
                        g_free(ainfo->file);
4737
                        ainfo->file = file;
4738
                }
4739
                if (size)
4740
                        ainfo->size = size;
4741

    
4742
                gtk_clist_set_text(clist, row, COL_MIMETYPE,
4743
                                   ainfo->content_type);
4744
                gtk_clist_set_text(clist, row, COL_SIZE,