Statistics
| Revision:

root / src / compose.c @ 2471

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