Statistics
| Revision:

root / src / compose.c @ 2877

History | View | Annotate | Download (218 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2010 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include "defs.h"
25
26
#ifndef PANGO_ENABLE_ENGINE
27
#  define PANGO_ENABLE_ENGINE
28
#endif
29
30
#include <glib.h>
31
#include <glib/gi18n.h>
32
#include <gdk/gdkkeysyms.h>
33
#include <gtk/gtkmain.h>
34
#include <gtk/gtkmenu.h>
35
#include <gtk/gtkmenuitem.h>
36
#include <gtk/gtkitemfactory.h>
37
#include <gtk/gtkcheckmenuitem.h>
38
#include <gtk/gtkoptionmenu.h>
39
#include <gtk/gtkwidget.h>
40
#include <gtk/gtkliststore.h>
41
#include <gtk/gtktreeview.h>
42
#include <gtk/gtktreeselection.h>
43
#include <gtk/gtkcellrenderertext.h>
44
#include <gtk/gtkvpaned.h>
45
#include <gtk/gtkentry.h>
46
#include <gtk/gtkeditable.h>
47
#include <gtk/gtktextview.h>
48
#include <gtk/gtkwindow.h>
49
#include <gtk/gtksignal.h>
50
#include <gtk/gtkvbox.h>
51
#include <gtk/gtkcontainer.h>
52
#include <gtk/gtktoolbar.h>
53
#include <gtk/gtktoolitem.h>
54
#include <gtk/gtktoolbutton.h>
55
#include <gtk/gtkseparatortoolitem.h>
56
#include <gtk/gtktable.h>
57
#include <gtk/gtkhbox.h>
58
#include <gtk/gtklabel.h>
59
#include <gtk/gtkcheckbutton.h>
60
#include <gtk/gtkscrolledwindow.h>
61
#include <gtk/gtktreeview.h>
62
#include <gtk/gtkdnd.h>
63
#include <gtk/gtkclipboard.h>
64
#include <gtk/gtkstock.h>
65
#include <gtk/gtkdialog.h>
66
#include <gtk/gtkimage.h>
67
#include <pango/pango-break.h>
68
69
#if USE_GTKSPELL
70
#  include <gtk/gtkradiomenuitem.h>
71
#  include <gtkspell/gtkspell.h>
72
#if USE_ENCHANT
73
#  include <enchant/enchant.h>
74
#else
75
#  include <aspell.h>
76
#endif
77
#endif
78
79
#include <stdio.h>
80
#include <stdlib.h>
81
#include <string.h>
82
#include <ctype.h>
83
#include <sys/types.h>
84
#include <sys/stat.h>
85
#include <unistd.h>
86
#include <time.h>
87
#include <stdlib.h>
88
#if HAVE_SYS_WAIT_H
89
#  include <sys/wait.h>
90
#endif
91
#include <signal.h>
92
#include <errno.h>
93
#ifdef G_OS_WIN32
94
#  include <windows.h>
95
#endif
96
97
#include "main.h"
98
#include "mainwindow.h"
99
#include "compose.h"
100
#include "addressbook.h"
101
#include "folderview.h"
102
#include "procmsg.h"
103
#include "menu.h"
104
#include "stock_pixmap.h"
105
#include "send_message.h"
106
#include "imap.h"
107
#include "news.h"
108
#include "customheader.h"
109
#include "prefs_common.h"
110
#include "prefs_common_dialog.h"
111
#include "prefs_account.h"
112
#include "prefs_toolbar.h"
113
#include "action.h"
114
#include "account.h"
115
#include "account_dialog.h"
116
#include "filesel.h"
117
#include "procheader.h"
118
#include "procmime.h"
119
#include "statusbar.h"
120
#include "about.h"
121
#include "base64.h"
122
#include "quoted-printable.h"
123
#include "codeconv.h"
124
#include "utils.h"
125
#include "gtkutils.h"
126
#include "socket.h"
127
#include "alertpanel.h"
128
#include "manage_window.h"
129
#include "gtkshruler.h"
130
#include "folder.h"
131
#include "filter.h"
132
#include "addr_compl.h"
133
#include "quote_fmt.h"
134
#include "template.h"
135
#include "undo.h"
136
#include "plugin.h"
137
138
#if USE_GPGME
139
#  include "rfc2015.h"
140
#endif
141
142
enum
143
{
144
        COL_MIMETYPE,
145
        COL_SIZE,
146
        COL_NAME,
147
        COL_ATTACH_INFO,
148
        N_ATTACH_COLS
149
};
150
151
typedef enum
152
{
153
        COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE,
154
        COMPOSE_ACTION_MOVE_FORWARD_CHARACTER,
155
        COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER,
156
        COMPOSE_ACTION_MOVE_FORWARD_WORD,
157
        COMPOSE_ACTION_MOVE_BACKWARD_WORD,
158
        COMPOSE_ACTION_MOVE_END_OF_LINE,
159
        COMPOSE_ACTION_MOVE_NEXT_LINE,
160
        COMPOSE_ACTION_MOVE_PREVIOUS_LINE,
161
        COMPOSE_ACTION_DELETE_FORWARD_CHARACTER,
162
        COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER,
163
        COMPOSE_ACTION_DELETE_FORWARD_WORD,
164
        COMPOSE_ACTION_DELETE_BACKWARD_WORD,
165
        COMPOSE_ACTION_DELETE_LINE,
166
        COMPOSE_ACTION_DELETE_LINE_N,
167
        COMPOSE_ACTION_DELETE_TO_LINE_END
168
} ComposeAction;
169
170
#define B64_LINE_SIZE                57
171
#define B64_BUFFSIZE                77
172
173
#define MAX_REFERENCES_LEN        999
174
175
#define TEXTVIEW_MARGIN                6
176
177
static GdkColor quote_color = {0, 0, 0, 0xbfff};
178
179
static GList *compose_list = NULL;
180
181
static Compose *compose_create                        (PrefsAccount        *account,
182
                                                 ComposeMode         mode);
183
static Compose *compose_find_window_by_target        (MsgInfo        *msginfo);
184
static gboolean compose_window_exist                (gint                 x,
185
                                                 gint                 y);
186
static void compose_connect_changed_callbacks        (Compose        *compose);
187
188
static GtkWidget *compose_toolbar_create        (Compose        *compose);
189
static GtkWidget *compose_toolbar_create_from_list
190
                                                (Compose        *compose,
191
                                                 GList                *item_list);
192
static void compose_set_toolbar_button_visibility
193
                                                (Compose        *compose);
194
195
static GtkWidget *compose_account_option_menu_create
196
                                                (Compose        *compose,
197
                                                 GtkWidget        *hbox);
198
static void compose_set_out_encoding                (Compose        *compose);
199
static void compose_set_template_menu                (Compose        *compose);
200
static void compose_template_apply                (Compose        *compose,
201
                                                 Template        *tmpl,
202
                                                 gboolean         replace);
203
static void compose_destroy                        (Compose        *compose);
204
205
static void compose_entry_show                        (Compose        *compose,
206
                                                 ComposeEntryType type);
207
static GtkEntry *compose_get_entry                (Compose        *compose,
208
                                                 ComposeEntryType type);
209
static void compose_entries_set                        (Compose        *compose,
210
                                                 const gchar        *mailto);
211
static void compose_entries_set_from_item        (Compose        *compose,
212
                                                 FolderItem        *item,
213
                                                 ComposeMode         mode);
214
static gint compose_parse_header                (Compose        *compose,
215
                                                 MsgInfo        *msginfo);
216
static gchar *compose_parse_references                (const gchar        *ref,
217
                                                 const gchar        *msgid);
218
static gint compose_parse_source_msg                (Compose        *compose,
219
                                                 MsgInfo        *msginfo);
220
221
static gchar *compose_quote_fmt                        (Compose        *compose,
222
                                                 MsgInfo        *msginfo,
223
                                                 const gchar        *fmt,
224
                                                 const gchar        *qmark,
225
                                                 const gchar        *body);
226
227
static void compose_reply_set_entry                (Compose        *compose,
228
                                                 MsgInfo        *msginfo,
229
                                                 ComposeMode         mode);
230
static void compose_reedit_set_entry                (Compose        *compose,
231
                                                 MsgInfo        *msginfo);
232
233
static void compose_insert_sig                        (Compose        *compose,
234
                                                 gboolean         append,
235
                                                 gboolean         replace,
236
                                                 gboolean         scroll);
237
static void compose_enable_sig                        (Compose        *compose);
238
static gchar *compose_get_signature_str                (Compose        *compose);
239
240
static void compose_insert_file                        (Compose        *compose,
241
                                                 const gchar        *file,
242
                                                 gboolean         scroll);
243
244
static void compose_attach_append                (Compose        *compose,
245
                                                 const gchar        *file,
246
                                                 const gchar        *filename,
247
                                                 const gchar        *content_type);
248
static void compose_attach_parts                (Compose        *compose,
249
                                                 MsgInfo        *msginfo);
250
251
static void compose_wrap_paragraph                (Compose        *compose,
252
                                                 GtkTextIter        *par_iter);
253
static void compose_wrap_all                        (Compose        *compose);
254
static void compose_wrap_all_full                (Compose        *compose,
255
                                                 gboolean         autowrap);
256
257
static void compose_set_title                        (Compose        *compose);
258
static void compose_select_account                (Compose        *compose,
259
                                                 PrefsAccount        *account,
260
                                                 gboolean         init);
261
262
static gboolean compose_check_for_valid_recipient
263
                                                (Compose        *compose);
264
static gboolean compose_check_entries                (Compose        *compose);
265
static gboolean compose_check_attachments        (Compose        *compose);
266
static gboolean compose_check_recipients        (Compose        *compose);
267
268
static void compose_add_new_recipients_to_addressbook
269
                                                (Compose        *compose);
270
271
static gint compose_send                        (Compose        *compose);
272
static gint compose_write_to_file                (Compose        *compose,
273
                                                 const gchar        *file,
274
                                                 gboolean         is_draft);
275
static gint compose_write_body_to_file                (Compose        *compose,
276
                                                 const gchar        *file);
277
static gint compose_redirect_write_to_file        (Compose        *compose,
278
                                                 const gchar        *file);
279
static gint compose_remove_reedit_target        (Compose        *compose);
280
static gint compose_queue                        (Compose        *compose,
281
                                                 const gchar        *file);
282
static gint compose_write_attach                (Compose        *compose,
283
                                                 FILE                *fp,
284
                                                 const gchar        *charset);
285
static gint compose_write_headers                (Compose        *compose,
286
                                                 FILE                *fp,
287
                                                 const gchar        *charset,
288
                                                 const gchar        *body_charset,
289
                                                 EncodingType         encoding,
290
                                                 gboolean         is_draft);
291
static gint compose_redirect_write_headers        (Compose        *compose,
292
                                                 FILE                *fp);
293
294
static void compose_convert_header                (Compose        *compose,
295
                                                 gchar                *dest,
296
                                                 gint                 len,
297
                                                 const gchar        *src,
298
                                                 gint                 header_len,
299
                                                 gboolean         addr_field,
300
                                                 const gchar        *encoding);
301
static gchar *compose_convert_filename                (Compose        *compose,
302
                                                 const gchar        *src,
303
                                                 const gchar        *param_name,
304
                                                 const gchar        *encoding);
305
static void compose_generate_msgid                (Compose        *compose,
306
                                                 gchar                *buf,
307
                                                 gint                 len);
308
309
static void compose_attach_info_free                (AttachInfo        *ainfo);
310
static void compose_attach_remove_selected        (Compose        *compose);
311
312
static void compose_attach_property                (Compose        *compose);
313
static void compose_attach_property_create        (gboolean        *cancelled);
314
static void attach_property_ok                        (GtkWidget        *widget,
315
                                                 gboolean        *cancelled);
316
static void attach_property_cancel                (GtkWidget        *widget,
317
                                                 gboolean        *cancelled);
318
static gint attach_property_delete_event        (GtkWidget        *widget,
319
                                                 GdkEventAny        *event,
320
                                                 gboolean        *cancelled);
321
static gboolean attach_property_key_pressed        (GtkWidget        *widget,
322
                                                 GdkEventKey        *event,
323
                                                 gboolean        *cancelled);
324
325
static void compose_attach_open                        (Compose        *compose);
326
327
static void compose_exec_ext_editor                (Compose        *compose);
328
static gboolean compose_ext_editor_kill                (Compose        *compose);
329
static void compose_ext_editor_child_exit        (GPid                 pid,
330
                                                 gint                 status,
331
                                                 gpointer         data);
332
static void compose_set_ext_editor_sensitive        (Compose        *compose,
333
                                                 gboolean         sensitive);
334
335
static void compose_undo_state_changed                (UndoMain        *undostruct,
336
                                                 gint                 undo_state,
337
                                                 gint                 redo_state,
338
                                                 gpointer         data);
339
340
static gint calc_cursor_xpos        (GtkTextView        *text,
341
                                 gint                 extra,
342
                                 gint                 char_width);
343
344
/* callback functions */
345
346
static gboolean compose_edit_size_alloc (GtkEditable        *widget,
347
                                         GtkAllocation        *allocation,
348
                                         GtkSHRuler        *shruler);
349
350
static void toolbar_send_cb                (GtkWidget        *widget,
351
                                         gpointer         data);
352
static void toolbar_send_later_cb        (GtkWidget        *widget,
353
                                         gpointer         data);
354
static void toolbar_draft_cb                (GtkWidget        *widget,
355
                                         gpointer         data);
356
static void toolbar_insert_cb                (GtkWidget        *widget,
357
                                         gpointer         data);
358
static void toolbar_attach_cb                (GtkWidget        *widget,
359
                                         gpointer         data);
360
static void toolbar_sig_cb                (GtkWidget        *widget,
361
                                         gpointer         data);
362
static void toolbar_ext_editor_cb        (GtkWidget        *widget,
363
                                         gpointer         data);
364
static void toolbar_linewrap_cb                (GtkWidget        *widget,
365
                                         gpointer         data);
366
static void toolbar_address_cb                (GtkWidget        *widget,
367
                                         gpointer         data);
368
static void toolbar_prefs_common_cb        (GtkWidget        *widget,
369
                                         gpointer         data);
370
static void toolbar_prefs_account_cb        (GtkWidget        *widget,
371
                                         gpointer         data);
372
373
static gboolean toolbar_button_pressed        (GtkWidget        *widget,
374
                                         GdkEventButton        *event,
375
                                         gpointer         data);
376
377
static void account_activated                (GtkMenuItem        *menuitem,
378
                                         gpointer         data);
379
380
static void attach_selection_changed        (GtkTreeSelection        *selection,
381
                                         gpointer                 data);
382
383
static gboolean attach_button_pressed        (GtkWidget        *widget,
384
                                         GdkEventButton        *event,
385
                                         gpointer         data);
386
static gboolean attach_key_pressed        (GtkWidget        *widget,
387
                                         GdkEventKey        *event,
388
                                         gpointer         data);
389
390
static void compose_send_cb                (gpointer         data,
391
                                         guint                 action,
392
                                         GtkWidget        *widget);
393
static void compose_send_later_cb        (gpointer         data,
394
                                         guint                 action,
395
                                         GtkWidget        *widget);
396
397
static void compose_draft_cb                (gpointer         data,
398
                                         guint                 action,
399
                                         GtkWidget        *widget);
400
401
static void compose_attach_open_cb        (gpointer         data,
402
                                         guint                 action,
403
                                         GtkWidget        *widget);
404
static void compose_attach_cb                (gpointer         data,
405
                                         guint                 action,
406
                                         GtkWidget        *widget);
407
static void compose_insert_file_cb        (gpointer         data,
408
                                         guint                 action,
409
                                         GtkWidget        *widget);
410
static void compose_insert_sig_cb        (gpointer         data,
411
                                         guint                 action,
412
                                         GtkWidget        *widget);
413
414
static void compose_close_cb                (gpointer         data,
415
                                         guint                 action,
416
                                         GtkWidget        *widget);
417
418
static void compose_set_encoding_cb        (gpointer         data,
419
                                         guint                 action,
420
                                         GtkWidget        *widget);
421
422
static void compose_address_cb                (gpointer         data,
423
                                         guint                 action,
424
                                         GtkWidget        *widget);
425
static void compose_template_activate_cb(GtkWidget        *widget,
426
                                         gpointer         data);
427
428
static void compose_ext_editor_cb        (gpointer         data,
429
                                         guint                 action,
430
                                         GtkWidget        *widget);
431
432
static gint compose_delete_cb                (GtkWidget        *widget,
433
                                         GdkEventAny        *event,
434
                                         gpointer         data);
435
436
static gint compose_window_state_cb        (GtkWidget                *widget,
437
                                         GdkEventWindowState        *event,
438
                                         gpointer                 data);
439
440
static void compose_undo_cb                (Compose        *compose);
441
static void compose_redo_cb                (Compose        *compose);
442
static void compose_cut_cb                (Compose        *compose);
443
static void compose_copy_cb                (Compose        *compose);
444
static void compose_paste_cb                (Compose        *compose);
445
static void compose_paste_as_quote_cb        (Compose        *compose);
446
static void compose_allsel_cb                (Compose        *compose);
447
448
static void compose_grab_focus_cb        (GtkWidget        *widget,
449
                                         Compose        *compose);
450
451
#if USE_GPGME
452
static void compose_signing_toggled        (GtkWidget        *widget,
453
                                         Compose        *compose);
454
static void compose_encrypt_toggled        (GtkWidget        *widget,
455
                                         Compose        *compose);
456
#endif
457
458
#if 0
459
static void compose_attach_toggled        (GtkWidget        *widget,
460
                                         Compose        *compose);
461
#endif
462
463
static void compose_buffer_changed_cb        (GtkTextBuffer        *textbuf,
464
                                         Compose        *compose);
465
static void compose_changed_cb                (GtkEditable        *editable,
466
                                         Compose        *compose);
467
468
static void compose_wrap_cb                (gpointer         data,
469
                                         guint                 action,
470
                                         GtkWidget        *widget);
471
static void compose_toggle_autowrap_cb        (gpointer         data,
472
                                         guint                 action,
473
                                         GtkWidget        *widget);
474
475
static void compose_toggle_to_cb        (gpointer         data,
476
                                         guint                 action,
477
                                         GtkWidget        *widget);
478
static void compose_toggle_cc_cb        (gpointer         data,
479
                                         guint                 action,
480
                                         GtkWidget        *widget);
481
static void compose_toggle_bcc_cb        (gpointer         data,
482
                                         guint                 action,
483
                                         GtkWidget        *widget);
484
static void compose_toggle_replyto_cb        (gpointer         data,
485
                                         guint                 action,
486
                                         GtkWidget        *widget);
487
static void compose_toggle_followupto_cb(gpointer         data,
488
                                         guint                 action,
489
                                         GtkWidget        *widget);
490
static void compose_toggle_ruler_cb        (gpointer         data,
491
                                         guint                 action,
492
                                         GtkWidget        *widget);
493
static void compose_toggle_attach_cb        (gpointer         data,
494
                                         guint                 action,
495
                                         GtkWidget        *widget);
496
static void compose_customize_toolbar_cb(gpointer         data,
497
                                         guint                 action,
498
                                         GtkWidget        *widget);
499
500
static void compose_toggle_mdn_cb        (gpointer         data,
501
                                         guint                 action,
502
                                         GtkWidget        *widget);
503
504
#if USE_GPGME
505
static void compose_toggle_sign_cb        (gpointer         data,
506
                                         guint                 action,
507
                                         GtkWidget        *widget);
508
static void compose_toggle_encrypt_cb        (gpointer         data,
509
                                         guint                 action,
510
                                         GtkWidget        *widget);
511
#endif
512
513
#if USE_GTKSPELL
514
static void compose_set_spell_lang_menu (Compose        *compose);
515
static void compose_change_spell_lang_menu
516
                                        (Compose        *compose,
517
                                         const gchar        *lang);
518
static void compose_toggle_spell_cb        (gpointer         data,
519
                                         guint                 action,
520
                                         GtkWidget        *widget);
521
static void compose_set_spell_lang_cb        (GtkWidget        *widget,
522
                                         gpointer         data);
523
#endif
524
525
static void compose_attach_drag_received_cb (GtkWidget                *widget,
526
                                             GdkDragContext        *drag_context,
527
                                             gint                 x,
528
                                             gint                 y,
529
                                             GtkSelectionData        *data,
530
                                             guint                 info,
531
                                             guint                 time,
532
                                             gpointer                 user_data);
533
static void compose_insert_drag_received_cb (GtkWidget                *widget,
534
                                             GdkDragContext        *drag_context,
535
                                             gint                 x,
536
                                             gint                 y,
537
                                             GtkSelectionData        *data,
538
                                             guint                 info,
539
                                             guint                 time,
540
                                             gpointer                 user_data);
541
542
static void to_activated                (GtkWidget        *widget,
543
                                         Compose        *compose);
544
static void newsgroups_activated        (GtkWidget        *widget,
545
                                         Compose        *compose);
546
static void cc_activated                (GtkWidget        *widget,
547
                                         Compose        *compose);
548
static void bcc_activated                (GtkWidget        *widget,
549
                                         Compose        *compose);
550
static void replyto_activated                (GtkWidget        *widget,
551
                                         Compose        *compose);
552
static void followupto_activated        (GtkWidget        *widget,
553
                                         Compose        *compose);
554
static void subject_activated                (GtkWidget        *widget,
555
                                         Compose        *compose);
556
557
static void text_inserted                (GtkTextBuffer        *buffer,
558
                                         GtkTextIter        *iter,
559
                                         const gchar        *text,
560
                                         gint                 len,
561
                                         Compose        *compose);
562
563
static gboolean autosave_timeout        (gpointer         data);
564
565
566
static GtkItemFactoryEntry compose_popup_entries[] =
567
{
568
        {N_("/_Open"),                NULL, compose_attach_open_cb, 0, NULL},
569
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
570
        {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
571
        {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
572
        {N_("/---"),                NULL, NULL, 0, "<Separator>"},
573
        {N_("/_Properties..."),        NULL, compose_attach_property, 0, NULL}
574
};
575
576
static GtkItemFactoryEntry compose_entries[] =
577
{
578
        {N_("/_File"),                                NULL, NULL, 0, "<Branch>"},
579
        {N_("/_File/_Send"),                        "<shift><control>E",
580
                                                compose_send_cb, 0, NULL},
581
        {N_("/_File/Send _later"),                "<shift><control>S",
582
                                                compose_send_later_cb,  0, NULL},
583
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
584
        {N_("/_File/Save to _draft folder"),
585
                                                "<shift><control>D", compose_draft_cb, 0, NULL},
586
        {N_("/_File/Save and _keep editing"),
587
                                                "<control>S", compose_draft_cb, 1, NULL},
588
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
589
        {N_("/_File/_Attach file"),                "<control>M", compose_attach_cb,      0, NULL},
590
        {N_("/_File/_Insert file"),                "<control>I", compose_insert_file_cb, 0, NULL},
591
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
592
        {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig_cb,  0, NULL},
593
        {N_("/_File/A_ppend signature"),        "<shift><control>G", compose_insert_sig_cb,  1, NULL},
594
        {N_("/_File/---"),                        NULL, NULL, 0, "<Separator>"},
595
        {N_("/_File/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
596
597
        {N_("/_Edit"),                        NULL, NULL, 0, "<Branch>"},
598
        {N_("/_Edit/_Undo"),                "<control>Z", compose_undo_cb, 0, NULL},
599
        {N_("/_Edit/_Redo"),                "<control>Y", compose_redo_cb, 0, NULL},
600
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
601
        {N_("/_Edit/Cu_t"),                "<control>X", compose_cut_cb,    0, NULL},
602
        {N_("/_Edit/_Copy"),                "<control>C", compose_copy_cb,   0, NULL},
603
        {N_("/_Edit/_Paste"),                "<control>V", compose_paste_cb,  0, NULL},
604
        {N_("/_Edit/Paste as _quotation"),
605
                                        NULL, compose_paste_as_quote_cb, 0, NULL},
606
        {N_("/_Edit/Select _all"),        "<control>A", compose_allsel_cb, 0, NULL},
607
        {N_("/_Edit/---"),                NULL, NULL, 0, "<Separator>"},
608
        {N_("/_Edit/_Wrap current paragraph"),
609
                                        "<control>L", compose_wrap_cb, 0, NULL},
610
        {N_("/_Edit/Wrap all long _lines"),
611
                                        "<control><alt>L", compose_wrap_cb, 1, NULL},
612
        {N_("/_Edit/Aut_o wrapping"),        "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
613
        {N_("/_View"),                        NULL, NULL, 0, "<Branch>"},
614
        {N_("/_View/_To"),                NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
615
        {N_("/_View/_Cc"),                NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
616
        {N_("/_View/_Bcc"),                NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
617
        {N_("/_View/_Reply-To"),        NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
618
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
619
        {N_("/_View/_Followup-To"),        NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
620
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
621
        {N_("/_View/R_uler"),                NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
622
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
623
        {N_("/_View/_Attachment"),        NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
624
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
625
        {N_("/_View/Cu_stomize toolbar..."),
626
                                        NULL, compose_customize_toolbar_cb, 0, NULL},
627
        {N_("/_View/---"),                NULL, NULL, 0, "<Separator>"},
628
629
#define ENC_ACTION(action) \
630
        NULL, compose_set_encoding_cb, action, \
631
        "/View/Character encoding/Automatic"
632
633
        {N_("/_View/Character _encoding"), NULL, NULL, 0, "<Branch>"},
634
        {N_("/_View/Character _encoding/_Automatic"),
635
                        NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
636
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
637
638
        {N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"),
639
         ENC_ACTION(C_US_ASCII)},
640
        {N_("/_View/Character _encoding/Unicode (_UTF-8)"),
641
         ENC_ACTION(C_UTF_8)},
642
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
643
644
        {N_("/_View/Character _encoding/Western European (ISO-8859-_1)"),
645
         ENC_ACTION(C_ISO_8859_1)},
646
        {N_("/_View/Character _encoding/Western European (ISO-8859-15)"),
647
         ENC_ACTION(C_ISO_8859_15)},
648
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
649
650
        {N_("/_View/Character _encoding/Central European (ISO-8859-_2)"),
651
         ENC_ACTION(C_ISO_8859_2)},
652
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
653
654
        {N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"),
655
         ENC_ACTION(C_ISO_8859_13)},
656
        {N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"),
657
         ENC_ACTION(C_ISO_8859_4)},
658
        {N_("/_View/Character _encoding/Baltic (Windows-1257)"),
659
         ENC_ACTION(C_WINDOWS_1257)},
660
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
661
662
        {N_("/_View/Character _encoding/Greek (ISO-8859-_7)"),
663
         ENC_ACTION(C_ISO_8859_7)},
664
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
665
666
        {N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"),
667
         ENC_ACTION(C_ISO_8859_6)},
668
        {N_("/_View/Character _encoding/Arabic (Windows-1256)"),
669
         ENC_ACTION(C_WINDOWS_1256)},
670
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
671
672
        {N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"),
673
         ENC_ACTION(C_ISO_8859_8)},
674
        {N_("/_View/Character _encoding/Hebrew (Windows-1255)"),
675
         ENC_ACTION(C_WINDOWS_1255)},
676
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
677
678
        {N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"),
679
         ENC_ACTION(C_ISO_8859_9)},
680
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
681
682
        {N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"),
683
         ENC_ACTION(C_ISO_8859_5)},
684
        {N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"),
685
         ENC_ACTION(C_KOI8_R)},
686
        {N_("/_View/Character _encoding/Cyrillic (KOI8-U)"),
687
         ENC_ACTION(C_KOI8_U)},
688
        {N_("/_View/Character _encoding/Cyrillic (Windows-1251)"),
689
         ENC_ACTION(C_WINDOWS_1251)},
690
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
691
692
        {N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"),
693
         ENC_ACTION(C_ISO_2022_JP)},
694
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
695
696
        {N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"),
697
         ENC_ACTION(C_GB2312)},
698
        {N_("/_View/Character _encoding/Simplified Chinese (GBK)"),
699
         ENC_ACTION(C_GBK)},
700
        {N_("/_View/Character _encoding/Traditional Chinese (_Big5)"),
701
         ENC_ACTION(C_BIG5)},
702
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
703
704
        {N_("/_View/Character _encoding/Korean (EUC-_KR)"),
705
         ENC_ACTION(C_EUC_KR)},
706
        {N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
707
708
        {N_("/_View/Character _encoding/Thai (TIS-620)"),
709
         ENC_ACTION(C_TIS_620)},
710
        {N_("/_View/Character _encoding/Thai (Windows-874)"),
711
         ENC_ACTION(C_WINDOWS_874)},
712
713
        {N_("/_Tools"),                        NULL, NULL, 0, "<Branch>"},
714
        {N_("/_Tools/_Address book"),        "<shift><control>A", compose_address_cb , 0, NULL},
715
        {N_("/_Tools/_Template"),        NULL, NULL, 0, "<Branch>"},
716
#ifndef G_OS_WIN32
717
        {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
718
#endif
719
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
720
        {N_("/_Tools/Edit with e_xternal editor"),
721
                                        "<shift><control>X", compose_ext_editor_cb, 0, NULL},
722
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
723
        {N_("/_Tools/Request _disposition notification"),           NULL, compose_toggle_mdn_cb   , 0, "<ToggleItem>"},
724
725
#if USE_GPGME
726
        {N_("/_Tools/---"),                NULL, NULL, 0, "<Separator>"},
727
        {N_("/_Tools/PGP Si_gn"),           NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
728
        {N_("/_Tools/PGP _Encrypt"),        NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
729
#endif /* USE_GPGME */
730
731
#if USE_GTKSPELL
732
        {N_("/_Tools/---"),                        NULL, NULL, 0, "<Separator>"},
733
        {N_("/_Tools/_Check spell"),                NULL, compose_toggle_spell_cb, 0, "<ToggleItem>"},
734
        {N_("/_Tools/_Set spell language"),        NULL, NULL, 0, "<Branch>"},
735
#endif /* USE_GTKSPELL */
736
737
        {N_("/_Help"),                        NULL, NULL, 0, "<Branch>"},
738
        {N_("/_Help/_About"),                NULL, about_show, 0, NULL}
739
};
740
741
enum
742
{
743
        DRAG_TYPE_RFC822,
744
        DRAG_TYPE_URI_LIST,
745
746
        N_DRAG_TYPES
747
};
748
749
static GtkTargetEntry compose_drag_types[] =
750
{
751
        {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
752
        {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
753
};
754
755
756
Compose *compose_new(PrefsAccount *account, FolderItem *item,
757
                     const gchar *mailto, GPtrArray *attach_files)
758
{
759
        Compose *compose;
760
        GtkTextView *text;
761
        GtkTextBuffer *buffer;
762
        GtkTextIter iter;
763
764
        if (!account) account = cur_account;
765
        g_return_val_if_fail(account != NULL, NULL);
766
767
        compose = compose_create(account, COMPOSE_NEW);
768
769
        undo_block(compose->undostruct);
770
771
        if (prefs_common.auto_sig)
772
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
773
774
        text = GTK_TEXT_VIEW(compose->text);
775
        buffer = gtk_text_view_get_buffer(text);
776
        gtk_text_buffer_get_start_iter(buffer, &iter);
777
        gtk_text_buffer_place_cursor(buffer, &iter);
778
779
        if (account->protocol != A_NNTP) {
780
                if (mailto && *mailto != '\0') {
781
                        compose_entries_set(compose, mailto);
782
                        gtk_widget_grab_focus(compose->subject_entry);
783
                } else if (item) {
784
                        compose_entries_set_from_item
785
                                (compose, item, COMPOSE_NEW);
786
                        if (item->auto_to)
787
                                gtk_widget_grab_focus(compose->subject_entry);
788
                        else
789
                                gtk_widget_grab_focus(compose->to_entry);
790
                } else
791
                        gtk_widget_grab_focus(compose->to_entry);
792
        } else {
793
                if (mailto && *mailto != '\0') {
794
                        compose_entry_append(compose, mailto,
795
                                             COMPOSE_ENTRY_NEWSGROUPS);
796
                        gtk_widget_grab_focus(compose->subject_entry);
797
                } else
798
                        gtk_widget_grab_focus(compose->newsgroups_entry);
799
        }
800
801
        if (attach_files) {
802
                gint i;
803
                gchar *file;
804
805
                for (i = 0; i < attach_files->len; i++) {
806
                        gchar *utf8file;
807
808
                        file = g_ptr_array_index(attach_files, i);
809
                        utf8file = conv_filename_to_utf8(file);
810
                        compose_attach_append(compose, file, utf8file, NULL);
811
                        g_free(utf8file);
812
                }
813
        }
814
815
        undo_unblock(compose->undostruct);
816
817
        compose_connect_changed_callbacks(compose);
818
        compose_set_title(compose);
819
820
        syl_plugin_signal_emit("compose-created", compose);
821
822
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
823
                compose->autosave_tag =
824
                        g_timeout_add_full(G_PRIORITY_LOW,
825
                                           prefs_common.autosave_itv * 60 * 1000,
826
                                           autosave_timeout, compose, NULL);
827
        if (prefs_common.auto_exteditor)
828
                compose_exec_ext_editor(compose);
829
830
        return compose;
831
}
832
833
void compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode,
834
                   const gchar *body)
835
{
836
        Compose *compose;
837
        PrefsAccount *account = NULL;
838
        MsgInfo *replyinfo;
839
        GtkTextBuffer *buffer;
840
        GtkTextIter iter;
841
        gboolean quote = FALSE;
842
843
        g_return_if_fail(msginfo != NULL);
844
845
        if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
846
                quote = TRUE;
847
848
        if (msginfo->folder)
849
                account = account_find_from_item_property(msginfo->folder);
850
        if (!account && msginfo->to && prefs_common.reply_account_autosel) {
851
                gchar *to;
852
                Xstrdup_a(to, msginfo->to, return);
853
                extract_address(to);
854
                account = account_find_from_address(to);
855
        }
856
        if (!account && msginfo->folder && msginfo->folder->folder)
857
                account = msginfo->folder->folder->account;
858
        if (!account)
859
                account = cur_account;
860
        g_return_if_fail(account != NULL);
861
862
        compose = compose_create(account, COMPOSE_REPLY);
863
864
        replyinfo = procmsg_msginfo_get_full_info(msginfo);
865
        if (!replyinfo)
866
                replyinfo = procmsg_msginfo_copy(msginfo);
867
        if (msginfo->folder) {
868
                gchar *id;
869
                id = folder_item_get_identifier(msginfo->folder);
870
                if (id) {
871
                        compose->reply_target = g_strdup_printf
872
                                ("%s/%u", id, msginfo->msgnum);
873
                        g_free(id);
874
                }
875
        }
876
877
        if (compose_parse_header(compose, msginfo) < 0) return;
878
879
        undo_block(compose->undostruct);
880
881
        compose_reply_set_entry(compose, msginfo, mode);
882
        if (item)
883
                compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
884
885
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
886
887
        if (account->sig_before_quote && prefs_common.auto_sig) {
888
                GtkTextMark *mark;
889
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
890
                mark = gtk_text_buffer_get_insert(buffer);
891
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
892
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
893
        }
894
895
        if (quote) {
896
                gchar *qmark;
897
                gchar *quote_str;
898
899
                if (prefs_common.quotemark && *prefs_common.quotemark)
900
                        qmark = prefs_common.quotemark;
901
                else
902
                        qmark = "> ";
903
904
                quote_str = compose_quote_fmt(compose, replyinfo,
905
                                              prefs_common.quotefmt,
906
                                              qmark, body);
907
        }
908
909
        if (!account->sig_before_quote && prefs_common.auto_sig)
910
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
911
912
        if (quote && prefs_common.linewrap_quote)
913
                compose_wrap_all(compose);
914
915
        gtk_text_buffer_get_start_iter(buffer, &iter);
916
        gtk_text_buffer_place_cursor(buffer, &iter);
917
918
        gtk_widget_grab_focus(compose->text);
919
920
        undo_unblock(compose->undostruct);
921
922
        compose_connect_changed_callbacks(compose);
923
        compose_set_title(compose);
924
925
#if USE_GPGME
926
        if (rfc2015_is_available() && account->encrypt_reply &&
927
            MSG_IS_ENCRYPTED(replyinfo->flags)) {
928
                GtkItemFactory *ifactory;
929
930
                ifactory = gtk_item_factory_from_widget(compose->menubar);
931
                menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
932
        }
933
#endif
934
935
        procmsg_msginfo_free(replyinfo);
936
937
        syl_plugin_signal_emit("compose-created", compose);
938
939
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
940
                compose->autosave_tag =
941
                        g_timeout_add_full(G_PRIORITY_LOW,
942
                                           prefs_common.autosave_itv * 60 * 1000,
943
                                           autosave_timeout, compose, NULL);
944
        if (prefs_common.auto_exteditor)
945
                compose_exec_ext_editor(compose);
946
}
947
948
void compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach,
949
                     const gchar *body)
950
{
951
        Compose *compose;
952
        PrefsAccount *account = NULL;
953
        GtkTextView *text;
954
        GtkTextBuffer *buffer;
955
        GtkTextIter iter;
956
        GSList *cur;
957
        MsgInfo *msginfo;
958
        GString *forward_targets;
959
960
        g_return_if_fail(mlist != NULL);
961
962
        msginfo = (MsgInfo *)mlist->data;
963
964
        if (msginfo->folder)
965
                account = account_find_from_item(msginfo->folder);
966
        if (!account) account = cur_account;
967
        g_return_if_fail(account != NULL);
968
969
        compose = compose_create(account, COMPOSE_FORWARD);
970
971
        forward_targets = g_string_new("");
972
        for (cur = mlist; cur != NULL; cur = cur->next) {
973
                msginfo = (MsgInfo *)cur->data;
974
                if (msginfo->folder) {
975
                        gchar *id;
976
                        id = folder_item_get_identifier(msginfo->folder);
977
                        if (id) {
978
                                if (forward_targets->len > 0)
979
                                        g_string_append(forward_targets,
980
                                                        "\n ");
981
                                g_string_append_printf(forward_targets, "%s/%u",
982
                                                       id, msginfo->msgnum);
983
                                g_free(id);
984
                        }
985
                }
986
        }
987
        if (forward_targets->len > 0)
988
                compose->forward_targets = g_string_free(forward_targets,
989
                                                         FALSE);
990
        else
991
                g_string_free(forward_targets, TRUE);
992
993
        undo_block(compose->undostruct);
994
995
        compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
996
        if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
997
                compose_entry_append(compose, msginfo->subject,
998
                                     COMPOSE_ENTRY_SUBJECT);
999
        if (item)
1000
                compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
1001
1002
        text = GTK_TEXT_VIEW(compose->text);
1003
        buffer = gtk_text_view_get_buffer(text);
1004
1005
        if (account->sig_before_quote && prefs_common.auto_sig) {
1006
                GtkTextMark *mark;
1007
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1008
                mark = gtk_text_buffer_get_insert(buffer);
1009
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1010
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1011
        }
1012
1013
        for (cur = mlist; cur != NULL; cur = cur->next) {
1014
                msginfo = (MsgInfo *)cur->data;
1015
1016
                if (as_attach) {
1017
                        gchar *msgfile;
1018
1019
                        msgfile = procmsg_get_message_file_path(msginfo);
1020
                        if (!is_file_exist(msgfile))
1021
                                g_warning(_("%s: file not exist\n"), msgfile);
1022
                        else
1023
                                compose_attach_append(compose, msgfile, msgfile,
1024
                                                      "message/rfc822");
1025
1026
                        g_free(msgfile);
1027
                } else {
1028
                        gchar *qmark;
1029
                        gchar *quote_str;
1030
                        MsgInfo *full_msginfo;
1031
1032
                        full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1033
                        if (!full_msginfo)
1034
                                full_msginfo = procmsg_msginfo_copy(msginfo);
1035
1036
                        if (cur != mlist) {
1037
                                GtkTextMark *mark;
1038
                                mark = gtk_text_buffer_get_insert(buffer);
1039
                                gtk_text_buffer_get_iter_at_mark
1040
                                        (buffer, &iter, mark);
1041
                                gtk_text_buffer_insert
1042
                                        (buffer, &iter, "\n\n", 2);
1043
                        }
1044
1045
                        if (prefs_common.fw_quotemark &&
1046
                            *prefs_common.fw_quotemark)
1047
                                qmark = prefs_common.fw_quotemark;
1048
                        else
1049
                                qmark = "> ";
1050
1051
                        quote_str = compose_quote_fmt(compose, full_msginfo,
1052
                                                      prefs_common.fw_quotefmt,
1053
                                                      qmark, body);
1054
                        compose_attach_parts(compose, msginfo);
1055
1056
                        procmsg_msginfo_free(full_msginfo);
1057
1058
                        if (body) break;
1059
                }
1060
        }
1061
1062
        if (!account->sig_before_quote && prefs_common.auto_sig)
1063
                compose_insert_sig(compose, TRUE, FALSE, FALSE);
1064
1065
        if (prefs_common.linewrap_quote)
1066
                compose_wrap_all(compose);
1067
1068
        gtk_text_buffer_get_start_iter(buffer, &iter);
1069
        gtk_text_buffer_place_cursor(buffer, &iter);
1070
1071
        undo_unblock(compose->undostruct);
1072
1073
        compose_connect_changed_callbacks(compose);
1074
        compose_set_title(compose);
1075
1076
        if (account->protocol != A_NNTP)
1077
                gtk_widget_grab_focus(compose->to_entry);
1078
        else
1079
                gtk_widget_grab_focus(compose->newsgroups_entry);
1080
1081
        syl_plugin_signal_emit("compose-created", compose);
1082
1083
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1084
                compose->autosave_tag =
1085
                        g_timeout_add_full(G_PRIORITY_LOW,
1086
                                           prefs_common.autosave_itv * 60 * 1000,
1087
                                           autosave_timeout, compose, NULL);
1088
        if (prefs_common.auto_exteditor)
1089
                compose_exec_ext_editor(compose);
1090
}
1091
1092
void compose_redirect(MsgInfo *msginfo, FolderItem *item)
1093
{
1094
        Compose *compose;
1095
        PrefsAccount *account;
1096
        GtkTextView *text;
1097
        GtkTextBuffer *buffer;
1098
        GtkTextMark *mark;
1099
        GtkTextIter iter;
1100
        FILE *fp;
1101
        gchar buf[BUFFSIZE];
1102
1103
        g_return_if_fail(msginfo != NULL);
1104
        g_return_if_fail(msginfo->folder != NULL);
1105
1106
        account = account_find_from_item(msginfo->folder);
1107
        if (!account) account = cur_account;
1108
        g_return_if_fail(account != NULL);
1109
1110
        compose = compose_create(account, COMPOSE_REDIRECT);
1111
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1112
1113
        if (compose_parse_header(compose, msginfo) < 0) return;
1114
1115
        undo_block(compose->undostruct);
1116
1117
        if (msginfo->subject)
1118
                compose_entry_set(compose, msginfo->subject,
1119
                                  COMPOSE_ENTRY_SUBJECT);
1120
        compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
1121
1122
        text = GTK_TEXT_VIEW(compose->text);
1123
        buffer = gtk_text_view_get_buffer(text);
1124
        mark = gtk_text_buffer_get_insert(buffer);
1125
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1126
1127
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1128
                g_warning(_("Can't get text part\n"));
1129
        else {
1130
                gboolean prev_autowrap = compose->autowrap;
1131
1132
                compose->autowrap = FALSE;
1133
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1134
                        strcrchomp(buf);
1135
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1136
                }
1137
                compose->autowrap = prev_autowrap;
1138
                fclose(fp);
1139
        }
1140
        compose_attach_parts(compose, msginfo);
1141
1142
        if (account->protocol != A_NNTP)
1143
                gtk_widget_grab_focus(compose->to_entry);
1144
        else
1145
                gtk_widget_grab_focus(compose->newsgroups_entry);
1146
1147
        gtk_text_view_set_editable(text, FALSE);
1148
1149
        undo_unblock(compose->undostruct);
1150
1151
        compose_connect_changed_callbacks(compose);
1152
        compose_set_title(compose);
1153
1154
        syl_plugin_signal_emit("compose-created", compose);
1155
}
1156
1157
void compose_reedit(MsgInfo *msginfo)
1158
{
1159
        Compose *compose;
1160
        PrefsAccount *account;
1161
        GtkTextView *text;
1162
        GtkTextBuffer *buffer;
1163
        GtkTextMark *mark;
1164
        GtkTextIter iter;
1165
        FILE *fp;
1166
        gchar buf[BUFFSIZE];
1167
        const gchar *str;
1168
        GtkWidget *focus_widget = NULL;
1169
        GtkItemFactory *ifactory;
1170
1171
        g_return_if_fail(msginfo != NULL);
1172
        g_return_if_fail(msginfo->folder != NULL);
1173
1174
        account = account_find_from_msginfo(msginfo);
1175
        if (!account) account = cur_account;
1176
        g_return_if_fail(account != NULL);
1177
1178
        if (msginfo->folder->stype == F_DRAFT ||
1179
            msginfo->folder->stype == F_QUEUE) {
1180
                compose = compose_find_window_by_target(msginfo);
1181
                if (compose) {
1182
                        debug_print
1183
                                ("compose_reedit(): existing window found.\n");
1184
                        gtk_window_present(GTK_WINDOW(compose->window));
1185
                        return;
1186
                }
1187
        }
1188
1189
        compose = compose_create(account, COMPOSE_REEDIT);
1190
        compose->targetinfo = procmsg_msginfo_copy(msginfo);
1191
1192
        if (compose_parse_header(compose, msginfo) < 0) return;
1193
        compose_parse_source_msg(compose, msginfo);
1194
1195
        undo_block(compose->undostruct);
1196
1197
        compose_reedit_set_entry(compose, msginfo);
1198
1199
        text = GTK_TEXT_VIEW(compose->text);
1200
        buffer = gtk_text_view_get_buffer(text);
1201
        mark = gtk_text_buffer_get_insert(buffer);
1202
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1203
1204
        if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1205
                g_warning(_("Can't get text part\n"));
1206
        else {
1207
                gboolean prev_autowrap = compose->autowrap;
1208
1209
                compose->autowrap = FALSE;
1210
                while (fgets(buf, sizeof(buf), fp) != NULL) {
1211
                        strcrchomp(buf);
1212
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
1213
                }
1214
                compose_enable_sig(compose);
1215
                compose->autowrap = prev_autowrap;
1216
                fclose(fp);
1217
        }
1218
        compose_attach_parts(compose, msginfo);
1219
1220
        gtk_text_buffer_get_start_iter(buffer, &iter);
1221
        gtk_text_buffer_place_cursor(buffer, &iter);
1222
1223
        if (account->protocol != A_NNTP) {
1224
                str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
1225
                if (!str || *str == '\0')
1226
                        focus_widget = compose->to_entry;
1227
        } else {
1228
                str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
1229
                if (!str || *str == '\0')
1230
                        focus_widget = compose->newsgroups_entry;
1231
        }
1232
        if (!focus_widget) {
1233
                str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
1234
                if (!str || *str == '\0')
1235
                        focus_widget = compose->subject_entry;
1236
        }
1237
        if (focus_widget)
1238
                gtk_widget_grab_focus(focus_widget);
1239
        else
1240
                gtk_widget_grab_focus(compose->text);
1241
1242
        undo_unblock(compose->undostruct);
1243
1244
        compose_connect_changed_callbacks(compose);
1245
        compose_set_title(compose);
1246
1247
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1248
        if (compose->use_mdn) {
1249
                menu_set_active(ifactory, "/Tools/Request disposition notification", TRUE);
1250
        }
1251
        menu_set_active(ifactory, "/Edit/Auto wrapping", compose->autowrap);
1252
#if USE_GTKSPELL
1253
        menu_set_active(ifactory, "/Tools/Check spell", compose->check_spell);
1254
        compose_change_spell_lang_menu(compose, compose->spell_lang);
1255
#endif
1256
#if USE_GPGME
1257
        menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing);
1258
        menu_set_active(ifactory, "/Tools/PGP Encrypt", compose->use_encryption);
1259
#endif
1260
1261
        syl_plugin_signal_emit("compose-created", compose);
1262
1263
        if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1264
                compose->autosave_tag =
1265
                        g_timeout_add_full(G_PRIORITY_LOW,
1266
                                           prefs_common.autosave_itv * 60 * 1000,
1267
                                           autosave_timeout, compose, NULL);
1268
        if (prefs_common.auto_exteditor)
1269
                compose_exec_ext_editor(compose);
1270
}
1271
1272
GList *compose_get_compose_list(void)
1273
{
1274
        return compose_list;
1275
}
1276
1277
static void compose_entry_show(Compose *compose, ComposeEntryType type)
1278
{
1279
        GtkItemFactory *ifactory;
1280
1281
        ifactory = gtk_item_factory_from_widget(compose->menubar);
1282
1283
        switch (type) {
1284
        case COMPOSE_ENTRY_CC:
1285
                menu_set_active(ifactory, "/View/Cc", TRUE);
1286
                break;
1287
        case COMPOSE_ENTRY_BCC:
1288
                menu_set_active(ifactory, "/View/Bcc", TRUE);
1289
                break;
1290
        case COMPOSE_ENTRY_REPLY_TO:
1291
                menu_set_active(ifactory, "/View/Reply-To", TRUE);
1292
                break;
1293
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1294
                menu_set_active(ifactory, "/View/Followup-To", TRUE);
1295
                break;
1296
        case COMPOSE_ENTRY_TO:
1297
                menu_set_active(ifactory, "/View/To", TRUE);
1298
                break;
1299
        default:
1300
                break;
1301
        }
1302
}
1303
1304
static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
1305
{
1306
        GtkEntry *entry;
1307
1308
        switch (type) {
1309
        case COMPOSE_ENTRY_CC:
1310
                entry = GTK_ENTRY(compose->cc_entry);
1311
                break;
1312
        case COMPOSE_ENTRY_BCC:
1313
                entry = GTK_ENTRY(compose->bcc_entry);
1314
                break;
1315
        case COMPOSE_ENTRY_REPLY_TO:
1316
                entry = GTK_ENTRY(compose->reply_entry);
1317
                break;
1318
        case COMPOSE_ENTRY_SUBJECT:
1319
                entry = GTK_ENTRY(compose->subject_entry);
1320
                break;
1321
        case COMPOSE_ENTRY_NEWSGROUPS:
1322
                entry = GTK_ENTRY(compose->newsgroups_entry);
1323
                break;
1324
        case COMPOSE_ENTRY_FOLLOWUP_TO:
1325
                entry = GTK_ENTRY(compose->followup_entry);
1326
                break;
1327
        case COMPOSE_ENTRY_TO:
1328
        default:
1329
                entry = GTK_ENTRY(compose->to_entry);
1330
                break;
1331
        }
1332
1333
        return entry;
1334
}
1335
1336
void compose_entry_set(Compose *compose, const gchar *text,
1337
                       ComposeEntryType type)
1338
{
1339
        GtkEntry *entry;
1340
1341
        if (!text) return;
1342
1343
        compose_entry_show(compose, type);
1344
        entry = compose_get_entry(compose, type);
1345
1346
        gtk_entry_set_text(entry, text);
1347
}
1348
1349
void compose_entry_append(Compose *compose, const gchar *text,
1350
                          ComposeEntryType type)
1351
{
1352
        GtkEntry *entry;
1353
        const gchar *str;
1354
        gint pos;
1355
1356
        if (!text || *text == '\0') return;
1357
1358
        compose_entry_show(compose, type);
1359
        entry = compose_get_entry(compose, type);
1360
1361
        if (type != COMPOSE_ENTRY_SUBJECT) {
1362
                str = gtk_entry_get_text(entry);
1363
                if (*str != '\0') {
1364
                        pos = entry->text_length;
1365
                        gtk_editable_insert_text(GTK_EDITABLE(entry),
1366
                                                 ", ", -1, &pos);
1367
                }
1368
        }
1369
1370
        pos = entry->text_length;
1371
        gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1372
}
1373
1374
gchar *compose_entry_get_text(Compose *compose, ComposeEntryType type)
1375
{
1376
        GtkEntry *entry;
1377
1378
        entry = compose_get_entry(compose, type);
1379
        return gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1380
}
1381
1382
static void compose_entries_set(Compose *compose, const gchar *mailto)
1383
{
1384
        gchar *to = NULL;
1385
        gchar *cc = NULL;
1386
        gchar *subject = NULL;
1387
        gchar *inreplyto = NULL;
1388
        gchar *body = NULL;
1389
1390
        scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body);
1391
1392
        if (to)
1393
                compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1394
        if (cc)
1395
                compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1396
        if (subject)
1397
                compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1398
        if (inreplyto) {
1399
                if (strchr(inreplyto, '<'))
1400
                        extract_parenthesis(inreplyto, '<', '>');
1401
                remove_space(inreplyto);
1402
                compose->inreplyto = g_strdup(inreplyto);
1403
        }
1404
        if (body) {
1405
                GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1406
                GtkTextBuffer *buffer;
1407
                GtkTextMark *mark;
1408
                GtkTextIter iter;
1409
                gboolean prev_autowrap = compose->autowrap;
1410
1411
                compose->autowrap = FALSE;
1412
1413
                buffer = gtk_text_view_get_buffer(text);
1414
                mark = gtk_text_buffer_get_insert(buffer);
1415
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1416
1417
                gtk_text_buffer_insert(buffer, &iter, body, -1);
1418
                gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1419
1420
                compose->autowrap = prev_autowrap;
1421
                if (compose->autowrap)
1422
                        compose_wrap_all(compose);
1423
        }
1424
1425
        g_free(to);
1426
        g_free(cc);
1427
        g_free(subject);
1428
        g_free(inreplyto);
1429
        g_free(body);
1430
}
1431
1432
static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1433
                                          ComposeMode mode)
1434
{
1435
        g_return_if_fail(item != NULL);
1436
1437
        if (item->auto_to) {
1438
                if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1439
                        compose_entry_set(compose, item->auto_to,
1440
                                          COMPOSE_ENTRY_TO);
1441
        }
1442
        if (item->auto_cc)
1443
                compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1444
        if (item->auto_bcc)
1445
                compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1446
        if (item->auto_replyto)
1447
                compose_entry_set(compose, item->auto_replyto,
1448
                                  COMPOSE_ENTRY_REPLY_TO);
1449
}
1450
1451
#undef ACTIVATE_MENU
1452
1453
static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1454
{
1455
        static HeaderEntry hentry[] = {{"Reply-To:",        NULL, TRUE},
1456
                                       {"Cc:",                NULL, TRUE},
1457
                                       {"References:",        NULL, FALSE},
1458
                                       {"Bcc:",                NULL, TRUE},
1459
                                       {"Newsgroups:",  NULL, TRUE},
1460
                                       {"Followup-To:", NULL, TRUE},
1461
                                       {"List-Post:",   NULL, FALSE},
1462
                                       {"Content-Type:",NULL, FALSE},
1463
                                       {NULL,                NULL, FALSE}};
1464
1465
        enum
1466
        {
1467
                H_REPLY_TO        = 0,
1468
                H_CC                = 1,
1469
                H_REFERENCES        = 2,
1470
                H_BCC                = 3,
1471
                H_NEWSGROUPS    = 4,
1472
                H_FOLLOWUP_TO        = 5,
1473
                H_LIST_POST     = 6,
1474
                H_CONTENT_TYPE  = 7
1475
        };
1476
1477
        FILE *fp;
1478
        gchar *charset = NULL;
1479
1480
        g_return_val_if_fail(msginfo != NULL, -1);
1481
1482
        if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1483
        procheader_get_header_fields(fp, hentry);
1484
        fclose(fp);
1485
1486
        if (hentry[H_CONTENT_TYPE].body != NULL) {
1487
                procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body,
1488
                                               NULL, &charset, NULL, NULL);
1489
                g_free(hentry[H_CONTENT_TYPE].body);
1490
                hentry[H_CONTENT_TYPE].body = NULL;
1491
        }
1492
        if (hentry[H_REPLY_TO].body != NULL) {
1493
                if (hentry[H_REPLY_TO].body[0] != '\0') {
1494
                        compose->replyto =
1495
                                conv_unmime_header(hentry[H_REPLY_TO].body,
1496
                                                   charset);
1497
                }
1498
                g_free(hentry[H_REPLY_TO].body);
1499
                hentry[H_REPLY_TO].body = NULL;
1500
        }
1501
        if (hentry[H_CC].body != NULL) {
1502
                compose->cc = conv_unmime_header(hentry[H_CC].body, charset);
1503
                g_free(hentry[H_CC].body);
1504
                hentry[H_CC].body = NULL;
1505
        }
1506
        if (hentry[H_REFERENCES].body != NULL) {
1507
                if (compose->mode == COMPOSE_REEDIT)
1508
                        compose->references = hentry[H_REFERENCES].body;
1509
                else {
1510
                        compose->references = compose_parse_references
1511
                                (hentry[H_REFERENCES].body, msginfo->msgid);
1512
                        g_free(hentry[H_REFERENCES].body);
1513
                }
1514
                hentry[H_REFERENCES].body = NULL;
1515
        }
1516
        if (hentry[H_BCC].body != NULL) {
1517
                if (compose->mode == COMPOSE_REEDIT)
1518
                        compose->bcc =
1519
                                conv_unmime_header(hentry[H_BCC].body, charset);
1520
                g_free(hentry[H_BCC].body);
1521
                hentry[H_BCC].body = NULL;
1522
        }
1523
        if (hentry[H_NEWSGROUPS].body != NULL) {
1524
                compose->newsgroups = hentry[H_NEWSGROUPS].body;
1525
                hentry[H_NEWSGROUPS].body = NULL;
1526
        }
1527
        if (hentry[H_FOLLOWUP_TO].body != NULL) {
1528
                if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1529
                        compose->followup_to =
1530
                                conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1531
                                                   charset);
1532
                }
1533
                g_free(hentry[H_FOLLOWUP_TO].body);
1534
                hentry[H_FOLLOWUP_TO].body = NULL;
1535
        }
1536
        if (hentry[H_LIST_POST].body != NULL) {
1537
                gchar *to = NULL;
1538
1539
                extract_address(hentry[H_LIST_POST].body);
1540
                if (hentry[H_LIST_POST].body[0] != '\0') {
1541
                        scan_mailto_url(hentry[H_LIST_POST].body,
1542
                                        &to, NULL, NULL, NULL, NULL, NULL);
1543
                        if (to) {
1544
                                g_free(compose->ml_post);
1545
                                compose->ml_post = to;
1546
                        }
1547
                }
1548
                g_free(hentry[H_LIST_POST].body);
1549
                hentry[H_LIST_POST].body = NULL;
1550
        }
1551
1552
        g_free(charset);
1553
1554
        if (compose->mode == COMPOSE_REEDIT) {
1555
                if (msginfo->inreplyto && *msginfo->inreplyto)
1556
                        compose->inreplyto = g_strdup(msginfo->inreplyto);
1557
                return 0;
1558
        }
1559
1560
        if (msginfo->msgid && *msginfo->msgid)
1561
                compose->inreplyto = g_strdup(msginfo->msgid);
1562
1563
        if (!compose->references) {
1564
                if (msginfo->msgid && *msginfo->msgid) {
1565
                        if (msginfo->inreplyto && *msginfo->inreplyto)
1566
                                compose->references =
1567
                                        g_strdup_printf("<%s>\n\t<%s>",
1568
                                                        msginfo->inreplyto,
1569
                                                        msginfo->msgid);
1570
                        else
1571
                                compose->references =
1572
                                        g_strconcat("<", msginfo->msgid, ">",
1573
                                                    NULL);
1574
                } else if (msginfo->inreplyto && *msginfo->inreplyto) {
1575
                        compose->references =
1576
                                g_strconcat("<", msginfo->inreplyto, ">",
1577
                                            NULL);
1578
                }
1579
        }
1580
1581
        return 0;
1582
}
1583
1584
static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1585
{
1586
        GSList *ref_id_list, *cur;
1587
        GString *new_ref;
1588
        gchar *new_ref_str;
1589
1590
        ref_id_list = references_list_append(NULL, ref);
1591
        if (!ref_id_list) return NULL;
1592
        if (msgid && *msgid)
1593
                ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1594
1595
        for (;;) {
1596
                gint len = 0;
1597
1598
                for (cur = ref_id_list; cur != NULL; cur = cur->next)
1599
                        /* "<" + Message-ID + ">" + CR+LF+TAB */
1600
                        len += strlen((gchar *)cur->data) + 5;
1601
1602
                if (len > MAX_REFERENCES_LEN) {
1603
                        /* remove second message-ID */
1604
                        if (ref_id_list && ref_id_list->next &&
1605
                            ref_id_list->next->next) {
1606
                                g_free(ref_id_list->next->data);
1607
                                ref_id_list = g_slist_remove
1608
                                        (ref_id_list, ref_id_list->next->data);
1609
                        } else {
1610
                                slist_free_strings(ref_id_list);
1611
                                g_slist_free(ref_id_list);
1612
                                return NULL;
1613
                        }
1614
                } else
1615
                        break;
1616
        }
1617
1618
        new_ref = g_string_new("");
1619
        for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1620
                if (new_ref->len > 0)
1621
                        g_string_append(new_ref, "\n\t");
1622
                g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1623
        }
1624
1625
        slist_free_strings(ref_id_list);
1626
        g_slist_free(ref_id_list);
1627
1628
        new_ref_str = new_ref->str;
1629
        g_string_free(new_ref, FALSE);
1630
1631
        return new_ref_str;
1632
}
1633
1634
static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo)
1635
{
1636
        static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE},
1637
                                       {"X-Sylpheed-Forward:", NULL, FALSE},
1638
                                       {"REP:", NULL, FALSE},
1639
                                       {"FWD:", NULL, FALSE},
1640
                                       {"Disposition-Notification-To:", NULL, FALSE},
1641
                                       {"X-Sylpheed-Compose-AutoWrap:", NULL, FALSE},
1642
                                       {"X-Sylpheed-Compose-CheckSpell:", NULL, FALSE},
1643
                                       {"X-Sylpheed-Compose-SpellLang:", NULL, FALSE},
1644
                                       {"X-Sylpheed-Compose-UseSigning:", NULL, FALSE},
1645
                                       {"X-Sylpheed-Compose-UseEncryption:", NULL, FALSE},
1646
                                       {NULL, NULL, FALSE}};
1647
1648
        enum
1649
        {
1650
                H_X_SYLPHEED_REPLY = 0,
1651
                H_X_SYLPHEED_FORWARD = 1,
1652
                H_REP = 2,
1653
                H_FWD = 3,
1654
                H_MDN = 4,
1655
                H_X_SYLPHEED_COMPOSE_AUTOWRAP = 5,
1656
                H_X_SYLPHEED_COMPOSE_CHECKSPELL = 6,
1657
                H_X_SYLPHEED_COMPOSE_SPELLLANG = 7,
1658
                H_X_SYLPHEED_COMPOSE_USESIGNING = 8,
1659
                H_X_SYLPHEED_COMPOSE_USEENCRYPTION = 9
1660
        };
1661
1662
        gchar *file;
1663
        FILE *fp;
1664
        gchar *str;
1665
        gchar buf[BUFFSIZE];
1666
        gint hnum;
1667
1668
        g_return_val_if_fail(msginfo != NULL, -1);
1669
1670
        file = procmsg_get_message_file(msginfo);
1671
1672
        if ((fp = g_fopen(file, "rb")) == NULL) {
1673
                FILE_OP_ERROR(file, "fopen");
1674
                return -1;
1675
        }
1676
1677
        while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
1678
               != -1) {
1679
                str = buf + strlen(hentry[hnum].name);
1680
                while (g_ascii_isspace(*str))
1681
                        ++str;
1682
                if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) &&
1683
                    !compose->reply_target) {
1684
                        compose->reply_target = g_strdup(str);
1685
                } else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) &&
1686
                           !compose->forward_targets) {
1687
                        compose->forward_targets = g_strdup(str);
1688
                } else if (hnum == H_MDN) {
1689
                        compose->use_mdn = TRUE;
1690
                } else if (hnum == H_X_SYLPHEED_COMPOSE_AUTOWRAP) {
1691
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1692
                                compose->autowrap = TRUE;
1693
                        else
1694
                                compose->autowrap = FALSE;
1695
#if USE_GTKSPELL
1696
                } else if (hnum == H_X_SYLPHEED_COMPOSE_CHECKSPELL) {
1697
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1698
                                compose->check_spell = TRUE;
1699
                        else
1700
                                compose->check_spell = FALSE;
1701
                } else if (hnum == H_X_SYLPHEED_COMPOSE_SPELLLANG) {
1702
                        g_free(compose->spell_lang);
1703
                        compose->spell_lang = g_strdup(str);
1704
#endif
1705
#if USE_GPGME
1706
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USESIGNING) {
1707
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1708
                                compose->use_signing = TRUE;
1709
                        else
1710
                                compose->use_signing = FALSE;
1711
                } else if (hnum == H_X_SYLPHEED_COMPOSE_USEENCRYPTION) {
1712
                        if (g_ascii_strcasecmp(str, "TRUE") == 0)
1713
                                compose->use_encryption = TRUE;
1714
                        else
1715
                                compose->use_encryption = FALSE;
1716
#endif
1717
                }
1718
        }
1719
1720
        fclose(fp);
1721
        g_free(file);
1722
1723
        return 0;
1724
}
1725
1726
static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1727
                                const gchar *fmt, const gchar *qmark,
1728
                                const gchar *body)
1729
{
1730
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1731
        GtkTextBuffer *buffer;
1732
        GtkTextMark *mark;
1733
        GtkTextIter iter;
1734
        static MsgInfo dummyinfo;
1735
        gchar *quote_str = NULL;
1736
        gchar *buf;
1737
        gchar *p, *lastp;
1738
        gint len;
1739
        gboolean prev_autowrap;
1740
1741
        if (!msginfo)
1742
                msginfo = &dummyinfo;
1743
1744
        if (qmark != NULL) {
1745
                quote_fmt_init(msginfo, NULL, NULL);
1746
                quote_fmt_scan_string(qmark);
1747
                quote_fmt_parse();
1748
1749
                buf = quote_fmt_get_buffer();
1750
                if (buf == NULL)
1751
                        alertpanel_error(_("Quote mark format error."));
1752
                else
1753
                        Xstrdup_a(quote_str, buf, return NULL)
1754
        }
1755
1756
        if (fmt && *fmt != '\0') {
1757
                quote_fmt_init(msginfo, quote_str, body);
1758
                quote_fmt_scan_string(fmt);
1759
                quote_fmt_parse();
1760
1761
                buf = quote_fmt_get_buffer();
1762
                if (buf == NULL) {
1763
                        alertpanel_error(_("Message reply/forward format error."));
1764
                        return NULL;
1765
                }
1766
        } else
1767
                buf = "";
1768
1769
        prev_autowrap = compose->autowrap;
1770
        compose->autowrap = FALSE;
1771
1772
        buffer = gtk_text_view_get_buffer(text);
1773
        mark = gtk_text_buffer_get_insert(buffer);
1774
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1775
1776
        for (p = buf; *p != '\0'; ) {
1777
                lastp = strchr(p, '\n');
1778
                len = lastp ? lastp - p + 1 : -1;
1779
                gtk_text_buffer_insert(buffer, &iter, p, len);
1780
                if (lastp)
1781
                        p = lastp + 1;
1782
                else
1783
                        break;
1784
        }
1785
1786
        compose->autowrap = prev_autowrap;
1787
        if (compose->autowrap)
1788
                compose_wrap_all(compose);
1789
1790
        return buf;
1791
}
1792
1793
static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1794
                                    ComposeMode mode)
