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