Statistics
| Revision:

root / src / compose.c @ 2886

History | View | Annotate | Download (219 kB)

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