1795
{
1796
        GSList *cc_list = NULL;
1797
        GSList *cur;
1798
        GHashTable *to_table;
1799
        gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1800
        gchar *from_str = NULL, *to_str = NULL, *cc_str = NULL,
1801
              *replyto_str = NULL;
1802
        gboolean address_only = prefs_common.reply_address_only;
1803
1804
        g_return_if_fail(compose->account != NULL);
1805
        g_return_if_fail(msginfo != NULL);
1806
1807
        switch (COMPOSE_MODE(mode)) {
1808
        case COMPOSE_REPLY_TO_SENDER:
1809
                ignore_replyto = TRUE;
1810
                break;
1811
        case COMPOSE_REPLY_TO_ALL:
1812
                to_all = TRUE;
1813
                break;
1814
        case COMPOSE_REPLY_TO_LIST:
1815
                to_ml = TRUE;
1816
                break;
1817
        default:
1818
                break;
1819
        }
1820
1821
        if (address_only) {
1822
                from_str = extract_addresses(msginfo->from);
1823
                to_str = extract_addresses(msginfo->to);
1824
                cc_str = extract_addresses(compose->cc);
1825
                replyto_str = extract_addresses(compose->replyto);
1826
        } else {
1827
                from_str = g_strdup(msginfo->from);
1828
                to_str = g_strdup(msginfo->to);
1829
                cc_str = g_strdup(compose->cc);
1830
                replyto_str = g_strdup(compose->replyto);
1831
        }
1832
1833
        if (compose->account->protocol != A_NNTP) {
1834
                if (to_ml && compose->ml_post) {
1835
                        /* don't reply to list for confirmation request etc. */
1836
                        if ((!to_str ||
1837
                             !strcasestr_with_skip_quote(to_str,
1838
                                                         compose->ml_post)) &&
1839
                            (!cc_str ||
1840
                             !strcasestr_with_skip_quote(cc_str,
1841
                                                         compose->ml_post)))
1842
                                to_ml = FALSE;
1843
                }
1844
                if (to_ml && compose->ml_post) {
1845
                        compose_entry_set(compose, compose->ml_post,
1846
                                          COMPOSE_ENTRY_TO);
1847
                        if (replyto_str &&
1848
                            !address_equal(replyto_str, compose->ml_post))
1849
                                compose_entry_set(compose, replyto_str,
1850
                                                  COMPOSE_ENTRY_CC);
1851
                } else if (prefs_common.inherit_recipient_on_self_reply &&
1852
                           address_equal(compose->account->address, from_str)) {
1853
                        compose_entry_set(compose, to_str, COMPOSE_ENTRY_TO);
1854
                        if (to_all) {
1855
                                compose_entry_set(compose, cc_str,
1856
                                                  COMPOSE_ENTRY_CC);
1857
                                to_all = FALSE;
1858
                        }
1859
                } else {
1860
                        compose_entry_set(compose, 
1861
                                          (replyto_str && !ignore_replyto)
1862
                                          ? replyto_str
1863
                                          : from_str ? from_str : "",
1864
                                          COMPOSE_ENTRY_TO);
1865
                }
1866
        } else {
1867
                if (ignore_replyto) {
1868
                        compose_entry_set(compose, from_str ? from_str : "",
1869
                                          COMPOSE_ENTRY_TO);
1870
                } else {
1871
                        if (to_all) {
1872
                                compose_entry_set
1873
                                        (compose, 
1874
                                         (replyto_str && !ignore_replyto)
1875
                                         ? replyto_str
1876
                                         : from_str ? from_str : "",
1877
                                         COMPOSE_ENTRY_TO);
1878
                        }
1879
                        compose_entry_set(compose,
1880
                                          compose->followup_to ? compose->followup_to
1881
                                          : compose->newsgroups ? compose->newsgroups
1882
                                          : "",
1883
                                          COMPOSE_ENTRY_NEWSGROUPS);
1884
                }
1885
        }
1886
1887
        if (msginfo->subject && *msginfo->subject) {
1888
                gchar *buf;
1889
                gchar *p;
1890
1891
                buf = g_strdup(msginfo->subject);
1892
1893
                if (msginfo->folder && msginfo->folder->trim_compose_subject)
1894
                        trim_subject(buf);
1895
1896
                while (!g_ascii_strncasecmp(buf, "Re:", 3)) {
1897
                        p = buf + 3;
1898
                        while (g_ascii_isspace(*p)) p++;
1899
                        memmove(buf, p, strlen(p) + 1);
1900
                }
1901
1902
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1903
                compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1904
1905
                g_free(buf);
1906
        } else
1907
                compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1908
1909
        if (!compose->replyto && to_ml && compose->ml_post)
1910
                goto done;
1911
        if (!to_all)
1912
                goto done;
1913
1914
        if (replyto_str && from_str)
1915
                cc_list = address_list_append_orig(cc_list, from_str);
1916
        cc_list = address_list_append_orig(cc_list, to_str);
1917
        cc_list = address_list_append_orig(cc_list, cc_str);
1918
1919
        to_table = g_hash_table_new(g_str_hash, g_str_equal);
1920
        if (replyto_str) {
1921
                gchar *replyto;
1922
1923
                replyto = g_strdup(replyto_str);
1924
                extract_address(replyto);
1925
                g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1926
        } else if (from_str) {
1927
                gchar *from;
1928
1929
                from = g_strdup(from_str);
1930
                extract_address(from);
1931
                g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1932
        }
1933
        if (compose->account->address)
1934
                g_hash_table_insert(to_table,
1935
                                    g_strdup(compose->account->address),
1936
                                    GINT_TO_POINTER(1));
1937
1938
        /* remove duplicate addresses */
1939
        for (cur = cc_list; cur != NULL; ) {
1940
                gchar *addr = (gchar *)cur->data;
1941
                GSList *next = cur->next;
1942
                gchar *addr_;
1943
1944
                addr_ = g_strdup(addr);
1945
                extract_address(addr_);
1946
                if (g_hash_table_lookup(to_table, addr_) != NULL) {
1947
                        cc_list = g_slist_remove(cc_list, addr);
1948
                        g_free(addr_);
1949
                        g_free(addr);
1950
                } else
1951
                        g_hash_table_insert(to_table, addr_, cur);
1952
1953
                cur = next;
1954
        }
1955
        hash_free_strings(to_table);
1956
        g_hash_table_destroy(to_table);
1957
1958
        if (cc_list) {
1959
                for (cur = cc_list; cur != NULL; cur = cur->next)
1960
                        compose_entry_append(compose, (gchar *)cur->data,
1961
                                             COMPOSE_ENTRY_CC);
1962
                slist_free_strings(cc_list);
1963
                g_slist_free(cc_list);
1964
        }
1965
1966
done:
1967
        g_free(from_str);
1968
        g_free(to_str);
1969
        g_free(cc_str);
1970
        g_free(replyto_str);
1971
}
1972
1973
static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1974
{
1975
        g_return_if_fail(msginfo != NULL);
1976
        g_return_if_fail(compose->account != NULL);
1977
1978
        compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1979
        compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1980
        compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1981
        compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1982
        if (compose->account->protocol == A_NNTP) {
1983
                compose_entry_set(compose, compose->newsgroups,
1984
                                  COMPOSE_ENTRY_NEWSGROUPS);
1985
                compose_entry_set(compose, compose->followup_to,
1986
                                  COMPOSE_ENTRY_FOLLOWUP_TO);
1987
        }
1988
        compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1989
}
1990
1991
static void compose_insert_sig(Compose *compose, gboolean append,
1992
                               gboolean replace, gboolean scroll)
