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