2008-03-07 [paul] 3.3.1cvs17
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 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/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
57
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <ctype.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <unistd.h>
68 #include <time.h>
69 #include <stdlib.h>
70 #if HAVE_SYS_WAIT_H
71 #  include <sys/wait.h>
72 #endif
73 #include <signal.h>
74 #include <errno.h>
75 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
76 #include <libgen.h>
77 #endif
78
79 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
80 #  include <wchar.h>
81 #  include <wctype.h>
82 #endif
83
84 #include "claws.h"
85 #include "main.h"
86 #include "mainwindow.h"
87 #include "compose.h"
88 #include "addressbook.h"
89 #include "folderview.h"
90 #include "procmsg.h"
91 #include "menu.h"
92 #include "stock_pixmap.h"
93 #include "send_message.h"
94 #include "imap.h"
95 #include "news.h"
96 #include "customheader.h"
97 #include "prefs_common.h"
98 #include "prefs_account.h"
99 #include "action.h"
100 #include "account.h"
101 #include "filesel.h"
102 #include "procheader.h"
103 #include "procmime.h"
104 #include "statusbar.h"
105 #include "about.h"
106 #include "base64.h"
107 #include "quoted-printable.h"
108 #include "codeconv.h"
109 #include "utils.h"
110 #include "gtkutils.h"
111 #include "socket.h"
112 #include "alertpanel.h"
113 #include "manage_window.h"
114 #include "gtkshruler.h"
115 #include "folder.h"
116 #include "addr_compl.h"
117 #include "quote_fmt.h"
118 #include "undo.h"
119 #include "foldersel.h"
120 #include "toolbar.h"
121 #include "inc.h"
122 #include "message_search.h"
123 #include "combobox.h"
124 #include "hooks.h"
125 #include "privacy.h"
126 #include "timing.h"
127
128 enum
129 {
130         COL_MIMETYPE = 0,
131         COL_SIZE     = 1,
132         COL_NAME     = 2,
133         COL_DATA     = 3,
134         COL_AUTODATA = 4,
135         N_COL_COLUMNS
136 };
137
138 #define N_ATTACH_COLS   (N_COL_COLUMNS)
139
140 typedef enum
141 {
142         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
143         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
144         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
145         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
146         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
147         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
148         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
149         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
150         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
151         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
152         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
153         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
154         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
155         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
156         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
157 } ComposeCallAdvancedAction;
158
159 typedef enum
160 {
161         PRIORITY_HIGHEST = 1,
162         PRIORITY_HIGH,
163         PRIORITY_NORMAL,
164         PRIORITY_LOW,
165         PRIORITY_LOWEST
166 } PriorityLevel;
167
168 typedef enum
169 {
170         COMPOSE_INSERT_SUCCESS,
171         COMPOSE_INSERT_READ_ERROR,
172         COMPOSE_INSERT_INVALID_CHARACTER,
173         COMPOSE_INSERT_NO_FILE
174 } ComposeInsertResult;
175
176 typedef enum
177 {
178         COMPOSE_WRITE_FOR_SEND,
179         COMPOSE_WRITE_FOR_STORE
180 } ComposeWriteType;
181
182 typedef enum
183 {
184         COMPOSE_QUOTE_FORCED,
185         COMPOSE_QUOTE_CHECK,
186         COMPOSE_QUOTE_SKIP
187 } ComposeQuoteMode;
188
189 #define B64_LINE_SIZE           57
190 #define B64_BUFFSIZE            77
191
192 #define MAX_REFERENCES_LEN      999
193
194 static GList *compose_list = NULL;
195
196 static Compose *compose_generic_new                     (PrefsAccount   *account,
197                                                  const gchar    *to,
198                                                  FolderItem     *item,
199                                                  GPtrArray      *attach_files,
200                                                  GList          *listAddress );
201
202 static Compose *compose_create                  (PrefsAccount   *account,
203                                                  FolderItem              *item,
204                                                  ComposeMode     mode,
205                                                  gboolean batch);
206
207 static void compose_entry_mark_default_to       (Compose          *compose,
208                                          const gchar      *address);
209 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
210                                          ComposeQuoteMode        quote_mode,
211                                          gboolean        to_all,
212                                          gboolean        to_sender,
213                                          const gchar    *body);
214 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
215                                          GSList         *msginfo_list);
216 static Compose *compose_reply                   (MsgInfo        *msginfo,
217                                          ComposeQuoteMode        quote_mode,
218                                          gboolean        to_all,
219                                          gboolean        to_ml,
220                                          gboolean        to_sender,
221                                          const gchar    *body);
222 static Compose *compose_reply_mode              (ComposeMode     mode, 
223                                          GSList         *msginfo_list, 
224                                          gchar          *body);
225 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
226 static void compose_update_privacy_systems_menu(Compose *compose);
227
228 static GtkWidget *compose_account_option_menu_create
229                                                 (Compose        *compose);
230 static void compose_set_out_encoding            (Compose        *compose);
231 static void compose_set_template_menu           (Compose        *compose);
232 static void compose_template_apply              (Compose        *compose,
233                                                  Template       *tmpl,
234                                                  gboolean        replace);
235 static void compose_destroy                     (Compose        *compose);
236
237 static void compose_entries_set                 (Compose        *compose,
238                                                  const gchar    *mailto,
239                                                  ComposeEntryType to_type);
240 static gint compose_parse_header                (Compose        *compose,
241                                                  MsgInfo        *msginfo);
242 static gchar *compose_parse_references          (const gchar    *ref,
243                                                  const gchar    *msgid);
244
245 static gchar *compose_quote_fmt                 (Compose        *compose,
246                                                  MsgInfo        *msginfo,
247                                                  const gchar    *fmt,
248                                                  const gchar    *qmark,
249                                                  const gchar    *body,
250                                                  gboolean        rewrap,
251                                                  gboolean        need_unescape,
252                                                  const gchar *err_msg);
253
254 static void compose_reply_set_entry             (Compose        *compose,
255                                                  MsgInfo        *msginfo,
256                                                  gboolean        to_all,
257                                                  gboolean        to_ml,
258                                                  gboolean        to_sender,
259                                                  gboolean
260                                                  followup_and_reply_to);
261 static void compose_reedit_set_entry            (Compose        *compose,
262                                                  MsgInfo        *msginfo);
263
264 static void compose_insert_sig                  (Compose        *compose,
265                                                  gboolean        replace);
266 static gchar *compose_get_signature_str         (Compose        *compose);
267 static ComposeInsertResult compose_insert_file  (Compose        *compose,
268                                                  const gchar    *file);
269
270 static gboolean compose_attach_append           (Compose        *compose,
271                                                  const gchar    *file,
272                                                  const gchar    *type,
273                                                  const gchar    *content_type);
274 static void compose_attach_parts                (Compose        *compose,
275                                                  MsgInfo        *msginfo);
276
277 static gboolean compose_beautify_paragraph      (Compose        *compose,
278                                                  GtkTextIter    *par_iter,
279                                                  gboolean        force);
280 static void compose_wrap_all                    (Compose        *compose);
281 static void compose_wrap_all_full               (Compose        *compose,
282                                                  gboolean        autowrap);
283
284 static void compose_set_title                   (Compose        *compose);
285 static void compose_select_account              (Compose        *compose,
286                                                  PrefsAccount   *account,
287                                                  gboolean        init);
288
289 static PrefsAccount *compose_current_mail_account(void);
290 /* static gint compose_send                     (Compose        *compose); */
291 static gboolean compose_check_for_valid_recipient
292                                                 (Compose        *compose);
293 static gboolean compose_check_entries           (Compose        *compose,
294                                                  gboolean       check_everything);
295 static gint compose_write_to_file               (Compose        *compose,
296                                                  FILE           *fp,
297                                                  gint            action,
298                                                  gboolean        attach_parts);
299 static gint compose_write_body_to_file          (Compose        *compose,
300                                                  const gchar    *file);
301 static gint compose_remove_reedit_target        (Compose        *compose,
302                                                  gboolean        force);
303 static void compose_remove_draft                        (Compose        *compose);
304 static gint compose_queue_sub                   (Compose        *compose,
305                                                  gint           *msgnum,
306                                                  FolderItem     **item,
307                                                  gchar          **msgpath,
308                                                  gboolean       check_subject,
309                                                  gboolean       remove_reedit_target);
310 static void compose_add_attachments             (Compose        *compose,
311                                                  MimeInfo       *parent);
312 static gchar *compose_get_header                (Compose        *compose);
313
314 static void compose_convert_header              (Compose        *compose,
315                                                  gchar          *dest,
316                                                  gint            len,
317                                                  gchar          *src,
318                                                  gint            header_len,
319                                                  gboolean        addr_field);
320
321 static void compose_attach_info_free            (AttachInfo     *ainfo);
322 static void compose_attach_remove_selected      (Compose        *compose);
323
324 static void compose_attach_property             (Compose        *compose);
325 static void compose_attach_property_create      (gboolean       *cancelled);
326 static void attach_property_ok                  (GtkWidget      *widget,
327                                                  gboolean       *cancelled);
328 static void attach_property_cancel              (GtkWidget      *widget,
329                                                  gboolean       *cancelled);
330 static gint attach_property_delete_event        (GtkWidget      *widget,
331                                                  GdkEventAny    *event,
332                                                  gboolean       *cancelled);
333 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
334                                                  GdkEventKey    *event,
335                                                  gboolean       *cancelled);
336
337 static void compose_exec_ext_editor             (Compose        *compose);
338 #ifdef G_OS_UNIX
339 static gint compose_exec_ext_editor_real        (const gchar    *file);
340 static gboolean compose_ext_editor_kill         (Compose        *compose);
341 static gboolean compose_input_cb                (GIOChannel     *source,
342                                                  GIOCondition    condition,
343                                                  gpointer        data);
344 static void compose_set_ext_editor_sensitive    (Compose        *compose,
345                                                  gboolean        sensitive);
346 #endif /* G_OS_UNIX */
347
348 static void compose_undo_state_changed          (UndoMain       *undostruct,
349                                                  gint            undo_state,
350                                                  gint            redo_state,
351                                                  gpointer        data);
352
353 static void compose_create_header_entry (Compose *compose);
354 static void compose_add_header_entry    (Compose *compose, const gchar *header, gchar *text);
355 static void compose_remove_header_entries(Compose *compose);
356
357 static void compose_update_priority_menu_item(Compose * compose);
358 #if USE_ASPELL
359 static void compose_spell_menu_changed  (void *data);
360 #endif
361 static void compose_add_field_list      ( Compose *compose,
362                                           GList *listAddress );
363
364 /* callback functions */
365
366 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
367                                          GtkAllocation  *allocation,
368                                          GtkSHRuler     *shruler);
369 static void account_activated           (GtkComboBox *optmenu,
370                                          gpointer        data);
371 static void attach_selected             (GtkTreeView    *tree_view, 
372                                          GtkTreePath    *tree_path,
373                                          GtkTreeViewColumn *column, 
374                                          Compose *compose);
375 static gboolean attach_button_pressed   (GtkWidget      *widget,
376                                          GdkEventButton *event,
377                                          gpointer        data);
378 static gboolean attach_key_pressed      (GtkWidget      *widget,
379                                          GdkEventKey    *event,
380                                          gpointer        data);
381 static void compose_send_cb             (gpointer        data,
382                                          guint           action,
383                                          GtkWidget      *widget);
384 static void compose_send_later_cb       (gpointer        data,
385                                          guint           action,
386                                          GtkWidget      *widget);
387
388 static void compose_draft_cb            (gpointer        data,
389                                          guint           action,
390                                          GtkWidget      *widget);
391
392 static void compose_attach_cb           (gpointer        data,
393                                          guint           action,
394                                          GtkWidget      *widget);
395 static void compose_insert_file_cb      (gpointer        data,
396                                          guint           action,
397                                          GtkWidget      *widget);
398 static void compose_insert_sig_cb       (gpointer        data,
399                                          guint           action,
400                                          GtkWidget      *widget);
401
402 static void compose_close_cb            (gpointer        data,
403                                          guint           action,
404                                          GtkWidget      *widget);
405
406 static void compose_set_encoding_cb     (gpointer        data,
407                                          guint           action,
408                                          GtkWidget      *widget);
409
410 static void compose_address_cb          (gpointer        data,
411                                          guint           action,
412                                          GtkWidget      *widget);
413 static void compose_template_activate_cb(GtkWidget      *widget,
414                                          gpointer        data);
415
416 static void compose_ext_editor_cb       (gpointer        data,
417                                          guint           action,
418                                          GtkWidget      *widget);
419
420 static gint compose_delete_cb           (GtkWidget      *widget,
421                                          GdkEventAny    *event,
422                                          gpointer        data);
423
424 static void compose_undo_cb             (Compose        *compose);
425 static void compose_redo_cb             (Compose        *compose);
426 static void compose_cut_cb              (Compose        *compose);
427 static void compose_copy_cb             (Compose        *compose);
428 static void compose_paste_cb            (Compose        *compose);
429 static void compose_paste_as_quote_cb   (Compose        *compose);
430 static void compose_paste_no_wrap_cb    (Compose        *compose);
431 static void compose_paste_wrap_cb       (Compose        *compose);
432 static void compose_allsel_cb           (Compose        *compose);
433
434 static void compose_advanced_action_cb  (Compose                   *compose,
435                                          ComposeCallAdvancedAction  action);
436
437 static void compose_grab_focus_cb       (GtkWidget      *widget,
438                                          Compose        *compose);
439
440 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
441                                          Compose        *compose);
442
443 static void compose_wrap_cb             (gpointer        data,
444                                          guint           action,
445                                          GtkWidget      *widget);
446 static void compose_find_cb             (gpointer        data,
447                                          guint           action,
448                                          GtkWidget      *widget);
449 static void compose_toggle_autowrap_cb  (gpointer        data,
450                                          guint           action,
451                                          GtkWidget      *widget);
452
453 static void compose_toggle_ruler_cb     (gpointer        data,
454                                          guint           action,
455                                          GtkWidget      *widget);
456 static void compose_toggle_sign_cb      (gpointer        data,
457                                          guint           action,
458                                          GtkWidget      *widget);
459 static void compose_toggle_encrypt_cb   (gpointer        data,
460                                          guint           action,
461                                          GtkWidget      *widget);
462 static void compose_set_privacy_system_cb(GtkWidget      *widget,
463                                           gpointer        data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system     (Compose *compose, 
466                                          PrefsAccount *account,
467                                          gboolean warn);
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
471                                              GtkWidget *widget);
472 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
473                                              GtkWidget *widget);
474 static void compose_set_priority_cb     (gpointer        data,
475                                          guint           action,
476                                          GtkWidget      *widget);
477 static void compose_reply_change_mode   (gpointer        data,
478                                          ComposeMode    action,
479                                          GtkWidget      *widget);
480
481 static void compose_attach_drag_received_cb (GtkWidget          *widget,
482                                              GdkDragContext     *drag_context,
483                                              gint                x,
484                                              gint                y,
485                                              GtkSelectionData   *data,
486                                              guint               info,
487                                              guint               time,
488                                              gpointer            user_data);
489 static void compose_insert_drag_received_cb (GtkWidget          *widget,
490                                              GdkDragContext     *drag_context,
491                                              gint                x,
492                                              gint                y,
493                                              GtkSelectionData   *data,
494                                              guint               info,
495                                              guint               time,
496                                              gpointer            user_data);
497 static void compose_header_drag_received_cb (GtkWidget          *widget,
498                                              GdkDragContext     *drag_context,
499                                              gint                x,
500                                              gint                y,
501                                              GtkSelectionData   *data,
502                                              guint               info,
503                                              guint               time,
504                                              gpointer            user_data);
505
506 static gboolean compose_drag_drop           (GtkWidget *widget,
507                                              GdkDragContext *drag_context,
508                                              gint x, gint y,
509                                              guint time, gpointer user_data);
510
511 static void text_inserted               (GtkTextBuffer  *buffer,
512                                          GtkTextIter    *iter,
513                                          const gchar    *text,
514                                          gint            len,
515                                          Compose        *compose);
516 static Compose *compose_generic_reply(MsgInfo *msginfo,
517                                   ComposeQuoteMode quote_mode,
518                                   gboolean to_all,
519                                   gboolean to_ml,
520                                   gboolean to_sender,
521                                   gboolean followup_and_reply_to,
522                                   const gchar *body);
523
524 static gboolean compose_headerentry_changed_cb     (GtkWidget          *entry,
525                                             ComposeHeaderEntry *headerentry);
526 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
527                                             GdkEventKey        *event,
528                                             ComposeHeaderEntry *headerentry);
529
530 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
531
532 static void compose_allow_user_actions (Compose *compose, gboolean allow);
533
534 #if USE_ASPELL
535 static void compose_check_all              (Compose *compose);
536 static void compose_highlight_all          (Compose *compose);
537 static void compose_check_backwards        (Compose *compose);
538 static void compose_check_forwards_go      (Compose *compose);
539 #endif
540
541 static gint compose_defer_auto_save_draft       (Compose        *compose);
542 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
543
544 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
545
546 #ifdef USE_ASPELL
547 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
548                                                 FolderItem *folder_item);
549 #endif
550 static void compose_attach_update_label(Compose *compose);
551
552 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
553
554 static GtkItemFactoryEntry compose_popup_entries[] =
555 {
556         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
557         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
558         {"/---",                NULL, NULL, 0, "<Separator>"},
559         {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
560 };
561
562 static GtkItemFactoryEntry compose_entries[] =
563 {
564         {N_("/_Message"),                               NULL, NULL, 0, "<Branch>"},
565         {N_("/_Message/S_end"),         "<control>Return",
566                                         compose_send_cb, 0, NULL},
567         {N_("/_Message/Send _later"),   "<shift><control>S",
568                                         compose_send_later_cb,  0, NULL},
569         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
570         {N_("/_Message/_Attach file"),          "<control>M", compose_attach_cb,      0, NULL},
571         {N_("/_Message/_Insert file"),          "<control>I", compose_insert_file_cb, 0, NULL},
572         {N_("/_Message/Insert si_gnature"),     "<control>G", compose_insert_sig_cb,  0, NULL},
573         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
574         {N_("/_Message/_Save"),
575                                                 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
576         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
577         {N_("/_Message/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
578
579         {N_("/_Edit"),                  NULL, NULL, 0, "<Branch>"},
580         {N_("/_Edit/_Undo"),            "<control>Z", compose_undo_cb, 0, NULL},
581         {N_("/_Edit/_Redo"),            "<control>Y", compose_redo_cb, 0, NULL},
582         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
583         {N_("/_Edit/Cu_t"),             "<control>X", compose_cut_cb,    0, NULL},
584         {N_("/_Edit/_Copy"),            "<control>C", compose_copy_cb,   0, NULL},
585         {N_("/_Edit/_Paste"),           "<control>V", compose_paste_cb,  0, NULL},
586         {N_("/_Edit/Special paste"),    NULL, NULL, 0, "<Branch>"},
587         {N_("/_Edit/Special paste/as _quotation"),
588                                         NULL, compose_paste_as_quote_cb, 0, NULL},
589         {N_("/_Edit/Special paste/_wrapped"),
590                                         NULL, compose_paste_wrap_cb, 0, NULL},
591         {N_("/_Edit/Special paste/_unwrapped"),
592                                         NULL, compose_paste_no_wrap_cb, 0, NULL},
593         {N_("/_Edit/Select _all"),      "<control>A", compose_allsel_cb, 0, NULL},
594         {N_("/_Edit/A_dvanced"),        NULL, NULL, 0, "<Branch>"},
595         {N_("/_Edit/A_dvanced/Move a character backward"),
596                                         "<shift><control>B",
597                                         compose_advanced_action_cb,
598                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
599                                         NULL},
600         {N_("/_Edit/A_dvanced/Move a character forward"),
601                                         "<shift><control>F",
602                                         compose_advanced_action_cb,
603                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
604                                         NULL},
605         {N_("/_Edit/A_dvanced/Move a word backward"),
606                                         NULL, /* "<alt>B" */
607                                         compose_advanced_action_cb,
608                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
609                                         NULL},
610         {N_("/_Edit/A_dvanced/Move a word forward"),
611                                         NULL, /* "<alt>F" */
612                                         compose_advanced_action_cb,
613                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
614                                         NULL},
615         {N_("/_Edit/A_dvanced/Move to beginning of line"),
616                                         NULL, /* "<control>A" */
617                                         compose_advanced_action_cb,
618                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
619                                         NULL},
620         {N_("/_Edit/A_dvanced/Move to end of line"),
621                                         "<control>E",
622                                         compose_advanced_action_cb,
623                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
624                                         NULL},
625         {N_("/_Edit/A_dvanced/Move to previous line"),
626                                         "<control>P",
627                                         compose_advanced_action_cb,
628                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
629                                         NULL},
630         {N_("/_Edit/A_dvanced/Move to next line"),
631                                         "<control>N",
632                                         compose_advanced_action_cb,
633                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
634                                         NULL},
635         {N_("/_Edit/A_dvanced/Delete a character backward"),
636                                         "<control>H",
637                                         compose_advanced_action_cb,
638                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
639                                         NULL},
640         {N_("/_Edit/A_dvanced/Delete a character forward"),
641                                         "<control>D",
642                                         compose_advanced_action_cb,
643                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
644                                         NULL},
645         {N_("/_Edit/A_dvanced/Delete a word backward"),
646                                         NULL, /* "<control>W" */
647                                         compose_advanced_action_cb,
648                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
649                                         NULL},
650         {N_("/_Edit/A_dvanced/Delete a word forward"),
651                                         NULL, /* "<alt>D", */
652                                         compose_advanced_action_cb,
653                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
654                                         NULL},
655         {N_("/_Edit/A_dvanced/Delete line"),
656                                         "<control>U",
657                                         compose_advanced_action_cb,
658                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
659                                         NULL},
660         {N_("/_Edit/A_dvanced/Delete entire line"),
661                                         NULL,
662                                         compose_advanced_action_cb,
663                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
664                                         NULL},
665         {N_("/_Edit/A_dvanced/Delete to end of line"),
666                                         "<control>K",
667                                         compose_advanced_action_cb,
668                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
669                                         NULL},
670         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
671         {N_("/_Edit/_Find"),
672                                         "<control>F", compose_find_cb, 0, NULL},
673         {N_("/_Edit/---"),                      NULL, NULL, 0, "<Separator>"},
674         {N_("/_Edit/_Wrap current paragraph"),
675                                         "<control>L", compose_wrap_cb, 0, NULL},
676         {N_("/_Edit/Wrap all long _lines"),
677                                         "<control><alt>L", compose_wrap_cb, 1, NULL},
678         {N_("/_Edit/Aut_o wrapping"),   "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
679         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
680         {N_("/_Edit/Edit with e_xternal editor"),
681                                         "<shift><control>X", compose_ext_editor_cb, 0, NULL},
682 #if USE_ASPELL
683         {N_("/_Spelling"),              NULL, NULL, 0, "<Branch>"},
684         {N_("/_Spelling/_Check all or check selection"),
685                                         NULL, compose_check_all, 0, NULL},
686         {N_("/_Spelling/_Highlight all misspelled words"),
687                                         NULL, compose_highlight_all, 0, NULL},
688         {N_("/_Spelling/Check _backwards misspelled word"),
689                                         NULL, compose_check_backwards , 0, NULL},
690         {N_("/_Spelling/_Forward to next misspelled word"),
691                                         NULL, compose_check_forwards_go, 0, NULL},
692         {N_("/_Spelling/---"),          NULL, NULL, 0, "<Separator>"},
693         {N_("/_Spelling/Options"),
694                                         NULL, NULL, 0, "<Branch>"},
695 #endif
696         {N_("/_Options"),               NULL, NULL, 0, "<Branch>"},
697         {N_("/_Options/Reply _mode"),           NULL, NULL,   0, "<Branch>"},
698         {N_("/_Options/Reply _mode/_Normal"),   NULL, compose_reply_change_mode,   COMPOSE_REPLY, "<RadioItem>"},
699         {N_("/_Options/Reply _mode/_All"),              NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
700         {N_("/_Options/Reply _mode/_Sender"),           NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
701         {N_("/_Options/Reply _mode/_Mailing-list"),     NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
702         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
703         {N_("/_Options/Privacy _System"),               NULL, NULL,   0, "<Branch>"},
704         {N_("/_Options/Privacy _System/None"),  NULL, NULL,   0, "<RadioItem>"},
705         {N_("/_Options/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
706         {N_("/_Options/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
707         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
708         {N_("/_Options/_Priority"),     NULL,           NULL,   0, "<Branch>"},
709         {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
710         {N_("/_Options/Priority/Hi_gh"),    NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
711         {N_("/_Options/Priority/_Normal"),  NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
712         {N_("/_Options/Priority/Lo_w"),    NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
713         {N_("/_Options/Priority/_Lowest"),  NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
714         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
715         {N_("/_Options/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
716         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
717         {N_("/_Options/Remo_ve references"),    NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
718         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
719
720 #define ENC_ACTION(action) \
721         NULL, compose_set_encoding_cb, action, \
722         "/Options/Character encoding/Automatic"
723
724         {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
725         {N_("/_Options/Character _encoding/_Automatic"),
726                         NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
727         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
728
729         {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
730          ENC_ACTION(C_US_ASCII)},
731         {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
732          ENC_ACTION(C_UTF_8)},
733         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
734
735         {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
736         {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
737          ENC_ACTION(C_ISO_8859_1)},
738         {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
739          ENC_ACTION(C_ISO_8859_15)},
740         {N_("/_Options/Character _encoding/Western European/Windows-1252"),
741          ENC_ACTION(C_WINDOWS_1252)},
742
743         {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
744          ENC_ACTION(C_ISO_8859_2)},
745
746         {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
747         {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
748          ENC_ACTION(C_ISO_8859_13)},
749         {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
750          ENC_ACTION(C_ISO_8859_4)},
751
752         {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
753          ENC_ACTION(C_ISO_8859_7)},
754
755         {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
756         {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
757          ENC_ACTION(C_ISO_8859_8)},
758         {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
759          ENC_ACTION(C_WINDOWS_1255)},
760
761         {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
762         {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
763          ENC_ACTION(C_ISO_8859_6)},
764         {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
765          ENC_ACTION(C_CP1256)},
766
767         {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
768          ENC_ACTION(C_ISO_8859_9)},
769
770         {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
771         {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
772          ENC_ACTION(C_ISO_8859_5)},
773         {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
774          ENC_ACTION(C_KOI8_R)},
775         {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
776          ENC_ACTION(C_KOI8_U)},
777         {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
778          ENC_ACTION(C_WINDOWS_1251)},
779
780         {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
781         {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
782          ENC_ACTION(C_ISO_2022_JP)},
783         {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
784          ENC_ACTION(C_ISO_2022_JP_2)},
785         {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
786          ENC_ACTION(C_EUC_JP)},
787         {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
788          ENC_ACTION(C_SHIFT_JIS)},
789
790         {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
791         {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
792          ENC_ACTION(C_GB2312)},
793         {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
794          ENC_ACTION(C_GBK)},
795         {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
796          ENC_ACTION(C_BIG5)},
797         {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
798          ENC_ACTION(C_EUC_TW)},
799
800         {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
801         {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
802          ENC_ACTION(C_EUC_KR)},
803         {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
804          ENC_ACTION(C_ISO_2022_KR)},
805
806         {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
807         {N_("/_Options/Character _encoding/Thai/TIS-620"),
808          ENC_ACTION(C_TIS_620)},
809         {N_("/_Options/Character _encoding/Thai/Windows-874"),
810          ENC_ACTION(C_WINDOWS_874)},
811
812         {N_("/_Tools"),                 NULL, NULL, 0, "<Branch>"},
813         {N_("/_Tools/Show _ruler"),     NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
814         {N_("/_Tools/_Address book"),   "<shift><control>A", compose_address_cb , 0, NULL},
815         {N_("/_Tools/_Template"),       NULL, NULL, 0, "<Branch>"},
816         {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
817         {N_("/_Help"),                  NULL, NULL, 0, "<Branch>"},
818         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
819 };
820
821 static GtkTargetEntry compose_mime_types[] =
822 {
823         {"text/uri-list", 0, 0},
824         {"UTF8_STRING", 0, 0},
825         {"text/plain", 0, 0}
826 };
827
828 static gboolean compose_put_existing_to_front(MsgInfo *info)
829 {
830         GList *compose_list = compose_get_compose_list();
831         GList *elem = NULL;
832         
833         if (compose_list) {
834                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
835                      elem = elem->next) {
836                         Compose *c = (Compose*)elem->data;
837
838                         if (!c->targetinfo || !c->targetinfo->msgid ||
839                             !info->msgid)
840                                 continue;
841
842                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
843                                 gtkut_window_popup(c->window);
844                                 return TRUE;
845                         }
846                 }
847         }
848         return FALSE;
849 }
850
851 static GdkColor quote_color1 = 
852         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
853 static GdkColor quote_color2 = 
854         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
855 static GdkColor quote_color3 = 
856         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
857
858 static GdkColor quote_bgcolor1 = 
859         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
860 static GdkColor quote_bgcolor2 = 
861         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
862 static GdkColor quote_bgcolor3 = 
863         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
864
865 static GdkColor signature_color = {
866         (gulong)0,
867         (gushort)0x7fff,
868         (gushort)0x7fff,
869         (gushort)0x7fff
870 };
871
872 static GdkColor uri_color = {
873         (gulong)0,
874         (gushort)0,
875         (gushort)0,
876         (gushort)0
877 };
878
879 static void compose_create_tags(GtkTextView *text, Compose *compose)
880 {
881         GtkTextBuffer *buffer;
882         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
883         GdkColormap *cmap;
884         GdkColor color[8];
885         gboolean success[8];
886         int i;
887
888         buffer = gtk_text_view_get_buffer(text);
889
890         if (prefs_common.enable_color) {
891                 /* grab the quote colors, converting from an int to a GdkColor */
892                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
893                                                &quote_color1);
894                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
895                                                &quote_color2);
896                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
897                                                &quote_color3);
898                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
899                                                &quote_bgcolor1);
900                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
901                                                &quote_bgcolor2);
902                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
903                                                &quote_bgcolor3);
904                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
905                                                &signature_color);
906                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
907                                                &uri_color);
908         } else {
909                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
910                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
911         }
912
913         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
914                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
915                                            "foreground-gdk", &quote_color1,
916                                            "paragraph-background-gdk", &quote_bgcolor1,
917                                            NULL);
918                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
919                                            "foreground-gdk", &quote_color2,
920                                            "paragraph-background-gdk", &quote_bgcolor2,
921                                            NULL);
922                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
923                                            "foreground-gdk", &quote_color3,
924                                            "paragraph-background-gdk", &quote_bgcolor3,
925                                            NULL);
926         } else {
927                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
928                                            "foreground-gdk", &quote_color1,
929                                            NULL);
930                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
931                                            "foreground-gdk", &quote_color2,
932                                            NULL);
933                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
934                                            "foreground-gdk", &quote_color3,
935                                            NULL);
936         }
937         
938         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
939                                    "foreground-gdk", &signature_color,
940                                    NULL);
941         
942         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
943                                         "foreground-gdk", &uri_color,
944                                          NULL);
945         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
946         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
947
948         color[0] = quote_color1;
949         color[1] = quote_color2;
950         color[2] = quote_color3;
951         color[3] = quote_bgcolor1;
952         color[4] = quote_bgcolor2;
953         color[5] = quote_bgcolor3;
954         color[6] = signature_color;
955         color[7] = uri_color;
956         cmap = gdk_drawable_get_colormap(compose->window->window);
957         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
958
959         for (i = 0; i < 8; i++) {
960                 if (success[i] == FALSE) {
961                         GtkStyle *style;
962
963                         g_warning("Compose: color allocation failed.\n");
964                         style = gtk_widget_get_style(GTK_WIDGET(text));
965                         quote_color1 = quote_color2 = quote_color3 = 
966                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
967                                 signature_color = uri_color = black;
968                 }
969         }
970 }
971
972 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
973                      GPtrArray *attach_files)
974 {
975         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
976 }
977
978 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
979 {
980         return compose_generic_new(account, mailto, item, NULL, NULL);
981 }
982
983 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
984 {
985         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
986 }
987
988 #define SCROLL_TO_CURSOR(compose) {                             \
989         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
990                 gtk_text_view_get_buffer(                       \
991                         GTK_TEXT_VIEW(compose->text)));         \
992         gtk_text_view_scroll_mark_onscreen(                     \
993                 GTK_TEXT_VIEW(compose->text),                   \
994                 cmark);                                         \
995 }
996
997 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
998                              GPtrArray *attach_files, GList *listAddress )
999 {
1000         Compose *compose;
1001         GtkTextView *textview;
1002         GtkTextBuffer *textbuf;
1003         GtkTextIter iter;
1004         GtkItemFactory *ifactory;
1005         const gchar *subject_format = NULL;
1006         const gchar *body_format = NULL;
1007         gchar *mailto_from = NULL;
1008         PrefsAccount *mailto_account = NULL;
1009         MsgInfo* dummyinfo = NULL;
1010
1011         /* check if mailto defines a from */
1012         if (mailto && *mailto != '\0') {
1013                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
1014                 /* mailto defines a from, check if we can get account prefs from it,
1015                    if not, the account prefs will be guessed using other ways, but we'll keep
1016                    the from anyway */
1017                 if (mailto_from)
1018                         mailto_account = account_find_from_address(mailto_from, TRUE);
1019                 if (mailto_account)
1020                         account = mailto_account;
1021         }
1022
1023         /* if no account prefs set from mailto, set if from folder prefs (if any) */
1024         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1025                 account = account_find_from_id(item->prefs->default_account);
1026
1027         /* if no account prefs set, fallback to the current one */
1028         if (!account) account = cur_account;
1029         g_return_val_if_fail(account != NULL, NULL);
1030
1031         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1032
1033         /* override from name if mailto asked for it */
1034         if (mailto_from) {
1035                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1036                 g_free(mailto_from);
1037         } else
1038                 /* override from name according to folder properties */
1039                 if (item && item->prefs &&
1040                         item->prefs->compose_with_format &&
1041                         item->prefs->compose_override_from_format &&
1042                         *item->prefs->compose_override_from_format != '\0') {
1043
1044                         gchar *tmp = NULL;
1045                         gchar *buf = NULL;
1046
1047                         dummyinfo = compose_msginfo_new_from_compose(compose);
1048
1049                         /* decode \-escape sequences in the internal representation of the quote format */
1050                         tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
1051                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1052
1053 #ifdef USE_ASPELL
1054                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1055                                         compose->gtkaspell);
1056 #else
1057                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1058 #endif
1059                         quote_fmt_scan_string(tmp);
1060                         quote_fmt_parse();
1061
1062                         buf = quote_fmt_get_buffer();
1063                         if (buf == NULL)
1064                                 alertpanel_error(_("New message From format error."));
1065                         else
1066                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1067                         quote_fmt_reset_vartable();
1068
1069                         g_free(tmp);
1070                 }
1071
1072         ifactory = gtk_item_factory_from_widget(compose->menubar);
1073
1074         compose->replyinfo = NULL;
1075         compose->fwdinfo   = NULL;
1076
1077         textview = GTK_TEXT_VIEW(compose->text);
1078         textbuf = gtk_text_view_get_buffer(textview);
1079         compose_create_tags(textview, compose);
1080
1081         undo_block(compose->undostruct);
1082 #ifdef USE_ASPELL
1083         compose_set_dictionaries_from_folder_prefs(compose, item);
1084 #endif
1085
1086         if (account->auto_sig)
1087                 compose_insert_sig(compose, FALSE);
1088         gtk_text_buffer_get_start_iter(textbuf, &iter);
1089         gtk_text_buffer_place_cursor(textbuf, &iter);
1090
1091         if (account->protocol != A_NNTP) {
1092                 if (mailto && *mailto != '\0') {
1093                         compose_entries_set(compose, mailto, COMPOSE_TO);
1094
1095                 } else if (item && item->prefs->enable_default_to) {
1096                         compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1097                         compose_entry_mark_default_to(compose, item->prefs->default_to);
1098                 }
1099                 if (item && item->ret_rcpt) {
1100                         menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1101                 }
1102         } else {
1103                 if (mailto && *mailto != '\0') {
1104                         if (!strchr(mailto, '@'))
1105                                 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1106                         else
1107                                 compose_entries_set(compose, mailto, COMPOSE_TO);
1108                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1109                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1110                 }
1111                 /*
1112                  * CLAWS: just don't allow return receipt request, even if the user
1113                  * may want to send an email. simple but foolproof.
1114                  */
1115                 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE); 
1116         }
1117         compose_add_field_list( compose, listAddress );
1118
1119         if (item && item->prefs && item->prefs->compose_with_format) {
1120                 subject_format = item->prefs->compose_subject_format;
1121                 body_format = item->prefs->compose_body_format;
1122         } else if (account->compose_with_format) {
1123                 subject_format = account->compose_subject_format;
1124                 body_format = account->compose_body_format;
1125         } else if (prefs_common.compose_with_format) {
1126                 subject_format = prefs_common.compose_subject_format;
1127                 body_format = prefs_common.compose_body_format;
1128         }
1129
1130         if (subject_format || body_format) {
1131
1132                 if ( subject_format
1133                          && *subject_format != '\0' )
1134                 {
1135                         gchar *subject = NULL;
1136                         gchar *tmp = NULL;
1137                         gchar *buf = NULL;
1138
1139                         if (!dummyinfo)
1140                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1141
1142                         /* decode \-escape sequences in the internal representation of the quote format */
1143                         tmp = malloc(strlen(subject_format)+1);
1144                         pref_get_unescaped_pref(tmp, subject_format);
1145
1146                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1147 #ifdef USE_ASPELL
1148                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1149                                         compose->gtkaspell);
1150 #else
1151                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1152 #endif
1153                         quote_fmt_scan_string(tmp);
1154                         quote_fmt_parse();
1155
1156                         buf = quote_fmt_get_buffer();
1157                         if (buf == NULL)
1158                                 alertpanel_error(_("New message subject format error."));
1159                         else
1160                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1161                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1162                         quote_fmt_reset_vartable();
1163
1164                         g_free(subject);
1165                         g_free(tmp);
1166                 }
1167
1168                 if ( body_format
1169                          && *body_format != '\0' )
1170                 {
1171                         GtkTextView *text;
1172                         GtkTextBuffer *buffer;
1173                         GtkTextIter start, end;
1174                         gchar *tmp = NULL;
1175
1176                         if (!dummyinfo)
1177                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1178
1179                         text = GTK_TEXT_VIEW(compose->text);
1180                         buffer = gtk_text_view_get_buffer(text);
1181                         gtk_text_buffer_get_start_iter(buffer, &start);
1182                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1183                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1184
1185                         compose_quote_fmt(compose, dummyinfo,
1186                                           body_format,
1187                                           NULL, tmp, FALSE, TRUE,
1188                                                   _("New message body format error at line %d."));
1189                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1190                         quote_fmt_reset_vartable();
1191
1192                         g_free(tmp);
1193                 }
1194
1195         }
1196         procmsg_msginfo_free( dummyinfo );
1197
1198         if (attach_files) {
1199                 gint i;
1200                 gchar *file;
1201
1202                 for (i = 0; i < attach_files->len; i++) {
1203                         file = g_ptr_array_index(attach_files, i);
1204                         compose_attach_append(compose, file, file, NULL);
1205                 }
1206         }
1207
1208         compose_show_first_last_header(compose, TRUE);
1209
1210         /* Set save folder */
1211         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212                 gchar *folderidentifier;
1213
1214                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1215                 folderidentifier = folder_item_get_identifier(item);
1216                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1217                 g_free(folderidentifier);
1218         }
1219         
1220         gtk_widget_grab_focus(compose->header_last->entry);
1221
1222         undo_unblock(compose->undostruct);
1223
1224         if (prefs_common.auto_exteditor)
1225                 compose_exec_ext_editor(compose);
1226
1227         compose->draft_timeout_tag = -1;
1228         SCROLL_TO_CURSOR(compose);
1229
1230         compose->modified = FALSE;
1231         compose_set_title(compose);
1232         return compose;
1233 }
1234
1235 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1236                 gboolean override_pref)
1237 {
1238         gchar *privacy = NULL;
1239
1240         g_return_if_fail(compose != NULL);
1241         g_return_if_fail(account != NULL);
1242
1243         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1244                 return;
1245
1246         if (account->default_privacy_system
1247         &&  strlen(account->default_privacy_system)) {
1248                 privacy = account->default_privacy_system;
1249         } else {
1250                 GSList *privacy_avail = privacy_get_system_ids();
1251                 if (privacy_avail && g_slist_length(privacy_avail)) {
1252                         privacy = (gchar *)(privacy_avail->data);
1253                 }
1254         }
1255         if (privacy != NULL) {
1256                 if (compose->privacy_system == NULL)
1257                         compose->privacy_system = g_strdup(privacy);
1258                 else if (*(compose->privacy_system) == '\0') {
1259                         g_free(compose->privacy_system);
1260                         compose->privacy_system = g_strdup(privacy);
1261                 }
1262                 compose_update_privacy_system_menu_item(compose, FALSE);
1263                 compose_use_encryption(compose, TRUE);
1264         }
1265 }       
1266
1267 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1268 {
1269         gchar *privacy = NULL;
1270
1271         if (account->default_privacy_system
1272         &&  strlen(account->default_privacy_system)) {
1273                 privacy = account->default_privacy_system;
1274         } else {
1275                 GSList *privacy_avail = privacy_get_system_ids();
1276                 if (privacy_avail && g_slist_length(privacy_avail)) {
1277                         privacy = (gchar *)(privacy_avail->data);
1278                 }
1279         }
1280         if (privacy != NULL) {
1281                 if (compose->privacy_system == NULL)
1282                         compose->privacy_system = g_strdup(privacy);
1283                 compose_update_privacy_system_menu_item(compose, FALSE);
1284                 compose_use_signing(compose, TRUE);
1285         }
1286 }       
1287
1288 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1289 {
1290         MsgInfo *msginfo;
1291         guint list_len;
1292         Compose *compose = NULL;
1293         GtkItemFactory *ifactory = NULL;
1294         
1295         g_return_val_if_fail(msginfo_list != NULL, NULL);
1296
1297         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1298         g_return_val_if_fail(msginfo != NULL, NULL);
1299
1300         list_len = g_slist_length(msginfo_list);
1301
1302         switch (mode) {
1303         case COMPOSE_REPLY:
1304                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1305                               FALSE, prefs_common.default_reply_list, FALSE, body);
1306                 break;
1307         case COMPOSE_REPLY_WITH_QUOTE:
1308                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1309                         FALSE, prefs_common.default_reply_list, FALSE, body);
1310                 break;
1311         case COMPOSE_REPLY_WITHOUT_QUOTE:
1312                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1313                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1314                 break;
1315         case COMPOSE_REPLY_TO_SENDER:
1316                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1317                               FALSE, FALSE, TRUE, body);
1318                 break;
1319         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1320                 compose = compose_followup_and_reply_to(msginfo,
1321                                               COMPOSE_QUOTE_CHECK,
1322                                               FALSE, FALSE, body);
1323                 break;
1324         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1325                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1326                         FALSE, FALSE, TRUE, body);
1327                 break;
1328         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1329                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1330                         FALSE, FALSE, TRUE, NULL);
1331                 break;
1332         case COMPOSE_REPLY_TO_ALL:
1333                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1334                         TRUE, FALSE, FALSE, body);
1335                 break;
1336         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1337                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1338                         TRUE, FALSE, FALSE, body);
1339                 break;
1340         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1341                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1342                         TRUE, FALSE, FALSE, NULL);
1343                 break;
1344         case COMPOSE_REPLY_TO_LIST:
1345                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1346                         FALSE, TRUE, FALSE, body);
1347                 break;
1348         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1349                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1350                         FALSE, TRUE, FALSE, body);
1351                 break;
1352         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1353                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1354                         FALSE, TRUE, FALSE, NULL);
1355                 break;
1356         case COMPOSE_FORWARD:
1357                 if (prefs_common.forward_as_attachment) {
1358                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1359                         return compose;
1360                 } else {
1361                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1362                         return compose;
1363                 }
1364                 break;
1365         case COMPOSE_FORWARD_INLINE:
1366                 /* check if we reply to more than one Message */
1367                 if (list_len == 1) {
1368                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1369                         break;
1370                 } 
1371                 /* more messages FALL THROUGH */
1372         case COMPOSE_FORWARD_AS_ATTACH:
1373                 compose = compose_forward_multiple(NULL, msginfo_list);
1374                 break;
1375         case COMPOSE_REDIRECT:
1376                 compose = compose_redirect(NULL, msginfo, FALSE);
1377                 break;
1378         default:
1379                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1380         }
1381         
1382         if (compose == NULL) {
1383                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1384                 return NULL;
1385         }
1386         ifactory = gtk_item_factory_from_widget(compose->menubar);
1387
1388         compose->rmode = mode;
1389         switch (compose->rmode) {
1390         case COMPOSE_REPLY:
1391         case COMPOSE_REPLY_WITH_QUOTE:
1392         case COMPOSE_REPLY_WITHOUT_QUOTE:
1393         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1394                 debug_print("reply mode Normal\n");
1395                 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1396                 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1397                 break;
1398         case COMPOSE_REPLY_TO_SENDER:
1399         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1400         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1401                 debug_print("reply mode Sender\n");
1402                 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1403                 break;
1404         case COMPOSE_REPLY_TO_ALL:
1405         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1406         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1407                 debug_print("reply mode All\n");
1408                 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1409                 break;
1410         case COMPOSE_REPLY_TO_LIST:
1411         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1412         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1413                 debug_print("reply mode List\n");
1414                 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1415                 break;
1416         default:
1417                 break;
1418         }
1419         return compose;
1420 }
1421
1422 static Compose *compose_reply(MsgInfo *msginfo,
1423                                    ComposeQuoteMode quote_mode,
1424                                    gboolean to_all,
1425                                    gboolean to_ml,
1426                                    gboolean to_sender, 
1427                    const gchar *body)
1428 {
1429         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1430                               to_sender, FALSE, body);
1431 }
1432
1433 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1434                                    ComposeQuoteMode quote_mode,
1435                                    gboolean to_all,
1436                                    gboolean to_sender,
1437                                    const gchar *body)
1438 {
1439         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1440                               to_sender, TRUE, body);
1441 }
1442
1443 static void compose_extract_original_charset(Compose *compose)
1444 {
1445         MsgInfo *info = NULL;
1446         if (compose->replyinfo) {
1447                 info = compose->replyinfo;
1448         } else if (compose->fwdinfo) {
1449                 info = compose->fwdinfo;
1450         } else if (compose->targetinfo) {
1451                 info = compose->targetinfo;
1452         }
1453         if (info) {
1454                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1455                 MimeInfo *partinfo = mimeinfo;
1456                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1457                         partinfo = procmime_mimeinfo_next(partinfo);
1458                 if (partinfo) {
1459                         compose->orig_charset = 
1460                                 g_strdup(procmime_mimeinfo_get_parameter(
1461                                                 partinfo, "charset"));
1462                 }
1463                 procmime_mimeinfo_free_all(mimeinfo);
1464         }
1465 }
1466
1467 #define SIGNAL_BLOCK(buffer) {                                  \
1468         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1469                                 G_CALLBACK(compose_changed_cb), \
1470                                 compose);                       \
1471         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1472                                 G_CALLBACK(text_inserted),      \
1473                                 compose);                       \
1474 }
1475
1476 #define SIGNAL_UNBLOCK(buffer) {                                \
1477         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1478                                 G_CALLBACK(compose_changed_cb), \
1479                                 compose);                       \
1480         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1481                                 G_CALLBACK(text_inserted),      \
1482                                 compose);                       \
1483 }
1484
1485 static Compose *compose_generic_reply(MsgInfo *msginfo,
1486                                   ComposeQuoteMode quote_mode,
1487                                   gboolean to_all, gboolean to_ml,
1488                                   gboolean to_sender,
1489                                   gboolean followup_and_reply_to,
1490                                   const gchar *body)
1491 {
1492         GtkItemFactory *ifactory;
1493         Compose *compose;
1494         PrefsAccount *account = NULL;
1495         GtkTextView *textview;
1496         GtkTextBuffer *textbuf;
1497         gboolean quote = FALSE;
1498         const gchar *qmark = NULL;
1499         const gchar *body_fmt = NULL;
1500         START_TIMING("");
1501         g_return_val_if_fail(msginfo != NULL, NULL);
1502         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1503
1504         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1505
1506         g_return_val_if_fail(account != NULL, NULL);
1507
1508         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1509
1510         compose->updating = TRUE;
1511
1512         ifactory = gtk_item_factory_from_widget(compose->menubar);
1513
1514         menu_set_active(ifactory, "/Options/Remove references", FALSE);
1515         menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1516
1517         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1518         if (!compose->replyinfo)
1519                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1520
1521         compose_extract_original_charset(compose);
1522         
1523         if (msginfo->folder && msginfo->folder->ret_rcpt)
1524                 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1525
1526         /* Set save folder */
1527         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1528                 gchar *folderidentifier;
1529
1530                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1531                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1532                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1533                 g_free(folderidentifier);
1534         }
1535
1536         if (compose_parse_header(compose, msginfo) < 0) {
1537                 compose->updating = FALSE;
1538                 compose_destroy(compose);
1539                 return NULL;
1540         }
1541
1542         /* override from name according to folder properties */
1543         if (msginfo->folder && msginfo->folder->prefs &&
1544                 msginfo->folder->prefs->reply_with_format &&
1545                 msginfo->folder->prefs->reply_override_from_format &&
1546                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1547
1548                 gchar *tmp = NULL;
1549                 gchar *buf = NULL;
1550
1551                 /* decode \-escape sequences in the internal representation of the quote format */
1552                 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1553                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1554
1555 #ifdef USE_ASPELL
1556                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1557                                 compose->gtkaspell);
1558 #else
1559                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1560 #endif
1561                 quote_fmt_scan_string(tmp);
1562                 quote_fmt_parse();
1563
1564                 buf = quote_fmt_get_buffer();
1565                 if (buf == NULL)
1566                         alertpanel_error(_("Message reply From format error."));
1567                 else
1568                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1569                 quote_fmt_reset_vartable();
1570
1571                 g_free(tmp);
1572         }
1573
1574         textview = (GTK_TEXT_VIEW(compose->text));
1575         textbuf = gtk_text_view_get_buffer(textview);
1576         compose_create_tags(textview, compose);
1577
1578         undo_block(compose->undostruct);
1579 #ifdef USE_ASPELL
1580                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1581 #endif
1582
1583         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1584                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1585                 /* use the reply format of folder (if enabled), or the account's one
1586                    (if enabled) or fallback to the global reply format, which is always
1587                    enabled (even if empty), and use the relevant quotemark */
1588                 quote = TRUE;
1589                 if (msginfo->folder && msginfo->folder->prefs &&
1590                                 msginfo->folder->prefs->reply_with_format) {
1591                         qmark = msginfo->folder->prefs->reply_quotemark;
1592                         body_fmt = msginfo->folder->prefs->reply_body_format;
1593
1594                 } else if (account->reply_with_format) {
1595                         qmark = account->reply_quotemark;
1596                         body_fmt = account->reply_body_format;
1597
1598                 } else {
1599                         qmark = prefs_common.quotemark;
1600                         body_fmt = prefs_common.quotefmt;
1601                 }
1602         }
1603
1604         if (quote) {
1605                 /* empty quotemark is not allowed */
1606                 if (qmark == NULL || *qmark == '\0')
1607                         qmark = "> ";
1608                 compose_quote_fmt(compose, compose->replyinfo,
1609                                   body_fmt, qmark, body, FALSE, TRUE,
1610                                           _("Message reply format error at line %d."));
1611                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1612                 quote_fmt_reset_vartable();
1613         }
1614
1615         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1616                 compose_force_encryption(compose, account, FALSE);
1617         }
1618
1619         SIGNAL_BLOCK(textbuf);
1620         
1621         if (account->auto_sig)
1622                 compose_insert_sig(compose, FALSE);
1623
1624         compose_wrap_all(compose);
1625
1626         SIGNAL_UNBLOCK(textbuf);
1627         
1628         gtk_widget_grab_focus(compose->text);
1629
1630         undo_unblock(compose->undostruct);
1631
1632         if (prefs_common.auto_exteditor)
1633                 compose_exec_ext_editor(compose);
1634                 
1635         compose->modified = FALSE;
1636         compose_set_title(compose);
1637
1638         compose->updating = FALSE;
1639         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1640         SCROLL_TO_CURSOR(compose);
1641         
1642         if (compose->deferred_destroy) {
1643                 compose_destroy(compose);
1644                 return NULL;
1645         }
1646         END_TIMING();
1647         return compose;
1648 }
1649
1650 #define INSERT_FW_HEADER(var, hdr) \
1651 if (msginfo->var && *msginfo->var) { \
1652         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1653         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1654         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1655 }
1656
1657 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1658                          gboolean as_attach, const gchar *body,
1659                          gboolean no_extedit,
1660                          gboolean batch)
1661 {
1662         Compose *compose;
1663         GtkTextView *textview;
1664         GtkTextBuffer *textbuf;
1665         GtkTextIter iter;
1666
1667         g_return_val_if_fail(msginfo != NULL, NULL);
1668         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1669
1670         if (!account && 
1671             !(account = compose_guess_forward_account_from_msginfo
1672                                 (msginfo)))
1673                 account = cur_account;
1674
1675         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1676
1677         compose->updating = TRUE;
1678         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1679         if (!compose->fwdinfo)
1680                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1681
1682         compose_extract_original_charset(compose);
1683
1684         if (msginfo->subject && *msginfo->subject) {
1685                 gchar *buf, *buf2, *p;
1686
1687                 buf = p = g_strdup(msginfo->subject);
1688                 p += subject_get_prefix_length(p);
1689                 memmove(buf, p, strlen(p) + 1);
1690
1691                 buf2 = g_strdup_printf("Fw: %s", buf);
1692                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1693                 
1694                 g_free(buf);
1695                 g_free(buf2);
1696         }
1697
1698         /* override from name according to folder properties */
1699         if (msginfo->folder && msginfo->folder->prefs &&
1700                 msginfo->folder->prefs->forward_with_format &&
1701                 msginfo->folder->prefs->forward_override_from_format &&
1702                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1703
1704                 gchar *tmp = NULL;
1705                 gchar *buf = NULL;
1706                 MsgInfo *full_msginfo = NULL;
1707
1708                 if (!as_attach)
1709                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1710                 if (!full_msginfo)
1711                         full_msginfo = procmsg_msginfo_copy(msginfo);
1712
1713                 /* decode \-escape sequences in the internal representation of the quote format */
1714                 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1715                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1716
1717 #ifdef USE_ASPELL
1718                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1719                                 compose->gtkaspell);
1720 #else
1721                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1722 #endif
1723                 quote_fmt_scan_string(tmp);
1724                 quote_fmt_parse();
1725
1726                 buf = quote_fmt_get_buffer();
1727                 if (buf == NULL)
1728                         alertpanel_error(_("Message forward From format error."));
1729                 else
1730                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1731                 quote_fmt_reset_vartable();
1732
1733                 g_free(tmp);
1734                 procmsg_msginfo_free(full_msginfo);
1735         }
1736
1737         textview = GTK_TEXT_VIEW(compose->text);
1738         textbuf = gtk_text_view_get_buffer(textview);
1739         compose_create_tags(textview, compose);
1740         
1741         undo_block(compose->undostruct);
1742         if (as_attach) {
1743                 gchar *msgfile;
1744
1745                 msgfile = procmsg_get_message_file(msginfo);
1746                 if (!is_file_exist(msgfile))
1747                         g_warning("%s: file not exist\n", msgfile);
1748                 else
1749                         compose_attach_append(compose, msgfile, msgfile,
1750                                               "message/rfc822");
1751
1752                 g_free(msgfile);
1753         } else {
1754                 const gchar *qmark = NULL;
1755                 const gchar *body_fmt = prefs_common.fw_quotefmt;
1756                 MsgInfo *full_msginfo;
1757
1758                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1759                 if (!full_msginfo)
1760                         full_msginfo = procmsg_msginfo_copy(msginfo);
1761
1762                 /* use the forward format of folder (if enabled), or the account's one
1763                    (if enabled) or fallback to the global forward format, which is always
1764                    enabled (even if empty), and use the relevant quotemark */
1765                 if (msginfo->folder && msginfo->folder->prefs &&
1766                                 msginfo->folder->prefs->forward_with_format) {
1767                         qmark = msginfo->folder->prefs->forward_quotemark;
1768                         body_fmt = msginfo->folder->prefs->forward_body_format;
1769
1770                 } else if (account->forward_with_format) {
1771                         qmark = account->forward_quotemark;
1772                         body_fmt = account->forward_body_format;
1773
1774                 } else {
1775                         qmark = prefs_common.fw_quotemark;
1776                         body_fmt = prefs_common.fw_quotefmt;
1777                 }
1778
1779                 /* empty quotemark is not allowed */
1780                 if (qmark == NULL || *qmark == '\0')
1781                         qmark = "> ";
1782
1783                 compose_quote_fmt(compose, full_msginfo,
1784                                   body_fmt, qmark, body, FALSE, TRUE,
1785                                           _("Message forward format error at line %d."));
1786                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1787                 quote_fmt_reset_vartable();
1788                 compose_attach_parts(compose, msginfo);
1789
1790                 procmsg_msginfo_free(full_msginfo);
1791         }
1792
1793         SIGNAL_BLOCK(textbuf);
1794
1795         if (account->auto_sig)
1796                 compose_insert_sig(compose, FALSE);
1797
1798         compose_wrap_all(compose);
1799
1800         SIGNAL_UNBLOCK(textbuf);
1801         
1802         gtk_text_buffer_get_start_iter(textbuf, &iter);
1803         gtk_text_buffer_place_cursor(textbuf, &iter);
1804
1805         gtk_widget_grab_focus(compose->header_last->entry);
1806
1807         if (!no_extedit && prefs_common.auto_exteditor)
1808                 compose_exec_ext_editor(compose);
1809         
1810         /*save folder*/
1811         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1812                 gchar *folderidentifier;
1813
1814                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1815                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1816                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1817                 g_free(folderidentifier);
1818         }
1819
1820         undo_unblock(compose->undostruct);
1821         
1822         compose->modified = FALSE;
1823         compose_set_title(compose);
1824
1825         compose->updating = FALSE;
1826         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1827         SCROLL_TO_CURSOR(compose);
1828
1829         if (compose->deferred_destroy) {
1830                 compose_destroy(compose);
1831                 return NULL;
1832         }
1833
1834         return compose;
1835 }
1836
1837 #undef INSERT_FW_HEADER
1838
1839 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1840 {
1841         Compose *compose;
1842         GtkTextView *textview;
1843         GtkTextBuffer *textbuf;
1844         GtkTextIter iter;
1845         GSList *msginfo;
1846         gchar *msgfile;
1847         gboolean single_mail = TRUE;
1848         
1849         g_return_val_if_fail(msginfo_list != NULL, NULL);
1850
1851         if (g_slist_length(msginfo_list) > 1)
1852                 single_mail = FALSE;
1853
1854         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1855                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1856                         return NULL;
1857
1858         /* guess account from first selected message */
1859         if (!account && 
1860             !(account = compose_guess_forward_account_from_msginfo
1861                                 (msginfo_list->data)))
1862                 account = cur_account;
1863
1864         g_return_val_if_fail(account != NULL, NULL);
1865
1866         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1867                 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1868                 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1869         }
1870
1871         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1872
1873         compose->updating = TRUE;
1874
1875         /* override from name according to folder properties */
1876         if (msginfo_list->data) {
1877                 MsgInfo *msginfo = msginfo_list->data;
1878
1879                 if (msginfo->folder && msginfo->folder->prefs &&
1880                         msginfo->folder->prefs->forward_with_format &&
1881                         msginfo->folder->prefs->forward_override_from_format &&
1882                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1883
1884                         gchar *tmp = NULL;
1885                         gchar *buf = NULL;
1886
1887                         /* decode \-escape sequences in the internal representation of the quote format */
1888                         tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1889                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1890
1891 #ifdef USE_ASPELL
1892                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1893                                         compose->gtkaspell);
1894 #else
1895                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1896 #endif
1897                         quote_fmt_scan_string(tmp);
1898                         quote_fmt_parse();
1899
1900                         buf = quote_fmt_get_buffer();
1901                         if (buf == NULL)
1902                                 alertpanel_error(_("Message forward From format error."));
1903                         else
1904                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1905                         quote_fmt_reset_vartable();
1906
1907                         g_free(tmp);
1908                 }
1909         }
1910
1911         textview = GTK_TEXT_VIEW(compose->text);
1912         textbuf = gtk_text_view_get_buffer(textview);
1913         compose_create_tags(textview, compose);
1914         
1915         undo_block(compose->undostruct);
1916         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1917                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1918
1919                 if (!is_file_exist(msgfile))
1920                         g_warning("%s: file not exist\n", msgfile);
1921                 else
1922                         compose_attach_append(compose, msgfile, msgfile,
1923                                 "message/rfc822");
1924                 g_free(msgfile);
1925         }
1926         
1927         if (single_mail) {
1928                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1929                 if (info->subject && *info->subject) {
1930                         gchar *buf, *buf2, *p;
1931
1932                         buf = p = g_strdup(info->subject);
1933                         p += subject_get_prefix_length(p);
1934                         memmove(buf, p, strlen(p) + 1);
1935
1936                         buf2 = g_strdup_printf("Fw: %s", buf);
1937                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1938
1939                         g_free(buf);
1940                         g_free(buf2);
1941                 }
1942         } else {
1943                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1944                         _("Fw: multiple emails"));
1945         }
1946
1947         SIGNAL_BLOCK(textbuf);
1948         
1949         if (account->auto_sig)
1950                 compose_insert_sig(compose, FALSE);
1951
1952         compose_wrap_all(compose);
1953
1954         SIGNAL_UNBLOCK(textbuf);
1955         
1956         gtk_text_buffer_get_start_iter(textbuf, &iter);
1957         gtk_text_buffer_place_cursor(textbuf, &iter);
1958
1959         gtk_widget_grab_focus(compose->header_last->entry);
1960         undo_unblock(compose->undostruct);
1961         compose->modified = FALSE;
1962         compose_set_title(compose);
1963
1964         compose->updating = FALSE;
1965         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1966         SCROLL_TO_CURSOR(compose);
1967
1968         if (compose->deferred_destroy) {
1969                 compose_destroy(compose);
1970                 return NULL;
1971         }
1972
1973         return compose;
1974 }
1975
1976 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
1977 {
1978         GtkTextIter start = *iter;
1979         GtkTextIter end_iter;
1980         int start_pos = gtk_text_iter_get_offset(&start);
1981         gchar *str = NULL;
1982         if (!compose->account->sig_sep)
1983                 return FALSE;
1984         
1985         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1986                 start_pos+strlen(compose->account->sig_sep));
1987
1988         /* check sig separator */
1989         str = gtk_text_iter_get_text(&start, &end_iter);
1990         if (!strcmp(str, compose->account->sig_sep)) {
1991                 gchar *tmp = NULL;
1992                 /* check end of line (\n) */
1993                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1994                         start_pos+strlen(compose->account->sig_sep));
1995                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1996                         start_pos+strlen(compose->account->sig_sep)+1);
1997                 tmp = gtk_text_iter_get_text(&start, &end_iter);
1998                 if (!strcmp(tmp,"\n")) {
1999                         g_free(str);
2000                         g_free(tmp);
2001                         return TRUE;
2002                 }
2003                 g_free(tmp);    
2004         }
2005         g_free(str);
2006
2007         return FALSE;
2008 }
2009
2010 static void compose_colorize_signature(Compose *compose)
2011 {
2012         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2013         GtkTextIter iter;
2014         GtkTextIter end_iter;
2015         gtk_text_buffer_get_start_iter(buffer, &iter);
2016         while (gtk_text_iter_forward_line(&iter))
2017                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2018                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2019                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2020                 }
2021 }
2022
2023 #define BLOCK_WRAP() {                                                  \
2024         prev_autowrap = compose->autowrap;                              \
2025         buffer = gtk_text_view_get_buffer(                              \
2026                                         GTK_TEXT_VIEW(compose->text));  \
2027         compose->autowrap = FALSE;                                      \
2028                                                                         \
2029         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2030                                 G_CALLBACK(compose_changed_cb),         \
2031                                 compose);                               \
2032         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2033                                 G_CALLBACK(text_inserted),              \
2034                                 compose);                               \
2035 }
2036 #define UNBLOCK_WRAP() {                                                \
2037         compose->autowrap = prev_autowrap;                              \
2038         if (compose->autowrap) {                                        \
2039                 gint old = compose->draft_timeout_tag;                  \
2040                 compose->draft_timeout_tag = -2;                        \
2041                 compose_wrap_all(compose);                              \
2042                 compose->draft_timeout_tag = old;                       \
2043         }                                                               \
2044                                                                         \
2045         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2046                                 G_CALLBACK(compose_changed_cb),         \
2047                                 compose);                               \
2048         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2049                                 G_CALLBACK(text_inserted),              \
2050                                 compose);                               \
2051 }
2052
2053 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2054 {
2055         Compose *compose = NULL;
2056         PrefsAccount *account = NULL;
2057         GtkTextView *textview;
2058         GtkTextBuffer *textbuf;
2059         GtkTextMark *mark;
2060         GtkTextIter iter;
2061         FILE *fp;
2062         gchar buf[BUFFSIZE];
2063         gboolean use_signing = FALSE;
2064         gboolean use_encryption = FALSE;
2065         gchar *privacy_system = NULL;
2066         int priority = PRIORITY_NORMAL;
2067         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2068
2069         g_return_val_if_fail(msginfo != NULL, NULL);
2070         g_return_val_if_fail(msginfo->folder != NULL, NULL);
2071
2072         if (compose_put_existing_to_front(msginfo)) {
2073                 return NULL;
2074         }
2075
2076         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2077             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2078                 gchar queueheader_buf[BUFFSIZE];
2079                 gint id, param;
2080
2081                 /* Select Account from queue headers */
2082                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2083                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2084                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2085                         account = account_find_from_id(id);
2086                 }
2087                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2088                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2089                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2090                         account = account_find_from_id(id);
2091                 }
2092                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2093                                              sizeof(queueheader_buf), "NAID:")) {
2094                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2095                         account = account_find_from_id(id);
2096                 }
2097                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2098                                                     sizeof(queueheader_buf), "MAID:")) {
2099                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2100                         account = account_find_from_id(id);
2101                 }
2102                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2103                                                                 sizeof(queueheader_buf), "S:")) {
2104                         account = account_find_from_address(queueheader_buf, FALSE);
2105                 }
2106                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2107                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2108                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2109                         use_signing = param;
2110                         
2111                 }
2112                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2113                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2114                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2115                         use_signing = param;
2116                         
2117                 }
2118                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2119                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2120                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2121                         use_encryption = param;
2122                 }
2123                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2124                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2125                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2126                         use_encryption = param;
2127                 }
2128                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2129                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2130                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2131                 }
2132                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2133                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2134                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2135                 }
2136                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2137                                              sizeof(queueheader_buf), "X-Priority: ")) {
2138                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2139                         priority = param;
2140                 }
2141                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2142                                              sizeof(queueheader_buf), "RMID:")) {
2143                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2144                         if (tokens[0] && tokens[1] && tokens[2]) {
2145                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2146                                 if (orig_item != NULL) {
2147                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2148                                 }
2149                         }
2150                         g_strfreev(tokens);
2151                 }
2152                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2153                                              sizeof(queueheader_buf), "FMID:")) {
2154                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2155                         if (tokens[0] && tokens[1] && tokens[2]) {
2156                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2157                                 if (orig_item != NULL) {
2158                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2159                                 }
2160                         }
2161                         g_strfreev(tokens);
2162                 }
2163         } else {
2164                 account = msginfo->folder->folder->account;
2165         }
2166
2167         if (!account && prefs_common.reedit_account_autosel) {
2168                 gchar from[BUFFSIZE];
2169                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2170                         extract_address(from);
2171                         account = account_find_from_address(from, FALSE);
2172                 }
2173         }
2174         if (!account) {
2175                 account = cur_account;
2176         }
2177         g_return_val_if_fail(account != NULL, NULL);
2178
2179         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2180         
2181         compose->replyinfo = replyinfo;
2182         compose->fwdinfo = fwdinfo;
2183
2184         compose->updating = TRUE;
2185         compose->priority = priority;
2186
2187         if (privacy_system != NULL) {
2188                 compose->privacy_system = privacy_system;
2189                 compose_use_signing(compose, use_signing);
2190                 compose_use_encryption(compose, use_encryption);
2191                 compose_update_privacy_system_menu_item(compose, FALSE);
2192         } else {
2193                 activate_privacy_system(compose, account, FALSE);
2194         }
2195
2196         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2197
2198         compose_extract_original_charset(compose);
2199
2200         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2201             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2202                 gchar queueheader_buf[BUFFSIZE];
2203
2204                 /* Set message save folder */
2205                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2206                         gint startpos = 0;
2207
2208                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2209                         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2210                         gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2211                 }
2212                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2213                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2214                         if (active) {
2215                                 GtkItemFactory *ifactory;
2216                                 ifactory = gtk_item_factory_from_widget(compose->menubar);
2217                                 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2218                         }
2219                 }
2220         }
2221         
2222         if (compose_parse_header(compose, msginfo) < 0) {
2223                 compose->updating = FALSE;
2224                 compose_destroy(compose);
2225                 return NULL;
2226         }
2227         compose_reedit_set_entry(compose, msginfo);
2228
2229         textview = GTK_TEXT_VIEW(compose->text);
2230         textbuf = gtk_text_view_get_buffer(textview);
2231         compose_create_tags(textview, compose);
2232
2233         mark = gtk_text_buffer_get_insert(textbuf);
2234         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2235
2236         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2237                                         G_CALLBACK(compose_changed_cb),
2238                                         compose);
2239         
2240         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2241                 fp = procmime_get_first_encrypted_text_content(msginfo);
2242                 if (fp) {
2243                         compose_force_encryption(compose, account, TRUE);
2244                 }
2245         } else {
2246                 fp = procmime_get_first_text_content(msginfo);
2247         }
2248         if (fp == NULL) {
2249                 g_warning("Can't get text part\n");
2250         }
2251
2252         if (fp != NULL) {
2253                 gboolean prev_autowrap = compose->autowrap;
2254                 GtkTextBuffer *buffer = textbuf;
2255                 BLOCK_WRAP();
2256                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2257                         strcrchomp(buf);
2258                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2259                 }
2260                 UNBLOCK_WRAP();
2261                 fclose(fp);
2262         }
2263         
2264         compose_attach_parts(compose, msginfo);
2265
2266         compose_colorize_signature(compose);
2267
2268         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2269                                         G_CALLBACK(compose_changed_cb),
2270                                         compose);
2271
2272         gtk_widget_grab_focus(compose->text);
2273
2274         if (prefs_common.auto_exteditor) {
2275                 compose_exec_ext_editor(compose);
2276         }
2277         compose->modified = FALSE;
2278         compose_set_title(compose);
2279
2280         compose->updating = FALSE;
2281         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2282         SCROLL_TO_CURSOR(compose);
2283
2284         if (compose->deferred_destroy) {
2285                 compose_destroy(compose);
2286                 return NULL;
2287         }
2288         
2289         compose->sig_str = compose_get_signature_str(compose);
2290         
2291         return compose;
2292 }
2293
2294 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2295                                                  gboolean batch)
2296 {
2297         Compose *compose;
2298         gchar *filename;
2299         GtkItemFactory *ifactory;
2300         FolderItem *item;
2301
2302         g_return_val_if_fail(msginfo != NULL, NULL);
2303
2304         if (!account)
2305                 account = account_get_reply_account(msginfo,
2306                                         prefs_common.reply_account_autosel);
2307         g_return_val_if_fail(account != NULL, NULL);
2308
2309         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2310
2311         compose->updating = TRUE;
2312
2313         ifactory = gtk_item_factory_from_widget(compose->menubar);
2314         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2315         compose->replyinfo = NULL;
2316         compose->fwdinfo = NULL;
2317
2318         compose_show_first_last_header(compose, TRUE);
2319
2320         gtk_widget_grab_focus(compose->header_last->entry);
2321
2322         filename = procmsg_get_message_file(msginfo);
2323
2324         if (filename == NULL) {
2325                 compose->updating = FALSE;
2326                 compose_destroy(compose);
2327
2328                 return NULL;
2329         }
2330
2331         compose->redirect_filename = filename;
2332         
2333         /* Set save folder */
2334         item = msginfo->folder;
2335         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2336                 gchar *folderidentifier;
2337
2338                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2339                 folderidentifier = folder_item_get_identifier(item);
2340                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2341                 g_free(folderidentifier);
2342         }
2343
2344         compose_attach_parts(compose, msginfo);
2345
2346         if (msginfo->subject)
2347                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2348                                    msginfo->subject);
2349         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2350
2351         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2352                                           _("Message redirect format error at line %d."));
2353         quote_fmt_reset_vartable();
2354         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2355
2356         compose_colorize_signature(compose);
2357
2358         ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2359         menu_set_sensitive(ifactory, "/Add...", FALSE);
2360         menu_set_sensitive(ifactory, "/Remove", FALSE);
2361         menu_set_sensitive(ifactory, "/Properties...", FALSE);
2362
2363         ifactory = gtk_item_factory_from_widget(compose->menubar);
2364         menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2365         menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2366         menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2367         menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2368         menu_set_sensitive(ifactory, "/Edit", FALSE);
2369         menu_set_sensitive(ifactory, "/Options", FALSE);
2370         menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2371         menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2372         
2373         if (compose->toolbar->draft_btn)
2374                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2375         if (compose->toolbar->insert_btn)
2376                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2377         if (compose->toolbar->attach_btn)
2378                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2379         if (compose->toolbar->sig_btn)
2380                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2381         if (compose->toolbar->exteditor_btn)
2382                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2383         if (compose->toolbar->linewrap_current_btn)
2384                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2385         if (compose->toolbar->linewrap_all_btn)
2386                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2387
2388         compose->modified = FALSE;
2389         compose_set_title(compose);
2390         compose->updating = FALSE;
2391         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2392         SCROLL_TO_CURSOR(compose);
2393
2394         if (compose->deferred_destroy) {
2395                 compose_destroy(compose);
2396                 return NULL;
2397         }
2398         
2399         return compose;
2400 }
2401
2402 GList *compose_get_compose_list(void)
2403 {
2404         return compose_list;
2405 }
2406
2407 void compose_entry_append(Compose *compose, const gchar *address,
2408                           ComposeEntryType type)
2409 {
2410         const gchar *header;
2411         gchar *cur, *begin;
2412         gboolean in_quote = FALSE;
2413         if (!address || *address == '\0') return;
2414
2415         switch (type) {
2416         case COMPOSE_CC:
2417                 header = N_("Cc:");
2418                 break;
2419         case COMPOSE_BCC:
2420                 header = N_("Bcc:");
2421                 break;
2422         case COMPOSE_REPLYTO:
2423                 header = N_("Reply-To:");
2424                 break;
2425         case COMPOSE_NEWSGROUPS:
2426                 header = N_("Newsgroups:");
2427                 break;
2428         case COMPOSE_FOLLOWUPTO:
2429                 header = N_( "Followup-To:");
2430                 break;
2431         case COMPOSE_TO:
2432         default:
2433                 header = N_("To:");
2434                 break;
2435         }
2436         header = prefs_common_translated_header_name(header);
2437         
2438         cur = begin = (gchar *)address;
2439         
2440         /* we separate the line by commas, but not if we're inside a quoted
2441          * string */
2442         while (*cur != '\0') {
2443                 if (*cur == '"') 
2444                         in_quote = !in_quote;
2445                 if (*cur == ',' && !in_quote) {
2446                         gchar *tmp = g_strdup(begin);
2447                         gchar *o_tmp = tmp;
2448                         tmp[cur-begin]='\0';
2449                         cur++;
2450                         begin = cur;
2451                         while (*tmp == ' ' || *tmp == '\t')
2452                                 tmp++;
2453                         compose_add_header_entry(compose, header, tmp);
2454                         g_free(o_tmp);
2455                         continue;
2456                 }
2457                 cur++;
2458         }
2459         if (begin < cur) {
2460                 gchar *tmp = g_strdup(begin);
2461                 gchar *o_tmp = tmp;
2462                 tmp[cur-begin]='\0';
2463                 cur++;
2464                 begin = cur;
2465                 while (*tmp == ' ' || *tmp == '\t')
2466                         tmp++;
2467                 compose_add_header_entry(compose, header, tmp);
2468                 g_free(o_tmp);          
2469         }
2470 }
2471
2472 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2473 {
2474         static GdkColor yellow;
2475         static GdkColor black;
2476         static gboolean yellow_initialised = FALSE;
2477         GSList *h_list;
2478         GtkEntry *entry;
2479                 
2480         if (!yellow_initialised) {
2481                 gdk_color_parse("#f5f6be", &yellow);
2482                 gdk_color_parse("#000000", &black);
2483                 yellow_initialised = gdk_colormap_alloc_color(
2484                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2485                 yellow_initialised &= gdk_colormap_alloc_color(
2486                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2487         }
2488
2489         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2490                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2491                 if (gtk_entry_get_text(entry) && 
2492                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2493                         if (yellow_initialised) {
2494                                 gtk_widget_modify_base(
2495                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2496                                         GTK_STATE_NORMAL, &yellow);
2497                                 gtk_widget_modify_text(
2498                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2499                                         GTK_STATE_NORMAL, &black);
2500                         }
2501                 }
2502         }
2503 }
2504
2505 void compose_toolbar_cb(gint action, gpointer data)
2506 {
2507         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2508         Compose *compose = (Compose*)toolbar_item->parent;
2509         
2510         g_return_if_fail(compose != NULL);
2511
2512         switch(action) {
2513         case A_SEND:
2514                 compose_send_cb(compose, 0, NULL);
2515                 break;
2516         case A_SENDL:
2517                 compose_send_later_cb(compose, 0, NULL);
2518                 break;
2519         case A_DRAFT:
2520                 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2521                 break;
2522         case A_INSERT:
2523                 compose_insert_file_cb(compose, 0, NULL);
2524                 break;
2525         case A_ATTACH:
2526                 compose_attach_cb(compose, 0, NULL);
2527                 break;
2528         case A_SIG:
2529                 compose_insert_sig(compose, FALSE);
2530                 break;
2531         case A_EXTEDITOR:
2532                 compose_ext_editor_cb(compose, 0, NULL);
2533                 break;
2534         case A_LINEWRAP_CURRENT:
2535                 compose_beautify_paragraph(compose, NULL, TRUE);
2536                 break;
2537         case A_LINEWRAP_ALL:
2538                 compose_wrap_all_full(compose, TRUE);
2539                 break;
2540         case A_ADDRBOOK:
2541                 compose_address_cb(compose, 0, NULL);
2542                 break;
2543 #ifdef USE_ASPELL
2544         case A_CHECK_SPELLING:
2545                 compose_check_all(compose);
2546                 break;
2547 #endif
2548         default:
2549                 break;
2550         }
2551 }
2552
2553 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2554 {
2555         gchar *to = NULL;
2556         gchar *cc = NULL;
2557         gchar *bcc = NULL;
2558         gchar *subject = NULL;
2559         gchar *body = NULL;
2560         gchar *temp = NULL;
2561         gsize  len = 0;
2562         gchar **attach = NULL;
2563
2564         /* get mailto parts but skip from */
2565         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2566
2567         if (to)
2568                 compose_entry_append(compose, to, to_type);
2569         if (cc)
2570                 compose_entry_append(compose, cc, COMPOSE_CC);
2571         if (bcc)
2572                 compose_entry_append(compose, bcc, COMPOSE_BCC);
2573         if (subject) {
2574                 if (!g_utf8_validate (subject, -1, NULL)) {
2575                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2576                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2577                         g_free(temp);
2578                 } else {
2579                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2580                 }
2581         }
2582         if (body) {
2583                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2584                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2585                 GtkTextMark *mark;
2586                 GtkTextIter iter;
2587                 gboolean prev_autowrap = compose->autowrap;
2588
2589                 compose->autowrap = FALSE;
2590
2591                 mark = gtk_text_buffer_get_insert(buffer);
2592                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2593
2594                 if (!g_utf8_validate (body, -1, NULL)) {
2595                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2596                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2597                         g_free(temp);
2598                 } else {
2599                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2600                 }
2601                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2602
2603                 compose->autowrap = prev_autowrap;
2604                 if (compose->autowrap)
2605                         compose_wrap_all(compose);
2606         }
2607
2608         if (attach) {
2609                 gint i = 0, att = 0;
2610                 gchar *warn_files = NULL;
2611                 while (attach[i] != NULL) {
2612                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2613                         if (utf8_filename) {
2614                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2615                                         gchar *tmp = g_strdup_printf("%s%s\n",
2616                                                         warn_files?warn_files:"",
2617                                                         utf8_filename);
2618                                         g_free(warn_files);
2619                                         warn_files = tmp;
2620                                         att++;
2621                                 }
2622                                 g_free(utf8_filename);
2623                         } else {
2624                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2625                         }
2626                         i++;
2627                 }
2628                 if (warn_files) {
2629                         alertpanel_notice(ngettext(
2630                         "The following file has been attached: \n%s",
2631                         "The following files have been attached: \n%s", att), warn_files);
2632                         g_free(warn_files);
2633                 }
2634         }
2635         g_free(to);
2636         g_free(cc);
2637         g_free(bcc);
2638         g_free(subject);
2639         g_free(body);
2640         g_strfreev(attach);
2641 }
2642
2643 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2644 {
2645         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2646                                        {"Cc:",          NULL, TRUE},
2647                                        {"References:",  NULL, FALSE},
2648                                        {"Bcc:",         NULL, TRUE},
2649                                        {"Newsgroups:",  NULL, TRUE},
2650                                        {"Followup-To:", NULL, TRUE},
2651                                        {"List-Post:",   NULL, FALSE},
2652                                        {"X-Priority:",  NULL, FALSE},
2653                                        {NULL,           NULL, FALSE}};
2654
2655         enum
2656         {
2657                 H_REPLY_TO      = 0,
2658                 H_CC            = 1,
2659                 H_REFERENCES    = 2,
2660                 H_BCC           = 3,
2661                 H_NEWSGROUPS    = 4,
2662                 H_FOLLOWUP_TO   = 5,
2663                 H_LIST_POST     = 6,
2664                 H_X_PRIORITY    = 7
2665         };
2666
2667         FILE *fp;
2668
2669         g_return_val_if_fail(msginfo != NULL, -1);
2670
2671         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2672         procheader_get_header_fields(fp, hentry);
2673         fclose(fp);
2674
2675         if (hentry[H_REPLY_TO].body != NULL) {
2676                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2677                         compose->replyto =
2678                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2679                                                    NULL);
2680                 }
2681                 g_free(hentry[H_REPLY_TO].body);
2682                 hentry[H_REPLY_TO].body = NULL;
2683         }
2684         if (hentry[H_CC].body != NULL) {
2685                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2686                 g_free(hentry[H_CC].body);
2687                 hentry[H_CC].body = NULL;
2688         }
2689         if (hentry[H_REFERENCES].body != NULL) {
2690                 if (compose->mode == COMPOSE_REEDIT)
2691                         compose->references = hentry[H_REFERENCES].body;
2692                 else {
2693                         compose->references = compose_parse_references
2694                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2695                         g_free(hentry[H_REFERENCES].body);
2696                 }
2697                 hentry[H_REFERENCES].body = NULL;
2698         }
2699         if (hentry[H_BCC].body != NULL) {
2700                 if (compose->mode == COMPOSE_REEDIT)
2701                         compose->bcc =
2702                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2703                 g_free(hentry[H_BCC].body);
2704                 hentry[H_BCC].body = NULL;
2705         }
2706         if (hentry[H_NEWSGROUPS].body != NULL) {
2707                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2708                 hentry[H_NEWSGROUPS].body = NULL;
2709         }
2710         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2711                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2712                         compose->followup_to =
2713                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2714                                                    NULL);
2715                 }
2716                 g_free(hentry[H_FOLLOWUP_TO].body);
2717                 hentry[H_FOLLOWUP_TO].body = NULL;
2718         }
2719         if (hentry[H_LIST_POST].body != NULL) {
2720                 gchar *to = NULL;
2721
2722                 extract_address(hentry[H_LIST_POST].body);
2723                 if (hentry[H_LIST_POST].body[0] != '\0') {
2724                         scan_mailto_url(hentry[H_LIST_POST].body,
2725                                         NULL, &to, NULL, NULL, NULL, NULL, NULL);
2726                         if (to) {
2727                                 g_free(compose->ml_post);
2728                                 compose->ml_post = to;
2729                         }
2730                 }
2731                 g_free(hentry[H_LIST_POST].body);
2732                 hentry[H_LIST_POST].body = NULL;
2733         }
2734
2735         /* CLAWS - X-Priority */
2736         if (compose->mode == COMPOSE_REEDIT)
2737                 if (hentry[H_X_PRIORITY].body != NULL) {
2738                         gint priority;
2739                         
2740                         priority = atoi(hentry[H_X_PRIORITY].body);
2741                         g_free(hentry[H_X_PRIORITY].body);
2742                         
2743                         hentry[H_X_PRIORITY].body = NULL;
2744                         
2745                         if (priority < PRIORITY_HIGHEST || 
2746                             priority > PRIORITY_LOWEST)
2747                                 priority = PRIORITY_NORMAL;
2748                         
2749                         compose->priority =  priority;
2750                 }
2751  
2752         if (compose->mode == COMPOSE_REEDIT) {
2753                 if (msginfo->inreplyto && *msginfo->inreplyto)
2754                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2755                 return 0;
2756         }
2757
2758         if (msginfo->msgid && *msginfo->msgid)
2759                 compose->inreplyto = g_strdup(msginfo->msgid);
2760
2761         if (!compose->references) {
2762                 if (msginfo->msgid && *msginfo->msgid) {
2763                         if (msginfo->inreplyto && *msginfo->inreplyto)
2764                                 compose->references =
2765                                         g_strdup_printf("<%s>\n\t<%s>",
2766                                                         msginfo->inreplyto,
2767                                                         msginfo->msgid);
2768                         else
2769                                 compose->references =
2770                                         g_strconcat("<", msginfo->msgid, ">",
2771                                                     NULL);
2772                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2773                         compose->references =
2774                                 g_strconcat("<", msginfo->inreplyto, ">",
2775                                             NULL);
2776                 }
2777         }
2778
2779         return 0;
2780 }
2781
2782 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2783 {
2784         GSList *ref_id_list, *cur;
2785         GString *new_ref;
2786         gchar *new_ref_str;
2787
2788         ref_id_list = references_list_append(NULL, ref);
2789         if (!ref_id_list) return NULL;
2790         if (msgid && *msgid)
2791                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2792
2793         for (;;) {
2794                 gint len = 0;
2795
2796                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2797                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2798                         len += strlen((gchar *)cur->data) + 5;
2799
2800                 if (len > MAX_REFERENCES_LEN) {
2801                         /* remove second message-ID */
2802                         if (ref_id_list && ref_id_list->next &&
2803                             ref_id_list->next->next) {
2804                                 g_free(ref_id_list->next->data);
2805                                 ref_id_list = g_slist_remove
2806                                         (ref_id_list, ref_id_list->next->data);
2807                         } else {
2808                                 slist_free_strings(ref_id_list);
2809                                 g_slist_free(ref_id_list);
2810                                 return NULL;
2811                         }
2812                 } else
2813                         break;
2814         }
2815
2816         new_ref = g_string_new("");
2817         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2818                 if (new_ref->len > 0)
2819                         g_string_append(new_ref, "\n\t");
2820                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2821         }
2822
2823         slist_free_strings(ref_id_list);
2824         g_slist_free(ref_id_list);
2825
2826         new_ref_str = new_ref->str;
2827         g_string_free(new_ref, FALSE);
2828
2829         return new_ref_str;
2830 }
2831
2832 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2833                                 const gchar *fmt, const gchar *qmark,
2834                                 const gchar *body, gboolean rewrap,
2835                                 gboolean need_unescape,
2836                                 const gchar *err_msg)
2837 {
2838         MsgInfo* dummyinfo = NULL;
2839         gchar *quote_str = NULL;
2840         gchar *buf;
2841         gboolean prev_autowrap;
2842         const gchar *trimmed_body = body;
2843         gint cursor_pos = -1;
2844         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2845         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2846         GtkTextIter iter;
2847         GtkTextMark *mark;
2848         
2849
2850         SIGNAL_BLOCK(buffer);
2851
2852         if (!msginfo) {
2853                 dummyinfo = compose_msginfo_new_from_compose(compose);
2854                 msginfo = dummyinfo;
2855         }
2856
2857         if (qmark != NULL) {
2858 #ifdef USE_ASPELL
2859                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2860                                 compose->gtkaspell);
2861 #else
2862                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2863 #endif
2864                 quote_fmt_scan_string(qmark);
2865                 quote_fmt_parse();
2866
2867                 buf = quote_fmt_get_buffer();
2868                 if (buf == NULL)
2869                         alertpanel_error(_("Quote mark format error."));
2870                 else
2871                         Xstrdup_a(quote_str, buf, goto error)
2872         }
2873
2874         if (fmt && *fmt != '\0') {
2875
2876                 if (trimmed_body)
2877                         while (*trimmed_body == '\n')
2878                                 trimmed_body++;
2879
2880 #ifdef USE_ASPELL
2881                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2882                                 compose->gtkaspell);
2883 #else
2884                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2885 #endif
2886                 if (need_unescape) {
2887                         gchar *tmp = NULL;
2888
2889                         /* decode \-escape sequences in the internal representation of the quote format */
2890                         tmp = malloc(strlen(fmt)+1);
2891                         pref_get_unescaped_pref(tmp, fmt);
2892                         quote_fmt_scan_string(tmp);
2893                         quote_fmt_parse();
2894                         g_free(tmp);
2895                 } else {
2896                         quote_fmt_scan_string(fmt);
2897                         quote_fmt_parse();
2898                 }
2899
2900                 buf = quote_fmt_get_buffer();
2901                 if (buf == NULL) {
2902                         gint line = quote_fmt_get_line();
2903                         alertpanel_error(err_msg, line);
2904                         goto error;
2905                 }
2906         } else
2907                 buf = "";
2908
2909         prev_autowrap = compose->autowrap;
2910         compose->autowrap = FALSE;
2911
2912         mark = gtk_text_buffer_get_insert(buffer);
2913         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2914         if (g_utf8_validate(buf, -1, NULL)) { 
2915                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2916         } else {
2917                 gchar *tmpout = NULL;
2918                 tmpout = conv_codeset_strdup
2919                         (buf, conv_get_locale_charset_str_no_utf8(),
2920                          CS_INTERNAL);
2921                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2922                         g_free(tmpout);
2923                         tmpout = g_malloc(strlen(buf)*2+1);
2924                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2925                 }
2926                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2927                 g_free(tmpout);
2928         }
2929
2930         cursor_pos = quote_fmt_get_cursor_pos();
2931         compose->set_cursor_pos = cursor_pos;
2932         if (cursor_pos == -1) {
2933                 cursor_pos = 0;
2934         }
2935         gtk_text_buffer_get_start_iter(buffer, &iter);
2936         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2937         gtk_text_buffer_place_cursor(buffer, &iter);
2938
2939         compose->autowrap = prev_autowrap;
2940         if (compose->autowrap && rewrap)
2941                 compose_wrap_all(compose);
2942
2943         goto ok;
2944
2945 error:
2946         buf = NULL;
2947 ok:
2948         SIGNAL_UNBLOCK(buffer);
2949
2950         procmsg_msginfo_free( dummyinfo );
2951
2952         return buf;
2953 }
2954
2955 /* if ml_post is of type addr@host and from is of type
2956  * addr-anything@host, return TRUE
2957  */
2958 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2959 {
2960         gchar *left_ml = NULL;
2961         gchar *right_ml = NULL;
2962         gchar *left_from = NULL;
2963         gchar *right_from = NULL;
2964         gboolean result = FALSE;
2965         
2966         if (!ml_post || !from)
2967                 return FALSE;
2968         
2969         left_ml = g_strdup(ml_post);
2970         if (strstr(left_ml, "@")) {
2971                 right_ml = strstr(left_ml, "@")+1;
2972                 *(strstr(left_ml, "@")) = '\0';
2973         }
2974         
2975         left_from = g_strdup(from);
2976         if (strstr(left_from, "@")) {
2977                 right_from = strstr(left_from, "@")+1;
2978                 *(strstr(left_from, "@")) = '\0';
2979         }
2980         
2981         if (left_ml && left_from && right_ml && right_from
2982         &&  !strncmp(left_from, left_ml, strlen(left_ml))
2983         &&  !strcmp(right_from, right_ml)) {
2984                 result = TRUE;
2985         }
2986         g_free(left_ml);
2987         g_free(left_from);
2988         
2989         return result;
2990 }
2991
2992 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2993 {
2994         gchar *my_addr1, *my_addr2;
2995         
2996         if (!addr1 || !addr2)
2997                 return FALSE;
2998
2999         Xstrdup_a(my_addr1, addr1, return FALSE);
3000         Xstrdup_a(my_addr2, addr2, return FALSE);
3001         
3002         extract_address(my_addr1);
3003         extract_address(my_addr2);
3004         
3005         return !strcasecmp(my_addr1, my_addr2);
3006 }
3007
3008 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3009                                     gboolean to_all, gboolean to_ml,
3010                                     gboolean to_sender,
3011                                     gboolean followup_and_reply_to)
3012 {
3013         GSList *cc_list = NULL;
3014         GSList *cur;
3015         gchar *from = NULL;
3016         gchar *replyto = NULL;
3017         GHashTable *to_table;
3018
3019         gboolean reply_to_ml = FALSE;
3020         gboolean default_reply_to = FALSE;
3021
3022         g_return_if_fail(compose->account != NULL);
3023         g_return_if_fail(msginfo != NULL);
3024
3025         reply_to_ml = to_ml && compose->ml_post;
3026
3027         default_reply_to = msginfo->folder && 
3028                 msginfo->folder->prefs->enable_default_reply_to;
3029
3030         if (compose->account->protocol != A_NNTP) {
3031                 if (reply_to_ml && !default_reply_to) {
3032                         
3033                         gboolean is_subscr = is_subscription(compose->ml_post,
3034                                                              msginfo->from);
3035                         if (!is_subscr) {
3036                                 /* normal answer to ml post with a reply-to */
3037                                 compose_entry_append(compose,
3038                                            compose->ml_post,
3039                                            COMPOSE_TO);
3040                                 if (compose->replyto
3041                                 &&  !same_address(compose->ml_post, compose->replyto))
3042                                         compose_entry_append(compose,
3043                                                 compose->replyto,
3044                                                 COMPOSE_CC);
3045                         } else {
3046                                 /* answer to subscription confirmation */
3047                                 if (compose->replyto)
3048                                         compose_entry_append(compose,
3049                                                 compose->replyto,
3050                                                 COMPOSE_TO);
3051                                 else if (msginfo->from)
3052                                         compose_entry_append(compose,
3053                                                 msginfo->from,
3054                                                 COMPOSE_TO);
3055                         }
3056                 }
3057                 else if (!(to_all || to_sender) && default_reply_to) {
3058                         compose_entry_append(compose,
3059                             msginfo->folder->prefs->default_reply_to,
3060                             COMPOSE_TO);
3061                         compose_entry_mark_default_to(compose,
3062                                 msginfo->folder->prefs->default_reply_to);
3063                 } else {
3064                         gchar *tmp1 = NULL;
3065                         if (!msginfo->from)
3066                                 return;
3067                         Xstrdup_a(tmp1, msginfo->from, return);
3068                         extract_address(tmp1);
3069                         if (to_all || to_sender ||
3070                             !account_find_from_address(tmp1, FALSE))
3071                                 compose_entry_append(compose,
3072                                  (compose->replyto && !to_sender)
3073                                           ? compose->replyto :
3074                                           msginfo->from ? msginfo->from : "",
3075                                           COMPOSE_TO);
3076                         else if (!to_all && !to_sender) {
3077                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3078                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3079                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3080                                         if (compose->replyto) {
3081                                                 compose_entry_append(compose,
3082                                                         compose->replyto,
3083                                                         COMPOSE_TO);
3084                                         } else {
3085                                                 compose_entry_append(compose,
3086                                                           msginfo->from ? msginfo->from : "",
3087                                                           COMPOSE_TO);
3088                                         }
3089                                 } else {
3090                                         /* replying to own mail, use original recp */
3091                                         compose_entry_append(compose,
3092                                                   msginfo->to ? msginfo->to : "",
3093                                                   COMPOSE_TO);
3094                                         compose_entry_append(compose,
3095                                                   msginfo->cc ? msginfo->cc : "",
3096                                                   COMPOSE_CC);
3097                                 }
3098                         }
3099                 }
3100         } else {
3101                 if (to_sender || (compose->followup_to && 
3102                         !strncmp(compose->followup_to, "poster", 6)))
3103                         compose_entry_append
3104                                 (compose, 
3105                                  (compose->replyto ? compose->replyto :
3106                                         msginfo->from ? msginfo->from : ""),
3107                                  COMPOSE_TO);
3108                                  
3109                 else if (followup_and_reply_to || to_all) {
3110                         compose_entry_append
3111                                 (compose,
3112                                  (compose->replyto ? compose->replyto :
3113                                  msginfo->from ? msginfo->from : ""),
3114                                  COMPOSE_TO);                           
3115                 
3116                         compose_entry_append
3117                                 (compose,
3118                                  compose->followup_to ? compose->followup_to :
3119                                  compose->newsgroups ? compose->newsgroups : "",
3120                                  COMPOSE_NEWSGROUPS);
3121                 } 
3122                 else 
3123                         compose_entry_append
3124                                 (compose,
3125                                  compose->followup_to ? compose->followup_to :
3126                                  compose->newsgroups ? compose->newsgroups : "",
3127                                  COMPOSE_NEWSGROUPS);
3128         }
3129
3130         if (msginfo->subject && *msginfo->subject) {
3131                 gchar *buf, *buf2;
3132                 gchar *p;
3133
3134                 buf = p = g_strdup(msginfo->subject);
3135                 p += subject_get_prefix_length(p);
3136                 memmove(buf, p, strlen(p) + 1);
3137
3138                 buf2 = g_strdup_printf("Re: %s", buf);
3139                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3140
3141                 g_free(buf2);
3142                 g_free(buf);
3143         } else
3144                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3145
3146         if (to_ml && compose->ml_post) return;
3147         if (!to_all || compose->account->protocol == A_NNTP) return;
3148
3149         if (compose->replyto) {
3150                 Xstrdup_a(replyto, compose->replyto, return);
3151                 extract_address(replyto);
3152         }
3153         if (msginfo->from) {
3154                 Xstrdup_a(from, msginfo->from, return);
3155                 extract_address(from);
3156         }
3157
3158         if (replyto && from)
3159                 cc_list = address_list_append_with_comments(cc_list, from);
3160         if (to_all && msginfo->folder && 
3161             msginfo->folder->prefs->enable_default_reply_to)
3162                 cc_list = address_list_append_with_comments(cc_list,
3163                                 msginfo->folder->prefs->default_reply_to);
3164         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3165         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3166
3167         to_table = g_hash_table_new(g_str_hash, g_str_equal);
3168         if (replyto)
3169                 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3170         if (compose->account) {
3171                 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3172                                     GINT_TO_POINTER(1));
3173         }
3174         /* remove address on To: and that of current account */
3175         for (cur = cc_list; cur != NULL; ) {
3176                 GSList *next = cur->next;
3177                 gchar *addr;
3178
3179                 addr = g_utf8_strdown(cur->data, -1);
3180                 extract_address(addr);
3181
3182                 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3183                         cc_list = g_slist_remove(cc_list, cur->data);
3184                 else
3185                         g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3186
3187                 cur = next;
3188         }
3189         hash_free_strings(to_table);
3190         g_hash_table_destroy(to_table);
3191
3192         if (cc_list) {
3193                 for (cur = cc_list; cur != NULL; cur = cur->next)
3194                         compose_entry_append(compose, (gchar *)cur->data,
3195                                              COMPOSE_CC);
3196                 slist_free_strings(cc_list);
3197                 g_slist_free(cc_list);
3198         }
3199
3200 }
3201
3202 #define SET_ENTRY(entry, str) \
3203 { \
3204         if (str && *str) \
3205                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3206 }
3207
3208 #define SET_ADDRESS(type, str) \
3209 { \
3210         if (str && *str) \
3211                 compose_entry_append(compose, str, type); \
3212 }
3213
3214 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3215 {
3216         g_return_if_fail(msginfo != NULL);
3217
3218         SET_ENTRY(subject_entry, msginfo->subject);
3219         SET_ENTRY(from_name, msginfo->from);
3220         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3221         SET_ADDRESS(COMPOSE_CC, compose->cc);
3222         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3223         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3224         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3225         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3226
3227         compose_update_priority_menu_item(compose);
3228         compose_update_privacy_system_menu_item(compose, FALSE);
3229         compose_show_first_last_header(compose, TRUE);
3230 }
3231
3232 #undef SET_ENTRY
3233 #undef SET_ADDRESS
3234
3235 static void compose_insert_sig(Compose *compose, gboolean replace)
3236 {
3237         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3238         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3239         GtkTextMark *mark;
3240         GtkTextIter iter, iter_end;
3241         gint cur_pos;
3242         gboolean prev_autowrap;
3243         gboolean found = FALSE;
3244         gboolean exists = FALSE;
3245         
3246         g_return_if_fail(compose->account != NULL);
3247
3248         BLOCK_WRAP();
3249
3250         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3251                                         G_CALLBACK(compose_changed_cb),
3252                                         compose);
3253         
3254         mark = gtk_text_buffer_get_insert(buffer);
3255         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3256         cur_pos = gtk_text_iter_get_offset (&iter);
3257
3258         gtk_text_buffer_get_end_iter(buffer, &iter);
3259
3260         exists = (compose->sig_str != NULL);
3261
3262         if (replace) {
3263                 GtkTextIter first_iter, start_iter, end_iter;
3264
3265                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3266
3267                 if (!exists || compose->sig_str[0] == '\0')
3268                         found = FALSE;
3269                 else
3270                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3271                                         compose->signature_tag);
3272
3273                 if (found) {
3274                         /* include previous \n\n */
3275                         gtk_text_iter_backward_chars(&first_iter, 2);
3276                         start_iter = first_iter;
3277                         end_iter = first_iter;
3278                         /* skip re-start */
3279                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3280                                         compose->signature_tag);
3281                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3282                                         compose->signature_tag);
3283                         if (found) {
3284                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3285                                 iter = start_iter;
3286                         }
3287                 } 
3288         } 
3289
3290         g_free(compose->sig_str);
3291         compose->sig_str = compose_get_signature_str(compose);
3292
3293         cur_pos = gtk_text_iter_get_offset(&iter);
3294
3295         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3296                 g_free(compose->sig_str);
3297                 compose->sig_str = NULL;
3298         } else {
3299                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3300                 /* remove \n\n */
3301                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3302                 gtk_text_iter_forward_chars(&iter, 2);
3303                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3304                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3305
3306                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3307                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3308         }
3309         /* put the cursor where it should be 
3310          * either where the quote_fmt says, either before the signature */
3311         if (compose->set_cursor_pos < 0)
3312                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3313         else
3314                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3315                         compose->set_cursor_pos);
3316                 
3317         gtk_text_buffer_place_cursor(buffer, &iter);
3318         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3319                                         G_CALLBACK(compose_changed_cb),
3320                                         compose);
3321                 
3322         UNBLOCK_WRAP();
3323 }
3324
3325 static gchar *compose_get_signature_str(Compose *compose)
3326 {
3327         gchar *sig_body = NULL;
3328         gchar *sig_str = NULL;
3329         gchar *utf8_sig_str = NULL;
3330
3331         g_return_val_if_fail(compose->account != NULL, NULL);
3332
3333         if (!compose->account->sig_path)
3334                 return NULL;
3335
3336         if (compose->account->sig_type == SIG_FILE) {
3337                 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3338                         g_warning("can't open signature file: %s\n",
3339                                   compose->account->sig_path);
3340                         return NULL;
3341                 }
3342         }
3343
3344         if (compose->account->sig_type == SIG_COMMAND)
3345                 sig_body = get_command_output(compose->account->sig_path);
3346         else {
3347                 gchar *tmp;
3348
3349                 tmp = file_read_to_str(compose->account->sig_path);
3350                 if (!tmp)
3351                         return NULL;
3352                 sig_body = normalize_newlines(tmp);
3353                 g_free(tmp);
3354         }
3355
3356         if (compose->account->sig_sep) {
3357                 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3358                                       NULL);
3359                 g_free(sig_body);
3360         } else
3361                 sig_str = g_strconcat("\n\n", sig_body, NULL);
3362
3363         if (sig_str) {
3364                 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3365                         utf8_sig_str = sig_str;
3366                 else {
3367                         utf8_sig_str = conv_codeset_strdup
3368                                 (sig_str, conv_get_locale_charset_str_no_utf8(),
3369                                  CS_INTERNAL);
3370                         g_free(sig_str);
3371                 }
3372         }
3373
3374         return utf8_sig_str;
3375 }
3376
3377 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3378 {
3379         GtkTextView *text;
3380         GtkTextBuffer *buffer;
3381         GtkTextMark *mark;
3382         GtkTextIter iter;
3383         const gchar *cur_encoding;
3384         gchar buf[BUFFSIZE];
3385         gint len;
3386         FILE *fp;
3387         gboolean prev_autowrap;
3388         gboolean badtxt = FALSE;
3389
3390         g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3391
3392         if ((fp = g_fopen(file, "rb")) == NULL) {
3393                 FILE_OP_ERROR(file, "fopen");
3394                 return COMPOSE_INSERT_READ_ERROR;
3395         }
3396
3397         prev_autowrap = compose->autowrap;
3398         compose->autowrap = FALSE;
3399
3400         text = GTK_TEXT_VIEW(compose->text);
3401         buffer = gtk_text_view_get_buffer(text);
3402         mark = gtk_text_buffer_get_insert(buffer);
3403         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3404
3405         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3406                                         G_CALLBACK(text_inserted),
3407                                         compose);
3408
3409         cur_encoding = conv_get_locale_charset_str_no_utf8();
3410
3411         while (fgets(buf, sizeof(buf), fp) != NULL) {
3412                 gchar *str;
3413
3414                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3415                         str = g_strdup(buf);
3416                 else
3417                         str = conv_codeset_strdup
3418                                 (buf, cur_encoding, CS_INTERNAL);
3419                 if (!str) continue;
3420
3421                 /* strip <CR> if DOS/Windows file,
3422                    replace <CR> with <LF> if Macintosh file. */
3423                 strcrchomp(str);
3424                 len = strlen(str);
3425                 if (len > 0 && str[len - 1] != '\n') {
3426                         while (--len >= 0)
3427                                 if (str[len] == '\r') str[len] = '\n';
3428                 }
3429
3430                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3431                 g_free(str);
3432         }
3433
3434         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3435                                           G_CALLBACK(text_inserted),
3436                                           compose);
3437         compose->autowrap = prev_autowrap;
3438         if (compose->autowrap)
3439                 compose_wrap_all(compose);
3440
3441         fclose(fp);
3442
3443         if (badtxt)
3444                 return COMPOSE_INSERT_INVALID_CHARACTER;
3445         else 
3446                 return COMPOSE_INSERT_SUCCESS;
3447 }
3448
3449 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3450                                   const gchar *filename,
3451                                   const gchar *content_type)
3452 {
3453         AttachInfo *ainfo;
3454         GtkTreeIter iter;
3455         FILE *fp;
3456         off_t size;
3457         GAuto *auto_ainfo;
3458         gchar *size_text;
3459         GtkListStore *store;
3460         gchar *name;
3461         gboolean has_binary = FALSE;
3462
3463         if (!is_file_exist(file)) {
3464                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3465                 gboolean result = FALSE;
3466                 if (file_from_uri && is_file_exist(file_from_uri)) {
3467                         result = compose_attach_append(
3468                                                 compose, file_from_uri,
3469                                                 filename,
3470                                                 content_type);
3471                 }
3472                 g_free(file_from_uri);
3473                 if (result)
3474                         return TRUE;
3475                 alertpanel_error("File %s doesn't exist\n", filename);
3476                 return FALSE;
3477         }
3478         if ((size = get_file_size(file)) < 0) {
3479                 alertpanel_error("Can't get file size of %s\n", filename);
3480                 return FALSE;
3481         }
3482         if (size == 0) {
3483                 alertpanel_error(_("File %s is empty."), filename);
3484                 return FALSE;
3485         }
3486         if ((fp = g_fopen(file, "rb")) == NULL) {
3487                 alertpanel_error(_("Can't read %s."), filename);
3488                 return FALSE;
3489         }
3490         fclose(fp);
3491
3492         ainfo = g_new0(AttachInfo, 1);
3493         auto_ainfo = g_auto_pointer_new_with_free
3494                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3495         ainfo->file = g_strdup(file);
3496
3497         if (content_type) {
3498                 ainfo->content_type = g_strdup(content_type);
3499                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3500                         MsgInfo *msginfo;
3501                         MsgFlags flags = {0, 0};
3502
3503                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3504                                 ainfo->encoding = ENC_7BIT;
3505                         else
3506                                 ainfo->encoding = ENC_8BIT;
3507
3508                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3509                         if (msginfo && msginfo->subject)
3510                                 name = g_strdup(msginfo->subject);
3511                         else
3512                                 name = g_path_get_basename(filename ? filename : file);
3513
3514                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3515
3516                         procmsg_msginfo_free(msginfo);
3517                 } else {
3518                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3519                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3520                         else
3521                                 ainfo->encoding = ENC_BASE64;
3522                         name = g_path_get_basename(filename ? filename : file);
3523                         ainfo->name = g_strdup(name);
3524                 }
3525                 g_free(name);
3526         } else {
3527                 ainfo->content_type = procmime_get_mime_type(file);
3528                 if (!ainfo->content_type) {
3529                         ainfo->content_type =
3530                                 g_strdup("application/octet-stream");
3531                         ainfo->encoding = ENC_BASE64;
3532                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3533                         ainfo->encoding =
3534                                 procmime_get_encoding_for_text_file(file, &has_binary);
3535                 else
3536                         ainfo->encoding = ENC_BASE64;
3537                 name = g_path_get_basename(filename ? filename : file);
3538                 ainfo->name = g_strdup(name);   
3539                 g_free(name);
3540         }
3541
3542         if (ainfo->name != NULL
3543         &&  !strcmp(ainfo->name, ".")) {
3544                 g_free(ainfo->name);
3545                 ainfo->name = NULL;
3546         }
3547
3548         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3549                 g_free(ainfo->content_type);
3550                 ainfo->content_type = g_strdup("application/octet-stream");
3551         }
3552
3553         ainfo->size = size;
3554         size_text = to_human_readable(size);
3555
3556         store = GTK_LIST_STORE(gtk_tree_view_get_model
3557                         (GTK_TREE_VIEW(compose->attach_clist)));
3558                 
3559         gtk_list_store_append(store, &iter);
3560         gtk_list_store_set(store, &iter, 
3561                            COL_MIMETYPE, ainfo->content_type,
3562                            COL_SIZE, size_text,
3563                            COL_NAME, ainfo->name,
3564                            COL_DATA, ainfo,
3565                            COL_AUTODATA, auto_ainfo,
3566                            -1);
3567         
3568         g_auto_pointer_free(auto_ainfo);
3569         compose_attach_update_label(compose);
3570         return TRUE;
3571 }
3572
3573 static void compose_use_signing(Compose *compose, gboolean use_signing)
3574 {
3575         GtkItemFactory *ifactory;
3576         GtkWidget *menuitem = NULL;
3577
3578         compose->use_signing = use_signing;
3579         ifactory = gtk_item_factory_from_widget(compose->menubar);
3580         menuitem = gtk_item_factory_get_item
3581                 (ifactory, "/Options/Sign");
3582         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3583                                        use_signing);
3584 }
3585
3586 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3587 {
3588         GtkItemFactory *ifactory;
3589         GtkWidget *menuitem = NULL;
3590
3591         compose->use_encryption = use_encryption;
3592         ifactory = gtk_item_factory_from_widget(compose->menubar);
3593         menuitem = gtk_item_factory_get_item
3594                 (ifactory, "/Options/Encrypt");
3595
3596         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3597                                        use_encryption);
3598 }
3599
3600 #define NEXT_PART_NOT_CHILD(info)  \
3601 {  \
3602         node = info->node;  \
3603         while (node->children)  \
3604                 node = g_node_last_child(node);  \
3605         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3606 }
3607
3608 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3609 {
3610         MimeInfo *mimeinfo;
3611         MimeInfo *child;
3612         MimeInfo *firsttext = NULL;
3613         MimeInfo *encrypted = NULL;
3614         GNode    *node;
3615         gchar *outfile;
3616         const gchar *partname = NULL;
3617
3618         mimeinfo = procmime_scan_message(msginfo);
3619         if (!mimeinfo) return;
3620
3621         if (mimeinfo->node->children == NULL) {
3622                 procmime_mimeinfo_free_all(mimeinfo);
3623                 return;
3624         }
3625
3626         /* find first content part */
3627         child = (MimeInfo *) mimeinfo->node->children->data;
3628         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3629                 child = (MimeInfo *)child->node->children->data;
3630
3631         if (child->type == MIMETYPE_TEXT) {
3632                 firsttext = child;
3633                 debug_print("First text part found\n");
3634         } else if (compose->mode == COMPOSE_REEDIT &&
3635                  child->type == MIMETYPE_APPLICATION &&
3636                  !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3637                 encrypted = (MimeInfo *)child->node->parent->data;
3638         }
3639      
3640         child = (MimeInfo *) mimeinfo->node->children->data;
3641         while (child != NULL) {
3642                 gint err;
3643
3644                 if (child == encrypted) {
3645                         /* skip this part of tree */
3646                         NEXT_PART_NOT_CHILD(child);
3647                         continue;
3648                 }
3649
3650                 if (child->type == MIMETYPE_MULTIPART) {
3651                         /* get the actual content */
3652                         child = procmime_mimeinfo_next(child);
3653                         continue;
3654                 }
3655                     
3656                 if (child == firsttext) {
3657                         child = procmime_mimeinfo_next(child);
3658                         continue;
3659                 }
3660
3661                 outfile = procmime_get_tmp_file_name(child);
3662                 if ((err = procmime_get_part(outfile, child)) < 0)
3663                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3664                 else {
3665                         gchar *content_type;
3666
3667                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3668
3669                         /* if we meet a pgp signature, we don't attach it, but
3670                          * we force signing. */
3671                         if ((strcmp(content_type, "application/pgp-signature") &&
3672                             strcmp(content_type, "application/pkcs7-signature") &&
3673                             strcmp(content_type, "application/x-pkcs7-signature"))
3674                             || compose->mode == COMPOSE_REDIRECT) {
3675                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3676                                 if (partname == NULL)
3677                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3678                                 if (partname == NULL)
3679                                         partname = "";
3680                                 compose_attach_append(compose, outfile, 
3681                                                       partname, content_type);
3682                         } else {
3683                                 compose_force_signing(compose, compose->account);
3684                         }
3685                         g_free(content_type);
3686                 }
3687                 g_free(outfile);
3688                 NEXT_PART_NOT_CHILD(child);
3689         }
3690         procmime_mimeinfo_free_all(mimeinfo);
3691 }
3692
3693 #undef NEXT_PART_NOT_CHILD
3694
3695
3696
3697 typedef enum {
3698         WAIT_FOR_INDENT_CHAR,
3699         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3700 } IndentState;
3701
3702 /* return indent length, we allow:
3703    indent characters followed by indent characters or spaces/tabs,
3704    alphabets and numbers immediately followed by indent characters,
3705    and the repeating sequences of the above
3706    If quote ends with multiple spaces, only the first one is included. */
3707 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3708                                     const GtkTextIter *start, gint *len)
3709 {
3710         GtkTextIter iter = *start;
3711         gunichar wc;
3712         gchar ch[6];
3713         gint clen;
3714         IndentState state = WAIT_FOR_INDENT_CHAR;
3715         gboolean is_space;
3716         gboolean is_indent;
3717         gint alnum_count = 0;
3718         gint space_count = 0;
3719         gint quote_len = 0;
3720
3721         if (prefs_common.quote_chars == NULL) {
3722                 return 0 ;
3723         }
3724
3725         while (!gtk_text_iter_ends_line(&iter)) {
3726                 wc = gtk_text_iter_get_char(&iter);
3727                 if (g_unichar_iswide(wc))
3728                         break;
3729                 clen = g_unichar_to_utf8(wc, ch);
3730                 if (clen != 1)
3731                         break;
3732
3733                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3734                 is_space = g_unichar_isspace(wc);
3735
3736                 if (state == WAIT_FOR_INDENT_CHAR) {
3737                         if (!is_indent && !g_unichar_isalnum(wc))
3738                                 break;
3739                         if (is_indent) {
3740                                 quote_len += alnum_count + space_count + 1;
3741                                 alnum_count = space_count = 0;
3742                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3743                         } else
3744                                 alnum_count++;
3745                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3746                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3747                                 break;
3748                         if (is_space)
3749                                 space_count++;
3750                         else if (is_indent) {
3751                                 quote_len += alnum_count + space_count + 1;
3752                                 alnum_count = space_count = 0;
3753                         } else {
3754                                 alnum_count++;
3755                                 state = WAIT_FOR_INDENT_CHAR;
3756                         }
3757                 }
3758
3759                 gtk_text_iter_forward_char(&iter);
3760         }
3761
3762         if (quote_len > 0 && space_count > 0)
3763                 quote_len++;
3764
3765         if (len)
3766                 *len = quote_len;
3767
3768         if (quote_len > 0) {
3769                 iter = *start;
3770                 gtk_text_iter_forward_chars(&iter, quote_len);
3771                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3772         }
3773
3774         return NULL;
3775 }
3776
3777 /* return TRUE if the line is itemized */
3778 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3779                                     const GtkTextIter *start)
3780 {
3781         GtkTextIter iter = *start;
3782         gunichar wc;
3783         gchar ch[6];
3784         gint clen;
3785
3786         if (gtk_text_iter_ends_line(&iter))
3787                 return FALSE;
3788
3789         while (1) {
3790                 wc = gtk_text_iter_get_char(&iter);
3791                 if (!g_unichar_isspace(wc))
3792                         break;
3793                 gtk_text_iter_forward_char(&iter);
3794                 if (gtk_text_iter_ends_line(&iter))
3795                         return FALSE;
3796         }
3797
3798         clen = g_unichar_to_utf8(wc, ch);
3799         if (clen != 1)
3800                 return FALSE;
3801
3802         if (!strchr("*-+", ch[0]))
3803                 return FALSE;
3804
3805         gtk_text_iter_forward_char(&iter);
3806         if (gtk_text_iter_ends_line(&iter))
3807                 return FALSE;
3808         wc = gtk_text_iter_get_char(&iter);
3809         if (g_unichar_isspace(wc))
3810                 return TRUE;
3811
3812         return FALSE;
3813 }
3814
3815 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3816                                            const GtkTextIter *start,
3817                                            GtkTextIter *break_pos,
3818                                            gint max_col,
3819                                            gint quote_len)
3820 {
3821         GtkTextIter iter = *start, line_end = *start;
3822         PangoLogAttr *attrs;
3823         gchar *str;
3824         gchar *p;
3825         gint len;
3826         gint i;
3827         gint col = 0;
3828         gint pos = 0;
3829         gboolean can_break = FALSE;
3830         gboolean do_break = FALSE;
3831         gboolean was_white = FALSE;
3832         gboolean prev_dont_break = FALSE;
3833
3834         gtk_text_iter_forward_to_line_end(&line_end);
3835         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3836         len = g_utf8_strlen(str, -1);
3837         
3838         if (len == 0) {
3839                 g_free(str);
3840                 g_warning("compose_get_line_break_pos: len = 0!\n");
3841                 return FALSE;
3842         }
3843
3844         /* g_print("breaking line: %d: %s (len = %d)\n",
3845                 gtk_text_iter_get_line(&iter), str, len); */
3846
3847         attrs = g_new(PangoLogAttr, len + 1);
3848
3849         pango_default_break(str, -1, NULL, attrs, len + 1);
3850
3851         p = str;
3852
3853         /* skip quote and leading spaces */
3854         for (i = 0; *p != '\0' && i < len; i++) {
3855                 gunichar wc;
3856
3857                 wc = g_utf8_get_char(p);
3858                 if (i >= quote_len && !g_unichar_isspace(wc))
3859                         break;
3860                 if (g_unichar_iswide(wc))
3861                         col += 2;
3862                 else if (*p == '\t')
3863                         col += 8;
3864                 else
3865                         col++;
3866                 p = g_utf8_next_char(p);
3867         }
3868
3869         for (; *p != '\0' && i < len; i++) {
3870                 PangoLogAttr *attr = attrs + i;
3871                 gunichar wc;
3872                 gint uri_len;
3873
3874                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3875                         pos = i;
3876                 
3877                 was_white = attr->is_white;
3878
3879                 /* don't wrap URI */
3880                 if ((uri_len = get_uri_len(p)) > 0) {
3881                         col += uri_len;
3882                         if (pos > 0 && col > max_col) {
3883                                 do_break = TRUE;
3884                                 break;
3885                         }
3886                         i += uri_len - 1;
3887                         p += uri_len;
3888                         can_break = TRUE;
3889                         continue;
3890                 }
3891
3892                 wc = g_utf8_get_char(p);
3893                 if (g_unichar_iswide(wc)) {
3894                         col += 2;
3895                         if (prev_dont_break && can_break && attr->is_line_break)
3896                                 pos = i;
3897                 } else if (*p == '\t')
3898                         col += 8;
3899                 else
3900                         col++;
3901                 if (pos > 0 && col > max_col) {
3902                         do_break = TRUE;
3903                         break;
3904                 }
3905
3906                 if (*p == '-' || *p == '/')
3907                         prev_dont_break = TRUE;
3908                 else
3909                         prev_dont_break = FALSE;
3910
3911                 p = g_utf8_next_char(p);
3912                 can_break = TRUE;
3913         }
3914
3915         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3916
3917         g_free(attrs);
3918         g_free(str);
3919
3920         *break_pos = *start;
3921         gtk_text_iter_set_line_offset(break_pos, pos);
3922
3923         return do_break;
3924 }
3925
3926 static gboolean compose_join_next_line(Compose *compose,
3927                                        GtkTextBuffer *buffer,
3928                                        GtkTextIter *iter,
3929                                        const gchar *quote_str)
3930 {
3931         GtkTextIter iter_ = *iter, cur, prev, next, end;
3932         PangoLogAttr attrs[3];
3933         gchar *str;
3934         gchar *next_quote_str;
3935         gunichar wc1, wc2;
3936         gint quote_len;
3937         gboolean keep_cursor = FALSE;
3938
3939         if (!gtk_text_iter_forward_line(&iter_) ||
3940             gtk_text_iter_ends_line(&iter_)) {
3941                 return FALSE;
3942         }
3943         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3944
3945         if ((quote_str || next_quote_str) &&
3946             strcmp2(quote_str, next_quote_str) != 0) {
3947                 g_free(next_quote_str);
3948                 return FALSE;
3949         }
3950         g_free(next_quote_str);
3951
3952         end = iter_;
3953         if (quote_len > 0) {
3954                 gtk_text_iter_forward_chars(&end, quote_len);
3955                 if (gtk_text_iter_ends_line(&end)) {
3956                         return FALSE;
3957                 }
3958         }
3959
3960         /* don't join itemized lines */
3961         if (compose_is_itemized(buffer, &end)) {
3962                 return FALSE;
3963         }
3964
3965         /* don't join signature separator */
3966         if (compose_is_sig_separator(compose, buffer, &iter_)) {
3967                 return FALSE;
3968         }
3969         /* delete quote str */
3970         if (quote_len > 0)
3971                 gtk_text_buffer_delete(buffer, &iter_, &end);
3972
3973         /* don't join line breaks put by the user */
3974         prev = cur = iter_;
3975         gtk_text_iter_backward_char(&cur);
3976         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3977                 gtk_text_iter_forward_char(&cur);
3978                 *iter = cur;
3979                 return FALSE;
3980         }
3981         gtk_text_iter_forward_char(&cur);
3982         /* delete linebreak and extra spaces */
3983         while (gtk_text_iter_backward_char(&cur)) {
3984                 wc1 = gtk_text_iter_get_char(&cur);
3985                 if (!g_unichar_isspace(wc1))
3986                         break;
3987                 prev = cur;
3988         }
3989         next = cur = iter_;
3990         while (!gtk_text_iter_ends_line(&cur)) {
3991                 wc1 = gtk_text_iter_get_char(&cur);
3992                 if (!g_unichar_isspace(wc1))
3993                         break;
3994                 gtk_text_iter_forward_char(&cur);
3995                 next = cur;
3996         }
3997         if (!gtk_text_iter_equal(&prev, &next)) {
3998                 GtkTextMark *mark;
3999
4000                 mark = gtk_text_buffer_get_insert(buffer);
4001                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4002                 if (gtk_text_iter_equal(&prev, &cur))
4003                         keep_cursor = TRUE;
4004                 gtk_text_buffer_delete(buffer, &prev, &next);
4005         }
4006         iter_ = prev;
4007
4008         /* insert space if required */
4009         gtk_text_iter_backward_char(&prev);
4010         wc1 = gtk_text_iter_get_char(&prev);
4011         wc2 = gtk_text_iter_get_char(&next);
4012         gtk_text_iter_forward_char(&next);
4013         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4014         pango_default_break(str, -1, NULL, attrs, 3);
4015         if (!attrs[1].is_line_break ||
4016             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4017                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4018                 if (keep_cursor) {
4019                         gtk_text_iter_backward_char(&iter_);
4020                         gtk_text_buffer_place_cursor(buffer, &iter_);
4021                 }
4022         }
4023         g_free(str);
4024
4025         *iter = iter_;
4026         return TRUE;
4027 }
4028
4029 #define ADD_TXT_POS(bp_, ep_, pti_) \
4030         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4031                 last = last->next; \
4032                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4033                 last->next = NULL; \
4034         } else { \
4035                 g_warning("alloc error scanning URIs\n"); \
4036         }
4037
4038 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4039 {
4040         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4041         GtkTextBuffer *buffer;
4042         GtkTextIter iter, break_pos, end_of_line;
4043         gchar *quote_str = NULL;
4044         gint quote_len;
4045         gboolean wrap_quote = prefs_common.linewrap_quote;
4046         gboolean prev_autowrap = compose->autowrap;
4047         gint startq_offset = -1, noq_offset = -1;
4048         gint uri_start = -1, uri_stop = -1;
4049         gint nouri_start = -1, nouri_stop = -1;
4050         gint num_blocks = 0;
4051         gint quotelevel = -1;
4052         gboolean modified = force;
4053         gboolean removed = FALSE;
4054         gboolean modified_before_remove = FALSE;
4055         gint lines = 0;
4056         gboolean start = TRUE;
4057
4058         if (force) {
4059                 modified = TRUE;
4060         }
4061         if (compose->draft_timeout_tag == -2) {
4062                 modified = TRUE;
4063         }
4064
4065         compose->autowrap = FALSE;
4066
4067         buffer = gtk_text_view_get_buffer(text);
4068         undo_wrapping(compose->undostruct, TRUE);
4069         if (par_iter) {
4070                 iter = *par_iter;
4071         } else {
4072                 GtkTextMark *mark;
4073                 mark = gtk_text_buffer_get_insert(buffer);
4074                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4075         }
4076
4077
4078         if (compose->draft_timeout_tag == -2) {
4079                 if (gtk_text_iter_ends_line(&iter)) {
4080                         while (gtk_text_iter_ends_line(&iter) &&
4081                                gtk_text_iter_forward_line(&iter))
4082                                 ;
4083                 } else {
4084                         while (gtk_text_iter_backward_line(&iter)) {
4085                                 if (gtk_text_iter_ends_line(&iter)) {
4086                                         gtk_text_iter_forward_line(&iter);
4087                                         break;
4088                                 }
4089                         }
4090                 }
4091         } else {
4092                 /* move to line start */
4093                 gtk_text_iter_set_line_offset(&iter, 0);
4094         }
4095         /* go until paragraph end (empty line) */
4096         while (start || !gtk_text_iter_ends_line(&iter)) {
4097                 gchar *scanpos = NULL;
4098                 /* parse table - in order of priority */
4099                 struct table {
4100                         const gchar *needle; /* token */
4101
4102                         /* token search function */
4103                         gchar    *(*search)     (const gchar *haystack,
4104                                                  const gchar *needle);
4105                         /* part parsing function */
4106                         gboolean  (*parse)      (const gchar *start,
4107                                                  const gchar *scanpos,
4108                                                  const gchar **bp_,
4109                                                  const gchar **ep_,
4110                                                  gboolean hdr);
4111                         /* part to URI function */
4112                         gchar    *(*build_uri)  (const gchar *bp,
4113                                                  const gchar *ep);
4114                 };
4115
4116                 static struct table parser[] = {
4117                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4118                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4119                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4120                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4121                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4122                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4123                         {"@",        strcasestr, get_email_part, make_email_string}
4124                 };
4125                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4126                 gint last_index = PARSE_ELEMS;
4127                 gint  n;
4128                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4129                 gint walk_pos;
4130                 
4131                 start = FALSE;
4132                 if (!prev_autowrap && num_blocks == 0) {
4133                         num_blocks++;
4134                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4135                                         G_CALLBACK(text_inserted),
4136                                         compose);
4137                 }
4138                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4139                         goto colorize;
4140
4141                 uri_start = uri_stop = -1;
4142                 quote_len = 0;
4143                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4144
4145                 if (quote_str) {
4146                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4147                         if (startq_offset == -1) 
4148                                 startq_offset = gtk_text_iter_get_offset(&iter);
4149                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4150                         if (quotelevel > 2) {
4151                                 /* recycle colors */
4152                                 if (prefs_common.recycle_quote_colors)
4153                                         quotelevel %= 3;
4154                                 else
4155                                         quotelevel = 2;
4156                         }
4157                         if (!wrap_quote) {
4158                                 goto colorize;
4159                         }
4160                 } else {
4161                         if (startq_offset == -1)
4162                                 noq_offset = gtk_text_iter_get_offset(&iter);
4163                         quotelevel = -1;
4164                 }
4165
4166                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4167                         goto colorize;
4168                 }
4169                 if (gtk_text_iter_ends_line(&iter)) {
4170                         goto colorize;
4171                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4172                                                prefs_common.linewrap_len,
4173                                                quote_len)) {
4174                         GtkTextIter prev, next, cur;
4175
4176                         if (prev_autowrap != FALSE || force) {
4177                                 compose->automatic_break = TRUE;
4178                                 modified = TRUE;
4179                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4180                                 compose->automatic_break = FALSE;
4181                         } else if (quote_str && wrap_quote) {
4182                                 compose->automatic_break = TRUE;
4183                                 modified = TRUE;
4184                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4185                                 compose->automatic_break = FALSE;
4186                         } else 
4187                                 goto colorize;
4188                         /* remove trailing spaces */
4189                         cur = break_pos;
4190                         gtk_text_iter_backward_char(&cur);
4191                         prev = next = cur;
4192                         while (!gtk_text_iter_starts_line(&cur)) {
4193                                 gunichar wc;
4194
4195                                 gtk_text_iter_backward_char(&cur);
4196                                 wc = gtk_text_iter_get_char(&cur);
4197                                 if (!g_unichar_isspace(wc))
4198                                         break;
4199                                 prev = cur;
4200                         }
4201                         if (!gtk_text_iter_equal(&prev, &next)) {
4202                                 gtk_text_buffer_delete(buffer, &prev, &next);
4203                                 break_pos = next;
4204                                 gtk_text_iter_forward_char(&break_pos);
4205                         }
4206
4207                         if (quote_str)
4208                                 gtk_text_buffer_insert(buffer, &break_pos,
4209                                                        quote_str, -1);
4210
4211                         iter = break_pos;
4212                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4213
4214                         /* move iter to current line start */
4215                         gtk_text_iter_set_line_offset(&iter, 0);
4216                         if (quote_str) {
4217                                 g_free(quote_str);
4218                                 quote_str = NULL;
4219                         }
4220                         continue;       
4221                 } else {
4222                         /* move iter to next line start */
4223                         iter = break_pos;
4224                         lines++;
4225                 }
4226
4227 colorize:
4228                 if (!prev_autowrap && num_blocks > 0) {
4229                         num_blocks--;
4230                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4231                                         G_CALLBACK(text_inserted),
4232                                         compose);
4233                 }
4234                 end_of_line = iter;
4235                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4236                         gtk_text_iter_forward_char(&end_of_line);
4237                 }
4238                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4239
4240                 nouri_start = gtk_text_iter_get_offset(&iter);
4241                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4242
4243                 walk_pos = gtk_text_iter_get_offset(&iter);
4244                 /* FIXME: this looks phony. scanning for anything in the parse table */
4245                 for (n = 0; n < PARSE_ELEMS; n++) {
4246                         gchar *tmp;
4247
4248                         tmp = parser[n].search(walk, parser[n].needle);
4249                         if (tmp) {
4250                                 if (scanpos == NULL || tmp < scanpos) {
4251                                         scanpos = tmp;
4252                                         last_index = n;
4253                                 }
4254                         }                                       
4255                 }
4256
4257                 bp = ep = 0;
4258                 if (scanpos) {
4259                         /* check if URI can be parsed */
4260                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4261                                         (const gchar **)&ep, FALSE)
4262                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4263                                         walk = ep;
4264                         } else
4265                                 walk = scanpos +
4266                                         strlen(parser[last_index].needle);
4267                 } 
4268                 if (bp && ep) {
4269                         uri_start = walk_pos + (bp - o_walk);
4270                         uri_stop  = walk_pos + (ep - o_walk);
4271                 }
4272                 g_free(o_walk);
4273                 o_walk = NULL;
4274                 gtk_text_iter_forward_line(&iter);
4275                 g_free(quote_str);
4276                 quote_str = NULL;
4277                 if (startq_offset != -1) {
4278                         GtkTextIter startquote, endquote;
4279                         gtk_text_buffer_get_iter_at_offset(
4280                                 buffer, &startquote, startq_offset);
4281                         endquote = iter;
4282
4283                         switch (quotelevel) {
4284                         case 0: 
4285                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4286                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4287                                         gtk_text_buffer_apply_tag_by_name(
4288                                                 buffer, "quote0", &startquote, &endquote);
4289                                         gtk_text_buffer_remove_tag_by_name(
4290                                                 buffer, "quote1", &startquote, &endquote);
4291                                         gtk_text_buffer_remove_tag_by_name(
4292                                                 buffer, "quote2", &startquote, &endquote);
4293                                         modified = TRUE;
4294                                 }
4295                                 break;
4296                         case 1: 
4297                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4298                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4299                                         gtk_text_buffer_apply_tag_by_name(
4300                                                 buffer, "quote1", &startquote, &endquote);
4301                                         gtk_text_buffer_remove_tag_by_name(
4302                                                 buffer, "quote0", &startquote, &endquote);
4303                                         gtk_text_buffer_remove_tag_by_name(
4304                                                 buffer, "quote2", &startquote, &endquote);
4305                                         modified = TRUE;
4306                                 }
4307                                 break;
4308                         case 2: 
4309                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4310                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4311                                         gtk_text_buffer_apply_tag_by_name(
4312                                                 buffer, "quote2", &startquote, &endquote);
4313                                         gtk_text_buffer_remove_tag_by_name(
4314                                                 buffer, "quote0", &startquote, &endquote);
4315                                         gtk_text_buffer_remove_tag_by_name(
4316                                                 buffer, "quote1", &startquote, &endquote);
4317                                         modified = TRUE;
4318                                 }
4319                                 break;
4320                         }
4321                         startq_offset = -1;
4322                 } else if (noq_offset != -1) {
4323                         GtkTextIter startnoquote, endnoquote;
4324                         gtk_text_buffer_get_iter_at_offset(
4325                                 buffer, &startnoquote, noq_offset);
4326                         endnoquote = iter;
4327
4328                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4329                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4330                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4331                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4332                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4333                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4334                                 gtk_text_buffer_remove_tag_by_name(
4335                                         buffer, "quote0", &startnoquote, &endnoquote);
4336                                 gtk_text_buffer_remove_tag_by_name(
4337                                         buffer, "quote1", &startnoquote, &endnoquote);
4338                                 gtk_text_buffer_remove_tag_by_name(
4339                                         buffer, "quote2", &startnoquote, &endnoquote);
4340                                 modified = TRUE;
4341                         }
4342                         noq_offset = -1;
4343                 }
4344                 
4345                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4346                         GtkTextIter nouri_start_iter, nouri_end_iter;
4347                         gtk_text_buffer_get_iter_at_offset(
4348                                 buffer, &nouri_start_iter, nouri_start);
4349                         gtk_text_buffer_get_iter_at_offset(
4350                                 buffer, &nouri_end_iter, nouri_stop);
4351                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4352                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4353                                 gtk_text_buffer_remove_tag_by_name(
4354                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4355                                 modified_before_remove = modified;
4356                                 modified = TRUE;
4357                                 removed = TRUE;
4358                         }
4359                 }
4360                 if (uri_start >= 0 && uri_stop > 0) {
4361                         GtkTextIter uri_start_iter, uri_end_iter, back;
4362                         gtk_text_buffer_get_iter_at_offset(
4363                                 buffer, &uri_start_iter, uri_start);
4364                         gtk_text_buffer_get_iter_at_offset(
4365                                 buffer, &uri_end_iter, uri_stop);
4366                         back = uri_end_iter;
4367                         gtk_text_iter_backward_char(&back);
4368                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4369                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4370                                 gtk_text_buffer_apply_tag_by_name(
4371                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4372                                 modified = TRUE;
4373                                 if (removed && !modified_before_remove) {
4374                                         modified = FALSE;
4375                                 } 
4376                         }
4377                 }
4378                 if (!modified) {
4379                         debug_print("not modified, out after %d lines\n", lines);
4380                         goto end;
4381                 }
4382         }
4383
4384         debug_print("modified, out after %d lines\n", lines);
4385 end:
4386         if (par_iter)
4387                 *par_iter = iter;
4388         undo_wrapping(compose->undostruct, FALSE);
4389         compose->autowrap = prev_autowrap;
4390         
4391         return modified;
4392 }
4393
4394 void compose_action_cb(void *data)
4395 {
4396         Compose *compose = (Compose *)data;
4397         compose_wrap_all(compose);
4398 }
4399
4400 static void compose_wrap_all(Compose *compose)
4401 {
4402         compose_wrap_all_full(compose, FALSE);
4403 }
4404
4405 static void compose_wrap_all_full(Compose *compose, gboolean force)
4406 {
4407         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4408         GtkTextBuffer *buffer;
4409         GtkTextIter iter;
4410         gboolean modified = TRUE;
4411
4412         buffer = gtk_text_view_get_buffer(text);
4413
4414         gtk_text_buffer_get_start_iter(buffer, &iter);
4415         while (!gtk_text_iter_is_end(&iter) && modified)
4416                 modified = compose_beautify_paragraph(compose, &iter, force);
4417
4418 }
4419
4420 static void compose_set_title(Compose *compose)
4421 {
4422         gchar *str;
4423         gchar *edited;
4424         gchar *subject;
4425         
4426         edited = compose->modified ? _(" [Edited]") : "";
4427         
4428         subject = gtk_editable_get_chars(
4429                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4430
4431 #ifndef MAEMO
4432         if (subject && strlen(subject))
4433                 str = g_strdup_printf(_("%s - Compose message%s"),
4434                                       subject, edited); 
4435         else
4436                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4437 #else
4438         str = g_strdup(_("Compose message"));
4439 #endif
4440
4441         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4442         g_free(str);
4443         g_free(subject);
4444 }
4445
4446 /**
4447  * compose_current_mail_account:
4448  * 
4449  * Find a current mail account (the currently selected account, or the
4450  * default account, if a news account is currently selected).  If a
4451  * mail account cannot be found, display an error message.
4452  * 
4453  * Return value: Mail account, or NULL if not found.
4454  **/
4455 static PrefsAccount *
4456 compose_current_mail_account(void)
4457 {
4458         PrefsAccount *ac;
4459
4460         if (cur_account && cur_account->protocol != A_NNTP)
4461                 ac = cur_account;
4462         else {
4463                 ac = account_get_default();
4464                 if (!ac || ac->protocol == A_NNTP) {
4465                         alertpanel_error(_("Account for sending mail is not specified.\n"
4466                                            "Please select a mail account before sending."));
4467                         return NULL;
4468                 }
4469         }
4470         return ac;
4471 }
4472
4473 #define QUOTE_IF_REQUIRED(out, str)                                     \
4474 {                                                                       \
4475         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4476                 gchar *__tmp;                                           \
4477                 gint len;                                               \
4478                                                                         \
4479                 len = strlen(str) + 3;                                  \
4480                 if ((__tmp = alloca(len)) == NULL) {                    \
4481                         g_warning("can't allocate memory\n");           \
4482                         g_string_free(header, TRUE);                    \
4483                         return NULL;                                    \
4484                 }                                                       \
4485                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4486                 out = __tmp;                                            \
4487         } else {                                                        \
4488                 gchar *__tmp;                                           \
4489                                                                         \
4490                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4491                         g_warning("can't allocate memory\n");           \
4492                         g_string_free(header, TRUE);                    \
4493                         return NULL;                                    \
4494                 } else                                                  \
4495                         strcpy(__tmp, str);                             \
4496                                                                         \
4497                 out = __tmp;                                            \
4498         }                                                               \
4499 }
4500
4501 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4502 {                                                                       \
4503         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4504                 gchar *__tmp;                                           \
4505                 gint len;                                               \
4506                                                                         \
4507                 len = strlen(str) + 3;                                  \
4508                 if ((__tmp = alloca(len)) == NULL) {                    \
4509                         g_warning("can't allocate memory\n");           \
4510                         errret;                                         \
4511                 }                                                       \
4512                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4513                 out = __tmp;                                            \
4514         } else {                                                        \
4515                 gchar *__tmp;                                           \
4516                                                                         \
4517                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4518                         g_warning("can't allocate memory\n");           \
4519                         errret;                                         \
4520                 } else                                                  \
4521                         strcpy(__tmp, str);                             \
4522                                                                         \
4523                 out = __tmp;                                            \
4524         }                                                               \
4525 }
4526
4527 static void compose_select_account(Compose *compose, PrefsAccount *account,
4528                                    gboolean init)
4529 {
4530         GtkItemFactory *ifactory;
4531         gchar *from = NULL;
4532
4533         g_return_if_fail(account != NULL);
4534
4535         compose->account = account;
4536
4537         if (account->name && *account->name) {
4538                 gchar *buf;
4539                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4540                 from = g_strdup_printf("%s <%s>",
4541                                        buf, account->address);
4542                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4543         } else {
4544                 from = g_strdup_printf("<%s>",
4545                                        account->address);
4546                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4547         }
4548
4549         g_free(from);
4550
4551         compose_set_title(compose);
4552
4553         ifactory = gtk_item_factory_from_widget(compose->menubar);
4554
4555         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4556                 menu_set_active(ifactory, "/Options/Sign", TRUE);
4557         else
4558                 menu_set_active(ifactory, "/Options/Sign", FALSE);
4559         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4560                 menu_set_active(ifactory, "/Options/Encrypt", TRUE);
4561         else
4562                 menu_set_active(ifactory, "/Options/Encrypt", FALSE);
4563                                        
4564         activate_privacy_system(compose, account, FALSE);
4565
4566         if (!init && compose->mode != COMPOSE_REDIRECT) {
4567                 undo_block(compose->undostruct);
4568                 compose_insert_sig(compose, TRUE);
4569                 undo_unblock(compose->undostruct);
4570         }
4571
4572 #ifdef USE_ASPELL
4573         /* use account's dict info if set */
4574         if (compose->gtkaspell) {
4575                 if (account->enable_default_dictionary)
4576                         gtkaspell_change_dict(compose->gtkaspell,
4577                                         account->default_dictionary, FALSE);
4578                 if (account->enable_default_alt_dictionary)
4579                         gtkaspell_change_alt_dict(compose->gtkaspell,
4580                                         account->default_alt_dictionary);
4581                 if (account->enable_default_dictionary
4582                         || account->enable_default_alt_dictionary)
4583                         compose_spell_menu_changed(compose);
4584         }
4585 #endif
4586 }
4587
4588 gboolean compose_check_for_valid_recipient(Compose *compose) {
4589         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4590         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4591         gboolean recipient_found = FALSE;
4592         GSList *list;
4593         gchar **strptr;
4594
4595         /* free to and newsgroup list */
4596         slist_free_strings(compose->to_list);
4597         g_slist_free(compose->to_list);
4598         compose->to_list = NULL;
4599                         
4600         slist_free_strings(compose->newsgroup_list);
4601         g_slist_free(compose->newsgroup_list);
4602         compose->newsgroup_list = NULL;
4603
4604         /* search header entries for to and newsgroup entries */
4605         for (list = compose->header_list; list; list = list->next) {
4606                 gchar *header;
4607                 gchar *entry;
4608                 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4609                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4610                 g_strstrip(entry);
4611                 g_strstrip(header);
4612                 if (entry[0] != '\0') {
4613                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4614                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4615                                         compose->to_list = address_list_append(compose->to_list, entry);
4616                                         recipient_found = TRUE;
4617                                 }
4618                         }
4619                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4620                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4621                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4622                                         recipient_found = TRUE;
4623                                 }
4624                         }
4625                 }
4626                 g_free(header);
4627                 g_free(entry);
4628         }
4629         return recipient_found;
4630 }
4631
4632 static gboolean compose_check_for_set_recipients(Compose *compose)
4633 {
4634         if (compose->account->set_autocc && compose->account->auto_cc) {
4635                 gboolean found_other = FALSE;
4636                 GSList *list;
4637                 /* search header entries for to and newsgroup entries */
4638                 for (list = compose->header_list; list; list = list->next) {
4639                         gchar *entry;
4640                         gchar *header;
4641                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4642                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4643                         g_strstrip(entry);
4644                         g_strstrip(header);
4645                         if (strcmp(entry, compose->account->auto_cc)
4646                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4647                                 found_other = TRUE;
4648                                 g_free(entry);
4649                                 break;
4650                         }
4651                         g_free(entry);
4652                         g_free(header);
4653                 }
4654                 if (!found_other) {
4655                         AlertValue aval;
4656                         if (compose->batch) {
4657                                 gtk_widget_show_all(compose->window);
4658                         }
4659                         aval = alertpanel(_("Send"),
4660                                           _("The only recipient is the default CC address. Send anyway?"),
4661                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4662                         if (aval != G_ALERTALTERNATE)
4663                                 return FALSE;
4664                 }
4665         }
4666         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4667                 gboolean found_other = FALSE;
4668                 GSList *list;
4669                 /* search header entries for to and newsgroup entries */
4670                 for (list = compose->header_list; list; list = list->next) {
4671                         gchar *entry;
4672                         gchar *header;
4673                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4674                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4675                         g_strstrip(entry);
4676                         g_strstrip(header);
4677                         if (strcmp(entry, compose->account->auto_bcc)
4678                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4679                                 found_other = TRUE;
4680                                 g_free(entry);
4681                                 break;
4682                         }
4683                         g_free(entry);
4684                         g_free(header);
4685                 }
4686                 if (!found_other) {
4687                         AlertValue aval;
4688                         if (compose->batch) {
4689                                 gtk_widget_show_all(compose->window);
4690                         }
4691                         aval = alertpanel(_("Send"),
4692                                           _("The only recipient is the default BCC address. Send anyway?"),
4693                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4694                         if (aval != G_ALERTALTERNATE)
4695                                 return FALSE;
4696                 }
4697         }
4698         return TRUE;
4699 }
4700
4701 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4702 {
4703         const gchar *str;
4704
4705         if (compose_check_for_valid_recipient(compose) == FALSE) {
4706                 if (compose->batch) {
4707                         gtk_widget_show_all(compose->window);
4708                 }
4709                 alertpanel_error(_("Recipient is not specified."));
4710                 return FALSE;
4711         }
4712
4713         if (compose_check_for_set_recipients(compose) == FALSE) {
4714                 return FALSE;
4715         }
4716
4717         if (!compose->batch) {
4718                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4719                 if (*str == '\0' && check_everything == TRUE && 
4720                     compose->mode != COMPOSE_REDIRECT) {
4721                         AlertValue aval;
4722                         gchar *button_label;
4723                         gchar *message;
4724
4725                         if (compose->sending)
4726                                 button_label = _("+_Send");
4727                         else
4728                                 button_label = _("+_Queue");
4729                         message = g_strdup_printf(_("Subject is empty. %s"),
4730                                         compose->sending?_("Send it anyway?"):
4731                                         _("Queue it anyway?"));
4732
4733                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4734                                           GTK_STOCK_CANCEL, button_label, NULL);
4735                         g_free(message);
4736                         if (aval != G_ALERTALTERNATE)
4737                                 return FALSE;
4738                 }
4739         }
4740
4741         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4742                 return FALSE;
4743
4744         return TRUE;
4745 }
4746
4747 gint compose_send(Compose *compose)
4748 {
4749         gint msgnum;
4750         FolderItem *folder = NULL;
4751         gint val = -1;
4752         gchar *msgpath = NULL;
4753         gboolean discard_window = FALSE;
4754         gchar *errstr = NULL;
4755         gchar *tmsgid = NULL;
4756         MainWindow *mainwin = mainwindow_get_mainwindow();
4757         gboolean queued_removed = FALSE;
4758
4759         if (prefs_common.send_dialog_invisible
4760                         || compose->batch == TRUE)
4761                 discard_window = TRUE;
4762
4763         compose_allow_user_actions (compose, FALSE);
4764         compose->sending = TRUE;
4765
4766         if (compose_check_entries(compose, TRUE) == FALSE) {
4767                 if (compose->batch) {
4768                         gtk_widget_show_all(compose->window);
4769                 }
4770                 goto bail;
4771         }
4772
4773         inc_lock();
4774         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4775
4776         if (val) {
4777                 if (compose->batch) {
4778                         gtk_widget_show_all(compose->window);
4779                 }
4780                 if (val == -4) {
4781                         alertpanel_error(_("Could not queue message for sending:\n\n"
4782                                            "Charset conversion failed."));
4783                 } else if (val == -5) {
4784                         alertpanel_error(_("Could not queue message for sending:\n\n"
4785                                            "Couldn't get recipient encryption key."));
4786                 } else if (val == -6) {
4787                         /* silent error */
4788                 } else if (val == -3) {
4789                         if (privacy_peek_error())
4790                         alertpanel_error(_("Could not queue message for sending:\n\n"
4791                                            "Signature failed: %s"), privacy_get_error());
4792                 } else if (val == -2 && errno != 0) {
4793                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4794                 } else {
4795                         alertpanel_error(_("Could not queue message for sending."));
4796                 }
4797                 goto bail;
4798         }
4799
4800         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4801         if (discard_window) {
4802                 compose->sending = FALSE;
4803                 compose_close(compose);
4804                 /* No more compose access in the normal codepath 
4805                  * after this point! */
4806                 compose = NULL;
4807         }
4808
4809         if (msgnum == 0) {
4810                 alertpanel_error(_("The message was queued but could not be "
4811                                    "sent.\nUse \"Send queued messages\" from "
4812                                    "the main window to retry."));
4813                 if (!discard_window) {
4814                         goto bail;
4815                 }
4816                 inc_unlock();
4817                 g_free(tmsgid);
4818                 return -1;
4819         }
4820         if (msgpath == NULL) {
4821                 msgpath = folder_item_fetch_msg(folder, msgnum);
4822                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4823                 g_free(msgpath);
4824         } else {
4825                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4826                 g_unlink(msgpath);
4827                 g_free(msgpath);
4828         }
4829         if (!discard_window) {
4830                 if (val != 0) {
4831                         if (!queued_removed)
4832                                 folder_item_remove_msg(folder, msgnum);
4833                         folder_item_scan(folder);
4834                         if (tmsgid) {
4835                                 /* make sure we delete that */
4836                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4837                                 if (tmp) {
4838                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4839                                         folder_item_remove_msg(folder, tmp->msgnum);
4840                                         procmsg_msginfo_free(tmp);
4841                                 } 
4842                         }
4843                 }
4844         }
4845
4846         if (val == 0) {
4847                 if (!queued_removed)
4848                         folder_item_remove_msg(folder, msgnum);
4849                 folder_item_scan(folder);
4850                 if (tmsgid) {
4851                         /* make sure we delete that */
4852                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4853                         if (tmp) {
4854                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4855                                 folder_item_remove_msg(folder, tmp->msgnum);
4856                                 procmsg_msginfo_free(tmp);
4857                         }
4858                 }
4859                 if (!discard_window) {
4860                         compose->sending = FALSE;
4861                         compose_allow_user_actions (compose, TRUE);
4862                         compose_close(compose);
4863                 }
4864         } else {
4865                 if (errstr) {
4866                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4867                                    "the main window to retry."), errstr);
4868                         g_free(errstr);
4869                 } else {
4870                         alertpanel_error_log(_("The message was queued but could not be "
4871                                    "sent.\nUse \"Send queued messages\" from "
4872                                    "the main window to retry."));
4873                 }
4874                 if (!discard_window) {
4875                         goto bail;              
4876                 }
4877                 inc_unlock();
4878                 g_free(tmsgid);
4879                 return -1;
4880         }
4881         g_free(tmsgid);
4882         inc_unlock();
4883         toolbar_main_set_sensitive(mainwin);
4884         main_window_set_menu_sensitive(mainwin);
4885         return 0;
4886
4887 bail:
4888         inc_unlock();
4889         g_free(tmsgid);
4890         compose_allow_user_actions (compose, TRUE);
4891         compose->sending = FALSE;
4892         compose->modified = TRUE; 
4893         toolbar_main_set_sensitive(mainwin);
4894         main_window_set_menu_sensitive(mainwin);
4895
4896         return -1;
4897 }
4898
4899 static gboolean compose_use_attach(Compose *compose) 
4900 {
4901         GtkTreeModel *model = gtk_tree_view_get_model
4902                                 (GTK_TREE_VIEW(compose->attach_clist));
4903         return gtk_tree_model_iter_n_children(model, NULL) > 0;
4904 }
4905
4906 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
4907                                                            FILE *fp)
4908 {
4909         gchar buf[BUFFSIZE];
4910         gchar *str;
4911         gboolean first_to_address;
4912         gboolean first_cc_address;
4913         GSList *list;
4914         ComposeHeaderEntry *headerentry;
4915         const gchar *headerentryname;
4916         const gchar *cc_hdr;
4917         const gchar *to_hdr;
4918         gboolean err = FALSE;
4919
4920         debug_print("Writing redirect header\n");
4921
4922         cc_hdr = prefs_common_translated_header_name("Cc:");
4923         to_hdr = prefs_common_translated_header_name("To:");
4924
4925         first_to_address = TRUE;
4926         for (list = compose->header_list; list; list = list->next) {
4927                 headerentry = ((ComposeHeaderEntry *)list->data);
4928                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4929
4930                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4931                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4932                         Xstrdup_a(str, entstr, return -1);
4933                         g_strstrip(str);
4934                         if (str[0] != '\0') {
4935                                 compose_convert_header
4936                                         (compose, buf, sizeof(buf), str,
4937                                         strlen("Resent-To") + 2, TRUE);
4938
4939                                 if (first_to_address) {
4940                                         err |= (fprintf(fp, "Resent-To: ") < 0);
4941                                         first_to_address = FALSE;
4942                                 } else {
4943                                         err |= (fprintf(fp, ",") < 0);
4944                                 }
4945                                 err |= (fprintf(fp, "%s", buf) < 0);
4946                         }
4947                 }
4948         }
4949         if (!first_to_address) {
4950                 err |= (fprintf(fp, "\n") < 0);
4951         }
4952
4953         first_cc_address = TRUE;
4954         for (list = compose->header_list; list; list = list->next) {
4955                 headerentry = ((ComposeHeaderEntry *)list->data);
4956                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4957
4958                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4959                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4960                         Xstrdup_a(str, strg, return -1);
4961                         g_strstrip(str);
4962                         if (str[0] != '\0') {
4963                                 compose_convert_header
4964                                         (compose, buf, sizeof(buf), str,
4965                                         strlen("Resent-Cc") + 2, TRUE);
4966
4967                                 if (first_cc_address) {
4968                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
4969                                         first_cc_address = FALSE;
4970                                 } else {
4971                                         err |= (fprintf(fp, ",") < 0);
4972                                 }
4973                                 err |= (fprintf(fp, "%s", buf) < 0);
4974                         }
4975                 }
4976         }
4977         if (!first_cc_address) {
4978                 err |= (fprintf(fp, "\n") < 0);
4979         }
4980         
4981         return (err ? -1:0);
4982 }
4983
4984 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4985 {
4986         gchar buf[BUFFSIZE];
4987         gchar *str;
4988         const gchar *entstr;
4989         /* struct utsname utsbuf; */
4990         gboolean err = FALSE;
4991
4992         g_return_val_if_fail(fp != NULL, -1);
4993         g_return_val_if_fail(compose->account != NULL, -1);
4994         g_return_val_if_fail(compose->account->address != NULL, -1);
4995
4996         /* Resent-Date */
4997         get_rfc822_date(buf, sizeof(buf));
4998         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4999
5000         /* Resent-From */
5001         if (compose->account->name && *compose->account->name) {
5002                 compose_convert_header
5003                         (compose, buf, sizeof(buf), compose->account->name,
5004                          strlen("From: "), TRUE);
5005                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5006                         buf, compose->account->address) < 0);
5007         } else
5008                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5009
5010         /* Subject */
5011         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5012         if (*entstr != '\0') {
5013                 Xstrdup_a(str, entstr, return -1);
5014                 g_strstrip(str);
5015                 if (*str != '\0') {
5016                         compose_convert_header(compose, buf, sizeof(buf), str,
5017                                                strlen("Subject: "), FALSE);
5018                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5019                 }
5020         }
5021
5022         /* Resent-Message-ID */
5023         if (compose->account->set_domain && compose->account->domain) {
5024                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5025         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5026                 g_snprintf(buf, sizeof(buf), "%s", 
5027                         strchr(compose->account->address, '@') ?
5028                                 strchr(compose->account->address, '@')+1 :
5029                                 compose->account->address);
5030         } else {
5031                 g_snprintf(buf, sizeof(buf), "%s", "");
5032         }
5033
5034         if (compose->account->gen_msgid) {
5035                 generate_msgid(buf, sizeof(buf));
5036                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5037                 compose->msgid = g_strdup(buf);
5038         } else {
5039                 compose->msgid = NULL;
5040         }
5041
5042         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5043                 return -1;
5044
5045         /* separator between header and body */
5046         err |= (fputs("\n", fp) == EOF);
5047
5048         return (err ? -1:0);
5049 }
5050
5051 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5052 {
5053         FILE *fp;
5054         size_t len;
5055         gchar buf[BUFFSIZE];
5056         int i = 0;
5057         gboolean skip = FALSE;
5058         gboolean err = FALSE;
5059         gchar *not_included[]={
5060                 "Return-Path:",         "Delivered-To:",        "Received:",
5061                 "Subject:",             "X-UIDL:",              "AF:",
5062                 "NF:",                  "PS:",                  "SRH:",
5063                 "SFN:",                 "DSR:",                 "MID:",
5064                 "CFG:",                 "PT:",                  "S:",
5065                 "RQ:",                  "SSV:",                 "NSV:",
5066                 "SSH:",                 "R:",                   "MAID:",
5067                 "NAID:",                "RMID:",                "FMID:",
5068                 "SCF:",                 "RRCPT:",               "NG:",
5069                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5070                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5071                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5072                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5073                 NULL
5074                 };
5075         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5076                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5077                 return -1;
5078         }
5079
5080         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5081                 skip = FALSE;
5082                 for (i = 0; not_included[i] != NULL; i++) {
5083                         if (g_ascii_strncasecmp(buf, not_included[i],
5084                                                 strlen(not_included[i])) == 0) {
5085                                 skip = TRUE;
5086                                 break;
5087                         }
5088                 }
5089                 if (skip)
5090                         continue;
5091                 if (fputs(buf, fdest) == -1)
5092                         goto error;
5093
5094                 if (!prefs_common.redirect_keep_from) {
5095                         if (g_ascii_strncasecmp(buf, "From:",
5096                                           strlen("From:")) == 0) {
5097                                 err |= (fputs(" (by way of ", fdest) == EOF);
5098                                 if (compose->account->name
5099                                     && *compose->account->name) {
5100                                         compose_convert_header
5101                                                 (compose, buf, sizeof(buf),
5102                                                  compose->account->name,
5103                                                  strlen("From: "),
5104                                                  FALSE);
5105                                         err |= (fprintf(fdest, "%s <%s>",
5106                                                 buf,
5107                                                 compose->account->address) < 0);
5108                                 } else
5109                                         err |= (fprintf(fdest, "%s",
5110                                                 compose->account->address) < 0);
5111                                 err |= (fputs(")", fdest) == EOF);
5112                         }
5113                 }
5114
5115                 if (fputs("\n", fdest) == -1)
5116                         goto error;
5117         }
5118
5119         if (err)
5120                 goto error;
5121
5122         if (compose_redirect_write_headers(compose, fdest))
5123                 goto error;
5124
5125         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5126                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5127                         goto error;
5128         }
5129
5130         fclose(fp);
5131
5132         return 0;
5133 error:
5134         fclose(fp);
5135
5136         return -1;
5137 }
5138
5139 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5140 {
5141         GtkTextBuffer *buffer;
5142         GtkTextIter start, end;
5143         gchar *chars;
5144         gchar *buf;
5145         const gchar *out_codeset;
5146         EncodingType encoding;
5147         MimeInfo *mimemsg, *mimetext;
5148         gint line;
5149
5150         if (action == COMPOSE_WRITE_FOR_SEND)
5151                 attach_parts = TRUE;
5152
5153         /* create message MimeInfo */
5154         mimemsg = procmime_mimeinfo_new();
5155         mimemsg->type = MIMETYPE_MESSAGE;
5156         mimemsg->subtype = g_strdup("rfc822");
5157         mimemsg->content = MIMECONTENT_MEM;
5158         mimemsg->tmp = TRUE; /* must free content later */
5159         mimemsg->data.mem = compose_get_header(compose);
5160
5161         /* Create text part MimeInfo */
5162         /* get all composed text */
5163         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5164         gtk_text_buffer_get_start_iter(buffer, &start);
5165         gtk_text_buffer_get_end_iter(buffer, &end);
5166         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5167         if (is_ascii_str(chars)) {
5168                 buf = chars;
5169                 chars = NULL;
5170                 out_codeset = CS_US_ASCII;
5171                 encoding = ENC_7BIT;
5172         } else {
5173                 const gchar *src_codeset = CS_INTERNAL;
5174
5175                 out_codeset = conv_get_charset_str(compose->out_encoding);
5176
5177                 if (!out_codeset) {
5178                         gchar *test_conv_global_out = NULL;
5179                         gchar *test_conv_reply = NULL;
5180
5181                         /* automatic mode. be automatic. */
5182                         codeconv_set_strict(TRUE);
5183                         
5184                         out_codeset = conv_get_outgoing_charset_str();
5185                         if (out_codeset) {
5186                                 debug_print("trying to convert to %s\n", out_codeset);
5187                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5188                         }
5189                         
5190                         if (!test_conv_global_out && compose->orig_charset
5191                         &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5192                                 out_codeset = compose->orig_charset;
5193                                 debug_print("failure; trying to convert to %s\n", out_codeset);
5194                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5195                         }
5196                         
5197                         if (!test_conv_global_out && !test_conv_reply) {
5198                                 /* we're lost */
5199                                 out_codeset = CS_INTERNAL;
5200                                 debug_print("failure; finally using %s\n", out_codeset);
5201                         }
5202                         g_free(test_conv_global_out);
5203                         g_free(test_conv_reply);
5204                         codeconv_set_strict(FALSE);
5205                 }
5206
5207                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
5208                         out_codeset = CS_ISO_8859_1;
5209
5210                 if (prefs_common.encoding_method == CTE_BASE64)
5211                         encoding = ENC_BASE64;
5212                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5213                         encoding = ENC_QUOTED_PRINTABLE;
5214                 else if (prefs_common.encoding_method == CTE_8BIT)
5215                         encoding = ENC_8BIT;
5216                 else
5217                         encoding = procmime_get_encoding_for_charset(out_codeset);
5218
5219                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5220                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5221
5222                 if (action == COMPOSE_WRITE_FOR_SEND) {
5223                         codeconv_set_strict(TRUE);
5224                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5225                         codeconv_set_strict(FALSE);
5226
5227                         if (!buf) {
5228                                 AlertValue aval;
5229                                 gchar *msg;
5230
5231                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5232                                                         "to the specified %s charset.\n"
5233                                                         "Send it as %s?"), out_codeset, src_codeset);
5234                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5235                                                       NULL, ALERT_ERROR, G_ALERTDEFAULT);
5236                                 g_free(msg);
5237
5238                                 if (aval != G_ALERTALTERNATE) {
5239                                         g_free(chars);
5240                                         return -3;
5241                                 } else {
5242                                         buf = chars;
5243                                         out_codeset = src_codeset;
5244                                         chars = NULL;
5245                                 }
5246                         }
5247                 } else {
5248                         buf = chars;
5249                         out_codeset = src_codeset;
5250                         chars = NULL;
5251                 }
5252         }
5253         g_free(chars);
5254
5255         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5256                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5257                     strstr(buf, "\nFrom ") != NULL) {
5258                         encoding = ENC_QUOTED_PRINTABLE;
5259                 }
5260         }
5261
5262         mimetext = procmime_mimeinfo_new();
5263         mimetext->content = MIMECONTENT_MEM;
5264         mimetext->tmp = TRUE; /* must free content later */
5265         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5266          * and free the data, which we need later. */
5267         mimetext->data.mem = g_strdup(buf); 
5268         mimetext->type = MIMETYPE_TEXT;
5269         mimetext->subtype = g_strdup("plain");
5270         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5271                             g_strdup(out_codeset));
5272                             
5273         /* protect trailing spaces when signing message */
5274         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5275             privacy_system_can_sign(compose->privacy_system)) {
5276                 encoding = ENC_QUOTED_PRINTABLE;
5277         }
5278         
5279         debug_print("main text: %zd bytes encoded as %s in %d\n",
5280                 strlen(buf), out_codeset, encoding);
5281
5282         /* check for line length limit */
5283         if (action == COMPOSE_WRITE_FOR_SEND &&
5284             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5285             check_line_length(buf, 1000, &line) < 0) {
5286                 AlertValue aval;
5287                 gchar *msg;
5288
5289                 msg = g_strdup_printf
5290                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5291                            "The contents of the message might be broken on the way to the delivery.\n"
5292                            "\n"
5293                            "Send it anyway?"), line + 1);
5294                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5295                 g_free(msg);
5296                 if (aval != G_ALERTALTERNATE) {
5297                         g_free(buf);
5298                         return -1;
5299                 }
5300         }
5301         
5302         if (encoding != ENC_UNKNOWN)
5303                 procmime_encode_content(mimetext, encoding);
5304
5305         /* append attachment parts */
5306         if (compose_use_attach(compose) && attach_parts) {
5307                 MimeInfo *mimempart;
5308                 gchar *boundary = NULL;
5309                 mimempart = procmime_mimeinfo_new();
5310                 mimempart->content = MIMECONTENT_EMPTY;
5311                 mimempart->type = MIMETYPE_MULTIPART;
5312                 mimempart->subtype = g_strdup("mixed");
5313
5314                 do {
5315                         g_free(boundary);
5316                         boundary = generate_mime_boundary(NULL);
5317                 } while (strstr(buf, boundary) != NULL);
5318
5319                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5320                                     boundary);
5321
5322                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5323
5324                 g_node_append(mimempart->node, mimetext->node);
5325                 g_node_append(mimemsg->node, mimempart->node);
5326
5327                 compose_add_attachments(compose, mimempart);
5328         } else
5329                 g_node_append(mimemsg->node, mimetext->node);
5330
5331         g_free(buf);
5332
5333         /* sign message if sending */
5334         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5335             privacy_system_can_sign(compose->privacy_system))
5336                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5337                         return -2;
5338
5339         procmime_write_mimeinfo(mimemsg, fp);
5340         
5341         procmime_mimeinfo_free_all(mimemsg);
5342
5343         return 0;
5344 }
5345
5346 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5347 {
5348         GtkTextBuffer *buffer;
5349         GtkTextIter start, end;
5350         FILE *fp;
5351         size_t len;
5352         gchar *chars, *tmp;
5353
5354         if ((fp = g_fopen(file, "wb")) == NULL) {
5355                 FILE_OP_ERROR(file, "fopen");
5356                 return -1;
5357         }
5358
5359         /* chmod for security */
5360         if (change_file_mode_rw(fp, file) < 0) {
5361                 FILE_OP_ERROR(file, "chmod");
5362                 g_warning("can't change file mode\n");
5363         }
5364
5365         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5366         gtk_text_buffer_get_start_iter(buffer, &start);
5367         gtk_text_buffer_get_end_iter(buffer, &end);
5368         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5369
5370         chars = conv_codeset_strdup
5371                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5372
5373         g_free(tmp);
5374         if (!chars) return -1;
5375
5376         /* write body */
5377         len = strlen(chars);
5378         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5379                 FILE_OP_ERROR(file, "fwrite");
5380                 g_free(chars);
5381                 fclose(fp);
5382                 g_unlink(file);
5383                 return -1;
5384         }
5385
5386         g_free(chars);
5387
5388         if (fclose(fp) == EOF) {
5389                 FILE_OP_ERROR(file, "fclose");
5390                 g_unlink(file);
5391                 return -1;
5392         }
5393         return 0;
5394 }
5395
5396 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5397 {
5398         FolderItem *item;
5399         MsgInfo *msginfo = compose->targetinfo;
5400
5401         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5402         if (!msginfo) return -1;
5403
5404         if (!force && MSG_IS_LOCKED(msginfo->flags))
5405                 return 0;
5406
5407         item = msginfo->folder;
5408         g_return_val_if_fail(item != NULL, -1);
5409
5410         if (procmsg_msg_exist(msginfo) &&
5411             (folder_has_parent_of_type(item, F_QUEUE) ||
5412              folder_has_parent_of_type(item, F_DRAFT) 
5413              || msginfo == compose->autosaved_draft)) {
5414                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5415                         g_warning("can't remove the old message\n");
5416                         return -1;
5417                 } else {
5418                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5419                 }
5420         }
5421
5422         return 0;
5423 }
5424
5425 static void compose_remove_draft(Compose *compose)
5426 {
5427         FolderItem *drafts;
5428         MsgInfo *msginfo = compose->targetinfo;
5429         drafts = account_get_special_folder(compose->account, F_DRAFT);
5430
5431         if (procmsg_msg_exist(msginfo)) {
5432                 folder_item_remove_msg(drafts, msginfo->msgnum);
5433         }
5434
5435 }
5436
5437 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5438                    gboolean remove_reedit_target)
5439 {
5440         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5441 }
5442
5443 static gboolean compose_warn_encryption(Compose *compose)
5444 {
5445         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5446         AlertValue val = G_ALERTALTERNATE;
5447         
5448         if (warning == NULL)
5449                 return TRUE;
5450
5451         val = alertpanel_full(_("Encryption warning"), warning,
5452                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5453                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5454         if (val & G_ALERTDISABLE) {
5455                 val &= ~G_ALERTDISABLE;
5456                 if (val == G_ALERTALTERNATE)
5457                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5458                                 TRUE);
5459         }
5460
5461         if (val == G_ALERTALTERNATE) {
5462                 return TRUE;
5463         } else {
5464                 return FALSE;
5465         } 
5466 }
5467
5468 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5469                               gchar **msgpath, gboolean check_subject,
5470                               gboolean remove_reedit_target)
5471 {
5472         FolderItem *queue;
5473         gchar *tmp;
5474         FILE *fp;
5475         GSList *cur;
5476         gint num;
5477         static gboolean lock = FALSE;
5478         PrefsAccount *mailac = NULL, *newsac = NULL;
5479         gboolean err = FALSE;
5480
5481         debug_print("queueing message...\n");
5482         g_return_val_if_fail(compose->account != NULL, -1);
5483
5484         lock = TRUE;
5485         
5486         if (compose_check_entries(compose, check_subject) == FALSE) {
5487                 lock = FALSE;
5488                 if (compose->batch) {
5489                         gtk_widget_show_all(compose->window);
5490                 }
5491                 return -1;
5492         }
5493
5494         if (!compose->to_list && !compose->newsgroup_list) {
5495                 g_warning("can't get recipient list.");
5496                 lock = FALSE;
5497                 return -1;
5498         }
5499
5500         if (compose->to_list) {
5501                 if (compose->account->protocol != A_NNTP)
5502                         mailac = compose->account;
5503                 else if (cur_account && cur_account->protocol != A_NNTP)
5504                         mailac = cur_account;
5505                 else if (!(mailac = compose_current_mail_account())) {
5506                         lock = FALSE;
5507                         alertpanel_error(_("No account for sending mails available!"));
5508                         return -1;
5509                 }
5510         }
5511
5512         if (compose->newsgroup_list) {
5513                 if (compose->account->protocol == A_NNTP)
5514                         newsac = compose->account;
5515                 else if (!newsac->protocol != A_NNTP) {
5516                         lock = FALSE;
5517                         alertpanel_error(_("No account for posting news available!"));
5518                         return -1;
5519                 }                       
5520         }
5521
5522         /* write queue header */
5523         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5524                               G_DIR_SEPARATOR, compose, (guint) rand());
5525         debug_print("queuing to %s\n", tmp);
5526         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5527                 FILE_OP_ERROR(tmp, "fopen");
5528                 g_free(tmp);
5529                 lock = FALSE;
5530                 return -2;
5531         }
5532
5533         if (change_file_mode_rw(fp, tmp) < 0) {
5534                 FILE_OP_ERROR(tmp, "chmod");
5535                 g_warning("can't change file mode\n");
5536         }
5537
5538         /* queueing variables */
5539         err |= (fprintf(fp, "AF:\n") < 0);
5540         err |= (fprintf(fp, "NF:0\n") < 0);
5541         err |= (fprintf(fp, "PS:10\n") < 0);
5542         err |= (fprintf(fp, "SRH:1\n") < 0);
5543         err |= (fprintf(fp, "SFN:\n") < 0);
5544         err |= (fprintf(fp, "DSR:\n") < 0);
5545         if (compose->msgid)
5546                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5547         else
5548                 err |= (fprintf(fp, "MID:\n") < 0);
5549         err |= (fprintf(fp, "CFG:\n") < 0);
5550         err |= (fprintf(fp, "PT:0\n") < 0);
5551         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5552         err |= (fprintf(fp, "RQ:\n") < 0);
5553         if (mailac)
5554                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5555         else
5556                 err |= (fprintf(fp, "SSV:\n") < 0);
5557         if (newsac)
5558                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5559         else
5560                 err |= (fprintf(fp, "NSV:\n") < 0);
5561         err |= (fprintf(fp, "SSH:\n") < 0);
5562         /* write recepient list */
5563         if (compose->to_list) {
5564                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5565                 for (cur = compose->to_list->next; cur != NULL;
5566                      cur = cur->next)
5567                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5568                 err |= (fprintf(fp, "\n") < 0);
5569         }
5570         /* write newsgroup list */
5571         if (compose->newsgroup_list) {
5572                 err |= (fprintf(fp, "NG:") < 0);
5573                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5574                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5575                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5576                 err |= (fprintf(fp, "\n") < 0);
5577         }
5578         /* Sylpheed account IDs */
5579         if (mailac)
5580                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5581         if (newsac)
5582                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5583
5584         
5585         if (compose->privacy_system != NULL) {
5586                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5587                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5588                 if (compose->use_encryption) {
5589                         gchar *encdata;
5590                         if (!compose_warn_encryption(compose)) {
5591                                 lock = FALSE;
5592                                 fclose(fp);
5593                                 g_unlink(tmp);
5594                                 g_free(tmp);
5595                                 return -6;
5596                         }
5597                         if (mailac && mailac->encrypt_to_self) {
5598                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5599                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5600                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5601                                 g_slist_free(tmp_list);
5602                         } else {
5603                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5604                         }
5605                         if (encdata != NULL) {
5606                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5607                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5608                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5609                                                 encdata) < 0);
5610                                 } /* else we finally dont want to encrypt */
5611                         } else {
5612                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5613                                 /* and if encdata was null, it means there's been a problem in 
5614                                  * key selection */
5615                                 lock = FALSE;
5616                                 fclose(fp);
5617                                 g_unlink(tmp);
5618                                 g_free(tmp);
5619                                 return -5;
5620                         }
5621                         g_free(encdata);
5622                 }
5623         }
5624
5625         /* Save copy folder */
5626         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5627                 gchar *savefolderid;
5628                 
5629                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5630                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5631                 g_free(savefolderid);
5632         }
5633         /* Save copy folder */
5634         if (compose->return_receipt) {
5635                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5636         }
5637         /* Message-ID of message replying to */
5638         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5639                 gchar *folderid;
5640                 
5641                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5642                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5643                 g_free(folderid);
5644         }
5645         /* Message-ID of message forwarding to */
5646         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5647                 gchar *folderid;
5648                 
5649                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5650                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5651                 g_free(folderid);
5652         }
5653
5654         /* end of headers */
5655         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5656
5657         if (compose->redirect_filename != NULL) {
5658                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5659                         lock = FALSE;
5660                         fclose(fp);
5661                         g_unlink(tmp);
5662                         g_free(tmp);
5663                         return -2;
5664                 }
5665         } else {
5666                 gint result = 0;
5667                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5668                         lock = FALSE;
5669                         fclose(fp);
5670                         g_unlink(tmp);
5671                         g_free(tmp);
5672                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5673                 }
5674         }
5675         if (err == TRUE) {
5676                 g_warning("failed to write queue message\n");
5677                 fclose(fp);
5678                 g_unlink(tmp);
5679                 g_free(tmp);
5680                 lock = FALSE;
5681                 return -2;
5682         }
5683         if (fclose(fp) == EOF) {
5684                 FILE_OP_ERROR(tmp, "fclose");
5685                 g_unlink(tmp);
5686                 g_free(tmp);
5687                 lock = FALSE;
5688                 return -2;
5689         }
5690
5691         if (item && *item) {
5692                 queue = *item;
5693         } else {
5694                 queue = account_get_special_folder(compose->account, F_QUEUE);
5695         }
5696         if (!queue) {
5697                 g_warning("can't find queue folder\n");
5698                 g_unlink(tmp);
5699                 g_free(tmp);
5700                 lock = FALSE;
5701                 return -1;
5702         }
5703         folder_item_scan(queue);
5704         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5705                 g_warning("can't queue the message\n");
5706                 g_unlink(tmp);
5707                 g_free(tmp);
5708                 lock = FALSE;
5709                 return -1;
5710         }
5711         
5712         if (msgpath == NULL) {
5713                 g_unlink(tmp);
5714                 g_free(tmp);
5715         } else
5716                 *msgpath = tmp;
5717
5718         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5719                 compose_remove_reedit_target(compose, FALSE);
5720         }
5721
5722         if ((msgnum != NULL) && (item != NULL)) {
5723                 *msgnum = num;
5724                 *item = queue;
5725         }
5726
5727         return 0;
5728 }
5729
5730 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5731 {
5732         AttachInfo *ainfo;
5733         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5734         MimeInfo *mimepart;
5735         struct stat statbuf;
5736         gchar *type, *subtype;
5737         GtkTreeModel *model;
5738         GtkTreeIter iter;
5739
5740         model = gtk_tree_view_get_model(tree_view);
5741         
5742         if (!gtk_tree_model_get_iter_first(model, &iter))
5743                 return;
5744         do {
5745                 gtk_tree_model_get(model, &iter,
5746                                    COL_DATA, &ainfo,
5747                                    -1);
5748                                                            
5749                 mimepart = procmime_mimeinfo_new();
5750                 mimepart->content = MIMECONTENT_FILE;
5751                 mimepart->data.filename = g_strdup(ainfo->file);
5752                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5753                 mimepart->offset = 0;
5754
5755                 stat(ainfo->file, &statbuf);
5756                 mimepart->length = statbuf.st_size;
5757
5758                 type = g_strdup(ainfo->content_type);
5759
5760                 if (!strchr(type, '/')) {
5761                         g_free(type);
5762                         type = g_strdup("application/octet-stream");
5763                 }
5764
5765                 subtype = strchr(type, '/') + 1;
5766                 *(subtype - 1) = '\0';
5767                 mimepart->type = procmime_get_media_type(type);
5768                 mimepart->subtype = g_strdup(subtype);
5769                 g_free(type);
5770
5771                 if (mimepart->type == MIMETYPE_MESSAGE && 
5772                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5773                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5774                 } else {
5775                         if (ainfo->name) {
5776                                 g_hash_table_insert(mimepart->typeparameters,
5777                                             g_strdup("name"), g_strdup(ainfo->name));
5778                                 g_hash_table_insert(mimepart->dispositionparameters,
5779                                             g_strdup("filename"), g_strdup(ainfo->name));
5780                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5781                         }
5782                 }
5783
5784                 if (compose->use_signing) {
5785                         if (ainfo->encoding == ENC_7BIT)
5786                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5787                         else if (ainfo->encoding == ENC_8BIT)
5788                                 ainfo->encoding = ENC_BASE64;
5789                 }
5790                 
5791                 procmime_encode_content(mimepart, ainfo->encoding);
5792
5793                 g_node_append(parent->node, mimepart->node);
5794         } while (gtk_tree_model_iter_next(model, &iter));
5795 }
5796
5797 #define IS_IN_CUSTOM_HEADER(header) \
5798         (compose->account->add_customhdr && \
5799          custom_header_find(compose->account->customhdr_list, header) != NULL)
5800
5801 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5802                                                     GString *header, 
5803                                                     const gchar *fieldname,
5804                                                     const gchar *seperator)
5805 {
5806         gchar *str, *fieldname_w_colon;
5807         gboolean add_field = FALSE;
5808         GSList *list;
5809         ComposeHeaderEntry *headerentry;
5810         const gchar *headerentryname;
5811         const gchar *trans_fieldname;
5812         GString *fieldstr;
5813
5814         if (IS_IN_CUSTOM_HEADER(fieldname))
5815                 return;
5816
5817         debug_print("Adding %s-fields\n", fieldname);
5818
5819         fieldstr = g_string_sized_new(64);
5820
5821         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5822         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5823
5824         for (list = compose->header_list; list; list = list->next) {
5825                 headerentry = ((ComposeHeaderEntry *)list->data);
5826                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
5827
5828                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5829                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5830                         g_strstrip(str);
5831                         if (str[0] != '\0') {
5832                                 if (add_field)
5833                                         g_string_append(fieldstr, seperator);
5834                                 g_string_append(fieldstr, str);
5835                                 add_field = TRUE;
5836                         }
5837                         g_free(str);
5838                 }
5839         }
5840         if (add_field) {
5841                 gchar *buf;
5842
5843                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5844                 compose_convert_header
5845                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5846                         strlen(fieldname) + 2, TRUE);
5847                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5848                 g_free(buf);
5849         }
5850
5851         g_free(fieldname_w_colon);
5852         g_string_free(fieldstr, TRUE);
5853
5854         return;
5855 }
5856
5857 static gchar *compose_get_header(Compose *compose)
5858 {
5859         gchar buf[BUFFSIZE];
5860         const gchar *entry_str;
5861         gchar *str;
5862         gchar *name;
5863         GSList *list;
5864         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5865         GString *header;
5866         gchar *from_name = NULL, *from_address = NULL;
5867         gchar *tmp;
5868
5869         g_return_val_if_fail(compose->account != NULL, NULL);
5870         g_return_val_if_fail(compose->account->address != NULL, NULL);
5871
5872         header = g_string_sized_new(64);
5873
5874         /* Date */
5875         get_rfc822_date(buf, sizeof(buf));
5876         g_string_append_printf(header, "Date: %s\n", buf);
5877
5878         /* From */
5879         
5880         if (compose->account->name && *compose->account->name) {
5881                 gchar *buf;
5882                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5883                 tmp = g_strdup_printf("%s <%s>",
5884                         buf, compose->account->address);
5885         } else {
5886                 tmp = g_strdup_printf("%s",
5887                         compose->account->address);
5888         }
5889         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5890         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5891                 /* use default */
5892                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5893                 from_address = g_strdup(compose->account->address);
5894         } else {
5895                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5896                 /* extract name and address */
5897                 if (strstr(spec, " <") && strstr(spec, ">")) {
5898                         from_address = g_strdup(strrchr(spec, '<')+1);
5899                         *(strrchr(from_address, '>')) = '\0';
5900                         from_name = g_strdup(spec);
5901                         *(strrchr(from_name, '<')) = '\0';
5902                 } else {
5903                         from_name = NULL;
5904                         from_address = g_strdup(spec);
5905                 }
5906                 g_free(spec);
5907         }
5908         g_free(tmp);
5909         
5910         
5911         if (from_name && *from_name) {
5912                 compose_convert_header
5913                         (compose, buf, sizeof(buf), from_name,
5914                          strlen("From: "), TRUE);
5915                 QUOTE_IF_REQUIRED(name, buf);
5916                 
5917                 g_string_append_printf(header, "From: %s <%s>\n",
5918                         name, from_address);
5919         } else
5920                 g_string_append_printf(header, "From: %s\n", from_address);
5921         
5922         g_free(from_name);
5923         g_free(from_address);
5924
5925         /* To */
5926         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5927
5928         /* Newsgroups */
5929         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5930
5931         /* Cc */
5932         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5933
5934         /* Bcc */
5935         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5936
5937         /* Subject */
5938         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5939
5940         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5941                 g_strstrip(str);
5942                 if (*str != '\0') {
5943                         compose_convert_header(compose, buf, sizeof(buf), str,
5944                                                strlen("Subject: "), FALSE);
5945                         g_string_append_printf(header, "Subject: %s\n", buf);
5946                 }
5947         }
5948         g_free(str);
5949
5950         /* Message-ID */
5951         if (compose->account->set_domain && compose->account->domain) {
5952                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5953         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5954                 g_snprintf(buf, sizeof(buf), "%s", 
5955                         strchr(compose->account->address, '@') ?
5956                                 strchr(compose->account->address, '@')+1 :
5957                                 compose->account->address);
5958         } else {
5959                 g_snprintf(buf, sizeof(buf), "%s", "");
5960         }
5961         
5962         if (compose->account->gen_msgid) {
5963                 generate_msgid(buf, sizeof(buf));
5964                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5965                 compose->msgid = g_strdup(buf);
5966         } else {
5967                 compose->msgid = NULL;
5968         }
5969
5970         if (compose->remove_references == FALSE) {
5971                 /* In-Reply-To */
5972                 if (compose->inreplyto && compose->to_list)
5973                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5974         
5975                 /* References */
5976                 if (compose->references)
5977                         g_string_append_printf(header, "References: %s\n", compose->references);
5978         }
5979
5980         /* Followup-To */
5981         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5982
5983         /* Reply-To */
5984         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5985
5986         /* Organization */
5987         if (compose->account->organization &&
5988             strlen(compose->account->organization) &&
5989             !IS_IN_CUSTOM_HEADER("Organization")) {
5990                 compose_convert_header(compose, buf, sizeof(buf),
5991                                        compose->account->organization,
5992                                        strlen("Organization: "), FALSE);
5993                 g_string_append_printf(header, "Organization: %s\n", buf);
5994         }
5995
5996         /* Program version and system info */
5997         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5998             !compose->newsgroup_list) {
5999                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6000                         prog_version,
6001                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6002                         TARGET_ALIAS);
6003         }
6004         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6005                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6006                         prog_version,
6007                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6008                         TARGET_ALIAS);
6009         }
6010
6011         /* custom headers */
6012         if (compose->account->add_customhdr) {
6013                 GSList *cur;
6014
6015                 for (cur = compose->account->customhdr_list; cur != NULL;
6016                      cur = cur->next) {
6017                         CustomHeader *chdr = (CustomHeader *)cur->data;
6018
6019                         if (custom_header_is_allowed(chdr->name)) {
6020                                 compose_convert_header
6021                                         (compose, buf, sizeof(buf),
6022                                          chdr->value ? chdr->value : "",
6023                                          strlen(chdr->name) + 2, FALSE);
6024                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6025                         }
6026                 }
6027         }
6028
6029         /* PRIORITY */
6030         switch (compose->priority) {
6031                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6032                                                    "X-Priority: 1 (Highest)\n");
6033                         break;
6034                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6035                                                 "X-Priority: 2 (High)\n");
6036                         break;
6037                 case PRIORITY_NORMAL: break;
6038                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6039                                                "X-Priority: 4 (Low)\n");
6040                         break;
6041                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6042                                                   "X-Priority: 5 (Lowest)\n");
6043                         break;
6044                 default: debug_print("compose: priority unknown : %d\n",
6045                                      compose->priority);
6046         }
6047
6048         /* Request Return Receipt */
6049         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6050                 if (compose->return_receipt) {
6051                         if (compose->account->name
6052                             && *compose->account->name) {
6053                                 compose_convert_header(compose, buf, sizeof(buf), 
6054                                                        compose->account->name, 
6055                                                        strlen("Disposition-Notification-To: "),
6056                                                        TRUE);
6057                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6058                         } else
6059                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6060                 }
6061         }
6062
6063         /* get special headers */
6064         for (list = compose->header_list; list; list = list->next) {
6065                 ComposeHeaderEntry *headerentry;
6066                 gchar *tmp;
6067                 gchar *headername;
6068                 gchar *headername_wcolon;
6069                 const gchar *headername_trans;
6070                 gchar *headervalue;
6071                 gchar **string;
6072                 gboolean standard_header = FALSE;
6073
6074                 headerentry = ((ComposeHeaderEntry *)list->data);
6075
6076                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child)));
6077                 g_strstrip(tmp);
6078                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6079                         g_free(tmp);
6080                         continue;
6081                 }
6082
6083                 if (!strstr(tmp, ":")) {
6084                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6085                         headername = g_strdup(tmp);
6086                 } else {
6087                         headername_wcolon = g_strdup(tmp);
6088                         headername = g_strdup(strtok(tmp, ":"));
6089                 }
6090                 g_free(tmp);
6091                 
6092                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6093                 Xstrdup_a(headervalue, entry_str, return NULL);
6094                 subst_char(headervalue, '\r', ' ');
6095                 subst_char(headervalue, '\n', ' ');
6096                 string = std_headers;
6097                 while (*string != NULL) {
6098                         headername_trans = prefs_common_translated_header_name(*string);
6099                         if (!strcmp(headername_trans, headername_wcolon))
6100                                 standard_header = TRUE;
6101                         string++;
6102                 }
6103                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6104                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6105                                 
6106                 g_free(headername);
6107                 g_free(headername_wcolon);              
6108         }
6109
6110         str = header->str;
6111         g_string_free(header, FALSE);
6112
6113         return str;
6114 }
6115
6116 #undef IS_IN_CUSTOM_HEADER
6117
6118 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6119                                    gint header_len, gboolean addr_field)
6120 {
6121         gchar *tmpstr = NULL;
6122         const gchar *out_codeset = NULL;
6123
6124         g_return_if_fail(src != NULL);
6125         g_return_if_fail(dest != NULL);
6126
6127         if (len < 1) return;
6128
6129         tmpstr = g_strdup(src);
6130
6131         subst_char(tmpstr, '\n', ' ');
6132         subst_char(tmpstr, '\r', ' ');
6133         g_strchomp(tmpstr);
6134
6135         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6136                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6137                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6138                 g_free(tmpstr);
6139                 tmpstr = mybuf;
6140         }
6141
6142         codeconv_set_strict(TRUE);
6143         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6144                 conv_get_charset_str(compose->out_encoding));
6145         codeconv_set_strict(FALSE);
6146         
6147         if (!dest || *dest == '\0') {
6148                 gchar *test_conv_global_out = NULL;
6149                 gchar *test_conv_reply = NULL;
6150
6151                 /* automatic mode. be automatic. */
6152                 codeconv_set_strict(TRUE);
6153
6154                 out_codeset = conv_get_outgoing_charset_str();
6155                 if (out_codeset) {
6156                         debug_print("trying to convert to %s\n", out_codeset);
6157                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6158                 }
6159
6160                 if (!test_conv_global_out && compose->orig_charset
6161                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6162                         out_codeset = compose->orig_charset;
6163                         debug_print("failure; trying to convert to %s\n", out_codeset);
6164                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6165                 }
6166
6167                 if (!test_conv_global_out && !test_conv_reply) {
6168                         /* we're lost */
6169                         out_codeset = CS_INTERNAL;
6170                         debug_print("finally using %s\n", out_codeset);
6171                 }
6172                 g_free(test_conv_global_out);
6173                 g_free(test_conv_reply);
6174                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6175                                         out_codeset);
6176                 codeconv_set_strict(FALSE);
6177         }
6178         g_free(tmpstr);
6179 }
6180
6181 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6182 {
6183         gchar *address;
6184
6185         g_return_if_fail(user_data != NULL);
6186
6187         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6188         g_strstrip(address);
6189         if (*address != '\0') {
6190                 gchar *name = procheader_get_fromname(address);
6191                 extract_address(address);
6192                 addressbook_add_contact(name, address, NULL, NULL);
6193         }
6194         g_free(address);
6195 }
6196
6197 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6198 {
6199         GtkWidget *menuitem;
6200         gchar *address;
6201
6202         g_return_if_fail(menu != NULL);
6203         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6204
6205         menuitem = gtk_separator_menu_item_new();
6206         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6207         gtk_widget_show(menuitem);
6208
6209         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6210         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6211
6212         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6213         g_strstrip(address);
6214         if (*address == '\0') {
6215                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6216         }
6217
6218         g_signal_connect(G_OBJECT(menuitem), "activate",
6219                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6220         gtk_widget_show(menuitem);
6221 }
6222
6223 static void compose_create_header_entry(Compose *compose) 
6224 {
6225         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6226
6227         GtkWidget *combo;
6228         GtkWidget *entry;
6229         gchar **string;
6230         const gchar *header = NULL;
6231         ComposeHeaderEntry *headerentry;
6232         gboolean standard_header = FALSE;
6233         
6234         headerentry = g_new0(ComposeHeaderEntry, 1);
6235
6236         /* Combo box */
6237         combo = gtk_combo_box_entry_new_text();
6238         string = headers; 
6239         while(*string != NULL) {
6240                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6241                         (gchar*)prefs_common_translated_header_name(*string));
6242                 string++;
6243         }
6244         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6245         g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6246                          G_CALLBACK(compose_grab_focus_cb), compose);
6247         gtk_widget_show(combo);
6248         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6249                         compose->header_nextrow, compose->header_nextrow+1,
6250                         GTK_SHRINK, GTK_FILL, 0, 0);
6251         if (compose->header_last) {     
6252                 const gchar *last_header_entry = gtk_entry_get_text(
6253                                 GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6254                 string = headers;
6255                 while (*string != NULL) {
6256                         if (!strcmp(*string, last_header_entry))
6257                                 standard_header = TRUE;
6258                         string++;
6259                 }
6260                 if (standard_header)
6261                         header = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6262         }
6263         if (!compose->header_last || !standard_header) {
6264                 switch(compose->account->protocol) {
6265                         case A_NNTP:
6266                                 header = prefs_common_translated_header_name("Newsgroups:");
6267                                 break;
6268                         default:
6269                                 header = prefs_common_translated_header_name("To:");
6270                                 break;
6271                 }                                                                   
6272         }
6273         if (header)
6274                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), header);
6275
6276         g_signal_connect_after(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6277                          G_CALLBACK(compose_grab_focus_cb), compose);
6278
6279         /* Entry field */
6280         entry = gtk_entry_new(); 
6281         gtk_widget_show(entry);
6282         gtk_tooltips_set_tip(compose->tooltips, entry,
6283                 _("Use <tab> to autocomplete from addressbook"), NULL);
6284         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6285                         compose->header_nextrow, compose->header_nextrow+1,
6286                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6287
6288         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6289                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6290                          headerentry);
6291         g_signal_connect(G_OBJECT(entry), "changed", 
6292                          G_CALLBACK(compose_headerentry_changed_cb), 
6293                          headerentry);
6294         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6295                          G_CALLBACK(compose_grab_focus_cb), compose);
6296                          
6297         /* email dnd */
6298         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6299                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6300                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6301         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6302                          G_CALLBACK(compose_header_drag_received_cb),
6303                          entry);
6304         g_signal_connect(G_OBJECT(entry), "drag-drop",
6305                          G_CALLBACK(compose_drag_drop),
6306                          compose);
6307         g_signal_connect(G_OBJECT(entry), "populate-popup",
6308                          G_CALLBACK(compose_entry_popup_extend),
6309                          NULL);
6310         
6311         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6312
6313         headerentry->compose = compose;
6314         headerentry->combo = combo;
6315         headerentry->entry = entry;
6316         headerentry->headernum = compose->header_nextrow;
6317
6318         compose->header_nextrow++;
6319         compose->header_last = headerentry;             
6320         compose->header_list =
6321                 g_slist_append(compose->header_list,
6322                                headerentry);
6323 }
6324
6325 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6326 {
6327         ComposeHeaderEntry *last_header;
6328         
6329         last_header = compose->header_last;
6330
6331         gtk_entry_set_text(GTK_ENTRY(GTK_BIN(last_header->combo)->child), header);
6332         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6333 }
6334
6335 static void compose_remove_header_entries(Compose *compose) 
6336 {
6337         GSList *list;
6338         for (list = compose->header_list; list; list = list->next) {
6339                 ComposeHeaderEntry *headerentry = 
6340                         (ComposeHeaderEntry *)list->data;
6341                 gtk_widget_destroy(headerentry->combo);
6342                 gtk_widget_destroy(headerentry->entry);
6343                 g_free(headerentry);
6344         }
6345         compose->header_last = NULL;
6346         g_slist_free(compose->header_list);
6347         compose->header_list = NULL;
6348         compose->header_nextrow = 1;
6349         compose_create_header_entry(compose);
6350 }
6351
6352 static GtkWidget *compose_create_header(Compose *compose) 
6353 {
6354         GtkWidget *from_optmenu_hbox;
6355         GtkWidget *header_scrolledwin;
6356         GtkWidget *header_table;
6357
6358         gint count = 0;
6359
6360         /* header labels and entries */
6361         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6362         gtk_widget_show(header_scrolledwin);
6363         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6364
6365         header_table = gtk_table_new(2, 2, FALSE);
6366         gtk_widget_show(header_table);
6367         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6368         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6369         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_NONE);
6370         count = 0;
6371
6372         /* option menu for selecting accounts */
6373         from_optmenu_hbox = compose_account_option_menu_create(compose);
6374         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6375                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6376         count++;
6377
6378         compose->header_table = header_table;
6379         compose->header_list = NULL;
6380         compose->header_nextrow = count;
6381
6382         compose_create_header_entry(compose);
6383
6384         compose->table            = NULL;
6385
6386         return header_scrolledwin ;
6387 }
6388
6389 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6390 {
6391         Compose *compose = (Compose *)data;
6392         GdkEventButton event;
6393         
6394         event.button = 3;
6395         event.time = gtk_get_current_event_time();
6396
6397         return attach_button_pressed(compose->attach_clist, &event, compose);
6398 }
6399
6400 static GtkWidget *compose_create_attach(Compose *compose)
6401 {
6402         GtkWidget *attach_scrwin;
6403         GtkWidget *attach_clist;
6404
6405         GtkListStore *store;
6406         GtkCellRenderer *renderer;
6407         GtkTreeViewColumn *column;
6408         GtkTreeSelection *selection;
6409
6410         /* attachment list */
6411         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6412         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6413                                        GTK_POLICY_AUTOMATIC,
6414                                        GTK_POLICY_AUTOMATIC);
6415         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6416
6417         store = gtk_list_store_new(N_ATTACH_COLS, 
6418                                    G_TYPE_STRING,
6419                                    G_TYPE_STRING,
6420                                    G_TYPE_STRING,
6421                                    G_TYPE_POINTER,
6422                                    G_TYPE_AUTO_POINTER,
6423                                    -1);
6424         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6425                                         (GTK_TREE_MODEL(store)));
6426         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6427         g_object_unref(store);
6428         
6429         renderer = gtk_cell_renderer_text_new();
6430         column = gtk_tree_view_column_new_with_attributes
6431                         (_("Mime type"), renderer, "text", 
6432                          COL_MIMETYPE, NULL);
6433         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6434         
6435         renderer = gtk_cell_renderer_text_new();
6436         column = gtk_tree_view_column_new_with_attributes
6437                         (_("Size"), renderer, "text", 
6438                          COL_SIZE, NULL);
6439         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6440         
6441         renderer = gtk_cell_renderer_text_new();
6442         column = gtk_tree_view_column_new_with_attributes
6443                         (_("Name"), renderer, "text", 
6444                          COL_NAME, NULL);
6445         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6446
6447         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6448                                      prefs_common.use_stripes_everywhere);
6449         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6450         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6451
6452         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6453                          G_CALLBACK(attach_selected), compose);
6454         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6455                          G_CALLBACK(attach_button_pressed), compose);
6456 #ifndef MAEMO
6457         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6458                          G_CALLBACK(popup_attach_button_pressed), compose);
6459 #else
6460         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6461                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6462         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6463                          G_CALLBACK(popup_attach_button_pressed), compose);
6464 #endif
6465         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6466                          G_CALLBACK(attach_key_pressed), compose);
6467
6468         /* drag and drop */
6469         gtk_drag_dest_set(attach_clist,
6470                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6471                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6472                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6473         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6474                          G_CALLBACK(compose_attach_drag_received_cb),
6475                          compose);
6476         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6477                          G_CALLBACK(compose_drag_drop),
6478                          compose);
6479
6480         compose->attach_scrwin = attach_scrwin;
6481         compose->attach_clist  = attach_clist;
6482
6483         return attach_scrwin;
6484 }
6485
6486 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6487 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6488
6489 static GtkWidget *compose_create_others(Compose *compose)
6490 {
6491         GtkWidget *table;
6492         GtkWidget *savemsg_checkbtn;
6493         GtkWidget *savemsg_entry;
6494         GtkWidget *savemsg_select;
6495         
6496         guint rowcount = 0;
6497         gchar *folderidentifier;
6498
6499         /* Table for settings */
6500         table = gtk_table_new(3, 1, FALSE);
6501         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6502         gtk_widget_show(table);
6503         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6504         rowcount = 0;
6505
6506         /* Save Message to folder */
6507         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6508         gtk_widget_show(savemsg_checkbtn);
6509         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6510         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6511                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6512         }
6513         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6514                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6515
6516         savemsg_entry = gtk_entry_new();
6517         gtk_widget_show(savemsg_entry);
6518         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6519         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6520         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6521                          G_CALLBACK(compose_grab_focus_cb), compose);
6522         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6523                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6524                                   (compose->account, F_OUTBOX));
6525                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6526                 g_free(folderidentifier);
6527         }
6528
6529         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6530         gtk_widget_show(savemsg_select);
6531         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6532         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6533                          G_CALLBACK(compose_savemsg_select_cb),
6534                          compose);
6535
6536         rowcount++;
6537
6538         compose->savemsg_checkbtn = savemsg_checkbtn;
6539         compose->savemsg_entry = savemsg_entry;
6540
6541         return table;   
6542 }
6543
6544 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6545 {
6546         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6547                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6548 }
6549
6550 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6551 {
6552         FolderItem *dest;
6553         gchar * path;
6554
6555         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6556         if (!dest) return;
6557
6558         path = folder_item_get_identifier(dest);
6559
6560         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6561         g_free(path);
6562 }
6563
6564 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6565                                   GdkAtom clip, GtkTextIter *insert_place);
6566
6567
6568 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6569                                        Compose *compose)
6570 {
6571         gint prev_autowrap;
6572         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6573 #if USE_ASPELL
6574         if (event->button == 3) {
6575                 GtkTextIter iter;
6576                 GtkTextIter sel_start, sel_end;
6577                 gboolean stuff_selected;
6578                 gint x, y;
6579                 /* move the cursor to allow GtkAspell to check the word
6580                  * under the mouse */
6581                 if (event->x && event->y) {
6582                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6583                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6584                                 &x, &y);
6585                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6586                                 &iter, x, y);
6587                 } else {
6588                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6589                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6590                 }
6591                 /* get selection */
6592                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6593                                 buffer,
6594                                 &sel_start, &sel_end);
6595
6596                 gtk_text_buffer_place_cursor (buffer, &iter);
6597                 /* reselect stuff */
6598                 if (stuff_selected 
6599                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6600                         gtk_text_buffer_select_range(buffer,
6601                                 &sel_start, &sel_end);
6602                 }
6603                 return FALSE; /* pass the event so that the right-click goes through */
6604         }
6605 #endif
6606         if (event->button == 2) {
6607                 GtkTextIter iter;
6608                 gint x, y;
6609                 BLOCK_WRAP();
6610                 
6611                 /* get the middle-click position to paste at the correct place */
6612                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6613                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6614                         &x, &y);
6615                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6616                         &iter, x, y);
6617                 
6618                 entry_paste_clipboard(compose, text, 
6619                                 prefs_common.linewrap_pastes,
6620                                 GDK_SELECTION_PRIMARY, &iter);
6621                 UNBLOCK_WRAP();
6622                 return TRUE;
6623         }
6624         return FALSE;
6625 }
6626
6627 #if USE_ASPELL
6628 static void compose_spell_menu_changed(void *data)
6629 {
6630         Compose *compose = (Compose *)data;
6631         GSList *items;
6632         GtkWidget *menuitem;
6633         GtkWidget *parent_item;
6634         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6635         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6636         GSList *spell_menu;
6637
6638         if (compose->gtkaspell == NULL)
6639                 return;
6640
6641         parent_item = gtk_item_factory_get_item(ifactory, 
6642                         "/Spelling/Options");
6643
6644         /* setting the submenu removes /Spelling/Options from the factory 
6645          * so we need to save it */
6646
6647         if (parent_item == NULL) {
6648                 parent_item = compose->aspell_options_menu;
6649                 gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
6650         } else
6651                 compose->aspell_options_menu = parent_item;
6652
6653         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6654
6655         spell_menu = g_slist_reverse(spell_menu);
6656         for (items = spell_menu;
6657              items; items = items->next) {
6658                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6659                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6660                 gtk_widget_show(GTK_WIDGET(menuitem));
6661         }
6662         g_slist_free(spell_menu);
6663
6664         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6665         
6666 }
6667 #endif
6668
6669 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6670 {
6671         Compose *compose = (Compose *)data;
6672         GdkEventButton event;
6673         
6674         event.button = 3;
6675         event.time = gtk_get_current_event_time();
6676         event.x = 0;
6677         event.y = 0;
6678
6679         return text_clicked(compose->text, &event, compose);
6680 }
6681
6682 static gboolean compose_force_window_origin = TRUE;
6683 static Compose *compose_create(PrefsAccount *account,
6684                                                  FolderItem *folder,
6685                                                  ComposeMode mode,
6686                                                  gboolean batch)
6687 {
6688         Compose   *compose;
6689         GtkWidget *window;
6690         GtkWidget *vbox;
6691         GtkWidget *menubar;
6692         GtkWidget *handlebox;
6693
6694         GtkWidget *notebook;
6695         
6696         GtkWidget *attach_hbox;
6697         GtkWidget *attach_lab1;
6698         GtkWidget *attach_lab2;
6699
6700         GtkWidget *vbox2;
6701
6702         GtkWidget *label;
6703         GtkWidget *subject_hbox;
6704         GtkWidget *subject_frame;
6705         GtkWidget *subject_entry;
6706         GtkWidget *subject;
6707         GtkWidget *paned;
6708
6709         GtkWidget *edit_vbox;
6710         GtkWidget *ruler_hbox;
6711         GtkWidget *ruler;
6712         GtkWidget *scrolledwin;
6713         GtkWidget *text;
6714         GtkTextBuffer *buffer;
6715         GtkClipboard *clipboard;
6716
6717         UndoMain *undostruct;
6718
6719         gchar *titles[N_ATTACH_COLS];
6720         guint n_menu_entries;
6721         GtkWidget *popupmenu;
6722         GtkItemFactory *popupfactory;
6723         GtkItemFactory *ifactory;
6724         GtkWidget *tmpl_menu;
6725         gint n_entries;
6726         GtkWidget *menuitem;
6727
6728 #if USE_ASPELL
6729         GtkAspell * gtkaspell = NULL;
6730 #endif
6731
6732         static GdkGeometry geometry;
6733
6734         g_return_val_if_fail(account != NULL, NULL);
6735
6736         debug_print("Creating compose window...\n");
6737         compose = g_new0(Compose, 1);
6738
6739         titles[COL_MIMETYPE] = _("MIME type");
6740         titles[COL_SIZE]     = _("Size");
6741         titles[COL_NAME]     = _("Name");
6742
6743         compose->batch = batch;
6744         compose->account = account;
6745         compose->folder = folder;
6746         
6747         compose->mutex = g_mutex_new();
6748         compose->set_cursor_pos = -1;
6749
6750         compose->tooltips = gtk_tooltips_new();
6751
6752         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6753
6754         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6755         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6756
6757         if (!geometry.max_width) {
6758                 geometry.max_width = gdk_screen_width();
6759                 geometry.max_height = gdk_screen_height();
6760         }
6761
6762         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6763                                       &geometry, GDK_HINT_MAX_SIZE);
6764         if (!geometry.min_width) {
6765                 geometry.min_width = 600;
6766                 geometry.min_height = 480;
6767         }
6768         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6769                                       &geometry, GDK_HINT_MIN_SIZE);
6770
6771 #ifndef MAEMO   
6772         if (compose_force_window_origin)
6773                 gtk_widget_set_uposition(window, prefs_common.compose_x, 
6774                                  prefs_common.compose_y);
6775 #endif
6776         g_signal_connect(G_OBJECT(window), "delete_event",
6777                          G_CALLBACK(compose_delete_cb), compose);
6778         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6779         gtk_widget_realize(window);
6780
6781         gtkut_widget_set_composer_icon(window);
6782
6783         vbox = gtk_vbox_new(FALSE, 0);
6784         gtk_container_add(GTK_CONTAINER(window), vbox);
6785
6786         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
6787         menubar = menubar_create(window, compose_entries,
6788                                  n_menu_entries, "<Compose>", compose);
6789         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6790
6791         if (prefs_common.toolbar_detachable) {
6792                 handlebox = gtk_handle_box_new();
6793         } else {
6794                 handlebox = gtk_hbox_new(FALSE, 0);
6795         }
6796         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6797
6798         gtk_widget_realize(handlebox);
6799 #ifdef MAEMO
6800         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6801                                           (gpointer)compose);
6802 #else
6803         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6804                                           (gpointer)compose);
6805 #endif
6806
6807         vbox2 = gtk_vbox_new(FALSE, 2);
6808         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6809         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6810         
6811         /* Notebook */
6812         notebook = gtk_notebook_new();
6813         gtk_widget_set_size_request(notebook, -1, 130);
6814         gtk_widget_show(notebook);
6815
6816         /* header labels and entries */
6817         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6818                         compose_create_header(compose),
6819                         gtk_label_new_with_mnemonic(_("Hea_der")));
6820         /* attachment list */
6821         attach_hbox = gtk_hbox_new(FALSE, 0);
6822         gtk_widget_show(attach_hbox);
6823         
6824         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6825         gtk_widget_show(attach_lab1);
6826         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6827         
6828         attach_lab2 = gtk_label_new("");
6829         gtk_widget_show(attach_lab2);
6830         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6831         
6832         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6833                         compose_create_attach(compose),
6834                         attach_hbox);
6835         /* Others Tab */
6836         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6837                         compose_create_others(compose),
6838                         gtk_label_new_with_mnemonic(_("Othe_rs")));
6839
6840         /* Subject */
6841         subject_hbox = gtk_hbox_new(FALSE, 0);
6842         gtk_widget_show(subject_hbox);
6843
6844         subject_frame = gtk_frame_new(NULL);
6845         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6846         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6847         gtk_widget_show(subject_frame);
6848
6849         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6850         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6851         gtk_widget_show(subject);
6852
6853         label = gtk_label_new(_("Subject:"));
6854         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6855         gtk_widget_show(label);
6856
6857         subject_entry = gtk_entry_new();
6858         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6859         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6860                          G_CALLBACK(compose_grab_focus_cb), compose);
6861         gtk_widget_show(subject_entry);
6862         compose->subject_entry = subject_entry;
6863         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6864         
6865         edit_vbox = gtk_vbox_new(FALSE, 0);
6866
6867         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6868
6869         /* ruler */
6870         ruler_hbox = gtk_hbox_new(FALSE, 0);
6871         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6872
6873         ruler = gtk_shruler_new();
6874         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6875         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6876                            BORDER_WIDTH);
6877
6878         /* text widget */
6879         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6880         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6881                                        GTK_POLICY_AUTOMATIC,
6882                                        GTK_POLICY_AUTOMATIC);
6883         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6884                                             GTK_SHADOW_IN);
6885         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6886         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6887
6888         text = gtk_text_view_new();
6889         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6890         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6891         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6892         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6893         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6894         
6895         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6896
6897         g_signal_connect_after(G_OBJECT(text), "size_allocate",
6898                                G_CALLBACK(compose_edit_size_alloc),
6899                                ruler);
6900         g_signal_connect(G_OBJECT(buffer), "changed",
6901                          G_CALLBACK(compose_changed_cb), compose);
6902         g_signal_connect(G_OBJECT(text), "grab_focus",
6903                          G_CALLBACK(compose_grab_focus_cb), compose);
6904         g_signal_connect(G_OBJECT(buffer), "insert_text",
6905                          G_CALLBACK(text_inserted), compose);
6906         g_signal_connect(G_OBJECT(text), "button_press_event",
6907                          G_CALLBACK(text_clicked), compose);
6908 #ifndef MAEMO
6909         g_signal_connect(G_OBJECT(text), "popup-menu",
6910                          G_CALLBACK(compose_popup_menu), compose);
6911 #else
6912         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6913                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6914         g_signal_connect(G_OBJECT(text), "tap-and-hold",
6915                          G_CALLBACK(compose_popup_menu), compose);
6916 #endif
6917         g_signal_connect(G_OBJECT(subject_entry), "changed",
6918                          G_CALLBACK(compose_changed_cb), compose);
6919
6920         /* drag and drop */
6921         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6922                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6923                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6924         g_signal_connect(G_OBJECT(text), "drag_data_received",
6925                          G_CALLBACK(compose_insert_drag_received_cb),
6926                          compose);
6927         g_signal_connect(G_OBJECT(text), "drag-drop",
6928                          G_CALLBACK(compose_drag_drop),
6929                          compose);
6930         gtk_widget_show_all(vbox);
6931
6932         /* pane between attach clist and text */
6933         paned = gtk_vpaned_new();
6934         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
6935         gtk_container_add(GTK_CONTAINER(vbox2), paned);
6936 #ifdef MAEMO
6937         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
6938                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
6939         else
6940                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
6941 #endif
6942         gtk_paned_add1(GTK_PANED(paned), notebook);
6943         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
6944         gtk_widget_show_all(paned);
6945
6946
6947         if (prefs_common.textfont) {
6948                 PangoFontDescription *font_desc;
6949
6950                 font_desc = pango_font_description_from_string
6951                         (prefs_common.textfont);
6952                 if (font_desc) {
6953                         gtk_widget_modify_font(text, font_desc);
6954                         pango_font_description_free(font_desc);
6955                 }
6956         }
6957
6958         n_entries = sizeof(compose_popup_entries) /
6959                 sizeof(compose_popup_entries[0]);
6960         popupmenu = menu_create_items(compose_popup_entries, n_entries,
6961                                       "<Compose>", &popupfactory,
6962                                       compose);
6963
6964         ifactory = gtk_item_factory_from_widget(menubar);
6965         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6966         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6967         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
6968
6969         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6970
6971         undostruct = undo_init(text);
6972         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
6973                                    menubar);
6974
6975         address_completion_start(window);
6976
6977         compose->window        = window;
6978         compose->vbox          = vbox;
6979         compose->menubar       = menubar;
6980         compose->handlebox     = handlebox;
6981
6982         compose->vbox2         = vbox2;
6983
6984         compose->paned = paned;
6985
6986         compose->attach_label  = attach_lab2;
6987
6988         compose->notebook      = notebook;
6989         compose->edit_vbox     = edit_vbox;
6990         compose->ruler_hbox    = ruler_hbox;
6991         compose->ruler         = ruler;
6992         compose->scrolledwin   = scrolledwin;
6993         compose->text          = text;
6994
6995         compose->focused_editable = NULL;
6996
6997         compose->popupmenu    = popupmenu;
6998         compose->popupfactory = popupfactory;
6999
7000         compose->tmpl_menu = tmpl_menu;
7001
7002         compose->mode = mode;
7003         compose->rmode = mode;
7004
7005         compose->targetinfo = NULL;
7006         compose->replyinfo  = NULL;
7007         compose->fwdinfo    = NULL;
7008
7009         compose->replyto     = NULL;
7010         compose->cc          = NULL;
7011         compose->bcc         = NULL;
7012         compose->followup_to = NULL;
7013
7014         compose->ml_post     = NULL;
7015
7016         compose->inreplyto   = NULL;
7017         compose->references  = NULL;
7018         compose->msgid       = NULL;
7019         compose->boundary    = NULL;
7020
7021         compose->autowrap       = prefs_common.autowrap;
7022
7023         compose->use_signing    = FALSE;
7024         compose->use_encryption = FALSE;
7025         compose->privacy_system = NULL;
7026
7027         compose->modified = FALSE;
7028
7029         compose->return_receipt = FALSE;
7030
7031         compose->to_list        = NULL;
7032         compose->newsgroup_list = NULL;
7033
7034         compose->undostruct = undostruct;
7035
7036         compose->sig_str = NULL;
7037
7038         compose->exteditor_file    = NULL;
7039         compose->exteditor_pid     = -1;
7040         compose->exteditor_tag     = -1;
7041         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7042
7043 #if USE_ASPELL
7044         menu_set_sensitive(ifactory, "/Spelling", FALSE);
7045         if (mode != COMPOSE_REDIRECT) {
7046                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7047                     strcmp(prefs_common.dictionary, "")) {
7048                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7049                                                   prefs_common.dictionary,
7050                                                   prefs_common.alt_dictionary,
7051                                                   conv_get_locale_charset_str(),
7052                                                   prefs_common.misspelled_col,
7053                                                   prefs_common.check_while_typing,
7054                                                   prefs_common.recheck_when_changing_dict,
7055                                                   prefs_common.use_alternate,
7056                                                   prefs_common.use_both_dicts,
7057                                                   GTK_TEXT_VIEW(text),
7058                                                   GTK_WINDOW(compose->window),
7059                                                   compose_spell_menu_changed,
7060                                                   compose);
7061                         if (!gtkaspell) {
7062                                 alertpanel_error(_("Spell checker could not "
7063                                                 "be started.\n%s"),
7064                                                 gtkaspell_checkers_strerror());
7065                                 gtkaspell_checkers_reset_error();
7066                         } else {
7067                                 if (!gtkaspell_set_sug_mode(gtkaspell,
7068                                                 prefs_common.aspell_sugmode)) {
7069                                         debug_print("Aspell: could not set "
7070                                                     "suggestion mode %s\n",
7071                                                     gtkaspell_checkers_strerror());
7072                                         gtkaspell_checkers_reset_error();
7073                                 }
7074
7075                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
7076                         }
7077                 }
7078         }
7079         compose->gtkaspell = gtkaspell;
7080         compose_spell_menu_changed(compose);
7081 #endif
7082
7083         compose_select_account(compose, account, TRUE);
7084
7085         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
7086         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7087                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7088
7089         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7090                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7091         
7092         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7093                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7094
7095         menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
7096
7097         if (account->protocol != A_NNTP)
7098                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7099                                 prefs_common_translated_header_name("To:"));
7100         else
7101                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7102                                 prefs_common_translated_header_name("Newsgroups:"));
7103
7104         addressbook_set_target_compose(compose);
7105         
7106         if (mode != COMPOSE_REDIRECT)
7107                 compose_set_template_menu(compose);
7108         else {
7109                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
7110                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
7111         }
7112
7113         compose_list = g_list_append(compose_list, compose);
7114
7115         if (!prefs_common.show_ruler)
7116                 gtk_widget_hide(ruler_hbox);
7117                 
7118         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
7119         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
7120                                        prefs_common.show_ruler);
7121
7122         /* Priority */
7123         compose->priority = PRIORITY_NORMAL;
7124         compose_update_priority_menu_item(compose);
7125
7126         compose_set_out_encoding(compose);
7127         
7128         /* Actions menu */
7129         compose_update_actions_menu(compose);
7130
7131         /* Privacy Systems menu */
7132         compose_update_privacy_systems_menu(compose);
7133
7134         activate_privacy_system(compose, account, TRUE);
7135         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7136         if (batch) {
7137                 gtk_widget_realize(window);
7138         } else {
7139                 gtk_widget_show(window);
7140 #ifdef MAEMO
7141                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7142                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7143 #endif
7144         }
7145         
7146         return compose;
7147 }
7148
7149 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7150 {
7151         GList *accounts;
7152         GtkWidget *hbox;
7153         GtkWidget *optmenu;
7154         GtkWidget *optmenubox;
7155         GtkListStore *menu;
7156         GtkTreeIter iter;
7157         GtkWidget *from_name = NULL;
7158
7159         gint num = 0, def_menu = 0;
7160         
7161         accounts = account_get_list();
7162         g_return_val_if_fail(accounts != NULL, NULL);
7163
7164         optmenubox = gtk_event_box_new();
7165         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7166         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7167
7168         hbox = gtk_hbox_new(FALSE, 6);
7169         from_name = gtk_entry_new();
7170         
7171         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7172                          G_CALLBACK(compose_grab_focus_cb), compose);
7173
7174         for (; accounts != NULL; accounts = accounts->next, num++) {
7175                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7176                 gchar *name, *from = NULL;
7177
7178                 if (ac == compose->account) def_menu = num;
7179
7180                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7181                                        ac->account_name);
7182                 
7183                 if (ac == compose->account) {
7184                         if (ac->name && *ac->name) {
7185                                 gchar *buf;
7186                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7187                                 from = g_strdup_printf("%s <%s>",
7188                                                        buf, ac->address);
7189                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7190                         } else {
7191                                 from = g_strdup_printf("%s",
7192                                                        ac->address);
7193                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7194                         }
7195                 }
7196                 COMBOBOX_ADD(menu, name, ac->account_id);
7197                 g_free(name);
7198                 g_free(from);
7199         }
7200
7201         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7202
7203         g_signal_connect(G_OBJECT(optmenu), "changed",
7204                         G_CALLBACK(account_activated),
7205                         compose);
7206         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7207                          G_CALLBACK(compose_entry_popup_extend),
7208                          NULL);
7209
7210         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7211         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7212         
7213         gtk_tooltips_set_tip(compose->tooltips, optmenubox,
7214                 _("Account to use for this email"), NULL);
7215         gtk_tooltips_set_tip(compose->tooltips, from_name,
7216                 _("Sender address to be used"), NULL);
7217
7218         compose->from_name = from_name;
7219         
7220         return hbox;
7221 }
7222
7223 static void compose_set_priority_cb(gpointer data,
7224                                     guint action,
7225                                     GtkWidget *widget)
7226 {
7227         Compose *compose = (Compose *) data;
7228         compose->priority = action;
7229 }
7230
7231 static void compose_reply_change_mode(gpointer data,
7232                                     ComposeMode action,
7233                                     GtkWidget *widget)
7234 {
7235         Compose *compose = (Compose *) data;
7236         gboolean was_modified = compose->modified;
7237
7238         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7239         
7240         g_return_if_fail(compose->replyinfo != NULL);
7241         
7242         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7243                 ml = TRUE;
7244         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7245                 followup = TRUE;
7246         if (action == COMPOSE_REPLY_TO_ALL)
7247                 all = TRUE;
7248         if (action == COMPOSE_REPLY_TO_SENDER)
7249                 sender = TRUE;
7250         if (action == COMPOSE_REPLY_TO_LIST)
7251                 ml = TRUE;
7252
7253         compose_remove_header_entries(compose);
7254         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7255         if (compose->account->set_autocc && compose->account->auto_cc)
7256                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7257
7258         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7259                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7260         
7261         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7262                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7263         compose_show_first_last_header(compose, TRUE);
7264         compose->modified = was_modified;
7265         compose_set_title(compose);
7266 }
7267
7268 static void compose_update_priority_menu_item(Compose * compose)
7269 {
7270         GtkItemFactory *ifactory;
7271         GtkWidget *menuitem = NULL;
7272
7273         ifactory = gtk_item_factory_from_widget(compose->menubar);
7274         
7275         switch (compose->priority) {
7276                 case PRIORITY_HIGHEST:
7277                         menuitem = gtk_item_factory_get_item
7278                                 (ifactory, "/Options/Priority/Highest");
7279                         break;
7280                 case PRIORITY_HIGH:
7281                         menuitem = gtk_item_factory_get_item
7282                                 (ifactory, "/Options/Priority/High");
7283                         break;
7284                 case PRIORITY_NORMAL:
7285                         menuitem = gtk_item_factory_get_item
7286                                 (ifactory, "/Options/Priority/Normal");
7287                         break;
7288                 case PRIORITY_LOW:
7289                         menuitem = gtk_item_factory_get_item
7290                                 (ifactory, "/Options/Priority/Low");
7291                         break;
7292                 case PRIORITY_LOWEST:
7293                         menuitem = gtk_item_factory_get_item
7294                                 (ifactory, "/Options/Priority/Lowest");
7295                         break;
7296         }
7297         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7298 }       
7299
7300 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7301 {
7302         Compose *compose = (Compose *) data;
7303         gchar *systemid;
7304         GtkItemFactory *ifactory;
7305         gboolean can_sign = FALSE, can_encrypt = FALSE;
7306
7307         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7308
7309         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7310                 return;
7311
7312         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7313         g_free(compose->privacy_system);
7314         compose->privacy_system = NULL;
7315         if (systemid != NULL) {
7316                 compose->privacy_system = g_strdup(systemid);
7317
7318                 can_sign = privacy_system_can_sign(systemid);
7319                 can_encrypt = privacy_system_can_encrypt(systemid);
7320         }
7321
7322         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7323
7324         ifactory = gtk_item_factory_from_widget(compose->menubar);
7325         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7326         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7327 }
7328
7329 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7330 {
7331         static gchar *branch_path = "/Options/Privacy System";
7332         GtkItemFactory *ifactory;
7333         GtkWidget *menuitem = NULL;
7334         GList *amenu;
7335         gboolean can_sign = FALSE, can_encrypt = FALSE;
7336         gboolean found = FALSE;
7337
7338         ifactory = gtk_item_factory_from_widget(compose->menubar);
7339
7340         if (compose->privacy_system != NULL) {
7341                 gchar *systemid;
7342                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7343                 g_return_if_fail(menuitem != NULL);
7344
7345                 amenu = GTK_MENU_SHELL(menuitem)->children;
7346                 menuitem = NULL;
7347                 while (amenu != NULL) {
7348                         GList *alist = amenu->next;
7349
7350                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7351                         if (systemid != NULL) {
7352                                 if (strcmp(systemid, compose->privacy_system) == 0) {
7353                                         menuitem = GTK_WIDGET(amenu->data);
7354
7355                                         can_sign = privacy_system_can_sign(systemid);
7356                                         can_encrypt = privacy_system_can_encrypt(systemid);
7357                                         found = TRUE;
7358                                         break;
7359                                 } 
7360                         } else if (strlen(compose->privacy_system) == 0) {
7361                                         menuitem = GTK_WIDGET(amenu->data);
7362
7363                                         can_sign = FALSE;
7364                                         can_encrypt = FALSE;
7365                                         found = TRUE;
7366                                         break;
7367                         }
7368
7369                         amenu = alist;
7370                 }
7371                 if (menuitem != NULL)
7372                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7373                 
7374                 if (warn && !found && strlen(compose->privacy_system)) {
7375                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7376                                   "will not be able to sign or encrypt this message."),
7377                                   compose->privacy_system);
7378                 }
7379         } 
7380
7381         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7382         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7383 }       
7384  
7385 static void compose_set_out_encoding(Compose *compose)
7386 {
7387         GtkItemFactoryEntry *entry;
7388         GtkItemFactory *ifactory;
7389         CharSet out_encoding;
7390         gchar *path, *p, *q;
7391         GtkWidget *item;
7392
7393         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7394         ifactory = gtk_item_factory_from_widget(compose->menubar);
7395
7396         for (entry = compose_entries; entry->callback != compose_address_cb;
7397              entry++) {
7398                 if (entry->callback == compose_set_encoding_cb &&
7399                     (CharSet)entry->callback_action == out_encoding) {
7400                         p = q = path = g_strdup(entry->path);
7401                         while (*p) {
7402                                 if (*p == '_') {
7403                                         if (p[1] == '_') {
7404                                                 p++;
7405                                                 *q++ = '_';
7406                                         }
7407                                 } else
7408                                         *q++ = *p;
7409                                 p++;
7410                         }
7411                         *q = '\0';
7412                         item = gtk_item_factory_get_item(ifactory, path);
7413                         gtk_widget_activate(item);
7414                         g_free(path);
7415                         break;
7416                 }
7417         }
7418 }
7419
7420 static void compose_set_template_menu(Compose *compose)
7421 {
7422         GSList *tmpl_list, *cur;
7423         GtkWidget *menu;
7424         GtkWidget *item;
7425
7426         tmpl_list = template_get_config();
7427
7428         menu = gtk_menu_new();
7429
7430         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7431                 Template *tmpl = (Template *)cur->data;
7432
7433                 item = gtk_menu_item_new_with_label(tmpl->name);
7434                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7435                 g_signal_connect(G_OBJECT(item), "activate",
7436                                  G_CALLBACK(compose_template_activate_cb),
7437                                  compose);
7438                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7439                 gtk_widget_show(item);
7440         }
7441
7442         gtk_widget_show(menu);
7443         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7444 }
7445
7446 void compose_update_actions_menu(Compose *compose)
7447 {
7448         GtkItemFactory *ifactory;
7449
7450         ifactory = gtk_item_factory_from_widget(compose->menubar);
7451         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
7452 }
7453
7454 static void compose_update_privacy_systems_menu(Compose *compose)
7455 {
7456         static gchar *branch_path = "/Options/Privacy System";
7457         GtkItemFactory *ifactory;
7458         GtkWidget *menuitem;
7459         GSList *systems, *cur;
7460         GList *amenu;
7461         GtkWidget *widget;
7462         GtkWidget *system_none;
7463         GSList *group;
7464
7465         ifactory = gtk_item_factory_from_widget(compose->menubar);
7466
7467         /* remove old entries */
7468         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7469         g_return_if_fail(menuitem != NULL);
7470
7471         amenu = GTK_MENU_SHELL(menuitem)->children->next;
7472         while (amenu != NULL) {
7473                 GList *alist = amenu->next;
7474                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
7475                 amenu = alist;
7476         }
7477
7478         system_none = gtk_item_factory_get_widget(ifactory,
7479                 "/Options/Privacy System/None");
7480
7481         g_signal_connect(G_OBJECT(system_none), "activate",
7482                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7483
7484         systems = privacy_get_system_ids();
7485         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7486                 gchar *systemid = cur->data;
7487
7488                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7489                 widget = gtk_radio_menu_item_new_with_label(group,
7490                         privacy_system_get_name(systemid));
7491                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7492                                        g_strdup(systemid), g_free);
7493                 g_signal_connect(G_OBJECT(widget), "activate",
7494                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7495
7496                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
7497                 gtk_widget_show(widget);
7498                 g_free(systemid);
7499         }
7500         g_slist_free(systems);
7501 }
7502
7503 void compose_reflect_prefs_all(void)
7504 {
7505         GList *cur;
7506         Compose *compose;
7507
7508         for (cur = compose_list; cur != NULL; cur = cur->next) {
7509                 compose = (Compose *)cur->data;
7510                 compose_set_template_menu(compose);
7511         }
7512 }
7513
7514 void compose_reflect_prefs_pixmap_theme(void)
7515 {
7516         GList *cur;
7517         Compose *compose;
7518
7519         for (cur = compose_list; cur != NULL; cur = cur->next) {
7520                 compose = (Compose *)cur->data;
7521                 toolbar_update(TOOLBAR_COMPOSE, compose);
7522         }
7523 }
7524
7525 static const gchar *compose_quote_char_from_context(Compose *compose)
7526 {
7527         const gchar *qmark = NULL;
7528
7529         g_return_val_if_fail(compose != NULL, NULL);
7530
7531         switch (compose->mode) {
7532                 /* use forward-specific quote char */
7533                 case COMPOSE_FORWARD:
7534                 case COMPOSE_FORWARD_AS_ATTACH:
7535                 case COMPOSE_FORWARD_INLINE:
7536                         if (compose->folder && compose->folder->prefs &&
7537                                         compose->folder->prefs->forward_with_format)
7538                                 qmark = compose->folder->prefs->forward_quotemark;
7539                         else if (compose->account->forward_with_format)
7540                                 qmark = compose->account->forward_quotemark;
7541                         else
7542                                 qmark = prefs_common.fw_quotemark;
7543                         break;
7544
7545                 /* use reply-specific quote char in all other modes */
7546                 default:
7547                         if (compose->folder && compose->folder->prefs &&
7548                                         compose->folder->prefs->reply_with_format)
7549                                 qmark = compose->folder->prefs->reply_quotemark;
7550                         else if (compose->account->reply_with_format)
7551                                 qmark = compose->account->reply_quotemark;
7552                         else
7553                                 qmark = prefs_common.quotemark;
7554                         break;
7555         }
7556
7557         if (qmark == NULL || *qmark == '\0')
7558                 qmark = "> ";
7559
7560         return qmark;
7561 }
7562
7563 static void compose_template_apply(Compose *compose, Template *tmpl,
7564                                    gboolean replace)
7565 {
7566         GtkTextView *text;
7567         GtkTextBuffer *buffer;
7568         GtkTextMark *mark;
7569         GtkTextIter iter;
7570         const gchar *qmark;
7571         gchar *parsed_str = NULL;
7572         gint cursor_pos = 0;
7573         const gchar *err_msg = _("Template body format error at line %d.");
7574         if (!tmpl) return;
7575
7576         /* process the body */
7577
7578         text = GTK_TEXT_VIEW(compose->text);
7579         buffer = gtk_text_view_get_buffer(text);
7580
7581         if (tmpl->value) {
7582                 qmark = compose_quote_char_from_context(compose);
7583
7584                 if (compose->replyinfo != NULL) {
7585
7586                         if (replace)
7587                                 gtk_text_buffer_set_text(buffer, "", -1);
7588                         mark = gtk_text_buffer_get_insert(buffer);
7589                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7590
7591                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7592                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7593
7594                 } else if (compose->fwdinfo != NULL) {
7595
7596                         if (replace)
7597                                 gtk_text_buffer_set_text(buffer, "", -1);
7598                         mark = gtk_text_buffer_get_insert(buffer);
7599                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7600
7601                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7602                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7603
7604                 } else {
7605                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7606
7607                         GtkTextIter start, end;
7608                         gchar *tmp = NULL;
7609
7610                         gtk_text_buffer_get_start_iter(buffer, &start);
7611                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7612                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7613
7614                         /* clear the buffer now */
7615                         if (replace)
7616                                 gtk_text_buffer_set_text(buffer, "", -1);
7617
7618                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7619                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7620                         procmsg_msginfo_free( dummyinfo );
7621
7622                         g_free( tmp );
7623                 } 
7624         } else {
7625                 if (replace)
7626                         gtk_text_buffer_set_text(buffer, "", -1);
7627                 mark = gtk_text_buffer_get_insert(buffer);
7628                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7629         }       
7630
7631         if (replace && parsed_str && compose->account->auto_sig)
7632                 compose_insert_sig(compose, FALSE);
7633
7634         if (replace && parsed_str) {
7635                 gtk_text_buffer_get_start_iter(buffer, &iter);
7636                 gtk_text_buffer_place_cursor(buffer, &iter);
7637         }
7638         
7639         if (parsed_str) {
7640                 cursor_pos = quote_fmt_get_cursor_pos();
7641                 compose->set_cursor_pos = cursor_pos;
7642                 if (cursor_pos == -1)
7643                         cursor_pos = 0;
7644                 gtk_text_buffer_get_start_iter(buffer, &iter);
7645                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7646                 gtk_text_buffer_place_cursor(buffer, &iter);
7647         }
7648
7649         /* process the other fields */
7650
7651         compose_template_apply_fields(compose, tmpl);
7652         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7653         quote_fmt_reset_vartable();
7654         compose_changed_cb(NULL, compose);
7655 }
7656
7657 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7658 {
7659         MsgInfo* dummyinfo = NULL;
7660         MsgInfo *msginfo = NULL;
7661         gchar *buf = NULL;
7662
7663         if (compose->replyinfo != NULL)
7664                 msginfo = compose->replyinfo;
7665         else if (compose->fwdinfo != NULL)
7666                 msginfo = compose->fwdinfo;
7667         else {
7668                 dummyinfo = compose_msginfo_new_from_compose(compose);
7669                 msginfo = dummyinfo;
7670         }
7671
7672         if (tmpl->from && *tmpl->from != '\0') {
7673 #ifdef USE_ASPELL
7674                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7675                                 compose->gtkaspell);
7676 #else
7677                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7678 #endif
7679                 quote_fmt_scan_string(tmpl->from);
7680                 quote_fmt_parse();
7681
7682                 buf = quote_fmt_get_buffer();
7683                 if (buf == NULL) {
7684                         alertpanel_error(_("Template From format error."));
7685                 } else {
7686                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7687                 }
7688         }
7689
7690         if (tmpl->to && *tmpl->to != '\0') {
7691 #ifdef USE_ASPELL
7692                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7693                                 compose->gtkaspell);
7694 #else
7695                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7696 #endif
7697                 quote_fmt_scan_string(tmpl->to);
7698                 quote_fmt_parse();
7699
7700                 buf = quote_fmt_get_buffer();
7701                 if (buf == NULL) {
7702                         alertpanel_error(_("Template To format error."));
7703                 } else {
7704                         compose_entry_append(compose, buf, COMPOSE_TO);
7705                 }
7706         }
7707
7708         if (tmpl->cc && *tmpl->cc != '\0') {
7709 #ifdef USE_ASPELL
7710                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7711                                 compose->gtkaspell);
7712 #else
7713                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7714 #endif
7715                 quote_fmt_scan_string(tmpl->cc);
7716                 quote_fmt_parse();
7717
7718                 buf = quote_fmt_get_buffer();
7719                 if (buf == NULL) {
7720                         alertpanel_error(_("Template Cc format error."));
7721                 } else {
7722                         compose_entry_append(compose, buf, COMPOSE_CC);
7723                 }
7724         }
7725
7726         if (tmpl->bcc && *tmpl->bcc != '\0') {
7727 #ifdef USE_ASPELL
7728                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7729                                 compose->gtkaspell);
7730 #else
7731                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7732 #endif
7733                 quote_fmt_scan_string(tmpl->bcc);
7734                 quote_fmt_parse();
7735
7736                 buf = quote_fmt_get_buffer();
7737                 if (buf == NULL) {
7738                         alertpanel_error(_("Template Bcc format error."));
7739                 } else {
7740                         compose_entry_append(compose, buf, COMPOSE_BCC);
7741                 }
7742         }
7743
7744         /* process the subject */
7745         if (tmpl->subject && *tmpl->subject != '\0') {
7746 #ifdef USE_ASPELL
7747                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7748                                 compose->gtkaspell);
7749 #else
7750                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7751 #endif
7752                 quote_fmt_scan_string(tmpl->subject);
7753                 quote_fmt_parse();
7754
7755                 buf = quote_fmt_get_buffer();
7756                 if (buf == NULL) {
7757                         alertpanel_error(_("Template subject format error."));
7758                 } else {
7759                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7760                 }
7761         }
7762
7763         procmsg_msginfo_free( dummyinfo );
7764 }
7765
7766 static void compose_destroy(Compose *compose)
7767 {
7768         GtkTextBuffer *buffer;
7769         GtkClipboard *clipboard;
7770
7771         compose_list = g_list_remove(compose_list, compose);
7772
7773         if (compose->updating) {
7774                 debug_print("danger, not destroying anything now\n");
7775                 compose->deferred_destroy = TRUE;
7776                 return;
7777         }
7778         /* NOTE: address_completion_end() does nothing with the window
7779          * however this may change. */
7780         address_completion_end(compose->window);
7781
7782         slist_free_strings(compose->to_list);
7783         g_slist_free(compose->to_list);
7784         slist_free_strings(compose->newsgroup_list);
7785         g_slist_free(compose->newsgroup_list);
7786         slist_free_strings(compose->header_list);
7787         g_slist_free(compose->header_list);
7788
7789         procmsg_msginfo_free(compose->targetinfo);
7790         procmsg_msginfo_free(compose->replyinfo);
7791         procmsg_msginfo_free(compose->fwdinfo);
7792
7793         g_free(compose->replyto);
7794         g_free(compose->cc);
7795         g_free(compose->bcc);
7796         g_free(compose->newsgroups);
7797         g_free(compose->followup_to);
7798
7799         g_free(compose->ml_post);
7800
7801         g_free(compose->inreplyto);
7802         g_free(compose->references);
7803         g_free(compose->msgid);
7804         g_free(compose->boundary);
7805
7806         g_free(compose->redirect_filename);
7807         if (compose->undostruct)
7808                 undo_destroy(compose->undostruct);
7809
7810         g_free(compose->sig_str);
7811
7812         g_free(compose->exteditor_file);
7813
7814         g_free(compose->orig_charset);
7815
7816         g_free(compose->privacy_system);
7817
7818         if (addressbook_get_target_compose() == compose)
7819                 addressbook_set_target_compose(NULL);
7820
7821 #if USE_ASPELL
7822         if (compose->gtkaspell) {
7823                 gtkaspell_delete(compose->gtkaspell);
7824                 compose->gtkaspell = NULL;
7825         }
7826 #endif
7827
7828         prefs_common.compose_width = compose->scrolledwin->allocation.width;
7829         prefs_common.compose_height = compose->window->allocation.height;
7830
7831         if (!gtk_widget_get_parent(compose->paned))
7832                 gtk_widget_destroy(compose->paned);
7833         gtk_widget_destroy(compose->popupmenu);
7834
7835         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7836         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7837         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7838
7839         gtk_widget_destroy(compose->window);
7840         toolbar_destroy(compose->toolbar);
7841         g_free(compose->toolbar);
7842         g_mutex_free(compose->mutex);
7843         g_free(compose);
7844 }
7845
7846 static void compose_attach_info_free(AttachInfo *ainfo)
7847 {
7848         g_free(ainfo->file);
7849         g_free(ainfo->content_type);
7850         g_free(ainfo->name);
7851         g_free(ainfo);
7852 }
7853
7854 static void compose_attach_update_label(Compose *compose)
7855 {
7856         GtkTreeIter iter;
7857         gint i = 1;
7858         gchar *text;
7859         GtkTreeModel *model;
7860         
7861         if(compose == NULL)
7862                 return;
7863                 
7864         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7865         if(!gtk_tree_model_get_iter_first(model, &iter)) {
7866                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
7867                 return;
7868         }
7869         
7870         while(gtk_tree_model_iter_next(model, &iter))
7871                 i++;
7872         
7873         text = g_strdup_printf("(%d)", i);
7874         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7875         g_free(text);
7876 }
7877
7878 static void compose_attach_remove_selected(Compose *compose)
7879 {
7880         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7881         GtkTreeSelection *selection;
7882         GList *sel, *cur;
7883         GtkTreeModel *model;
7884
7885         selection = gtk_tree_view_get_selection(tree_view);
7886         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7887
7888         if (!sel) 
7889                 return;
7890
7891         for (cur = sel; cur != NULL; cur = cur->next) {
7892                 GtkTreePath *path = cur->data;
7893                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7894                                                 (model, cur->data);
7895                 cur->data = ref;
7896                 gtk_tree_path_free(path);
7897         }
7898
7899         for (cur = sel; cur != NULL; cur = cur->next) {
7900                 GtkTreeRowReference *ref = cur->data;
7901                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7902                 GtkTreeIter iter;
7903
7904                 if (gtk_tree_model_get_iter(model, &iter, path))
7905                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7906                 
7907                 gtk_tree_path_free(path);
7908                 gtk_tree_row_reference_free(ref);
7909         }
7910
7911         g_list_free(sel);
7912         compose_attach_update_label(compose);
7913 }
7914
7915 static struct _AttachProperty
7916 {
7917         GtkWidget *window;
7918         GtkWidget *mimetype_entry;
7919         GtkWidget *encoding_optmenu;
7920         GtkWidget *path_entry;
7921         GtkWidget *filename_entry;
7922         GtkWidget *ok_btn;
7923         GtkWidget *cancel_btn;
7924 } attach_prop;
7925
7926 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
7927 {       
7928         gtk_tree_path_free((GtkTreePath *)ptr);
7929 }
7930
7931 static void compose_attach_property(Compose *compose)
7932 {
7933         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7934         AttachInfo *ainfo;
7935         GtkComboBox *optmenu;
7936         GtkTreeSelection *selection;
7937         GList *sel;
7938         GtkTreeModel *model;
7939         GtkTreeIter iter;
7940         GtkTreePath *path;
7941         static gboolean cancelled;
7942
7943         /* only if one selected */
7944         selection = gtk_tree_view_get_selection(tree_view);
7945         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
7946                 return;
7947
7948         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7949         if (!sel)
7950                 return;
7951
7952         path = (GtkTreePath *) sel->data;
7953         gtk_tree_model_get_iter(model, &iter, path);
7954         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
7955         
7956         if (!ainfo) {
7957                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
7958                 g_list_free(sel);
7959                 return;
7960         }               
7961         g_list_free(sel);
7962
7963         if (!attach_prop.window)
7964                 compose_attach_property_create(&cancelled);
7965         gtk_widget_grab_focus(attach_prop.ok_btn);
7966         gtk_widget_show(attach_prop.window);
7967         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
7968
7969         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
7970         if (ainfo->encoding == ENC_UNKNOWN)
7971                 combobox_select_by_data(optmenu, ENC_BASE64);
7972         else
7973                 combobox_select_by_data(optmenu, ainfo->encoding);
7974
7975         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
7976                            ainfo->content_type ? ainfo->content_type : "");
7977         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
7978                            ainfo->file ? ainfo->file : "");
7979         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
7980                            ainfo->name ? ainfo->name : "");
7981
7982         for (;;) {
7983                 const gchar *entry_text;
7984                 gchar *text;
7985                 gchar *cnttype = NULL;
7986                 gchar *file = NULL;
7987                 off_t size = 0;
7988
7989                 cancelled = FALSE;
7990                 gtk_main();
7991
7992                 gtk_widget_hide(attach_prop.window);
7993                 
7994                 if (cancelled) 
7995                         break;
7996
7997                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
7998                 if (*entry_text != '\0') {
7999                         gchar *p;
8000
8001                         text = g_strstrip(g_strdup(entry_text));
8002                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8003                                 cnttype = g_strdup(text);
8004                                 g_free(text);
8005                         } else {
8006                                 alertpanel_error(_("Invalid MIME type."));
8007                                 g_free(text);
8008                                 continue;
8009                         }
8010                 }
8011
8012                 ainfo->encoding = combobox_get_active_data(optmenu);
8013
8014                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8015                 if (*entry_text != '\0') {
8016                         if (is_file_exist(entry_text) &&
8017                             (size = get_file_size(entry_text)) > 0)
8018                                 file = g_strdup(entry_text);
8019                         else {
8020                                 alertpanel_error
8021                                         (_("File doesn't exist or is empty."));
8022                                 g_free(cnttype);
8023                                 continue;
8024                         }
8025                 }
8026
8027                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8028                 if (*entry_text != '\0') {
8029                         g_free(ainfo->name);
8030                         ainfo->name = g_strdup(entry_text);
8031                 }
8032
8033                 if (cnttype) {
8034                         g_free(ainfo->content_type);
8035                         ainfo->content_type = cnttype;
8036                 }
8037                 if (file) {
8038                         g_free(ainfo->file);
8039                         ainfo->file = file;
8040                 }
8041                 if (size)
8042                         ainfo->size = size;
8043
8044                 /* update tree store */
8045                 text = to_human_readable(ainfo->size);
8046                 gtk_tree_model_get_iter(model, &iter, path);
8047                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8048                                    COL_MIMETYPE, ainfo->content_type,
8049                                    COL_SIZE, text,
8050                                    COL_NAME, ainfo->name,
8051                                    -1);
8052                 
8053                 break;
8054         }
8055
8056         gtk_tree_path_free(path);
8057 }
8058
8059 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8060 { \
8061         label = gtk_label_new(str); \
8062         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8063                          GTK_FILL, 0, 0, 0); \
8064         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8065  \
8066         entry = gtk_entry_new(); \
8067         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8068                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8069 }
8070
8071 static void compose_attach_property_create(gboolean *cancelled)
8072 {
8073         GtkWidget *window;
8074         GtkWidget *vbox;
8075         GtkWidget *table;
8076         GtkWidget *label;
8077         GtkWidget *mimetype_entry;
8078         GtkWidget *hbox;
8079         GtkWidget *optmenu;
8080         GtkListStore *optmenu_menu;
8081         GtkWidget *path_entry;
8082         GtkWidget *filename_entry;
8083         GtkWidget *hbbox;
8084         GtkWidget *ok_btn;
8085         GtkWidget *cancel_btn;
8086         GList     *mime_type_list, *strlist;
8087         GtkTreeIter iter;
8088
8089         debug_print("Creating attach_property window...\n");
8090
8091         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8092         gtk_widget_set_size_request(window, 480, -1);
8093         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8094         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8095         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8096         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8097         g_signal_connect(G_OBJECT(window), "delete_event",
8098                          G_CALLBACK(attach_property_delete_event),
8099                          cancelled);
8100         g_signal_connect(G_OBJECT(window), "key_press_event",
8101                          G_CALLBACK(attach_property_key_pressed),
8102                          cancelled);
8103
8104         vbox = gtk_vbox_new(FALSE, 8);
8105         gtk_container_add(GTK_CONTAINER(window), vbox);
8106
8107         table = gtk_table_new(4, 2, FALSE);
8108         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8109         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8110         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8111
8112         label = gtk_label_new(_("MIME type")); 
8113         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8114                          GTK_FILL, 0, 0, 0); 
8115         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8116         mimetype_entry = gtk_combo_box_entry_new_text(); 
8117         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8118                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8119                          
8120         /* stuff with list */
8121         mime_type_list = procmime_get_mime_type_list();
8122         strlist = NULL;
8123         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8124                 MimeType *type = (MimeType *) mime_type_list->data;
8125                 gchar *tmp;
8126
8127                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8128
8129                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8130                         g_free(tmp);
8131                 else
8132                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8133                                         (GCompareFunc)strcmp2);
8134         }
8135
8136         for (mime_type_list = strlist; mime_type_list != NULL; 
8137                 mime_type_list = mime_type_list->next) {
8138                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8139                 g_free(mime_type_list->data);
8140         }
8141         g_list_free(strlist);
8142         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8143         mimetype_entry = GTK_BIN(mimetype_entry)->child;                         
8144
8145         label = gtk_label_new(_("Encoding"));
8146         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8147                          GTK_FILL, 0, 0, 0);
8148         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8149
8150         hbox = gtk_hbox_new(FALSE, 0);
8151         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8152                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8153
8154         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8155         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8156
8157         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8158         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8159         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8160         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8161         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8162
8163         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8164
8165         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8166         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8167
8168         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8169                                       &ok_btn, GTK_STOCK_OK,
8170                                       NULL, NULL);
8171         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8172         gtk_widget_grab_default(ok_btn);
8173
8174         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8175                          G_CALLBACK(attach_property_ok),
8176                          cancelled);
8177         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8178                          G_CALLBACK(attach_property_cancel),
8179                          cancelled);
8180
8181         gtk_widget_show_all(vbox);
8182
8183         attach_prop.window           = window;
8184         attach_prop.mimetype_entry   = mimetype_entry;
8185         attach_prop.encoding_optmenu = optmenu;
8186         attach_prop.path_entry       = path_entry;
8187         attach_prop.filename_entry   = filename_entry;
8188         attach_prop.ok_btn           = ok_btn;
8189         attach_prop.cancel_btn       = cancel_btn;
8190 }
8191
8192 #undef SET_LABEL_AND_ENTRY
8193
8194 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8195 {
8196         *cancelled = FALSE;
8197         gtk_main_quit();
8198 }
8199
8200 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8201 {
8202         *cancelled = TRUE;
8203         gtk_main_quit();
8204 }
8205
8206 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8207                                          gboolean *cancelled)
8208 {
8209         *cancelled = TRUE;
8210         gtk_main_quit();
8211
8212         return TRUE;
8213 }
8214
8215 static gboolean attach_property_key_pressed(GtkWidget *widget,
8216                                             GdkEventKey *event,
8217                                             gboolean *cancelled)
8218 {
8219         if (event && event->keyval == GDK_Escape) {
8220                 *cancelled = TRUE;
8221                 gtk_main_quit();
8222         }
8223         return FALSE;
8224 }
8225
8226 static void compose_exec_ext_editor(Compose *compose)
8227 {
8228 #ifdef G_OS_UNIX
8229         gchar *tmp;
8230         pid_t pid;
8231         gint pipe_fds[2];
8232
8233         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8234                               G_DIR_SEPARATOR, compose);
8235
8236         if (pipe(pipe_fds) < 0) {
8237                 perror("pipe");
8238                 g_free(tmp);
8239                 return;
8240         }
8241
8242         if ((pid = fork()) < 0) {
8243                 perror("fork");
8244                 g_free(tmp);
8245                 return;
8246         }
8247
8248         if (pid != 0) {
8249                 /* close the write side of the pipe */
8250                 close(pipe_fds[1]);
8251
8252                 compose->exteditor_file    = g_strdup(tmp);
8253                 compose->exteditor_pid     = pid;
8254
8255                 compose_set_ext_editor_sensitive(compose, FALSE);
8256
8257                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8258                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8259                                                         G_IO_IN,
8260                                                         compose_input_cb,
8261                                                         compose);
8262         } else {        /* process-monitoring process */
8263                 pid_t pid_ed;
8264
8265                 if (setpgid(0, 0))
8266                         perror("setpgid");
8267
8268                 /* close the read side of the pipe */
8269                 close(pipe_fds[0]);
8270
8271                 if (compose_write_body_to_file(compose, tmp) < 0) {
8272                         fd_write_all(pipe_fds[1], "2\n", 2);
8273                         _exit(1);
8274                 }
8275
8276                 pid_ed = compose_exec_ext_editor_real(tmp);
8277                 if (pid_ed < 0) {
8278                         fd_write_all(pipe_fds[1], "1\n", 2);
8279                         _exit(1);
8280                 }
8281
8282                 /* wait until editor is terminated */
8283                 waitpid(pid_ed, NULL, 0);
8284
8285                 fd_write_all(pipe_fds[1], "0\n", 2);
8286
8287                 close(pipe_fds[1]);
8288                 _exit(0);
8289         }
8290
8291         g_free(tmp);
8292 #endif /* G_OS_UNIX */
8293 }
8294
8295 #ifdef G_OS_UNIX
8296 static gint compose_exec_ext_editor_real(const gchar *file)
8297 {
8298         gchar buf[1024];
8299         gchar *p;
8300         gchar **cmdline;
8301         pid_t pid;
8302
8303         g_return_val_if_fail(file != NULL, -1);
8304
8305         if ((pid = fork()) < 0) {
8306                 perror("fork");
8307                 return -1;
8308         }
8309
8310         if (pid != 0) return pid;
8311
8312         /* grandchild process */
8313
8314         if (setpgid(0, getppid()))
8315                 perror("setpgid");
8316
8317         if (prefs_common_get_ext_editor_cmd() &&
8318             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8319             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8320                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8321         } else {
8322                 if (prefs_common_get_ext_editor_cmd())
8323                         g_warning("External editor command line is invalid: '%s'\n",
8324                                   prefs_common_get_ext_editor_cmd());
8325                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8326         }
8327
8328         cmdline = strsplit_with_quote(buf, " ", 1024);
8329         execvp(cmdline[0], cmdline);
8330
8331         perror("execvp");
8332         g_strfreev(cmdline);
8333
8334         _exit(1);
8335 }
8336
8337 static gboolean compose_ext_editor_kill(Compose *compose)
8338 {
8339         pid_t pgid = compose->exteditor_pid * -1;
8340         gint ret;
8341
8342         ret = kill(pgid, 0);
8343
8344         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8345                 AlertValue val;
8346                 gchar *msg;
8347
8348                 msg = g_strdup_printf
8349                         (_("The external editor is still working.\n"
8350                            "Force terminating the process?\n"
8351                            "process group id: %d"), -pgid);
8352                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8353                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8354                         
8355                 g_free(msg);
8356
8357                 if (val == G_ALERTALTERNATE) {
8358                         g_source_remove(compose->exteditor_tag);
8359                         g_io_channel_shutdown(compose->exteditor_ch,
8360                                               FALSE, NULL);
8361                         g_io_channel_unref(compose->exteditor_ch);
8362
8363                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8364                         waitpid(compose->exteditor_pid, NULL, 0);
8365
8366                         g_warning("Terminated process group id: %d", -pgid);
8367                         g_warning("Temporary file: %s",
8368                                   compose->exteditor_file);
8369
8370                         compose_set_ext_editor_sensitive(compose, TRUE);
8371
8372                         g_free(compose->exteditor_file);
8373                         compose->exteditor_file    = NULL;
8374                         compose->exteditor_pid     = -1;
8375                         compose->exteditor_ch      = NULL;
8376                         compose->exteditor_tag     = -1;
8377                 } else
8378                         return FALSE;
8379         }
8380
8381         return TRUE;
8382 }
8383
8384 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8385                                  gpointer data)
8386 {
8387         gchar buf[3] = "3";
8388         Compose *compose = (Compose *)data;
8389         gsize bytes_read;
8390
8391         debug_print(_("Compose: input from monitoring process\n"));
8392
8393         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8394
8395         g_io_channel_shutdown(source, FALSE, NULL);
8396         g_io_channel_unref(source);
8397
8398         waitpid(compose->exteditor_pid, NULL, 0);
8399
8400         if (buf[0] == '0') {            /* success */
8401                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8402                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8403
8404                 gtk_text_buffer_set_text(buffer, "", -1);
8405                 compose_insert_file(compose, compose->exteditor_file);
8406                 compose_changed_cb(NULL, compose);
8407
8408                 if (g_unlink(compose->exteditor_file) < 0)
8409                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8410         } else if (buf[0] == '1') {     /* failed */
8411                 g_warning("Couldn't exec external editor\n");
8412                 if (g_unlink(compose->exteditor_file) < 0)
8413                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8414         } else if (buf[0] == '2') {
8415                 g_warning("Couldn't write to file\n");
8416         } else if (buf[0] == '3') {
8417                 g_warning("Pipe read failed\n");
8418         }
8419
8420         compose_set_ext_editor_sensitive(compose, TRUE);
8421
8422         g_free(compose->exteditor_file);
8423         compose->exteditor_file    = NULL;
8424         compose->exteditor_pid     = -1;
8425         compose->exteditor_ch      = NULL;
8426         compose->exteditor_tag     = -1;
8427
8428         return FALSE;
8429 }
8430
8431 static void compose_set_ext_editor_sensitive(Compose *compose,
8432                                              gboolean sensitive)
8433 {
8434         GtkItemFactory *ifactory;
8435
8436         ifactory = gtk_item_factory_from_widget(compose->menubar);
8437
8438         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
8439         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
8440         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
8441         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
8442         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
8443         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
8444         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
8445                            sensitive);
8446
8447         gtk_widget_set_sensitive(compose->text,                       sensitive);
8448         if (compose->toolbar->send_btn)
8449                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8450         if (compose->toolbar->sendl_btn)
8451                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8452         if (compose->toolbar->draft_btn)
8453                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8454         if (compose->toolbar->insert_btn)
8455                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8456         if (compose->toolbar->sig_btn)
8457                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8458         if (compose->toolbar->exteditor_btn)
8459                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8460         if (compose->toolbar->linewrap_current_btn)
8461                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8462         if (compose->toolbar->linewrap_all_btn)
8463                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8464 }
8465 #endif /* G_OS_UNIX */
8466
8467 /**
8468  * compose_undo_state_changed:
8469  *
8470  * Change the sensivity of the menuentries undo and redo
8471  **/
8472 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8473                                        gint redo_state, gpointer data)
8474 {
8475         GtkWidget *widget = GTK_WIDGET(data);
8476         GtkItemFactory *ifactory;
8477
8478         g_return_if_fail(widget != NULL);
8479
8480         ifactory = gtk_item_factory_from_widget(widget);
8481
8482         switch (undo_state) {
8483         case UNDO_STATE_TRUE:
8484                 if (!undostruct->undo_state) {
8485                         undostruct->undo_state = TRUE;
8486                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
8487                 }
8488                 break;
8489         case UNDO_STATE_FALSE:
8490                 if (undostruct->undo_state) {
8491                         undostruct->undo_state = FALSE;
8492                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
8493                 }
8494                 break;
8495         case UNDO_STATE_UNCHANGED:
8496                 break;
8497         case UNDO_STATE_REFRESH:
8498                 menu_set_sensitive(ifactory, "/Edit/Undo",
8499                                    undostruct->undo_state);
8500                 break;
8501         default:
8502                 g_warning("Undo state not recognized");
8503                 break;
8504         }
8505
8506         switch (redo_state) {
8507         case UNDO_STATE_TRUE:
8508                 if (!undostruct->redo_state) {
8509                         undostruct->redo_state = TRUE;
8510                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
8511                 }
8512                 break;
8513         case UNDO_STATE_FALSE:
8514                 if (undostruct->redo_state) {
8515                         undostruct->redo_state = FALSE;
8516                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
8517                 }
8518                 break;
8519         case UNDO_STATE_UNCHANGED:
8520                 break;
8521         case UNDO_STATE_REFRESH:
8522                 menu_set_sensitive(ifactory, "/Edit/Redo",
8523                                    undostruct->redo_state);
8524                 break;
8525         default:
8526                 g_warning("Redo state not recognized");
8527                 break;
8528         }
8529 }
8530
8531 /* callback functions */
8532
8533 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8534  * includes "non-client" (windows-izm) in calculation, so this calculation
8535  * may not be accurate.
8536  */
8537 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8538                                         GtkAllocation *allocation,
8539                                         GtkSHRuler *shruler)
8540 {
8541         if (prefs_common.show_ruler) {
8542                 gint char_width = 0, char_height = 0;
8543                 gint line_width_in_chars;
8544
8545                 gtkut_get_font_size(GTK_WIDGET(widget),
8546                                     &char_width, &char_height);
8547                 line_width_in_chars =
8548                         (allocation->width - allocation->x) / char_width;
8549
8550                 /* got the maximum */
8551                 gtk_ruler_set_range(GTK_RULER(shruler),
8552                                     0.0, line_width_in_chars, 0,
8553                                     /*line_width_in_chars*/ char_width);
8554         }
8555
8556         return TRUE;
8557 }
8558
8559 static void account_activated(GtkComboBox *optmenu, gpointer data)
8560 {
8561         Compose *compose = (Compose *)data;
8562
8563         PrefsAccount *ac;
8564         gchar *folderidentifier;
8565         gint account_id = 0;
8566         GtkTreeModel *menu;
8567         GtkTreeIter iter;
8568
8569         /* Get ID of active account in the combo box */
8570         menu = gtk_combo_box_get_model(optmenu);
8571         gtk_combo_box_get_active_iter(optmenu, &iter);
8572         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8573
8574         ac = account_find_from_id(account_id);
8575         g_return_if_fail(ac != NULL);
8576
8577         if (ac != compose->account)
8578                 compose_select_account(compose, ac, FALSE);
8579
8580         /* Set message save folder */
8581         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8582                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8583         }
8584         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8585                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8586                            
8587         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8588         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8589                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8590                                   (compose->account, F_OUTBOX));
8591                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8592                 g_free(folderidentifier);
8593         }
8594 }
8595
8596 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8597                             GtkTreeViewColumn *column, Compose *compose)
8598 {
8599         compose_attach_property(compose);
8600 }
8601
8602 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8603                                       gpointer data)
8604 {
8605         Compose *compose = (Compose *)data;
8606         GtkTreeSelection *attach_selection;
8607         gint attach_nr_selected;
8608         GtkItemFactory *ifactory;
8609         
8610         if (!event) return FALSE;
8611
8612         if (event->button == 3) {
8613                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8614                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8615                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
8616                         
8617                 if (attach_nr_selected > 0)
8618                 {
8619                         menu_set_sensitive(ifactory, "/Remove", TRUE);
8620                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
8621                 } else {
8622                         menu_set_sensitive(ifactory, "/Remove", FALSE);
8623                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
8624                 }
8625                         
8626                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8627                                NULL, NULL, event->button, event->time);
8628                 return TRUE;                           
8629         }
8630
8631         return FALSE;
8632 }
8633
8634 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8635                                    gpointer data)
8636 {
8637         Compose *compose = (Compose *)data;
8638
8639         if (!event) return FALSE;
8640
8641         switch (event->keyval) {
8642         case GDK_Delete:
8643                 compose_attach_remove_selected(compose);
8644                 break;
8645         }
8646         return FALSE;
8647 }
8648
8649 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8650 {
8651         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
8652         toolbar_comp_set_sensitive(compose, allow);
8653         menu_set_sensitive(ifactory, "/Message", allow);
8654         menu_set_sensitive(ifactory, "/Edit", allow);
8655 #if USE_ASPELL
8656         menu_set_sensitive(ifactory, "/Spelling", allow);
8657 #endif  
8658         menu_set_sensitive(ifactory, "/Options", allow);
8659         menu_set_sensitive(ifactory, "/Tools", allow);
8660         menu_set_sensitive(ifactory, "/Help", allow);
8661         
8662         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8663
8664 }
8665
8666 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
8667 {
8668         Compose *compose = (Compose *)data;
8669         
8670         if (prefs_common.work_offline && 
8671             !inc_offline_should_override(TRUE,
8672                 _("Claws Mail needs network access in order "
8673                   "to send this email.")))
8674                 return;
8675         
8676         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8677                 g_source_remove(compose->draft_timeout_tag);
8678                 compose->draft_timeout_tag = -1;
8679         }
8680
8681         compose_send(compose);
8682 }
8683
8684 static void compose_send_later_cb(gpointer data, guint action,
8685                                   GtkWidget *widget)
8686 {
8687         Compose *compose = (Compose *)data;
8688         gint val;
8689
8690         inc_lock();
8691         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8692         inc_unlock();
8693
8694         if (!val) {
8695                 compose_close(compose);
8696         } else if (val == -1) {
8697                 alertpanel_error(_("Could not queue message."));
8698         } else if (val == -2) {
8699                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8700         } else if (val == -3) {
8701                 if (privacy_peek_error())
8702                 alertpanel_error(_("Could not queue message for sending:\n\n"
8703                                    "Signature failed: %s"), privacy_get_error());
8704         } else if (val == -4) {
8705                 alertpanel_error(_("Could not queue message for sending:\n\n"
8706                                    "Charset conversion failed."));
8707         } else if (val == -5) {
8708                 alertpanel_error(_("Could not queue message for sending:\n\n"
8709                                    "Couldn't get recipient encryption key."));
8710         } else if (val == -6) {
8711                 /* silent error */
8712         }
8713         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8714 }
8715
8716 #define DRAFTED_AT_EXIT "drafted_at_exit"
8717 static void compose_register_draft(MsgInfo *info)
8718 {
8719         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8720                                       DRAFTED_AT_EXIT, NULL);
8721         FILE *fp = fopen(filepath, "ab");
8722         
8723         if (fp) {
8724                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8725                                 info->msgnum);
8726                 fclose(fp);
8727         }
8728                 
8729         g_free(filepath);       
8730 }
8731
8732 gboolean compose_draft (gpointer data, guint action) 
8733 {
8734         Compose *compose = (Compose *)data;
8735         FolderItem *draft;
8736         gchar *tmp;
8737         gint msgnum;
8738         MsgFlags flag = {0, 0};
8739         static gboolean lock = FALSE;
8740         MsgInfo *newmsginfo;
8741         FILE *fp;
8742         gboolean target_locked = FALSE;
8743         gboolean err = FALSE;
8744
8745         if (lock) return FALSE;
8746
8747         if (compose->sending)
8748                 return TRUE;
8749
8750         draft = account_get_special_folder(compose->account, F_DRAFT);
8751         g_return_val_if_fail(draft != NULL, FALSE);
8752         
8753         if (!g_mutex_trylock(compose->mutex)) {
8754                 /* we don't want to lock the mutex once it's available,
8755                  * because as the only other part of compose.c locking
8756                  * it is compose_close - which means once unlocked,
8757                  * the compose struct will be freed */
8758                 debug_print("couldn't lock mutex, probably sending\n");
8759                 return FALSE;
8760         }
8761         
8762         lock = TRUE;
8763
8764         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8765                               G_DIR_SEPARATOR, compose);
8766         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8767                 FILE_OP_ERROR(tmp, "fopen");
8768                 goto warn_err;
8769         }
8770
8771         /* chmod for security */
8772         if (change_file_mode_rw(fp, tmp) < 0) {
8773                 FILE_OP_ERROR(tmp, "chmod");
8774                 g_warning("can't change file mode\n");
8775         }
8776
8777         /* Save draft infos */
8778         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8779         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8780
8781         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8782                 gchar *savefolderid;
8783
8784                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8785                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8786                 g_free(savefolderid);
8787         }
8788         if (compose->return_receipt) {
8789                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8790         }
8791         if (compose->privacy_system) {
8792                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8793                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8794                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8795         }
8796
8797         /* Message-ID of message replying to */
8798         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8799                 gchar *folderid;
8800                 
8801                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8802                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8803                 g_free(folderid);
8804         }
8805         /* Message-ID of message forwarding to */
8806         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8807                 gchar *folderid;
8808                 
8809                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8810                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8811                 g_free(folderid);
8812         }
8813
8814         /* end of headers */
8815         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8816
8817         if (err) {
8818                 fclose(fp);
8819                 goto warn_err;
8820         }
8821
8822         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8823                 fclose(fp);
8824                 goto warn_err;
8825         }
8826         if (fclose(fp) == EOF) {
8827                 goto warn_err;
8828         }
8829         
8830         if (compose->targetinfo) {
8831                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8832                 flag.perm_flags = target_locked?MSG_LOCKED:0;
8833         }
8834         flag.tmp_flags = MSG_DRAFT;
8835
8836         folder_item_scan(draft);
8837         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8838                 MsgInfo *tmpinfo = NULL;
8839                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8840                 if (compose->msgid) {
8841                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8842                 }
8843                 if (tmpinfo) {
8844                         msgnum = tmpinfo->msgnum;
8845                         procmsg_msginfo_free(tmpinfo);
8846                         debug_print("got draft msgnum %d from scanning\n", msgnum);
8847                 } else {
8848                         debug_print("didn't get draft msgnum after scanning\n");
8849                 }
8850         } else {
8851                 debug_print("got draft msgnum %d from adding\n", msgnum);
8852         }
8853         if (msgnum < 0) {
8854 warn_err:
8855                 g_unlink(tmp);
8856                 g_free(tmp);
8857                 if (action != COMPOSE_AUTO_SAVE) {
8858                         if (action != COMPOSE_DRAFT_FOR_EXIT)
8859                                 alertpanel_error(_("Could not save draft."));
8860                         else {
8861                                 AlertValue val;
8862                                 gtkut_window_popup(compose->window);
8863                                 val = alertpanel_full(_("Could not save draft"),
8864                                         _("Could not save draft.\n"
8865                                         "Do you want to cancel exit or discard this email?"),
8866                                           _("_Cancel exit"), _("_Discard email"), NULL,
8867                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8868                                 if (val == G_ALERTALTERNATE) {
8869                                         lock = FALSE;
8870                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8871                                         compose_close(compose);
8872                                         return TRUE;
8873                                 } else {
8874                                         lock = FALSE;
8875                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8876                                         return FALSE;
8877                                 }
8878                         }
8879                 }
8880                 goto unlock;
8881         }
8882         g_free(tmp);
8883
8884         if (compose->mode == COMPOSE_REEDIT) {
8885                 compose_remove_reedit_target(compose, TRUE);
8886         }
8887
8888         newmsginfo = folder_item_get_msginfo(draft, msgnum);
8889
8890         if (newmsginfo) {
8891                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8892                 if (target_locked)
8893                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8894                 else
8895                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8896                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8897                         procmsg_msginfo_set_flags(newmsginfo, 0,
8898                                                   MSG_HAS_ATTACHMENT);
8899
8900                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8901                         compose_register_draft(newmsginfo);
8902                 }
8903                 procmsg_msginfo_free(newmsginfo);
8904         }
8905         
8906         folder_item_scan(draft);
8907         
8908         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8909                 lock = FALSE;
8910                 g_mutex_unlock(compose->mutex); /* must be done before closing */
8911                 compose_close(compose);
8912                 return TRUE;
8913         } else {
8914                 struct stat s;
8915                 gchar *path;
8916
8917                 path = folder_item_fetch_msg(draft, msgnum);
8918                 if (path == NULL) {
8919                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
8920                         goto unlock;
8921                 }
8922                 if (g_stat(path, &s) < 0) {
8923                         FILE_OP_ERROR(path, "stat");
8924                         g_free(path);
8925                         goto unlock;
8926                 }
8927                 g_free(path);
8928
8929                 procmsg_msginfo_free(compose->targetinfo);
8930                 compose->targetinfo = procmsg_msginfo_new();
8931                 compose->targetinfo->msgnum = msgnum;
8932                 compose->targetinfo->size = s.st_size;
8933                 compose->targetinfo->mtime = s.st_mtime;
8934                 compose->targetinfo->folder = draft;
8935                 if (target_locked)
8936                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
8937                 compose->mode = COMPOSE_REEDIT;
8938                 
8939                 if (action == COMPOSE_AUTO_SAVE) {
8940                         compose->autosaved_draft = compose->targetinfo;
8941                 }
8942                 compose->modified = FALSE;
8943                 compose_set_title(compose);
8944         }
8945 unlock:
8946         lock = FALSE;
8947         g_mutex_unlock(compose->mutex);
8948         return TRUE;
8949 }
8950
8951 void compose_clear_exit_drafts(void)
8952 {
8953         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8954                                       DRAFTED_AT_EXIT, NULL);
8955         if (is_file_exist(filepath))
8956                 g_unlink(filepath);
8957         
8958         g_free(filepath);
8959 }
8960
8961 void compose_reopen_exit_drafts(void)
8962 {
8963         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8964                                       DRAFTED_AT_EXIT, NULL);
8965         FILE *fp = fopen(filepath, "rb");
8966         gchar buf[1024];
8967         
8968         if (fp) {
8969                 while (fgets(buf, sizeof(buf), fp)) {
8970                         gchar **parts = g_strsplit(buf, "\t", 2);
8971                         const gchar *folder = parts[0];
8972                         int msgnum = parts[1] ? atoi(parts[1]):-1;
8973                         
8974                         if (folder && *folder && msgnum > -1) {
8975                                 FolderItem *item = folder_find_item_from_identifier(folder);
8976                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
8977                                 if (info)
8978                                         compose_reedit(info, FALSE);
8979                         }
8980                         g_strfreev(parts);
8981                 }       
8982                 fclose(fp);
8983         }       
8984         g_free(filepath);
8985         compose_clear_exit_drafts();
8986 }
8987
8988 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
8989 {
8990         compose_draft(data, action);
8991 }
8992
8993 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
8994 {
8995         if (compose && file_list) {
8996                 GList *tmp;
8997
8998                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8999                         gchar *file = (gchar *) tmp->data;
9000                         gchar *utf8_filename = conv_filename_to_utf8(file);
9001                         compose_attach_append(compose, file, utf8_filename, NULL);
9002                         compose_changed_cb(NULL, compose);
9003                         if (free_data) {
9004                         g_free(file);
9005                                 tmp->data = NULL;
9006                         }
9007                         g_free(utf8_filename);
9008                 }
9009         }
9010 }
9011
9012 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
9013 {
9014         Compose *compose = (Compose *)data;
9015         GList *file_list;
9016
9017         if (compose->redirect_filename != NULL)
9018                 return;
9019
9020         file_list = filesel_select_multiple_files_open(_("Select file"));
9021
9022         if (file_list) {
9023                 compose_attach_from_list(compose, file_list, TRUE);
9024                 g_list_free(file_list);
9025         }
9026 }
9027
9028 static void compose_insert_file_cb(gpointer data, guint action,
9029                                    GtkWidget *widget)
9030 {
9031         Compose *compose = (Compose *)data;
9032         GList *file_list;
9033
9034         file_list = filesel_select_multiple_files_open(_("Select file"));
9035
9036         if (file_list) {
9037                 GList *tmp;
9038
9039                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9040                         gchar *file = (gchar *) tmp->data;
9041                         gchar *filedup = g_strdup(file);
9042                         gchar *shortfile = g_path_get_basename(filedup);
9043                         ComposeInsertResult res;
9044
9045                         res = compose_insert_file(compose, file);
9046                         if (res == COMPOSE_INSERT_READ_ERROR) {
9047                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9048                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9049                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9050                                                    "for the current encoding, insertion may be incorrect."), shortfile);
9051                         }
9052                         g_free(shortfile);
9053                         g_free(filedup);
9054                         g_free(file);
9055                 }
9056                 g_list_free(file_list);
9057         }
9058 }
9059
9060 static void compose_insert_sig_cb(gpointer data, guint action,
9061                                   GtkWidget *widget)
9062 {
9063         Compose *compose = (Compose *)data;
9064
9065         compose_insert_sig(compose, FALSE);
9066 }
9067
9068 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9069                               gpointer data)
9070 {
9071         gint x, y;
9072         Compose *compose = (Compose *)data;
9073
9074         gtkut_widget_get_uposition(widget, &x, &y);
9075         prefs_common.compose_x = x;
9076         prefs_common.compose_y = y;
9077
9078         if (compose->sending || compose->updating)
9079                 return TRUE;
9080         compose_close_cb(compose, 0, NULL);
9081         return TRUE;
9082 }
9083
9084 void compose_close_toolbar(Compose *compose)
9085 {
9086         compose_close_cb(compose, 0, NULL);
9087 }
9088
9089 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
9090 {
9091         Compose *compose = (Compose *)data;
9092         AlertValue val;
9093
9094 #ifdef G_OS_UNIX
9095         if (compose->exteditor_tag != -1) {
9096                 if (!compose_ext_editor_kill(compose))
9097                         return;
9098         }
9099 #endif
9100
9101         if (compose->modified) {
9102                 if (!g_mutex_trylock(compose->mutex)) {
9103                         /* we don't want to lock the mutex once it's available,
9104                          * because as the only other part of compose.c locking
9105                          * it is compose_close - which means once unlocked,
9106                          * the compose struct will be freed */
9107                         debug_print("couldn't lock mutex, probably sending\n");
9108                         return;
9109                 }
9110                 val = alertpanel(_("Discard message"),
9111                                  _("This message has been modified. Discard it?"),
9112                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9113                 g_mutex_unlock(compose->mutex);
9114                 switch (val) {
9115                 case G_ALERTDEFAULT:
9116                         if (prefs_common.autosave)
9117                                 compose_remove_draft(compose);                  
9118                         break;
9119                 case G_ALERTALTERNATE:
9120                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
9121                         return;
9122                 default:
9123                         return;
9124                 }
9125         }
9126
9127         compose_close(compose);
9128 }
9129
9130 static void compose_set_encoding_cb(gpointer data, guint action,
9131                                     GtkWidget *widget)
9132 {
9133         Compose *compose = (Compose *)data;
9134
9135         if (GTK_CHECK_MENU_ITEM(widget)->active)
9136                 compose->out_encoding = (CharSet)action;
9137 }
9138
9139 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
9140 {
9141         Compose *compose = (Compose *)data;
9142
9143         addressbook_open(compose);
9144 }
9145
9146 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9147 {
9148         Compose *compose = (Compose *)data;
9149         Template *tmpl;
9150         gchar *msg;
9151         AlertValue val;
9152
9153         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9154         g_return_if_fail(tmpl != NULL);
9155
9156         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9157                               tmpl->name);
9158         val = alertpanel(_("Apply template"), msg,
9159                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9160         g_free(msg);
9161
9162         if (val == G_ALERTDEFAULT)
9163                 compose_template_apply(compose, tmpl, TRUE);
9164         else if (val == G_ALERTALTERNATE)
9165                 compose_template_apply(compose, tmpl, FALSE);
9166 }
9167
9168 static void compose_ext_editor_cb(gpointer data, guint action,
9169                                   GtkWidget *widget)
9170 {
9171         Compose *compose = (Compose *)data;
9172
9173         compose_exec_ext_editor(compose);
9174 }
9175
9176 static void compose_undo_cb(Compose *compose)
9177 {
9178         gboolean prev_autowrap = compose->autowrap;
9179
9180         compose->autowrap = FALSE;
9181         undo_undo(compose->undostruct);
9182         compose->autowrap = prev_autowrap;
9183 }
9184
9185 static void compose_redo_cb(Compose *compose)
9186 {
9187         gboolean prev_autowrap = compose->autowrap;
9188         
9189         compose->autowrap = FALSE;
9190         undo_redo(compose->undostruct);
9191         compose->autowrap = prev_autowrap;
9192 }
9193
9194 static void entry_cut_clipboard(GtkWidget *entry)
9195 {
9196         if (GTK_IS_EDITABLE(entry))
9197                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9198         else if (GTK_IS_TEXT_VIEW(entry))
9199                 gtk_text_buffer_cut_clipboard(
9200                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9201                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9202                         TRUE);
9203 }
9204
9205 static void entry_copy_clipboard(GtkWidget *entry)
9206 {
9207         if (GTK_IS_EDITABLE(entry))
9208                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9209         else if (GTK_IS_TEXT_VIEW(entry))
9210                 gtk_text_buffer_copy_clipboard(
9211                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9212                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9213 }
9214
9215 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9216                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9217 {
9218         if (GTK_IS_TEXT_VIEW(entry)) {
9219                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9220                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9221                 GtkTextIter start_iter, end_iter;
9222                 gint start, end;
9223                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9224
9225                 if (contents == NULL)
9226                         return;
9227
9228                 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9229
9230                 /* we shouldn't delete the selection when middle-click-pasting, or we
9231                  * can't mid-click-paste our own selection */
9232                 if (clip != GDK_SELECTION_PRIMARY) {
9233                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9234                 }
9235                 
9236                 if (insert_place == NULL) {
9237                         /* if insert_place isn't specified, insert at the cursor.
9238                          * used for Ctrl-V pasting */
9239                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9240                         start = gtk_text_iter_get_offset(&start_iter);
9241                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9242                 } else {
9243                         /* if insert_place is specified, paste here.
9244                          * used for mid-click-pasting */
9245                         start = gtk_text_iter_get_offset(insert_place);
9246                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9247                 }
9248                 
9249                 if (!wrap) {
9250                         /* paste unwrapped: mark the paste so it's not wrapped later */
9251                         end = start + strlen(contents);
9252                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9253                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9254                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9255                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9256                         /* rewrap paragraph now (after a mid-click-paste) */
9257                         mark_start = gtk_text_buffer_get_insert(buffer);
9258                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9259                         gtk_text_iter_backward_char(&start_iter);
9260                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9261                 }
9262         } else if (GTK_IS_EDITABLE(entry))
9263                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9264         
9265 }
9266
9267 static void entry_allsel(GtkWidget *entry)
9268 {
9269         if (GTK_IS_EDITABLE(entry))
9270                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9271         else if (GTK_IS_TEXT_VIEW(entry)) {
9272                 GtkTextIter startiter, enditer;
9273                 GtkTextBuffer *textbuf;
9274
9275                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9276                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9277                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9278
9279                 gtk_text_buffer_move_mark_by_name(textbuf, 
9280                         "selection_bound", &startiter);
9281                 gtk_text_buffer_move_mark_by_name(textbuf, 
9282                         "insert", &enditer);
9283         }
9284 }
9285
9286 static void compose_cut_cb(Compose *compose)
9287 {
9288         if (compose->focused_editable 
9289 #ifndef MAEMO
9290             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9291 #endif
9292             )
9293                 entry_cut_clipboard(compose->focused_editable);
9294 }
9295
9296 static void compose_copy_cb(Compose *compose)
9297 {
9298         if (compose->focused_editable 
9299 #ifndef MAEMO
9300             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9301 #endif
9302             )
9303                 entry_copy_clipboard(compose->focused_editable);
9304 }
9305
9306 static void compose_paste_cb(Compose *compose)
9307 {
9308         gint prev_autowrap;
9309         GtkTextBuffer *buffer;
9310         BLOCK_WRAP();
9311         if (compose->focused_editable &&
9312             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9313                 entry_paste_clipboard(compose, compose->focused_editable, 
9314                                 prefs_common.linewrap_pastes,
9315                                 GDK_SELECTION_CLIPBOARD, NULL);
9316         UNBLOCK_WRAP();
9317 }
9318
9319 static void compose_paste_as_quote_cb(Compose *compose)
9320 {
9321         gint wrap_quote = prefs_common.linewrap_quote;
9322         if (compose->focused_editable 
9323 #ifndef MAEMO
9324             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9325 #endif
9326             ) {
9327                 /* let text_insert() (called directly or at a later time
9328                  * after the gtk_editable_paste_clipboard) know that 
9329                  * text is to be inserted as a quotation. implemented
9330                  * by using a simple refcount... */
9331                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9332                                                 G_OBJECT(compose->focused_editable),
9333                                                 "paste_as_quotation"));
9334                 g_object_set_data(G_OBJECT(compose->focused_editable),
9335                                     "paste_as_quotation",
9336                                     GINT_TO_POINTER(paste_as_quotation + 1));
9337                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9338                 entry_paste_clipboard(compose, compose->focused_editable, 
9339                                 prefs_common.linewrap_pastes,
9340                                 GDK_SELECTION_CLIPBOARD, NULL);
9341                 prefs_common.linewrap_quote = wrap_quote;
9342         }
9343 }
9344
9345 static void compose_paste_no_wrap_cb(Compose *compose)
9346 {
9347         gint prev_autowrap;
9348         GtkTextBuffer *buffer;
9349         BLOCK_WRAP();
9350         if (compose->focused_editable 
9351 #ifndef MAEMO
9352             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9353 #endif
9354             )
9355                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9356                         GDK_SELECTION_CLIPBOARD, NULL);
9357         UNBLOCK_WRAP();
9358 }
9359
9360 static void compose_paste_wrap_cb(Compose *compose)
9361 {
9362         gint prev_autowrap;
9363         GtkTextBuffer *buffer;
9364         BLOCK_WRAP();
9365         if (compose->focused_editable 
9366 #ifndef MAEMO
9367             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9368 #endif
9369             )
9370                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9371                         GDK_SELECTION_CLIPBOARD, NULL);
9372         UNBLOCK_WRAP();
9373 }
9374
9375 static void compose_allsel_cb(Compose *compose)
9376 {
9377         if (compose->focused_editable 
9378 #ifndef MAEMO
9379             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9380 #endif
9381             )
9382                 entry_allsel(compose->focused_editable);
9383 }
9384
9385 static void textview_move_beginning_of_line (GtkTextView *text)
9386 {
9387         GtkTextBuffer *buffer;
9388         GtkTextMark *mark;
9389         GtkTextIter ins;
9390
9391         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9392
9393         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9394         mark = gtk_text_buffer_get_insert(buffer);
9395         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9396         gtk_text_iter_set_line_offset(&ins, 0);
9397         gtk_text_buffer_place_cursor(buffer, &ins);
9398 }
9399
9400 static void textview_move_forward_character (GtkTextView *text)
9401 {
9402         GtkTextBuffer *buffer;
9403         GtkTextMark *mark;
9404         GtkTextIter ins;
9405
9406         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9407
9408         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9409         mark = gtk_text_buffer_get_insert(buffer);
9410         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9411         if (gtk_text_iter_forward_cursor_position(&ins))
9412                 gtk_text_buffer_place_cursor(buffer, &ins);
9413 }
9414
9415 static void textview_move_backward_character (GtkTextView *text)
9416 {
9417         GtkTextBuffer *buffer;
9418         GtkTextMark *mark;
9419         GtkTextIter ins;
9420
9421         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9422
9423         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9424         mark = gtk_text_buffer_get_insert(buffer);
9425         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9426         if (gtk_text_iter_backward_cursor_position(&ins))
9427                 gtk_text_buffer_place_cursor(buffer, &ins);
9428 }
9429
9430 static void textview_move_forward_word (GtkTextView *text)
9431 {
9432         GtkTextBuffer *buffer;
9433         GtkTextMark *mark;
9434         GtkTextIter ins;
9435         gint count;
9436
9437         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9438
9439         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9440         mark = gtk_text_buffer_get_insert(buffer);
9441         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9442         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9443         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9444                 gtk_text_iter_backward_word_start(&ins);
9445                 gtk_text_buffer_place_cursor(buffer, &ins);
9446         }
9447 }
9448
9449 static void textview_move_backward_word (GtkTextView *text)
9450 {
9451         GtkTextBuffer *buffer;
9452         GtkTextMark *mark;
9453         GtkTextIter ins;
9454         gint count;
9455
9456         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9457
9458         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9459         mark = gtk_text_buffer_get_insert(buffer);
9460         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9461         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9462         if (gtk_text_iter_backward_word_starts(&ins, 1))
9463                 gtk_text_buffer_place_cursor(buffer, &ins);
9464 }
9465
9466 static void textview_move_end_of_line (GtkTextView *text)
9467 {
9468         GtkTextBuffer *buffer;
9469         GtkTextMark *mark;
9470         GtkTextIter ins;
9471
9472         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9473
9474         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9475         mark = gtk_text_buffer_get_insert(buffer);
9476         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9477         if (gtk_text_iter_forward_to_line_end(&ins))
9478                 gtk_text_buffer_place_cursor(buffer, &ins);
9479 }
9480
9481 static void textview_move_next_line (GtkTextView *text)
9482 {
9483         GtkTextBuffer *buffer;
9484         GtkTextMark *mark;
9485         GtkTextIter ins;
9486         gint offset;
9487
9488         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9489
9490         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9491         mark = gtk_text_buffer_get_insert(buffer);
9492         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9493         offset = gtk_text_iter_get_line_offset(&ins);
9494         if (gtk_text_iter_forward_line(&ins)) {
9495                 gtk_text_iter_set_line_offset(&ins, offset);
9496                 gtk_text_buffer_place_cursor(buffer, &ins);
9497         }
9498 }
9499
9500 static void textview_move_previous_line (GtkTextView *text)
9501 {
9502         GtkTextBuffer *buffer;
9503         GtkTextMark *mark;
9504         GtkTextIter ins;
9505         gint offset;
9506
9507         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9508
9509         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9510         mark = gtk_text_buffer_get_insert(buffer);
9511         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9512         offset = gtk_text_iter_get_line_offset(&ins);
9513         if (gtk_text_iter_backward_line(&ins)) {
9514                 gtk_text_iter_set_line_offset(&ins, offset);
9515                 gtk_text_buffer_place_cursor(buffer, &ins);
9516         }
9517 }
9518
9519 static void textview_delete_forward_character (GtkTextView *text)
9520 {
9521         GtkTextBuffer *buffer;
9522         GtkTextMark *mark;
9523         GtkTextIter ins, end_iter;
9524
9525         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9526
9527         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9528         mark = gtk_text_buffer_get_insert(buffer);
9529         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9530         end_iter = ins;
9531         if (gtk_text_iter_forward_char(&end_iter)) {
9532                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9533         }
9534 }
9535
9536 static void textview_delete_backward_character (GtkTextView *text)
9537 {
9538         GtkTextBuffer *buffer;
9539         GtkTextMark *mark;
9540         GtkTextIter ins, end_iter;
9541
9542         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9543
9544         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9545         mark = gtk_text_buffer_get_insert(buffer);
9546         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9547         end_iter = ins;
9548         if (gtk_text_iter_backward_char(&end_iter)) {
9549                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9550         }
9551 }
9552
9553 static void textview_delete_forward_word (GtkTextView *text)
9554 {
9555         GtkTextBuffer *buffer;
9556         GtkTextMark *mark;
9557         GtkTextIter ins, end_iter;
9558
9559         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9560
9561         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9562         mark = gtk_text_buffer_get_insert(buffer);
9563         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9564         end_iter = ins;
9565         if (gtk_text_iter_forward_word_end(&end_iter)) {
9566                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9567         }
9568 }
9569
9570 static void textview_delete_backward_word (GtkTextView *text)
9571 {
9572         GtkTextBuffer *buffer;
9573         GtkTextMark *mark;
9574         GtkTextIter ins, end_iter;
9575
9576         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9577
9578         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9579         mark = gtk_text_buffer_get_insert(buffer);
9580         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9581         end_iter = ins;
9582         if (gtk_text_iter_backward_word_start(&end_iter)) {
9583                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9584         }
9585 }
9586
9587 static void textview_delete_line (GtkTextView *text)
9588 {
9589         GtkTextBuffer *buffer;
9590         GtkTextMark *mark;
9591         GtkTextIter ins, start_iter, end_iter;
9592         gboolean found;
9593
9594         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9595
9596         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9597         mark = gtk_text_buffer_get_insert(buffer);
9598         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9599
9600         start_iter = ins;
9601         gtk_text_iter_set_line_offset(&start_iter, 0);
9602
9603         end_iter = ins;
9604         if (gtk_text_iter_ends_line(&end_iter))
9605                 found = gtk_text_iter_forward_char(&end_iter);
9606         else
9607                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9608
9609         if (found)
9610                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9611 }
9612
9613 static void textview_delete_to_line_end (GtkTextView *text)
9614 {
9615         GtkTextBuffer *buffer;
9616         GtkTextMark *mark;
9617         GtkTextIter ins, end_iter;
9618         gboolean found;
9619
9620         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9621
9622         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9623         mark = gtk_text_buffer_get_insert(buffer);
9624         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9625         end_iter = ins;
9626         if (gtk_text_iter_ends_line(&end_iter))
9627                 found = gtk_text_iter_forward_char(&end_iter);
9628         else
9629                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9630         if (found)
9631                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9632 }
9633
9634 static void compose_advanced_action_cb(Compose *compose,
9635                                         ComposeCallAdvancedAction action)
9636 {
9637         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9638         static struct {
9639                 void (*do_action) (GtkTextView *text);
9640         } action_table[] = {
9641                 {textview_move_beginning_of_line},
9642                 {textview_move_forward_character},
9643                 {textview_move_backward_character},
9644                 {textview_move_forward_word},
9645                 {textview_move_backward_word},
9646                 {textview_move_end_of_line},
9647                 {textview_move_next_line},
9648                 {textview_move_previous_line},
9649                 {textview_delete_forward_character},
9650                 {textview_delete_backward_character},
9651                 {textview_delete_forward_word},
9652                 {textview_delete_backward_word},
9653                 {textview_delete_line},
9654                 {NULL}, /* gtk_stext_delete_line_n */
9655                 {textview_delete_to_line_end}
9656         };
9657
9658         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9659
9660         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9661             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9662                 if (action_table[action].do_action)
9663                         action_table[action].do_action(text);
9664                 else
9665                         g_warning("Not implemented yet.");
9666         }
9667 }
9668
9669 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9670 {
9671         gchar *str = NULL;
9672         
9673         if (GTK_IS_EDITABLE(widget)) {
9674                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9675                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9676                         strlen(str));
9677                 g_free(str);
9678                 if (widget->parent && widget->parent->parent
9679                  && widget->parent->parent->parent) {
9680                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9681                                 gint y = widget->allocation.y;
9682                                 gint height = widget->allocation.height;
9683                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9684                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9685
9686                                 if (y < (int)shown->value) {
9687                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9688                                 }
9689                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9690                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9691                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9692                                                         y + height - (int)shown->page_size - 1);
9693                                         } else {
9694                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9695                                                         (int)shown->upper - (int)shown->page_size - 1);
9696                                         }
9697                                 }
9698                         }
9699                 }
9700         }
9701
9702         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9703                 compose->focused_editable = widget;
9704         
9705 #ifdef MAEMO
9706         if (GTK_IS_TEXT_VIEW(widget) 
9707             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9708                 gtk_widget_ref(compose->notebook);
9709                 gtk_widget_ref(compose->edit_vbox);
9710                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9711                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9712                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9713                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9714                 gtk_widget_unref(compose->notebook);
9715                 gtk_widget_unref(compose->edit_vbox);
9716                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9717                                         G_CALLBACK(compose_grab_focus_cb),
9718                                         compose);
9719                 gtk_widget_grab_focus(widget);
9720                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9721                                         G_CALLBACK(compose_grab_focus_cb),
9722                                         compose);
9723         } else if (!GTK_IS_TEXT_VIEW(widget) 
9724                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9725                 gtk_widget_ref(compose->notebook);
9726                 gtk_widget_ref(compose->edit_vbox);
9727                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9728                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9729                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9730                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9731                 gtk_widget_unref(compose->notebook);
9732                 gtk_widget_unref(compose->edit_vbox);
9733                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9734                                         G_CALLBACK(compose_grab_focus_cb),
9735                                         compose);
9736                 gtk_widget_grab_focus(widget);
9737                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9738                                         G_CALLBACK(compose_grab_focus_cb),
9739                                         compose);
9740         }
9741 #endif
9742 }
9743
9744 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9745 {
9746         compose->modified = TRUE;
9747 #ifndef MAEMO
9748         compose_set_title(compose);
9749 #endif
9750 }
9751
9752 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
9753 {
9754         Compose *compose = (Compose *)data;
9755
9756         if (action == 1)
9757                 compose_wrap_all_full(compose, TRUE);
9758         else
9759                 compose_beautify_paragraph(compose, NULL, TRUE);
9760 }
9761
9762 static void compose_find_cb(gpointer data, guint action, GtkWidget *widget)
9763 {
9764         Compose *compose = (Compose *)data;
9765
9766         message_search_compose(compose);
9767 }
9768
9769 static void compose_toggle_autowrap_cb(gpointer data, guint action,
9770                                        GtkWidget *widget)
9771 {
9772         Compose *compose = (Compose *)data;
9773         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9774         if (compose->autowrap)
9775                 compose_wrap_all_full(compose, TRUE);
9776         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9777 }
9778
9779 static void compose_toggle_sign_cb(gpointer data, guint action,
9780                                    GtkWidget *widget)
9781 {
9782         Compose *compose = (Compose *)data;
9783
9784         if (GTK_CHECK_MENU_ITEM(widget)->active)
9785                 compose->use_signing = TRUE;
9786         else
9787                 compose->use_signing = FALSE;
9788 }
9789
9790 static void compose_toggle_encrypt_cb(gpointer data, guint action,
9791                                       GtkWidget *widget)
9792 {
9793         Compose *compose = (Compose *)data;
9794
9795         if (GTK_CHECK_MENU_ITEM(widget)->active)
9796                 compose->use_encryption = TRUE;
9797         else
9798                 compose->use_encryption = FALSE;
9799 }
9800
9801 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
9802 {
9803         g_free(compose->privacy_system);
9804
9805         compose->privacy_system = g_strdup(account->default_privacy_system);
9806         compose_update_privacy_system_menu_item(compose, warn);
9807 }
9808
9809 static void compose_toggle_ruler_cb(gpointer data, guint action,
9810                                     GtkWidget *widget)
9811 {
9812         Compose *compose = (Compose *)data;
9813
9814         if (GTK_CHECK_MENU_ITEM(widget)->active) {
9815                 gtk_widget_show(compose->ruler_hbox);
9816                 prefs_common.show_ruler = TRUE;
9817         } else {
9818                 gtk_widget_hide(compose->ruler_hbox);
9819                 gtk_widget_queue_resize(compose->edit_vbox);
9820                 prefs_common.show_ruler = FALSE;
9821         }
9822 }
9823
9824 static void compose_attach_drag_received_cb (GtkWidget          *widget,
9825                                              GdkDragContext     *context,
9826                                              gint                x,
9827                                              gint                y,
9828                                              GtkSelectionData   *data,
9829                                              guint               info,
9830                                              guint               time,
9831                                              gpointer            user_data)
9832 {
9833         Compose *compose = (Compose *)user_data;
9834         GList *list, *tmp;
9835
9836         if (gdk_atom_name(data->type) && 
9837             !strcmp(gdk_atom_name(data->type), "text/uri-list")
9838             && gtk_drag_get_source_widget(context) != 
9839                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9840                 list = uri_list_extract_filenames((const gchar *)data->data);
9841                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9842                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9843                         compose_attach_append
9844                                 (compose, (const gchar *)tmp->data,
9845                                  utf8_filename, NULL);
9846                         g_free(utf8_filename);
9847                 }
9848                 if (list) compose_changed_cb(NULL, compose);
9849                 list_free_strings(list);
9850                 g_list_free(list);
9851         } else if (gtk_drag_get_source_widget(context) 
9852                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9853                 /* comes from our summaryview */
9854                 SummaryView * summaryview = NULL;
9855                 GSList * list = NULL, *cur = NULL;
9856                 
9857                 if (mainwindow_get_mainwindow())
9858                         summaryview = mainwindow_get_mainwindow()->summaryview;
9859                 
9860                 if (summaryview)
9861                         list = summary_get_selected_msg_list(summaryview);
9862                 
9863                 for (cur = list; cur; cur = cur->next) {
9864                         MsgInfo *msginfo = (MsgInfo *)cur->data;
9865                         gchar *file = NULL;
9866                         if (msginfo)
9867                                 file = procmsg_get_message_file_full(msginfo, 
9868                                         TRUE, TRUE);
9869                         if (file) {
9870                                 compose_attach_append(compose, (const gchar *)file, 
9871                                         (const gchar *)file, "message/rfc822");
9872                                 g_free(file);
9873                         }
9874                 }
9875                 g_slist_free(list);
9876         }
9877 }
9878
9879 static gboolean compose_drag_drop(GtkWidget *widget,
9880                                   GdkDragContext *drag_context,
9881                                   gint x, gint y,
9882                                   guint time, gpointer user_data)
9883 {
9884         /* not handling this signal makes compose_insert_drag_received_cb
9885          * called twice */
9886         return TRUE;                                     
9887 }
9888
9889 static void compose_insert_drag_received_cb (GtkWidget          *widget,
9890                                              GdkDragContext     *drag_context,
9891                                              gint                x,
9892                                              gint                y,
9893                                              GtkSelectionData   *data,
9894                                              guint               info,
9895                                              guint               time,
9896                                              gpointer            user_data)
9897 {
9898         Compose *compose = (Compose *)user_data;
9899         GList *list, *tmp;
9900
9901         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
9902          * does not work */
9903         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
9904                 AlertValue val = G_ALERTDEFAULT;
9905
9906                 list = uri_list_extract_filenames((const gchar *)data->data);
9907
9908                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
9909                         /* Assume a list of no files, and data has ://, is a remote link */
9910                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
9911                         gchar *tmpfile = get_tmp_file();
9912                         str_write_to_file(tmpdata, tmpfile);
9913                         g_free(tmpdata);  
9914                         compose_insert_file(compose, tmpfile);
9915                         g_unlink(tmpfile);
9916                         g_free(tmpfile);
9917                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9918                         compose_beautify_paragraph(compose, NULL, TRUE);
9919                         return;
9920                 }
9921                 switch (prefs_common.compose_dnd_mode) {
9922                         case COMPOSE_DND_ASK:
9923                                 val = alertpanel_full(_("Insert or attach?"),
9924                                          _("Do you want to insert the contents of the file(s) "
9925                                            "into the message body, or attach it to the email?"),
9926                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
9927                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
9928                                 break;
9929                         case COMPOSE_DND_INSERT:
9930                                 val = G_ALERTALTERNATE;
9931                                 break;
9932                         case COMPOSE_DND_ATTACH:
9933                                 val = G_ALERTOTHER;
9934                                 break;
9935                         default:
9936                                 /* unexpected case */
9937                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
9938                 }
9939
9940                 if (val & G_ALERTDISABLE) {
9941                         val &= ~G_ALERTDISABLE;
9942                         /* remember what action to perform by default, only if we don't click Cancel */
9943                         if (val == G_ALERTALTERNATE)
9944                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
9945                         else if (val == G_ALERTOTHER)
9946                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
9947                 }
9948
9949                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
9950                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
9951                         list_free_strings(list);
9952                         g_list_free(list);
9953                         return;
9954                 } else if (val == G_ALERTOTHER) {
9955                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
9956                         list_free_strings(list);
9957                         g_list_free(list);
9958                         return;
9959                 } 
9960
9961                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9962                         compose_insert_file(compose, (const gchar *)tmp->data);
9963                 }
9964                 list_free_strings(list);
9965                 g_list_free(list);
9966                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9967                 return;
9968         } else {
9969 #if GTK_CHECK_VERSION(2, 8, 0)
9970                 /* do nothing, handled by GTK */
9971 #else
9972                 gchar *tmpfile = get_tmp_file();
9973                 str_write_to_file((const gchar *)data->data, tmpfile);
9974                 compose_insert_file(compose, tmpfile);
9975                 g_unlink(tmpfile);
9976                 g_free(tmpfile);
9977                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9978 #endif
9979                 return;
9980         }
9981         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9982 }
9983
9984 static void compose_header_drag_received_cb (GtkWidget          *widget,
9985                                              GdkDragContext     *drag_context,
9986                                              gint                x,
9987                                              gint                y,
9988                                              GtkSelectionData   *data,
9989                                              guint               info,
9990                                              guint               time,
9991                                              gpointer            user_data)
9992 {
9993         GtkEditable *entry = (GtkEditable *)user_data;
9994         gchar *email = (gchar *)data->data;
9995
9996         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
9997          * does not work */
9998
9999         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10000                 gchar *decoded=g_new(gchar, strlen(email));
10001                 int start = 0;
10002
10003                 email += strlen("mailto:");
10004                 decode_uri(decoded, email); /* will fit */
10005                 gtk_editable_delete_text(entry, 0, -1);
10006                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10007                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10008                 g_free(decoded);
10009                 return;
10010         }
10011         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10012 }
10013
10014 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
10015                                              GtkWidget *widget)
10016 {
10017         Compose *compose = (Compose *)data;
10018
10019         if (GTK_CHECK_MENU_ITEM(widget)->active)
10020                 compose->return_receipt = TRUE;
10021         else
10022                 compose->return_receipt = FALSE;
10023 }
10024
10025 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
10026                                              GtkWidget *widget)
10027 {
10028         Compose *compose = (Compose *)data;
10029
10030         if (GTK_CHECK_MENU_ITEM(widget)->active)
10031                 compose->remove_references = TRUE;
10032         else
10033                 compose->remove_references = FALSE;
10034 }
10035
10036 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10037                                             GdkEventKey *event,
10038                                             ComposeHeaderEntry *headerentry)
10039 {
10040         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10041             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10042             !(event->state & GDK_MODIFIER_MASK) &&
10043             (event->keyval == GDK_BackSpace) &&
10044             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10045                 gtk_container_remove
10046                         (GTK_CONTAINER(headerentry->compose->header_table),
10047                          headerentry->combo);
10048                 gtk_container_remove
10049                         (GTK_CONTAINER(headerentry->compose->header_table),
10050                          headerentry->entry);
10051                 headerentry->compose->header_list =
10052                         g_slist_remove(headerentry->compose->header_list,
10053                                        headerentry);
10054                 g_free(headerentry);
10055         } else  if (event->keyval == GDK_Tab) {
10056                 if (headerentry->compose->header_last == headerentry) {
10057                         /* Override default next focus, and give it to subject_entry
10058                          * instead of notebook tabs
10059                          */
10060                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10061                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10062                         return TRUE;
10063                 }
10064         }
10065         return FALSE;
10066 }
10067
10068 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10069                                     ComposeHeaderEntry *headerentry)
10070 {
10071         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10072                 compose_create_header_entry(headerentry->compose);
10073                 g_signal_handlers_disconnect_matched
10074                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10075                          0, 0, NULL, NULL, headerentry);
10076                 
10077                 /* Automatically scroll down */
10078                 compose_show_first_last_header(headerentry->compose, FALSE);
10079                 
10080         }
10081         return FALSE;
10082 }
10083
10084 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10085 {
10086         GtkAdjustment *vadj;
10087
10088         g_return_if_fail(compose);
10089         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10090         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10091
10092         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10093         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10094         gtk_adjustment_changed(vadj);
10095 }
10096
10097 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10098                           const gchar *text, gint len, Compose *compose)
10099 {
10100         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10101                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10102         GtkTextMark *mark;
10103
10104         g_return_if_fail(text != NULL);
10105
10106         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10107                                         G_CALLBACK(text_inserted),
10108                                         compose);
10109         if (paste_as_quotation) {
10110                 gchar *new_text;
10111                 const gchar *qmark;
10112                 guint pos = 0;
10113                 GtkTextIter start_iter;
10114
10115                 if (len < 0)
10116                         len = strlen(text);
10117
10118                 new_text = g_strndup(text, len);
10119
10120                 qmark = compose_quote_char_from_context(compose);
10121
10122                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10123                 gtk_text_buffer_place_cursor(buffer, iter);
10124
10125                 pos = gtk_text_iter_get_offset(iter);
10126
10127                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10128                                                   _("Quote format error at line %d."));
10129                 quote_fmt_reset_vartable();
10130                 g_free(new_text);
10131                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10132                                   GINT_TO_POINTER(paste_as_quotation - 1));
10133                                   
10134                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10135                 gtk_text_buffer_place_cursor(buffer, iter);
10136                 gtk_text_buffer_delete_mark(buffer, mark);
10137
10138                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10139                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10140                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10141                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10142                 gtk_text_buffer_delete_mark(buffer, mark);
10143         } else {
10144                 if (strcmp(text, "\n") || compose->automatic_break
10145                 || gtk_text_iter_starts_line(iter))
10146                         gtk_text_buffer_insert(buffer, iter, text, len);
10147                 else {
10148                         /* check if the preceding is just whitespace or quote */
10149                         GtkTextIter start_line;
10150                         gchar *tmp = NULL, *quote = NULL;
10151                         gint quote_len = 0, is_normal = 0;
10152                         start_line = *iter;
10153                         gtk_text_iter_set_line_offset(&start_line, 0); 
10154                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10155                         g_strstrip(tmp);
10156                         if (*tmp == '\0') {
10157                                 is_normal = 1;
10158                         } else {
10159                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10160                                 if (quote)
10161                                         is_normal = 1;
10162                                 g_free(quote);
10163                         }
10164                         g_free(tmp);
10165                         
10166                         if (is_normal) {
10167                                 gtk_text_buffer_insert(buffer, iter, text, len);
10168                         } else {
10169                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10170                                         iter, text, len, "no_join", NULL);
10171                         }
10172                 }
10173         }
10174         
10175         if (!paste_as_quotation) {
10176                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10177                 compose_beautify_paragraph(compose, iter, FALSE);
10178                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10179                 gtk_text_buffer_delete_mark(buffer, mark);
10180         }
10181
10182         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10183                                           G_CALLBACK(text_inserted),
10184                                           compose);
10185         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10186
10187         if (prefs_common.autosave && 
10188             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10189             compose->draft_timeout_tag != -2 /* disabled while loading */)
10190                 compose->draft_timeout_tag = g_timeout_add
10191                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10192 }
10193 static gint compose_defer_auto_save_draft(Compose *compose)
10194 {
10195         compose->draft_timeout_tag = -1;
10196         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
10197         return FALSE;
10198 }
10199
10200 #if USE_ASPELL
10201 static void compose_check_all(Compose *compose)
10202 {
10203         if (compose->gtkaspell)
10204                 gtkaspell_check_all(compose->gtkaspell);
10205 }
10206
10207 static void compose_highlight_all(Compose *compose)
10208 {
10209         if (compose->gtkaspell)
10210                 gtkaspell_highlight_all(compose->gtkaspell);
10211 }
10212
10213 static void compose_check_backwards(Compose *compose)
10214 {
10215         if (compose->gtkaspell) 
10216                 gtkaspell_check_backwards(compose->gtkaspell);
10217         else {
10218                 GtkItemFactory *ifactory;
10219                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
10220                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
10221                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
10222         }
10223 }
10224
10225 static void compose_check_forwards_go(Compose *compose)
10226 {
10227         if (compose->gtkaspell) 
10228                 gtkaspell_check_forwards_go(compose->gtkaspell);
10229         else {
10230                 GtkItemFactory *ifactory;
10231                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
10232                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
10233                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
10234         }
10235 }
10236 #endif
10237
10238 /*!
10239  *\brief        Guess originating forward account from MsgInfo and several 
10240  *              "common preference" settings. Return NULL if no guess. 
10241  */
10242 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10243 {
10244         PrefsAccount *account = NULL;
10245         
10246         g_return_val_if_fail(msginfo, NULL);
10247         g_return_val_if_fail(msginfo->folder, NULL);
10248         g_return_val_if_fail(msginfo->folder->prefs, NULL);
10249
10250         if (msginfo->folder->prefs->enable_default_account)
10251                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10252                 
10253         if (!account) 
10254                 account = msginfo->folder->folder->account;
10255                 
10256         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10257                 gchar *to;
10258                 Xstrdup_a(to, msginfo->to, return NULL);
10259                 extract_address(to);
10260                 account = account_find_from_address(to, FALSE);
10261         }
10262
10263         if (!account && prefs_common.forward_account_autosel) {
10264                 gchar cc[BUFFSIZE];
10265                 if (!procheader_get_header_from_msginfo
10266                         (msginfo, cc,sizeof cc , "Cc:")) { 
10267                         gchar *buf = cc + strlen("Cc:");
10268                         extract_address(buf);
10269                         account = account_find_from_address(buf, FALSE);
10270                 }
10271         }
10272         
10273         if (!account && prefs_common.forward_account_autosel) {
10274                 gchar deliveredto[BUFFSIZE];
10275                 if (!procheader_get_header_from_msginfo
10276                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10277                         gchar *buf = deliveredto + strlen("Delivered-To:");
10278                         extract_address(buf);
10279                         account = account_find_from_address(buf, FALSE);
10280                 }
10281         }
10282         
10283         return account;
10284 }
10285
10286 gboolean compose_close(Compose *compose)
10287 {
10288         gint x, y;
10289
10290         if (!g_mutex_trylock(compose->mutex)) {
10291                 /* we have to wait for the (possibly deferred by auto-save)
10292                  * drafting to be done, before destroying the compose under
10293                  * it. */
10294                 debug_print("waiting for drafting to finish...\n");
10295                 compose_allow_user_actions(compose, FALSE);
10296                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10297                 return FALSE;
10298         }
10299         g_return_val_if_fail(compose, FALSE);
10300         gtkut_widget_get_uposition(compose->window, &x, &y);
10301         prefs_common.compose_x = x;
10302         prefs_common.compose_y = y;
10303         g_mutex_unlock(compose->mutex);
10304         compose_destroy(compose);
10305         return FALSE;
10306 }
10307
10308 /**
10309  * Add entry field for each address in list.
10310  * \param compose     E-Mail composition object.
10311  * \param listAddress List of (formatted) E-Mail addresses.
10312  */
10313 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10314         GList *node;
10315         gchar *addr;
10316         node = listAddress;
10317         while( node ) {
10318                 addr = ( gchar * ) node->data;
10319                 compose_entry_append( compose, addr, COMPOSE_TO );
10320                 node = g_list_next( node );
10321         }
10322 }
10323
10324 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10325                                     guint action, gboolean opening_multiple)
10326 {
10327         gchar *body = NULL;
10328         GSList *new_msglist = NULL;
10329         MsgInfo *tmp_msginfo = NULL;
10330         gboolean originally_enc = FALSE;
10331         Compose *compose = NULL;
10332
10333         g_return_if_fail(msgview != NULL);
10334
10335         g_return_if_fail(msginfo_list != NULL);
10336
10337         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10338                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10339                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10340
10341                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10342                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10343                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10344                                                 orig_msginfo, mimeinfo);
10345                         if (tmp_msginfo != NULL) {
10346                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10347
10348                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10349                                 tmp_msginfo->folder = orig_msginfo->folder;
10350                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10351                                 if (orig_msginfo->tags)
10352                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10353                         }
10354                 }
10355         }
10356
10357         if (!opening_multiple)
10358                 body = messageview_get_selection(msgview);
10359
10360         if (new_msglist) {
10361                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10362                 procmsg_msginfo_free(tmp_msginfo);
10363                 g_slist_free(new_msglist);
10364         } else
10365                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10366
10367         if (compose && originally_enc) {
10368                 compose_force_encryption(compose, compose->account, FALSE);
10369         }
10370
10371         g_free(body);
10372 }
10373
10374 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10375                                     guint action)
10376 {
10377         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10378         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10379                 GSList *cur = msginfo_list;
10380                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10381                                                "messages. Opening the windows "
10382                                                "could take some time. Do you "
10383                                                "want to continue?"), 
10384                                                g_slist_length(msginfo_list));
10385                 if (g_slist_length(msginfo_list) > 9
10386                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10387                     != G_ALERTALTERNATE) {
10388                         g_free(msg);
10389                         return;
10390                 }
10391                 g_free(msg);
10392                 /* We'll open multiple compose windows */
10393                 /* let the WM place the next windows */
10394                 compose_force_window_origin = FALSE;
10395                 for (; cur; cur = cur->next) {
10396                         GSList tmplist;
10397                         tmplist.data = cur->data;
10398                         tmplist.next = NULL;
10399                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10400                 }
10401                 compose_force_window_origin = TRUE;
10402         } else {
10403                 /* forwarding multiple mails as attachments is done via a
10404                  * single compose window */
10405                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10406         }
10407 }
10408
10409 void compose_set_position(Compose *compose, gint pos)
10410 {
10411         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10412
10413         gtkut_text_view_set_position(text, pos);
10414 }
10415
10416 gboolean compose_search_string(Compose *compose,
10417                                 const gchar *str, gboolean case_sens)
10418 {
10419         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10420
10421         return gtkut_text_view_search_string(text, str, case_sens);
10422 }
10423
10424 gboolean compose_search_string_backward(Compose *compose,
10425                                 const gchar *str, gboolean case_sens)
10426 {
10427         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10428
10429         return gtkut_text_view_search_string_backward(text, str, case_sens);
10430 }
10431
10432 /* allocate a msginfo structure and populate its data from a compose data structure */
10433 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10434 {
10435         MsgInfo *newmsginfo;
10436         GSList *list;
10437         gchar buf[BUFFSIZE];
10438
10439         g_return_val_if_fail( compose != NULL, NULL );
10440
10441         newmsginfo = procmsg_msginfo_new();
10442
10443         /* date is now */
10444         get_rfc822_date(buf, sizeof(buf));
10445         newmsginfo->date = g_strdup(buf);
10446
10447         /* from */
10448         if (compose->from_name) {
10449                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10450                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10451         }
10452
10453         /* subject */
10454         if (compose->subject_entry)
10455                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10456
10457         /* to, cc, reply-to, newsgroups */
10458         for (list = compose->header_list; list; list = list->next) {
10459                 gchar *header = gtk_editable_get_chars(
10460                                                                 GTK_EDITABLE(
10461                                                                 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10462                 gchar *entry = gtk_editable_get_chars(
10463                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10464
10465                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10466                         if ( newmsginfo->to == NULL ) {
10467                                 newmsginfo->to = g_strdup(entry);
10468                         } else if (entry && *entry) {
10469                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10470                                 g_free(newmsginfo->to);
10471                                 newmsginfo->to = tmp;
10472                         }
10473                 } else
10474                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10475                         if ( newmsginfo->cc == NULL ) {
10476                                 newmsginfo->cc = g_strdup(entry);
10477                         } else if (entry && *entry) {
10478                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10479                                 g_free(newmsginfo->cc);
10480                                 newmsginfo->cc = tmp;
10481                         }
10482                 } else
10483                 if ( strcasecmp(header,
10484                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10485                         if ( newmsginfo->newsgroups == NULL ) {
10486                                 newmsginfo->newsgroups = g_strdup(entry);
10487                         } else if (entry && *entry) {
10488                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10489                                 g_free(newmsginfo->newsgroups);
10490                                 newmsginfo->newsgroups = tmp;
10491                         }
10492                 }
10493
10494                 g_free(header);
10495                 g_free(entry);  
10496         }
10497
10498         /* other data is unset */
10499
10500         return newmsginfo;
10501 }
10502
10503 #ifdef USE_ASPELL
10504 /* update compose's dictionaries from folder dict settings */
10505 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10506                                                 FolderItem *folder_item)
10507 {
10508         g_return_if_fail(compose != NULL);
10509
10510         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10511                 FolderItemPrefs *prefs = folder_item->prefs;
10512
10513                 if (prefs->enable_default_dictionary)
10514                         gtkaspell_change_dict(compose->gtkaspell,
10515                                         prefs->default_dictionary, FALSE);
10516                 if (folder_item->prefs->enable_default_alt_dictionary)
10517                         gtkaspell_change_alt_dict(compose->gtkaspell,
10518                                         prefs->default_alt_dictionary);
10519                 if (prefs->enable_default_dictionary
10520                         || prefs->enable_default_alt_dictionary)
10521                         compose_spell_menu_changed(compose);
10522         }
10523 }
10524 #endif
10525
10526 /*
10527  * End of Source.
10528  */