1993
{
1994
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1995
        GtkTextBuffer *buffer;
1996
        GtkTextMark *mark;
1997
        GtkTextIter iter;
1998
        gchar *sig_str;
1999
        gboolean prev_autowrap;
2000
2001
        g_return_if_fail(compose->account != NULL);
2002
2003
        prev_autowrap = compose->autowrap;
2004
        compose->autowrap = FALSE;
2005
2006
        buffer = gtk_text_view_get_buffer(text);
2007
        mark = gtk_text_buffer_get_insert(buffer);
2008
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2009
2010
        if (replace) {
2011
                GtkTextIter start_iter, end_iter;
2012
2013
                gtk_text_buffer_get_start_iter(buffer, &start_iter);
2014
                gtk_text_buffer_get_end_iter(buffer, &iter);
2015
2016
                while (gtk_text_iter_begins_tag
2017
                        (&start_iter, compose->sig_tag) ||
2018
                       gtk_text_iter_forward_to_tag_toggle
2019
                        (&start_iter, compose->sig_tag)) {
2020
                        end_iter = start_iter;
2021
                        if (gtk_text_iter_forward_to_tag_toggle
2022
                                (&end_iter, compose->sig_tag)) {
2023
                                gtk_text_buffer_delete
2024
                                        (buffer, &start_iter, &end_iter);
2025
                                iter = start_iter;
2026
                        }
2027
                }
2028
        }
2029
2030
        if (scroll) {
2031
                if (append)
2032
                        gtk_text_buffer_get_end_iter(buffer, &iter);
2033
                else
2034
                        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2035
        }
2036
2037
        sig_str = compose_get_signature_str(compose);
2038
        if (sig_str) {
2039
                if (!replace)
2040
                        gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
2041
                else if (!gtk_text_iter_starts_line(&iter))
2042
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2043
                gtk_text_buffer_insert_with_tags
2044
                        (buffer, &iter, sig_str, -1, compose->sig_tag, NULL);
2045
                g_free(sig_str);
2046
                if (scroll)
2047
                        gtk_text_buffer_place_cursor(buffer, &iter);
2048
        }
2049
2050
        compose->autowrap = prev_autowrap;
2051
        if (compose->autowrap)
2052
                compose_wrap_all(compose);
2053
2054
        if (scroll)
2055
                gtk_text_view_scroll_mark_onscreen(text, mark);
2056
}
2057
2058
static void compose_enable_sig(Compose *compose)
2059
{
2060
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2061
        GtkTextBuffer *buffer;
2062
        GtkTextIter iter, start, end;
2063
        gchar *sig_str;
2064
2065
        g_return_if_fail(compose->account != NULL);
2066
2067
        buffer = gtk_text_view_get_buffer(text);
2068
        gtk_text_buffer_get_start_iter(buffer, &iter);
2069
2070
        sig_str = compose_get_signature_str(compose);
2071
        if (!sig_str)
2072
                return;
2073
2074
        if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) {
2075
                end = start;
2076
                gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1));
2077
                gtk_text_buffer_apply_tag(buffer, compose->sig_tag,
2078
                                          &start, &end);
2079
        }
2080
2081
        g_free(sig_str);
2082
}
2083
2084
static gchar *compose_get_signature_str(Compose *compose)
2085
{
2086
        gchar *sig_path;
2087
        gchar *sig_body = NULL;
2088
        gchar *utf8_sig_body = NULL;
2089
        gchar *utf8_sig_str = NULL;
2090
2091
        g_return_val_if_fail(compose->account != NULL, NULL);
2092
2093
        if (compose->account->sig_type == SIG_DIRECT) {
2094
                gchar *p, *sp;
2095
2096
                if (!compose->account->sig_text)
2097
                        return NULL;
2098
2099
                sp = compose->account->sig_text;
2100
                p = sig_body = g_malloc(strlen(compose->account->sig_text) + 1);
2101
                while (*sp) {
2102
                        if (*sp == '\\' && *(sp + 1) == 'n') {
2103
                                *p++ = '\n';
2104
                                sp += 2;
2105
                        } else
2106
                                *p++ = *sp++;
2107
                }
2108
                *p = '\0';
2109
2110
                if (prefs_common.sig_sep) {
2111
                        utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2112
                                                   sig_body, NULL);
2113
                        g_free(sig_body);
2114
                } else
2115
                        utf8_sig_str = sig_body;
2116
2117
                return utf8_sig_str;
2118
        }
2119
2120
        if (!compose->account->sig_path)
2121
                return NULL;
2122
2123
        if (g_path_is_absolute(compose->account->sig_path) ||
2124
            compose->account->sig_type == SIG_COMMAND)
2125
                sig_path = g_strdup(compose->account->sig_path);
2126
        else {
2127
#ifdef G_OS_WIN32
2128
                sig_path = g_strconcat(get_rc_dir(),
2129
#else
2130
                sig_path = g_strconcat(get_home_dir(),
2131
#endif
2132
                                       G_DIR_SEPARATOR_S,
2133
                                       compose->account->sig_path, NULL);
2134
        }
2135
2136
        if (compose->account->sig_type == SIG_FILE) {
2137
                if (!is_file_or_fifo_exist(sig_path)) {
2138
                        debug_print("can't open signature file: %s\n",
2139
                                    sig_path);
2140
                        g_free(sig_path);
2141
                        return NULL;
2142
                }
2143
        }
2144
2145
        if (compose->account->sig_type == SIG_COMMAND)
2146
                sig_body = get_command_output(sig_path);
2147
        else {
2148
                gchar *tmp;
2149
2150
                tmp = file_read_to_str(sig_path);
2151
                if (!tmp)
2152
                        return NULL;
2153
                sig_body = normalize_newlines(tmp);
2154
                g_free(tmp);
2155
        }
2156
        g_free(sig_path);
2157
2158
        if (sig_body) {
2159
                gint error = 0;
2160
2161
                utf8_sig_body = conv_codeset_strdup_full
2162
                        (sig_body, conv_get_locale_charset_str(),
2163
                         CS_INTERNAL, &error);
2164
                if (!utf8_sig_body || error != 0) {
2165
                        if (g_utf8_validate(sig_body, -1, NULL) == TRUE) {
2166
                                g_free(utf8_sig_body);
2167
                                utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2168
                        }
2169
                } else {
2170
                        g_free(sig_body);
2171
                        sig_body = utf8_sig_body;
2172
                        utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2173
                }
2174
                g_free(sig_body);
2175
        }
2176
2177
        if (prefs_common.sig_sep) {
2178
                utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2179
                                           utf8_sig_body, NULL);
2180
                g_free(utf8_sig_body);
2181
        } else
2182
                utf8_sig_str = utf8_sig_body;
2183
2184
        return utf8_sig_str;
2185
}
2186
2187
static void compose_insert_file(Compose *compose, const gchar *file,
2188
                                gboolean scroll)
2189
{
2190
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2191
        GtkTextBuffer *buffer;
2192
        GtkTextMark *mark;
2193
        GtkTextIter iter;
2194
        const gchar *cur_encoding;
2195
        gchar buf[BUFFSIZE];
2196
        gint len;
2197
        FILE *fp;
2198
        gboolean prev_autowrap;
2199
        CharSet enc;
2200
2201
        g_return_if_fail(file != NULL);
2202
2203
        enc = conv_check_file_encoding(file);
2204
2205
        if ((fp = g_fopen(file, "rb")) == NULL) {
2206
                FILE_OP_ERROR(file, "fopen");
2207
                return;
2208
        }
2209
2210
        prev_autowrap = compose->autowrap;
2211
        compose->autowrap = FALSE;
2212
2213
        buffer = gtk_text_view_get_buffer(text);
2214
        mark = gtk_text_buffer_get_insert(buffer);
2215
        gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2216
2217
        cur_encoding = conv_get_locale_charset_str();
2218
2219
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2220
                gchar *str;
2221
                gint error = 0;
2222
2223
                if (enc == C_UTF_8) {
2224
                        str = conv_utf8todisp(buf, NULL);
2225
                } else {
2226
                        str = conv_codeset_strdup_full(buf, cur_encoding,
2227
                                                       CS_INTERNAL, &error);
2228
                        if (!str || error != 0) {
2229
                                if (g_utf8_validate(buf, -1, NULL) == TRUE) {
2230
                                        g_free(str);
2231
                                        str = g_strdup(buf);
2232
                                }
2233
                        }
2234
                        if (!str) continue;
2235
                }
2236
2237
                /* strip <CR> if DOS/Windows file,
2238
                   replace <CR> with <LF> if Macintosh file. */
2239
                strcrchomp(str);
2240
                len = strlen(str);
2241
                if (len > 0 && str[len - 1] != '\n') {
2242
                        while (--len >= 0)
2243
                                if (str[len] == '\r') str[len] = '\n';
2244
                }
2245
2246
                gtk_text_buffer_insert(buffer, &iter, str, -1);
2247
                g_free(str);
2248
        }
