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