0537ee27a549feb777b9357983e389ff01a8e1c2
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkitemfactory.h>
32 #include <gtk/gtkcheckmenuitem.h>
33 #include <gtk/gtkoptionmenu.h>
34 #include <gtk/gtkwidget.h>
35 #include <gtk/gtkclist.h>
36 #include <gtk/gtkctree.h>
37 #include <gtk/gtkvpaned.h>
38 #include <gtk/gtkentry.h>
39 #include <gtk/gtkeditable.h>
40 #include <gtk/gtkwindow.h>
41 #include <gtk/gtksignal.h>
42 #include <gtk/gtkvbox.h>
43 #include <gtk/gtkcontainer.h>
44 #include <gtk/gtkhandlebox.h>
45 #include <gtk/gtktoolbar.h>
46 #include <gtk/gtktable.h>
47 #include <gtk/gtkhbox.h>
48 #include <gtk/gtklabel.h>
49 #include <gtk/gtkscrolledwindow.h>
50 #include <gtk/gtkthemes.h>
51 #include <gtk/gtkdnd.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include <unistd.h>
57 #include <time.h>
58 /* #include <sys/utsname.h> */
59 #include <stdlib.h>
60 #include <sys/wait.h>
61 #include <signal.h>
62 #include <errno.h>
63
64 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
65 #  include <wchar.h>
66 #  include <wctype.h>
67 #endif
68
69
70 #include "gtkstext.h"
71
72 #include "intl.h"
73 #include "main.h"
74 #include "mainwindow.h"
75 #include "compose.h"
76 #include "addressbook.h"
77 #include "folderview.h"
78 #include "procmsg.h"
79 #include "menu.h"
80 #include "send.h"
81 #include "news.h"
82 #include "customheader.h"
83 #include "prefs_common.h"
84 #include "prefs_account.h"
85 #include "account.h"
86 #include "filesel.h"
87 #include "procheader.h"
88 #include "procmime.h"
89 #include "statusbar.h"
90 #include "about.h"
91 #include "base64.h"
92 #include "codeconv.h"
93 #include "utils.h"
94 #include "gtkutils.h"
95 #include "socket.h"
96 #include "alertpanel.h"
97 #include "manage_window.h"
98 #include "gtkshruler.h"
99 #include "folder.h"
100 #include "addr_compl.h"
101 #include "template_select.h"
102
103 #if USE_GPGME
104 #  include "rfc2015.h"
105 #endif
106
107 #include "quote_fmt.h"
108
109 typedef enum
110 {
111         COL_MIMETYPE = 0,
112         COL_SIZE     = 1,
113         COL_NAME     = 2
114 } AttachColumnPos;
115
116 #define N_ATTACH_COLS           3
117
118 #define B64_LINE_SIZE           57
119 #define B64_BUFFSIZE            77
120
121 #define MAX_REFERENCES_LEN      999
122
123 static GdkColor quote_color = {0, 0, 0, 0xbfff};
124
125 static GList *compose_list = NULL;
126
127 static Compose *compose_create                  (PrefsAccount   *account,
128                                                  ComposeMode     mode);
129 static void compose_toolbar_create              (Compose        *compose,
130                                                  GtkWidget      *container);
131 static GtkWidget *compose_account_option_menu_create
132                                                 (Compose        *compose);
133 static void compose_destroy                     (Compose        *compose);
134
135 static gint compose_parse_header                (Compose        *compose,
136                                                  MsgInfo        *msginfo);
137 static gchar *compose_parse_references          (const gchar    *ref,
138                                                  const gchar    *msgid);
139 static void compose_quote_file                  (Compose        *compose,
140                                                  MsgInfo        *msginfo,
141                                                  FILE           *fp);
142 static gchar *compose_quote_parse_fmt           (Compose        *compose,
143                                                  MsgInfo        *msginfo,
144                                                  const gchar    *fmt);
145 static void compose_reply_set_entry             (Compose        *compose,
146                                                  MsgInfo        *msginfo,
147                                                  gboolean        to_all,
148                                                  gboolean        to_sender,
149                                                  gboolean
150                                                  followup_and_reply_to);
151 static void compose_reedit_set_entry            (Compose        *compose,
152                                                  MsgInfo        *msginfo);
153 static void compose_insert_sig                  (Compose        *compose);
154 static void compose_insert_file                 (Compose        *compose,
155                                                  const gchar    *file);
156 static void compose_attach_append               (Compose        *compose,
157                                                  const gchar    *file,
158                                                  ContentType     cnttype);
159 static void compose_attach_append_with_type(Compose *compose,
160                                             const gchar *file,
161                                             const gchar *type,
162                                             ContentType cnttype);
163 static void compose_wrap_line                   (Compose        *compose);
164 static void compose_wrap_line_all               (Compose        *compose);
165 static void compose_set_title                   (Compose        *compose);
166
167 static PrefsAccount *compose_current_mail_account(void);
168 /* static gint compose_send                     (Compose        *compose); */
169 static gint compose_write_to_file               (Compose        *compose,
170                                                  const gchar    *file,
171                                                  gboolean        is_draft);
172 static gint compose_write_body_to_file          (Compose        *compose,
173                                                  const gchar    *file);
174 static gint compose_save_to_outbox              (Compose        *compose,
175                                                  const gchar    *file);
176 static gint compose_remove_reedit_target        (Compose        *compose);
177 static gint compose_queue                       (Compose        *compose,
178                                                  const gchar    *file);
179 static void compose_write_attach                (Compose        *compose,
180                                                  FILE           *fp);
181 static gint compose_write_headers               (Compose        *compose,
182                                                  FILE           *fp,
183                                                  const gchar    *charset,
184                                                  EncodingType    encoding,
185                                                  gboolean        is_draft);
186
187 static void compose_convert_header              (gchar          *dest,
188                                                  gint            len,
189                                                  gchar          *src,
190                                                  gint            header_len);
191 static void compose_generate_msgid              (Compose        *compose,
192                                                  gchar          *buf,
193                                                  gint            len);
194
195 static void compose_attach_info_free            (AttachInfo     *ainfo);
196 static void compose_attach_remove_selected      (Compose        *compose);
197
198 static void compose_attach_property             (Compose        *compose);
199 static void compose_attach_property_create      (gboolean       *cancelled);
200 static void attach_property_ok                  (GtkWidget      *widget,
201                                                  gboolean       *cancelled);
202 static void attach_property_cancel              (GtkWidget      *widget,
203                                                  gboolean       *cancelled);
204 static gint attach_property_delete_event        (GtkWidget      *widget,
205                                                  GdkEventAny    *event,
206                                                  gboolean       *cancelled);
207 static void attach_property_key_pressed         (GtkWidget      *widget,
208                                                  GdkEventKey    *event,
209                                                  gboolean       *cancelled);
210
211 static void compose_exec_ext_editor             (Compose           *compose);
212 static gint compose_exec_ext_editor_real        (const gchar       *file);
213 static gboolean compose_ext_editor_kill         (Compose           *compose);
214 static void compose_input_cb                    (gpointer           data,
215                                                  gint               source,
216                                                  GdkInputCondition  condition);
217 static void compose_set_ext_editor_sensitive    (Compose           *compose,
218                                                  gboolean           sensitive);
219
220 static gint calc_cursor_xpos    (GtkSText       *text,
221                                  gint            extra,
222                                  gint            char_width);
223
224 /* callback functions */
225
226 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
227                                          GtkAllocation  *allocation,
228                                          GtkSHRuler     *shruler);
229
230 static void toolbar_send_cb             (GtkWidget      *widget,
231                                          gpointer        data);
232 static void toolbar_send_later_cb       (GtkWidget      *widget,
233                                          gpointer        data);
234 static void toolbar_draft_cb            (GtkWidget      *widget,
235                                          gpointer        data);
236 static void toolbar_insert_cb           (GtkWidget      *widget,
237                                          gpointer        data);
238 static void toolbar_attach_cb           (GtkWidget      *widget,
239                                          gpointer        data);
240 static void toolbar_sig_cb              (GtkWidget      *widget,
241                                          gpointer        data);
242 static void toolbar_ext_editor_cb       (GtkWidget      *widget,
243                                          gpointer        data);
244 static void toolbar_linewrap_cb         (GtkWidget      *widget,
245                                          gpointer        data);
246 static void toolbar_address_cb          (GtkWidget      *widget,
247                                          gpointer        data);
248 static void template_select_cb          (gpointer        daat,
249                                          guint           action,
250                                          GtkWidget      *widget);
251
252
253 static void select_account(Compose * compose, PrefsAccount * ac);
254 static void account_activated           (GtkMenuItem    *menuitem,
255                                          gpointer        data);
256
257 static void attach_selected             (GtkCList       *clist,
258                                          gint            row,
259                                          gint            column,
260                                          GdkEvent       *event,
261                                          gpointer        data);
262 static void attach_button_pressed       (GtkWidget      *widget,
263                                          GdkEventButton *event,
264                                          gpointer        data);
265 static void attach_key_pressed          (GtkWidget      *widget,
266                                          GdkEventKey    *event,
267                                          gpointer        data);
268
269 static void compose_send_cb             (gpointer        data,
270                                          guint           action,
271                                          GtkWidget      *widget);
272 static void compose_send_later_cb       (gpointer        data,
273                                          guint           action,
274                                          GtkWidget      *widget);
275
276 static void compose_draft_cb            (gpointer        data,
277                                          guint           action,
278                                          GtkWidget      *widget);
279
280 static void compose_attach_cb           (gpointer        data,
281                                          guint           action,
282                                          GtkWidget      *widget);
283 static void compose_insert_file_cb      (gpointer        data,
284                                          guint           action,
285                                          GtkWidget      *widget);
286
287 static void compose_close_cb            (gpointer        data,
288                                          guint           action,
289                                          GtkWidget      *widget);
290 static void compose_address_cb          (gpointer        data,
291                                          guint           action,
292                                          GtkWidget      *widget);
293
294 static void compose_ext_editor_cb       (gpointer        data,
295                                          guint           action,
296                                          GtkWidget      *widget);
297
298 static gint compose_delete_cb           (GtkWidget      *widget,
299                                          GdkEventAny    *event,
300                                          gpointer        data);
301 static void compose_destroy_cb          (GtkWidget      *widget,
302                                          Compose        *compose);
303
304 static void compose_cut_cb              (Compose        *compose);
305 static void compose_copy_cb             (Compose        *compose);
306 static void compose_paste_cb            (Compose        *compose);
307 static void compose_allsel_cb           (Compose        *compose);
308
309 static void compose_grab_focus_cb       (GtkWidget      *widget,
310                                          Compose        *compose);
311
312 static void compose_changed_cb          (GtkEditable    *editable,
313                                          Compose        *compose);
314 static void compose_button_press_cb     (GtkWidget      *widget,
315                                          GdkEventButton *event,
316                                          Compose        *compose);
317 #if 0
318 static void compose_key_press_cb        (GtkWidget      *widget,
319                                          GdkEventKey    *event,
320                                          Compose        *compose);
321 #endif
322
323 static void compose_toggle_to_cb        (gpointer        data,
324                                          guint           action,
325                                          GtkWidget      *widget);
326 static void compose_toggle_cc_cb        (gpointer        data,
327                                          guint           action,
328                                          GtkWidget      *widget);
329 static void compose_toggle_bcc_cb       (gpointer        data,
330                                          guint           action,
331                                          GtkWidget      *widget);
332 static void compose_toggle_replyto_cb   (gpointer        data,
333                                          guint           action,
334                                          GtkWidget      *widget);
335 static void compose_toggle_followupto_cb(gpointer        data,
336                                          guint           action,
337                                          GtkWidget      *widget);
338 static void compose_toggle_attach_cb    (gpointer        data,
339                                          guint           action,
340                                          GtkWidget      *widget);
341 static void compose_toggle_ruler_cb     (gpointer        data,
342                                          guint           action,
343                                          GtkWidget      *widget);
344 #if USE_GPGME
345 static void compose_toggle_sign_cb      (gpointer        data,
346                                          guint           action,
347                                          GtkWidget      *widget);
348 static void compose_toggle_encrypt_cb   (gpointer        data,
349                                          guint           action,
350                                          GtkWidget      *widget);
351 #endif
352 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
353                                              GtkWidget *widget);
354
355 static void compose_attach_drag_received_cb (GtkWidget          *widget,
356                                              GdkDragContext     *drag_context,
357                                              gint                x,
358                                              gint                y,
359                                              GtkSelectionData   *data,
360                                              guint               info,
361                                              guint               time,
362                                              gpointer            user_data);
363 static void compose_insert_drag_received_cb (GtkWidget          *widget,
364                                              GdkDragContext     *drag_context,
365                                              gint                x,
366                                              gint                y,
367                                              GtkSelectionData   *data,
368                                              guint               info,
369                                              guint               time,
370                                              gpointer            user_data);
371
372 static void to_activated                (GtkWidget      *widget,
373                                          Compose        *compose);
374 static void newsgroups_activated        (GtkWidget      *widget,
375                                          Compose        *compose);
376 static void subject_activated           (GtkWidget      *widget,
377                                          Compose        *compose);
378 static void cc_activated                (GtkWidget      *widget,
379                                          Compose        *compose);
380 static void bcc_activated               (GtkWidget      *widget,
381                                          Compose        *compose);
382 static void replyto_activated           (GtkWidget      *widget,
383                                          Compose        *compose);
384 static void followupto_activated        (GtkWidget      *widget,
385                                          Compose        *compose);
386 static void compose_attach_parts(Compose * compose,
387                                  MsgInfo * msginfo);
388
389 static gchar *compose_quote_fmt         (Compose        *compose,
390                                          MsgInfo        *msginfo,
391                                          const gchar    *fmt,
392                                          const gchar    * qmark);
393
394 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
395                                   gboolean to_all,
396                                   gboolean ignore_replyto,
397                                   gboolean followup_and_reply_to);
398
399 Compose * compose_generic_new (PrefsAccount     *account,
400                                const gchar      *to,
401                                FolderItem       *item);
402
403 static GtkItemFactoryEntry compose_popup_entries[] =
404 {
405         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
406         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
407         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
408         {N_("/_Property..."),   NULL, compose_attach_property, 0, NULL}
409 };
410
411 static GtkItemFactoryEntry compose_entries[] =
412 {
413         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
414         {N_("/_File/_Attach file"),             "<control>M", compose_attach_cb,      0, NULL},
415         {N_("/_File/_Insert file"),             "<control>I", compose_insert_file_cb, 0, NULL},
416         {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig,     0, NULL},
417         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
418         {N_("/_File/_Close"),                   "<alt>W", compose_close_cb, 0, NULL},
419
420         {N_("/_Edit"),             NULL, NULL, 0, "<Branch>"},
421         {N_("/_Edit/_Undo"),       "<control>Z", NULL, 0, NULL},
422         {N_("/_Edit/_Redo"),       "<control>Y", NULL, 0, NULL},
423         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
424         {N_("/_Edit/Cu_t"),        "<control>X", compose_cut_cb,    0, NULL},
425         {N_("/_Edit/_Copy"),       "<control>C", compose_copy_cb,   0, NULL},
426         {N_("/_Edit/_Paste"),      "<control>V", compose_paste_cb,  0, NULL},
427         {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
428         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
429         {N_("/_Edit/_Wrap current paragraph"), "<alt>L", compose_wrap_line, 0, NULL},
430         {N_("/_Edit/Wrap all long _lines"),
431                         "<shift><alt>L", compose_wrap_line_all, 0, NULL},
432         {N_("/_Edit/Edit with e_xternal editor"),
433                         "<alt>X", compose_ext_editor_cb, 0, NULL},
434
435         {N_("/_Message"),               NULL, NULL, 0, "<Branch>"},
436         {N_("/_Message/_Send"),         "<shift><control>S",
437                                         compose_send_cb, 0, NULL},
438         {N_("/_Message/Send _later"),   "<shift><alt>S",
439                                         compose_send_later_cb,  0, NULL},
440         {N_("/_Message/Save to _draft folder"),
441                                         "<alt>D", compose_draft_cb, 0, NULL},
442         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
443         {N_("/_Message/_To"),           NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
444         {N_("/_Message/_Cc"),           NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
445         {N_("/_Message/_Bcc"),          NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
446         {N_("/_Message/_Reply to"),     NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
447         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
448         {N_("/_Message/_Followup to"),  NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
449         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
450         {N_("/_Message/_Attach"),       NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
451 #if USE_GPGME
452         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
453         {N_("/_Message/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
454         {N_("/_Message/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
455 #endif /* USE_GPGME */
456         {N_("/_Message/---"),           NULL,           NULL,   0, "<Separator>"},
457         {N_("/_Message/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
458         {N_("/_Tool"),                  NULL, NULL, 0, "<Branch>"},
459         {N_("/_Tool/Show _ruler"),      NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
460         {N_("/_Tool/_Address book"),    "<alt>A", compose_address_cb , 0, NULL},
461         {N_("/_Tool/_Templates ..."),   NULL, template_select_cb, 0, NULL},
462         {N_("/_Help"),                  NULL, NULL, 0, "<LastBranch>"},
463         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
464 };
465
466 static GtkTargetEntry compose_mime_types[] =
467 {
468         {"text/uri-list", 0, 0}
469 };
470
471 Compose * compose_new(PrefsAccount *account)
472 {
473         return compose_generic_new(account, NULL, NULL);
474 }
475
476 Compose * compose_new_with_recipient(PrefsAccount *account, const gchar *to)
477 {
478         return compose_generic_new(account, to, NULL);
479 }
480
481 Compose * compose_new_with_folderitem(PrefsAccount *account, FolderItem *item)
482 {
483         return compose_generic_new(account, NULL, item);
484 }
485
486 Compose * compose_generic_new(PrefsAccount *account, const gchar *to, FolderItem *item)
487 {
488         Compose *compose;
489
490         if (!account) account = cur_account;
491         g_return_val_if_fail(account != NULL, NULL);
492
493         compose = compose_create(account, COMPOSE_NEW);
494         compose->replyinfo = NULL;
495
496         if (prefs_common.auto_sig)
497                 compose_insert_sig(compose);
498         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
499         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
500
501         if (account->protocol != A_NNTP) {
502                 if (to) {
503                         compose_entry_append(compose, to, COMPOSE_TO);
504                         gtk_widget_grab_focus(compose->subject_entry);
505                 } else {
506                         if(item && item->prefs->enable_default_to) {
507                                 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
508                         } else {
509                                 gtk_widget_grab_focus(compose->to_entry);
510                         }
511                 }
512                 if(item && item->prefs->request_return_receipt) {
513                         GtkItemFactory *ifactory;
514                 
515                         ifactory = gtk_item_factory_from_widget(compose->menubar);
516                         menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
517                 }
518         } else {
519                 if (to) {
520                         compose_entry_append(compose, to, COMPOSE_NEWSGROUPS);
521                         gtk_widget_grab_focus(compose->subject_entry);
522                 } else
523                         gtk_widget_grab_focus(compose->newsgroups_entry);
524         }
525
526         if (prefs_common.auto_exteditor)
527                 compose_exec_ext_editor(compose);
528
529         return compose;
530 }
531
532 #define CHANGE_FLAGS(msginfo) \
533 { \
534 if (msginfo->folder->folder->change_flags != NULL) \
535 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
536                                       msginfo->folder, \
537                                       msginfo); \
538 }
539
540 /*
541 Compose * compose_new_followup_and_replyto(PrefsAccount *account,
542                                            const gchar *followupto, gchar * to)
543 {
544         Compose *compose;
545
546         if (!account) account = cur_account;
547         g_return_val_if_fail(account != NULL, NULL);
548         g_return_val_if_fail(account->protocol != A_NNTP, NULL);
549
550         compose = compose_create(account, COMPOSE_NEW);
551
552         if (prefs_common.auto_sig)
553                 compose_insert_sig(compose);
554         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
555         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
556
557         compose_entry_append(compose, to, COMPOSE_TO);
558         compose_entry_append(compose, followupto, COMPOSE_NEWSGROUPS);
559         gtk_widget_grab_focus(compose->subject_entry);
560
561         return compose;
562 }
563 */
564
565 void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all,
566                    gboolean ignore_replyto)
567 {
568         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, FALSE);
569 }
570
571 void compose_followup_and_reply_to(MsgInfo *msginfo, gboolean quote,
572                                    gboolean to_all,
573                                    gboolean ignore_replyto)
574 {
575         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, TRUE);
576 }
577
578 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
579                                   gboolean to_all,
580                                   gboolean ignore_replyto,
581                                   gboolean followup_and_reply_to)
582 {
583         Compose *compose;
584         PrefsAccount *account;
585         PrefsAccount *reply_account;
586         GtkSText *text;
587
588         g_return_if_fail(msginfo != NULL);
589         g_return_if_fail(msginfo->folder != NULL);
590
591         account = msginfo->folder->folder->account;
592         if (!account && msginfo->to && prefs_common.reply_account_autosel) {
593                 gchar *to;
594                 Xstrdup_a(to, msginfo->to, return);
595                 extract_address(to);
596                 account = account_find_from_address(to);
597         }
598         if(!account&& prefs_common.reply_account_autosel) {
599                 gchar cc[BUFFSIZE];
600                 if(!get_header_from_msginfo(msginfo,cc,sizeof(cc),"CC:")){ /* Found a CC header */
601                         extract_address(cc);
602                         account = account_find_from_address(cc);
603                 }        
604         }
605
606         if (!account) account = cur_account;
607         g_return_if_fail(account != NULL);
608
609         if (ignore_replyto && account->protocol == A_NNTP &&
610             !followup_and_reply_to) {
611                 reply_account =
612                         account_find_from_address(account->address);
613                 if (!reply_account)
614                         reply_account = compose_current_mail_account();
615                 if (!reply_account)
616                         return;
617         } else
618                 reply_account = account;
619         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
620         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
621
622         CHANGE_FLAGS(msginfo);
623
624         compose = compose_create(account, COMPOSE_REPLY);
625         compose->replyinfo = msginfo;
626
627
628         if (followup_and_reply_to) {
629                 gtk_widget_show(compose->to_hbox);
630                 gtk_widget_show(compose->to_entry);
631                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
632                 compose->use_to = TRUE;
633         }
634
635         if(msginfo->folder && msginfo->folder->prefs->request_return_receipt) {
636                 GtkItemFactory *ifactory;
637         
638                 ifactory = gtk_item_factory_from_widget(compose->menubar);
639                 menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
640         }
641
642         if (compose_parse_header(compose, msginfo) < 0) return;
643         compose_reply_set_entry(compose, msginfo, to_all, ignore_replyto,
644                                 followup_and_reply_to);
645
646         text = GTK_STEXT(compose->text);
647         gtk_stext_freeze(text);
648
649         if (quote) {
650                 FILE *fp;
651                 gchar *quote_str;
652
653                 if ((fp = procmime_get_text_part(msginfo)) == NULL)
654                         g_warning(_("Can't get text part\n"));
655                 else {
656                         gchar * qmark;
657
658                         if (prefs_common.quotemark && *prefs_common.quotemark)
659                                 qmark = prefs_common.quotemark;
660                         else
661                                 qmark = "> ";
662
663                         quote_str = compose_quote_fmt(compose, msginfo,
664                                                       prefs_common.quotefmt,
665                                                       qmark);
666
667                         /*
668                         quote_str = compose_quote_parse_fmt
669                                 (compose, msginfo, prefs_common.quotefmt);
670                         */
671
672                         if (quote_str != NULL)
673                                 gtk_stext_insert(text, NULL, NULL, NULL,
674                                                  quote_str, -1);
675                         /*                      g_free(quote_str); */
676                         /* compose_quote_file(compose, msginfo, fp); */
677                         fclose(fp);
678                 }
679         }
680
681         if (prefs_common.auto_sig)
682                 compose_insert_sig(compose);
683         gtk_editable_set_position(GTK_EDITABLE(text), 0);
684         gtk_stext_set_point(text, 0);
685
686         gtk_stext_thaw(text);
687         gtk_widget_grab_focus(compose->text);
688
689         if (prefs_common.auto_exteditor)
690                 compose_exec_ext_editor(compose);
691 }
692
693
694 static gchar *procmime_get_file_name(MimeInfo *mimeinfo)
695 {
696         gchar *base;
697         gchar *filename;
698
699         g_return_val_if_fail(mimeinfo != NULL, NULL);
700
701         base = mimeinfo->filename ? mimeinfo->filename
702                 : mimeinfo->name ? mimeinfo->name : NULL;
703
704         if (MIME_TEXT_HTML == mimeinfo->mime_type && base == NULL){
705                 filename = g_strdup_printf("%s%smimetmp%i.html",
706                                            get_mime_tmp_dir(),
707                                            G_DIR_SEPARATOR_S,
708                                            mimeinfo);
709                 return filename;
710         }
711         else {
712                 base = base ? base : "";
713                 base = g_basename(base);
714                 if (*base == '\0') {
715                         filename = g_strdup_printf("%s%smimetmp%i",
716                                                    get_mime_tmp_dir(),
717                                                    G_DIR_SEPARATOR_S,
718                                                    mimeinfo);
719                         return filename;
720                 }
721         }
722
723         filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
724                                base, NULL);
725
726         return filename;
727 }
728
729 static gchar * mime_extract_file(gchar * source, MimeInfo *partinfo)
730 {
731         gchar *filename;
732
733         if (!partinfo) return;
734
735         filename = procmime_get_file_name(partinfo);
736
737         if (procmime_get_part(filename, source, partinfo) < 0)
738                 alertpanel_error
739                         (_("Can't get the part of multipart message."));
740
741         return filename;
742 }
743
744 static void compose_attach_parts(Compose * compose,
745                                  MsgInfo * msginfo)
746 {
747
748         FILE *fp;
749         gchar *file;
750         MimeInfo *mimeinfo;
751         MsgInfo *tmpmsginfo;
752         gchar *p;
753         gchar *boundary;
754         gint boundary_len = 0;
755         gchar buf[BUFFSIZE];
756         glong fpos, prev_fpos;
757         gint npart;
758         gchar * source;
759         gchar * filename;
760
761         g_return_if_fail(msginfo != NULL);
762         
763 #if USE_GPGME
764         for (;;) {
765                 if ((fp = procmsg_open_message(msginfo)) == NULL) return;
766                 mimeinfo = procmime_scan_mime_header(fp);
767                 if (!mimeinfo) break;
768
769                 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
770                     rfc2015_is_encrypted(mimeinfo)) {
771                         MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
772                 }
773                 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
774                     !msginfo->plaintext_file  &&
775                     !msginfo->decryption_failed) {
776                         rfc2015_decrypt_message(msginfo, mimeinfo, fp);
777                         if (msginfo->plaintext_file &&
778                             !msginfo->decryption_failed) {
779                                 fclose(fp);
780                                 continue;
781                         }
782                 }
783                 
784                 break;
785         }
786 #else /* !USE_GPGME */
787         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
788         mimeinfo = procmime_scan_mime_header(fp);
789 #endif /* USE_GPGME */
790
791         fclose(fp);
792         if (!mimeinfo) return;
793         if (mimeinfo->mime_type == MIME_TEXT)
794                 return;
795
796         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
797
798         g_return_if_fail(mimeinfo != NULL);
799         g_return_if_fail(mimeinfo->mime_type != MIME_TEXT);
800
801         if (mimeinfo->mime_type == MIME_MULTIPART) {
802                 g_return_if_fail(mimeinfo->boundary != NULL);
803                 g_return_if_fail(mimeinfo->sub == NULL);
804         }
805         g_return_if_fail(fp != NULL);
806
807         boundary = mimeinfo->boundary;
808
809         if (boundary) {
810                 boundary_len = strlen(boundary);
811
812                 /* look for first boundary */
813                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL)
814                         if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
815                 if (!p) {
816                         fclose(fp);
817                         return;
818                 }
819         }
820
821         if ((fpos = ftell(fp)) < 0) {
822                 perror("ftell");
823                 fclose(fp);
824                 return;
825         }
826
827         for (npart = 0;; npart++) {
828                 MimeInfo *partinfo;
829                 gboolean eom = FALSE;
830
831                 prev_fpos = fpos;
832
833                 partinfo = procmime_scan_mime_header(fp);
834                 if (!partinfo) break;
835
836                 if (npart != 0)
837                         procmime_mimeinfo_insert(mimeinfo, partinfo);
838                 else
839                         procmime_mimeinfo_free(partinfo);
840
841                 /* look for next boundary */
842                 buf[0] = '\0';
843                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
844                         if (IS_BOUNDARY(buf, boundary, boundary_len)) {
845                                 if (buf[2 + boundary_len]     == '-' &&
846                                     buf[2 + boundary_len + 1] == '-')
847                                         eom = TRUE;
848                                 break;
849                         }
850                 }
851                 if (p == NULL)
852                         eom = TRUE;     /* broken MIME message */
853                 fpos = ftell(fp);
854
855                 partinfo->size = fpos - prev_fpos - strlen(buf);
856
857                 if (eom) break;
858         }
859
860         source = procmsg_get_message_file_path(msginfo);
861
862         g_return_if_fail(mimeinfo != NULL);
863
864         if (!mimeinfo->main && mimeinfo->parent)
865                 {
866                         filename = mime_extract_file(source, mimeinfo);
867
868                         compose_attach_append_with_type(compose, filename,
869                                                         mimeinfo->content_type,
870                                                         mimeinfo->mime_type);
871
872                         g_free(filename);
873                 }
874
875         if (mimeinfo->sub && mimeinfo->sub->children)
876                 {
877                         filename = mime_extract_file(source, mimeinfo->sub);
878
879                         compose_attach_append_with_type(compose, filename,
880                                                         mimeinfo->content_type,
881                                                         mimeinfo->sub->mime_type);
882
883                         g_free(filename);
884                 }
885
886         if (mimeinfo->children) {
887                 MimeInfo *child;
888
889                 child = mimeinfo->children;
890                 while (child) {
891                         filename = mime_extract_file(source, child);
892
893                         compose_attach_append_with_type(compose, filename,
894                                                         child->content_type,
895                                                         child->mime_type);
896
897                         g_free(filename);
898
899                         child = child->next;
900                 }
901         }
902
903         fclose(fp);
904
905         procmime_mimeinfo_free_all(mimeinfo);
906 }
907
908
909 #define INSERT_FW_HEADER(var, hdr) \
910 if (msginfo->var && *msginfo->var) { \
911         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
912         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
913         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
914 }
915
916 Compose * compose_forward(PrefsAccount * account, MsgInfo *msginfo,
917                           gboolean as_attach)
918 {
919         Compose *compose;
920         /*      PrefsAccount *account; */
921         GtkSText *text;
922         FILE *fp;
923         gchar buf[BUFFSIZE];
924
925         g_return_val_if_fail(msginfo != NULL, NULL);
926         g_return_val_if_fail(msginfo->folder != NULL, NULL);
927
928         account = msginfo->folder->folder->account;
929         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
930                 gchar *to;
931                 Xstrdup_a(to, msginfo->to, return);
932                 extract_address(to);
933                 account = account_find_from_address(to);
934         }
935
936         if(!account&& prefs_common.forward_account_autosel) {
937                 gchar cc[BUFFSIZE];
938                 if(!get_header_from_msginfo(msginfo,cc,sizeof(cc),"CC:")){ /* Found a CC header */
939                         extract_address(cc);
940                         account = account_find_from_address(cc);
941                 }
942         }
943
944         if (account == NULL) {
945                 account = cur_account;
946                 /*
947                 account = msginfo->folder->folder->account;
948                 if (!account) account = cur_account;
949                 */
950         }
951         g_return_val_if_fail(account != NULL, NULL);
952
953         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
954         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
955         CHANGE_FLAGS(msginfo);
956
957         compose = compose_create(account, COMPOSE_FORWARD);
958
959         if (msginfo->subject && *msginfo->subject) {
960                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Fw: ");
961                 gtk_entry_append_text(GTK_ENTRY(compose->subject_entry),
962                                       msginfo->subject);
963         }
964
965         text = GTK_STEXT(compose->text);
966         gtk_stext_freeze(text);
967
968         if (as_attach) {
969                 gchar *msgfile;
970
971                 msgfile = procmsg_get_message_file_path(msginfo);
972                 if (!is_file_exist(msgfile))
973                         g_warning(_("%s: file not exist\n"), msgfile);
974                 else
975                         compose_attach_append(compose, msgfile,
976                                               MIME_MESSAGE_RFC822);
977
978                 g_free(msgfile);
979         } else {
980                 FILE *fp;
981                 gchar *quote_str;
982
983                 if ((fp = procmime_get_text_part(msginfo)) == NULL)
984                         g_warning(_("Can't get text part\n"));
985                 else {
986                         gchar * qmark;
987
988                         if (prefs_common.fw_quotemark &&
989                             *prefs_common.fw_quotemark)
990                                 qmark = prefs_common.fw_quotemark;
991                         else
992                                 qmark = "> ";
993
994                         quote_str = compose_quote_fmt(compose, msginfo,
995                                                       prefs_common.fw_quotefmt,
996                                                       qmark);
997
998                         if (quote_str != NULL)
999                                 gtk_stext_insert(text, NULL, NULL, NULL,
1000                                                  quote_str, -1);
1001
1002                         fclose(fp);
1003                 }
1004
1005                 compose_attach_parts(compose, msginfo);
1006         }
1007
1008         if (prefs_common.auto_sig)
1009                 compose_insert_sig(compose);
1010         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1011         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1012
1013         gtk_stext_thaw(text);
1014         if (account->protocol != A_NNTP)
1015                 gtk_widget_grab_focus(compose->to_entry);
1016         else
1017                 gtk_widget_grab_focus(compose->newsgroups_entry);
1018
1019         if (prefs_common.auto_exteditor)
1020                 compose_exec_ext_editor(compose);
1021
1022         return compose;
1023 }
1024
1025 #undef INSERT_FW_HEADER
1026
1027 Compose * compose_forward_multiple(PrefsAccount * account, 
1028                           GSList *msginfo_list) 
1029 {
1030         Compose *compose;
1031         GtkSText *text;
1032         GSList *msginfo;
1033         gchar *msgfile;
1034
1035         g_return_val_if_fail(msginfo_list != NULL, NULL);
1036         
1037         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1038                 if ( ((MsgInfo *)msginfo->data)->folder == NULL )
1039                         return NULL;
1040         }
1041
1042         if (account == NULL) {
1043                 account = cur_account;
1044                 /*
1045                 account = msginfo->folder->folder->account;
1046                 if (!account) account = cur_account;
1047                 */
1048         }
1049         g_return_val_if_fail(account != NULL, NULL);
1050
1051         compose = compose_create(account, COMPOSE_FORWARD);
1052
1053         text = GTK_STEXT(compose->text);
1054         gtk_stext_freeze(text);
1055
1056         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1057                 msgfile = procmsg_get_message_file_path((MsgInfo *)msginfo->data);
1058                 if (!is_file_exist(msgfile))
1059                         g_warning(_("%s: file not exist\n"), msgfile);
1060                 else
1061                         compose_attach_append(compose, msgfile,
1062                                 MIME_MESSAGE_RFC822);
1063                 g_free(msgfile);
1064         }
1065
1066         if (prefs_common.auto_sig)
1067                 compose_insert_sig(compose);
1068         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1069         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1070
1071         gtk_stext_thaw(text);
1072         if (account->protocol != A_NNTP)
1073                 gtk_widget_grab_focus(compose->to_entry);
1074         else
1075                 gtk_widget_grab_focus(compose->newsgroups_entry);
1076
1077         return compose;
1078 }
1079
1080 void compose_reedit(MsgInfo *msginfo)
1081 {
1082         Compose *compose;
1083         PrefsAccount *account;
1084         GtkSText *text;
1085         FILE *fp;
1086         gchar buf[BUFFSIZE];
1087
1088         g_return_if_fail(msginfo != NULL);
1089         g_return_if_fail(msginfo->folder != NULL);
1090
1091         account = msginfo->folder->folder->account;
1092
1093         if(!account&& prefs_common.reedit_account_autosel) {
1094                 gchar from[BUFFSIZE];
1095                 if(!get_header_from_msginfo(msginfo,from,sizeof(from),"FROM:")){ /* Found a FROM header */
1096                         extract_address(from);
1097                         account = account_find_from_address(from);
1098                 }
1099         }
1100         if (!account) account = cur_account;
1101         g_return_if_fail(account != NULL);
1102
1103         compose = compose_create(account, COMPOSE_REEDIT);
1104         compose->targetinfo = procmsg_msginfo_copy(msginfo);
1105
1106         if (compose_parse_header(compose, msginfo) < 0) return;
1107         compose_reedit_set_entry(compose, msginfo);
1108
1109         text = GTK_STEXT(compose->text);
1110         gtk_stext_freeze(text);
1111
1112         if ((fp = procmime_get_text_part(msginfo)) == NULL)
1113                 g_warning(_("Can't get text part\n"));
1114         else {
1115                 while (fgets(buf, sizeof(buf), fp) != NULL)
1116                         gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1117                 fclose(fp);
1118         }
1119         compose_attach_parts(compose, msginfo);
1120
1121         gtk_stext_thaw(text);
1122         gtk_widget_grab_focus(compose->text);
1123
1124         if (prefs_common.auto_exteditor)
1125                 compose_exec_ext_editor(compose);
1126 }
1127
1128 GList *compose_get_compose_list(void)
1129 {
1130         return compose_list;
1131 }
1132
1133 void compose_entry_append(Compose *compose, const gchar *address,
1134                           ComposeEntryType type)
1135 {
1136         GtkEntry *entry;
1137         const gchar *text;
1138
1139         if (!address || *address == '\0') return;
1140
1141         switch (type) {
1142         case COMPOSE_CC:
1143                 entry = GTK_ENTRY(compose->cc_entry);
1144                 break;
1145         case COMPOSE_BCC:
1146                 entry = GTK_ENTRY(compose->bcc_entry);
1147                 break;
1148         case COMPOSE_NEWSGROUPS:
1149                 entry = GTK_ENTRY(compose->newsgroups_entry);
1150                 break;
1151         case COMPOSE_TO:
1152         default:
1153                 entry = GTK_ENTRY(compose->to_entry);
1154                 break;
1155         }
1156
1157         text = gtk_entry_get_text(entry);
1158         if (*text != '\0')
1159                 gtk_entry_append_text(entry, ", ");
1160         gtk_entry_append_text(entry, address);
1161 }
1162
1163 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1164 {
1165         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
1166                                        {"Cc:",          NULL, FALSE},
1167                                        {"References:",  NULL, FALSE},
1168                                        {"Bcc:",         NULL, FALSE},
1169                                        {"Newsgroups:",  NULL, FALSE},
1170                                        {"Followup-To:", NULL, FALSE},
1171                                        {NULL,           NULL, FALSE}};
1172
1173         enum
1174         {
1175                 H_REPLY_TO      = 0,
1176                 H_CC            = 1,
1177                 H_REFERENCES    = 2,
1178                 H_BCC           = 3,
1179                 H_NEWSGROUPS    = 4,
1180                 H_FOLLOWUP_TO   = 5
1181         };
1182
1183         FILE *fp;
1184
1185         g_return_val_if_fail(msginfo != NULL, -1);
1186
1187         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1188         procheader_get_header_fields(fp, hentry);
1189         fclose(fp);
1190
1191         if (hentry[H_REPLY_TO].body != NULL) {
1192                 conv_unmime_header_overwrite(hentry[H_REPLY_TO].body);
1193                 compose->replyto = hentry[H_REPLY_TO].body;
1194                 hentry[H_REPLY_TO].body = NULL;
1195         }
1196         if (hentry[H_CC].body != NULL) {
1197                 conv_unmime_header_overwrite(hentry[H_CC].body);
1198                 compose->cc = hentry[H_CC].body;
1199                 hentry[H_CC].body = NULL;
1200         }
1201         if (hentry[H_REFERENCES].body != NULL) {
1202                 if (compose->mode == COMPOSE_REEDIT)
1203                         compose->references = hentry[H_REFERENCES].body;
1204                 else {
1205                         compose->references = compose_parse_references
1206                                 (hentry[H_REFERENCES].body, msginfo->msgid);
1207                         g_free(hentry[H_REFERENCES].body);
1208                 }
1209                 hentry[H_REFERENCES].body = NULL;
1210         }
1211         if (hentry[H_BCC].body != NULL) {
1212                 if (compose->mode == COMPOSE_REEDIT) {
1213                         conv_unmime_header_overwrite(hentry[H_BCC].body);
1214                         compose->bcc = hentry[H_BCC].body;
1215                 } else
1216                         g_free(hentry[H_BCC].body);
1217                 hentry[H_BCC].body = NULL;
1218         }
1219         if (hentry[H_NEWSGROUPS].body != NULL) {
1220                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
1221                 hentry[H_NEWSGROUPS].body = NULL;
1222         }
1223         if (hentry[H_FOLLOWUP_TO].body != NULL) {
1224                 conv_unmime_header_overwrite(hentry[H_FOLLOWUP_TO].body);
1225                 compose->followup_to = hentry[H_FOLLOWUP_TO].body;
1226                 hentry[H_FOLLOWUP_TO].body = NULL;
1227         }
1228
1229         if (compose->mode == COMPOSE_REEDIT && msginfo->inreplyto)
1230                 compose->inreplyto = g_strdup(msginfo->inreplyto);
1231         else if (compose->mode != COMPOSE_REEDIT &&
1232                  msginfo->msgid && *msginfo->msgid) {
1233                 compose->inreplyto = g_strdup(msginfo->msgid);
1234
1235                 if (!compose->references) {
1236                         if (msginfo->inreplyto && *msginfo->inreplyto)
1237                                 compose->references =
1238                                         g_strdup_printf("<%s>\n\t<%s>",
1239                                                         msginfo->inreplyto,
1240                                                         msginfo->msgid);
1241                         else
1242                                 compose->references =
1243                                         g_strconcat("<", msginfo->msgid, ">",
1244                                                     NULL);
1245                 }
1246         }
1247
1248         return 0;
1249 }
1250
1251 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1252 {
1253         GSList *ref_id_list, *cur;
1254         GString *new_ref;
1255         gchar *new_ref_str;
1256
1257         ref_id_list = references_list_append(NULL, ref);
1258         if (!ref_id_list) return NULL;
1259         if (msgid && *msgid)
1260                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1261
1262         for (;;) {
1263                 gint len = 0;
1264
1265                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
1266                         /* "<" + Message-ID + ">" + CR+LF+TAB */
1267                         len += strlen((gchar *)cur->data) + 5;
1268
1269                 if (len > MAX_REFERENCES_LEN) {
1270                         /* remove second message-ID */
1271                         if (ref_id_list && ref_id_list->next &&
1272                             ref_id_list->next->next) {
1273                                 g_free(ref_id_list->next->data);
1274                                 ref_id_list = g_slist_remove
1275                                         (ref_id_list, ref_id_list->next->data);
1276                         } else {
1277                                 slist_free_strings(ref_id_list);
1278                                 g_slist_free(ref_id_list);
1279                                 return NULL;
1280                         }
1281                 } else
1282                         break;
1283         }
1284
1285         new_ref = g_string_new("");
1286         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1287                 if (new_ref->len > 0)
1288                         g_string_append(new_ref, "\n\t");
1289                 g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1290         }
1291
1292         slist_free_strings(ref_id_list);
1293         g_slist_free(ref_id_list);
1294
1295         new_ref_str = new_ref->str;
1296         g_string_free(new_ref, FALSE);
1297
1298         return new_ref_str;
1299 }
1300
1301 /*
1302 static void compose_quote_file(Compose *compose, MsgInfo *msginfo, FILE *fp)
1303 {
1304         GtkSText *text = GTK_STEXT(compose->text);
1305         gchar *qmark;
1306         gchar *quote_str;
1307         GdkColor *qcolor = NULL;
1308         gchar buf[BUFFSIZE];
1309         gint qlen;
1310         gchar *linep, *cur, *leftp;
1311         gint line_len, cur_len;
1312         gint wrap_len;
1313         gint str_len;
1314         gint ch_len;
1315
1316         // if (prefs_common.enable_color) qcolor = &quote_color;
1317         if (prefs_common.quotemark && *prefs_common.quotemark)
1318                 qmark = prefs_common.quotemark;
1319         else
1320                 qmark = "> ";
1321         quote_str = compose_quote_parse_fmt(compose, msginfo, qmark);
1322         g_return_if_fail(quote_str != NULL);
1323         qlen = strlen(quote_str);
1324
1325         if (!prefs_common.linewrap_quote ||
1326             prefs_common.linewrap_len <= qlen) {
1327                 while (fgets(buf, sizeof(buf), fp) != NULL) {
1328                         gtk_stext_insert(text, NULL, qcolor, NULL,
1329                                         quote_str, -1);
1330                         gtk_stext_insert(text, NULL, qcolor, NULL, buf, -1);
1331                 }
1332                 g_free(quote_str);
1333                 return;
1334         }
1335
1336         wrap_len = prefs_common.linewrap_len - qlen;
1337
1338         while (fgets(buf, sizeof(buf), fp) != NULL) {
1339                 strretchomp(buf);
1340                 str_len = strlen(buf);
1341
1342                 if (str_len <= wrap_len) {
1343                         gtk_stext_insert(text, NULL, qcolor, NULL,
1344                                         quote_str, -1);
1345                         gtk_stext_insert(text, NULL, qcolor, NULL, buf, -1);
1346                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
1347                         continue;
1348                 }
1349
1350                 linep = cur = leftp = buf;
1351                 line_len = cur_len = 0;
1352
1353                 while (*cur != '\0') {
1354                         ch_len = mblen(cur, MB_CUR_MAX);
1355                         if (ch_len < 0) ch_len = 1;
1356
1357                         if (ch_len == 1 && isspace(*cur)) {
1358                                 linep = cur + ch_len;
1359                                 line_len = cur_len + ch_len;
1360                         }
1361
1362                         if (cur_len + ch_len > wrap_len && line_len > 0) {
1363                                 gtk_stext_insert(text, NULL, qcolor, NULL,
1364                                                 quote_str, -1);
1365
1366                                 if (isspace(*(linep - 1)))
1367                                         gtk_stext_insert(text, NULL,
1368                                                         qcolor, NULL,
1369                                                         leftp, line_len - 1);
1370                                 else
1371                                         gtk_stext_insert(text, NULL,
1372                                                         qcolor, NULL,
1373                                                         leftp, line_len);
1374                                 gtk_stext_insert(text, NULL, NULL, NULL,
1375                                                 "\n", 1);
1376
1377                                 leftp = linep;
1378                                 cur_len = cur_len - line_len + ch_len;
1379                                 line_len = 0;
1380                                 cur += ch_len;
1381                                 continue;
1382                         }
1383
1384                         if (ch_len > 1) {
1385                                 linep = cur + ch_len;
1386                                 line_len = cur_len + ch_len;
1387                         }
1388                         cur_len += ch_len;
1389                         cur += ch_len;
1390                 }
1391
1392                 if (*leftp) {
1393                         gtk_stext_insert(text, NULL, qcolor, NULL,
1394                                         quote_str, -1);
1395                         gtk_stext_insert(text, NULL, qcolor, NULL, leftp, -1);
1396                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
1397                 }
1398         }
1399
1400         g_free(quote_str);
1401 }
1402 */
1403
1404 /*
1405 static gchar *compose_quote_parse_fmt(Compose *compose, MsgInfo *msginfo,
1406                                       const gchar *fmt)
1407 {
1408         gchar *ext_str;
1409         size_t buf_len = 1024;
1410         size_t ext_len = 0;
1411         gchar *str;
1412         gchar *mbs;
1413         wchar_t *wcsfmt;
1414         wchar_t *sp;
1415         gchar tmp[3];
1416
1417         if (!fmt || *fmt == '\0') return 0;
1418
1419         Xalloca(mbs, sizeof(wchar_t) + 1, return 0);
1420         Xalloca(wcsfmt, (strlen(fmt) + 1) * sizeof(wchar_t), return 0);
1421         mbstowcs(wcsfmt, fmt, strlen(fmt) + 1);
1422         sp = wcsfmt;
1423
1424         ext_str = g_malloc(sizeof(gchar) * buf_len);
1425         g_return_val_if_fail(ext_str != NULL, NULL);
1426
1427         while (*sp) {
1428                 gint len;
1429
1430                 len = wctomb(mbs, *sp);
1431                 mbs[len] = '\0';
1432
1433                 if (*mbs == '%') {
1434                         gchar *p;
1435
1436                         wctomb(mbs, *(++sp));
1437                         str = NULL;
1438
1439                         switch (*mbs) {
1440                         case 'd':
1441                                 str = msginfo->date;
1442                                 sp++;
1443                                 break;
1444                         case 'f':
1445                                 str = msginfo->from;
1446                                 sp++;
1447                                 break;
1448                         case 'I':
1449                                 if (!msginfo->fromname) {sp++; break;}
1450                                 p = msginfo->fromname;
1451                                 tmp[0] = tmp[1] = tmp[2] = '\0';
1452
1453                                 if (*p && isalnum(*p))
1454                                         tmp[0] = toupper(*p);
1455                                 else {
1456                                         sp++;
1457                                         break;
1458                                 }
1459
1460                                 while (*p) {
1461                                         while (*p && !isspace(*p)) p++;
1462                                         while (*p && isspace(*p)) p++;
1463                                         if (*p && isalnum(*p))
1464                                                 tmp[1] = toupper(*p);
1465                                 }
1466
1467                                 if (tmp[1]) str = tmp;
1468                                 sp++;
1469                                 break;
1470                         case 'n':
1471                                 str = msginfo->fromname;
1472                                 sp++;
1473                                 break;
1474                         case 'N':
1475                                 if (!msginfo->fromname) {sp++; break;}
1476                                 Xstrdup_a(str, msginfo->fromname,
1477                                           {sp++; break;});
1478                                 p = str;
1479                                 while (*p && !isspace(*p)) p++;
1480                                 *p = '\0';
1481                                 sp++;
1482                                 break;
1483                         case 's':
1484                                 str = msginfo->subject;
1485                                 sp++;
1486                                 break;
1487                         case 't':
1488                                 str = msginfo->to;
1489                                 sp++;
1490                                 break;
1491                         case 'c':
1492                                 str = msginfo->cc;
1493                                 sp++;
1494                                 break;
1495                         case 'i':
1496                                 if (!msginfo->msgid) {sp++; break;}
1497                                 Xalloca(str, strlen(msginfo->msgid) + 3,
1498                                         {sp++; break;});
1499                                 g_snprintf(str, strlen(msginfo->msgid) + 3,
1500                                            "<%s>", msginfo->msgid);
1501                                 sp++;
1502                                 break;
1503                         case '%':
1504                                 str = "%";
1505                                 sp++;
1506                                 break;
1507                         default:
1508                                 break;
1509                         }
1510
1511                         if (str) {
1512                                 while (ext_len + strlen(str) + 1 > buf_len)
1513                                         buf_len += 1024;
1514                                 ext_str = g_realloc(ext_str,
1515                                                     sizeof(gchar) * buf_len);
1516                                 g_return_val_if_fail(ext_str != NULL, NULL);
1517                                 strcpy(ext_str + ext_len, str);
1518                                 ext_len += strlen(str);
1519                         }
1520                 } else if (*mbs == '\\') {
1521                         wctomb(mbs, *(++sp));
1522                         str = NULL;
1523
1524                         switch (*mbs) {
1525                         case 'n':
1526                                 str = "\n";
1527                                 break;
1528                         case 't':
1529                                 str = "\t";
1530                                 break;
1531                         case '\\':
1532                                 str = "\\";
1533                                 break;
1534                         default:
1535                                 break;
1536                         }
1537
1538                         if (str) {
1539                                 while (ext_len + strlen(str) + 1 > buf_len)
1540                                         buf_len += 1024;
1541                                 ext_str = g_realloc(ext_str,
1542                                                     sizeof(gchar) * buf_len);
1543                                 g_return_val_if_fail(ext_str != NULL, NULL);
1544                                 strcpy(ext_str + ext_len, str);
1545                                 ext_len += strlen(str);
1546                                 sp++;
1547                         }
1548                 } else {
1549                         while (ext_len + len + 1 > buf_len) buf_len += 1024;
1550                         ext_str = g_realloc(ext_str, sizeof(gchar) * buf_len);
1551                         g_return_val_if_fail(ext_str != NULL, NULL);
1552                         strcpy(ext_str + ext_len, mbs);
1553                         ext_len += len;
1554                         sp++;
1555                 }
1556         }
1557
1558         if (ext_str)
1559                 ext_str = g_realloc(ext_str, strlen(ext_str) + 1);
1560
1561         return ext_str;
1562 }
1563 */
1564
1565 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1566                                     gboolean to_all, gboolean ignore_replyto,
1567                                     gboolean followup_and_reply_to)
1568 {
1569         GSList *cc_list;
1570         GSList *cur;
1571         gchar *from;
1572         GHashTable *to_table;
1573
1574         g_return_if_fail(compose->account != NULL);
1575         g_return_if_fail(msginfo != NULL);
1576
1577         if ((compose->account->protocol != A_NNTP) || followup_and_reply_to)
1578                 gtk_entry_set_text(GTK_ENTRY(compose->to_entry),
1579                                    ( (compose->replyto && !ignore_replyto) 
1580                                      ? compose->replyto
1581                                      : msginfo->from ? msginfo->from : ""));
1582         if (compose->account->protocol == A_NNTP)
1583                 gtk_entry_set_text(GTK_ENTRY(compose->newsgroups_entry),
1584                                    compose->followup_to ? compose->followup_to
1585                                    : compose->newsgroups ? compose->newsgroups
1586                                    : "");
1587
1588         if (msginfo->subject && *msginfo->subject) {
1589                 gchar *buf, *buf2, *p;
1590
1591                 buf = g_strdup(msginfo->subject);
1592                 while (!strncasecmp(buf, "Re:", 3)) {
1593                         p = buf + 3;
1594                         while (isspace(*p)) p++;
1595                         memmove(buf, p, strlen(p) + 1);
1596                 }
1597
1598                 buf2 = g_strdup_printf("Re: %s", buf);
1599                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1600                 g_free(buf2);
1601                 g_free(buf);
1602         } else
1603                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
1604
1605         if (!to_all || compose->account->protocol == A_NNTP) return;
1606
1607         from = g_strdup(compose->replyto ? compose->replyto :
1608                         msginfo->from ? msginfo->from : "");
1609         extract_address(from);
1610
1611         cc_list = address_list_append(NULL, msginfo->to);
1612         cc_list = address_list_append(cc_list, compose->cc);
1613
1614         to_table = g_hash_table_new(g_str_hash, g_str_equal);
1615         g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1616         if (compose->account)
1617                 g_hash_table_insert(to_table, compose->account->address,
1618                                     GINT_TO_POINTER(1));
1619
1620         /* remove address on To: and that of current account */
1621         for (cur = cc_list; cur != NULL; ) {
1622                 GSList *next = cur->next;
1623
1624                 if (g_hash_table_lookup(to_table, cur->data) != NULL)
1625                         cc_list = g_slist_remove(cc_list, cur->data);
1626                 else
1627                         g_hash_table_insert(to_table, cur->data, cur);
1628
1629                 cur = next;
1630         }
1631         g_hash_table_destroy(to_table);
1632         g_free(from);
1633
1634         if (cc_list) {
1635                 for (cur = cc_list; cur != NULL; cur = cur->next)
1636                         compose_entry_append(compose, (gchar *)cur->data,
1637                                              COMPOSE_CC);
1638                 slist_free_strings(cc_list);
1639                 g_slist_free(cc_list);
1640         }
1641 }
1642
1643 #define SET_ENTRY(entry, str) \
1644 { \
1645         if (str && *str) \
1646                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
1647 }
1648
1649 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1650 {
1651         g_return_if_fail(msginfo != NULL);
1652
1653         SET_ENTRY(to_entry, msginfo->to);
1654         SET_ENTRY(subject_entry, msginfo->subject);
1655         SET_ENTRY(cc_entry, compose->cc);
1656         SET_ENTRY(bcc_entry, compose->bcc);
1657         SET_ENTRY(reply_entry, compose->replyto);
1658
1659         if (compose->bcc) {
1660                 GtkItemFactory *ifactory;
1661                 GtkWidget *menuitem;
1662
1663                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1664                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Bcc");
1665                 gtk_check_menu_item_set_active
1666                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1667         }
1668         if (compose->replyto) {
1669                 GtkItemFactory *ifactory;
1670                 GtkWidget *menuitem;
1671
1672                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1673                 menuitem = gtk_item_factory_get_item
1674                         (ifactory, "/Message/Reply to");
1675                 gtk_check_menu_item_set_active
1676                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1677         }
1678 }
1679
1680 #undef SET_ENTRY
1681
1682 static void compose_exec_sig(Compose *compose, gchar *sigfile)
1683 {
1684         FILE *tmpfp;
1685         pid_t thepid;
1686         gchar *sigtext;
1687         FILE  *sigprg;
1688         gchar  *buf;
1689         size_t buf_len = 128;
1690  
1691         if (strlen(sigfile) < 2)
1692           return;
1693  
1694         sigprg = popen(sigfile+1, "r");
1695         if (sigprg) {
1696
1697                 buf = g_malloc(buf_len);
1698
1699                 if (!buf) {
1700                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1701                         "Unable to insert signature (malloc failed)\n", -1);
1702
1703                         pclose(sigprg);
1704                         return;
1705                 }
1706
1707                 while (!feof(sigprg)) {
1708                         bzero(buf, buf_len);
1709                         fread(buf, buf_len-1, 1, sigprg);
1710                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, buf, -1);
1711                 }
1712
1713                 g_free(buf);
1714                 pclose(sigprg);
1715         }
1716         else
1717         {
1718                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1719                 "Can't exec file: ", -1);
1720                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1721                 sigfile+1, -1);
1722         }
1723 }
1724
1725 static void compose_insert_sig(Compose *compose)
1726 {
1727         gchar *sigfile;
1728
1729         if (compose->account && compose->account->sig_path)
1730                 sigfile = g_strdup(compose->account->sig_path);
1731         else {
1732                 sigfile = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1733                                       DEFAULT_SIGNATURE, NULL);
1734         }
1735
1736         if (!is_file_or_fifo_exist(sigfile) & (sigfile[0] != '|')) {
1737                 g_free(sigfile);
1738                 return;
1739         }
1740
1741         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, "\n\n", 2);
1742         if (prefs_common.sig_sep) {
1743                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1744                                 prefs_common.sig_sep, -1);
1745                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1746                                 "\n", 1);
1747         }
1748
1749         if (sigfile[0] == '|')
1750         {
1751                 compose_exec_sig(compose, sigfile);
1752         }
1753         else
1754         {
1755                 compose_insert_file(compose, sigfile);
1756         }
1757         g_free(sigfile);
1758 }
1759
1760 static void compose_insert_file(Compose *compose, const gchar *file)
1761 {
1762         GtkSText *text = GTK_STEXT(compose->text);
1763         gchar buf[BUFFSIZE];
1764         FILE *fp;
1765
1766         g_return_if_fail(file != NULL);
1767
1768         if ((fp = fopen(file, "r")) == NULL) {
1769                 FILE_OP_ERROR(file, "fopen");
1770                 return;
1771         }
1772
1773         gtk_stext_freeze(text);
1774
1775         while (fgets(buf, sizeof(buf), fp) != NULL)
1776                 gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1777
1778         gtk_stext_thaw(text);
1779
1780         fclose(fp);
1781 }
1782
1783 static void compose_attach_info(Compose * compose, AttachInfo * ainfo,
1784                                 ContentType cnttype)
1785 {
1786         gchar *text[N_ATTACH_COLS];
1787         gint row;
1788
1789         text[COL_MIMETYPE] = ainfo->content_type;
1790         text[COL_SIZE] = to_human_readable(ainfo->size);
1791         text[COL_NAME] = ainfo->name;
1792
1793         row = gtk_clist_append(GTK_CLIST(compose->attach_clist), text);
1794         gtk_clist_set_row_data(GTK_CLIST(compose->attach_clist), row, ainfo);
1795
1796         if (cnttype != MIME_MESSAGE_RFC822)
1797                 compose_changed_cb(NULL, compose);
1798 }
1799
1800 static void compose_attach_append_with_type(Compose *compose,
1801                                             const gchar *file,
1802                                             const gchar *type,
1803                                             ContentType cnttype)
1804 {
1805         AttachInfo *ainfo;
1806         off_t size;
1807
1808         if (!is_file_exist(file)) {
1809                 g_warning(_("File %s doesn't exist\n"), file);
1810                 return;
1811         }
1812         if ((size = get_file_size(file)) < 0) {
1813                 g_warning(_("Can't get file size of %s\n"), file);
1814                 return;
1815         }
1816         if (size == 0) {
1817                 alertpanel_notice(_("File %s is empty\n"), file);
1818                 return;
1819         }
1820
1821         if (!compose->use_attach) {
1822                 GtkItemFactory *ifactory;
1823                 GtkWidget *menuitem;
1824
1825                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1826                 menuitem = gtk_item_factory_get_item(ifactory,
1827                                                      "/Message/Attach");
1828                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1829                                                TRUE);
1830         }
1831
1832         ainfo = g_new0(AttachInfo, 1);
1833         ainfo->file = g_strdup(file);
1834
1835         if (cnttype == MIME_MESSAGE_RFC822) {
1836                 ainfo->encoding = ENC_7BIT;
1837                 ainfo->name = g_strdup_printf(_("Message: %s"),
1838                                               g_basename(file));
1839         } else {
1840                 ainfo->encoding = ENC_BASE64;
1841                 ainfo->name = g_strdup(g_basename(file));
1842         }
1843
1844         ainfo->content_type = g_strdup(type);
1845         ainfo->size = size;
1846
1847         compose_attach_info(compose, ainfo, cnttype);
1848 }
1849
1850 static void compose_attach_append(Compose *compose, const gchar *file,
1851                                   ContentType cnttype)
1852 {
1853         AttachInfo *ainfo;
1854         gchar *text[N_ATTACH_COLS];
1855         off_t size;
1856         gint row;
1857
1858         if (!is_file_exist(file)) {
1859                 g_warning(_("File %s doesn't exist\n"), file);
1860                 return;
1861         }
1862         if ((size = get_file_size(file)) < 0) {
1863                 g_warning(_("Can't get file size of %s\n"), file);
1864                 return;
1865         }
1866         if (size == 0) {
1867                 alertpanel_notice(_("File %s is empty\n"), file);
1868                 return;
1869         }
1870
1871         if (!compose->use_attach) {
1872                 GtkItemFactory *ifactory;
1873                 GtkWidget *menuitem;
1874
1875                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1876                 menuitem = gtk_item_factory_get_item(ifactory,
1877                                                      "/Message/Attach");
1878                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1879                                                TRUE);
1880         }
1881
1882         ainfo = g_new0(AttachInfo, 1);
1883         ainfo->file = g_strdup(file);
1884
1885         if (cnttype == MIME_MESSAGE_RFC822) {
1886                 ainfo->content_type = g_strdup("message/rfc822");
1887                 ainfo->encoding = ENC_7BIT;
1888                 ainfo->name = g_strdup_printf(_("Message: %s"),
1889                                               g_basename(file));
1890         } else {
1891                 ainfo->content_type = procmime_get_mime_type(file);
1892                 if (!ainfo->content_type)
1893                         ainfo->content_type =
1894                                 g_strdup("application/octet-stream");
1895                 ainfo->encoding = ENC_BASE64;
1896                 ainfo->name = g_strdup(g_basename(file));
1897         }
1898         ainfo->size = size;
1899
1900         compose_attach_info(compose, ainfo, cnttype);
1901 }
1902
1903 static void compose_wrap_line(Compose *compose)
1904 {
1905         GtkSText *text = GTK_STEXT(compose->text);
1906         gint ch_len, last_ch_len;
1907         gchar cbuf[MB_CUR_MAX], last_ch;
1908         guint text_len;
1909         guint line_end;
1910         guint quoted;
1911         gint p_start, p_end;
1912         gint line_pos, cur_pos;
1913         gint line_len, cur_len;
1914
1915 #define GET_STEXT(pos)                                                        \
1916         if (text->use_wchar)                                                 \
1917                 ch_len = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, (pos))); \
1918         else {                                                               \
1919                 cbuf[0] = GTK_STEXT_INDEX(text, (pos));                       \
1920                 ch_len = 1;                                                  \
1921         }
1922
1923         gtk_stext_freeze(text);
1924
1925         text_len = gtk_stext_get_length(text);
1926
1927         /* check to see if the point is on the paragraph mark (empty line). */
1928         cur_pos = gtk_stext_get_point(text);
1929         GET_STEXT(cur_pos);
1930         if ((ch_len == 1 && *cbuf == '\n') || cur_pos == text_len) {
1931                 if (cur_pos == 0)
1932                         goto compose_end; /* on the paragraph mark */
1933                 GET_STEXT(cur_pos - 1);
1934                 if (ch_len == 1 && *cbuf == '\n')
1935                         goto compose_end; /* on the paragraph mark */
1936         }
1937
1938         /* find paragraph start. */
1939         line_end = quoted = 0;
1940         for (p_start = cur_pos; p_start >= 0; --p_start) {
1941                 GET_STEXT(p_start);
1942                 if (ch_len == 1 && *cbuf == '\n') {
1943                         if (quoted)
1944                                 goto compose_end; /* quoted part */
1945                         if (line_end) {
1946                                 p_start += 2;
1947                                 break;
1948                         }
1949                         line_end = 1;
1950                 } else {
1951                         if (ch_len == 1 && strchr(">:#", *cbuf))
1952                                 quoted = 1;
1953                         else if (ch_len != 1 || !isspace(*cbuf))
1954                                 quoted = 0;
1955
1956                         line_end = 0;
1957                 }
1958         }
1959         if (p_start < 0)
1960                 p_start = 0;
1961
1962         /* find paragraph end. */
1963         line_end = 0;
1964         for (p_end = cur_pos; p_end < text_len; p_end++) {
1965                 GET_STEXT(p_end);
1966                 if (ch_len == 1 && *cbuf == '\n') {
1967                         if (line_end) {
1968                                 p_end -= 1;
1969                                 break;
1970                         }
1971                         line_end = 1;
1972                 } else {
1973                         if (line_end && ch_len == 1 && strchr(">:#", *cbuf))
1974                                 goto compose_end; /* quoted part */
1975
1976                         line_end = 0;
1977                 }
1978         }
1979         if (p_end >= text_len)
1980                 p_end = text_len;
1981
1982         if (p_start >= p_end)
1983                 goto compose_end;
1984
1985         line_len = cur_len = 0;
1986         last_ch_len = 0;
1987         last_ch = '\0';
1988         line_pos = p_start;
1989         for (cur_pos = p_start; cur_pos < p_end; cur_pos++) {
1990                 guint space = 0;
1991
1992                 GET_STEXT(cur_pos);
1993
1994                 if (ch_len < 0) {
1995                         cbuf[0] = '\0';
1996                         ch_len = 1;
1997                 }
1998
1999                 if (ch_len == 1 && isspace(*cbuf))
2000                         space = 1;
2001
2002                 if (ch_len == 1 && *cbuf == '\n') {
2003                         guint replace = 0;
2004                         if (last_ch_len == 1 && !isspace(last_ch)) {
2005                                 if (cur_pos + 1 < p_end) {
2006                                         GET_STEXT(cur_pos + 1);
2007                                         if (ch_len == 1 && !isspace(*cbuf))
2008                                                 replace = 1;
2009                                 }
2010                         }
2011                         gtk_stext_set_point(text, cur_pos + 1);
2012                         gtk_stext_backward_delete(text, 1);
2013                         if (replace) {
2014                                 gtk_stext_set_point(text, cur_pos);
2015                                 gtk_stext_insert(text, NULL, NULL, NULL, " ", 1);
2016                                 space = 1;
2017                         }
2018                         else {
2019                                 p_end--;
2020                                 cur_pos--;
2021                                 continue;
2022                         }
2023                 }
2024
2025                 last_ch_len = ch_len;
2026                 last_ch = *cbuf;
2027
2028                 if (space) {
2029                         line_pos = cur_pos + 1;
2030                         line_len = cur_len + ch_len;
2031                 }
2032
2033                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2034                     line_len > 0) {
2035                         gint tlen = ch_len;
2036
2037                         GET_STEXT(line_pos - 1);
2038                         if (ch_len == 1 && isspace(*cbuf)) {
2039                                 gtk_stext_set_point(text, line_pos);
2040                                 gtk_stext_backward_delete(text, 1);
2041                                 p_end--;
2042                                 cur_pos--;
2043                                 line_pos--;
2044                                 cur_len--;
2045                                 line_len--;
2046                         }
2047                         ch_len = tlen;
2048
2049                         gtk_stext_set_point(text, line_pos);
2050                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2051                         p_end++;
2052                         cur_pos++;
2053                         line_pos++;
2054                         cur_len = cur_len - line_len + ch_len;
2055                         line_len = 0;
2056                         continue;
2057                 }
2058
2059                 if (ch_len > 1) {
2060                         line_pos = cur_pos + 1;
2061                         line_len = cur_len + ch_len;
2062                 }
2063                 cur_len += ch_len;
2064         }
2065
2066 compose_end:
2067         gtk_stext_thaw(text);
2068
2069 #undef GET_STEXT
2070 }
2071
2072 static void compose_wrap_line_all(Compose *compose)
2073 {
2074         GtkSText *text = GTK_STEXT(compose->text);
2075         guint text_len;
2076         guint line_pos = 0, cur_pos = 0;
2077         gint line_len = 0, cur_len = 0;
2078         gint ch_len;
2079         gchar cbuf[MB_CUR_MAX];
2080
2081         gtk_stext_freeze(text);
2082
2083         text_len = gtk_stext_get_length(text);
2084
2085         for (; cur_pos < text_len; cur_pos++) {
2086                 if (text->use_wchar)
2087                         ch_len = wctomb
2088                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2089                 else {
2090                         cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
2091                         ch_len = 1;
2092                 }
2093
2094                 if (ch_len == 1 && *cbuf == '\n') {
2095                         line_pos = cur_pos + 1;
2096                         line_len = cur_len = 0;
2097                         continue;
2098                 }
2099
2100                 if (ch_len < 0) {
2101                         cbuf[0] = '\0';
2102                         ch_len = 1;
2103                 }
2104
2105                 if (ch_len == 1 && isspace(*cbuf)) {
2106                         line_pos = cur_pos + 1;
2107                         line_len = cur_len + ch_len;
2108                 }
2109
2110                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2111                     line_len > 0) {
2112                         gint tlen;
2113
2114                         if (text->use_wchar)
2115                                 tlen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
2116                         else {
2117                                 cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
2118                                 tlen = 1;
2119                         }
2120                         if (tlen == 1 && isspace(*cbuf)) {
2121                                 gtk_stext_set_point(text, line_pos);
2122                                 gtk_stext_backward_delete(text, 1);
2123                                 text_len--;
2124                                 cur_pos--;
2125                                 line_pos--;
2126                                 cur_len--;
2127                                 line_len--;
2128                         }
2129
2130                         gtk_stext_set_point(text, line_pos);
2131                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2132                         text_len++;
2133                         cur_pos++;
2134                         line_pos++;
2135                         cur_len = cur_len - line_len + ch_len;
2136                         line_len = 0;
2137                         continue;
2138                 }
2139
2140                 if (ch_len > 1) {
2141                         line_pos = cur_pos + 1;
2142                         line_len = cur_len + ch_len;
2143                 }
2144                 cur_len += ch_len;
2145         }
2146
2147         gtk_stext_thaw(text);
2148 }
2149
2150 static void compose_set_title(Compose *compose)
2151 {
2152         gchar *str;
2153         gchar *edited;
2154
2155         edited = compose->modified ? _(" [Edited]") : "";
2156         if (compose->account && compose->account->address)
2157                 str = g_strdup_printf(_("%s - Compose message%s"),
2158                                       compose->account->address, edited);
2159         else
2160                 str = g_strdup_printf(_("Compose message%s"), edited);
2161         gtk_window_set_title(GTK_WINDOW(compose->window), str);
2162         g_free(str);
2163 }
2164
2165 /**
2166  * compose_current_mail_account:
2167  * 
2168  * Find a current mail account (the currently selected account, or the
2169  * default account, if a news account is currently selected).  If a
2170  * mail account cannot be found, display an error message.
2171  * 
2172  * Return value: Mail account, or NULL if not found.
2173  **/
2174 static PrefsAccount *
2175 compose_current_mail_account(void)
2176 {
2177         PrefsAccount *ac;
2178
2179         if (cur_account && cur_account->protocol != A_NNTP)
2180                 ac = cur_account;
2181         else {
2182                 ac = account_get_default();
2183                 if (!ac || ac->protocol == A_NNTP) {
2184                         alertpanel_error(_("Account for sending mail is not specified.\n"
2185                                            "Please select a mail account before sending."));
2186                         return NULL;
2187                 }
2188         }
2189         return ac;
2190 }
2191
2192 gint compose_send(Compose *compose)
2193 {
2194         gchar tmp[MAXPATHLEN + 1];
2195         gchar *to, *newsgroups;
2196         gint ok = 0;
2197         static gboolean lock = FALSE;
2198
2199         if (lock) return 1;
2200
2201         g_return_val_if_fail(compose->account != NULL, -1);
2202         g_return_val_if_fail(compose->orig_account != NULL, -1);
2203
2204         lock = TRUE;
2205
2206         to = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2207         newsgroups = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
2208         if (*to == '\0' && *newsgroups == '\0') {
2209                 alertpanel_error(_("Recipient is not specified."));
2210                 lock = FALSE;
2211                 return 1;
2212         }
2213
2214         /* write to temporary file */
2215         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
2216                    get_rc_dir(), G_DIR_SEPARATOR, (gint)compose);
2217
2218         if (prefs_common.linewrap_at_send)
2219                 compose_wrap_line_all(compose);
2220
2221         if (compose_write_to_file(compose, tmp, FALSE) < 0) {
2222                 lock = FALSE;
2223                 return -1;
2224         }
2225
2226         if (!compose->to_list && !compose->newsgroup_list) {
2227                 g_warning(_("can't get recipient list."));
2228                 unlink(tmp);
2229                 lock = FALSE;
2230                 return -1;
2231         }
2232
2233         if (compose->to_list) {
2234                 PrefsAccount *ac;
2235
2236                 /*
2237                 if (compose->account->protocol != A_NNTP)
2238                         ac = compose->account;
2239                 else if (compose->orig_account->protocol != A_NNTP)
2240                         ac = compose->orig_account;
2241                 else if (cur_account && cur_account->protocol != A_NNTP)
2242                         ac = cur_account;
2243                 else {
2244                         ac = compose_current_mail_account();
2245                         if (!ac) {
2246                                 unlink(tmp);
2247                                 lock = FALSE;
2248                                 return -1;
2249                         }
2250                 }
2251                 */
2252                 ac = compose->account;
2253
2254                 ok = send_message(tmp, ac, compose->to_list);
2255                 statusbar_pop_all();
2256         }
2257
2258         if (ok == 0 && compose->newsgroup_list) {
2259                 Folder *folder;
2260
2261                 if (compose->account->protocol == A_NNTP)
2262                         folder = FOLDER(compose->account->folder);
2263                 else
2264                         folder = FOLDER(compose->orig_account->folder);
2265
2266                 ok = news_post(folder, tmp);
2267                 if (ok < 0) {
2268                         alertpanel_error(_("Error occurred while posting the message to %s ."),
2269                                          compose->account->nntp_server);
2270                         unlink(tmp);
2271                         lock = FALSE;
2272                         return -1;
2273                 }
2274         }
2275
2276         /* queue message if failed to send */
2277         if (ok < 0) {
2278                 if (prefs_common.queue_msg) {
2279                         AlertValue val;
2280
2281                         val = alertpanel
2282                                 (_("Queueing"),
2283                                  _("Error occurred while sending the message.\n"
2284                                    "Put this message into queue folder?"),
2285                                  _("OK"), _("Cancel"), NULL);
2286                         if (G_ALERTDEFAULT == val) {
2287                                 ok = compose_queue(compose, tmp);
2288                                 if (ok < 0)
2289                                         alertpanel_error(_("Can't queue the message."));
2290                         }
2291                 } else
2292                         alertpanel_error(_("Error occurred while sending the message."));
2293         } else {
2294                 if (compose->mode == COMPOSE_REEDIT) {
2295                         compose_remove_reedit_target(compose);
2296                         if (compose->targetinfo)
2297                                 folderview_update_item
2298                                         (compose->targetinfo->folder, TRUE);
2299                 }
2300         }
2301
2302         /* save message to outbox */
2303         if (ok == 0 && prefs_common.savemsg) {
2304                 if (compose_save_to_outbox(compose, tmp) < 0)
2305                         alertpanel_error
2306                                 (_("Can't save the message to outbox."));
2307         }
2308
2309         unlink(tmp);
2310         lock = FALSE;
2311         return ok;
2312 }
2313
2314 static gint compose_write_to_file(Compose *compose, const gchar *file,
2315                                   gboolean is_draft)
2316 {
2317         FILE *fp;
2318         size_t len;
2319         gchar *chars;
2320         gchar *buf;
2321         const gchar *out_codeset;
2322         EncodingType encoding;
2323
2324         if ((fp = fopen(file, "w")) == NULL) {
2325                 FILE_OP_ERROR(file, "fopen");
2326                 return -1;
2327         }
2328
2329         /* chmod for security */
2330         if (change_file_mode_rw(fp, file) < 0) {
2331                 FILE_OP_ERROR(file, "chmod");
2332                 g_warning(_("can't change file mode\n"));
2333         }
2334
2335         /* get all composed text */
2336         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
2337         len = strlen(chars);
2338         if (is_ascii_str(chars)) {
2339                 buf = g_strdup(chars);
2340                 out_codeset = "US-ASCII";
2341                 encoding = ENC_7BIT;
2342         } else {
2343                 const gchar *src_codeset;
2344
2345                 out_codeset = conv_get_outgoing_charset_str();
2346                 if (!strcasecmp(out_codeset, "US-ASCII"))
2347                         out_codeset = "ISO-8859-1";
2348                 encoding = procmime_get_encoding_for_charset(out_codeset);
2349                 debug_print("charset = %s, encoding = %s\n",
2350                             out_codeset, procmime_get_encoding_str(encoding));
2351
2352                 src_codeset = conv_get_current_charset_str();
2353                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
2354                 if (!buf) {
2355                         g_free(chars);
2356                         fclose(fp);
2357                         unlink(file);
2358                         alertpanel_error(_("Can't convert the codeset of the message."));
2359                         return -1;
2360                 }
2361         }
2362         g_free(chars);
2363
2364         /* write headers */
2365         if (compose_write_headers
2366                 (compose, fp, out_codeset, encoding, is_draft) < 0) {
2367                 g_warning(_("can't write headers\n"));
2368                 fclose(fp);
2369                 /* unlink(file); */
2370                 g_free(buf);
2371                 return -1;
2372         }
2373
2374         if (compose->use_attach) {
2375 #if USE_GPGME
2376             /* This prolog message is ignored by mime software and
2377              * because it would make our signing/encryption task
2378              * tougher, we don't emit it in that case */
2379             if (!compose->use_signing && !compose->use_encryption)
2380 #endif
2381                 fputs("This is a multi-part message in MIME format.\n", fp);
2382
2383                 fprintf(fp, "\n--%s\n", compose->boundary);
2384                 fprintf(fp, "Content-Type: text/plain; charset=%s\n",
2385                         out_codeset);
2386                 fprintf(fp, "Content-Transfer-Encoding: %s\n",
2387                         procmime_get_encoding_str(encoding));
2388                 fputc('\n', fp);
2389         }
2390
2391         /* write body */
2392         len = strlen(buf);
2393         if (encoding == ENC_BASE64) {
2394                 gchar outbuf[B64_BUFFSIZE];
2395                 gint i, l;
2396
2397                 for (i = 0; i < len; i += B64_LINE_SIZE) {
2398                         l = MIN(B64_LINE_SIZE, len - i);
2399                         to64frombits(outbuf, buf + i, l);
2400                         fputs(outbuf, fp);
2401                         fputc('\n', fp);
2402                 }
2403         } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
2404                 FILE_OP_ERROR(file, "fwrite");
2405                 fclose(fp);
2406                 unlink(file);
2407                 g_free(buf);
2408                 return -1;
2409         }
2410         g_free(buf);
2411
2412         if (compose->use_attach)
2413                 compose_write_attach(compose, fp);
2414
2415         if (fclose(fp) == EOF) {
2416                 FILE_OP_ERROR(file, "fclose");
2417                 unlink(file);
2418                 return -1;
2419         }
2420
2421 #if USE_GPGME
2422         if (compose->use_signing) {
2423                 if (rfc2015_sign(file, compose->account) < 0) {
2424                         unlink(file);
2425                         return -1;
2426                 }
2427         }
2428         if (compose->use_encryption) {
2429                 if (rfc2015_encrypt(file, compose->to_list) < 0) {
2430                         unlink(file);
2431                         return -1;
2432                 }
2433         }
2434 #endif /* USE_GPGME */
2435
2436         return 0;
2437 }
2438
2439 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
2440 {
2441         FILE *fp;
2442         size_t len;
2443         gchar *chars;
2444
2445         if ((fp = fopen(file, "w")) == NULL) {
2446                 FILE_OP_ERROR(file, "fopen");
2447                 return -1;
2448         }
2449
2450         /* chmod for security */
2451         if (change_file_mode_rw(fp, file) < 0) {
2452                 FILE_OP_ERROR(file, "chmod");
2453                 g_warning(_("can't change file mode\n"));
2454         }
2455
2456         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
2457
2458         /* write body */
2459         len = strlen(chars);
2460         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
2461                 FILE_OP_ERROR(file, "fwrite");
2462                 g_free(chars);
2463                 fclose(fp);
2464                 unlink(file);
2465                 return -1;
2466         }
2467
2468         g_free(chars);
2469
2470         if (fclose(fp) == EOF) {
2471                 FILE_OP_ERROR(file, "fclose");
2472                 unlink(file);
2473                 return -1;
2474         }
2475         return 0;
2476 }
2477
2478 static gint compose_save_to_outbox(Compose *compose, const gchar *file)
2479 {
2480         FolderItem *outbox;
2481         gchar *path;
2482         gint num;
2483         FILE *fp;
2484
2485         debug_print(_("saving sent message...\n"));
2486
2487         outbox = folder_get_default_outbox();
2488         path = folder_item_get_path(outbox);
2489         if (!is_dir_exist(path))
2490                 make_dir_hier(path);
2491
2492         folder_item_scan(outbox);
2493         if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
2494                 g_free(path);
2495                 g_warning(_("can't save message\n"));
2496                 return -1;
2497         }
2498
2499         if ((fp = procmsg_open_mark_file(path, TRUE)) == NULL)
2500                 g_warning(_("can't open mark file\n"));
2501         else {
2502                 MsgInfo newmsginfo;
2503
2504                 newmsginfo.msgnum = num;
2505                 newmsginfo.flags.perm_flags = 0;
2506                 newmsginfo.flags.tmp_flags = 0;
2507                 procmsg_write_flags(&newmsginfo, fp);
2508                 fclose(fp);
2509         }
2510         g_free(path);
2511
2512         return 0;
2513 }
2514
2515 static gint compose_remove_reedit_target(Compose *compose)
2516 {
2517         FolderItem *item;
2518         MsgInfo *msginfo = compose->targetinfo;
2519
2520         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
2521         if (!msginfo) return -1;
2522
2523         item = msginfo->folder;
2524         g_return_val_if_fail(item != NULL, -1);
2525
2526         folder_item_scan(item);
2527         if (procmsg_msg_exist(msginfo) &&
2528             (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
2529                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
2530                         g_warning(_("can't remove the old message\n"));
2531                         return -1;
2532                 }
2533         }
2534
2535         return 0;
2536 }
2537
2538 static gint compose_queue(Compose *compose, const gchar *file)
2539 {
2540         FolderItem *queue;
2541         gchar *tmp, *queue_path;
2542         FILE *fp, *src_fp;
2543         GSList *cur;
2544         gchar buf[BUFFSIZE];
2545         gint num;
2546
2547         debug_print(_("queueing message...\n"));
2548         g_return_val_if_fail(compose->to_list != NULL, -1);
2549         g_return_val_if_fail(compose->account != NULL, -1);
2550
2551         tmp = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
2552                               G_DIR_SEPARATOR, (gint)compose);
2553         if ((fp = fopen(tmp, "w")) == NULL) {
2554                 FILE_OP_ERROR(tmp, "fopen");
2555                 g_free(tmp);
2556                 return -1;
2557         }
2558         if ((src_fp = fopen(file, "r")) == NULL) {
2559                 FILE_OP_ERROR(file, "fopen");
2560                 fclose(fp);
2561                 unlink(tmp);
2562                 g_free(tmp);
2563                 return -1;
2564         }
2565         if (change_file_mode_rw(fp, tmp) < 0) {
2566                 FILE_OP_ERROR(tmp, "chmod");
2567                 g_warning(_("can't change file mode\n"));
2568         }
2569
2570         /* queueing variables */
2571         fprintf(fp, "AF:\n");
2572         fprintf(fp, "NF:0\n");
2573         fprintf(fp, "PS:10\n");
2574         fprintf(fp, "SRH:1\n");
2575         fprintf(fp, "SFN:\n");
2576         fprintf(fp, "DSR:\n");
2577         if (compose->msgid)
2578                 fprintf(fp, "MID:<%s>\n", compose->msgid);
2579         else
2580                 fprintf(fp, "MID:\n");
2581         fprintf(fp, "CFG:\n");
2582         fprintf(fp, "PT:0\n");
2583         fprintf(fp, "S:%s\n", compose->account->address);
2584         fprintf(fp, "RQ:\n");
2585         if (compose->account->smtp_server)
2586                 fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
2587         else
2588                 fprintf(fp, "SSV:\n");
2589         if (compose->account->nntp_server)
2590                 fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
2591         else
2592                 fprintf(fp, "NSV:\n");
2593         fprintf(fp, "SSH:\n");
2594         fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
2595         for (cur = compose->to_list->next; cur != NULL; cur = cur->next)
2596                 fprintf(fp, ",<%s>", (gchar *)cur->data);
2597         fprintf(fp, "\n");
2598         /* Sylpheed account ID */
2599         fprintf(fp, "AID:%d\n", compose->account->account_id);
2600         fprintf(fp, "\n");
2601
2602         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2603                 if (fputs(buf, fp) == EOF) {
2604                         FILE_OP_ERROR(tmp, "fputs");
2605                         fclose(fp);
2606                         fclose(src_fp);
2607                         unlink(tmp);
2608                         g_free(tmp);
2609                         return -1;
2610                 }
2611         }
2612
2613         fclose(src_fp);
2614         if (fclose(fp) == EOF) {
2615                 FILE_OP_ERROR(tmp, "fclose");
2616                 unlink(tmp);
2617                 g_free(tmp);
2618                 return -1;
2619         }
2620
2621         queue = folder_get_default_queue();
2622
2623         folder_item_scan(queue);
2624         queue_path = folder_item_get_path(queue);
2625         if (!is_dir_exist(queue_path))
2626                 make_dir_hier(queue_path);
2627         if ((num = folder_item_add_msg(queue, tmp, TRUE)) < 0) {
2628                 g_warning(_("can't queue the message\n"));
2629                 unlink(tmp);
2630                 g_free(tmp);
2631                 g_free(queue_path);
2632                 return -1;
2633         }
2634         g_free(tmp);
2635
2636         if (compose->mode == COMPOSE_REEDIT) {
2637                 compose_remove_reedit_target(compose);
2638                 if (compose->targetinfo &&
2639                     compose->targetinfo->folder != queue)
2640                         folderview_update_item
2641                                 (compose->targetinfo->folder, TRUE);
2642         }
2643
2644         if ((fp = procmsg_open_mark_file(queue_path, TRUE)) == NULL)
2645                 g_warning(_("can't open mark file\n"));
2646         else {
2647                 MsgInfo newmsginfo;
2648
2649                 newmsginfo.msgnum = num;
2650                 newmsginfo.flags.perm_flags = 0;
2651                 newmsginfo.flags.tmp_flags = 0;
2652                 procmsg_write_flags(&newmsginfo, fp);
2653                 fclose(fp);
2654         }
2655         g_free(queue_path);
2656
2657         folder_item_scan(queue);
2658         folderview_update_item(queue, TRUE);
2659
2660         return 0;
2661 }
2662
2663 static void compose_write_attach(Compose *compose, FILE *fp)
2664 {
2665         AttachInfo *ainfo;
2666         GtkCList *clist = GTK_CLIST(compose->attach_clist);
2667         gint row;
2668         FILE *attach_fp;
2669         gchar filename[BUFFSIZE];
2670         gint len;
2671
2672         for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
2673              row++) {
2674                 if ((attach_fp = fopen(ainfo->file, "r")) == NULL) {
2675                         g_warning(_("Can't open file %s\n"), ainfo->file);
2676                         continue;
2677                 }
2678
2679                 fprintf(fp, "\n--%s\n", compose->boundary);
2680
2681                 if (!strcmp2(ainfo->content_type, "message/rfc822")) {
2682                         fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
2683                         fprintf(fp, "Content-Disposition: inline\n");
2684                 } else {
2685                         conv_encode_header(filename, sizeof(filename),
2686                                            ainfo->name, 12);
2687                         fprintf(fp, "Content-Type: %s;\n"
2688                                     " name=\"%s\"\n",
2689                                 ainfo->content_type, filename);
2690                         fprintf(fp, "Content-Disposition: attachment;\n"
2691                                     " filename=\"%s\"\n", filename);
2692                 }
2693
2694                 fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
2695                         procmime_get_encoding_str(ainfo->encoding));
2696
2697                 if (ainfo->encoding == ENC_7BIT) {
2698                         gchar buf[BUFFSIZE];
2699
2700                         while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
2701                                 len = strlen(buf);
2702                                 if (len > 1 && buf[len - 1] == '\n' &&
2703                                     buf[len - 2] == '\r') {
2704                                         buf[len - 2] = '\n';
2705                                         buf[len - 1] = '\0';
2706                                 }
2707                                 fputs(buf, fp);
2708                         }
2709                 } else {
2710                         gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
2711
2712                         while ((len = fread(inbuf, sizeof(gchar),
2713                                             B64_LINE_SIZE, attach_fp))
2714                                == B64_LINE_SIZE) {
2715                                 to64frombits(outbuf, inbuf, B64_LINE_SIZE);
2716                                 fputs(outbuf, fp);
2717                                 fputc('\n', fp);
2718                         }
2719                         if (len > 0 && feof(attach_fp)) {
2720                                 to64frombits(outbuf, inbuf, len);
2721                                 fputs(outbuf, fp);
2722                                 fputc('\n', fp);
2723                         }
2724                 }
2725
2726                 fclose(attach_fp);
2727         }
2728
2729         fprintf(fp, "\n--%s--\n", compose->boundary);
2730 }
2731
2732 #define IS_IN_CUSTOM_HEADER(header) \
2733         (compose->account->add_customhdr && \
2734          custom_header_find(compose->account->customhdr_list, header) != NULL)
2735
2736 static gint compose_write_headers(Compose *compose, FILE *fp,
2737                                   const gchar *charset, EncodingType encoding,
2738                                   gboolean is_draft)
2739 {
2740         gchar buf[BUFFSIZE];
2741         gchar *str;
2742         /* struct utsname utsbuf; */
2743
2744         g_return_val_if_fail(fp != NULL, -1);
2745         g_return_val_if_fail(charset != NULL, -1);
2746         g_return_val_if_fail(compose->account != NULL, -1);
2747         g_return_val_if_fail(compose->account->address != NULL, -1);
2748
2749         /* Date */
2750         if (compose->account->add_date) {
2751                 get_rfc822_date(buf, sizeof(buf));
2752                 fprintf(fp, "Date: %s\n", buf);
2753         }
2754
2755         /* From */
2756         if (!IS_IN_CUSTOM_HEADER("From")) {
2757                 if (compose->account->name && *compose->account->name) {
2758                         compose_convert_header
2759                                 (buf, sizeof(buf), compose->account->name,
2760                                  strlen("From: "));
2761                         fprintf(fp, "From: %s <%s>\n",
2762                                 buf, compose->account->address);
2763                 } else
2764                         fprintf(fp, "From: %s\n", compose->account->address);
2765         }
2766         
2767         slist_free_strings(compose->to_list);
2768         g_slist_free(compose->to_list);
2769         compose->to_list = NULL;
2770
2771         /* To */
2772         if (compose->use_to) {
2773                 str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2774                 if (*str != '\0') {
2775                         Xstrdup_a(str, str, return -1);
2776                         g_strstrip(str);
2777                         if (*str != '\0') {
2778                                 compose->to_list = address_list_append
2779                                         (compose->to_list, str);
2780                                 if (!IS_IN_CUSTOM_HEADER("To")) {
2781                                         compose_convert_header
2782                                                 (buf, sizeof(buf), str,
2783                                                  strlen("To: "));
2784                                         fprintf(fp, "To: %s\n", buf);
2785                                 }
2786                         }
2787                 }
2788         }
2789
2790         slist_free_strings(compose->newsgroup_list);
2791         g_slist_free(compose->newsgroup_list);
2792         compose->newsgroup_list = NULL;
2793
2794         /* Newsgroups */
2795         str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
2796         if (*str != '\0') {
2797                 Xstrdup_a(str, str, return -1);
2798                 g_strstrip(str);
2799                 remove_space(str);
2800                 if (*str != '\0') {
2801                         compose->newsgroup_list =
2802                                 newsgroup_list_append(compose->newsgroup_list,
2803                                                       str);
2804                         if (!IS_IN_CUSTOM_HEADER("Newsgroups")) {
2805                                 compose_convert_header(buf, sizeof(buf), str,
2806                                                        strlen("Newsgroups: "));
2807                                 fprintf(fp, "Newsgroups: %s\n", buf);
2808                         }
2809                 }
2810         }
2811
2812         if (!is_draft && !compose->to_list && !compose->newsgroup_list)
2813                 return -1;
2814
2815         /* Cc */
2816         if (compose->use_cc) {
2817                 str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
2818                 if (*str != '\0') {
2819                         Xstrdup_a(str, str, return -1);
2820                         g_strstrip(str);
2821                         if (*str != '\0') {
2822                                 compose->to_list = address_list_append
2823                                         (compose->to_list, str);
2824                                 if (!IS_IN_CUSTOM_HEADER("Cc")) {
2825                                         compose_convert_header
2826                                                 (buf, sizeof(buf), str,
2827                                                  strlen("Cc: "));
2828                                         fprintf(fp, "Cc: %s\n", buf);
2829                                 }
2830                         }
2831                 }
2832         }
2833
2834         /* Bcc */
2835         if (compose->use_bcc) {
2836                 str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
2837                 if (*str != '\0') {
2838                         Xstrdup_a(str, str, return -1);
2839                         g_strstrip(str);
2840                         if (*str != '\0') {
2841                                 compose->to_list = address_list_append
2842                                         (compose->to_list, str);
2843                                 compose_convert_header(buf, sizeof(buf), str,
2844                                                        strlen("Bcc: "));
2845                                 fprintf(fp, "Bcc: %s\n", buf);
2846                         }
2847                 }
2848         }
2849
2850         /* Subject */
2851         str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2852         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
2853                 Xstrdup_a(str, str, return -1);
2854                 g_strstrip(str);
2855                 if (*str != '\0') {
2856                         compose_convert_header(buf, sizeof(buf), str,
2857                                                strlen("Subject: "));
2858                         fprintf(fp, "Subject: %s\n", buf);
2859                 }
2860         }
2861
2862         /* Message-ID */
2863         if (compose->account->gen_msgid) {
2864                 compose_generate_msgid(compose, buf, sizeof(buf));
2865                 fprintf(fp, "Message-Id: <%s>\n", buf);
2866                 compose->msgid = g_strdup(buf);
2867         }
2868
2869         /* In-Reply-To */
2870         if (compose->inreplyto && compose->to_list)
2871                 fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
2872
2873         /* References */
2874         if (compose->references)
2875                 fprintf(fp, "References: %s\n", compose->references);
2876
2877         /* Followup-To */
2878         if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
2879                 str = gtk_entry_get_text(GTK_ENTRY(compose->followup_entry));
2880                 if (*str != '\0') {
2881                         Xstrdup_a(str, str, return -1);
2882                         g_strstrip(str);
2883                         remove_space(str);
2884                         if (*str != '\0') {
2885                                 compose_convert_header(buf, sizeof(buf), str,
2886                                                        strlen("Followup-To: "));
2887                                 fprintf(fp, "Followup-To: %s\n", buf);
2888                         }
2889                 }
2890         }
2891
2892         /* Reply-To */
2893         if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
2894                 str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
2895                 if (*str != '\0') {
2896                         Xstrdup_a(str, str, return -1);
2897                         g_strstrip(str);
2898                         if (*str != '\0') {
2899                                 compose_convert_header(buf, sizeof(buf), str,
2900                                                        strlen("Reply-To: "));
2901                                 fprintf(fp, "Reply-To: %s\n", buf);
2902                         }
2903                 }
2904         }
2905
2906         /* Organization */
2907         if (compose->account->organization &&
2908             !IS_IN_CUSTOM_HEADER("Organization")) {
2909                 compose_convert_header(buf, sizeof(buf),
2910                                        compose->account->organization,
2911                                        strlen("Organization: "));
2912                 fprintf(fp, "Organization: %s\n", buf);
2913         }
2914
2915         /* Program version and system info */
2916         /* uname(&utsbuf); */
2917         str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
2918         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
2919                 fprintf(fp, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
2920                         prog_version,
2921                         gtk_major_version, gtk_minor_version, gtk_micro_version,
2922                         HOST_ALIAS);
2923                         /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
2924         }
2925         str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
2926         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
2927                 fprintf(fp, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
2928                         prog_version,
2929                         gtk_major_version, gtk_minor_version, gtk_micro_version,
2930                         HOST_ALIAS);
2931                         /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
2932         }
2933
2934         /* custom headers */
2935         if (compose->account->add_customhdr) {
2936                 GSList *cur;
2937
2938                 for (cur = compose->account->customhdr_list; cur != NULL;
2939                      cur = cur->next) {
2940                         CustomHeader *chdr = (CustomHeader *)cur->data;
2941
2942                         if (strcasecmp(chdr->name, "Date")         != 0 &&
2943                             strcasecmp(chdr->name, "From")         != 0 &&
2944                             strcasecmp(chdr->name, "To")           != 0 &&
2945                             strcasecmp(chdr->name, "Sender")       != 0 &&
2946                             strcasecmp(chdr->name, "Message-Id")   != 0 &&
2947                             strcasecmp(chdr->name, "In-Reply-To")  != 0 &&
2948                             strcasecmp(chdr->name, "References")   != 0 &&
2949                             strcasecmp(chdr->name, "Mime-Version") != 0 &&
2950                             strcasecmp(chdr->name, "Content-Type") != 0 &&
2951                             strcasecmp(chdr->name, "Content-Transfer-Encoding")
2952                             != 0)
2953                                 compose_convert_header
2954                                         (buf, sizeof(buf),
2955                                          chdr->value ? chdr->value : "",
2956                                          strlen(chdr->name) + 2);
2957                                 fprintf(fp, "%s: %s\n", chdr->name, buf);
2958                 }
2959         }
2960
2961         /* MIME */
2962         fprintf(fp, "Mime-Version: 1.0\n");
2963         if (compose->use_attach) {
2964                 get_rfc822_date(buf, sizeof(buf));
2965                 subst_char(buf, ' ', '_');
2966                 subst_char(buf, ',', '_');
2967                 compose->boundary = g_strdup_printf("Multipart_%s_%08x",
2968                                                     buf, (guint)compose);
2969                 fprintf(fp,
2970                         "Content-Type: multipart/mixed;\n"
2971                         " boundary=\"%s\"\n", compose->boundary);
2972         } else {
2973                 fprintf(fp, "Content-Type: text/plain; charset=%s\n", charset);
2974                 fprintf(fp, "Content-Transfer-Encoding: %s\n",
2975                         procmime_get_encoding_str(encoding));
2976         }
2977
2978         /* Request Return Receipt */
2979         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
2980                 if (compose->return_receipt) {
2981                         if (compose->account->name
2982                             && *compose->account->name) {
2983                                 compose_convert_header(buf, sizeof(buf), compose->account->name, strlen("Disposition-Notification-To: "));
2984                                 fprintf(fp, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
2985                         } else
2986                                 fprintf(fp, "Disposition-Notification-To: %s\n", compose->account->address);
2987                 }
2988         }
2989
2990         /* separator between header and body */
2991         fputs("\n", fp);
2992
2993         return 0;
2994 }
2995
2996 #undef IS_IN_CUSTOM_HEADER
2997
2998 static void compose_convert_header(gchar *dest, gint len, gchar *src,
2999                                    gint header_len)
3000 {
3001         g_return_if_fail(src != NULL);
3002         g_return_if_fail(dest != NULL);
3003
3004         if (len < 1) return;
3005
3006         remove_return(src);
3007
3008         if (is_ascii_str(src)) {
3009                 strncpy2(dest, src, len);
3010                 dest[len - 1] = '\0';
3011                 return;
3012         } else
3013                 conv_encode_header(dest, len, src, header_len);
3014 }
3015
3016 static void compose_generate_msgid(Compose *compose, gchar *buf, gint len)
3017 {
3018         struct tm *lt;
3019         time_t t;
3020         gchar *addr;
3021
3022         t = time(NULL);
3023         lt = localtime(&t);
3024
3025         if (compose->account && compose->account->address &&
3026             *compose->account->address) {
3027                 if (strchr(compose->account->address, '@'))
3028                         addr = g_strdup(compose->account->address);
3029                 else
3030                         addr = g_strconcat(compose->account->address, "@",
3031                                            get_domain_name(), NULL);
3032         } else
3033                 addr = g_strconcat(g_get_user_name(), "@", get_domain_name(),
3034                                    NULL);
3035
3036         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x.%s",
3037                    lt->tm_year + 1900, lt->tm_mon + 1,
3038                    lt->tm_mday, lt->tm_hour,
3039                    lt->tm_min, lt->tm_sec,
3040                    (guint)random(), addr);
3041
3042         debug_print(_("generated Message-ID: %s\n"), buf);
3043
3044         g_free(addr);
3045 }
3046
3047 static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox,
3048                                     GtkWidget **entry, gint *count,
3049                                     const gchar *label_str,
3050                                     gboolean is_addr_entry)
3051 {
3052         GtkWidget *label;
3053
3054         if (GTK_TABLE(table)->nrows < (*count) + 1)
3055                 gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2);
3056
3057         *hbox = gtk_hbox_new(FALSE, 0);
3058         label = gtk_label_new
3059                 (prefs_common.trans_hdr ? gettext(label_str) : label_str);
3060         gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0);
3061         gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1,
3062                          GTK_FILL, 0, 2, 0);
3063         *entry = gtk_entry_new();
3064         gtk_table_attach_defaults
3065                 (GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1);
3066         if (GTK_TABLE(table)->nrows > (*count) + 1)
3067                 gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4);
3068
3069         if (is_addr_entry)
3070                 address_completion_register_entry(GTK_ENTRY(*entry));
3071
3072         (*count)++;
3073 }
3074
3075 static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
3076 {
3077         Compose   *compose;
3078         GtkWidget *window;
3079         GtkWidget *vbox;
3080         GtkWidget *menubar;
3081         GtkWidget *handlebox;
3082
3083         GtkWidget *vbox2;
3084
3085         GtkWidget *table_vbox;
3086         GtkWidget *label;
3087         GtkWidget *from_optmenu_hbox;
3088         GtkWidget *to_entry;
3089         GtkWidget *to_hbox;
3090         GtkWidget *newsgroups_entry;
3091         GtkWidget *newsgroups_hbox;
3092         GtkWidget *subject_entry;
3093         GtkWidget *cc_entry;
3094         GtkWidget *cc_hbox;
3095         GtkWidget *bcc_entry;
3096         GtkWidget *bcc_hbox;
3097         GtkWidget *reply_entry;
3098         GtkWidget *reply_hbox;
3099         GtkWidget *followup_entry;
3100         GtkWidget *followup_hbox;
3101
3102         GtkWidget *paned;
3103
3104         GtkWidget *attach_scrwin;
3105         GtkWidget *attach_clist;
3106
3107         GtkWidget *edit_vbox;
3108         GtkWidget *ruler_hbox;
3109         GtkWidget *ruler;
3110         GtkWidget *scrolledwin;
3111         GtkWidget *text;
3112
3113         GtkWidget *table;
3114         GtkWidget *hbox;
3115
3116         gchar *titles[] = {_("MIME type"), _("Size"), _("Name")};
3117         guint n_menu_entries;
3118         GtkStyle  *style, *new_style;
3119         GdkColormap *cmap;
3120         GdkColor color[1];
3121         gboolean success[1];
3122         GdkFont   *font;
3123         GtkWidget *popupmenu;
3124         GtkWidget *menuitem;
3125         GtkItemFactory *popupfactory;
3126         GtkItemFactory *ifactory;
3127         gint n_entries;
3128         gint count = 0;
3129         gint i;
3130
3131         g_return_val_if_fail(account != NULL, NULL);
3132
3133         debug_print(_("Creating compose window...\n"));
3134         compose = g_new0(Compose, 1);
3135
3136         compose->account = account;
3137         compose->orig_account = account;
3138
3139         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3140         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
3141         gtk_widget_set_usize(window, -1, prefs_common.compose_height);
3142         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
3143                            GTK_SIGNAL_FUNC(compose_delete_cb), compose);
3144         gtk_signal_connect(GTK_OBJECT(window), "destroy",
3145                            GTK_SIGNAL_FUNC(compose_destroy_cb), compose);
3146         gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
3147                            GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
3148         gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
3149                            GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
3150         gtk_widget_realize(window);
3151
3152         gtkut_widget_set_composer_icon(window);
3153
3154         vbox = gtk_vbox_new(FALSE, 0);
3155         gtk_container_add(GTK_CONTAINER(window), vbox);
3156
3157         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
3158         menubar = menubar_create(window, compose_entries,
3159                                  n_menu_entries, "<Compose>", compose);
3160         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
3161
3162         handlebox = gtk_handle_box_new();
3163         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
3164
3165         compose_toolbar_create(compose, handlebox);
3166
3167         vbox2 = gtk_vbox_new(FALSE, 2);
3168         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
3169         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
3170
3171         table_vbox = gtk_vbox_new(FALSE, 0);
3172         gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0);
3173         gtk_container_set_border_width(GTK_CONTAINER(table_vbox),
3174                                        BORDER_WIDTH * 2);
3175
3176         table = gtk_table_new(8, 2, FALSE);
3177         gtk_box_pack_start(GTK_BOX(table_vbox), table, FALSE, TRUE, 0);
3178
3179         /* option menu for selecting accounts */
3180         hbox = gtk_hbox_new(FALSE, 0);
3181         label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
3182         gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3183         gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, count, count + 1,
3184                          GTK_FILL, 0, 2, 0);
3185         from_optmenu_hbox = compose_account_option_menu_create(compose);
3186         gtk_table_attach_defaults(GTK_TABLE(table), from_optmenu_hbox,
3187                                   1, 2, count, count + 1);
3188         gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
3189         count++;
3190
3191         /* header labels and entries */
3192         compose_add_entry_field(table, &to_hbox, &to_entry, &count,
3193                                 "To:", TRUE); 
3194         gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
3195         compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry,
3196                                 &count, "Newsgroups:", FALSE);
3197         gtk_table_set_row_spacing(GTK_TABLE(table), 1, 4);
3198         compose_add_entry_field(table, &hbox, &subject_entry, &count,
3199                                 "Subject:", FALSE);
3200         gtk_table_set_row_spacing(GTK_TABLE(table), 2, 4);
3201         compose_add_entry_field(table, &cc_hbox, &cc_entry, &count,
3202                                 "Cc:", TRUE);
3203         gtk_table_set_row_spacing(GTK_TABLE(table), 3, 4);
3204         compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count,
3205                                 "Bcc:", TRUE);
3206         gtk_table_set_row_spacing(GTK_TABLE(table), 4, 4);
3207         compose_add_entry_field(table, &reply_hbox, &reply_entry, &count,
3208                                 "Reply-To:", TRUE);
3209         gtk_table_set_row_spacing(GTK_TABLE(table), 5, 4);
3210         compose_add_entry_field(table, &followup_hbox, &followup_entry, &count,
3211                                 "Followup-To:", FALSE);
3212         gtk_table_set_row_spacing(GTK_TABLE(table), 6, 4);
3213
3214         gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3215
3216         gtk_signal_connect(GTK_OBJECT(to_entry), "activate",
3217                            GTK_SIGNAL_FUNC(to_activated), compose);
3218         gtk_signal_connect(GTK_OBJECT(newsgroups_entry), "activate",
3219                            GTK_SIGNAL_FUNC(newsgroups_activated), compose);
3220         gtk_signal_connect(GTK_OBJECT(subject_entry), "activate",
3221                            GTK_SIGNAL_FUNC(subject_activated), compose);
3222         gtk_signal_connect(GTK_OBJECT(cc_entry), "activate",
3223                            GTK_SIGNAL_FUNC(cc_activated), compose);
3224         gtk_signal_connect(GTK_OBJECT(bcc_entry), "activate",
3225                            GTK_SIGNAL_FUNC(bcc_activated), compose);
3226         gtk_signal_connect(GTK_OBJECT(reply_entry), "activate",
3227                            GTK_SIGNAL_FUNC(replyto_activated), compose);
3228         gtk_signal_connect(GTK_OBJECT(followup_entry), "activate",
3229                            GTK_SIGNAL_FUNC(followupto_activated), compose);
3230
3231         gtk_signal_connect(GTK_OBJECT(subject_entry), "grab_focus",
3232                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3233         gtk_signal_connect(GTK_OBJECT(to_entry), "grab_focus",
3234                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3235         gtk_signal_connect(GTK_OBJECT(newsgroups_entry), "grab_focus",
3236                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3237         gtk_signal_connect(GTK_OBJECT(cc_entry), "grab_focus",
3238                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3239         gtk_signal_connect(GTK_OBJECT(bcc_entry), "grab_focus",
3240                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3241         gtk_signal_connect(GTK_OBJECT(reply_entry), "grab_focus",
3242                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3243         gtk_signal_connect(GTK_OBJECT(followup_entry), "grab_focus",
3244                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3245
3246         /* attachment list */
3247         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
3248         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
3249                                        GTK_POLICY_AUTOMATIC,
3250                                        GTK_POLICY_ALWAYS);
3251         gtk_widget_set_usize(attach_scrwin, -1, 80);
3252
3253         attach_clist = gtk_clist_new_with_titles(N_ATTACH_COLS, titles);
3254         gtk_clist_set_column_justification(GTK_CLIST(attach_clist), COL_SIZE,
3255                                            GTK_JUSTIFY_RIGHT);
3256         gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_MIMETYPE, 240);
3257         gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_SIZE, 64);
3258         gtk_clist_set_selection_mode(GTK_CLIST(attach_clist),
3259                                      GTK_SELECTION_EXTENDED);
3260         for (i = 0; i < N_ATTACH_COLS; i++)
3261                 GTK_WIDGET_UNSET_FLAGS
3262                         (GTK_CLIST(attach_clist)->column[i].button,
3263                          GTK_CAN_FOCUS);
3264         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
3265
3266         gtk_signal_connect(GTK_OBJECT(attach_clist), "select_row",
3267                            GTK_SIGNAL_FUNC(attach_selected), compose);
3268         gtk_signal_connect(GTK_OBJECT(attach_clist), "button_press_event",
3269                            GTK_SIGNAL_FUNC(attach_button_pressed), compose);
3270         gtk_signal_connect(GTK_OBJECT(attach_clist), "key_press_event",
3271                            GTK_SIGNAL_FUNC(attach_key_pressed), compose);
3272
3273         /* drag and drop */
3274         gtk_drag_dest_set(attach_clist,
3275                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
3276                           GDK_ACTION_COPY);
3277         gtk_signal_connect(GTK_OBJECT(attach_clist), "drag_data_received",
3278                            GTK_SIGNAL_FUNC(compose_attach_drag_received_cb),
3279                            compose);
3280
3281         /* pane between attach clist and text */
3282         paned = gtk_vpaned_new();
3283         gtk_paned_add1(GTK_PANED(paned), attach_scrwin);
3284         gtk_widget_ref(paned);
3285         gtk_widget_show_all(paned);
3286
3287         edit_vbox = gtk_vbox_new(FALSE, 0);
3288         gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0);
3289
3290         /* ruler */
3291         ruler_hbox = gtk_hbox_new(FALSE, 0);
3292         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
3293
3294         ruler = gtk_shruler_new();
3295         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
3296         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
3297                            BORDER_WIDTH + 1);
3298         gtk_widget_set_usize(ruler_hbox, 1, -1);
3299
3300         /* text widget */
3301         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
3302         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
3303                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3304         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
3305         gtk_widget_set_usize(scrolledwin, prefs_common.compose_width, -1);
3306
3307         text = gtk_stext_new(gtk_scrolled_window_get_hadjustment
3308                             (GTK_SCROLLED_WINDOW(scrolledwin)),
3309                             gtk_scrolled_window_get_vadjustment
3310                             (GTK_SCROLLED_WINDOW(scrolledwin)));
3311         GTK_STEXT(text)->default_tab_width = 8;
3312         gtk_stext_set_editable(GTK_STEXT(text), TRUE);
3313         if (prefs_common.smart_wrapping) {      
3314                 gtk_stext_set_word_wrap(GTK_STEXT(text), TRUE);
3315                 gtk_stext_set_wrap_rmargin(GTK_STEXT(text), prefs_common.linewrap_len);
3316         }               
3317
3318         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
3319
3320         gtk_signal_connect(GTK_OBJECT(text), "changed",
3321                            GTK_SIGNAL_FUNC(compose_changed_cb), compose);
3322         gtk_signal_connect(GTK_OBJECT(text), "grab_focus",
3323                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
3324         gtk_signal_connect_after(GTK_OBJECT(text), "button_press_event",
3325                                  GTK_SIGNAL_FUNC(compose_button_press_cb),
3326                                  compose);
3327 #if 0
3328         gtk_signal_connect_after(GTK_OBJECT(text), "key_press_event",
3329                                  GTK_SIGNAL_FUNC(compose_key_press_cb),
3330                                  compose);
3331 #endif
3332         gtk_signal_connect_after(GTK_OBJECT(text), "size_allocate",
3333                                  GTK_SIGNAL_FUNC(compose_edit_size_alloc),
3334                                  ruler);
3335
3336         /* drag and drop */
3337         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
3338                           GDK_ACTION_COPY);
3339         gtk_signal_connect(GTK_OBJECT(text), "drag_data_received",
3340                            GTK_SIGNAL_FUNC(compose_insert_drag_received_cb),
3341                            compose);
3342
3343         gtk_widget_show_all(vbox);
3344
3345         style = gtk_widget_get_style(text);
3346
3347         /* workaround for the slow down of GtkText when using Pixmap theme */
3348         if (style->engine) {
3349                 GtkThemeEngine *engine;
3350
3351                 engine = style->engine;
3352                 style->engine = NULL;
3353                 new_style = gtk_style_copy(style);
3354                 style->engine = engine;
3355         } else
3356                 new_style = gtk_style_copy(style);
3357
3358         if (prefs_common.textfont) {
3359                 CharSet charset;
3360
3361                 charset = conv_get_current_charset();
3362                 if (MB_CUR_MAX == 1) {
3363                         gchar *fontstr, *p;
3364
3365                         Xstrdup_a(fontstr, prefs_common.textfont, );
3366                         if (fontstr && (p = strchr(fontstr, ',')) != NULL)
3367                                 *p = '\0';
3368                         font = gdk_font_load(fontstr);
3369                 } else
3370                         font = gdk_fontset_load(prefs_common.textfont);
3371                 if (font) {
3372                         gdk_font_unref(new_style->font);
3373                         new_style->font = font;
3374                 }
3375         }
3376
3377         gtk_widget_set_style(text, new_style);
3378
3379         color[0] = quote_color;
3380         cmap = gdk_window_get_colormap(window->window);
3381         gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
3382         if (success[0] == FALSE) {
3383                 g_warning("Compose: color allocation failed.\n");
3384                 style = gtk_widget_get_style(text);
3385                 quote_color = style->black;
3386         }
3387
3388         n_entries = sizeof(compose_popup_entries) /
3389                 sizeof(compose_popup_entries[0]);
3390         popupmenu = menu_create_items(compose_popup_entries, n_entries,
3391                                       "<Compose>", &popupfactory,
3392                                       compose);
3393
3394         ifactory = gtk_item_factory_from_widget(menubar);
3395         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
3396         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
3397
3398         /*
3399         if (account->protocol == A_NNTP) {
3400                 gtk_widget_hide(to_hbox);
3401                 gtk_widget_hide(to_entry);
3402                 gtk_widget_hide(cc_hbox);
3403                 gtk_widget_hide(cc_entry);
3404                 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
3405                 gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
3406         } else {
3407                 gtk_widget_hide(newsgroups_hbox);
3408                 gtk_widget_hide(newsgroups_entry);
3409                 gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
3410
3411                 menu_set_sensitive(ifactory, "/Message/Followup to", FALSE);
3412         }
3413         */
3414
3415         switch (prefs_common.toolbar_style) {
3416         case TOOLBAR_NONE:
3417                 gtk_widget_hide(handlebox);
3418                 break;
3419         case TOOLBAR_ICON:
3420                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
3421                                       GTK_TOOLBAR_ICONS);
3422                 break;
3423         case TOOLBAR_TEXT:
3424                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
3425                                       GTK_TOOLBAR_TEXT);
3426                 break;
3427         case TOOLBAR_BOTH:
3428                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
3429                                       GTK_TOOLBAR_BOTH);
3430                 break;
3431         }
3432
3433         gtk_widget_show(window);
3434
3435         address_completion_start(window);
3436
3437         compose->window        = window;
3438         compose->vbox          = vbox;
3439         compose->menubar       = menubar;
3440         compose->handlebox     = handlebox;
3441
3442         compose->vbox2         = vbox2;
3443
3444         compose->table_vbox       = table_vbox;
3445         compose->table            = table;
3446         compose->to_hbox          = to_hbox;
3447         compose->to_entry         = to_entry;
3448         compose->newsgroups_hbox  = newsgroups_hbox;
3449         compose->newsgroups_entry = newsgroups_entry;
3450         compose->subject_entry    = subject_entry;
3451         compose->cc_hbox          = cc_hbox;
3452         compose->cc_entry         = cc_entry;
3453         compose->bcc_hbox         = bcc_hbox;
3454         compose->bcc_entry        = bcc_entry;
3455         compose->reply_hbox       = reply_hbox;
3456         compose->reply_entry      = reply_entry;
3457         compose->followup_hbox    = followup_hbox;
3458         compose->followup_entry   = followup_entry;
3459
3460         compose->paned = paned;
3461
3462         compose->attach_scrwin = attach_scrwin;
3463         compose->attach_clist  = attach_clist;
3464
3465         compose->edit_vbox     = edit_vbox;
3466         compose->ruler_hbox    = ruler_hbox;
3467         compose->ruler         = ruler;
3468         compose->scrolledwin   = scrolledwin;
3469         compose->text          = text;
3470
3471         compose->focused_editable = NULL;
3472
3473         compose->popupmenu    = popupmenu;
3474         compose->popupfactory = popupfactory;
3475
3476         compose->mode = mode;
3477
3478         compose->replyto     = NULL;
3479         compose->cc          = NULL;
3480         compose->bcc         = NULL;
3481         compose->followup_to = NULL;
3482         compose->inreplyto   = NULL;
3483         compose->references  = NULL;
3484         compose->msgid       = NULL;
3485         compose->boundary    = NULL;
3486
3487 #if USE_GPGME
3488         compose->use_signing    = FALSE;
3489         compose->use_encryption = FALSE;
3490 #endif /* USE_GPGME */
3491
3492         compose->modified = FALSE;
3493
3494         compose->return_receipt = FALSE;
3495
3496         compose->to_list        = NULL;
3497         compose->newsgroup_list = NULL;
3498
3499         compose->exteditor_file    = NULL;
3500         compose->exteditor_pid     = -1;
3501         compose->exteditor_readdes = -1;
3502         compose->exteditor_tag     = -1;
3503
3504         compose_set_title(compose);
3505
3506         compose->use_bcc        = FALSE;
3507         compose->use_replyto    = FALSE;
3508         compose->use_followupto = FALSE;
3509
3510         /*
3511         if (account->protocol != A_NNTP) {
3512                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
3513                 gtk_check_menu_item_set_active
3514                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3515                 gtk_widget_set_sensitive(menuitem, FALSE);
3516                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
3517                 gtk_check_menu_item_set_active
3518                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3519                 gtk_widget_set_sensitive(menuitem, FALSE);
3520         }
3521         */
3522         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT) {
3523                 compose->use_cc = TRUE;
3524                 gtk_entry_set_text(GTK_ENTRY(cc_entry), account->auto_cc);
3525                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
3526                 gtk_check_menu_item_set_active
3527                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3528         }
3529
3530         if (account->set_autobcc) {
3531                 compose->use_bcc = TRUE;
3532                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Bcc");
3533                 gtk_check_menu_item_set_active
3534                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3535                 if (account->auto_bcc && mode != COMPOSE_REEDIT)
3536                         gtk_entry_set_text(GTK_ENTRY(bcc_entry),
3537                                            account->auto_bcc);
3538         }
3539         if (account->set_autoreplyto) {
3540                 compose->use_replyto = TRUE;
3541                 menuitem = gtk_item_factory_get_item(ifactory,
3542                                                      "/Message/Reply to");
3543                 gtk_check_menu_item_set_active
3544                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3545                 if (account->auto_replyto && mode != COMPOSE_REEDIT)
3546                         gtk_entry_set_text(GTK_ENTRY(reply_entry),
3547                                            account->auto_replyto);
3548         }
3549
3550         menuitem = gtk_item_factory_get_item(ifactory, "/Tool/Show ruler");
3551         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3552                                        prefs_common.show_ruler);
3553
3554 #if USE_GPGME
3555         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Sign");
3556         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3557                                        prefs_common.default_sign);
3558         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Encrypt");
3559         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3560                                        prefs_common.default_encrypt);
3561 #endif /* USE_GPGME */
3562
3563         addressbook_set_target_compose(compose);
3564
3565         compose_list = g_list_append(compose_list, compose);
3566
3567         /*
3568         compose->use_to         = FALSE;
3569         compose->use_cc         = FALSE;
3570         */
3571         compose->use_attach     = FALSE;
3572
3573         if (!compose->use_bcc) {
3574                 gtk_widget_hide(bcc_hbox);
3575                 gtk_widget_hide(bcc_entry);
3576                 gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0);
3577         }
3578         if (!compose->use_replyto) {
3579                 gtk_widget_hide(reply_hbox);
3580                 gtk_widget_hide(reply_entry);
3581                 gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0);
3582         }
3583         if (!compose->use_followupto) {
3584                 gtk_widget_hide(followup_hbox);
3585                 gtk_widget_hide(followup_entry);
3586                 gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0);
3587         }
3588
3589         if (!prefs_common.show_ruler)
3590                 gtk_widget_hide(ruler_hbox);
3591
3592         select_account(compose, account);
3593
3594         return compose;
3595 }
3596
3597 #include "pixmaps/stock_mail_send.xpm"
3598 #include "pixmaps/stock_mail_send_queue.xpm"
3599 #include "pixmaps/stock_mail.xpm"
3600 #include "pixmaps/stock_paste.xpm"
3601 #include "pixmaps/stock_mail_attach.xpm"
3602 #include "pixmaps/stock_mail_compose.xpm"
3603 #include "pixmaps/linewrap.xpm"
3604 #include "pixmaps/tb_address_book.xpm"
3605
3606 #define CREATE_TOOLBAR_ICON(xpm_d) \
3607 { \
3608         icon = gdk_pixmap_create_from_xpm_d(container->window, &mask, \
3609                                             &container->style->white, \
3610                                             xpm_d); \
3611         icon_wid = gtk_pixmap_new(icon, mask); \
3612 }
3613
3614 static void compose_toolbar_create(Compose *compose, GtkWidget *container)
3615 {
3616         GtkWidget *toolbar;
3617         GdkPixmap *icon;
3618         GdkBitmap *mask;
3619         GtkWidget *icon_wid;
3620         GtkWidget *send_btn;
3621         GtkWidget *sendl_btn;
3622         GtkWidget *draft_btn;
3623         GtkWidget *insert_btn;
3624         GtkWidget *attach_btn;
3625         GtkWidget *sig_btn;
3626         GtkWidget *exteditor_btn;
3627         GtkWidget *linewrap_btn;
3628         GtkWidget *addrbook_btn;
3629
3630         toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
3631                                   GTK_TOOLBAR_BOTH);
3632         gtk_container_add(GTK_CONTAINER(container), toolbar);
3633         gtk_container_set_border_width(GTK_CONTAINER(container), 2);
3634         gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
3635         gtk_toolbar_set_space_style(GTK_TOOLBAR(toolbar),
3636                                     GTK_TOOLBAR_SPACE_LINE);
3637
3638         CREATE_TOOLBAR_ICON(stock_mail_send_xpm);
3639         send_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3640                                            _("Send"),
3641                                            _("Send message"),
3642                                            "Send",
3643                                            icon_wid, toolbar_send_cb, compose);
3644
3645         CREATE_TOOLBAR_ICON(stock_mail_send_queue_xpm);
3646         /* CREATE_TOOLBAR_ICON(tb_mail_queue_send_xpm); */
3647         sendl_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3648                                            _("Send later"),
3649                                            _("Put into queue folder and send later"),
3650                                            "Send later",
3651                                            icon_wid, toolbar_send_later_cb,
3652                                            compose);
3653
3654         CREATE_TOOLBAR_ICON(stock_mail_xpm);
3655         draft_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3656                                             _("Draft"),
3657                                             _("Save to draft folder"),
3658                                             "Draft",
3659                                             icon_wid, toolbar_draft_cb,
3660                                             compose);
3661
3662         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
3663
3664         CREATE_TOOLBAR_ICON(stock_paste_xpm);
3665         insert_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3666                                              _("Insert"),
3667                                              _("Insert file"),
3668                                              "Insert",
3669                                              icon_wid, toolbar_insert_cb,
3670                                              compose);
3671
3672         CREATE_TOOLBAR_ICON(stock_mail_attach_xpm);
3673         attach_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3674                                              _("Attach"),
3675                                              _("Attach file"),
3676                                              "Attach",
3677                                              icon_wid, toolbar_attach_cb,
3678                                              compose);
3679
3680         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
3681
3682         CREATE_TOOLBAR_ICON(stock_mail_xpm);
3683         sig_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3684                                           _("Signature"),
3685                                           _("Insert signature"),
3686                                           "Signature",
3687                                           icon_wid, toolbar_sig_cb, compose);
3688
3689         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
3690
3691         CREATE_TOOLBAR_ICON(stock_mail_compose_xpm);
3692         exteditor_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3693                                                 _("Editor"),
3694                                                 _("Edit with external editor"),
3695                                                 "Editor",
3696                                                 icon_wid,
3697                                                 toolbar_ext_editor_cb,
3698                                                 compose);
3699
3700         CREATE_TOOLBAR_ICON(linewrap_xpm);
3701         linewrap_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3702                                                _("Linewrap"),
3703                                                _("Wrap current paragraph"),
3704                                                "Linewrap",
3705                                                icon_wid,
3706                                                toolbar_linewrap_cb,
3707                                                compose);
3708
3709         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
3710
3711         CREATE_TOOLBAR_ICON(tb_address_book_xpm);
3712         addrbook_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
3713                                                _("Address"),
3714                                                _("Address book"),
3715                                                "Address",
3716                                                icon_wid, toolbar_address_cb,
3717                                                compose);
3718
3719         compose->toolbar       = toolbar;
3720         compose->send_btn      = send_btn;
3721         compose->sendl_btn     = sendl_btn;
3722         compose->draft_btn     = draft_btn;
3723         compose->insert_btn    = insert_btn;
3724         compose->attach_btn    = attach_btn;
3725         compose->sig_btn       = sig_btn;
3726         compose->exteditor_btn = exteditor_btn;
3727         compose->linewrap_btn  = linewrap_btn;
3728         compose->addrbook_btn  = addrbook_btn;
3729
3730         gtk_widget_show_all(toolbar);
3731 }
3732
3733 #undef CREATE_TOOLBAR_ICON
3734
3735 static GtkWidget *compose_account_option_menu_create(Compose *compose)
3736 {
3737         GList *accounts;
3738         GtkWidget *hbox;
3739         GtkWidget *optmenu;
3740         GtkWidget *menu;
3741         gint num = 0, def_menu = 0;
3742
3743         accounts = account_get_list();
3744         g_return_val_if_fail(accounts != NULL, NULL);
3745
3746         hbox = gtk_hbox_new(FALSE, 0);
3747         optmenu = gtk_option_menu_new();
3748         gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
3749         menu = gtk_menu_new();
3750
3751         for (; accounts != NULL; accounts = accounts->next, num++) {
3752                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
3753                 GtkWidget *menuitem;
3754                 gchar *name;
3755
3756                 if (ac == compose->account) def_menu = num;
3757
3758                 name = g_strdup_printf("%s: %s <%s>",
3759                                        ac->account_name, ac->name, ac->address);
3760                 MENUITEM_ADD(menu, menuitem, name, ac);
3761                 g_free(name);
3762                 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
3763                                    GTK_SIGNAL_FUNC(account_activated),
3764                                    compose);
3765         }
3766
3767         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
3768         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
3769
3770         return hbox;
3771 }
3772
3773 static void compose_destroy(Compose *compose)
3774 {
3775         gint row;
3776         GtkCList *clist = GTK_CLIST(compose->attach_clist);
3777         AttachInfo *ainfo;
3778
3779         /* NOTE: address_completion_end() does nothing with the window
3780          * however this may change. */
3781         address_completion_end(compose->window);
3782
3783         slist_free_strings(compose->to_list);
3784         g_slist_free(compose->to_list);
3785         slist_free_strings(compose->newsgroup_list);
3786         g_slist_free(compose->newsgroup_list);
3787
3788         procmsg_msginfo_free(compose->targetinfo);
3789
3790         g_free(compose->replyto);
3791         g_free(compose->cc);
3792         g_free(compose->bcc);
3793         g_free(compose->newsgroups);
3794         g_free(compose->followup_to);
3795
3796         g_free(compose->inreplyto);