2249
2250
        fclose(fp);
2251
2252
        compose->autowrap = prev_autowrap;
2253
        if (compose->autowrap)
2254
                compose_wrap_all(compose);
2255
2256
        if (scroll)
2257
                gtk_text_view_scroll_mark_onscreen(text, mark);
2258
}
2259
2260
static void compose_attach_append(Compose *compose, const gchar *file,
2261
                                  const gchar *filename,
2262
                                  const gchar *content_type)
2263
{
2264
        GtkTreeIter iter;
2265
        AttachInfo *ainfo;
2266
        FILE *fp;
2267
        off_t size;
2268
2269
        g_return_if_fail(file != NULL);
2270
        g_return_if_fail(*file != '\0');
2271
2272
        if (!is_file_exist(file)) {
2273
                g_warning(_("File %s doesn't exist\n"), file);
2274
                return;
2275
        }
2276
        if ((size = get_file_size(file)) < 0) {
2277
                g_warning(_("Can't get file size of %s\n"), file);
2278
                return;
2279
        }
2280
        if (size == 0) {
2281
                alertpanel_notice(_("File %s is empty."), file);
2282
                return;
2283
        }
2284
        if ((fp = g_fopen(file, "rb")) == NULL) {
2285
                alertpanel_error(_("Can't read %s."), file);
2286
                return;
2287
        }
2288
        fclose(fp);
2289
2290
        if (!compose->use_attach) {
2291
                GtkItemFactory *ifactory;
2292
2293
                ifactory = gtk_item_factory_from_widget(compose->menubar);
2294
                menu_set_active(ifactory, "/View/Attachment", TRUE);
2295
        }
2296
2297
        ainfo = g_new0(AttachInfo, 1);
2298
        ainfo->file = g_strdup(file);
2299
2300
        if (content_type) {
2301
                ainfo->content_type = g_strdup(content_type);
2302
                if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2303
                        MsgInfo *msginfo;
2304
                        MsgFlags flags = {0, 0};
2305
                        const gchar *name;
2306
2307
                        if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2308
                                ainfo->encoding = ENC_7BIT;
2309
                        else
2310
                                ainfo->encoding = ENC_8BIT;
2311
2312
                        msginfo = procheader_parse_file(file, flags, FALSE);
2313
                        if (msginfo && msginfo->subject)
2314
                                name = msginfo->subject;
2315
                        else
2316
                                name = g_basename(filename ? filename : file);
2317
2318
                        ainfo->name = g_strdup_printf(_("Message: %s"), name);
2319
2320
                        procmsg_msginfo_free(msginfo);
2321
                } else {
2322
                        if (!g_ascii_strncasecmp(content_type, "text", 4))
2323
                                ainfo->encoding = procmime_get_encoding_for_text_file(file);
2324
                        else
2325
                                ainfo->encoding = ENC_BASE64;
2326
                        ainfo->name = g_strdup
2327
                                (g_basename(filename ? filename : file));
2328
                }
2329
        } else {
2330
                ainfo->content_type = procmime_get_mime_type(file);
2331
                if (!ainfo->content_type) {
2332
                        ainfo->content_type =
2333
                                g_strdup("application/octet-stream");
2334
                        ainfo->encoding = ENC_BASE64;
2335
                } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
2336
                        ainfo->encoding =
2337
                                procmime_get_encoding_for_text_file(file);
2338
                else
2339
                        ainfo->encoding = ENC_BASE64;
2340
                ainfo->name = g_strdup(g_basename(filename ? filename : file));        
2341
        }
2342
        ainfo->size = size;
2343
2344
        gtk_list_store_append(compose->attach_store, &iter);
2345
        gtk_list_store_set(compose->attach_store, &iter,
2346
                           COL_MIMETYPE, ainfo->content_type,
2347
                           COL_SIZE, to_human_readable(ainfo->size),
2348
                           COL_NAME, ainfo->name,
2349
                           COL_ATTACH_INFO, ainfo,
2350
                           -1);
2351
}
2352
2353
static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2354
{
2355
        MimeInfo *mimeinfo;
2356
        MimeInfo *child;
2357
        gchar *infile;
2358
        gchar *outfile;
2359
2360
        mimeinfo = procmime_scan_message(msginfo);
2361
        if (!mimeinfo) return;
2362
2363
        infile = procmsg_get_message_file_path(msginfo);
2364
2365
        child = mimeinfo;
2366
        while (child != NULL) {
2367
                if (child->children || child->mime_type == MIME_MULTIPART)
2368
                        goto next;
2369
                if (child->mime_type != MIME_MESSAGE_RFC822 &&
2370
                    child->mime_type != MIME_IMAGE &&
2371
                    child->mime_type != MIME_AUDIO &&
2372
                    child->mime_type != MIME_VIDEO &&
2373
                    !child->filename && !child->name)
2374
                        goto next;
2375
2376
                outfile = procmime_get_tmp_file_name(child);
2377
                if (procmime_get_part(outfile, infile, child) < 0) {
2378
                        g_warning(_("Can't get the part of multipart message."));
2379
                        g_free(outfile);
2380
                        goto next;
2381
                }
2382
2383
                compose_attach_append
2384
                        (compose, outfile,
2385
                         child->filename ? child->filename : child->name,
2386
                         child->content_type);
2387
2388
                g_free(outfile);
2389
2390
next:
2391
                if (child->mime_type == MIME_MESSAGE_RFC822)
2392
                        child = child->next;
2393
                else
2394
                        child = procmime_mimeinfo_next(child);
2395
        }
2396
2397
        g_free(infile);
2398
        procmime_mimeinfo_free_all(mimeinfo);
2399
}
2400
2401
#define INDENT_CHARS        ">|#"
2402
2403
typedef enum {
2404
        WAIT_FOR_INDENT_CHAR,
2405
        WAIT_FOR_INDENT_CHAR_OR_SPACE,
2406
} IndentState;
2407
2408
/* return indent length, we allow:
2409
   indent characters followed by indent characters or spaces/tabs,
2410
   alphabets and numbers immediately followed by indent characters,
2411
   and the repeating sequences of the above
2412
   If quote ends with multiple spaces, only the first one is included. */
2413
static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
2414
                                    const GtkTextIter *start, gint *len)
2415
{
2416
        GtkTextIter iter = *start;
2417
        gunichar wc;
2418
        gchar ch[6];
2419
        gint clen;
2420
        IndentState state = WAIT_FOR_INDENT_CHAR;
2421
        gboolean is_space;
2422
        gboolean is_indent;
2423
        gint alnum_count = 0;
2424
        gint space_count = 0;
2425
        gint quote_len = 0;
2426
2427
        while (!gtk_text_iter_ends_line(&iter)) {
2428
                wc = gtk_text_iter_get_char(&iter);
2429
                if (g_unichar_iswide(wc))
2430
                        break;
2431
                clen = g_unichar_to_utf8(wc, ch);
2432
                if (clen != 1)
2433
                        break;
2434
2435
                is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE;
2436
                is_space = g_unichar_isspace(wc);
2437
2438
                if (state == WAIT_FOR_INDENT_CHAR) {
2439
                        if (!is_indent && !g_unichar_isalnum(wc))
2440
                                break;
2441
                        if (is_indent) {
2442
                                quote_len += alnum_count + space_count + 1;
2443
                                alnum_count = space_count = 0;
2444
                                state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2445
                        } else
2446
                                alnum_count++;
2447
                } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2448
                        if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2449
                                break;
2450
                        if (is_space)
2451
                                space_count++;
2452
                        else if (is_indent) {
2453
                                quote_len += alnum_count + space_count + 1;
2454
                                alnum_count = space_count = 0;
2455
                        } else {
2456
                                alnum_count++;
2457
                                state = WAIT_FOR_INDENT_CHAR;
2458
                        }
2459
                }
2460
2461
                gtk_text_iter_forward_char(&iter);
2462
        }
2463
2464
        if (quote_len > 0 && space_count > 0)
2465
                quote_len++;
2466
2467
        if (len)
2468
                *len = quote_len;
2469
2470
        if (quote_len > 0) {
2471
                iter = *start;
2472
                gtk_text_iter_forward_chars(&iter, quote_len);
2473
                return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2474
        }
2475
2476
        return NULL;
2477
}
2478
2479
/* return TRUE if the line is itemized */
2480
static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2481
                                    const GtkTextIter *start)
2482
{
2483
        GtkTextIter iter = *start;
2484
        gunichar wc;
2485
        gchar ch[6];
2486
        gint clen;
2487
2488
        if (gtk_text_iter_ends_line(&iter))
2489
                return FALSE;
2490
2491
        while (1) {
2492
                wc = gtk_text_iter_get_char(&iter);
2493
                if (!g_unichar_isspace(wc))
2494
                        break;
2495
                gtk_text_iter_forward_char(&iter);
2496
                if (gtk_text_iter_ends_line(&iter))
2497
                        return FALSE;
2498
        }
2499
2500
        clen = g_unichar_to_utf8(wc, ch);
2501
2502
        /* (1), 2), 3. etc. */
2503
        if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) {
2504
                gboolean digit_appeared = FALSE;
2505
2506
                if (ch[0] == '(')
2507
                        gtk_text_iter_forward_char(&iter);
2508
2509
                while (1) {
2510
                        wc = gtk_text_iter_get_char(&iter);
2511
                        clen = g_unichar_to_utf8(wc, ch);
2512
                        if (g_unichar_isdigit(wc)) {
2513
                                digit_appeared = TRUE;
2514
                                gtk_text_iter_forward_char(&iter);
2515
                                if (gtk_text_iter_ends_line(&iter))
2516
                                        return FALSE;
2517
                        } else if (clen == 1 &&
2518
                                   (ch[0] == ')' || ch[0] == '.')) {
2519
                                if (!digit_appeared)
2520
                                        return FALSE;
2521
                                gtk_text_iter_forward_char(&iter);
2522
                                if (gtk_text_iter_ends_line(&iter))
2523
                                        return TRUE;
2524
                                wc = gtk_text_iter_get_char(&iter);
2525
                                if (g_unichar_isspace(wc))
2526
                                        return TRUE;
2527
                                else
2528
                                        return FALSE;
2529
                        } else
2530
                                return FALSE;
2531
                }
2532
        }
2533
2534
        if (clen != 1)
2535
                return FALSE;
2536
        if (!strchr("*-+", ch[0]))
2537
                return FALSE;
2538
2539
        gtk_text_iter_forward_char(&iter);
2540
        if (gtk_text_iter_ends_line(&iter))
2541
                return FALSE;
2542
        wc = gtk_text_iter_get_char(&iter);
2543
        if (g_unichar_isspace(wc))
2544
                return TRUE;
2545
        else if (ch[0] == '-') {
2546
                /* -- */
2547
                clen = g_unichar_to_utf8(wc, ch);
2548
                if (clen == 1 && ch[0] == '-')
2549
                        return TRUE;
2550
        }
2551
2552
        return FALSE;
2553
}
2554
2555
static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2556
                                           const GtkTextIter *start,
2557
                                           GtkTextIter *break_pos,
2558
                                           gint max_col,
2559
                                           gint quote_len)
2560
{
2561
        GtkTextIter iter = *start, line_end = *start;
2562
        PangoLogAttr *attrs;
2563
        gchar *str;
2564
        gchar *p;
2565
        gint len;
2566
        gint i;
2567
        gint col = 0;
2568
        gint pos = 0;
2569
        gboolean can_break = FALSE;
2570
        gboolean do_break = FALSE;
2571
        gboolean prev_dont_break = FALSE;
2572
2573
        gtk_text_iter_forward_to_line_end(&line_end);
2574
        str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2575
        len = g_utf8_strlen(str, -1);
2576
        /* g_print("breaking line: %d: %s (len = %d)\n",
2577
                gtk_text_iter_get_line(&iter), str, len); */
2578
        attrs = g_new(PangoLogAttr, len + 1);
2579
2580
        pango_default_break(str, -1, NULL, attrs, len + 1);
2581
2582
        p = str;
2583
2584
        /* skip quote and leading spaces */
2585
        for (i = 0; *p != '\0' && i < len; i++) {
2586
                gunichar wc;
2587
2588
                wc = g_utf8_get_char(p);
2589
                if (i >= quote_len && !g_unichar_isspace(wc))
2590
                        break;
2591
                if (g_unichar_iswide(wc))
2592
                        col += 2;
2593
                else if (*p == '\t')
2594
                        col += 8;
2595
                else
2596
                        col++;
2597
                p = g_utf8_next_char(p);
2598
        }
2599
2600
        for (; *p != '\0' && i < len; i++) {
2601
                PangoLogAttr *attr = attrs + i;
2602
                gunichar wc;
2603
                gint uri_len;
2604
2605
                if (attr->is_line_break && can_break && !prev_dont_break)
2606
                        pos = i;
2607
2608
                /* don't wrap URI */
2609
                if ((uri_len = get_uri_len(p)) > 0) {
2610
                        col += uri_len;
2611
                        if (pos > 0 && col > max_col) {
2612
                                do_break = TRUE;
2613
                                break;
2614
                        }
2615
                        i += uri_len - 1;
2616
                        p += uri_len;
2617
                        can_break = TRUE;
2618
                        continue;
2619
                }
2620
2621
                wc = g_utf8_get_char(p);
2622
                if (g_unichar_iswide(wc)) {
2623
                        col += 2;
2624
                        if (prev_dont_break && can_break && attr->is_line_break)
2625
                                pos = i;
2626
                } else if (*p == '\t')
2627
                        col += 8;
2628
                else
2629
                        col++;
2630
                if (pos > 0 && col > max_col) {
2631
                        do_break = TRUE;
2632
                        break;
2633
                }
2634
2635
                if (*p == '-' || *p == '/')
2636
                        prev_dont_break = TRUE;
2637
                else
2638
                        prev_dont_break = FALSE;
2639
2640
                p = g_utf8_next_char(p);
2641
                can_break = TRUE;
2642
        }
2643
2644
        debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
2645
2646
        g_free(attrs);
2647
        g_free(str);
2648
2649
        *break_pos = *start;
2650
        gtk_text_iter_set_line_offset(break_pos, pos);
2651
2652
        return do_break;
2653
}
2654
2655
static gboolean compose_join_next_line(GtkTextBuffer *buffer,
2656
                                       GtkTextIter *iter,
2657
                                       const gchar *quote_str)
2658
{
2659
        GtkTextIter iter_ = *iter, cur, prev, next, end;
2660
        PangoLogAttr attrs[3];
2661
        gchar *str;
2662
        gchar *next_quote_str;
2663
        gunichar wc1, wc2;
2664
        gint quote_len;
2665
        gboolean keep_cursor = FALSE;
2666
2667
        if (!gtk_text_iter_forward_line(&iter_) ||
2668
            gtk_text_iter_ends_line(&iter_))
2669
                return FALSE;
2670
2671
        next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
2672
2673
        if ((quote_str || next_quote_str) &&
2674
            strcmp2(quote_str, next_quote_str) != 0) {
2675
                g_free(next_quote_str);
2676
                return FALSE;
2677
        }
2678
        g_free(next_quote_str);
2679
2680
        end = iter_;
2681
        if (quote_len > 0) {
2682
                gtk_text_iter_forward_chars(&end, quote_len);
2683
                if (gtk_text_iter_ends_line(&end))
2684
                        return FALSE;
2685
        }
2686
2687
        /* don't join itemized lines */
2688
        if (compose_is_itemized(buffer, &end))
2689
                return FALSE;
2690
2691
        /* delete quote str */
2692
        if (quote_len > 0)
2693
                gtk_text_buffer_delete(buffer, &iter_, &end);
2694
2695
        /* delete linebreak and extra spaces */
2696
        prev = cur = iter_;
2697
        while (gtk_text_iter_backward_char(&cur)) {
2698
                wc1 = gtk_text_iter_get_char(&cur);
2699
                if (!g_unichar_isspace(wc1))
2700
                        break;
2701
                prev = cur;
2702
        }
2703
        next = cur = iter_;
2704
        while (!gtk_text_iter_ends_line(&cur)) {
2705
                wc1 = gtk_text_iter_get_char(&cur);
2706
                if (!g_unichar_isspace(wc1))
2707
                        break;
2708
                gtk_text_iter_forward_char(&cur);
2709
                next = cur;
2710
        }
2711
        if (!gtk_text_iter_equal(&prev, &next)) {
2712
                GtkTextMark *mark;
2713
2714
                mark = gtk_text_buffer_get_insert(buffer);
2715
                gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
2716
                if (gtk_text_iter_equal(&prev, &cur))
2717
                        keep_cursor = TRUE;
2718
                gtk_text_buffer_delete(buffer, &prev, &next);
2719
        }
2720
        iter_ = prev;
2721
2722
        /* insert space if required */
2723
        gtk_text_iter_backward_char(&prev);
2724
        wc1 = gtk_text_iter_get_char(&prev);
2725
        wc2 = gtk_text_iter_get_char(&next);
2726
        gtk_text_iter_forward_char(&next);
2727
        str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
2728
        pango_default_break(str, -1, NULL, attrs, 3);
2729
        if (!attrs[1].is_line_break ||
2730
            (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
2731
                gtk_text_buffer_insert(buffer, &iter_, " ", 1);
2732
                if (keep_cursor) {
2733
                        gtk_text_iter_backward_char(&iter_);
2734
                        gtk_text_buffer_place_cursor(buffer, &iter_);
2735
                }
2736
        }
2737
        g_free(str);
2738
2739
        *iter = iter_;
2740
        return TRUE;
2741
}
2742
2743
static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
2744
{
2745
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2746
        GtkTextBuffer *buffer;
2747
        GtkTextIter iter, break_pos;
2748
        gchar *quote_str = NULL;
2749
        gint quote_len;
2750
        gboolean wrap_quote = prefs_common.linewrap_quote;
2751
        gboolean prev_autowrap = compose->autowrap;
2752
2753
        compose->autowrap = FALSE;
2754
2755
        buffer = gtk_text_view_get_buffer(text);
2756
2757
        if (par_iter) {
2758
                iter = *par_iter;
2759
        } else {
2760
                GtkTextMark *mark;
2761
                mark = gtk_text_buffer_get_insert(buffer);
2762
                gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2763
        }
2764
2765
        /* move to paragraph start */
2766
        gtk_text_iter_set_line_offset(&iter, 0);
2767
        if (gtk_text_iter_ends_line(&iter)) {
2768
                while (gtk_text_iter_ends_line(&iter) &&
2769
                       gtk_text_iter_forward_line(&iter))
2770
                        ;
2771
        } else {
2772
                while (gtk_text_iter_backward_line(&iter)) {
2773
                        if (gtk_text_iter_ends_line(&iter)) {
2774
                                gtk_text_iter_forward_line(&iter);
2775
                                break;
2776
                        }
2777
                }
2778
        }
2779
2780
        /* go until paragraph end (empty line) */
2781
        while (!gtk_text_iter_ends_line(&iter)) {
2782
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
2783
                if (quote_str) {
2784
                        if (!wrap_quote) {
2785
                                gtk_text_iter_forward_line(&iter);
2786
                                g_free(quote_str);
2787
                                continue;
2788
                        }
2789
                        debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
2790
                }
2791
2792
                if (compose_get_line_break_pos(buffer, &iter, &break_pos,
2793
                                               prefs_common.linewrap_len,
2794
                                               quote_len)) {
2795
                        GtkTextIter prev, next, cur;
2796
2797
                        gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
2798
2799
                        /* remove trailing spaces */
2800
                        cur = break_pos;
2801
                        gtk_text_iter_backward_char(&cur);
2802
                        prev = next = cur;
2803
                        while (!gtk_text_iter_starts_line(&cur)) {
2804
                                gunichar wc;
2805
2806
                                gtk_text_iter_backward_char(&cur);
2807
                                wc = gtk_text_iter_get_char(&cur);
2808
                                if (!g_unichar_isspace(wc))
2809
                                        break;
2810
                                prev = cur;
2811
                        }
2812
                        if (!gtk_text_iter_equal(&prev, &next)) {
2813
                                gtk_text_buffer_delete(buffer, &prev, &next);
2814
                                break_pos = next;
2815
                                gtk_text_iter_forward_char(&break_pos);
2816
                        }
2817
2818
                        if (quote_str)
2819
                                gtk_text_buffer_insert(buffer, &break_pos,
2820
                                                       quote_str, -1);
2821
2822
                        iter = break_pos;
2823
                        compose_join_next_line(buffer, &iter, quote_str);
2824
2825
                        /* move iter to current line start */
2826
                        gtk_text_iter_set_line_offset(&iter, 0);
2827
                } else {
2828
                        /* move iter to next line start */
2829
                        iter = break_pos;
2830
                        gtk_text_iter_forward_line(&iter);
2831
                }
2832
2833
                g_free(quote_str);
2834
        }
2835
2836
        if (par_iter)
2837
                *par_iter = iter;
2838
2839
        compose->autowrap = prev_autowrap;
2840
}
2841
2842
static void compose_wrap_all(Compose *compose)
2843
{
2844
        compose_wrap_all_full(compose, FALSE);
2845
}
2846
2847
static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
2848
{
2849
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2850
        GtkTextBuffer *buffer;
2851
        GtkTextIter iter;
2852
2853
        buffer = gtk_text_view_get_buffer(text);
2854
2855
        gtk_text_buffer_get_start_iter(buffer, &iter);
2856
        while (!gtk_text_iter_is_end(&iter))
2857
                compose_wrap_paragraph(compose, &iter);
2858
}
2859
2860
static void compose_set_title(Compose *compose)
2861
{
2862
        gchar *str;
2863
        gchar *edited;
2864
        const gchar *subject;
2865
2866
        subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2867
        if (!subject || subject[0] == '\0')
2868
                subject = _("(No Subject)");
2869
2870
        edited = compose->modified ? " *" : "";
2871
        str = g_strdup_printf(_("%s - Compose%s"), subject, edited);
2872
        gtk_window_set_title(GTK_WINDOW(compose->window), str);
2873
        g_free(str);
2874
}
2875
2876
static void compose_select_account(Compose *compose, PrefsAccount *account,
2877
                                   gboolean init)
2878
{
2879
        GtkItemFactory *ifactory;
2880
        PrefsAccount *prev_account;
2881
2882
        g_return_if_fail(account != NULL);
2883
2884
        prev_account = compose->account;
2885
        compose->account = account;
2886
2887
        compose_set_title(compose);
2888
2889
        ifactory = gtk_item_factory_from_widget(compose->menubar);
2890
2891
        if (account->protocol == A_NNTP &&
2892
            (init || prev_account->protocol != A_NNTP)) {
2893
                gtk_widget_show(compose->newsgroups_hbox);
2894
                gtk_widget_show(compose->newsgroups_entry);
2895
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2896
                compose->use_newsgroups = TRUE;
2897
2898
                menu_set_active(ifactory, "/View/To", FALSE);
2899
                menu_set_sensitive(ifactory, "/View/To", TRUE);
2900
                menu_set_active(ifactory, "/View/Cc", FALSE);
2901
                menu_set_sensitive(ifactory, "/View/Followup-To", TRUE);
2902
        } else if (account->protocol != A_NNTP &&
2903
                   (init || prev_account->protocol == A_NNTP)) {
2904
                gtk_widget_hide(compose->newsgroups_hbox);
2905
                gtk_widget_hide(compose->newsgroups_entry);
2906
                gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0);
2907
                gtk_widget_queue_resize(compose->table_vbox);
2908
                compose->use_newsgroups = FALSE;
2909
2910
                menu_set_active(ifactory, "/View/To", TRUE);
2911
                menu_set_sensitive(ifactory, "/View/To", FALSE);
2912
                menu_set_active(ifactory, "/View/Cc", TRUE);
2913
                menu_set_active(ifactory, "/View/Followup-To", FALSE);
2914
                menu_set_sensitive(ifactory, "/View/Followup-To", FALSE);
2915
        }
2916
2917
        if (account->set_autocc) {
2918
                compose_entry_show(compose, COMPOSE_ENTRY_CC);
2919
                if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2920
                        compose_entry_set(compose, account->auto_cc,
2921
                                          COMPOSE_ENTRY_CC);
2922
        }
2923
        if (account->set_autobcc) {
2924
                compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2925
                if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2926
                        compose_entry_set(compose, account->auto_bcc,
2927
                                          COMPOSE_ENTRY_BCC);
2928
        }
2929
        if (account->set_autoreplyto) {
2930
                compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2931
                if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2932
                        compose_entry_set(compose, account->auto_replyto,
2933
                                          COMPOSE_ENTRY_REPLY_TO);
2934
        }
2935
2936
#if USE_GPGME
2937
        if (rfc2015_is_available()) {
2938
                if (account->default_sign)
2939
                        menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2940
                if (account->default_encrypt)
2941
                        menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2942
        }
2943
#endif /* USE_GPGME */
2944
2945
        if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2946
                compose_insert_sig(compose, TRUE, TRUE, FALSE);
2947
}
2948
2949
static gboolean compose_check_for_valid_recipient(Compose *compose)
2950
{
2951
        const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
2952
        const gchar *newsgroups_raw = "";
2953
        gchar *to, *cc, *bcc;
2954
        gchar *newsgroups;
2955
2956
        if (compose->use_to)
2957
                to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2958
        if (compose->use_cc)
2959
                cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2960
        if (compose->use_bcc)
2961
                bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2962
        if (compose->use_newsgroups)
2963
                newsgroups_raw = gtk_entry_get_text
2964
                        (GTK_ENTRY(compose->newsgroups_entry));
2965
2966
        Xstrdup_a(to, to_raw, return FALSE);
2967
        Xstrdup_a(cc, cc_raw, return FALSE);
2968
        Xstrdup_a(bcc, bcc_raw, return FALSE);
2969
        Xstrdup_a(newsgroups, newsgroups_raw, return FALSE);
2970
        g_strstrip(to);
2971
        g_strstrip(cc);
2972
        g_strstrip(bcc);
2973
        g_strstrip(newsgroups);
2974
2975
        if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
2976
                return FALSE;
2977
        else
2978
                return TRUE;
2979
}
2980
2981
static gboolean compose_check_entries(Compose *compose)
2982
{
2983
        const gchar *str;
2984
2985
        if (compose_check_for_valid_recipient(compose) == FALSE) {
2986
                alertpanel_error(_("Recipient is not specified."));
2987
                return FALSE;
2988
        }
2989
2990
        str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2991
        if (*str == '\0') {
2992
                AlertValue aval;
2993
2994
                aval = alertpanel(_("Empty subject"),
2995
                                  _("Subject is empty. Send it anyway?"),
2996
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
2997
                if (aval != G_ALERTDEFAULT)
2998
                        return FALSE;
2999
        }
3000
3001
        return TRUE;
3002
}
3003
3004
static gboolean compose_check_attachments(Compose *compose)
3005
{
3006
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3007
        GtkTextBuffer *buffer;
3008
        GtkTextIter iter, line_end;
3009
        gchar *line;
3010
        gchar **strv;
3011
        gint i;
3012
        gboolean attach_found = FALSE;
3013
        gboolean valid = TRUE;
3014
3015
        if (!prefs_common.check_attach)
3016
                return TRUE;
3017
        if (!prefs_common.check_attach_str)
3018
                return TRUE;
3019
3020
        if (compose->use_attach &&
3021
            gtk_tree_model_iter_n_children
3022
                (GTK_TREE_MODEL(compose->attach_store), NULL) > 0)
3023
                return TRUE;
3024
3025
        buffer = gtk_text_view_get_buffer(text);
3026
        gtk_text_buffer_get_start_iter(buffer, &iter);
3027
        line_end = iter;
3028
3029
        strv = g_strsplit(prefs_common.check_attach_str, ",", -1);
3030
        for (i = 0; strv[i] != NULL; i++)
3031
                g_strstrip(strv[i]);
3032
3033
        while (valid) {
3034
                valid = gtk_text_iter_forward_to_line_end(&line_end);
3035
                line = gtk_text_buffer_get_text(buffer, &iter, &line_end,
3036
                                                FALSE);
3037
                iter = line_end;
3038
                if (get_quote_level(line) != -1)
3039
                        continue;
3040
3041
                for (i = 0; strv[i] != NULL; i++) {
3042
                        if (strv[i][0] == '\0')
3043
                                continue;
3044
                        if (strcasestr(line, strv[i])) {
3045
                                attach_found = TRUE;
3046
                                valid = FALSE;
3047
                                break;
3048
                        }
3049
                }
3050
3051
                g_free(line);
3052
        }
3053
3054
        g_strfreev(strv);
3055
3056
        if (attach_found) {
3057
                AlertValue aval;
3058
3059
                aval = alertpanel(_("Attachment is missing"),
3060
                                  _("There is no attachment. Send it without attachments?"),
3061
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3062
                if (aval != G_ALERTDEFAULT)
3063
                        return FALSE;
3064
        }
3065
3066
        return TRUE;
3067
}
3068
3069
static gint check_recp_delete_event(GtkWidget *widget, GdkEventAny *event,
3070
                                    gint *state)
3071
{
3072
        *state = GTK_RESPONSE_CANCEL;
3073
        return TRUE;
3074
}
3075
3076
static gboolean check_recp_key_pressed(GtkWidget *widget, GdkEventKey *event,
3077
                                       gint *state)
3078
{
3079
        if (event && event->keyval == GDK_Escape) {
3080
                *state = GTK_RESPONSE_CANCEL;
3081
                return TRUE;
3082
        }
3083
        return FALSE;
3084
}
3085
3086
static void check_recp_ok(GtkWidget *widget, gint *state)
3087
{
3088
        *state = GTK_RESPONSE_OK;
3089
}
3090
3091
static void check_recp_cancel(GtkWidget *widget, gint *state)
3092
{
3093
        *state = GTK_RESPONSE_CANCEL;
3094
}
3095
3096
static gboolean compose_check_recipients(Compose *compose)
3097
{
3098
        GtkWidget *window;
3099
        GtkWidget *vbox;
3100
        GtkWidget *hbox;
3101
        GtkWidget *image;
3102
        GtkWidget *vbox2;
3103
        GtkWidget *label;
3104
        GtkWidget *table;
3105
        GtkWidget *entry;
3106
        gchar buf[1024];
3107
        const gchar *text;
3108
        GtkWidget *scrwin;
3109
        GtkWidget *treeview;
3110
        GtkTreeStore *store;
3111
        GtkTreeViewColumn *column;
3112
        GtkCellRenderer *renderer;
3113
        GtkTreeIter iter, parent;
3114
        GtkWidget *hbbox;
3115
        GtkWidget *ok_btn;
3116
        GtkWidget *cancel_btn;
3117
        static PangoFontDescription *font_desc;
3118
        GtkStyle *style;
3119
3120
        GSList *cur, *to_list = NULL;
3121
        gboolean check_recp = FALSE;
3122
        gint state = 0;
3123
 
3124
        g_return_val_if_fail(compose->account != NULL, FALSE);
3125
        g_return_val_if_fail(compose->account->address != NULL, FALSE);
3126
3127
        if (!prefs_common.check_recipients)
3128
                return TRUE;
3129
3130
        if (prefs_common.check_recp_exclude) {
3131
                gchar **strv;
3132
                gint i;
3133
3134
                strv = g_strsplit(prefs_common.check_recp_exclude, ",", -1);
3135
                for (i = 0; strv[i] != NULL; i++)
3136
                        g_strstrip(strv[i]);
3137
3138
                if (compose->use_to) {
3139
                        text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3140
                        to_list = address_list_append_orig(NULL, text);
3141
                }
3142
                if (compose->use_cc) {
3143
                        text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3144
                        to_list = address_list_append_orig(to_list, text);
3145
                }
3146
                if (compose->use_bcc) {
3147
                        text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3148
                        to_list = address_list_append_orig(to_list, text);
3149
                }
3150
3151
                for (cur = to_list; cur != NULL; cur = cur->next) {
3152
                        for (i = 0; strv[i] != NULL; i++) {
3153
                                if (strv[i][0] == '\0')
3154
                                        continue;
3155
                                if (strcasestr((gchar *)cur->data, strv[i]))
3156
                                        break;
3157
                        }
3158
                        if (!strv[i]) {
3159
                                /* not found in exclude list */
3160
                                check_recp = TRUE;
3161
                                break;
3162
                        }
3163
                }
3164
3165
                slist_free_strings(to_list);
3166
                g_slist_free(to_list);
3167
                to_list = NULL;
3168
                g_strfreev(strv);
3169
        } else
3170
                check_recp = TRUE;
3171
3172
        if (!check_recp)
3173
                return TRUE;
3174
3175
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3176
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
3177
        gtk_window_set_title(GTK_WINDOW(window), _("Check recipients"));
3178
        gtk_window_set_position(GTK_WINDOW(window),
3179
                                GTK_WIN_POS_CENTER_ON_PARENT);
3180
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
3181
        gtk_widget_set_size_request(window, 480, -1);
3182
        gtk_widget_realize(window);
3183
        g_signal_connect(G_OBJECT(window), "delete_event",
3184
                         G_CALLBACK(check_recp_delete_event), &state);
3185
        g_signal_connect(G_OBJECT(window), "key_press_event",
3186
                         G_CALLBACK(check_recp_key_pressed), &state);
3187
3188
        vbox = gtk_vbox_new(FALSE, 8);
3189
        gtk_container_add(GTK_CONTAINER(window), vbox);
3190
3191
        hbox = gtk_hbox_new(FALSE, 12);
3192
        gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
3193
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3194
3195
        image = gtk_image_new_from_stock
3196
                (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
3197
        gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0);
3198
        gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
3199
3200
        vbox2 = gtk_vbox_new(FALSE, 12);
3201
        gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
3202
3203
        label = gtk_label_new(_("Check recipients"));
3204
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3205
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3206
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3207
3208
        if (!font_desc) {
3209
                gint size;
3210
3211
                size = pango_font_description_get_size
3212
                        (label->style->font_desc);
3213
                font_desc = pango_font_description_new();
3214
                pango_font_description_set_weight
3215
                        (font_desc, PANGO_WEIGHT_BOLD);
3216
                pango_font_description_set_size
3217
                        (font_desc, size * PANGO_SCALE_LARGE);
3218
        }
3219
        if (font_desc)
3220
                gtk_widget_modify_font(label, font_desc);
3221
3222
        label = gtk_label_new
3223
                (_("Really send this mail to the following addresses?"));
3224
        gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3225
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3226
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3227
        gtk_label_set_selectable(GTK_LABEL(label), TRUE);
3228
        GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS);
3229
3230
        table = gtk_table_new(2, 2, FALSE);
3231
        gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
3232
        gtk_table_set_row_spacings(GTK_TABLE(table), 4);
3233
        gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3234
3235
        hbox = gtk_hbox_new(FALSE, 0);
3236
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:")
3237
                              : "From:");
3238
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3239
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1,
3240
                         GTK_FILL, 0, 2, 0);
