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