7b2e6fa0bce61a3e81598d80cfaabc830e1e8ced
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #ifndef PANGO_ENABLE_ENGINE
28 #  define PANGO_ENABLE_ENGINE
29 #endif
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35
36 #include <pango/pango-break.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <time.h>
45 #include <stdlib.h>
46 #if HAVE_SYS_WAIT_H
47 #  include <sys/wait.h>
48 #endif
49 #include <signal.h>
50 #include <errno.h>
51 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
52 #include <libgen.h>
53 #endif
54
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
56 #  include <wchar.h>
57 #  include <wctype.h>
58 #endif
59
60 #include "claws.h"
61 #include "main.h"
62 #include "mainwindow.h"
63 #include "compose.h"
64 #ifndef USE_NEW_ADDRBOOK
65         #include "addressbook.h"
66 #else
67         #include "addressbook-dbus.h"
68         #include "addressadd.h"
69 #endif
70 #include "folderview.h"
71 #include "procmsg.h"
72 #include "menu.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
75 #include "imap.h"
76 #include "news.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
80 #include "action.h"
81 #include "account.h"
82 #include "filesel.h"
83 #include "procheader.h"
84 #include "procmime.h"
85 #include "statusbar.h"
86 #include "about.h"
87 #include "base64.h"
88 #include "quoted-printable.h"
89 #include "codeconv.h"
90 #include "utils.h"
91 #include "gtkutils.h"
92 #include "gtkshruler.h"
93 #include "socket.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
96 #include "folder.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
100 #include "undo.h"
101 #include "foldersel.h"
102 #include "toolbar.h"
103 #include "inc.h"
104 #include "message_search.h"
105 #include "combobox.h"
106 #include "hooks.h"
107 #include "privacy.h"
108 #include "timing.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
111
112 enum
113 {
114         COL_MIMETYPE = 0,
115         COL_SIZE     = 1,
116         COL_NAME     = 2,
117         COL_CHARSET  = 3,
118         COL_DATA     = 4,
119         COL_AUTODATA = 5,
120         N_COL_COLUMNS
121 };
122
123 #define N_ATTACH_COLS   (N_COL_COLUMNS)
124
125 typedef enum
126 {
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
142
143 typedef enum
144 {
145         PRIORITY_HIGHEST = 1,
146         PRIORITY_HIGH,
147         PRIORITY_NORMAL,
148         PRIORITY_LOW,
149         PRIORITY_LOWEST
150 } PriorityLevel;
151
152 typedef enum
153 {
154         COMPOSE_INSERT_SUCCESS,
155         COMPOSE_INSERT_READ_ERROR,
156         COMPOSE_INSERT_INVALID_CHARACTER,
157         COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
159
160 typedef enum
161 {
162         COMPOSE_WRITE_FOR_SEND,
163         COMPOSE_WRITE_FOR_STORE
164 } ComposeWriteType;
165
166 typedef enum
167 {
168         COMPOSE_QUOTE_FORCED,
169         COMPOSE_QUOTE_CHECK,
170         COMPOSE_QUOTE_SKIP
171 } ComposeQuoteMode;
172
173 typedef enum {
174     TO_FIELD_PRESENT,
175     SUBJECT_FIELD_PRESENT,
176     BODY_FIELD_PRESENT,
177     NO_FIELD_PRESENT
178 } MailField;
179
180 #define B64_LINE_SIZE           57
181 #define B64_BUFFSIZE            77
182
183 #define MAX_REFERENCES_LEN      999
184
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
187
188 static Compose *compose_generic_new                     (PrefsAccount   *account,
189                                                  const gchar    *to,
190                                                  FolderItem     *item,
191                                                  GList          *attach_files,
192                                                  GList          *listAddress );
193
194 static Compose *compose_create                  (PrefsAccount   *account,
195                                                  FolderItem              *item,
196                                                  ComposeMode     mode,
197                                                  gboolean batch);
198
199 static void compose_entry_mark_default_to       (Compose          *compose,
200                                          const gchar      *address);
201 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
202                                          ComposeQuoteMode        quote_mode,
203                                          gboolean        to_all,
204                                          gboolean        to_sender,
205                                          const gchar    *body);
206 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
207                                          GSList         *msginfo_list);
208 static Compose *compose_reply                   (MsgInfo        *msginfo,
209                                          ComposeQuoteMode        quote_mode,
210                                          gboolean        to_all,
211                                          gboolean        to_ml,
212                                          gboolean        to_sender,
213                                          const gchar    *body);
214 static Compose *compose_reply_mode              (ComposeMode     mode, 
215                                          GSList         *msginfo_list, 
216                                          gchar          *body);
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
219
220 static GtkWidget *compose_account_option_menu_create
221                                                 (Compose        *compose);
222 static void compose_set_out_encoding            (Compose        *compose);
223 static void compose_set_template_menu           (Compose        *compose);
224 static void compose_destroy                     (Compose        *compose);
225
226 static MailField compose_entries_set            (Compose        *compose,
227                                                  const gchar    *mailto,
228                                                  ComposeEntryType to_type);
229 static gint compose_parse_header                (Compose        *compose,
230                                                  MsgInfo        *msginfo);
231 static gint compose_parse_manual_headers        (Compose        *compose,
232                                                  MsgInfo        *msginfo,
233                                                  HeaderEntry    *entries);
234 static gchar *compose_parse_references          (const gchar    *ref,
235                                                  const gchar    *msgid);
236
237 static gchar *compose_quote_fmt                 (Compose        *compose,
238                                                  MsgInfo        *msginfo,
239                                                  const gchar    *fmt,
240                                                  const gchar    *qmark,
241                                                  const gchar    *body,
242                                                  gboolean        rewrap,
243                                                  gboolean        need_unescape,
244                                                  const gchar *err_msg);
245
246 static void compose_reply_set_entry             (Compose        *compose,
247                                                  MsgInfo        *msginfo,
248                                                  gboolean        to_all,
249                                                  gboolean        to_ml,
250                                                  gboolean        to_sender,
251                                                  gboolean
252                                                  followup_and_reply_to);
253 static void compose_reedit_set_entry            (Compose        *compose,
254                                                  MsgInfo        *msginfo);
255
256 static void compose_insert_sig                  (Compose        *compose,
257                                                  gboolean        replace);
258 static ComposeInsertResult compose_insert_file  (Compose        *compose,
259                                                  const gchar    *file);
260
261 static gboolean compose_attach_append           (Compose        *compose,
262                                                  const gchar    *file,
263                                                  const gchar    *type,
264                                                  const gchar    *content_type,
265                                                  const gchar    *charset);
266 static void compose_attach_parts                (Compose        *compose,
267                                                  MsgInfo        *msginfo);
268
269 static gboolean compose_beautify_paragraph      (Compose        *compose,
270                                                  GtkTextIter    *par_iter,
271                                                  gboolean        force);
272 static void compose_wrap_all                    (Compose        *compose);
273 static void compose_wrap_all_full               (Compose        *compose,
274                                                  gboolean        autowrap);
275
276 static void compose_set_title                   (Compose        *compose);
277 static void compose_select_account              (Compose        *compose,
278                                                  PrefsAccount   *account,
279                                                  gboolean        init);
280
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send                     (Compose        *compose); */
283 static gboolean compose_check_for_valid_recipient
284                                                 (Compose        *compose);
285 static gboolean compose_check_entries           (Compose        *compose,
286                                                  gboolean       check_everything);
287 static gint compose_write_to_file               (Compose        *compose,
288                                                  FILE           *fp,
289                                                  gint            action,
290                                                  gboolean        attach_parts);
291 static gint compose_write_body_to_file          (Compose        *compose,
292                                                  const gchar    *file);
293 static gint compose_remove_reedit_target        (Compose        *compose,
294                                                  gboolean        force);
295 static void compose_remove_draft                        (Compose        *compose);
296 static gint compose_queue_sub                   (Compose        *compose,
297                                                  gint           *msgnum,
298                                                  FolderItem     **item,
299                                                  gchar          **msgpath,
300                                                  gboolean       check_subject,
301                                                  gboolean       remove_reedit_target);
302 static int compose_add_attachments              (Compose        *compose,
303                                                  MimeInfo       *parent);
304 static gchar *compose_get_header                (Compose        *compose);
305 static gchar *compose_get_manual_headers_info   (Compose        *compose);
306
307 static void compose_convert_header              (Compose        *compose,
308                                                  gchar          *dest,
309                                                  gint            len,
310                                                  gchar          *src,
311                                                  gint            header_len,
312                                                  gboolean        addr_field);
313
314 static void compose_attach_info_free            (AttachInfo     *ainfo);
315 static void compose_attach_remove_selected      (GtkAction      *action,
316                                                  gpointer        data);
317
318 static void compose_template_apply              (Compose        *compose,
319                                                  Template       *tmpl,
320                                                  gboolean        replace);
321 static void compose_attach_property             (GtkAction      *action,
322                                                  gpointer        data);
323 static void compose_attach_property_create      (gboolean       *cancelled);
324 static void attach_property_ok                  (GtkWidget      *widget,
325                                                  gboolean       *cancelled);
326 static void attach_property_cancel              (GtkWidget      *widget,
327                                                  gboolean       *cancelled);
328 static gint attach_property_delete_event        (GtkWidget      *widget,
329                                                  GdkEventAny    *event,
330                                                  gboolean       *cancelled);
331 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
332                                                  GdkEventKey    *event,
333                                                  gboolean       *cancelled);
334
335 static void compose_exec_ext_editor             (Compose        *compose);
336 #ifdef G_OS_UNIX
337 static gint compose_exec_ext_editor_real        (const gchar    *file);
338 static gboolean compose_ext_editor_kill         (Compose        *compose);
339 static gboolean compose_input_cb                (GIOChannel     *source,
340                                                  GIOCondition    condition,
341                                                  gpointer        data);
342 static void compose_set_ext_editor_sensitive    (Compose        *compose,
343                                                  gboolean        sensitive);
344 #endif /* G_OS_UNIX */
345
346 static void compose_undo_state_changed          (UndoMain       *undostruct,
347                                                  gint            undo_state,
348                                                  gint            redo_state,
349                                                  gpointer        data);
350
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry    (Compose *compose, const gchar *header,
353                                          gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
355
356 static void compose_update_priority_menu_item(Compose * compose);
357 #if USE_ENCHANT
358 static void compose_spell_menu_changed  (void *data);
359 static void compose_dict_changed        (void *data);
360 #endif
361 static void compose_add_field_list      ( Compose *compose,
362                                           GList *listAddress );
363
364 /* callback functions */
365
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367                                          GtkAllocation *allocation,
368                                          Compose *compose);
369 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
370                                          GtkAllocation  *allocation,
371                                          GtkSHRuler     *shruler);
372 static void account_activated           (GtkComboBox *optmenu,
373                                          gpointer        data);
374 static void attach_selected             (GtkTreeView    *tree_view, 
375                                          GtkTreePath    *tree_path,
376                                          GtkTreeViewColumn *column, 
377                                          Compose *compose);
378 static gboolean attach_button_pressed   (GtkWidget      *widget,
379                                          GdkEventButton *event,
380                                          gpointer        data);
381 static gboolean attach_key_pressed      (GtkWidget      *widget,
382                                          GdkEventKey    *event,
383                                          gpointer        data);
384 static void compose_send_cb             (GtkAction      *action, gpointer data);
385 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
386
387 static void compose_save_cb             (GtkAction      *action,
388                                          gpointer        data);
389
390 static void compose_attach_cb           (GtkAction      *action,
391                                          gpointer        data);
392 static void compose_insert_file_cb      (GtkAction      *action,
393                                          gpointer        data);
394 static void compose_insert_sig_cb       (GtkAction      *action,
395                                          gpointer        data);
396
397 static void compose_close_cb            (GtkAction      *action,
398                                          gpointer        data);
399 static void compose_print_cb            (GtkAction      *action,
400                                          gpointer        data);
401
402 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
403
404 static void compose_address_cb          (GtkAction      *action,
405                                          gpointer        data);
406 static void about_show_cb               (GtkAction      *action,
407                                          gpointer        data);
408 static void compose_template_activate_cb(GtkWidget      *widget,
409                                          gpointer        data);
410
411 static void compose_ext_editor_cb       (GtkAction      *action,
412                                          gpointer        data);
413
414 static gint compose_delete_cb           (GtkWidget      *widget,
415                                          GdkEventAny    *event,
416                                          gpointer        data);
417
418 static void compose_undo_cb             (GtkAction      *action,
419                                          gpointer        data);
420 static void compose_redo_cb             (GtkAction      *action,
421                                          gpointer        data);
422 static void compose_cut_cb              (GtkAction      *action,
423                                          gpointer        data);
424 static void compose_copy_cb             (GtkAction      *action,
425                                          gpointer        data);
426 static void compose_paste_cb            (GtkAction      *action,
427                                          gpointer        data);
428 static void compose_paste_as_quote_cb   (GtkAction      *action,
429                                          gpointer        data);
430 static void compose_paste_no_wrap_cb    (GtkAction      *action,
431                                          gpointer        data);
432 static void compose_paste_wrap_cb       (GtkAction      *action,
433                                          gpointer        data);
434 static void compose_allsel_cb           (GtkAction      *action,
435                                          gpointer        data);
436
437 static void compose_advanced_action_cb  (GtkAction      *action,
438                                          gpointer        data);
439
440 static void compose_grab_focus_cb       (GtkWidget      *widget,
441                                          Compose        *compose);
442
443 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
444                                          Compose        *compose);
445
446 static void compose_wrap_cb             (GtkAction      *action,
447                                          gpointer        data);
448 static void compose_wrap_all_cb         (GtkAction      *action,
449                                          gpointer        data);
450 static void compose_find_cb             (GtkAction      *action,
451                                          gpointer        data);
452 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
453                                          gpointer        data);
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
455                                          gpointer        data);
456
457 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
458                                          gpointer        data);
459 static void compose_toggle_sign_cb      (GtkToggleAction *action,
460                                          gpointer        data);
461 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
462                                          gpointer        data);
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system     (Compose *compose, 
466                                          PrefsAccount *account,
467                                          gboolean warn);
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
471                                          gpointer        data);
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
473                                          gpointer        data);
474 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
477
478 static void compose_attach_drag_received_cb (GtkWidget          *widget,
479                                              GdkDragContext     *drag_context,
480                                              gint                x,
481                                              gint                y,
482                                              GtkSelectionData   *data,
483                                              guint               info,
484                                              guint               time,
485                                              gpointer            user_data);
486 static void compose_insert_drag_received_cb (GtkWidget          *widget,
487                                              GdkDragContext     *drag_context,
488                                              gint                x,
489                                              gint                y,
490                                              GtkSelectionData   *data,
491                                              guint               info,
492                                              guint               time,
493                                              gpointer            user_data);
494 static void compose_header_drag_received_cb (GtkWidget          *widget,
495                                              GdkDragContext     *drag_context,
496                                              gint                x,
497                                              gint                y,
498                                              GtkSelectionData   *data,
499                                              guint               info,
500                                              guint               time,
501                                              gpointer            user_data);
502
503 static gboolean compose_drag_drop           (GtkWidget *widget,
504                                              GdkDragContext *drag_context,
505                                              gint x, gint y,
506                                              guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
508                                         (GtkWidget    *widget,
509                                          GdkEventKey  *event,
510                                          Compose      *user_data);
511
512 static void text_inserted               (GtkTextBuffer  *buffer,
513                                          GtkTextIter    *iter,
514                                          const gchar    *text,
515                                          gint            len,
516                                          Compose        *compose);
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518                                   ComposeQuoteMode quote_mode,
519                                   gboolean to_all,
520                                   gboolean to_ml,
521                                   gboolean to_sender,
522                                   gboolean followup_and_reply_to,
523                                   const gchar *body);
524
525 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
526                                             ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
528                                             GdkEventKey        *event,
529                                             ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531                                         ComposeHeaderEntry *headerentry);
532
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
534
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
536
537 static void compose_nothing_cb             (GtkAction *action, gpointer data)
538 {
539
540 }
541
542 #if USE_ENCHANT
543 static void compose_check_all              (GtkAction *action, gpointer data);
544 static void compose_highlight_all          (GtkAction *action, gpointer data);
545 static void compose_check_backwards        (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
547 #endif
548
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
550
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
552
553 #ifdef USE_ENCHANT
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555                                                 FolderItem *folder_item);
556 #endif
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559                                      gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
561
562 static GtkActionEntry compose_popup_entries[] =
563 {
564         {"Compose",                     NULL, "Compose" },
565         {"Compose/Add",                 NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566         {"Compose/Remove",                      NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567         {"Compose/---",                 NULL, "---", NULL, NULL, NULL },
568         {"Compose/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
569 };
570
571 static GtkActionEntry compose_entries[] =
572 {
573         {"Menu",                                NULL, "Menu" },
574 /* menus */
575         {"Message",                     NULL, N_("_Message") },
576         {"Edit",                        NULL, N_("_Edit") },
577 #if USE_ENCHANT
578         {"Spelling",                    NULL, N_("_Spelling") },
579 #endif
580         {"Options",                     NULL, N_("_Options") },
581         {"Tools",                       NULL, N_("_Tools") },
582         {"Help",                        NULL, N_("_Help") },
583 /* Message menu */
584         {"Message/Send",                NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585         {"Message/SendLater",           NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586         {"Message/---",                 NULL, "---" },
587
588         {"Message/AttachFile",          NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589         {"Message/InsertFile",          NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590         {"Message/InsertSig",           NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591         /* {"Message/---",              NULL, "---" }, */
592         {"Message/Save",                NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593         /* {"Message/---",              NULL, "---" }, */
594         {"Message/Print",               NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595         /* {"Message/---",              NULL, "---" }, */
596         {"Message/Close",               NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
597
598 /* Edit menu */
599         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601         {"Edit/---",                    NULL, "---" },
602
603         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
606
607         {"Edit/SpecialPaste",           NULL, N_("_Special paste") },
608         {"Edit/SpecialPaste/AsQuotation",       NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609         {"Edit/SpecialPaste/Wrapped",   NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610         {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
611
612         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
613
614         {"Edit/Advanced",               NULL, N_("A_dvanced") },
615         {"Edit/Advanced/BackChar",      NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
616         {"Edit/Advanced/ForwChar",      NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
617         {"Edit/Advanced/BackWord",      NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618         {"Edit/Advanced/ForwWord",      NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619         {"Edit/Advanced/BegLine",       NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
620         {"Edit/Advanced/EndLine",       NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
621         {"Edit/Advanced/PrevLine",      NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
622         {"Edit/Advanced/NextLine",      NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
623         {"Edit/Advanced/DelBackChar",   NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
624         {"Edit/Advanced/DelForwChar",   NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
625         {"Edit/Advanced/DelBackWord",   NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626         {"Edit/Advanced/DelForwWord",   NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627         {"Edit/Advanced/DelLine",       NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628         {"Edit/Advanced/DelEndLine",    NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
629
630         /* {"Edit/---",                 NULL, "---" }, */
631         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
632
633         /* {"Edit/---",                 NULL, "---" }, */
634         {"Edit/WrapPara",               NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635         {"Edit/WrapAllLines",           NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636         /* {"Edit/---",                 NULL, "---" }, */
637         {"Edit/ExtEditor",              NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
638 #if USE_ENCHANT
639 /* Spelling menu */
640         {"Spelling/CheckAllSel",        NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641         {"Spelling/HighlightAll",       NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642         {"Spelling/CheckBackwards",     NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643         {"Spelling/ForwardNext",        NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
644
645         {"Spelling/---",                NULL, "---" },
646         {"Spelling/Options",            NULL, N_("_Options") },
647 #endif
648
649 /* Options menu */
650
651         {"Options/ReplyMode",           NULL, N_("Reply _mode") },
652         {"Options/---",                 NULL, "---" },
653         {"Options/PrivacySystem",       NULL, N_("Privacy _System") },
654         {"Options/PrivacySystem/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655
656         /* {"Options/---",              NULL, "---" }, */
657
658         {"Options/Priority",            NULL, N_("_Priority") },
659
660         {"Options/Encoding",            NULL, N_("Character _encoding") },
661         {"Options/Encoding/---",        NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663         { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
664
665         {"Options/Encoding/Western",    NULL, N_("Western European") },
666         {"Options/Encoding/Baltic",     NULL, N_("Baltic") },
667         {"Options/Encoding/Hebrew",     NULL, N_("Hebrew") },
668         {"Options/Encoding/Arabic",     NULL, N_("Arabic") },
669         {"Options/Encoding/Cyrillic",   NULL, N_("Cyrillic") },
670         {"Options/Encoding/Japanese",   NULL, N_("Japanese") },
671         {"Options/Encoding/Chinese",    NULL, N_("Chinese") },
672         {"Options/Encoding/Korean",     NULL, N_("Korean") },
673         {"Options/Encoding/Thai",       NULL, N_("Thai") },
674
675 /* Tools menu */
676         {"Tools/AddressBook",           NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
677
678         {"Tools/Template",      NULL, N_("_Template") },
679         {"Tools/Template/PlaceHolder",  NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680         {"Tools/Actions",       NULL, N_("Actio_ns") },
681         {"Tools/Actions/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
682
683 /* Help menu */
684         {"Help/About",          NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
685 };
686
687 static GtkToggleActionEntry compose_toggle_entries[] =
688 {
689         {"Edit/AutoWrap",               NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690         {"Edit/AutoIndent",             NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691         {"Options/Sign",                NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692         {"Options/Encrypt",             NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693         {"Options/RequestRetRcpt",      NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694         {"Options/RemoveReferences",    NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695         {"Tools/ShowRuler",             NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
696 };
697
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
699 {
700         {"Options/ReplyMode/Normal",    NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701         {"Options/ReplyMode/All",       NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702         {"Options/ReplyMode/Sender",    NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703         {"Options/ReplyMode/List",      NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
704 };
705
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
707 {
708         {"Options/Priority/Highest",    NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709         {"Options/Priority/High",       NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710         {"Options/Priority/Normal",     NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711         {"Options/Priority/Low",        NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712         {"Options/Priority/Lowest",     NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
713 };
714
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
716 {
717         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
749 };
750
751 static GtkTargetEntry compose_mime_types[] =
752 {
753         {"text/uri-list", 0, 0},
754         {"UTF8_STRING", 0, 0},
755         {"text/plain", 0, 0}
756 };
757
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
759 {
760         GList *compose_list = compose_get_compose_list();
761         GList *elem = NULL;
762         
763         if (compose_list) {
764                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
765                      elem = elem->next) {
766                         Compose *c = (Compose*)elem->data;
767
768                         if (!c->targetinfo || !c->targetinfo->msgid ||
769                             !info->msgid)
770                                 continue;
771
772                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773                                 gtkut_window_popup(c->window);
774                                 return TRUE;
775                         }
776                 }
777         }
778         return FALSE;
779 }
780
781 static GdkColor quote_color1 = 
782         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 = 
784         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 = 
786         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787
788 static GdkColor quote_bgcolor1 = 
789         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 = 
791         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 = 
793         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
794
795 static GdkColor signature_color = {
796         (gulong)0,
797         (gushort)0x7fff,
798         (gushort)0x7fff,
799         (gushort)0x7fff
800 };
801
802 static GdkColor uri_color = {
803         (gulong)0,
804         (gushort)0,
805         (gushort)0,
806         (gushort)0
807 };
808
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
810 {
811         GtkTextBuffer *buffer;
812         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
814         GdkColormap *cmap;
815         gboolean success[8];
816         int i;
817         GdkColor color[8];
818 #endif
819
820         buffer = gtk_text_view_get_buffer(text);
821
822         if (prefs_common.enable_color) {
823                 /* grab the quote colors, converting from an int to a GdkColor */
824                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
825                                                &quote_color1);
826                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
827                                                &quote_color2);
828                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
829                                                &quote_color3);
830                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
831                                                &quote_bgcolor1);
832                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
833                                                &quote_bgcolor2);
834                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
835                                                &quote_bgcolor3);
836                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
837                                                &signature_color);
838                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
839                                                &uri_color);
840         } else {
841                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
842                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
843         }
844
845         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847                                            "foreground-gdk", &quote_color1,
848                                            "paragraph-background-gdk", &quote_bgcolor1,
849                                            NULL);
850                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851                                            "foreground-gdk", &quote_color2,
852                                            "paragraph-background-gdk", &quote_bgcolor2,
853                                            NULL);
854                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855                                            "foreground-gdk", &quote_color3,
856                                            "paragraph-background-gdk", &quote_bgcolor3,
857                                            NULL);
858         } else {
859                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860                                            "foreground-gdk", &quote_color1,
861                                            NULL);
862                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863                                            "foreground-gdk", &quote_color2,
864                                            NULL);
865                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866                                            "foreground-gdk", &quote_color3,
867                                            NULL);
868         }
869         
870         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871                                    "foreground-gdk", &signature_color,
872                                    NULL);
873         
874         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875                                         "foreground-gdk", &uri_color,
876                                          NULL);
877         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
879
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881         color[0] = quote_color1;
882         color[1] = quote_color2;
883         color[2] = quote_color3;
884         color[3] = quote_bgcolor1;
885         color[4] = quote_bgcolor2;
886         color[5] = quote_bgcolor3;
887         color[6] = signature_color;
888         color[7] = uri_color;
889
890         cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
892
893         for (i = 0; i < 8; i++) {
894                 if (success[i] == FALSE) {
895                         g_warning("Compose: color allocation failed.\n");
896                         quote_color1 = quote_color2 = quote_color3 = 
897                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
898                                 signature_color = uri_color = black;
899                 }
900         }
901 #endif
902 }
903
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
905                      GList *attach_files)
906 {
907         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
908 }
909
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
911 {
912         return compose_generic_new(account, mailto, item, NULL, NULL);
913 }
914
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
916 {
917         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
918 }
919
920 #define SCROLL_TO_CURSOR(compose) {                             \
921         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
922                 gtk_text_view_get_buffer(                       \
923                         GTK_TEXT_VIEW(compose->text)));         \
924         gtk_text_view_scroll_mark_onscreen(                     \
925                 GTK_TEXT_VIEW(compose->text),                   \
926                 cmark);                                         \
927 }
928
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
930 {
931         GtkEditable *entry;
932         if (folderidentifier) {
933                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
934                 prefs_common.compose_save_to_history = add_history(
935                                 prefs_common.compose_save_to_history, folderidentifier);
936                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937                                 prefs_common.compose_save_to_history);
938         }
939
940         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941         if (folderidentifier)
942                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
943         else
944                 gtk_entry_set_text(GTK_ENTRY(entry), "");
945 }
946
947 static gchar *compose_get_save_to(Compose *compose)
948 {
949         GtkEditable *entry;
950         gchar *result = NULL;
951         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
952         result = gtk_editable_get_chars(entry, 0, -1);
953         
954         if (result) {
955                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
956                 prefs_common.compose_save_to_history = add_history(
957                                 prefs_common.compose_save_to_history, result);
958                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
959                                 prefs_common.compose_save_to_history);
960         }
961         return result;
962 }
963
964 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
965                              GList *attach_files, GList *listAddress )
966 {
967         Compose *compose;
968         GtkTextView *textview;
969         GtkTextBuffer *textbuf;
970         GtkTextIter iter;
971         const gchar *subject_format = NULL;
972         const gchar *body_format = NULL;
973         gchar *mailto_from = NULL;
974         PrefsAccount *mailto_account = NULL;
975         MsgInfo* dummyinfo = NULL;
976         gint cursor_pos = -1;
977         MailField mfield = NO_FIELD_PRESENT;
978         gchar* buf;
979         GtkTextMark *mark;
980
981         /* check if mailto defines a from */
982         if (mailto && *mailto != '\0') {
983                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
984                 /* mailto defines a from, check if we can get account prefs from it,
985                    if not, the account prefs will be guessed using other ways, but we'll keep
986                    the from anyway */
987                 if (mailto_from)
988                         mailto_account = account_find_from_address(mailto_from, TRUE);
989                 if (mailto_account)
990                         account = mailto_account;
991         }
992
993         /* if no account prefs set from mailto, set if from folder prefs (if any) */
994         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
995                 account = account_find_from_id(item->prefs->default_account);
996
997         /* if no account prefs set, fallback to the current one */
998         if (!account) account = cur_account;
999         cm_return_val_if_fail(account != NULL, NULL);
1000
1001         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1002
1003         /* override from name if mailto asked for it */
1004         if (mailto_from) {
1005                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1006                 g_free(mailto_from);
1007         } else
1008                 /* override from name according to folder properties */
1009                 if (item && item->prefs &&
1010                         item->prefs->compose_with_format &&
1011                         item->prefs->compose_override_from_format &&
1012                         *item->prefs->compose_override_from_format != '\0') {
1013
1014                         gchar *tmp = NULL;
1015                         gchar *buf = NULL;
1016
1017                         dummyinfo = compose_msginfo_new_from_compose(compose);
1018
1019                         /* decode \-escape sequences in the internal representation of the quote format */
1020                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1021                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1022
1023 #ifdef USE_ENCHANT
1024                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1025                                         compose->gtkaspell);
1026 #else
1027                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1028 #endif
1029                         quote_fmt_scan_string(tmp);
1030                         quote_fmt_parse();
1031
1032                         buf = quote_fmt_get_buffer();
1033                         if (buf == NULL)
1034                                 alertpanel_error(_("New message From format error."));
1035                         else
1036                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1037                         quote_fmt_reset_vartable();
1038
1039                         g_free(tmp);
1040                 }
1041
1042         compose->replyinfo = NULL;
1043         compose->fwdinfo   = NULL;
1044
1045         textview = GTK_TEXT_VIEW(compose->text);
1046         textbuf = gtk_text_view_get_buffer(textview);
1047         compose_create_tags(textview, compose);
1048
1049         undo_block(compose->undostruct);
1050 #ifdef USE_ENCHANT
1051         compose_set_dictionaries_from_folder_prefs(compose, item);
1052 #endif
1053
1054         if (account->auto_sig)
1055                 compose_insert_sig(compose, FALSE);
1056         gtk_text_buffer_get_start_iter(textbuf, &iter);
1057         gtk_text_buffer_place_cursor(textbuf, &iter);
1058
1059         if (account->protocol != A_NNTP) {
1060                 if (mailto && *mailto != '\0') {
1061                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1062
1063                 } else {
1064                         compose_set_folder_prefs(compose, item, TRUE);
1065                 }
1066                 if (item && item->ret_rcpt) {
1067                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1068                 }
1069         } else {
1070                 if (mailto && *mailto != '\0') {
1071                         if (!strchr(mailto, '@'))
1072                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1073                         else
1074                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1075                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1076                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1077                         mfield = TO_FIELD_PRESENT;
1078                 }
1079                 /*
1080                  * CLAWS: just don't allow return receipt request, even if the user
1081                  * may want to send an email. simple but foolproof.
1082                  */
1083                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1084         }
1085         compose_add_field_list( compose, listAddress );
1086
1087         if (item && item->prefs && item->prefs->compose_with_format) {
1088                 subject_format = item->prefs->compose_subject_format;
1089                 body_format = item->prefs->compose_body_format;
1090         } else if (account->compose_with_format) {
1091                 subject_format = account->compose_subject_format;
1092                 body_format = account->compose_body_format;
1093         } else if (prefs_common.compose_with_format) {
1094                 subject_format = prefs_common.compose_subject_format;
1095                 body_format = prefs_common.compose_body_format;
1096         }
1097
1098         if (subject_format || body_format) {
1099
1100                 if ( subject_format
1101                          && *subject_format != '\0' )
1102                 {
1103                         gchar *subject = NULL;
1104                         gchar *tmp = NULL;
1105                         gchar *buf = NULL;
1106
1107                         if (!dummyinfo)
1108                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1109
1110                         /* decode \-escape sequences in the internal representation of the quote format */
1111                         tmp = g_malloc(strlen(subject_format)+1);
1112                         pref_get_unescaped_pref(tmp, subject_format);
1113
1114                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1115 #ifdef USE_ENCHANT
1116                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1117                                         compose->gtkaspell);
1118 #else
1119                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1120 #endif
1121                         quote_fmt_scan_string(tmp);
1122                         quote_fmt_parse();
1123
1124                         buf = quote_fmt_get_buffer();
1125                         if (buf == NULL)
1126                                 alertpanel_error(_("New message subject format error."));
1127                         else
1128                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1129                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1130                         quote_fmt_reset_vartable();
1131
1132                         g_free(subject);
1133                         g_free(tmp);
1134                         mfield = SUBJECT_FIELD_PRESENT;
1135                 }
1136
1137                 if ( body_format
1138                          && *body_format != '\0' )
1139                 {
1140                         GtkTextView *text;
1141                         GtkTextBuffer *buffer;
1142                         GtkTextIter start, end;
1143                         gchar *tmp = NULL;
1144
1145                         if (!dummyinfo)
1146                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1147
1148                         text = GTK_TEXT_VIEW(compose->text);
1149                         buffer = gtk_text_view_get_buffer(text);
1150                         gtk_text_buffer_get_start_iter(buffer, &start);
1151                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1152                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1153
1154                         compose_quote_fmt(compose, dummyinfo,
1155                                           body_format,
1156                                           NULL, tmp, FALSE, TRUE,
1157                                                   _("The body of the \"New message\" template has an error at line %d."));
1158                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1159                         quote_fmt_reset_vartable();
1160
1161                         g_free(tmp);
1162 #ifdef USE_ENCHANT
1163                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1164                                 gtkaspell_highlight_all(compose->gtkaspell);
1165 #endif
1166                         mfield = BODY_FIELD_PRESENT;
1167                 }
1168
1169         }
1170         procmsg_msginfo_free( dummyinfo );
1171
1172         if (attach_files) {
1173                 GList *curr;
1174                 AttachInfo *ainfo;
1175
1176                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1177                         ainfo = (AttachInfo *) curr->data;
1178                         compose_attach_append(compose, ainfo->file, ainfo->name,
1179                                         ainfo->content_type, ainfo->charset);
1180                 }
1181         }
1182
1183         compose_show_first_last_header(compose, TRUE);
1184
1185         /* Set save folder */
1186         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1187                 gchar *folderidentifier;
1188
1189                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1190                 folderidentifier = folder_item_get_identifier(item);
1191                 compose_set_save_to(compose, folderidentifier);
1192                 g_free(folderidentifier);
1193         }
1194
1195         /* Place cursor according to provided input (mfield) */
1196         switch (mfield) { 
1197                 case NO_FIELD_PRESENT:
1198                         if (compose->header_last)
1199                                 gtk_widget_grab_focus(compose->header_last->entry);
1200                         break;
1201                 case TO_FIELD_PRESENT:
1202                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1203                         if (buf) {
1204                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1205                                 g_free(buf);
1206                         }
1207                         gtk_widget_grab_focus(compose->subject_entry);
1208                         break;
1209                 case SUBJECT_FIELD_PRESENT:
1210                         textview = GTK_TEXT_VIEW(compose->text);
1211                         if (!textview)
1212                                 break;
1213                         textbuf = gtk_text_view_get_buffer(textview);
1214                         if (!textbuf)
1215                                 break;
1216                         mark = gtk_text_buffer_get_insert(textbuf);
1217                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1218                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1219                     /* 
1220                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1221                      * only defers where it comes to the variable body
1222                      * is not null. If no body is present compose->text
1223                      * will be null in which case you cannot place the
1224                      * cursor inside the component so. An empty component
1225                      * is therefore created before placing the cursor
1226                      */
1227                 case BODY_FIELD_PRESENT:
1228                         cursor_pos = quote_fmt_get_cursor_pos();
1229                         if (cursor_pos == -1)
1230                                 gtk_widget_grab_focus(compose->header_last->entry);
1231                         else
1232                                 gtk_widget_grab_focus(compose->text);
1233                         break;
1234         }
1235
1236         undo_unblock(compose->undostruct);
1237
1238         if (prefs_common.auto_exteditor)
1239                 compose_exec_ext_editor(compose);
1240
1241         compose->draft_timeout_tag = -1;
1242         SCROLL_TO_CURSOR(compose);
1243
1244         compose->modified = FALSE;
1245         compose_set_title(compose);
1246
1247         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1248
1249         return compose;
1250 }
1251
1252 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1253                 gboolean override_pref, const gchar *system)
1254 {
1255         const gchar *privacy = NULL;
1256
1257         cm_return_if_fail(compose != NULL);
1258         cm_return_if_fail(account != NULL);
1259
1260         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1261                 return;
1262
1263         if (system)
1264                 privacy = system;
1265         else if (account->default_privacy_system
1266         &&  strlen(account->default_privacy_system)) {
1267                 privacy = account->default_privacy_system;
1268         } else {
1269                 GSList *privacy_avail = privacy_get_system_ids();
1270                 if (privacy_avail && g_slist_length(privacy_avail)) {
1271                         privacy = (gchar *)(privacy_avail->data);
1272                 }
1273         }
1274         if (privacy != NULL) {
1275                 if (system) {
1276                         g_free(compose->privacy_system);
1277                         compose->privacy_system = NULL;
1278                 }
1279                 if (compose->privacy_system == NULL)
1280                         compose->privacy_system = g_strdup(privacy);
1281                 else if (*(compose->privacy_system) == '\0') {
1282                         g_free(compose->privacy_system);
1283                         compose->privacy_system = g_strdup(privacy);
1284                 }
1285                 compose_update_privacy_system_menu_item(compose, FALSE);
1286                 compose_use_encryption(compose, TRUE);
1287         }
1288 }       
1289
1290 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1291 {
1292         const gchar *privacy = NULL;
1293
1294         if (system)
1295                 privacy = system;
1296         else if (account->default_privacy_system
1297         &&  strlen(account->default_privacy_system)) {
1298                 privacy = account->default_privacy_system;
1299         } else {
1300                 GSList *privacy_avail = privacy_get_system_ids();
1301                 if (privacy_avail && g_slist_length(privacy_avail)) {
1302                         privacy = (gchar *)(privacy_avail->data);
1303                 }
1304         }
1305
1306         if (privacy != NULL) {
1307                 if (system) {
1308                         g_free(compose->privacy_system);
1309                         compose->privacy_system = NULL;
1310                 }
1311                 if (compose->privacy_system == NULL)
1312                         compose->privacy_system = g_strdup(privacy);
1313                 compose_update_privacy_system_menu_item(compose, FALSE);
1314                 compose_use_signing(compose, TRUE);
1315         }
1316 }       
1317
1318 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1319 {
1320         MsgInfo *msginfo;
1321         guint list_len;
1322         Compose *compose = NULL;
1323         
1324         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1325
1326         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1327         cm_return_val_if_fail(msginfo != NULL, NULL);
1328
1329         list_len = g_slist_length(msginfo_list);
1330
1331         switch (mode) {
1332         case COMPOSE_REPLY:
1333         case COMPOSE_REPLY_TO_ADDRESS:
1334                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1335                               FALSE, prefs_common.default_reply_list, FALSE, body);
1336                 break;
1337         case COMPOSE_REPLY_WITH_QUOTE:
1338                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1339                         FALSE, prefs_common.default_reply_list, FALSE, body);
1340                 break;
1341         case COMPOSE_REPLY_WITHOUT_QUOTE:
1342                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1343                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1344                 break;
1345         case COMPOSE_REPLY_TO_SENDER:
1346                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1347                               FALSE, FALSE, TRUE, body);
1348                 break;
1349         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1350                 compose = compose_followup_and_reply_to(msginfo,
1351                                               COMPOSE_QUOTE_CHECK,
1352                                               FALSE, FALSE, body);
1353                 break;
1354         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1355                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1356                         FALSE, FALSE, TRUE, body);
1357                 break;
1358         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1359                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1360                         FALSE, FALSE, TRUE, NULL);
1361                 break;
1362         case COMPOSE_REPLY_TO_ALL:
1363                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1364                         TRUE, FALSE, FALSE, body);
1365                 break;
1366         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1367                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1368                         TRUE, FALSE, FALSE, body);
1369                 break;
1370         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1371                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1372                         TRUE, FALSE, FALSE, NULL);
1373                 break;
1374         case COMPOSE_REPLY_TO_LIST:
1375                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1376                         FALSE, TRUE, FALSE, body);
1377                 break;
1378         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1379                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1380                         FALSE, TRUE, FALSE, body);
1381                 break;
1382         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1383                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1384                         FALSE, TRUE, FALSE, NULL);
1385                 break;
1386         case COMPOSE_FORWARD:
1387                 if (prefs_common.forward_as_attachment) {
1388                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1389                         return compose;
1390                 } else {
1391                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1392                         return compose;
1393                 }
1394                 break;
1395         case COMPOSE_FORWARD_INLINE:
1396                 /* check if we reply to more than one Message */
1397                 if (list_len == 1) {
1398                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1399                         break;
1400                 } 
1401                 /* more messages FALL THROUGH */
1402         case COMPOSE_FORWARD_AS_ATTACH:
1403                 compose = compose_forward_multiple(NULL, msginfo_list);
1404                 break;
1405         case COMPOSE_REDIRECT:
1406                 compose = compose_redirect(NULL, msginfo, FALSE);
1407                 break;
1408         default:
1409                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1410         }
1411         
1412         if (compose == NULL) {
1413                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1414                 return NULL;
1415         }
1416
1417         compose->rmode = mode;
1418         switch (compose->rmode) {
1419         case COMPOSE_REPLY:
1420         case COMPOSE_REPLY_WITH_QUOTE:
1421         case COMPOSE_REPLY_WITHOUT_QUOTE:
1422         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1423                 debug_print("reply mode Normal\n");
1424                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1425                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1426                 break;
1427         case COMPOSE_REPLY_TO_SENDER:
1428         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1429         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1430                 debug_print("reply mode Sender\n");
1431                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1432                 break;
1433         case COMPOSE_REPLY_TO_ALL:
1434         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1435         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1436                 debug_print("reply mode All\n");
1437                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1438                 break;
1439         case COMPOSE_REPLY_TO_LIST:
1440         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1441         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1442                 debug_print("reply mode List\n");
1443                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1444                 break;
1445         case COMPOSE_REPLY_TO_ADDRESS:
1446                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1447                 break;
1448         default:
1449                 break;
1450         }
1451         return compose;
1452 }
1453
1454 static Compose *compose_reply(MsgInfo *msginfo,
1455                                    ComposeQuoteMode quote_mode,
1456                                    gboolean to_all,
1457                                    gboolean to_ml,
1458                                    gboolean to_sender, 
1459                                    const gchar *body)
1460 {
1461         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1462                               to_sender, FALSE, body);
1463 }
1464
1465 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1466                                    ComposeQuoteMode quote_mode,
1467                                    gboolean to_all,
1468                                    gboolean to_sender,
1469                                    const gchar *body)
1470 {
1471         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1472                               to_sender, TRUE, body);
1473 }
1474
1475 static void compose_extract_original_charset(Compose *compose)
1476 {
1477         MsgInfo *info = NULL;
1478         if (compose->replyinfo) {
1479                 info = compose->replyinfo;
1480         } else if (compose->fwdinfo) {
1481                 info = compose->fwdinfo;
1482         } else if (compose->targetinfo) {
1483                 info = compose->targetinfo;
1484         }
1485         if (info) {
1486                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1487                 MimeInfo *partinfo = mimeinfo;
1488                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1489                         partinfo = procmime_mimeinfo_next(partinfo);
1490                 if (partinfo) {
1491                         compose->orig_charset = 
1492                                 g_strdup(procmime_mimeinfo_get_parameter(
1493                                                 partinfo, "charset"));
1494                 }
1495                 procmime_mimeinfo_free_all(mimeinfo);
1496         }
1497 }
1498
1499 #define SIGNAL_BLOCK(buffer) {                                  \
1500         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1501                                 G_CALLBACK(compose_changed_cb), \
1502                                 compose);                       \
1503         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1504                                 G_CALLBACK(text_inserted),      \
1505                                 compose);                       \
1506 }
1507
1508 #define SIGNAL_UNBLOCK(buffer) {                                \
1509         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1510                                 G_CALLBACK(compose_changed_cb), \
1511                                 compose);                       \
1512         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1513                                 G_CALLBACK(text_inserted),      \
1514                                 compose);                       \
1515 }
1516
1517 static Compose *compose_generic_reply(MsgInfo *msginfo,
1518                                   ComposeQuoteMode quote_mode,
1519                                   gboolean to_all, gboolean to_ml,
1520                                   gboolean to_sender,
1521                                   gboolean followup_and_reply_to,
1522                                   const gchar *body)
1523 {
1524         Compose *compose;
1525         PrefsAccount *account = NULL;
1526         GtkTextView *textview;
1527         GtkTextBuffer *textbuf;
1528         gboolean quote = FALSE;
1529         const gchar *qmark = NULL;
1530         const gchar *body_fmt = NULL;
1531         gchar *s_system = NULL;
1532         START_TIMING("");
1533         cm_return_val_if_fail(msginfo != NULL, NULL);
1534         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1535
1536         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1537
1538         cm_return_val_if_fail(account != NULL, NULL);
1539
1540         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1541
1542         compose->updating = TRUE;
1543
1544         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1545         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1546
1547         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1548         if (!compose->replyinfo)
1549                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1550
1551         compose_extract_original_charset(compose);
1552         
1553         if (msginfo->folder && msginfo->folder->ret_rcpt)
1554                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1555
1556         /* Set save folder */
1557         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1558                 gchar *folderidentifier;
1559
1560                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1561                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1562                 compose_set_save_to(compose, folderidentifier);
1563                 g_free(folderidentifier);
1564         }
1565
1566         if (compose_parse_header(compose, msginfo) < 0) {
1567                 compose->updating = FALSE;
1568                 compose_destroy(compose);
1569                 return NULL;
1570         }
1571
1572         /* override from name according to folder properties */
1573         if (msginfo->folder && msginfo->folder->prefs &&
1574                 msginfo->folder->prefs->reply_with_format &&
1575                 msginfo->folder->prefs->reply_override_from_format &&
1576                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1577
1578                 gchar *tmp = NULL;
1579                 gchar *buf = NULL;
1580
1581                 /* decode \-escape sequences in the internal representation of the quote format */
1582                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1583                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1584
1585 #ifdef USE_ENCHANT
1586                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1587                                 compose->gtkaspell);
1588 #else
1589                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1590 #endif
1591                 quote_fmt_scan_string(tmp);
1592                 quote_fmt_parse();
1593
1594                 buf = quote_fmt_get_buffer();
1595                 if (buf == NULL)
1596                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1597                 else
1598                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1599                 quote_fmt_reset_vartable();
1600
1601                 g_free(tmp);
1602         }
1603
1604         textview = (GTK_TEXT_VIEW(compose->text));
1605         textbuf = gtk_text_view_get_buffer(textview);
1606         compose_create_tags(textview, compose);
1607
1608         undo_block(compose->undostruct);
1609 #ifdef USE_ENCHANT
1610         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1611         gtkaspell_block_check(compose->gtkaspell);
1612 #endif
1613
1614         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1615                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1616                 /* use the reply format of folder (if enabled), or the account's one
1617                    (if enabled) or fallback to the global reply format, which is always
1618                    enabled (even if empty), and use the relevant quotemark */
1619                 quote = TRUE;
1620                 if (msginfo->folder && msginfo->folder->prefs &&
1621                                 msginfo->folder->prefs->reply_with_format) {
1622                         qmark = msginfo->folder->prefs->reply_quotemark;
1623                         body_fmt = msginfo->folder->prefs->reply_body_format;
1624
1625                 } else if (account->reply_with_format) {
1626                         qmark = account->reply_quotemark;
1627                         body_fmt = account->reply_body_format;
1628
1629                 } else {
1630                         qmark = prefs_common.quotemark;
1631                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1632                                 body_fmt = gettext(prefs_common.quotefmt);
1633                         else
1634                                 body_fmt = "";
1635                 }
1636         }
1637
1638         if (quote) {
1639                 /* empty quotemark is not allowed */
1640                 if (qmark == NULL || *qmark == '\0')
1641                         qmark = "> ";
1642                 compose_quote_fmt(compose, compose->replyinfo,
1643                                   body_fmt, qmark, body, FALSE, TRUE,
1644                                           _("The body of the \"Reply\" template has an error at line %d."));
1645                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1646                 quote_fmt_reset_vartable();
1647         }
1648
1649         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1650                 compose_force_encryption(compose, account, FALSE, s_system);
1651         }
1652
1653         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1654         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1655                 compose_force_signing(compose, account, s_system);
1656         }
1657         g_free(s_system);
1658
1659         SIGNAL_BLOCK(textbuf);
1660         
1661         if (account->auto_sig)
1662                 compose_insert_sig(compose, FALSE);
1663
1664         compose_wrap_all(compose);
1665
1666 #ifdef USE_ENCHANT
1667         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1668                 gtkaspell_highlight_all(compose->gtkaspell);
1669         gtkaspell_unblock_check(compose->gtkaspell);
1670 #endif
1671         SIGNAL_UNBLOCK(textbuf);
1672         
1673         gtk_widget_grab_focus(compose->text);
1674
1675         undo_unblock(compose->undostruct);
1676
1677         if (prefs_common.auto_exteditor)
1678                 compose_exec_ext_editor(compose);
1679                 
1680         compose->modified = FALSE;
1681         compose_set_title(compose);
1682
1683         compose->updating = FALSE;
1684         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1685         SCROLL_TO_CURSOR(compose);
1686         
1687         if (compose->deferred_destroy) {
1688                 compose_destroy(compose);
1689                 return NULL;
1690         }
1691         END_TIMING();
1692
1693         return compose;
1694 }
1695
1696 #define INSERT_FW_HEADER(var, hdr) \
1697 if (msginfo->var && *msginfo->var) { \
1698         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1699         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1700         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1701 }
1702
1703 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1704                          gboolean as_attach, const gchar *body,
1705                          gboolean no_extedit,
1706                          gboolean batch)
1707 {
1708         Compose *compose;
1709         GtkTextView *textview;
1710         GtkTextBuffer *textbuf;
1711         gint cursor_pos = -1;
1712         ComposeMode mode;
1713
1714         cm_return_val_if_fail(msginfo != NULL, NULL);
1715         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1716
1717         if (!account && 
1718             !(account = compose_guess_forward_account_from_msginfo
1719                                 (msginfo)))
1720                 account = cur_account;
1721
1722         if (!prefs_common.forward_as_attachment)
1723                 mode = COMPOSE_FORWARD_INLINE;
1724         else
1725                 mode = COMPOSE_FORWARD;
1726         compose = compose_create(account, msginfo->folder, mode, batch);
1727
1728         compose->updating = TRUE;
1729         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1730         if (!compose->fwdinfo)
1731                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1732
1733         compose_extract_original_charset(compose);
1734
1735         if (msginfo->subject && *msginfo->subject) {
1736                 gchar *buf, *buf2, *p;
1737
1738                 buf = p = g_strdup(msginfo->subject);
1739                 p += subject_get_prefix_length(p);
1740                 memmove(buf, p, strlen(p) + 1);
1741
1742                 buf2 = g_strdup_printf("Fw: %s", buf);
1743                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1744                 
1745                 g_free(buf);
1746                 g_free(buf2);
1747         }
1748
1749         /* override from name according to folder properties */
1750         if (msginfo->folder && msginfo->folder->prefs &&
1751                 msginfo->folder->prefs->forward_with_format &&
1752                 msginfo->folder->prefs->forward_override_from_format &&
1753                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1754
1755                 gchar *tmp = NULL;
1756                 gchar *buf = NULL;
1757                 MsgInfo *full_msginfo = NULL;
1758
1759                 if (!as_attach)
1760                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1761                 if (!full_msginfo)
1762                         full_msginfo = procmsg_msginfo_copy(msginfo);
1763
1764                 /* decode \-escape sequences in the internal representation of the quote format */
1765                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1766                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1767
1768 #ifdef USE_ENCHANT
1769                 gtkaspell_block_check(compose->gtkaspell);
1770                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1771                                 compose->gtkaspell);
1772 #else
1773                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1774 #endif
1775                 quote_fmt_scan_string(tmp);
1776                 quote_fmt_parse();
1777
1778                 buf = quote_fmt_get_buffer();
1779                 if (buf == NULL)
1780                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1781                 else
1782                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1783                 quote_fmt_reset_vartable();
1784
1785                 g_free(tmp);
1786                 procmsg_msginfo_free(full_msginfo);
1787         }
1788
1789         textview = GTK_TEXT_VIEW(compose->text);
1790         textbuf = gtk_text_view_get_buffer(textview);
1791         compose_create_tags(textview, compose);
1792         
1793         undo_block(compose->undostruct);
1794         if (as_attach) {
1795                 gchar *msgfile;
1796
1797                 msgfile = procmsg_get_message_file(msginfo);
1798                 if (!is_file_exist(msgfile))
1799                         g_warning("%s: file not exist\n", msgfile);
1800                 else
1801                         compose_attach_append(compose, msgfile, msgfile,
1802                                               "message/rfc822", NULL);
1803
1804                 g_free(msgfile);
1805         } else {
1806                 const gchar *qmark = NULL;
1807                 const gchar *body_fmt = NULL;
1808                 MsgInfo *full_msginfo;
1809
1810                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1811                 if (!full_msginfo)
1812                         full_msginfo = procmsg_msginfo_copy(msginfo);
1813
1814                 /* use the forward format of folder (if enabled), or the account's one
1815                    (if enabled) or fallback to the global forward format, which is always
1816                    enabled (even if empty), and use the relevant quotemark */
1817                 if (msginfo->folder && msginfo->folder->prefs &&
1818                                 msginfo->folder->prefs->forward_with_format) {
1819                         qmark = msginfo->folder->prefs->forward_quotemark;
1820                         body_fmt = msginfo->folder->prefs->forward_body_format;
1821
1822                 } else if (account->forward_with_format) {
1823                         qmark = account->forward_quotemark;
1824                         body_fmt = account->forward_body_format;
1825
1826                 } else {
1827                         qmark = prefs_common.fw_quotemark;
1828                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1829                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1830                         else
1831                                 body_fmt = "";
1832                 }
1833
1834                 /* empty quotemark is not allowed */
1835                 if (qmark == NULL || *qmark == '\0')
1836                         qmark = "> ";
1837
1838                 compose_quote_fmt(compose, full_msginfo,
1839                                   body_fmt, qmark, body, FALSE, TRUE,
1840                                           _("The body of the \"Forward\" template has an error at line %d."));
1841                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1842                 quote_fmt_reset_vartable();
1843                 compose_attach_parts(compose, msginfo);
1844
1845                 procmsg_msginfo_free(full_msginfo);
1846         }
1847
1848         SIGNAL_BLOCK(textbuf);
1849
1850         if (account->auto_sig)
1851                 compose_insert_sig(compose, FALSE);
1852
1853         compose_wrap_all(compose);
1854
1855 #ifdef USE_ENCHANT
1856         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1857                 gtkaspell_highlight_all(compose->gtkaspell);
1858         gtkaspell_unblock_check(compose->gtkaspell);
1859 #endif
1860         SIGNAL_UNBLOCK(textbuf);
1861         
1862         cursor_pos = quote_fmt_get_cursor_pos();
1863         if (cursor_pos == -1)
1864                 gtk_widget_grab_focus(compose->header_last->entry);
1865         else
1866                 gtk_widget_grab_focus(compose->text);
1867
1868         if (!no_extedit && prefs_common.auto_exteditor)
1869                 compose_exec_ext_editor(compose);
1870         
1871         /*save folder*/
1872         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1873                 gchar *folderidentifier;
1874
1875                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1876                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1877                 compose_set_save_to(compose, folderidentifier);
1878                 g_free(folderidentifier);
1879         }
1880
1881         undo_unblock(compose->undostruct);
1882         
1883         compose->modified = FALSE;
1884         compose_set_title(compose);
1885
1886         compose->updating = FALSE;
1887         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1888         SCROLL_TO_CURSOR(compose);
1889
1890         if (compose->deferred_destroy) {
1891                 compose_destroy(compose);
1892                 return NULL;
1893         }
1894
1895         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1896
1897         return compose;
1898 }
1899
1900 #undef INSERT_FW_HEADER
1901
1902 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1903 {
1904         Compose *compose;
1905         GtkTextView *textview;
1906         GtkTextBuffer *textbuf;
1907         GtkTextIter iter;
1908         GSList *msginfo;
1909         gchar *msgfile;
1910         gboolean single_mail = TRUE;
1911         
1912         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1913
1914         if (g_slist_length(msginfo_list) > 1)
1915                 single_mail = FALSE;
1916
1917         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1918                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1919                         return NULL;
1920
1921         /* guess account from first selected message */
1922         if (!account && 
1923             !(account = compose_guess_forward_account_from_msginfo
1924                                 (msginfo_list->data)))
1925                 account = cur_account;
1926
1927         cm_return_val_if_fail(account != NULL, NULL);
1928
1929         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1930                 if (msginfo->data) {
1931                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1932                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1933                 }
1934         }
1935
1936         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1937                 g_warning("no msginfo_list");
1938                 return NULL;
1939         }
1940
1941         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1942
1943         compose->updating = TRUE;
1944
1945         /* override from name according to folder properties */
1946         if (msginfo_list->data) {
1947                 MsgInfo *msginfo = msginfo_list->data;
1948
1949                 if (msginfo->folder && msginfo->folder->prefs &&
1950                         msginfo->folder->prefs->forward_with_format &&
1951                         msginfo->folder->prefs->forward_override_from_format &&
1952                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1953
1954                         gchar *tmp = NULL;
1955                         gchar *buf = NULL;
1956
1957                         /* decode \-escape sequences in the internal representation of the quote format */
1958                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1959                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1960
1961 #ifdef USE_ENCHANT
1962                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1963                                         compose->gtkaspell);
1964 #else
1965                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1966 #endif
1967                         quote_fmt_scan_string(tmp);
1968                         quote_fmt_parse();
1969
1970                         buf = quote_fmt_get_buffer();
1971                         if (buf == NULL)
1972                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1973                         else
1974                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1975                         quote_fmt_reset_vartable();
1976
1977                         g_free(tmp);
1978                 }
1979         }
1980
1981         textview = GTK_TEXT_VIEW(compose->text);
1982         textbuf = gtk_text_view_get_buffer(textview);
1983         compose_create_tags(textview, compose);
1984         
1985         undo_block(compose->undostruct);
1986         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1987                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1988
1989                 if (!is_file_exist(msgfile))
1990                         g_warning("%s: file not exist\n", msgfile);
1991                 else
1992                         compose_attach_append(compose, msgfile, msgfile,
1993                                 "message/rfc822", NULL);
1994                 g_free(msgfile);
1995         }
1996         
1997         if (single_mail) {
1998                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1999                 if (info->subject && *info->subject) {
2000                         gchar *buf, *buf2, *p;
2001
2002                         buf = p = g_strdup(info->subject);
2003                         p += subject_get_prefix_length(p);
2004                         memmove(buf, p, strlen(p) + 1);
2005
2006                         buf2 = g_strdup_printf("Fw: %s", buf);
2007                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2008
2009                         g_free(buf);
2010                         g_free(buf2);
2011                 }
2012         } else {
2013                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2014                         _("Fw: multiple emails"));
2015         }
2016
2017         SIGNAL_BLOCK(textbuf);
2018         
2019         if (account->auto_sig)
2020                 compose_insert_sig(compose, FALSE);
2021
2022         compose_wrap_all(compose);
2023
2024         SIGNAL_UNBLOCK(textbuf);
2025         
2026         gtk_text_buffer_get_start_iter(textbuf, &iter);
2027         gtk_text_buffer_place_cursor(textbuf, &iter);
2028
2029         gtk_widget_grab_focus(compose->header_last->entry);
2030         undo_unblock(compose->undostruct);
2031         compose->modified = FALSE;
2032         compose_set_title(compose);
2033
2034         compose->updating = FALSE;
2035         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2036         SCROLL_TO_CURSOR(compose);
2037
2038         if (compose->deferred_destroy) {
2039                 compose_destroy(compose);
2040                 return NULL;
2041         }
2042
2043         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2044
2045         return compose;
2046 }
2047
2048 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2049 {
2050         GtkTextIter start = *iter;
2051         GtkTextIter end_iter;
2052         int start_pos = gtk_text_iter_get_offset(&start);
2053         gchar *str = NULL;
2054         if (!compose->account->sig_sep)
2055                 return FALSE;
2056         
2057         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2058                 start_pos+strlen(compose->account->sig_sep));
2059
2060         /* check sig separator */
2061         str = gtk_text_iter_get_text(&start, &end_iter);
2062         if (!strcmp(str, compose->account->sig_sep)) {
2063                 gchar *tmp = NULL;
2064                 /* check end of line (\n) */
2065                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2066                         start_pos+strlen(compose->account->sig_sep));
2067                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2068                         start_pos+strlen(compose->account->sig_sep)+1);
2069                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2070                 if (!strcmp(tmp,"\n")) {
2071                         g_free(str);
2072                         g_free(tmp);
2073                         return TRUE;
2074                 }
2075                 g_free(tmp);    
2076         }
2077         g_free(str);
2078
2079         return FALSE;
2080 }
2081
2082 static void compose_colorize_signature(Compose *compose)
2083 {
2084         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2085         GtkTextIter iter;
2086         GtkTextIter end_iter;
2087         gtk_text_buffer_get_start_iter(buffer, &iter);
2088         while (gtk_text_iter_forward_line(&iter))
2089                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2090                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2091                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2092                 }
2093 }
2094
2095 #define BLOCK_WRAP() {                                                  \
2096         prev_autowrap = compose->autowrap;                              \
2097         buffer = gtk_text_view_get_buffer(                              \
2098                                         GTK_TEXT_VIEW(compose->text));  \
2099         compose->autowrap = FALSE;                                      \
2100                                                                         \
2101         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2102                                 G_CALLBACK(compose_changed_cb),         \
2103                                 compose);                               \
2104         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2105                                 G_CALLBACK(text_inserted),              \
2106                                 compose);                               \
2107 }
2108 #define UNBLOCK_WRAP() {                                                \
2109         compose->autowrap = prev_autowrap;                              \
2110         if (compose->autowrap) {                                        \
2111                 gint old = compose->draft_timeout_tag;                  \
2112                 compose->draft_timeout_tag = -2;                        \
2113                 compose_wrap_all(compose);                              \
2114                 compose->draft_timeout_tag = old;                       \
2115         }                                                               \
2116                                                                         \
2117         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2118                                 G_CALLBACK(compose_changed_cb),         \
2119                                 compose);                               \
2120         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2121                                 G_CALLBACK(text_inserted),              \
2122                                 compose);                               \
2123 }
2124
2125 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2126 {
2127         Compose *compose = NULL;
2128         PrefsAccount *account = NULL;
2129         GtkTextView *textview;
2130         GtkTextBuffer *textbuf;
2131         GtkTextMark *mark;
2132         GtkTextIter iter;
2133         FILE *fp;
2134         gchar buf[BUFFSIZE];
2135         gboolean use_signing = FALSE;
2136         gboolean use_encryption = FALSE;
2137         gchar *privacy_system = NULL;
2138         int priority = PRIORITY_NORMAL;
2139         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2140         gboolean autowrap = prefs_common.autowrap;
2141         gboolean autoindent = prefs_common.auto_indent;
2142         HeaderEntry *manual_headers = NULL;
2143
2144         cm_return_val_if_fail(msginfo != NULL, NULL);
2145         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2146
2147         if (compose_put_existing_to_front(msginfo)) {
2148                 return NULL;
2149         }
2150
2151         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2152             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2153             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2154                 gchar queueheader_buf[BUFFSIZE];
2155                 gint id, param;
2156
2157                 /* Select Account from queue headers */
2158                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2159                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2160                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2161                         account = account_find_from_id(id);
2162                 }
2163                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2164                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2165                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2166                         account = account_find_from_id(id);
2167                 }
2168                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2169                                              sizeof(queueheader_buf), "NAID:")) {
2170                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2171                         account = account_find_from_id(id);
2172                 }
2173                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2174                                                     sizeof(queueheader_buf), "MAID:")) {
2175                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2176                         account = account_find_from_id(id);
2177                 }
2178                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2179                                                                 sizeof(queueheader_buf), "S:")) {
2180                         account = account_find_from_address(queueheader_buf, FALSE);
2181                 }
2182                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2183                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2184                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2185                         use_signing = param;
2186                         
2187                 }
2188                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2189                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2190                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2191                         use_signing = param;
2192                         
2193                 }
2194                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2195                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2196                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2197                         use_encryption = param;
2198                 }
2199                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2200                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2201                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2202                         use_encryption = param;
2203                 }
2204                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2205                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2206                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2207                         autowrap = param;
2208                 }
2209                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2210                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2211                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2212                         autoindent = param;
2213                 }
2214                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2215                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2216                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2217                 }
2218                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2219                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2220                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2221                 }
2222                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2223                                              sizeof(queueheader_buf), "X-Priority: ")) {
2224                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2225                         priority = param;
2226                 }
2227                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2228                                              sizeof(queueheader_buf), "RMID:")) {
2229                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2230                         if (tokens[0] && tokens[1] && tokens[2]) {
2231                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2232                                 if (orig_item != NULL) {
2233                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2234                                 }
2235                         }
2236                         g_strfreev(tokens);
2237                 }
2238                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2239                                              sizeof(queueheader_buf), "FMID:")) {
2240                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2241                         if (tokens[0] && tokens[1] && tokens[2]) {
2242                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2243                                 if (orig_item != NULL) {
2244                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2245                                 }
2246                         }
2247                         g_strfreev(tokens);
2248                 }
2249                 /* Get manual headers */
2250                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2251                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2252                         if (*listmh != '\0') {
2253                                 debug_print("Got manual headers: %s\n", listmh);
2254                                 manual_headers = procheader_entries_from_str(listmh);
2255                         }
2256                         g_free(listmh);
2257                 }
2258         } else {
2259                 account = msginfo->folder->folder->account;
2260         }
2261
2262         if (!account && prefs_common.reedit_account_autosel) {
2263                 gchar from[BUFFSIZE];
2264                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2265                         extract_address(from);
2266                         account = account_find_from_address(from, FALSE);
2267                 }
2268         }
2269         if (!account) {
2270                 account = cur_account;
2271         }
2272         cm_return_val_if_fail(account != NULL, NULL);
2273
2274         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2275
2276         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2277         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2278         compose->autowrap = autowrap;
2279         compose->replyinfo = replyinfo;
2280         compose->fwdinfo = fwdinfo;
2281
2282         compose->updating = TRUE;
2283         compose->priority = priority;
2284
2285         if (privacy_system != NULL) {
2286                 compose->privacy_system = privacy_system;
2287                 compose_use_signing(compose, use_signing);
2288                 compose_use_encryption(compose, use_encryption);
2289                 compose_update_privacy_system_menu_item(compose, FALSE);
2290         } else {
2291                 activate_privacy_system(compose, account, FALSE);
2292         }
2293
2294         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2295
2296         compose_extract_original_charset(compose);
2297
2298         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2299             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2300             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2301                 gchar queueheader_buf[BUFFSIZE];
2302
2303                 /* Set message save folder */
2304                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2305                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2306                         compose_set_save_to(compose, &queueheader_buf[4]);
2307                 }
2308                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2309                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2310                         if (active) {
2311                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2312                         }
2313                 }
2314         }
2315         
2316         if (compose_parse_header(compose, msginfo) < 0) {
2317                 compose->updating = FALSE;
2318                 compose_destroy(compose);
2319                 return NULL;
2320         }
2321         compose_reedit_set_entry(compose, msginfo);
2322
2323         textview = GTK_TEXT_VIEW(compose->text);
2324         textbuf = gtk_text_view_get_buffer(textview);
2325         compose_create_tags(textview, compose);
2326
2327         mark = gtk_text_buffer_get_insert(textbuf);
2328         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2329
2330         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2331                                         G_CALLBACK(compose_changed_cb),
2332                                         compose);
2333         
2334         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2335                 fp = procmime_get_first_encrypted_text_content(msginfo);
2336                 if (fp) {
2337                         compose_force_encryption(compose, account, TRUE, NULL);
2338                 }
2339         } else {
2340                 fp = procmime_get_first_text_content(msginfo);
2341         }
2342         if (fp == NULL) {
2343                 g_warning("Can't get text part\n");
2344         }
2345
2346         if (fp != NULL) {
2347                 gboolean prev_autowrap;
2348                 GtkTextBuffer *buffer;
2349                 BLOCK_WRAP();
2350                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2351                         strcrchomp(buf);
2352                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2353                 }
2354                 UNBLOCK_WRAP();
2355                 fclose(fp);
2356         }
2357         
2358         compose_attach_parts(compose, msginfo);
2359
2360         compose_colorize_signature(compose);
2361
2362         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2363                                         G_CALLBACK(compose_changed_cb),
2364                                         compose);
2365
2366         if (manual_headers != NULL) {
2367                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2368                         procheader_entries_free(manual_headers);
2369                         compose->updating = FALSE;
2370                         compose_destroy(compose);
2371                         return NULL;
2372                 }
2373                 procheader_entries_free(manual_headers);
2374         }
2375
2376         gtk_widget_grab_focus(compose->text);
2377
2378         if (prefs_common.auto_exteditor) {
2379                 compose_exec_ext_editor(compose);
2380         }
2381         compose->modified = FALSE;
2382         compose_set_title(compose);
2383
2384         compose->updating = FALSE;
2385         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2386         SCROLL_TO_CURSOR(compose);
2387
2388         if (compose->deferred_destroy) {
2389                 compose_destroy(compose);
2390                 return NULL;
2391         }
2392         
2393         compose->sig_str = account_get_signature_str(compose->account);
2394         
2395         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2396
2397         return compose;
2398 }
2399
2400 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2401                                                  gboolean batch)
2402 {
2403         Compose *compose;
2404         gchar *filename;
2405         FolderItem *item;
2406
2407         cm_return_val_if_fail(msginfo != NULL, NULL);
2408
2409         if (!account)
2410                 account = account_get_reply_account(msginfo,
2411                                         prefs_common.reply_account_autosel);
2412         cm_return_val_if_fail(account != NULL, NULL);
2413
2414         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2415
2416         compose->updating = TRUE;
2417
2418         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2419         compose->replyinfo = NULL;
2420         compose->fwdinfo = NULL;
2421
2422         compose_show_first_last_header(compose, TRUE);
2423
2424         gtk_widget_grab_focus(compose->header_last->entry);
2425
2426         filename = procmsg_get_message_file(msginfo);
2427
2428         if (filename == NULL) {
2429                 compose->updating = FALSE;
2430                 compose_destroy(compose);
2431
2432                 return NULL;
2433         }
2434
2435         compose->redirect_filename = filename;
2436         
2437         /* Set save folder */
2438         item = msginfo->folder;
2439         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2440                 gchar *folderidentifier;
2441
2442                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2443                 folderidentifier = folder_item_get_identifier(item);
2444                 compose_set_save_to(compose, folderidentifier);
2445                 g_free(folderidentifier);
2446         }
2447
2448         compose_attach_parts(compose, msginfo);
2449
2450         if (msginfo->subject)
2451                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2452                                    msginfo->subject);
2453         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2454
2455         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2456                                           _("The body of the \"Redirect\" template has an error at line %d."));
2457         quote_fmt_reset_vartable();
2458         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2459
2460         compose_colorize_signature(compose);
2461
2462         
2463         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2464         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2465         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2466
2467         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2468         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2469         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2470         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2471         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2472         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2473         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2474         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2475         
2476         if (compose->toolbar->draft_btn)
2477                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2478         if (compose->toolbar->insert_btn)
2479                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2480         if (compose->toolbar->attach_btn)
2481                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2482         if (compose->toolbar->sig_btn)
2483                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2484         if (compose->toolbar->exteditor_btn)
2485                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2486         if (compose->toolbar->linewrap_current_btn)
2487                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2488         if (compose->toolbar->linewrap_all_btn)
2489                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2490
2491         compose->modified = FALSE;
2492         compose_set_title(compose);
2493         compose->updating = FALSE;
2494         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2495         SCROLL_TO_CURSOR(compose);
2496
2497         if (compose->deferred_destroy) {
2498                 compose_destroy(compose);
2499                 return NULL;
2500         }
2501         
2502         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2503
2504         return compose;
2505 }
2506
2507 GList *compose_get_compose_list(void)
2508 {
2509         return compose_list;
2510 }
2511
2512 void compose_entry_append(Compose *compose, const gchar *address,
2513                           ComposeEntryType type, ComposePrefType pref_type)
2514 {
2515         const gchar *header;
2516         gchar *cur, *begin;
2517         gboolean in_quote = FALSE;
2518         if (!address || *address == '\0') return;
2519
2520         switch (type) {
2521         case COMPOSE_CC:
2522                 header = N_("Cc:");
2523                 break;
2524         case COMPOSE_BCC:
2525                 header = N_("Bcc:");
2526                 break;
2527         case COMPOSE_REPLYTO:
2528                 header = N_("Reply-To:");
2529                 break;
2530         case COMPOSE_NEWSGROUPS:
2531                 header = N_("Newsgroups:");
2532                 break;
2533         case COMPOSE_FOLLOWUPTO:
2534                 header = N_( "Followup-To:");
2535                 break;
2536         case COMPOSE_INREPLYTO:
2537                 header = N_( "In-Reply-To:");
2538                 break;
2539         case COMPOSE_TO:
2540         default:
2541                 header = N_("To:");
2542                 break;
2543         }
2544         header = prefs_common_translated_header_name(header);
2545         
2546         cur = begin = (gchar *)address;
2547         
2548         /* we separate the line by commas, but not if we're inside a quoted
2549          * string */
2550         while (*cur != '\0') {
2551                 if (*cur == '"') 
2552                         in_quote = !in_quote;
2553                 if (*cur == ',' && !in_quote) {
2554                         gchar *tmp = g_strdup(begin);
2555                         gchar *o_tmp = tmp;
2556                         tmp[cur-begin]='\0';
2557                         cur++;
2558                         begin = cur;
2559                         while (*tmp == ' ' || *tmp == '\t')
2560                                 tmp++;
2561                         compose_add_header_entry(compose, header, tmp, pref_type);
2562                         g_free(o_tmp);
2563                         continue;
2564                 }
2565                 cur++;
2566         }
2567         if (begin < cur) {
2568                 gchar *tmp = g_strdup(begin);
2569                 gchar *o_tmp = tmp;
2570                 tmp[cur-begin]='\0';
2571                 while (*tmp == ' ' || *tmp == '\t')
2572                         tmp++;
2573                 compose_add_header_entry(compose, header, tmp, pref_type);
2574                 g_free(o_tmp);          
2575         }
2576 }
2577
2578 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2579 {
2580 #if !GTK_CHECK_VERSION(3, 0, 0)
2581         static GdkColor yellow;
2582         static GdkColor black;
2583         static gboolean yellow_initialised = FALSE;
2584 #else
2585         static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2586         static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2587 #endif
2588         GSList *h_list;
2589         GtkEntry *entry;
2590                 
2591 #if !GTK_CHECK_VERSION(3, 0, 0)
2592         if (!yellow_initialised) {
2593                 gdk_color_parse("#f5f6be", &yellow);
2594                 gdk_color_parse("#000000", &black);
2595                 yellow_initialised = gdk_colormap_alloc_color(
2596                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2597                 yellow_initialised &= gdk_colormap_alloc_color(
2598                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2599         }
2600 #endif
2601
2602         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2603                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2604                 if (gtk_entry_get_text(entry) && 
2605                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2606 #if !GTK_CHECK_VERSION(3, 0, 0)
2607                         if (yellow_initialised) {
2608 #endif
2609                                 gtk_widget_modify_base(
2610                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2611                                         GTK_STATE_NORMAL, &yellow);
2612                                 gtk_widget_modify_text(
2613                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2614                                         GTK_STATE_NORMAL, &black);
2615 #if !GTK_CHECK_VERSION(3, 0, 0)
2616                         }
2617 #endif
2618                 }
2619         }
2620 }
2621
2622 void compose_toolbar_cb(gint action, gpointer data)
2623 {
2624         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2625         Compose *compose = (Compose*)toolbar_item->parent;
2626         
2627         cm_return_if_fail(compose != NULL);
2628
2629         switch(action) {
2630         case A_SEND:
2631                 compose_send_cb(NULL, compose);
2632                 break;
2633         case A_SENDL:
2634                 compose_send_later_cb(NULL, compose);
2635                 break;
2636         case A_DRAFT:
2637                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2638                 break;
2639         case A_INSERT:
2640                 compose_insert_file_cb(NULL, compose);
2641                 break;
2642         case A_ATTACH:
2643                 compose_attach_cb(NULL, compose);
2644                 break;
2645         case A_SIG:
2646                 compose_insert_sig(compose, FALSE);
2647                 break;
2648         case A_EXTEDITOR:
2649                 compose_ext_editor_cb(NULL, compose);
2650                 break;
2651         case A_LINEWRAP_CURRENT:
2652                 compose_beautify_paragraph(compose, NULL, TRUE);
2653                 break;
2654         case A_LINEWRAP_ALL:
2655                 compose_wrap_all_full(compose, TRUE);
2656                 break;
2657         case A_ADDRBOOK:
2658                 compose_address_cb(NULL, compose);
2659                 break;
2660 #ifdef USE_ENCHANT
2661         case A_CHECK_SPELLING:
2662                 compose_check_all(NULL, compose);
2663                 break;
2664 #endif
2665         default:
2666                 break;
2667         }
2668 }
2669
2670 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2671 {
2672         gchar *to = NULL;
2673         gchar *cc = NULL;
2674         gchar *bcc = NULL;
2675         gchar *subject = NULL;
2676         gchar *body = NULL;
2677         gchar *temp = NULL;
2678         gsize  len = 0;
2679         gchar **attach = NULL;
2680         gchar *inreplyto = NULL;
2681         MailField mfield = NO_FIELD_PRESENT;
2682
2683         /* get mailto parts but skip from */
2684         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2685
2686         if (to) {
2687                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2688                 mfield = TO_FIELD_PRESENT;
2689         }
2690         if (cc)
2691                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2692         if (bcc)
2693                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2694         if (subject) {
2695                 if (!g_utf8_validate (subject, -1, NULL)) {
2696                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2697                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2698                         g_free(temp);
2699                 } else {
2700                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2701                 }
2702                 mfield = SUBJECT_FIELD_PRESENT;
2703         }
2704         if (body) {
2705                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2706                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2707                 GtkTextMark *mark;
2708                 GtkTextIter iter;
2709                 gboolean prev_autowrap = compose->autowrap;
2710
2711                 compose->autowrap = FALSE;
2712
2713                 mark = gtk_text_buffer_get_insert(buffer);
2714                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2715
2716                 if (!g_utf8_validate (body, -1, NULL)) {
2717                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2718                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2719                         g_free(temp);
2720                 } else {
2721                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2722                 }
2723                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2724
2725                 compose->autowrap = prev_autowrap;
2726                 if (compose->autowrap)
2727                         compose_wrap_all(compose);
2728                 mfield = BODY_FIELD_PRESENT;
2729         }
2730
2731         if (attach) {
2732                 gint i = 0, att = 0;
2733                 gchar *warn_files = NULL;
2734                 while (attach[i] != NULL) {
2735                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2736                         if (utf8_filename) {
2737                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2738                                         gchar *tmp = g_strdup_printf("%s%s\n",
2739                                                         warn_files?warn_files:"",
2740                                                         utf8_filename);
2741                                         g_free(warn_files);
2742                                         warn_files = tmp;
2743                                         att++;
2744                                 }
2745                                 g_free(utf8_filename);
2746                         } else {
2747                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2748                         }
2749                         i++;
2750                 }
2751                 if (warn_files) {
2752                         alertpanel_notice(ngettext(
2753                         "The following file has been attached: \n%s",
2754                         "The following files have been attached: \n%s", att), warn_files);
2755                         g_free(warn_files);
2756                 }
2757         }
2758         if (inreplyto)
2759                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2760
2761         g_free(to);
2762         g_free(cc);
2763         g_free(bcc);
2764         g_free(subject);
2765         g_free(body);
2766         g_strfreev(attach);
2767         g_free(inreplyto);
2768         
2769         return mfield;
2770 }
2771
2772 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2773 {
2774         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2775                                        {"Cc:",          NULL, TRUE},
2776                                        {"References:",  NULL, FALSE},
2777                                        {"Bcc:",         NULL, TRUE},
2778                                        {"Newsgroups:",  NULL, TRUE},
2779                                        {"Followup-To:", NULL, TRUE},
2780                                        {"List-Post:",   NULL, FALSE},
2781                                        {"X-Priority:",  NULL, FALSE},
2782                                        {NULL,           NULL, FALSE}};
2783
2784         enum
2785         {
2786                 H_REPLY_TO      = 0,
2787                 H_CC            = 1,
2788                 H_REFERENCES    = 2,
2789                 H_BCC           = 3,
2790                 H_NEWSGROUPS    = 4,
2791                 H_FOLLOWUP_TO   = 5,
2792                 H_LIST_POST     = 6,
2793                 H_X_PRIORITY    = 7
2794         };
2795
2796         FILE *fp;
2797
2798         cm_return_val_if_fail(msginfo != NULL, -1);
2799
2800         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2801         procheader_get_header_fields(fp, hentry);
2802         fclose(fp);
2803
2804         if (hentry[H_REPLY_TO].body != NULL) {
2805                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2806                         compose->replyto =
2807                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2808                                                    NULL, TRUE);
2809                 }
2810                 g_free(hentry[H_REPLY_TO].body);
2811                 hentry[H_REPLY_TO].body = NULL;
2812         }
2813         if (hentry[H_CC].body != NULL) {
2814                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2815                 g_free(hentry[H_CC].body);
2816                 hentry[H_CC].body = NULL;
2817         }
2818         if (hentry[H_REFERENCES].body != NULL) {
2819                 if (compose->mode == COMPOSE_REEDIT)
2820                         compose->references = hentry[H_REFERENCES].body;
2821                 else {
2822                         compose->references = compose_parse_references
2823                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2824                         g_free(hentry[H_REFERENCES].body);
2825                 }
2826                 hentry[H_REFERENCES].body = NULL;
2827         }
2828         if (hentry[H_BCC].body != NULL) {
2829                 if (compose->mode == COMPOSE_REEDIT)
2830                         compose->bcc =
2831                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2832                 g_free(hentry[H_BCC].body);
2833                 hentry[H_BCC].body = NULL;
2834         }
2835         if (hentry[H_NEWSGROUPS].body != NULL) {
2836                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2837                 hentry[H_NEWSGROUPS].body = NULL;
2838         }
2839         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2840                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2841                         compose->followup_to =
2842                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2843                                                    NULL, TRUE);
2844                 }
2845                 g_free(hentry[H_FOLLOWUP_TO].body);
2846                 hentry[H_FOLLOWUP_TO].body = NULL;
2847         }
2848         if (hentry[H_LIST_POST].body != NULL) {
2849                 gchar *to = NULL, *start = NULL;
2850
2851                 extract_address(hentry[H_LIST_POST].body);
2852                 if (hentry[H_LIST_POST].body[0] != '\0') {
2853                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2854                         
2855                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2856                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2857
2858                         if (to) {
2859                                 g_free(compose->ml_post);
2860                                 compose->ml_post = to;
2861                         }
2862                 }
2863                 g_free(hentry[H_LIST_POST].body);
2864                 hentry[H_LIST_POST].body = NULL;
2865         }
2866
2867         /* CLAWS - X-Priority */
2868         if (compose->mode == COMPOSE_REEDIT)
2869                 if (hentry[H_X_PRIORITY].body != NULL) {
2870                         gint priority;
2871                         
2872                         priority = atoi(hentry[H_X_PRIORITY].body);
2873                         g_free(hentry[H_X_PRIORITY].body);
2874                         
2875                         hentry[H_X_PRIORITY].body = NULL;
2876                         
2877                         if (priority < PRIORITY_HIGHEST || 
2878                             priority > PRIORITY_LOWEST)
2879                                 priority = PRIORITY_NORMAL;
2880                         
2881                         compose->priority =  priority;
2882                 }
2883  
2884         if (compose->mode == COMPOSE_REEDIT) {
2885                 if (msginfo->inreplyto && *msginfo->inreplyto)
2886                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2887                 return 0;
2888         }
2889
2890         if (msginfo->msgid && *msginfo->msgid)
2891                 compose->inreplyto = g_strdup(msginfo->msgid);
2892
2893         if (!compose->references) {
2894                 if (msginfo->msgid && *msginfo->msgid) {
2895                         if (msginfo->inreplyto && *msginfo->inreplyto)
2896                                 compose->references =
2897                                         g_strdup_printf("<%s>\n\t<%s>",
2898                                                         msginfo->inreplyto,
2899                                                         msginfo->msgid);
2900                         else
2901                                 compose->references =
2902                                         g_strconcat("<", msginfo->msgid, ">",
2903                                                     NULL);
2904                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2905                         compose->references =
2906                                 g_strconcat("<", msginfo->inreplyto, ">",
2907                                             NULL);
2908                 }
2909         }
2910
2911         return 0;
2912 }
2913
2914 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2915 {
2916         FILE *fp;
2917         HeaderEntry *he;
2918
2919         cm_return_val_if_fail(msginfo != NULL, -1);
2920
2921         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2922         procheader_get_header_fields(fp, entries);
2923         fclose(fp);
2924
2925         he = entries;
2926         while (he != NULL && he->name != NULL) {
2927                 GtkTreeIter iter;
2928                 GtkListStore *model = NULL;
2929
2930                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2931                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2932                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2933                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2934                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2935                 ++he;
2936         }
2937
2938         return 0;
2939 }
2940
2941 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2942 {
2943         GSList *ref_id_list, *cur;
2944         GString *new_ref;
2945         gchar *new_ref_str;
2946
2947         ref_id_list = references_list_append(NULL, ref);
2948         if (!ref_id_list) return NULL;
2949         if (msgid && *msgid)
2950                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2951
2952         for (;;) {
2953                 gint len = 0;
2954
2955                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2956                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2957                         len += strlen((gchar *)cur->data) + 5;
2958
2959                 if (len > MAX_REFERENCES_LEN) {
2960                         /* remove second message-ID */
2961                         if (ref_id_list && ref_id_list->next &&
2962                             ref_id_list->next->next) {
2963                                 g_free(ref_id_list->next->data);
2964                                 ref_id_list = g_slist_remove
2965                                         (ref_id_list, ref_id_list->next->data);
2966                         } else {
2967                                 slist_free_strings_full(ref_id_list);
2968                                 return NULL;
2969                         }
2970                 } else
2971                         break;
2972         }
2973
2974         new_ref = g_string_new("");
2975         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2976                 if (new_ref->len > 0)
2977                         g_string_append(new_ref, "\n\t");
2978                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2979         }
2980
2981         slist_free_strings_full(ref_id_list);
2982
2983         new_ref_str = new_ref->str;
2984         g_string_free(new_ref, FALSE);
2985
2986         return new_ref_str;
2987 }
2988
2989 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2990                                 const gchar *fmt, const gchar *qmark,
2991                                 const gchar *body, gboolean rewrap,
2992                                 gboolean need_unescape,
2993                                 const gchar *err_msg)
2994 {
2995         MsgInfo* dummyinfo = NULL;
2996         gchar *quote_str = NULL;
2997         gchar *buf;
2998         gboolean prev_autowrap;
2999         const gchar *trimmed_body = body;
3000         gint cursor_pos = -1;
3001         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3002         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3003         GtkTextIter iter;
3004         GtkTextMark *mark;
3005         
3006
3007         SIGNAL_BLOCK(buffer);
3008
3009         if (!msginfo) {
3010                 dummyinfo = compose_msginfo_new_from_compose(compose);
3011                 msginfo = dummyinfo;
3012         }
3013
3014         if (qmark != NULL) {
3015 #ifdef USE_ENCHANT
3016                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3017                                 compose->gtkaspell);
3018 #else
3019                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3020 #endif
3021                 quote_fmt_scan_string(qmark);
3022                 quote_fmt_parse();
3023
3024                 buf = quote_fmt_get_buffer();
3025                 if (buf == NULL)
3026                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3027                 else
3028                         Xstrdup_a(quote_str, buf, goto error)
3029         }
3030
3031         if (fmt && *fmt != '\0') {
3032
3033                 if (trimmed_body)
3034                         while (*trimmed_body == '\n')
3035                                 trimmed_body++;
3036
3037 #ifdef USE_ENCHANT
3038                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3039                                 compose->gtkaspell);
3040 #else
3041                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3042 #endif
3043                 if (need_unescape) {
3044                         gchar *tmp = NULL;
3045
3046                         /* decode \-escape sequences in the internal representation of the quote format */
3047                         tmp = g_malloc(strlen(fmt)+1);
3048                         pref_get_unescaped_pref(tmp, fmt);
3049                         quote_fmt_scan_string(tmp);
3050                         quote_fmt_parse();
3051                         g_free(tmp);
3052                 } else {
3053                         quote_fmt_scan_string(fmt);
3054                         quote_fmt_parse();
3055                 }
3056
3057                 buf = quote_fmt_get_buffer();
3058                 if (buf == NULL) {
3059                         gint line = quote_fmt_get_line();
3060                         alertpanel_error(err_msg, line);
3061                         goto error;
3062                 }
3063         } else
3064                 buf = "";
3065
3066         prev_autowrap = compose->autowrap;
3067         compose->autowrap = FALSE;
3068
3069         mark = gtk_text_buffer_get_insert(buffer);
3070         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3071         if (g_utf8_validate(buf, -1, NULL)) { 
3072                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3073         } else {
3074                 gchar *tmpout = NULL;
3075                 tmpout = conv_codeset_strdup
3076                         (buf, conv_get_locale_charset_str_no_utf8(),
3077                          CS_INTERNAL);
3078                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3079                         g_free(tmpout);
3080                         tmpout = g_malloc(strlen(buf)*2+1);
3081                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3082                 }
3083                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3084                 g_free(tmpout);
3085         }
3086
3087         cursor_pos = quote_fmt_get_cursor_pos();
3088         if (cursor_pos == -1)
3089                 cursor_pos = gtk_text_iter_get_offset(&iter);
3090         compose->set_cursor_pos = cursor_pos;
3091
3092         gtk_text_buffer_get_start_iter(buffer, &iter);
3093         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3094         gtk_text_buffer_place_cursor(buffer, &iter);
3095
3096         compose->autowrap = prev_autowrap;
3097         if (compose->autowrap && rewrap)
3098                 compose_wrap_all(compose);
3099
3100         goto ok;
3101
3102 error:
3103         buf = NULL;
3104 ok:
3105         SIGNAL_UNBLOCK(buffer);
3106
3107         procmsg_msginfo_free( dummyinfo );
3108
3109         return buf;
3110 }
3111
3112 /* if ml_post is of type addr@host and from is of type
3113  * addr-anything@host, return TRUE
3114  */
3115 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3116 {
3117         gchar *left_ml = NULL;
3118         gchar *right_ml = NULL;
3119         gchar *left_from = NULL;
3120         gchar *right_from = NULL;
3121         gboolean result = FALSE;
3122         
3123         if (!ml_post || !from)
3124                 return FALSE;
3125         
3126         left_ml = g_strdup(ml_post);
3127         if (strstr(left_ml, "@")) {
3128                 right_ml = strstr(left_ml, "@")+1;
3129                 *(strstr(left_ml, "@")) = '\0';
3130         }
3131         
3132         left_from = g_strdup(from);
3133         if (strstr(left_from, "@")) {
3134                 right_from = strstr(left_from, "@")+1;
3135                 *(strstr(left_from, "@")) = '\0';
3136         }
3137         
3138         if (left_ml && left_from && right_ml && right_from
3139         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3140         &&  !strcmp(right_from, right_ml)) {
3141                 result = TRUE;
3142         }
3143         g_free(left_ml);
3144         g_free(left_from);
3145         
3146         return result;
3147 }
3148
3149 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3150                                      gboolean respect_default_to)
3151 {
3152         if (!compose)
3153                 return;
3154         if (!folder || !folder->prefs)
3155                 return;
3156
3157         if (respect_default_to && folder->prefs->enable_default_to) {
3158                 compose_entry_append(compose, folder->prefs->default_to,
3159                                         COMPOSE_TO, PREF_FOLDER);
3160                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3161         }
3162         if (folder->prefs->enable_default_cc)
3163                 compose_entry_append(compose, folder->prefs->default_cc,
3164                                         COMPOSE_CC, PREF_FOLDER);
3165         if (folder->prefs->enable_default_bcc)
3166                 compose_entry_append(compose, folder->prefs->default_bcc,
3167                                         COMPOSE_BCC, PREF_FOLDER);
3168         if (folder->prefs->enable_default_replyto)
3169                 compose_entry_append(compose, folder->prefs->default_replyto,
3170                                         COMPOSE_REPLYTO, PREF_FOLDER);
3171 }
3172
3173 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3174 {
3175         gchar *buf, *buf2;
3176         gchar *p;
3177         
3178         if (!compose || !msginfo)
3179                 return;
3180
3181         if (msginfo->subject && *msginfo->subject) {
3182                 buf = p = g_strdup(msginfo->subject);
3183                 p += subject_get_prefix_length(p);
3184                 memmove(buf, p, strlen(p) + 1);
3185
3186                 buf2 = g_strdup_printf("Re: %s", buf);
3187                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3188
3189                 g_free(buf2);
3190                 g_free(buf);
3191         } else
3192                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3193 }
3194
3195 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3196                                     gboolean to_all, gboolean to_ml,
3197                                     gboolean to_sender,
3198                                     gboolean followup_and_reply_to)
3199 {
3200         GSList *cc_list = NULL;
3201         GSList *cur;
3202         gchar *from = NULL;
3203         gchar *replyto = NULL;
3204         gchar *ac_email = NULL;
3205
3206         gboolean reply_to_ml = FALSE;
3207         gboolean default_reply_to = FALSE;
3208
3209         cm_return_if_fail(compose->account != NULL);
3210         cm_return_if_fail(msginfo != NULL);
3211
3212         reply_to_ml = to_ml && compose->ml_post;
3213
3214         default_reply_to = msginfo->folder && 
3215                 msginfo->folder->prefs->enable_default_reply_to;
3216
3217         if (compose->account->protocol != A_NNTP) {
3218                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3219
3220                 if (reply_to_ml && !default_reply_to) {
3221                         
3222                         gboolean is_subscr = is_subscription(compose->ml_post,
3223                                                              msginfo->from);
3224                         if (!is_subscr) {
3225                                 /* normal answer to ml post with a reply-to */
3226                                 compose_entry_append(compose,
3227                                            compose->ml_post,
3228                                            COMPOSE_TO, PREF_ML);
3229                                 if (compose->replyto)
3230                                         compose_entry_append(compose,
3231                                                 compose->replyto,
3232                                                 COMPOSE_CC, PREF_ML);
3233                         } else {
3234                                 /* answer to subscription confirmation */
3235                                 if (compose->replyto)
3236                                         compose_entry_append(compose,
3237                                                 compose->replyto,
3238                                                 COMPOSE_TO, PREF_ML);
3239                                 else if (msginfo->from)
3240                                         compose_entry_append(compose,
3241                                                 msginfo->from,
3242                                                 COMPOSE_TO, PREF_ML);
3243                         }
3244                 }
3245                 else if (!(to_all || to_sender) && default_reply_to) {
3246                         compose_entry_append(compose,
3247                             msginfo->folder->prefs->default_reply_to,
3248                             COMPOSE_TO, PREF_FOLDER);
3249                         compose_entry_mark_default_to(compose,
3250                                 msginfo->folder->prefs->default_reply_to);
3251                 } else {
3252                         gchar *tmp1 = NULL;
3253                         if (!msginfo->from)
3254                                 return;
3255                         Xstrdup_a(tmp1, msginfo->from, return);
3256                         extract_address(tmp1);
3257                         if (to_all || to_sender ||
3258                             !account_find_from_address(tmp1, FALSE))
3259                                 compose_entry_append(compose,
3260                                  (compose->replyto && !to_sender)
3261                                           ? compose->replyto :
3262                                           msginfo->from ? msginfo->from : "",
3263                                           COMPOSE_TO, PREF_NONE);
3264                         else if (!to_all && !to_sender) {
3265                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3266                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3267                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3268                                         if (compose->replyto) {
3269                                                 compose_entry_append(compose,
3270                                                         compose->replyto,
3271                                                         COMPOSE_TO, PREF_NONE);
3272                                         } else {
3273                                                 compose_entry_append(compose,
3274                                                           msginfo->from ? msginfo->from : "",
3275                                                           COMPOSE_TO, PREF_NONE);
3276                                         }
3277                                 } else {
3278                                         /* replying to own mail, use original recp */
3279                                         compose_entry_append(compose,
3280                                                   msginfo->to ? msginfo->to : "",
3281                                                   COMPOSE_TO, PREF_NONE);
3282                                         compose_entry_append(compose,
3283                                                   msginfo->cc ? msginfo->cc : "",
3284                                                   COMPOSE_CC, PREF_NONE);
3285                                 }
3286                         }
3287                 }
3288         } else {
3289                 if (to_sender || (compose->followup_to && 
3290                         !strncmp(compose->followup_to, "poster", 6)))
3291                         compose_entry_append
3292                                 (compose, 
3293                                  (compose->replyto ? compose->replyto :
3294                                         msginfo->from ? msginfo->from : ""),
3295                                  COMPOSE_TO, PREF_NONE);
3296                                  
3297                 else if (followup_and_reply_to || to_all) {
3298                         compose_entry_append
3299                                 (compose,
3300                                  (compose->replyto ? compose->replyto :
3301                                  msginfo->from ? msginfo->from : ""),
3302                                  COMPOSE_TO, PREF_NONE);                                
3303                 
3304                         compose_entry_append
3305                                 (compose,
3306                                  compose->followup_to ? compose->followup_to :
3307                                  compose->newsgroups ? compose->newsgroups : "",
3308                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3309                 } 
3310                 else 
3311                         compose_entry_append
3312                                 (compose,
3313                                  compose->followup_to ? compose->followup_to :
3314                                  compose->newsgroups ? compose->newsgroups : "",
3315                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3316         }
3317         compose_reply_set_subject(compose, msginfo);
3318
3319         if (to_ml && compose->ml_post) return;
3320         if (!to_all || compose->account->protocol == A_NNTP) return;
3321
3322         if (compose->replyto) {
3323                 Xstrdup_a(replyto, compose->replyto, return);
3324                 extract_address(replyto);
3325         }
3326         if (msginfo->from) {
3327                 Xstrdup_a(from, msginfo->from, return);
3328                 extract_address(from);
3329         }
3330
3331         if (replyto && from)
3332                 cc_list = address_list_append_with_comments(cc_list, from);
3333         if (to_all && msginfo->folder && 
3334             msginfo->folder->prefs->enable_default_reply_to)
3335                 cc_list = address_list_append_with_comments(cc_list,
3336                                 msginfo->folder->prefs->default_reply_to);
3337         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3338         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3339
3340         ac_email = g_utf8_strdown(compose->account->address, -1);
3341
3342         if (cc_list) {
3343                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3344                         gchar *addr = g_utf8_strdown(cur->data, -1);
3345                         extract_address(addr);
3346                 
3347                         if (strcmp(ac_email, addr))
3348                                 compose_entry_append(compose, (gchar *)cur->data,
3349                                                      COMPOSE_CC, PREF_NONE);
3350                         else
3351                                 debug_print("Cc address same as compose account's, ignoring\n");
3352
3353                         g_free(addr);
3354                 }
3355                 
3356                 slist_free_strings_full(cc_list);
3357         }
3358         
3359         g_free(ac_email);
3360 }
3361
3362 #define SET_ENTRY(entry, str) \
3363 { \
3364         if (str && *str) \
3365                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3366 }
3367
3368 #define SET_ADDRESS(type, str) \
3369 { \
3370         if (str && *str) \
3371                 compose_entry_append(compose, str, type, PREF_NONE); \
3372 }
3373
3374 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3375 {
3376         cm_return_if_fail(msginfo != NULL);
3377
3378         SET_ENTRY(subject_entry, msginfo->subject);
3379         SET_ENTRY(from_name, msginfo->from);
3380         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3381         SET_ADDRESS(COMPOSE_CC, compose->cc);
3382         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3383         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3384         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3385         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3386
3387         compose_update_priority_menu_item(compose);
3388         compose_update_privacy_system_menu_item(compose, FALSE);
3389         compose_show_first_last_header(compose, TRUE);
3390 }
3391
3392 #undef SET_ENTRY
3393 #undef SET_ADDRESS
3394
3395 static void compose_insert_sig(Compose *compose, gboolean replace)
3396 {
3397         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3398         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3399         GtkTextMark *mark;
3400         GtkTextIter iter, iter_end;
3401         gint cur_pos, ins_pos;
3402         gboolean prev_autowrap;
3403         gboolean found = FALSE;
3404         gboolean exists = FALSE;
3405         
3406         cm_return_if_fail(compose->account != NULL);
3407
3408         BLOCK_WRAP();
3409
3410         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3411                                         G_CALLBACK(compose_changed_cb),
3412                                         compose);
3413         
3414         mark = gtk_text_buffer_get_insert(buffer);
3415         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3416         cur_pos = gtk_text_iter_get_offset (&iter);
3417         ins_pos = cur_pos;
3418
3419         gtk_text_buffer_get_end_iter(buffer, &iter);
3420
3421         exists = (compose->sig_str != NULL);
3422
3423         if (replace) {
3424                 GtkTextIter first_iter, start_iter, end_iter;
3425
3426                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3427
3428                 if (!exists || compose->sig_str[0] == '\0')
3429                         found = FALSE;
3430                 else
3431                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3432                                         compose->signature_tag);
3433
3434                 if (found) {
3435                         /* include previous \n\n */
3436                         gtk_text_iter_backward_chars(&first_iter, 1);
3437                         start_iter = first_iter;
3438                         end_iter = first_iter;
3439                         /* skip re-start */
3440                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3441                                         compose->signature_tag);
3442                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3443                                         compose->signature_tag);
3444                         if (found) {
3445                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3446                                 iter = start_iter;
3447                         }
3448                 } 
3449         } 
3450
3451         g_free(compose->sig_str);
3452         compose->sig_str = account_get_signature_str(compose->account);
3453
3454         cur_pos = gtk_text_iter_get_offset(&iter);
3455
3456         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3457                 g_free(compose->sig_str);
3458                 compose->sig_str = NULL;
3459         } else {
3460                 if (compose->sig_inserted == FALSE)
3461                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3462                 compose->sig_inserted = TRUE;
3463
3464                 cur_pos = gtk_text_iter_get_offset(&iter);
3465                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3466                 /* remove \n\n */
3467                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3468                 gtk_text_iter_forward_chars(&iter, 1);
3469                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3470                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3471
3472                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3473                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3474         }
3475
3476         /* put the cursor where it should be 
3477          * either where the quote_fmt says, either where it was */
3478         if (compose->set_cursor_pos < 0)
3479                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3480         else
3481                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3482                         compose->set_cursor_pos);
3483         
3484         compose->set_cursor_pos = -1;
3485         gtk_text_buffer_place_cursor(buffer, &iter);
3486         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3487                                         G_CALLBACK(compose_changed_cb),
3488                                         compose);
3489                 
3490         UNBLOCK_WRAP();
3491 }
3492
3493 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3494 {
3495         GtkTextView *text;
3496         GtkTextBuffer *buffer;
3497         GtkTextMark *mark;
3498         GtkTextIter iter;
3499         const gchar *cur_encoding;
3500         gchar buf[BUFFSIZE];
3501         gint len;
3502         FILE *fp;
3503         gboolean prev_autowrap;
3504         gboolean badtxt = FALSE;
3505         struct stat file_stat;
3506         int ret;
3507
3508         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3509
3510         /* get the size of the file we are about to insert */
3511         ret = g_stat(file, &file_stat);
3512         if (ret != 0) {
3513                 gchar *shortfile = g_path_get_basename(file);
3514                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3515                 g_free(shortfile);
3516                 return COMPOSE_INSERT_NO_FILE;
3517         } else if (prefs_common.warn_large_insert == TRUE) {
3518
3519                 /* ask user for confirmation if the file is large */
3520                 if (prefs_common.warn_large_insert_size < 0 ||
3521                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3522                         AlertValue aval;
3523                         gchar *msg;
3524
3525                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3526                                                 "in the message body. Are you sure you want to do that?"),
3527                                                 to_human_readable(file_stat.st_size));
3528                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3529                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3530                         g_free(msg);
3531
3532                         /* do we ask for confirmation next time? */
3533                         if (aval & G_ALERTDISABLE) {
3534                                 /* no confirmation next time, disable feature in preferences */
3535                                 aval &= ~G_ALERTDISABLE;
3536                                 prefs_common.warn_large_insert = FALSE;
3537                         }
3538
3539                         /* abort file insertion if user canceled action */
3540                         if (aval != G_ALERTALTERNATE) {
3541                                 return COMPOSE_INSERT_NO_FILE;
3542                         }
3543                 }
3544         }
3545
3546
3547         if ((fp = g_fopen(file, "rb")) == NULL) {
3548                 FILE_OP_ERROR(file, "fopen");
3549                 return COMPOSE_INSERT_READ_ERROR;
3550         }
3551
3552         prev_autowrap = compose->autowrap;
3553         compose->autowrap = FALSE;
3554
3555         text = GTK_TEXT_VIEW(compose->text);
3556         buffer = gtk_text_view_get_buffer(text);
3557         mark = gtk_text_buffer_get_insert(buffer);
3558         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3559
3560         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3561                                         G_CALLBACK(text_inserted),
3562                                         compose);
3563
3564         cur_encoding = conv_get_locale_charset_str_no_utf8();
3565
3566         while (fgets(buf, sizeof(buf), fp) != NULL) {
3567                 gchar *str;
3568
3569                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3570                         str = g_strdup(buf);
3571                 else
3572                         str = conv_codeset_strdup
3573                                 (buf, cur_encoding, CS_INTERNAL);
3574                 if (!str) continue;
3575
3576                 /* strip <CR> if DOS/Windows file,
3577                    replace <CR> with <LF> if Macintosh file. */
3578                 strcrchomp(str);
3579                 len = strlen(str);
3580                 if (len > 0 && str[len - 1] != '\n') {
3581                         while (--len >= 0)
3582                                 if (str[len] == '\r') str[len] = '\n';
3583                 }
3584
3585                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3586                 g_free(str);
3587         }
3588
3589         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3590                                           G_CALLBACK(text_inserted),
3591                                           compose);
3592         compose->autowrap = prev_autowrap;
3593         if (compose->autowrap)
3594                 compose_wrap_all(compose);
3595
3596         fclose(fp);
3597
3598         if (badtxt)
3599                 return COMPOSE_INSERT_INVALID_CHARACTER;
3600         else 
3601                 return COMPOSE_INSERT_SUCCESS;
3602 }
3603
3604 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3605                                   const gchar *filename,
3606                                   const gchar *content_type,
3607                                   const gchar *charset)
3608 {
3609         AttachInfo *ainfo;
3610         GtkTreeIter iter;
3611         FILE *fp;
3612         off_t size;
3613         GAuto *auto_ainfo;
3614         gchar *size_text;
3615         GtkListStore *store;
3616         gchar *name;
3617         gboolean has_binary = FALSE;
3618
3619         if (!is_file_exist(file)) {
3620                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3621                 gboolean result = FALSE;
3622                 if (file_from_uri && is_file_exist(file_from_uri)) {
3623                         result = compose_attach_append(
3624                                                 compose, file_from_uri,
3625                                                 filename, content_type,
3626                                                 charset);
3627                 }
3628                 g_free(file_from_uri);
3629                 if (result)
3630                         return TRUE;
3631                 alertpanel_error("File %s doesn't exist\n", filename);
3632                 return FALSE;
3633         }
3634         if ((size = get_file_size(file)) < 0) {
3635                 alertpanel_error("Can't get file size of %s\n", filename);
3636                 return FALSE;
3637         }
3638         if (size == 0) {
3639                 alertpanel_error(_("File %s is empty."), filename);
3640                 return FALSE;
3641         }
3642         if ((fp = g_fopen(file, "rb")) == NULL) {
3643                 alertpanel_error(_("Can't read %s."), filename);
3644                 return FALSE;
3645         }
3646         fclose(fp);
3647
3648         ainfo = g_new0(AttachInfo, 1);
3649         auto_ainfo = g_auto_pointer_new_with_free
3650                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3651         ainfo->file = g_strdup(file);
3652
3653         if (content_type) {
3654                 ainfo->content_type = g_strdup(content_type);
3655                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3656                         MsgInfo *msginfo;
3657                         MsgFlags flags = {0, 0};
3658
3659                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3660                                 ainfo->encoding = ENC_7BIT;
3661                         else
3662                                 ainfo->encoding = ENC_8BIT;
3663
3664                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3665                         if (msginfo && msginfo->subject)
3666                                 name = g_strdup(msginfo->subject);
3667                         else
3668                                 name = g_path_get_basename(filename ? filename : file);
3669
3670                         ainfo->name = g_strdup_printf(_("Message: %s"), name);