3241
        entry = gtk_entry_new();
3242
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3243
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3244
        style = gtk_widget_get_style(window);
3245
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3246
                               &style->bg[GTK_STATE_NORMAL]);
3247
        gtk_table_attach_defaults
3248
                (GTK_TABLE(table), entry, 1, 2, 0, 1);
3249
3250
        if (compose->account->name && *compose->account->name) {
3251
                g_snprintf(buf, sizeof(buf), "%s <%s>",
3252
                           compose->account->name, compose->account->address);
3253
                gtk_entry_set_text(GTK_ENTRY(entry), buf);
3254
        } else
3255
                gtk_entry_set_text(GTK_ENTRY(entry), compose->account->address);
3256
3257
        hbox = gtk_hbox_new(FALSE, 0);
3258
        label = gtk_label_new(prefs_common.trans_hdr ? _("Subject:")
3259
                              : "Subject:");
3260
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3261
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2,
3262
                         GTK_FILL, 0, 2, 0);
3263
        entry = gtk_entry_new();
3264
        gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3265
        gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3266
        style = gtk_widget_get_style(window);
3267
        gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3268
                               &style->bg[GTK_STATE_NORMAL]);
3269
        gtk_table_attach_defaults
3270
                (GTK_TABLE(table), entry, 1, 2, 1, 2);
3271
3272
        text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3273
        gtk_entry_set_text(GTK_ENTRY(entry), text);
3274
3275
        scrwin = gtk_scrolled_window_new(NULL, NULL);
3276
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
3277
                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3278
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
3279
                                            GTK_SHADOW_IN);
3280
        gtk_widget_set_size_request(scrwin, -1, 180);
3281
        gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
3282
3283
        store = gtk_tree_store_new(1, G_TYPE_STRING);
3284
        if (compose->use_to) {
3285
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3286
                to_list = address_list_append_orig(NULL, text);
3287
                if (to_list) {
3288
                        gtk_tree_store_append(store, &parent, NULL);
3289
                        gtk_tree_store_set(store, &parent, 0,
3290
                                           prefs_common.trans_hdr ?
3291
                                           _("To:") : "To:", -1);
3292
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3293
                                gtk_tree_store_append(store, &iter, &parent);
3294
                                gtk_tree_store_set(store, &iter, 0,
3295
                                                   (gchar *)cur->data, -1);
3296
                        }
3297
                        slist_free_strings(to_list);
3298
                        g_slist_free(to_list);
3299
                }
3300
        }
3301
        if (compose->use_cc) {
3302
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3303
                to_list = address_list_append_orig(NULL, text);
3304
                if (to_list) {
3305
                        gtk_tree_store_append(store, &parent, NULL);
3306
                        gtk_tree_store_set(store, &parent, 0,
3307
                                           prefs_common.trans_hdr ?
3308
                                           _("Cc:") : "Cc:", -1);
3309
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3310
                                gtk_tree_store_append(store, &iter, &parent);
3311
                                gtk_tree_store_set(store, &iter, 0,
3312
                                                   (gchar *)cur->data, -1);
3313
                        }
3314
                        slist_free_strings(to_list);
3315
                        g_slist_free(to_list);
3316
                }
3317
        }
3318
        if (compose->use_bcc) {
3319
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3320
                to_list = address_list_append_orig(NULL, text);
3321
                if (to_list) {
3322
                        gtk_tree_store_append(store, &parent, NULL);
3323
                        gtk_tree_store_set(store, &parent, 0,
3324
                                           prefs_common.trans_hdr ?
3325
                                           _("Bcc:") : "Bcc:", -1);
3326
                        for (cur = to_list; cur != NULL; cur = cur->next) {
3327
                                gtk_tree_store_append(store, &iter, &parent);
3328
                                gtk_tree_store_set(store, &iter, 0,
3329
                                                   (gchar *)cur->data, -1);
3330
                        }
3331
                        slist_free_strings(to_list);
3332
                        g_slist_free(to_list);
3333
                }
3334
        }
3335
3336
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3337
        g_object_unref(G_OBJECT(store));
3338
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
3339
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
3340
3341
        gtk_container_add(GTK_CONTAINER(scrwin), treeview);
3342
3343
        renderer = gtk_cell_renderer_text_new();
3344
        g_object_set(renderer, "ypad", 0, NULL);
3345
        column = gtk_tree_view_column_new_with_attributes
3346
                (_("Address"), renderer, "text", 0, NULL);
3347
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
3348
3349
        gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
3350
3351
        gtkut_stock_button_set_create(&hbbox, &ok_btn, _("_Send"),
3352
                                      &cancel_btn, GTK_STOCK_CANCEL,
3353
                                      NULL, NULL);
3354
        gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
3355
        gtk_widget_grab_default(ok_btn);
3356
        gtk_widget_grab_focus(ok_btn);
3357
3358
        g_signal_connect(G_OBJECT(ok_btn), "clicked",
3359
                         G_CALLBACK(check_recp_ok), &state);
3360
        g_signal_connect(G_OBJECT(cancel_btn), "clicked",
3361
                         G_CALLBACK(check_recp_cancel), &state);
3362
3363
        manage_window_set_transient(GTK_WINDOW(window));
3364
3365
        gtk_widget_show_all(window);
3366
3367
        while (state == 0)
3368
                gtk_main_iteration();
3369
3370
        gtk_widget_destroy(window);
3371
3372
        if (state == GTK_RESPONSE_OK)
3373
                return TRUE;
3374
3375
        return FALSE;
3376
}
3377
3378
static void compose_add_new_recipients_to_addressbook(Compose *compose)
3379
{
3380
        GSList *to_list = NULL, *cur;
3381
        const gchar *text;
3382
3383
        if (compose->use_to) {
3384
                text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3385
                to_list = address_list_append_orig(NULL, text);
3386
        }
3387
        if (compose->use_cc) {
3388
                text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3389
                to_list = address_list_append_orig(to_list, text);
3390
        }
3391
        if (compose->use_bcc) {
3392
                text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3393
                to_list = address_list_append_orig(to_list, text);
3394
        }
3395
3396
        for (cur = to_list; cur != NULL; cur = cur->next) {
3397
                gchar *orig_addr = cur->data;
3398
                gchar *name, *addr;
3399
3400
                name = procheader_get_fromname(orig_addr);
3401
                addr = g_strdup(orig_addr);
3402
                extract_address(addr);
3403
                if (!g_ascii_strcasecmp(name, addr)) {
3404
                        g_free(name);
3405
                        name = NULL;
3406
                }
3407
3408
                if (addressbook_has_address(addr))
3409
                        debug_print("compose_add_new_recipients_to_addressbook: address <%s> already registered.\n", addr);
3410
                else
3411
                        addressbook_add_contact_autoreg(name, addr, NULL);
3412
3413
                g_free(addr);
3414
                g_free(name);
3415
        }
3416
3417
        slist_free_strings(to_list);
3418
        g_slist_free(to_list);
3419
}
3420
3421
void compose_lock(Compose *compose)
3422
{
3423
        compose->lock_count++;
3424
}
3425
3426
void compose_unlock(Compose *compose)
3427
{
3428
        if (compose->lock_count > 0)
3429
                compose->lock_count--;
3430
}
3431
3432
void compose_block_modified(Compose *compose)
3433
{
3434
        compose->block_modified = TRUE;
3435
}
3436
3437
void compose_unblock_modified(Compose *compose)
3438
{
3439
        compose->block_modified = FALSE;
3440
}
3441
3442
static gint compose_send(Compose *compose)
3443
{
3444
        gchar tmp[MAXPATHLEN + 1];
3445
        gint ok = 0;
3446
3447
        if (compose->lock_count > 0)
3448
                return 1;
3449
3450
        g_return_val_if_fail(compose->account != NULL, -1);
3451
3452
        compose_lock(compose);
3453
3454
        if (compose_check_entries(compose) == FALSE) {
3455
                compose_unlock(compose);
3456
                return 1;
3457
        }
3458
        if (compose_check_attachments(compose) == FALSE) {
3459
                compose_unlock(compose);
3460
                return 1;
3461
        }
3462
        if (compose_check_recipients(compose) == FALSE) {
3463
                compose_unlock(compose);
3464
                return 1;
3465
        }
3466
3467
        if (!main_window_toggle_online_if_offline(main_window_get())) {
3468
                compose_unlock(compose);
3469
                return 1;
3470
        }
3471
3472
        /* write to temporary file */
3473
        g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
3474
                   get_tmp_dir(), G_DIR_SEPARATOR, compose);
3475
3476
        if (compose->mode == COMPOSE_REDIRECT) {
3477
                if (compose_redirect_write_to_file(compose, tmp) < 0) {
3478
                        compose_unlock(compose);
3479
                        return -1;
3480
                }
3481
        } else {
3482
                if (compose_write_to_file(compose, tmp, FALSE) < 0) {
3483
                        compose_unlock(compose);
3484
                        return -1;
3485
                }
3486
        }
3487
3488
        if (!compose->to_list && !compose->newsgroup_list) {
3489
                g_warning(_("can't get recipient list."));
3490
                g_unlink(tmp);
3491
                compose_unlock(compose);
3492
                return -1;
3493
        }
3494
3495
        if (compose->to_list) {
3496
                PrefsAccount *ac;
3497
3498
                if (compose->account->protocol != A_NNTP)
3499
                        ac = compose->account;
3500
                else {
3501
                        ac = account_find_from_address(compose->account->address);
3502
                        if (!ac) {
3503
                                if (cur_account && cur_account->protocol != A_NNTP)
3504
                                        ac = cur_account;
3505
                                else
3506
                                        ac = account_get_default();
3507
                        }
3508
                        if (!ac || ac->protocol == A_NNTP) {
3509
                                alertpanel_error(_("Account for sending mail is not specified.\n"
3510
                                                   "Please select a mail account before sending."));
3511
                                g_unlink(tmp);
3512
                                compose_unlock(compose);
3513
                                return -1;
3514
                        }
3515
                }
3516
                ok = send_message(tmp, ac, compose->to_list);
3517
                statusbar_pop_all();
3518
        }
3519
3520
        if (ok == 0 && compose->newsgroup_list) {
3521
                ok = news_post(FOLDER(compose->account->folder), tmp);
3522
                if (ok < 0) {
3523
                        alertpanel_error(_("Error occurred while posting the message to %s ."),
3524
                                         compose->account->nntp_server);
3525
                        g_unlink(tmp);
3526
                        compose_unlock(compose);
3527
                        return -1;
3528
                }
3529
        }
3530
3531
        if (ok == 0) {
3532
                if (compose->mode == COMPOSE_REEDIT) {
3533
                        compose_remove_reedit_target(compose);
3534
                        if (compose->targetinfo)
3535
                                folderview_update_item
3536
                                        (compose->targetinfo->folder, TRUE);
3537
                }
3538
3539
                if (compose->reply_target)
3540
                        send_message_set_reply_flag(compose->reply_target,
3541
                                                    compose->inreplyto);
3542
                else if (compose->forward_targets)
3543
                        send_message_set_forward_flags
3544
                                (compose->forward_targets);
3545
3546
                /* save message to outbox */
3547
                if (prefs_common.savemsg) {
3548
                        FolderItem *outbox;
3549
                        gboolean drop_done = FALSE;
3550
3551
                        /* filter sent message */
3552
                        if (prefs_common.filter_sent) {
3553
                                FilterInfo *fltinfo;
3554
3555
                                fltinfo = filter_info_new();
3556
                                fltinfo->account = compose->account;
3557
                                fltinfo->flags.perm_flags = 0;
3558
                                fltinfo->flags.tmp_flags = MSG_RECEIVED;
3559
3560
                                filter_apply(prefs_common.fltlist, tmp,
3561
                                             fltinfo);
3562
3563
                                drop_done = fltinfo->drop_done;
3564
                                folderview_update_all_updated(TRUE);
3565
                                filter_info_free(fltinfo);
3566
                        }
3567
3568
                        if (!drop_done) {
3569
                                outbox = account_get_special_folder
3570
                                        (compose->account, F_OUTBOX);
3571
                                if (procmsg_save_to_outbox(outbox, tmp) < 0)
3572
                                        alertpanel_error
3573
                                                (_("Can't save the message to outbox."));
3574
                                else
3575
                                        folderview_update_item(outbox, TRUE);
3576
                        }
3577
                }
3578
3579
                /* Add recipients to addressbook automatically */
3580
                if (prefs_common.recipients_autoreg) {
3581
                        compose_add_new_recipients_to_addressbook(compose);
3582
                }
3583
        }
3584
3585
        g_unlink(tmp);
3586
        compose_unlock(compose);
3587
3588
        return ok;
3589
}
3590
3591
#if USE_GPGME
3592
/* interfaces to rfc2015 to keep out the prefs stuff there.
3593
 * returns 0 on success and -1 on error. */
3594
static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
3595
{
3596
        const gchar *key_id = NULL;
3597
        GSList *key_list;
3598
3599
        switch (compose->account->sign_key) {
3600
        case SIGN_KEY_DEFAULT:
3601
                *pkey_list = NULL;
3602
                return 0;
3603
        case SIGN_KEY_BY_FROM:
3604
                key_id = compose->account->address;
3605
                break;
3606
        case SIGN_KEY_CUSTOM:
3607
                key_id = compose->account->sign_key_id;
3608
                break;
3609
        default:
3610
                break;
3611
        }
3612
3613
        key_list = rfc2015_create_signers_list(key_id);
3614
3615
        if (!key_list) {
3616
                alertpanel_error(_("Could not find any key associated with "
3617
                                   "currently selected key id `%s'."), key_id);
3618
                return -1;
3619
        }
3620
3621
        *pkey_list = key_list;
3622
        return 0;
3623
}
3624
3625
/* clearsign message body text */
3626
static gint compose_clearsign_text(Compose *compose, gchar **text)
3627
{
3628
        GSList *key_list;
3629
        gchar *tmp_file;
3630
3631
        tmp_file = get_tmp_file();
3632
        if (str_write_to_file(*text, tmp_file) < 0) {
3633
                g_free(tmp_file);
3634
                return -1;
3635
        }
3636
3637
        if (compose_create_signers_list(compose, &key_list) < 0) {
3638
                g_unlink(tmp_file);
3639
                g_free(tmp_file);
3640
                return -1;
3641
        }
3642
        if (rfc2015_clearsign(tmp_file, key_list) < 0) {
3643
                alertpanel_error(_("Can't sign the message."));
3644
                g_unlink(tmp_file);
3645
                g_free(tmp_file);
3646
                return -1;
3647
        }
3648
3649
        g_free(*text);
3650
        *text = file_read_to_str(tmp_file);
3651
        g_unlink(tmp_file);
3652
        g_free(tmp_file);
3653
        if (*text == NULL)
3654
                return -1;
3655
3656
        return 0;
3657
}
3658
3659
static gint compose_encrypt_armored(Compose *compose, gchar **text)
3660
{
3661
        gchar *tmp_file;
3662
3663
        tmp_file = get_tmp_file();
3664
        if (str_write_to_file(*text, tmp_file) < 0) {
3665
                g_free(tmp_file);
3666
                return -1;
3667
        }
3668
3669
        if (rfc2015_encrypt_armored(tmp_file, compose->to_list) < 0) {
3670
                alertpanel_error(_("Can't encrypt the message."));
3671
                g_unlink(tmp_file);
3672
                g_free(tmp_file);
3673
                return -1;
3674
        }
3675
3676
        g_free(*text);
3677
        *text = file_read_to_str(tmp_file);
3678
        g_unlink(tmp_file);
3679
        g_free(tmp_file);
3680
        if (*text == NULL)
3681
                return -1;
3682
3683
        return 0;
3684
}
3685
3686
static gint compose_encrypt_sign_armored(Compose *compose, gchar **text)
3687
{
3688
        GSList *key_list;
3689
        gchar *tmp_file;
3690
3691
        tmp_file = get_tmp_file();
3692
        if (str_write_to_file(*text, tmp_file) < 0) {
3693
                g_free(tmp_file);
3694
                return -1;
3695
        }
3696
3697
        if (compose_create_signers_list(compose, &key_list) < 0) {
3698
                g_unlink(tmp_file);
3699
                g_free(tmp_file);
3700
                return -1;
3701
        }
3702
3703
        if (rfc2015_encrypt_sign_armored
3704
                (tmp_file, compose->to_list, key_list) < 0) {
3705
                alertpanel_error(_("Can't encrypt or sign the message."));
3706
                g_unlink(tmp_file);
3707
                g_free(tmp_file);
3708
                return -1;
3709
        }
3710
3711
        g_free(*text);
3712
        *text = file_read_to_str(tmp_file);
3713
        g_unlink(tmp_file);
3714
        g_free(tmp_file);
3715
        if (*text == NULL)
3716
                return -1;
3717
3718
        return 0;
3719
}
3720
#endif /* USE_GPGME */
3721
3722
static gint compose_write_to_file(Compose *compose, const gchar *file,
3723
                                  gboolean is_draft)
3724
{
3725
        GtkTextBuffer *buffer;
3726
        GtkTextIter start, end;
3727
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
3728
        FILE *fp;
3729
        size_t len;
3730
        gchar *chars;
3731
        gchar *buf;
3732
        gchar *canon_buf;
3733
        const gchar *out_charset;
3734
        const gchar *body_charset;
3735
        const gchar *src_charset = CS_INTERNAL;
3736
        EncodingType encoding;
3737
        gint line;
3738
#if USE_GPGME
3739
        gboolean use_pgpmime_encryption = FALSE;
3740
        gboolean use_pgpmime_signing = FALSE;
3741
#endif
3742
3743
        if ((fp = g_fopen(file, "wb")) == NULL) {
3744
                FILE_OP_ERROR(file, "fopen");
3745
                return -1;
3746
        }
3747
3748
        /* chmod for security */
3749
        if (change_file_mode_rw(fp, file) < 0) {
3750
                FILE_OP_ERROR(file, "chmod");
3751
                g_warning(_("can't change file mode\n"));
3752
        }
3753
3754
        /* get outgoing charset */
3755
        out_charset = conv_get_charset_str(compose->out_encoding);
3756
        if (!out_charset)
3757
                out_charset = conv_get_outgoing_charset_str();
3758
        if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII))
3759
                out_charset = CS_ISO_8859_1;
3760
        body_charset = out_charset;
3761
3762
        /* get all composed text */
3763
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3764
        gtk_text_buffer_get_start_iter(buffer, &start);
3765
        gtk_text_buffer_get_end_iter(buffer, &end);
3766
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3767
        if (is_ascii_str(chars)) {
3768
                buf = chars;
3769
                chars = NULL;
3770
                body_charset = CS_US_ASCII;
3771
                encoding = ENC_7BIT;
3772
        } else {
3773
                gint error = 0;
3774
3775
                buf = conv_codeset_strdup_full
3776
                        (chars, src_charset, body_charset, &error);
3777
                if (!buf || error != 0) {
3778
                        AlertValue aval = G_ALERTDEFAULT;
3779
                        gchar *msg;
3780
3781
                        g_free(buf);
3782
3783
                        if (!is_draft) {
3784
                                msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n"
3785
                                                        "\n"
3786
                                                        "Send it as %s anyway?"),
3787
                                                      src_charset, body_charset,
3788
                                                      src_charset);
3789
                                aval = alertpanel_full
3790
                                        (_("Code conversion error"), msg, ALERT_ERROR,
3791
                                         G_ALERTALTERNATE,
3792
                                         FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3793
                                g_free(msg);
3794
                        }
3795
3796
                        if (aval != G_ALERTDEFAULT) {
3797
                                g_free(chars);
3798
                                fclose(fp);
3799
                                g_unlink(file);
3800
                                return -1;
3801
                        } else {
3802
                                buf = chars;
3803
                                out_charset = body_charset = src_charset;
3804
                                chars = NULL;
3805
                        }
3806
                }
3807
3808
                if (prefs_common.encoding_method == CTE_BASE64)
3809
                        encoding = ENC_BASE64;
3810
                else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3811
                        encoding = ENC_QUOTED_PRINTABLE;
3812
                else if (prefs_common.encoding_method == CTE_8BIT)
3813
                        encoding = ENC_8BIT;
3814
                else
3815
                        encoding = procmime_get_encoding_for_charset
3816
                                (body_charset);
3817
        }
3818
        g_free(chars);
3819
3820
        canon_buf = canonicalize_str(buf);
3821
        g_free(buf);
3822
        buf = canon_buf;
3823
3824
#if USE_GPGME
3825
        if (compose->use_signing && !compose->account->clearsign)
3826
                use_pgpmime_signing = TRUE;
3827
        if (compose->use_encryption && compose->account->ascii_armored) {
3828
                use_pgpmime_encryption = FALSE;
3829
                use_pgpmime_signing = FALSE;
3830
        }
3831
        if (compose->use_encryption && !compose->account->ascii_armored)
3832
                use_pgpmime_encryption = TRUE;
3833
3834
        /* protect trailing spaces */
3835
        if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) {
3836
                if (encoding == ENC_7BIT) {
3837
                        if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) {
3838
                                gchar *tmp;
3839
                                tmp = strchomp_all(buf);
3840
                                g_free(buf);
3841
                                buf = tmp;
3842
                        } else
3843
                                encoding = ENC_QUOTED_PRINTABLE;
3844
                } else if (encoding == ENC_8BIT) {
3845
                        encoding = procmime_get_encoding_for_str(buf);
3846
                        if (encoding == ENC_7BIT)
3847
                                encoding = ENC_QUOTED_PRINTABLE;
3848
                }
3849
        }
3850
3851
        if (rfc2015_is_available() && !is_draft) {
3852
                if ((compose->use_encryption &&
3853
                     compose->account->ascii_armored) ||
3854
                    (compose->use_signing && compose->account->clearsign)) {
3855
                        /* MIME encoding doesn't fit with cleartext signature */
3856
                        if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64)
3857
                                encoding = ENC_8BIT;
3858
3859
                }
3860
        }
3861
#endif
3862
3863
        debug_print("src encoding = %s, out encoding = %s, "
3864
                    "body encoding = %s, transfer encoding = %s\n",
3865
                    src_charset, out_charset, body_charset,
3866
                    procmime_get_encoding_str(encoding));
3867
3868
        /* check for line length limit */
3869
        if (!is_draft &&
3870
            encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
3871
            check_line_length(buf, 1000, &line) < 0) {
3872
                AlertValue aval;
3873
                gchar *msg;
3874
3875
                msg = g_strdup_printf
3876
                        (_("Line %d exceeds the line length limit (998 bytes).\n"
3877
                           "The contents of the message might be broken on the way to the delivery.\n"
3878
                           "\n"
3879
                           "Send it anyway?"), line + 1);
3880
                aval = alertpanel_full(_("Line length limit"),
3881
                                       msg, ALERT_WARNING,
3882
                                       G_ALERTALTERNATE, FALSE,
3883
                                       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3884
                if (aval != G_ALERTDEFAULT) {
3885
                        g_free(msg);
3886
                        fclose(fp);
3887
                        g_unlink(file);
3888
                        g_free(buf);
3889
                        return -1;
3890
                }
3891
        }
3892
3893
        /* write headers */
3894
        if (compose_write_headers(compose, fp, out_charset,
3895
                                  body_charset, encoding, is_draft) < 0) {
3896
                g_warning("can't write headers\n");
3897
                fclose(fp);
3898
                g_unlink(file);
3899
                g_free(buf);
3900
                return -1;
3901
        }
3902
3903
#if USE_GPGME
3904
        /* do ascii-armor encryption and/or clearsign */
3905
        if (rfc2015_is_available() && !is_draft) {
3906
                gint ret;
3907
3908
                if (compose->use_encryption && compose->account->ascii_armored) {
3909
                        if (compose->use_signing)
3910
                                ret = compose_encrypt_sign_armored(compose, &buf);
3911
                        else
3912
                                ret = compose_encrypt_armored(compose, &buf);
3913
                        if (ret < 0) {
3914
                                g_warning("ascii-armored encryption failed\n");
3915
                                fclose(fp);
3916
                                g_unlink(file);
3917
                                g_free(buf);
3918
                                return -1;
3919
                        }
3920
                } else if (compose->use_signing && compose->account->clearsign) {
3921
                        if (compose_clearsign_text(compose, &buf) < 0) {
3922
                                g_warning("clearsign failed\n");
3923
                                fclose(fp);
3924
                                g_unlink(file);
3925
                                g_free(buf);
3926
                                return -1;
3927
                        }
3928
                }
3929
        }
3930
#endif
3931
3932
        if (compose->use_attach &&
3933
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3934
#if USE_GPGME
3935
            /* This prolog message is ignored by mime software and
3936
             * because it would make our signing/encryption task
3937
             * tougher, we don't emit it in that case */
3938
            if (!rfc2015_is_available() ||
3939
                (!compose->use_signing && !compose->use_encryption))
3940
#endif
3941
                fputs("This is a multi-part message in MIME format.\n", fp);
3942
3943
                fprintf(fp, "\n--%s\n", compose->boundary);
3944
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
3945
                        body_charset);
3946
#if USE_GPGME
3947
                if (rfc2015_is_available() && use_pgpmime_signing)
3948
                        fprintf(fp, "Content-Disposition: inline\n");
3949
#endif
3950
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
3951
                        procmime_get_encoding_str(encoding));
3952
                fputc('\n', fp);
3953
        }
3954
3955
        /* write body */
3956
        len = strlen(buf);
3957
        if (encoding == ENC_BASE64) {
3958
                gchar outbuf[B64_BUFFSIZE];
3959
                gint i, l;
3960
3961
                for (i = 0; i < len; i += B64_LINE_SIZE) {
3962
                        l = MIN(B64_LINE_SIZE, len - i);
3963
                        base64_encode(outbuf, (guchar *)buf + i, l);
3964
                        fputs(outbuf, fp);
3965
                        fputc('\n', fp);
3966
                }
3967
        } else if (encoding == ENC_QUOTED_PRINTABLE) {
3968
                gchar *outbuf;
3969
                size_t outlen;
3970
3971
                outbuf = g_malloc(len * 4);
3972
                qp_encode_line(outbuf, (guchar *)buf);
3973
                outlen = strlen(outbuf);
3974
                if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
3975
                        FILE_OP_ERROR(file, "fwrite");
3976
                        fclose(fp);
3977
                        g_unlink(file);
3978
                        g_free(outbuf);
3979
                        g_free(buf);
3980
                        return -1;
3981
                }
3982
                g_free(outbuf);
3983
        } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
3984
                FILE_OP_ERROR(file, "fwrite");
3985
                fclose(fp);
3986
                g_unlink(file);
3987
                g_free(buf);
3988
                return -1;
3989
        }
3990
        g_free(buf);
3991
3992
        if (compose->use_attach &&
3993
            gtk_tree_model_iter_n_children(model, NULL) > 0) {
3994
                if (compose_write_attach(compose, fp, out_charset) < 0) {
3995
                        fclose(fp);
3996
                        g_unlink(file);
3997
                        return -1;
3998
                }
3999
        }
4000
4001
        if (fclose(fp) == EOF) {
4002
                FILE_OP_ERROR(file, "fclose");
4003
                g_unlink(file);
4004
                return -1;
4005
        }
4006
4007
#if USE_GPGME
4008
        if (!rfc2015_is_available() || is_draft) {
4009
                uncanonicalize_file_replace(file);
4010
                return 0;
4011
        }
4012
4013
        if (use_pgpmime_signing || use_pgpmime_encryption) {
4014
                if (canonicalize_file_replace(file) < 0) {
4015
                        g_unlink(file);
4016
                        return -1;
4017
                }
4018
        }
4019
4020
        if (use_pgpmime_signing && !use_pgpmime_encryption) {
4021
                GSList *key_list;
4022
4023
                if (compose_create_signers_list(compose, &key_list) < 0) {
4024
                        g_unlink(file);
4025
                        return -1;
4026
                }
4027
                if (rfc2015_sign(file, key_list) < 0) {
4028
                        alertpanel_error(_("Can't sign the message."));
4029
                        g_unlink(file);
4030
                        return -1;
4031
                }
4032
        } else if (use_pgpmime_encryption) {
4033
                GSList *key_list;
4034
4035
                if (compose->use_bcc) {
4036
                        const gchar *text;
4037
                        gchar *bcc;
4038
                        AlertValue aval;
4039
4040
                        text = gtk_entry_get_text
4041
                                (GTK_ENTRY(compose->bcc_entry));
4042
                        Xstrdup_a(bcc, text, { g_unlink(file); return -1; });
4043
                        g_strstrip(bcc);
4044
                        if (*bcc != '\0') {
4045
                                aval = alertpanel_full
4046
                                        (_("Encrypting with Bcc"),
4047
                                         _("This message has Bcc recipients. If this message is encrypted, all Bcc recipients will be visible by examing the encryption key list, leading to loss of confidentiality.\n"
4048
                                           "\n"
4049
                                           "Send it anyway?"),
4050
                                         ALERT_WARNING, G_ALERTDEFAULT, FALSE,
4051
                                         GTK_STOCK_YES, GTK_STOCK_NO, NULL);
4052
                                if (aval != G_ALERTDEFAULT) {
4053
                                        g_unlink(file);
4054
                                        return -1;
4055
                                }
4056
                        }
4057
                }
4058
                if (use_pgpmime_signing) {
4059
                        if (compose_create_signers_list
4060
                                (compose, &key_list) < 0) {
4061
                                g_unlink(file);
4062
                                return -1;
4063
                        }
4064
                        if (rfc2015_encrypt_sign(file, compose->to_list,
4065
                                                 key_list) < 0) {
4066
                                alertpanel_error(_("Can't encrypt or sign the message."));
4067
                                g_unlink(file);
4068
                                return -1;
4069
                        }
4070
                } else if (rfc2015_encrypt(file, compose->to_list) < 0) {
4071
                        alertpanel_error(_("Can't encrypt the message."));
4072
                        g_unlink(file);
4073
                        return -1;
4074
                }
4075
        }
4076
#endif /* USE_GPGME */
4077
4078
        uncanonicalize_file_replace(file);
4079
4080
        return 0;
4081
}
4082
4083
static gint compose_write_body_to_file(Compose *compose, const gchar *file)
4084
{
4085
        GtkTextBuffer *buffer;
4086
        GtkTextIter start, end;
4087
        FILE *fp;
4088
        size_t len;
4089
        gchar *chars, *tmp;
4090
4091
        if ((fp = g_fopen(file, "wb")) == NULL) {
4092
                FILE_OP_ERROR(file, "fopen");
4093
                return -1;
4094
        }
4095
4096
        /* chmod for security */
4097
        if (change_file_mode_rw(fp, file) < 0) {
4098
                FILE_OP_ERROR(file, "chmod");
4099
                g_warning(_("can't change file mode\n"));
4100
        }
4101
4102
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4103
        gtk_text_buffer_get_start_iter(buffer, &start);
4104
        gtk_text_buffer_get_end_iter(buffer, &end);
4105
        tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4106
4107
        chars = conv_codeset_strdup
4108
                (tmp, CS_INTERNAL, conv_get_locale_charset_str());
4109
4110
        g_free(tmp);
4111
4112
        if (!chars) {
4113
                fclose(fp);
4114
                g_unlink(file);
4115
                return -1;
4116
        }
4117
4118
        /* write body */
4119
        len = strlen(chars);
4120
        if (fwrite(chars, sizeof(gchar), len, fp) != len) {
4121
                FILE_OP_ERROR(file, "fwrite");
4122
                g_free(chars);
4123
                fclose(fp);
4124
                g_unlink(file);
4125
                return -1;
4126
        }
4127
4128
        g_free(chars);
4129
4130
        if (fclose(fp) == EOF) {
4131
                FILE_OP_ERROR(file, "fclose");
4132
                g_unlink(file);
4133
                return -1;
4134
        }
4135
        return 0;
4136
}
4137
4138
static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
4139
{
4140
        FILE *fp;
4141
        FILE *fdest;
4142
        size_t len;
4143
        gchar buf[BUFFSIZE];
4144
4145
        g_return_val_if_fail(file != NULL, -1);
4146
        g_return_val_if_fail(compose->account != NULL, -1);
4147
        g_return_val_if_fail(compose->account->address != NULL, -1);
4148
        g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
4149
        g_return_val_if_fail(compose->targetinfo != NULL, -1);
4150
4151
        if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
4152
                return -1;
4153
4154
        if ((fdest = g_fopen(file, "wb")) == NULL) {
4155
                FILE_OP_ERROR(file, "fopen");
4156
                fclose(fp);
4157
                return -1;
4158
        }
4159
4160
        if (change_file_mode_rw(fdest, file) < 0) {
4161
                FILE_OP_ERROR(file, "chmod");
4162
                g_warning(_("can't change file mode\n"));
4163
        }
4164
4165
        while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
4166
                if (g_ascii_strncasecmp(buf, "Return-Path:",
4167
                                        strlen("Return-Path:")) == 0 ||
4168
                    g_ascii_strncasecmp(buf, "Delivered-To:",
4169
                                        strlen("Delivered-To:")) == 0 ||
4170
                    g_ascii_strncasecmp(buf, "Received:",
4171
                                        strlen("Received:")) == 0 ||
4172
                    g_ascii_strncasecmp(buf, "Subject:",
4173
                                        strlen("Subject:")) == 0 ||
4174
                    g_ascii_strncasecmp(buf, "X-UIDL:",
4175
                                        strlen("X-UIDL:")) == 0)
4176
                        continue;
4177
4178
                if (fputs(buf, fdest) == EOF)
4179
                        goto error;
4180
4181
#if 0
4182
                if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) {
4183
                        fputs("\n (by way of ", fdest);
4184
                        if (compose->account->name) {
4185
                                compose_convert_header(compose,
4186
                                                       buf, sizeof(buf),
4187
                                                       compose->account->name,
4188
                                                       strlen(" (by way of "),
4189
                                                       FALSE, NULL);
4190
                                fprintf(fdest, "%s <%s>", buf,
4191
                                        compose->account->address);
4192
                        } else
4193
                                fputs(compose->account->address, fdest);
4194
                        fputs(")", fdest);
4195
                }
4196
#endif
4197
4198
                if (fputs("\n", fdest) == EOF)
4199
                        goto error;
4200
        }
4201
4202
        compose_redirect_write_headers(compose, fdest);
4203
4204
        while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4205
                if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
4206
                        FILE_OP_ERROR(file, "fwrite");
4207
                        goto error;
4208
                }
4209
        }
4210
4211
        fclose(fp);
4212
        if (fclose(fdest) == EOF) {
4213
                FILE_OP_ERROR(file, "fclose");
4214
                g_unlink(file);
4215
                return -1;
4216
        }
4217
4218
        return 0;
4219
error:
4220
        fclose(fp);
4221
        fclose(fdest);
4222
        g_unlink(file);
4223
4224
        return -1;
4225
}
4226
4227
static gint compose_remove_reedit_target(Compose *compose)
4228
{
4229
        FolderItem *item;
4230
        MsgInfo *msginfo = compose->targetinfo;
4231
4232
        g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4233
        if (!msginfo) return -1;
4234
4235
        item = msginfo->folder;
4236
        g_return_val_if_fail(item != NULL, -1);
4237
4238
        folder_item_scan(item);
4239
        if (procmsg_msg_exist(msginfo) &&
4240
            (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
4241
                if (folder_item_remove_msg(item, msginfo) < 0) {
4242
                        g_warning(_("can't remove the old message\n"));
4243
                        return -1;
4244
                }
4245
        }
4246
4247
        return 0;
4248
}
4249
4250
static gint compose_queue(Compose *compose, const gchar *file)
4251
{
4252
        FolderItem *queue;
4253
        gchar *tmp;
4254
        FILE *fp, *src_fp;
4255
        GSList *cur;
4256
        gchar buf[BUFFSIZE];
4257
        gint num;
4258
        MsgFlags flag = {0, MSG_QUEUED};
4259
4260
        debug_print(_("queueing message...\n"));
4261
        g_return_val_if_fail(compose->to_list != NULL ||
4262
                             compose->newsgroup_list != NULL,
4263
                             -1);
4264
        g_return_val_if_fail(compose->account != NULL, -1);
4265
4266
        tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4267
                              G_DIR_SEPARATOR, compose);
4268
        if ((fp = g_fopen(tmp, "wb")) == NULL) {
4269
                FILE_OP_ERROR(tmp, "fopen");
4270
                g_free(tmp);
4271
                return -1;
4272
        }
4273
        if ((src_fp = g_fopen(file, "rb")) == NULL) {
4274
                FILE_OP_ERROR(file, "fopen");
4275
                fclose(fp);
4276
                g_unlink(tmp);
4277
                g_free(tmp);
4278
                return -1;
4279
        }
4280
        if (change_file_mode_rw(fp, tmp) < 0) {
4281
                FILE_OP_ERROR(tmp, "chmod");
4282
                g_warning(_("can't change file mode\n"));
4283
        }
4284
4285
        /* queueing variables */
4286
        fprintf(fp, "AF:\n");
4287
        fprintf(fp, "NF:0\n");
4288
        fprintf(fp, "PS:10\n");
4289
        fprintf(fp, "SRH:1\n");
4290
        fprintf(fp, "SFN:\n");
4291
        fprintf(fp, "DSR:\n");
4292
        if (compose->msgid)
4293
                fprintf(fp, "MID:<%s>\n", compose->msgid);
4294
        else
4295
                fprintf(fp, "MID:\n");
4296
        fprintf(fp, "CFG:\n");
4297
        fprintf(fp, "PT:0\n");
4298
        fprintf(fp, "S:%s\n", compose->account->address);
4299
        fprintf(fp, "RQ:\n");
4300
        if (compose->account->smtp_server)
4301
                fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
4302
        else
4303
                fprintf(fp, "SSV:\n");
4304
        if (compose->account->nntp_server)
4305
                fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
4306
        else
4307
                fprintf(fp, "NSV:\n");
4308
        fprintf(fp, "SSH:\n");
4309
        if (compose->to_list) {
4310
                fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4311
                for (cur = compose->to_list->next; cur != NULL;
4312
                     cur = cur->next)
4313
                        fprintf(fp, ",<%s>", (gchar *)cur->data);
4314
                fprintf(fp, "\n");
4315
        } else
4316
                fprintf(fp, "R:\n");
4317
        /* Sylpheed account ID */
4318
        fprintf(fp, "AID:%d\n", compose->account->account_id);
4319
        /* Reply target */
4320
        if (compose->reply_target)
4321
                fprintf(fp, "REP:%s\n", compose->reply_target);
4322
        /* Forward target */
4323
        if (compose->forward_targets)
4324
                fprintf(fp, "FWD:%s\n", compose->forward_targets);
4325
        fprintf(fp, "\n");
4326
4327
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4328
                if (fputs(buf, fp) == EOF) {
4329
                        FILE_OP_ERROR(tmp, "fputs");
4330
                        fclose(fp);
4331
                        fclose(src_fp);
4332
                        g_unlink(tmp);
4333
                        g_free(tmp);
4334
                        return -1;
4335
                }
4336
        }
4337
4338
        fclose(src_fp);
4339
        if (fclose(fp) == EOF) {
4340
                FILE_OP_ERROR(tmp, "fclose");
4341
                g_unlink(tmp);
4342
                g_free(tmp);
4343
                return -1;
4344
        }
4345
4346
        queue = account_get_special_folder(compose->account, F_QUEUE);
4347
        if (!queue) {
4348
                g_warning(_("can't find queue folder\n"));
4349
                g_unlink(tmp);
4350
                g_free(tmp);
4351
                return -1;
4352
        }
4353
        folder_item_scan(queue);
4354
        if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
4355
                g_warning(_("can't queue the message\n"));
4356
                g_unlink(tmp);
4357
                g_free(tmp);
4358
                return -1;
4359
        }
4360
        g_free(tmp);
4361
4362
        if (compose->mode == COMPOSE_REEDIT) {
4363
                compose_remove_reedit_target(compose);
4364
                if (compose->targetinfo &&
4365
                    compose->targetinfo->folder != queue)
4366
                        folderview_update_item
4367
                                (compose->targetinfo->folder, TRUE);
4368
        }
4369
4370
        folder_item_scan(queue);
4371
        folderview_update_item(queue, TRUE);
4372
4373
        /* Add recipients to addressbook automatically */
4374
        if (prefs_common.recipients_autoreg) {
4375
                compose_add_new_recipients_to_addressbook(compose);
4376
        }
4377
4378
        main_window_set_menu_sensitive(main_window_get());
4379
        main_window_set_toolbar_sensitive(main_window_get());
4380
4381
        return 0;
4382
}
4383
4384
static gint compose_write_attach(Compose *compose, FILE *fp,
4385
                                 const gchar *charset)
4386
{
4387
        GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
4388
        GtkTreeIter iter;
4389
        gboolean valid;
4390
        AttachInfo *ainfo;
4391
        FILE *attach_fp;
4392
        gint len;
4393
        EncodingType encoding;
4394
4395
        for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
4396
             valid = gtk_tree_model_iter_next(model, &iter)) {
4397
                gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
4398
4399
                if (!is_file_exist(ainfo->file)) {
4400
                        alertpanel_error(_("File %s doesn't exist."),
4401
                                         ainfo->file);
4402
                        return -1;
4403
                }
4404
                if (get_file_size(ainfo->file) <= 0) {
4405
                        alertpanel_error(_("File %s is empty."), ainfo->file);
4406
                        return -1;
4407
                }
4408
                if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) {
4409
                        alertpanel_error(_("Can't open file %s."), ainfo->file);
4410
                        return -1;
4411
                }
4412
4413
                fprintf(fp, "\n--%s\n", compose->boundary);
4414
4415
                encoding = ainfo->encoding;
4416
4417
                if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) {
4418
                        fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
4419
                        fprintf(fp, "Content-Disposition: inline\n");
4420
4421
                        /* message/... shouldn't be encoded */
4422
                        if (encoding == ENC_QUOTED_PRINTABLE ||
4423
                            encoding == ENC_BASE64)
4424
                                encoding = ENC_8BIT;
4425
                } else {
4426
                        if (prefs_common.mime_fencoding_method ==
4427
                            FENC_RFC2231) {
4428
                                gchar *param;
4429
4430
                                param = compose_convert_filename
4431
                                        (compose, ainfo->name, "name", charset);
4432
                                fprintf(fp, "Content-Type: %s;\n"
4433
                                            "%s\n",
4434
                                        ainfo->content_type, param);
4435
                                g_free(param);
4436
                                param = compose_convert_filename
4437
                                        (compose, ainfo->name, "filename",
4438
                                         charset);
4439
                                fprintf(fp, "Content-Disposition: attachment;\n"
4440
                                            "%s\n", param);
4441
                                g_free(param);
4442
                        } else {
4443
                                gchar filename[BUFFSIZE];
4444
4445
                                compose_convert_header(compose, filename,
4446
                                                       sizeof(filename),
4447
                                                       ainfo->name, 12, FALSE,
4448
                                                       charset);
4449
                                fprintf(fp, "Content-Type: %s;\n"
4450
                                            " name=\"%s\"\n",
4451
                                        ainfo->content_type, filename);
4452
                                fprintf(fp, "Content-Disposition: attachment;\n"
4453
                                            " filename=\"%s\"\n", filename);
4454
                        }
4455
4456
#if USE_GPGME
4457
                        /* force encoding to protect trailing spaces */
4458
                        if (rfc2015_is_available() && compose->use_signing &&
4459
                            !compose->account->clearsign) {
4460
                                if (encoding == ENC_7BIT)
4461
                                        encoding = ENC_QUOTED_PRINTABLE;
4462
                                else if (encoding == ENC_8BIT)
4463
                                        encoding = ENC_BASE64;
4464
                        }
4465
#endif
4466
                }
4467
4468
                fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
4469
                        procmime_get_encoding_str(encoding));
4470
4471
                if (encoding == ENC_BASE64) {
4472
                        gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
4473
                        FILE *tmp_fp = attach_fp;
4474
                        gchar *tmp_file = NULL;
4475
                        ContentType content_type;
4476
4477
                        content_type =
4478
                                procmime_scan_mime_type(ainfo->content_type);
4479
                        if (content_type == MIME_TEXT ||
4480
                            content_type == MIME_TEXT_HTML ||
4481
                            content_type == MIME_MESSAGE_RFC822) {
4482
                                tmp_file = get_tmp_file();
4483
                                if (canonicalize_file(ainfo->file, tmp_file) < 0) {
4484
                                        g_free(tmp_file);
4485
                                        fclose(attach_fp);
4486
                                        return -1;
4487
                                }
4488
                                if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
4489
                                        FILE_OP_ERROR(tmp_file, "fopen");
4490
                                        g_unlink(tmp_file);
4491
                                        g_free(tmp_file);
4492
                                        fclose(attach_fp);
4493
                                        return -1;
4494
                                }
4495
                        }
4496
4497
                        while ((len = fread(inbuf, sizeof(gchar),
4498
                                            B64_LINE_SIZE, tmp_fp))
4499
                               == B64_LINE_SIZE) {
4500
                                base64_encode(outbuf, (guchar *)inbuf,
4501
                                              B64_LINE_SIZE);
4502
                                fputs(outbuf, fp);
4503
                                fputc('\n', fp);
4504
                        }
4505
                        if (len > 0 && feof(tmp_fp)) {
4506
                                base64_encode(outbuf, (guchar *)inbuf, len);
4507
                                fputs(outbuf, fp);
4508
                                fputc('\n', fp);
4509
                        }
4510
4511
                        if (tmp_file) {
4512
                                fclose(tmp_fp);
4513
                                g_unlink(tmp_file);
4514
                                g_free(tmp_file);
4515
                        }
4516
                } else if (encoding == ENC_QUOTED_PRINTABLE) {
4517
                        gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
4518
4519
                        while (fgets(inbuf, sizeof(inbuf), attach_fp) != NULL) {
4520
                                qp_encode_line(outbuf, (guchar *)inbuf);
4521
                                fputs(outbuf, fp);
4522
                        }
4523
                } else {
4524
                        gchar buf[BUFFSIZE];
4525
4526
                        while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
4527
                                strcrchomp(buf);
4528
                                fputs(buf, fp);
4529
                        }
4530
                }
4531
4532
                fclose(attach_fp);
4533
        }
4534
4535
        fprintf(fp, "\n--%s--\n", compose->boundary);
4536
        return 0;
4537
}
4538
4539
#define QUOTE_REQUIRED(str) \
4540
        (*str != '"' && strpbrk(str, ",.[]<>") != NULL)
4541
4542
#define PUT_RECIPIENT_HEADER(header, str)                                     \
4543
{                                                                             \
4544
        if (*str != '\0') {                                                     \
4545
                gchar *dest;                                                     \
4546
                                                                             \
4547
                dest = g_strdup(str);                                             \
4548
                g_strstrip(dest);                                             \
4549
                if (*dest != '\0') {                                             \
4550
                        compose->to_list = address_list_append                     \
4551
                                (compose->to_list, dest);                     \
4552
                        compose_convert_header                                     \
4553
                                (compose, buf, sizeof(buf), dest,             \
4554
                                 strlen(header) + 2, TRUE, charset);             \
4555
                        fprintf(fp, "%s: %s\n", header, buf);                     \
4556
                }                                                             \
4557
                g_free(dest);                                                     \
4558
        }                                                                     \
4559
}
4560
4561
#define IS_IN_CUSTOM_HEADER(header) \
4562
        (compose->account->add_customhdr && \
4563
         custom_header_find(compose->account->customhdr_list, header) != NULL)
4564
4565
static gint compose_write_headers(Compose *compose, FILE *fp,
4566
                                  const gchar *charset,
4567
                                  const gchar *body_charset,
4568
                                  EncodingType encoding, gboolean is_draft)
4569
{
4570
        gchar buf[BUFFSIZE];
4571
        const gchar *entry_str;
4572
        gchar *str;
4573
4574
        g_return_val_if_fail(fp != NULL, -1);
4575
        g_return_val_if_fail(charset != NULL, -1);
4576
        g_return_val_if_fail(compose->account != NULL, -1);
4577
        g_return_val_if_fail(compose->account->address != NULL, -1);
4578
4579
        /* Date */
4580
        if (compose->account->add_date) {
4581
                get_rfc822_date(buf, sizeof(buf));
4582
                fprintf(fp, "Date: %s\n", buf);
4583
        }
4584
4585
        /* From */
4586
        if (compose->account->name && *compose->account->name) {
4587
                compose_convert_header
4588
                        (compose, buf, sizeof(buf), compose->account->name,
4589
                         strlen("From: "), TRUE, charset);
4590
                if (QUOTE_REQUIRED(buf))
4591
                        fprintf(fp, "From: \"%s\" <%s>\n",
4592
                                buf, compose->account->address);
4593
                else
4594
                        fprintf(fp, "From: %s <%s>\n",
4595
                                buf, compose->account->address);
4596
        } else
4597
                fprintf(fp, "From: %s\n", compose->account->address);
4598
4599
        slist_free_strings(compose->to_list);
4600
        g_slist_free(compose->to_list);
4601
        compose->to_list = NULL;
4602
4603
        /* To */
4604
        if (compose->use_to) {
4605
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4606
                PUT_RECIPIENT_HEADER("To", entry_str);
4607
        }
4608
4609
        slist_free_strings(compose->newsgroup_list);
4610
        g_slist_free(compose->newsgroup_list);
4611
        compose->newsgroup_list = NULL;
4612
4613
        /* Newsgroups */
4614
        if (compose->use_newsgroups) {
4615
                entry_str = gtk_entry_get_text
4616
                        (GTK_ENTRY(compose->newsgroups_entry));
4617
                if (*entry_str != '\0') {
4618
                        Xstrdup_a(str, entry_str, return -1);
4619
                        g_strstrip(str);
4620
                        remove_space(str);
4621
                        if (*str != '\0') {
4622
                                compose->newsgroup_list =
4623
                                        newsgroup_list_append
4624
                                                (compose->newsgroup_list, str);
4625
                                compose_convert_header(compose,
4626
                                                       buf, sizeof(buf), str,
4627
                                                       strlen("Newsgroups: "),
4628
                                                       FALSE, charset);
4629
                                fprintf(fp, "Newsgroups: %s\n", buf);
4630
                        }
4631
                }
4632
        }
4633
4634
        /* Cc */
4635
        if (compose->use_cc) {
4636
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4637
                PUT_RECIPIENT_HEADER("Cc", entry_str);
4638
        }
4639
4640
        /* Bcc */
4641
        if (compose->use_bcc) {
4642
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4643
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4644
        }
4645
4646
        if (!is_draft && !compose->to_list && !compose->newsgroup_list)
4647
                return -1;
4648
4649
        /* Subject */
4650
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4651
        if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4652
                Xstrdup_a(str, entry_str, return -1);
4653
                g_strstrip(str);
4654
                if (*str != '\0') {
4655
                        compose_convert_header(compose, buf, sizeof(buf), str,
4656
                                               strlen("Subject: "), FALSE,
4657
                                               charset);
4658
                        fprintf(fp, "Subject: %s\n", buf);
4659
                }
4660
        }
4661
4662
        /* Message-ID */
4663
        if (compose->account->gen_msgid) {
4664
                compose_generate_msgid(compose, buf, sizeof(buf));
4665
                fprintf(fp, "Message-Id: <%s>\n", buf);
4666
                compose->msgid = g_strdup(buf);
4667
        }
4668
4669
        /* In-Reply-To */
4670
        if (compose->inreplyto && compose->to_list)
4671
                fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
4672
4673
        /* References */
4674
        if (compose->references)
4675
                fprintf(fp, "References: %s\n", compose->references);
4676
4677
        /* Followup-To */
4678
        if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
4679
                entry_str = gtk_entry_get_text
4680
                        (GTK_ENTRY(compose->followup_entry));
4681
                if (*entry_str != '\0') {
4682
                        Xstrdup_a(str, entry_str, return -1);
4683
                        g_strstrip(str);
4684
                        remove_space(str);
4685
                        if (*str != '\0') {
4686
                                compose_convert_header(compose,
4687
                                                       buf, sizeof(buf), str,
4688
                                                       strlen("Followup-To: "),
4689
                                                       FALSE, charset);
4690
                                fprintf(fp, "Followup-To: %s\n", buf);
4691
                        }
4692
                }
4693
        }
4694
4695
        /* Reply-To */
4696
        if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
4697
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4698
                if (*entry_str != '\0') {
4699
                        Xstrdup_a(str, entry_str, return -1);
4700
                        g_strstrip(str);
4701
                        if (*str != '\0') {
4702
                                compose_convert_header(compose,
4703
                                                       buf, sizeof(buf), str,
4704
                                                       strlen("Reply-To: "),
4705
                                                       TRUE, charset);
4706
                                fprintf(fp, "Reply-To: %s\n", buf);
4707
                        }
4708
                }
4709
        }
4710
4711
        /* Disposition-Notification-To */
4712
        if (compose->use_mdn &&
4713
            !IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
4714
                fprintf(fp, "Disposition-Notification-To: %s\n",
4715
                        compose->account->address);
4716
        }
4717
4718
        /* Organization */
4719
        if (compose->account->organization &&
4720
            !IS_IN_CUSTOM_HEADER("Organization")) {
4721
                compose_convert_header(compose, buf, sizeof(buf),
4722
                                       compose->account->organization,
4723
                                       strlen("Organization: "), FALSE,
4724
                                       charset);
4725
                fprintf(fp, "Organization: %s\n", buf);
4726
        }
4727
4728
        /* Program version and system info */
4729
        if (prefs_common.user_agent_str) {
4730
                if (compose->to_list && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
4731
                        fprintf(fp, "X-Mailer: %s\n",
4732
                                prefs_common.user_agent_str);
4733
                }
4734
                if (compose->newsgroup_list && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
4735
                        fprintf(fp, "X-Newsreader: %s\n",
4736
                                prefs_common.user_agent_str);
4737
                }
4738
        }
4739
4740
        /* custom headers */
4741
        if (compose->account->add_customhdr) {
4742
                GSList *cur;
4743
4744
                for (cur = compose->account->customhdr_list; cur != NULL;
4745
                     cur = cur->next) {
4746
                        CustomHeader *chdr = (CustomHeader *)cur->data;
4747
4748
                        if (g_ascii_strcasecmp(chdr->name, "Date") != 0 &&
4749
                            g_ascii_strcasecmp(chdr->name, "From") != 0 &&
4750
                            g_ascii_strcasecmp(chdr->name, "To") != 0 &&
4751
                         /* g_ascii_strcasecmp(chdr->name, "Sender") != 0 && */
4752
                            g_ascii_strcasecmp(chdr->name, "Message-Id") != 0 &&
4753
                            g_ascii_strcasecmp(chdr->name, "In-Reply-To") != 0 &&
4754
                            g_ascii_strcasecmp(chdr->name, "References") != 0 &&
4755
                            g_ascii_strcasecmp(chdr->name, "Mime-Version") != 0 &&
4756
                            g_ascii_strcasecmp(chdr->name, "Content-Type") != 0 &&
4757
                            g_ascii_strcasecmp(chdr->name, "Content-Transfer-Encoding") != 0) {
4758
                                compose_convert_header
4759
                                        (compose, buf, sizeof(buf),
4760
                                         chdr->value ? chdr->value : "",
4761
                                         strlen(chdr->name) + 2, FALSE,
4762
                                         charset);
4763
                                fprintf(fp, "%s: %s\n", chdr->name, buf);
4764
                        }
4765
                }
4766
        }
4767
4768
        /* MIME */
4769
        fprintf(fp, "Mime-Version: 1.0\n");
4770
        if (compose->use_attach &&
4771
            gtk_tree_model_iter_n_children
4772
                (GTK_TREE_MODEL(compose->attach_store), NULL) > 0) {
4773
                compose->boundary = generate_mime_boundary(NULL);
4774
                fprintf(fp,
4775
                        "Content-Type: multipart/mixed;\n"
4776
                        " boundary=\"%s\"\n", compose->boundary);
4777
        } else {
4778
                fprintf(fp, "Content-Type: text/plain; charset=%s\n",
4779
                        body_charset);
4780
#if USE_GPGME
4781
                if (rfc2015_is_available() &&
4782
                    compose->use_signing && !compose->account->clearsign)
4783
                        fprintf(fp, "Content-Disposition: inline\n");
4784
#endif
4785
                fprintf(fp, "Content-Transfer-Encoding: %s\n",
4786
                        procmime_get_encoding_str(encoding));
4787
        }
4788
4789
        /* X-Sylpheed headers */
4790
        if (is_draft) {
4791
                fprintf(fp, "X-Sylpheed-Account-Id: %d\n",
4792
                        compose->account->account_id);
4793
                if (compose->reply_target)
4794
                        fprintf(fp, "X-Sylpheed-Reply: %s\n",
4795
                                compose->reply_target);
4796
                else if (compose->forward_targets)
4797
                        fprintf(fp, "X-Sylpheed-Forward: %s\n",
4798
                                compose->forward_targets);
4799
                fprintf(fp, "X-Sylpheed-Compose-AutoWrap: %s\n",
4800
                        compose->autowrap ? "TRUE" : "FALSE");
4801
#if USE_GTKSPELL
4802
                fprintf(fp, "X-Sylpheed-Compose-CheckSpell: %s\n",
4803
                        compose->check_spell ? "TRUE" : "FALSE");
4804
                if (compose->spell_lang)
4805
                        fprintf(fp, "X-Sylpheed-Compose-SpellLang: %s\n",
4806
                                compose->spell_lang);
4807
#endif
4808
#if USE_GPGME
4809
                fprintf(fp, "X-Sylpheed-Compose-UseSigning: %s\n",
4810
                        compose->use_signing ? "TRUE" : "FALSE");
4811
                fprintf(fp, "X-Sylpheed-Compose-UseEncryption: %s\n",
4812
                        compose->use_encryption ? "TRUE" : "FALSE");
4813
#endif
4814
        }
4815
4816
        /* separator between header and body */
4817
        fputs("\n", fp);
4818
4819
        return 0;
4820
}
4821
4822
static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4823
{
4824
        gchar buf[BUFFSIZE];
4825
        const gchar *entry_str;
4826
        gchar *str;
4827
        const gchar *charset = NULL;
4828
4829
        g_return_val_if_fail(fp != NULL, -1);
4830
        g_return_val_if_fail(compose->account != NULL, -1);
4831
        g_return_val_if_fail(compose->account->address != NULL, -1);
4832
4833
        /* Resent-Date */
4834
        get_rfc822_date(buf, sizeof(buf));
4835
        fprintf(fp, "Resent-Date: %s\n", buf);
4836
4837
        /* Resent-From */
4838
        if (compose->account->name) {
4839
                compose_convert_header
4840
                        (compose, buf, sizeof(buf), compose->account->name,
4841
                         strlen("Resent-From: "), TRUE, NULL);
4842
                fprintf(fp, "Resent-From: %s <%s>\n",
4843
                        buf, compose->account->address);
4844
        } else
4845
                fprintf(fp, "Resent-From: %s\n", compose->account->address);
4846
4847
        slist_free_strings(compose->to_list);
4848
        g_slist_free(compose->to_list);
4849
        compose->to_list = NULL;
4850
4851
        /* Resent-To */
4852
        if (compose->use_to) {
4853
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4854
                PUT_RECIPIENT_HEADER("Resent-To", entry_str);
4855
        }
4856
        if (compose->use_cc) {
4857
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4858
                PUT_RECIPIENT_HEADER("Resent-Cc", entry_str);
4859
        }
4860
        if (compose->use_bcc) {
4861
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4862
                PUT_RECIPIENT_HEADER("Bcc", entry_str);
4863
        }
4864
4865
        slist_free_strings(compose->newsgroup_list);
4866
        g_slist_free(compose->newsgroup_list);
4867
        compose->newsgroup_list = NULL;
4868
4869
        /* Newsgroups */
4870
        if (compose->use_newsgroups) {
4871
                entry_str = gtk_entry_get_text
4872
                        (GTK_ENTRY(compose->newsgroups_entry));
4873
                if (*entry_str != '\0') {
4874
                        Xstrdup_a(str, entry_str, return -1);
4875
                        g_strstrip(str);
4876
                        remove_space(str);
4877
                        if (*str != '\0') {
4878
                                compose->newsgroup_list =
4879
                                        newsgroup_list_append
4880
                                                (compose->newsgroup_list, str);
4881
                                compose_convert_header(compose,
4882
                                                       buf, sizeof(buf), str,
4883
                                                       strlen("Newsgroups: "),
4884
                                                       FALSE, NULL);
4885
                                fprintf(fp, "Newsgroups: %s\n", buf);
4886
                        }
4887
                }
4888
        }
4889
4890
        if (!compose->to_list && !compose->newsgroup_list)
4891
                return -1;
4892
4893
        /* Subject */
4894
        entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4895
        if (*entry_str != '\0') {
4896
                Xstrdup_a(str, entry_str, return -1);
4897
                g_strstrip(str);
4898
                if (*str != '\0') {
4899
                        compose_convert_header(compose, buf, sizeof(buf), str,
4900
                                               strlen("Subject: "), FALSE,
4901
                                               NULL);
4902
                        fprintf(fp, "Subject: %s\n", buf);
4903
                }
4904
        }
4905
4906
        /* Resent-Message-Id */
4907
        if (compose->account->gen_msgid) {
4908
                compose_generate_msgid(compose, buf, sizeof(buf));
4909
                fprintf(fp, "Resent-Message-Id: <%s>\n", buf);
4910
                compose->msgid = g_strdup(buf);
4911
        }
4912
4913
        /* Followup-To */
4914
        if (compose->use_followupto) {
4915
                entry_str = gtk_entry_get_text
4916
                        (GTK_ENTRY(compose->followup_entry));
4917
                if (*entry_str != '\0') {
4918
                        Xstrdup_a(str, entry_str, return -1);
4919
                        g_strstrip(str);
4920
                        remove_space(str);
4921
                        if (*str != '\0') {
4922
                                compose_convert_header(compose,
4923
                                                       buf, sizeof(buf), str,
4924
                                                       strlen("Followup-To: "),
4925
                                                       FALSE, NULL);
4926
                                fprintf(fp, "Followup-To: %s\n", buf);
4927
                        }
4928
                }
4929
        }
4930
4931
        /* Resent-Reply-To */
4932
        if (compose->use_replyto) {
4933
                entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4934
                if (*entry_str != '\0') {
4935
                        Xstrdup_a(str, entry_str, return -1);
4936
                        g_strstrip(str);
4937
                        if (*str != '\0') {
4938
                                compose_convert_header
4939
                                        (compose, buf, sizeof(buf), str,
4940
                                         strlen("Resent-Reply-To: "), TRUE,
4941
                                         NULL);
4942
                                fprintf(fp, "Resent-Reply-To: %s\n", buf);
4943
                        }
4944
                }
4945
        }
4946
4947
        fputs("\n", fp);
4948
4949
        return 0;
4950
}
4951
4952
#undef IS_IN_CUSTOM_HEADER
4953
4954
static void compose_convert_header(Compose *compose, gchar *dest, gint len,
4955
                                   const gchar *src, gint header_len,
4956
                                   gboolean addr_field, const gchar *encoding)
4957
{
4958
        gchar *src_;
4959
4960
        g_return_if_fail(src != NULL);
4961
        g_return_if_fail(dest != NULL);
4962
4963
        if (len < 1) return;
4964
4965
        if (addr_field)
4966
                src_ = normalize_address_field(src);
4967
        else
4968
                src_ = g_strdup(src);
4969
        g_strchomp(src_);
4970
        if (!encoding)
4971
                encoding = conv_get_charset_str(compose->out_encoding);
4972
4973
        conv_encode_header(dest, len, src_, header_len, addr_field, encoding);
4974
4975
        g_free(src_);
4976
}
4977
4978
static gchar *compose_convert_filename(Compose *compose, const gchar *src,
4979
                                       const gchar *param_name,
4980
                                       const gchar *encoding)
4981
{
4982
        gchar *str;
4983
4984
        g_return_val_if_fail(src != NULL, NULL);
4985
4986
        if (!encoding)
4987
                encoding = conv_get_charset_str(compose->out_encoding);
4988
4989
        str = conv_encode_filename(src, param_name, encoding);
4990
4991
        return str;
4992
}
4993
4994
static void compose_generate_msgid(Compose *compose, gchar *buf, gint len)
4995
{
4996
        struct tm *lt;
4997
        time_t t;
4998
        gchar *addr;
4999
5000
        t = time(NULL);
5001
        lt = localtime(&t);
5002
5003
        if (compose->account && compose->account->address &&
5004
            *compose->account->address) {
5005
                if (strchr(compose->account->address, '@'))
5006
                        addr = g_strdup(compose->account->address);
5007
                else
5008
                        addr = g_strconcat(compose->account->address, "@",
5009
                                           get_domain_name(), NULL);
5010
        } else
5011
                addr = g_strconcat(g_get_user_name(), "@", get_domain_name(),
5012
                                   NULL);
5013
5014
        g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x.%s",
5015
                   lt->tm_year + 1900, lt->tm_mon + 1,
5016
                   lt->tm_mday, lt->tm_hour,
5017
                   lt->tm_min, lt->tm_sec,
5018
                   g_random_int(), addr);
5019
5020
        debug_print(_("generated Message-ID: %s\n"), buf);
5021
5022
        g_free(addr);
5023
}
5024
5025
static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox,
5026
                                    GtkWidget **entry, gint *count,
5027
                                    const gchar *label_str,
5028
                                    gboolean is_addr_entry)
5029
{
5030
        GtkWidget *label;
5031
5032
        if (GTK_TABLE(table)->nrows < (*count) + 1)
5033
                gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2);
5034
5035
        *hbox = gtk_hbox_new(FALSE, 0);
5036
        label = gtk_label_new
5037
                (prefs_common.trans_hdr ? gettext(label_str) : label_str);
5038
        gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0);
5039
        gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1,
5040
                         GTK_FILL, 0, 2, 0);
5041
        *entry = gtk_entry_new();
5042
        gtk_entry_set_max_length(GTK_ENTRY(*entry), MAX_ENTRY_LENGTH);
5043
        gtk_table_attach_defaults
5044
                (GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1);
5045
        if (GTK_TABLE(table)->nrows > (*count) + 1)
5046
                gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4);
5047
5048
        if (is_addr_entry && prefs_common.enable_address_completion)
5049
                address_completion_register_entry(GTK_ENTRY(*entry));
5050
5051
        (*count)++;
5052
}
5053
5054
static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
5055
{
5056
        Compose   *compose;
5057
        GtkWidget *window;
5058
        GtkWidget *vbox;
5059
        GtkWidget *menubar;
5060
        GtkWidget *toolbar;
5061
5062
        GtkWidget *vbox2;
5063
5064
        GtkWidget *table_vbox;
5065
        GtkWidget *table;
5066
        GtkWidget *hbox;
5067
        GtkWidget *label;
5068
        GtkWidget *from_optmenu_hbox;
5069
        GtkWidget *to_entry;
5070
        GtkWidget *to_hbox;
5071
        GtkWidget *newsgroups_entry;
5072
        GtkWidget *newsgroups_hbox;
5073
        GtkWidget *subject_entry;
5074
        GtkWidget *cc_entry;
5075
        GtkWidget *cc_hbox;
5076
        GtkWidget *bcc_entry;
5077
        GtkWidget *bcc_hbox;
5078
        GtkWidget *reply_entry;
5079
        GtkWidget *reply_hbox;
5080
        GtkWidget *followup_entry;
5081
        GtkWidget *followup_hbox;
5082
5083
#if USE_GPGME
5084
        GtkWidget *misc_hbox;
5085
        GtkWidget *signing_chkbtn;
5086
        GtkWidget *encrypt_chkbtn;
5087
#endif /* USE_GPGME */
5088
#if 0
5089
        GtkWidget *attach_img;
5090
        GtkWidget *attach_toggle;
5091
#endif
5092
5093
        GtkWidget *paned;
5094
5095
        GtkWidget *attach_scrwin;
5096
        GtkWidget *attach_treeview;
5097
        GtkListStore *store;
5098
        GtkTreeSelection *selection;
5099
        GtkTreeViewColumn *column;
5100
        GtkCellRenderer *renderer;
5101
5102
        GtkWidget *edit_vbox;
5103
        GtkWidget *ruler_hbox;
5104
        GtkWidget *ruler;
5105
        GtkWidget *scrolledwin;
5106
        GtkWidget *text;
5107
5108
        GtkTextBuffer *buffer;
5109
        GtkClipboard *clipboard;
5110
        GtkTextTag *sig_tag;
5111
5112
#if USE_GTKSPELL
5113
        GtkWidget *spell_menu;
5114
#endif /* USE_GTKSPELL */
5115
5116
        UndoMain *undostruct;
5117
5118
        guint n_menu_entries;
5119
        GdkColormap *cmap;
5120
        GdkColor color[1];
5121
        gboolean success[1];
5122
        GtkWidget *popupmenu;
5123
        GtkItemFactory *popupfactory;
5124
        GtkItemFactory *ifactory;
5125
        GtkWidget *tmpl_menu;
5126
        gint n_entries;
5127
        gint count = 0;
5128
5129
#ifndef G_OS_WIN32
5130
        static GdkGeometry geometry;
5131
#endif
5132
5133
        g_return_val_if_fail(account != NULL, NULL);
5134
5135
        debug_print(_("Creating compose window...\n"));
5136
        compose = g_new0(Compose, 1);
5137
5138
        compose->account = account;
5139
5140
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
5141
        gtk_window_set_wmclass(GTK_WINDOW(window), "compose", "Sylpheed");
5142
        gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
5143
        gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
5144
        gtkut_window_move(GTK_WINDOW(window), prefs_common.compose_x,
5145
                          prefs_common.compose_y);
5146
5147
#ifndef G_OS_WIN32
5148
        if (!geometry.max_width) {
5149
                geometry.max_width = gdk_screen_width();
5150
                geometry.max_height = gdk_screen_height();
5151
        }
5152
        gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
5153
                                      &geometry, GDK_HINT_MAX_SIZE);
5154
#endif
5155
5156
        g_signal_connect(G_OBJECT(window), "delete_event",
5157
                         G_CALLBACK(compose_delete_cb), compose);
5158
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
5159
5160
        vbox = gtk_vbox_new(FALSE, 0);
5161
        gtk_container_add(GTK_CONTAINER(window), vbox);
5162
5163
        n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
5164
        menubar = menubar_create(window, compose_entries,
5165
                                 n_menu_entries, "<Compose>", compose);
5166
        gtk_widget_set_size_request(menubar, 300, -1);
5167
        gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
5168
5169
        toolbar = compose_toolbar_create(compose);
5170
        gtk_widget_set_size_request(toolbar, 300, -1);
5171
        gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
5172
5173
        vbox2 = gtk_vbox_new(FALSE, 2);
5174
        gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
5175
        gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
5176
5177
        table_vbox = gtk_vbox_new(FALSE, 0);
5178
        gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0);
5179
        gtk_container_set_border_width(GTK_CONTAINER(table_vbox), BORDER_WIDTH);
5180
5181
        table = gtk_table_new(8, 2, FALSE);
5182
        gtk_box_pack_start(GTK_BOX(table_vbox), table, FALSE, TRUE, 0);
5183
5184
        /* option menu for selecting accounts */
5185
        hbox = gtk_hbox_new(FALSE, 0);
5186
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
5187
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
5188
        gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, count, count + 1,
5189
                         GTK_FILL, 0, 2, 0);
5190
5191
        from_optmenu_hbox = gtk_hbox_new(FALSE, 0);
5192
        gtk_table_attach_defaults(GTK_TABLE(table), from_optmenu_hbox,
5193
                                  1, 2, count, count + 1);
5194
        gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
5195
        count++;
5196
        compose_account_option_menu_create(compose, from_optmenu_hbox);
5197
5198
        /* header labels and entries */
5199
        compose_add_entry_field(table, &to_hbox, &to_entry, &count,
5200
                                "To:", TRUE); 
5201
        compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry,
5202
                                &count, "Newsgroups:", FALSE);
5203
        compose_add_entry_field(table, &cc_hbox, &cc_entry, &count,
5204
                                "Cc:", TRUE);
5205
        compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count,
5206
                                "Bcc:", TRUE);
5207
        compose_add_entry_field(table, &reply_hbox, &reply_entry, &count,
5208
                                "Reply-To:", TRUE);
5209
        compose_add_entry_field(table, &followup_hbox, &followup_entry, &count,
5210
                                "Followup-To:", FALSE);
5211
        compose_add_entry_field(table, &hbox, &subject_entry, &count,
5212
                                "Subject:", FALSE);
5213
5214
        gtk_table_set_col_spacings(GTK_TABLE(table), 4);
5215
5216
        g_signal_connect(G_OBJECT(to_entry), "activate",
5217
                         G_CALLBACK(to_activated), compose);
5218
        g_signal_connect(G_OBJECT(newsgroups_entry), "activate",
5219
                         G_CALLBACK(newsgroups_activated), compose);
5220
        g_signal_connect(G_OBJECT(cc_entry), "activate",
5221
                         G_CALLBACK(cc_activated), compose);
5222
        g_signal_connect(G_OBJECT(bcc_entry), "activate",
5223
                         G_CALLBACK(bcc_activated), compose);
5224
        g_signal_connect(G_OBJECT(reply_entry), "activate",
5225
                         G_CALLBACK(replyto_activated), compose);
5226
        g_signal_connect(G_OBJECT(followup_entry), "activate",
5227
                         G_CALLBACK(followupto_activated), compose);
5228
        g_signal_connect(G_OBJECT(subject_entry), "activate",
5229
                         G_CALLBACK(subject_activated), compose);
5230
5231
        g_signal_connect(G_OBJECT(to_entry), "grab_focus",
5232
                         G_CALLBACK(compose_grab_focus_cb), compose);
5233
        g_signal_connect(G_OBJECT(newsgroups_entry), "grab_focus",
5234
                         G_CALLBACK(compose_grab_focus_cb), compose);
5235
        g_signal_connect(G_OBJECT(cc_entry), "grab_focus",
5236
                         G_CALLBACK(compose_grab_focus_cb), compose);
5237
        g_signal_connect(G_OBJECT(bcc_entry), "grab_focus",
5238
                         G_CALLBACK(compose_grab_focus_cb), compose);
5239
        g_signal_connect(G_OBJECT(reply_entry), "grab_focus",
5240
                         G_CALLBACK(compose_grab_focus_cb), compose);
5241
        g_signal_connect(G_OBJECT(followup_entry), "grab_focus",
5242
                         G_CALLBACK(compose_grab_focus_cb), compose);
5243
        g_signal_connect(G_OBJECT(subject_entry), "grab_focus",
5244
                         G_CALLBACK(compose_grab_focus_cb), compose);
5245
5246
#if 0
5247
        attach_img = stock_pixbuf_widget(window, STOCK_PIXMAP_CLIP);
5248
        attach_toggle = gtk_toggle_button_new();
5249
        GTK_WIDGET_UNSET_FLAGS(attach_toggle, GTK_CAN_FOCUS);
5250
        gtk_container_add(GTK_CONTAINER(attach_toggle), attach_img);
5251
        gtk_box_pack_start(GTK_BOX(misc_hbox), attach_toggle, FALSE, FALSE, 8);
5252
        g_signal_connect(G_OBJECT(attach_toggle), "toggled",
5253
                         G_CALLBACK(compose_attach_toggled), compose);
5254
#endif
5255
5256
#if USE_GPGME
5257
        misc_hbox = gtk_hbox_new(FALSE, 0);
5258
        gtk_box_pack_start(GTK_BOX(vbox2), misc_hbox, FALSE, FALSE, 0);
5259
5260
        signing_chkbtn = gtk_check_button_new_with_label(_("PGP Sign"));
5261
        GTK_WIDGET_UNSET_FLAGS(signing_chkbtn, GTK_CAN_FOCUS);
5262
        gtk_box_pack_start(GTK_BOX(misc_hbox), signing_chkbtn, FALSE, FALSE, 8);
5263
        encrypt_chkbtn = gtk_check_button_new_with_label(_("PGP Encrypt"));
5264
        GTK_WIDGET_UNSET_FLAGS(encrypt_chkbtn, GTK_CAN_FOCUS);
5265
        gtk_box_pack_start(GTK_BOX(misc_hbox), encrypt_chkbtn, FALSE, FALSE, 8);
5266
5267
        g_signal_connect(G_OBJECT(signing_chkbtn), "toggled",
5268
                         G_CALLBACK(compose_signing_toggled), compose);
5269
        g_signal_connect(G_OBJECT(encrypt_chkbtn), "toggled",
5270
                         G_CALLBACK(compose_encrypt_toggled), compose);
5271
#endif /* USE_GPGME */
5272
5273
        /* attachment list */
5274
        attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
5275
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
5276
                                       GTK_POLICY_AUTOMATIC,
5277
                                       GTK_POLICY_ALWAYS);
5278
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(attach_scrwin),
5279
                                            GTK_SHADOW_IN);
5280
        gtk_widget_set_size_request(attach_scrwin, -1, 80);
5281
5282
        store = gtk_list_store_new(N_ATTACH_COLS, G_TYPE_STRING, G_TYPE_STRING,
5283
                                   G_TYPE_STRING, G_TYPE_POINTER);
5284
5285
        attach_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
5286
        g_object_unref(G_OBJECT(store));
5287
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(attach_treeview), TRUE);
5288
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_treeview), TRUE);
5289
        gtk_tree_view_set_search_column(GTK_TREE_VIEW(attach_treeview),
5290
                                        COL_NAME);
5291
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(attach_treeview), FALSE);
5292
5293
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_treeview));
5294
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
5295
5296
        gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_treeview);
5297
5298
        renderer = gtk_cell_renderer_text_new();
5299
        g_object_set(renderer, "ypad", 0, NULL);
5300
        column = gtk_tree_view_column_new_with_attributes
5301
                (_("Data type"), renderer, "text", COL_MIMETYPE, NULL);
5302
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5303
        gtk_tree_view_column_set_fixed_width(column, 240);
5304
        gtk_tree_view_column_set_resizable(column, TRUE);
5305
        gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5306
5307
        renderer = gtk_cell_renderer_text_new();
5308
        g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
5309
        column = gtk_tree_view_column_new_with_attributes
5310
                (_("Size"), renderer, "text", COL_SIZE, NULL);
5311
        gtk_tree_view_column_set_alignment(column, 1.0);
5312
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5313
        gtk_tree_view_column_set_fixed_width(column, 64);
5314
        gtk_tree_view_column_set_resizable(column, TRUE);
5315
        gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5316
5317
        renderer = gtk_cell_renderer_text_new();
5318
        g_object_set(renderer, "ypad", 0, NULL);
5319
        column = gtk_tree_view_column_new_with_attributes
5320
                (_("Name"), renderer, "text", COL_NAME, NULL);
5321
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5322
        gtk_tree_view_column_set_resizable(column, TRUE);
5323
        gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5324
5325
        g_signal_connect(G_OBJECT(selection), "changed",
5326
                         G_CALLBACK(attach_selection_changed), compose);
5327
        g_signal_connect(G_OBJECT(attach_treeview), "button_press_event",
5328
                         G_CALLBACK(attach_button_pressed), compose);
5329
        g_signal_connect(G_OBJECT(attach_treeview), "key_press_event",
5330
                         G_CALLBACK(attach_key_pressed), compose);
5331
5332
        /* drag and drop */
5333
        gtk_drag_dest_set(window,
5334
                          GTK_DEST_DEFAULT_ALL, compose_drag_types,
5335
                          N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE);
5336
        g_signal_connect(G_OBJECT(window), "drag-data-received",
5337
                         G_CALLBACK(compose_attach_drag_received_cb),
5338
                         compose);
5339
5340
        /* pane between attach tree view and text */
5341
        paned = gtk_vpaned_new();
5342
        gtk_paned_add1(GTK_PANED(paned), attach_scrwin);
5343
        gtk_widget_ref(paned);
5344
        gtk_widget_show_all(paned);
5345
5346
        edit_vbox = gtk_vbox_new(FALSE, 0);
5347
        gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0);
5348
5349
        /* ruler */
5350
        ruler_hbox = gtk_hbox_new(FALSE, 0);
5351
        gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
5352
5353
        ruler = gtk_shruler_new();
5354
        gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
5355
        gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE, 0);
5356
5357
        /* text widget */
5358
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
5359
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
5360
                                       GTK_POLICY_AUTOMATIC,
5361
                                       GTK_POLICY_ALWAYS);
5362
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
5363
                                            GTK_SHADOW_IN);
5364
        gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
5365
        gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width,
5366
                                    -1);
5367
5368
        text = gtk_text_view_new();
5369
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
5370
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
5371
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
5372
        gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN);
5373
        gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN);
5374
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
5375
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
5376
        sig_tag = gtk_text_buffer_create_tag(buffer, "signature", NULL);
5377
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
5378
5379
        gtk_shruler_set_start_pos(GTK_SHRULER(ruler),
5380
                                  text->style->xthickness + TEXTVIEW_MARGIN);
5381
5382
        g_signal_connect(G_OBJECT(text), "grab_focus",
5383
                         G_CALLBACK(compose_grab_focus_cb), compose);
5384
        g_signal_connect(G_OBJECT(buffer), "insert_text",
5385
                         G_CALLBACK(text_inserted), compose);
5386
        g_signal_connect_after(G_OBJECT(text), "size_allocate",
5387
                               G_CALLBACK(compose_edit_size_alloc),
5388
                               ruler);
5389
5390
        /* drag and drop */
5391
        gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_drag_types,
5392
                          N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE);
5393
        g_signal_connect(G_OBJECT(text), "drag-data-received",
5394
                         G_CALLBACK(compose_insert_drag_received_cb),
5395
                         compose);
5396
5397
        gtk_widget_show_all(vbox);
5398
5399
        if (prefs_common.textfont) {
5400
                PangoFontDescription *font_desc;
5401
5402
                font_desc = pango_font_description_from_string
5403
                        (prefs_common.textfont);
5404
                if (font_desc) {
5405
                        gtk_widget_modify_font(text, font_desc);
5406
                        pango_font_description_free(font_desc);
5407
                }
5408
        }
5409
5410
        gtk_text_view_set_pixels_above_lines
5411
                (GTK_TEXT_VIEW(text), prefs_common.line_space / 2);
5412
        gtk_text_view_set_pixels_below_lines
5413
                (GTK_TEXT_VIEW(text), prefs_common.line_space / 2);
5414
5415
        n_entries = sizeof(compose_popup_entries) /
5416
                sizeof(compose_popup_entries[0]);
5417
        popupmenu = menu_create_items(compose_popup_entries, n_entries,
5418
                                      "<Compose>", &popupfactory,
5419
                                      compose);
5420
5421
        ifactory = gtk_item_factory_from_widget(menubar);
5422
        menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
5423
        menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
5424
5425
        tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
5426
5427
#if USE_GTKSPELL
5428
        spell_menu = gtk_item_factory_get_item
5429
                (ifactory, "/Tools/Set spell language");
5430
#endif
5431
5432
        gtk_widget_hide(bcc_hbox);
5433
        gtk_widget_hide(bcc_entry);
5434
        gtk_widget_hide(reply_hbox);
5435
        gtk_widget_hide(reply_entry);
5436
        gtk_widget_hide(followup_hbox);
5437
        gtk_widget_hide(followup_entry);
5438
        gtk_widget_hide(ruler_hbox);
5439
        gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0);
5440
        gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0);
5441
        gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0);
5442
5443
        if (account->protocol == A_NNTP) {
5444
                gtk_widget_hide(to_hbox);
5445
                gtk_widget_hide(to_entry);
5446
                gtk_widget_hide(cc_hbox);
5447
                gtk_widget_hide(cc_entry);
5448
                gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
5449
                gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
5450
        } else {
5451
                gtk_widget_hide(newsgroups_hbox);
5452
                gtk_widget_hide(newsgroups_entry);
5453
                gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
5454
        }
5455
5456
#if USE_GPGME
5457
        if (!rfc2015_is_available())
5458
                gtk_widget_hide(misc_hbox);
5459
#endif
5460
5461
        undostruct = undo_init(text);
5462
        undo_set_change_state_func(undostruct, &compose_undo_state_changed,
5463
                                   menubar);
5464
5465
        address_completion_start(window);
5466
5467
        compose->window        = window;
5468
        compose->vbox               = vbox;
5469
        compose->menubar       = menubar;
5470
        compose->toolbar       = toolbar;
5471
5472
        compose->vbox2               = vbox2;
5473
5474
        compose->table_vbox       = table_vbox;
5475
        compose->table                  = table;
5476
        compose->to_hbox          = to_hbox;
5477
        compose->to_entry         = to_entry;
5478
        compose->newsgroups_hbox  = newsgroups_hbox;
5479
        compose->newsgroups_entry = newsgroups_entry;
5480
        compose->subject_entry    = subject_entry;
5481
        compose->cc_hbox          = cc_hbox;
5482
        compose->cc_entry         = cc_entry;
5483
        compose->bcc_hbox         = bcc_hbox;
5484
        compose->bcc_entry        = bcc_entry;
5485
        compose->reply_hbox       = reply_hbox;
5486
        compose->reply_entry      = reply_entry;
5487
        compose->followup_hbox    = followup_hbox;
5488
        compose->followup_entry   = followup_entry;
5489
5490
        /* compose->attach_toggle = attach_toggle; */
5491
#if USE_GPGME
5492
        compose->misc_hbox      = misc_hbox;
5493
        compose->signing_chkbtn = signing_chkbtn;
5494
        compose->encrypt_chkbtn = encrypt_chkbtn;
5495
#endif /* USE_GPGME */
5496
5497
        compose->paned = paned;
5498
5499
        compose->attach_scrwin   = attach_scrwin;
5500
        compose->attach_treeview = attach_treeview;
5501
        compose->attach_store    = store;
5502
5503
        compose->edit_vbox     = edit_vbox;
5504
        compose->ruler_hbox    = ruler_hbox;
5505
        compose->ruler         = ruler;
5506
        compose->scrolledwin   = scrolledwin;
5507
        compose->text               = text;
5508
5509
#ifdef USE_GTKSPELL
5510
        compose->check_spell = prefs_common.check_spell;
5511
        compose->spell_lang  = g_strdup(prefs_common.spell_lang);
5512
        compose->spell_menu  = spell_menu;
5513
        compose->dict_list   = NULL;
5514
#endif /* USE_GTKSPELL */
5515
5516
        compose->focused_editable = NULL;
5517
5518
        compose->popupmenu    = popupmenu;
5519
        compose->popupfactory = popupfactory;
5520
5521
        compose->tmpl_menu = tmpl_menu;
5522
5523
        compose->mode = mode;
5524
5525
        compose->targetinfo      = NULL;
5526
        compose->reply_target    = NULL;
5527
        compose->forward_targets = NULL;
5528
5529
        compose->replyto     = NULL;
5530
        compose->cc             = NULL;
5531
        compose->bcc             = NULL;
5532
        compose->followup_to = NULL;
5533
5534
        compose->ml_post     = NULL;
5535
5536
        compose->inreplyto   = NULL;
5537
        compose->references  = NULL;
5538
        compose->msgid       = NULL;
5539
        compose->boundary    = NULL;
5540
5541
        compose->autowrap       = prefs_common.autowrap;
5542
5543
        compose->use_to         = FALSE;
5544
        compose->use_cc         = FALSE;
5545
        compose->use_bcc        = FALSE;
5546
        compose->use_replyto    = FALSE;
5547
        compose->use_newsgroups = FALSE;
5548
        compose->use_followupto = FALSE;
5549
        compose->use_attach     = FALSE;
5550
5551
        compose->out_encoding   = C_AUTO;
5552
5553
        compose->use_mdn        = FALSE;
5554
5555
#if USE_GPGME
5556
        compose->use_signing    = FALSE;
5557
        compose->use_encryption = FALSE;
5558
#endif /* USE_GPGME */
5559
5560
        compose->modified = FALSE;
5561
5562
        compose->to_list        = NULL;
5563
        compose->newsgroup_list = NULL;
5564
5565
        compose->undostruct = undostruct;
5566
5567
        compose->sig_tag = sig_tag;
5568
5569
        compose->exteditor_file = NULL;
5570
        compose->exteditor_pid  = 0;
5571
        compose->exteditor_tag  = 0;
5572
5573
        compose->autosave_tag = 0;
5574
5575
        compose->window_maximized = prefs_common.compose_maximized;
5576
5577
        compose->block_modified = FALSE;
5578
5579
        compose_set_toolbar_button_visibility(compose);
5580
5581
        compose_select_account(compose, account, TRUE);
5582
5583
        if (prefs_common.compose_maximized)
5584
                gtk_window_maximize(GTK_WINDOW(window));
5585
5586
        g_signal_connect(G_OBJECT(window), "window_state_event",
5587
                         G_CALLBACK(compose_window_state_cb), compose);
5588
5589
        menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
5590
        menu_set_active(ifactory, "/View/Ruler", prefs_common.show_ruler);
5591
5592
        if (mode == COMPOSE_REDIRECT) {
5593
                menu_set_sensitive(ifactory, "/File/Save to draft folder", FALSE);
5594
                menu_set_sensitive(ifactory, "/File/Save and keep editing", FALSE);
5595
                menu_set_sensitive(ifactory, "/File/Attach file", FALSE);
5596
                menu_set_sensitive(ifactory, "/File/Insert file", FALSE);
5597
                menu_set_sensitive(ifactory, "/File/Insert signature", FALSE);
5598
                menu_set_sensitive(ifactory, "/Edit/Cut", FALSE);
5599
                menu_set_sensitive(ifactory, "/Edit/Paste", FALSE);
5600
                menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE);
5601
                menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE);
5602
                menu_set_sensitive(ifactory, "/Edit/Auto wrapping", FALSE);
5603
                menu_set_sensitive(ifactory, "/View/Attachment", FALSE);
5604
                menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
5605
#ifndef G_OS_WIN32
5606
                menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
5607
#endif
5608
                menu_set_sensitive(ifactory, "/Tools/Edit with external editor", FALSE);
5609
#if USE_GPGME
5610
                menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE);
5611
                menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE);
5612
#endif /* USE_GPGME */
5613
#if USE_GTKSPELL
5614
                menu_set_sensitive(ifactory, "/Tools/Check spell", FALSE);
5615
                menu_set_sensitive(ifactory, "/Tools/Set spell language",
5616
                                   FALSE);
5617
#endif
5618
5619
                if (compose->insert_btn)
5620
                        gtk_widget_set_sensitive(compose->insert_btn, FALSE);
5621
                if (compose->attach_btn)
5622
                        gtk_widget_set_sensitive(compose->attach_btn, FALSE);
5623
                if (compose->sig_btn)
5624
                        gtk_widget_set_sensitive(compose->sig_btn, FALSE);
5625
                if (compose->exteditor_btn)
5626
                        gtk_widget_set_sensitive(compose->exteditor_btn, FALSE);
5627
                if (compose->linewrap_btn)
5628
                        gtk_widget_set_sensitive(compose->linewrap_btn, FALSE);
5629
5630
                /* gtk_widget_set_sensitive(compose->attach_toggle, FALSE); */
5631
5632
                menu_set_sensitive_all(GTK_MENU_SHELL(compose->popupmenu),
5633
                                       FALSE);
5634
        }
5635
5636
#if USE_GPGME
5637
        if (!rfc2015_is_available()) {
5638
                menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE);
5639
                menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE);
5640
        }
5641
#endif /* USE_GPGME */
5642
5643
        compose_set_out_encoding(compose);
5644
        addressbook_set_target_compose(compose);
5645
#ifndef G_OS_WIN32
5646
        action_update_compose_menu(ifactory, compose);
5647
#endif
5648
        compose_set_template_menu(compose);
5649
5650
#if USE_GTKSPELL
5651
        compose_set_spell_lang_menu(compose);
5652
        if (mode != COMPOSE_REDIRECT)
5653
                menu_set_active(ifactory, "/Tools/Check spell",
5654
                                prefs_common.check_spell);
5655
#endif
5656
5657
        compose_list = g_list_append(compose_list, compose);
5658
5659
        gtk_widget_show(window);
5660
5661
        color[0] = quote_color;
5662
        cmap = gdk_window_get_colormap(window->window);
5663
        gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
5664
        if (success[0] == FALSE) {
5665
                GtkStyle *style;
5666
5667
                g_warning("Compose: color allocation failed.\n");
5668
                style = gtk_widget_get_style(text);
5669
                quote_color = style->black;
5670
        }
5671
5672
        return compose;
5673
}
5674
5675
static Compose *compose_find_window_by_target(MsgInfo *msginfo)
5676
{
5677
        GList *cur;
5678
        Compose *compose;
5679
5680
        g_return_val_if_fail(msginfo != NULL, NULL);
5681
5682
        for (cur = compose_list; cur != NULL; cur = cur->next) {
5683
                compose = cur->data;
5684
                if (procmsg_msginfo_equal(compose->targetinfo, msginfo))
5685
                        return compose;
5686
        }
5687
5688
        return NULL;
5689
}
5690
5691
static gboolean compose_window_exist(gint x, gint y)
5692
{
5693
        GList *cur;
5694
        Compose *compose;
5695
        gint x_, y_;
5696
5697
        for (cur = compose_list; cur != NULL; cur = cur->next) {
5698
                compose = cur->data;
5699
                gtkut_widget_get_uposition(compose->window, &x_, &y_);
5700
                if (x == x_ && y == y_)
5701
                        return TRUE;
5702
        }
5703
5704
        return FALSE;
5705
}
5706
5707
static void compose_connect_changed_callbacks(Compose *compose)
5708
{
5709
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
5710
        GtkTextBuffer *buffer;
5711
5712
        buffer = gtk_text_view_get_buffer(text);
5713
5714
        g_signal_connect(G_OBJECT(buffer), "changed",
5715
                         G_CALLBACK(compose_buffer_changed_cb), compose);
5716
        g_signal_connect(G_OBJECT(compose->to_entry), "changed",
5717
                         G_CALLBACK(compose_changed_cb), compose);
5718
        g_signal_connect(G_OBJECT(compose->newsgroups_entry), "changed",
5719
                         G_CALLBACK(compose_changed_cb), compose);
5720
        g_signal_connect(G_OBJECT(compose->cc_entry), "changed",
5721
                         G_CALLBACK(compose_changed_cb), compose);
5722
        g_signal_connect(G_OBJECT(compose->bcc_entry), "changed",
5723
                         G_CALLBACK(compose_changed_cb), compose);
5724
        g_signal_connect(G_OBJECT(compose->reply_entry), "changed",
5725
                         G_CALLBACK(compose_changed_cb), compose);
5726
        g_signal_connect(G_OBJECT(compose->followup_entry), "changed",
5727
                         G_CALLBACK(compose_changed_cb), compose);
5728
        g_signal_connect(G_OBJECT(compose->subject_entry), "changed",
5729
                         G_CALLBACK(compose_changed_cb), compose);
5730
}
5731
5732
static PrefsToolbarItem items[] =
5733
{
5734
        {T_SEND,                TRUE, toolbar_send_cb},
5735
        {T_SEND_LATER,                TRUE, toolbar_send_later_cb},
5736
        {T_DRAFT,                TRUE, toolbar_draft_cb},
5737
        {T_INSERT_FILE,                FALSE, toolbar_insert_cb},
5738
        {T_ATTACH_FILE,                FALSE, toolbar_attach_cb},
5739
        {T_SIGNATURE,                FALSE, toolbar_sig_cb},
5740
        {T_EDITOR,                FALSE, toolbar_ext_editor_cb},
5741
        {T_LINEWRAP,                FALSE, toolbar_linewrap_cb},
5742
        {T_ADDRESS_BOOK,        FALSE, toolbar_address_cb},
5743
        {T_COMMON_PREFS,        FALSE, toolbar_prefs_common_cb},
5744
        {T_ACCOUNT_PREFS,        FALSE, toolbar_prefs_account_cb},
5745
5746
        {-1, FALSE, NULL}
5747
};
5748
5749
static GtkWidget *compose_toolbar_create(Compose *compose)
5750
{
5751
        GtkWidget *toolbar;
5752
        const gchar *setting;
5753
        GList *item_list;
5754
5755
        if (prefs_common.compose_toolbar_setting &&
5756
            *prefs_common.compose_toolbar_setting != '\0')
5757
                setting = prefs_common.compose_toolbar_setting;
5758
        else
5759
                setting = prefs_toolbar_get_default_compose_setting_name_list();
5760
5761
        item_list = prefs_toolbar_get_item_list_from_name_list(setting);
5762
        toolbar = compose_toolbar_create_from_list(compose, item_list);
5763
        g_list_free(item_list);
5764
5765
        return toolbar;
5766
}
5767
5768
static GtkWidget *compose_toolbar_create_from_list(Compose *compose,
5769
                                                   GList *item_list)
5770
{
5771
        GtkWidget *toolbar;
5772
        GtkWidget *icon_wid;
5773
        GtkToolItem *toolitem;
5774
        gint i;
5775
        GList *cur;
5776
5777
        toolbar = gtk_toolbar_new();
5778
        gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
5779
                                    GTK_ORIENTATION_HORIZONTAL);
5780
        gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
5781
        g_signal_connect(G_OBJECT(toolbar), "button_press_event",
5782
                         G_CALLBACK(toolbar_button_pressed), compose);
5783
5784
        items[0].data = &compose->send_btn;
5785
        items[1].data = &compose->sendl_btn;
5786
        items[2].data = &compose->draft_btn;
5787
        items[3].data = &compose->insert_btn;
5788
        items[4].data = &compose->attach_btn;
5789
        items[5].data = &compose->sig_btn;
5790
        items[6].data = &compose->exteditor_btn;
5791
        items[7].data = &compose->linewrap_btn;
5792
        items[8].data = &compose->addrbook_btn;
5793
        items[9].data = &compose->prefs_common_btn;
5794
        items[10].data = &compose->prefs_account_btn;
5795
        for (i = 0; i <= 10; i++)
5796
                *(GtkWidget **)items[i].data = NULL;
5797
5798
        for (cur = item_list; cur != NULL; cur = cur->next) {
5799
                const PrefsDisplayItem *ditem = cur->data;
5800
                PrefsToolbarItem *item;
5801
                GtkTooltips *tips;
5802
                gint width;
5803
5804
                if (ditem->id == T_SEPARATOR) {
5805
                        toolitem = gtk_separator_tool_item_new();
5806
                        gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
5807
                        continue;
5808
                }
5809
5810
                for (item = items; item->id != -1; item++) {
5811
                        if (ditem->id == item->id)
5812
                                break;
5813
                }
5814
                if (item->id == -1)
5815
                        continue;
5816
5817
                if (ditem->stock_id) {
5818
                        icon_wid = gtk_image_new_from_stock
5819
                                (ditem->stock_id, GTK_ICON_SIZE_LARGE_TOOLBAR);
5820
                } else
5821
                        icon_wid = stock_pixbuf_widget(NULL, ditem->icon);
5822
5823
                toolitem = gtk_tool_button_new(icon_wid, gettext(ditem->label));
5824
                if (ditem->description) {
5825
                        tips = gtk_tooltips_new();
5826
                        gtk_tool_item_set_tooltip(toolitem, tips,
5827
                                                  gettext(ditem->description),
5828
                                                  ditem->name);
5829
                }
5830
5831
                gtkut_get_str_size(GTK_WIDGET(toolitem), gettext(ditem->label),
5832
                                   &width, NULL);
5833
                gtk_tool_item_set_homogeneous
5834
                        (toolitem, width < 52 ? TRUE : FALSE);
5835
                gtk_tool_item_set_is_important(toolitem, item->is_important);
5836
5837
                gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
5838
5839
                g_signal_connect(G_OBJECT(toolitem), "clicked",
5840
                                 G_CALLBACK(item->callback), compose);
5841
                g_signal_connect(G_OBJECT(GTK_BIN(toolitem)->child),
5842
                                 "button_press_event",
5843
                                 G_CALLBACK(toolbar_button_pressed), compose);
5844
5845
                *(GtkWidget **)item->data = GTK_WIDGET(toolitem);
5846
        }
5847
5848
        gtk_widget_show_all(toolbar);
5849
5850
        return toolbar;
5851
}
5852
5853
static void compose_set_toolbar_button_visibility(Compose *compose)
5854
{
5855
        GtkToolbarStyle style = GTK_TOOLBAR_BOTH_HORIZ;
5856
5857
        if (prefs_common.toolbar_style == TOOLBAR_NONE)
5858
                style = -1;
5859
        else if (prefs_common.toolbar_style == TOOLBAR_ICON)
5860
                style = GTK_TOOLBAR_ICONS;
5861
        else if (prefs_common.toolbar_style == TOOLBAR_TEXT)
5862
                style = GTK_TOOLBAR_TEXT;
5863
        else if (prefs_common.toolbar_style == TOOLBAR_BOTH)
5864
                style = GTK_TOOLBAR_BOTH;
5865
        else if (prefs_common.toolbar_style == TOOLBAR_BOTH_HORIZ)
5866
                style = GTK_TOOLBAR_BOTH_HORIZ;
5867
5868
        if (style != -1) {
5869
                gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar), style);
5870
                gtk_widget_show(compose->toolbar);
5871
                gtk_widget_queue_resize(compose->toolbar);
5872
        } else
5873
                gtk_widget_hide(compose->toolbar);
5874
}
5875
5876
static GtkWidget *compose_account_option_menu_create(Compose *compose,
5877
                                                     GtkWidget *hbox)
5878
{
5879
        GList *accounts;
5880
        GtkWidget *optmenu;
5881
        GtkWidget *menu;
5882
        gint num = 0, def_menu = 0;
5883
5884
        accounts = account_get_list();
5885
        g_return_val_if_fail(accounts != NULL, NULL);
5886
5887
        optmenu = gtk_option_menu_new();
5888
        gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
5889
        menu = gtk_menu_new();
5890
5891
        for (; accounts != NULL; accounts = accounts->next, num++) {
5892
                PrefsAccount *ac = (PrefsAccount *)accounts->data;
5893
                GtkWidget *menuitem;
5894
                gchar *name;
5895
5896
                if (ac == compose->account) def_menu = num;
5897
5898
                if (ac->name)
5899
                        name = g_strdup_printf("%s: %s <%s>",
5900
                                               ac->account_name,
5901
                                               ac->name, ac->address);
5902
                else
5903
                        name = g_strdup_printf("%s: %s",
5904
                                               ac->account_name, ac->address);
5905
                MENUITEM_ADD(menu, menuitem, name, ac);
5906
                g_free(name);
5907
                g_signal_connect(G_OBJECT(menuitem), "activate",
5908
                                 G_CALLBACK(account_activated),
5909
                                 compose);
5910
        }
5911