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);
3797         g_free(compose->references);
3798         g_free(compose->msgid);
3799         g_free(compose->boundary);
3800
3801         g_free(compose->exteditor_file);
3802
3803         for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
3804              row++)
3805                 compose_attach_info_free(ainfo);
3806
3807         if (addressbook_get_target_compose() == compose)
3808                 addressbook_set_target_compose(NULL);
3809
3810         prefs_common.compose_width = compose->scrolledwin->allocation.width;
3811         prefs_common.compose_height = compose->window->allocation.height;
3812
3813         gtk_widget_destroy(compose->paned);
3814
3815         g_free(compose);
3816
3817         compose_list = g_list_remove(compose_list, compose);
3818 }
3819
3820 static void compose_attach_info_free(AttachInfo *ainfo)
3821 {
3822         g_free(ainfo->file);
3823         g_free(ainfo->content_type);
3824         g_free(ainfo->name);
3825         g_free(ainfo);
3826 }
3827
3828 static void compose_attach_remove_selected(Compose *compose)
3829 {
3830         GtkCList *clist = GTK_CLIST(compose->attach_clist);
3831         AttachInfo *ainfo;
3832         gint row;
3833
3834         while (clist->selection != NULL) {
3835                 row = GPOINTER_TO_INT(clist->selection->data);
3836                 ainfo = gtk_clist_get_row_data(clist, row);
3837                 compose_attach_info_free(ainfo);
3838                 gtk_clist_remove(clist, row);
3839         }
3840 }
3841
3842 static struct _AttachProperty
3843 {
3844         GtkWidget *window;
3845         GtkWidget *mimetype_entry;
3846         GtkWidget *encoding_optmenu;
3847         GtkWidget *path_entry;
3848         GtkWidget *filename_entry;
3849         GtkWidget *ok_btn;
3850         GtkWidget *cancel_btn;
3851 } attach_prop;
3852
3853 static void compose_attach_property(Compose *compose)
3854 {
3855         GtkCList *clist = GTK_CLIST(compose->attach_clist);
3856         AttachInfo *ainfo;
3857         gint row;
3858         GtkOptionMenu *optmenu;
3859         static gboolean cancelled;
3860
3861         if (!clist->selection) return;
3862         row = GPOINTER_TO_INT(clist->selection->data);
3863
3864         ainfo = gtk_clist_get_row_data(clist, row);
3865         if (!ainfo) return;
3866
3867         if (!attach_prop.window)
3868                 compose_attach_property_create(&cancelled);
3869         gtk_widget_grab_focus(attach_prop.ok_btn);
3870         gtk_widget_show(attach_prop.window);
3871         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
3872
3873         optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu);
3874         if (ainfo->encoding == ENC_UNKNOWN)
3875                 gtk_option_menu_set_history(optmenu, ENC_BASE64);
3876         else
3877                 gtk_option_menu_set_history(optmenu, ainfo->encoding);
3878
3879         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
3880                            ainfo->content_type ? ainfo->content_type : "");
3881         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
3882                            ainfo->file ? ainfo->file : "");
3883         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
3884                            ainfo->name ? ainfo->name : "");
3885
3886         for (;;) {
3887                 gchar *text;
3888                 gchar *cnttype = NULL;
3889                 gchar *file = NULL;
3890                 off_t size = 0;
3891                 GtkWidget *menu;
3892                 GtkWidget *menuitem;
3893
3894                 cancelled = FALSE;
3895                 gtk_main();
3896
3897                 if (cancelled == TRUE) {
3898                         gtk_widget_hide(attach_prop.window);
3899                         break;
3900                 }
3901
3902                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
3903                 if (*text != '\0') {
3904                         gchar *p;
3905
3906                         text = g_strstrip(g_strdup(text));
3907                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
3908                                 cnttype = g_strdup(text);
3909                                 g_free(text);
3910                         } else {
3911                                 alertpanel_error(_("Invalid MIME type."));
3912                                 g_free(text);
3913                                 continue;
3914                         }
3915                 }
3916
3917                 menu = gtk_option_menu_get_menu(optmenu);
3918                 menuitem = gtk_menu_get_active(GTK_MENU(menu));
3919                 ainfo->encoding = GPOINTER_TO_INT
3920                         (gtk_object_get_user_data(GTK_OBJECT(menuitem)));
3921
3922                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
3923                 if (*text != '\0') {
3924                         if (is_file_exist(text) &&
3925                             (size = get_file_size(text)) > 0)
3926                                 file = g_strdup(text);
3927                         else {
3928                                 alertpanel_error
3929                                         (_("File doesn't exist or is empty."));
3930                                 g_free(cnttype);
3931                                 continue;
3932                         }
3933                 }
3934
3935                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
3936                 if (*text != '\0') {
3937                         g_free(ainfo->name);
3938                         ainfo->name = g_strdup(text);
3939                 }
3940
3941                 if (cnttype) {
3942                         g_free(ainfo->content_type);
3943                         ainfo->content_type = cnttype;
3944                 }
3945                 if (file) {
3946                         g_free(ainfo->file);
3947                         ainfo->file = file;
3948                 }
3949                 if (size)
3950                         ainfo->size = size;
3951
3952                 gtk_clist_set_text(clist, row, COL_MIMETYPE,
3953                                    ainfo->content_type);
3954                 gtk_clist_set_text(clist, row, COL_SIZE,
3955                                    to_human_readable(ainfo->size));
3956                 gtk_clist_set_text(clist, row, COL_NAME, ainfo->name);
3957
3958                 gtk_widget_hide(attach_prop.window);
3959                 break;
3960         }
3961 }
3962
3963 #define SET_LABEL_AND_ENTRY(str, entry, top) \
3964 { \
3965         label = gtk_label_new(str); \
3966         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
3967                          GTK_FILL, 0, 0, 0); \
3968         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
3969  \
3970         entry = gtk_entry_new(); \
3971         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
3972                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
3973 }
3974
3975 static void compose_attach_property_create(gboolean *cancelled)
3976 {
3977         GtkWidget *window;
3978         GtkWidget *vbox;
3979         GtkWidget *table;
3980         GtkWidget *label;
3981         GtkWidget *mimetype_entry;
3982         GtkWidget *hbox;
3983         GtkWidget *optmenu;
3984         GtkWidget *optmenu_menu;
3985         GtkWidget *menuitem;
3986         GtkWidget *path_entry;
3987         GtkWidget *filename_entry;
3988         GtkWidget *hbbox;
3989         GtkWidget *ok_btn;
3990         GtkWidget *cancel_btn;
3991         GList     *mime_type_list, *strlist;
3992
3993         debug_print("Creating attach_property window...\n");
3994
3995         window = gtk_window_new(GTK_WINDOW_DIALOG);
3996         gtk_widget_set_usize(window, 480, -1);
3997         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
3998         gtk_window_set_title(GTK_WINDOW(window), _("Property"));
3999         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
4000         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
4001         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
4002                            GTK_SIGNAL_FUNC(attach_property_delete_event),
4003                            cancelled);
4004         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
4005                            GTK_SIGNAL_FUNC(attach_property_key_pressed),
4006                            cancelled);
4007
4008         vbox = gtk_vbox_new(FALSE, 8);
4009         gtk_container_add(GTK_CONTAINER(window), vbox);
4010
4011         table = gtk_table_new(4, 2, FALSE);
4012         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
4013         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
4014         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
4015
4016         label = gtk_label_new(_("MIME type")); 
4017         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
4018                          GTK_FILL, 0, 0, 0); 
4019         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
4020         mimetype_entry = gtk_combo_new(); 
4021         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
4022                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
4023                          
4024         /* stuff with list */
4025         mime_type_list = procmime_get_mime_type_list();
4026         strlist = NULL;
4027         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
4028                 MimeType *type = (MimeType *) mime_type_list->data;
4029                 strlist = g_list_append(strlist, 
4030                                 g_strdup_printf("%s/%s",
4031                                         type->type, type->sub_type));
4032         }
4033         
4034         gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry), strlist);
4035
4036         for (mime_type_list = strlist; mime_type_list != NULL; 
4037                 mime_type_list = mime_type_list->next)
4038                 g_free(mime_type_list->data);
4039         g_list_free(strlist);
4040                          
4041         mimetype_entry = GTK_COMBO(mimetype_entry)->entry;                       
4042
4043         label = gtk_label_new(_("Encoding"));
4044         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
4045                          GTK_FILL, 0, 0, 0);
4046         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
4047
4048         hbox = gtk_hbox_new(FALSE, 0);
4049         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
4050                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
4051
4052         optmenu = gtk_option_menu_new();
4053         gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
4054
4055         optmenu_menu = gtk_menu_new();
4056         MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT);
4057         gtk_widget_set_sensitive(menuitem, FALSE);
4058         MENUITEM_ADD(optmenu_menu, menuitem, "8bit", ENC_8BIT);
4059         gtk_widget_set_sensitive(menuitem, FALSE);
4060         MENUITEM_ADD(optmenu_menu, menuitem, "quoted-printable", ENC_QUOTED_PRINTABLE);
4061         gtk_widget_set_sensitive(menuitem, FALSE);
4062         MENUITEM_ADD(optmenu_menu, menuitem, "base64", ENC_BASE64);
4063
4064         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
4065
4066         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
4067         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
4068
4069         gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
4070                                 &cancel_btn, _("Cancel"), NULL, NULL);
4071         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
4072         gtk_widget_grab_default(ok_btn);
4073
4074         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
4075                            GTK_SIGNAL_FUNC(attach_property_ok),
4076                            cancelled);
4077         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
4078                            GTK_SIGNAL_FUNC(attach_property_cancel),
4079                            cancelled);
4080
4081         gtk_widget_show_all(vbox);
4082
4083         attach_prop.window           = window;
4084         attach_prop.mimetype_entry   = mimetype_entry;
4085         attach_prop.encoding_optmenu = optmenu;
4086         attach_prop.path_entry       = path_entry;
4087         attach_prop.filename_entry   = filename_entry;
4088         attach_prop.ok_btn           = ok_btn;
4089         attach_prop.cancel_btn       = cancel_btn;
4090 }
4091
4092 #undef SET_LABEL_AND_ENTRY
4093
4094 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
4095 {
4096         *cancelled = FALSE;
4097         gtk_main_quit();
4098 }
4099
4100 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
4101 {
4102         *cancelled = TRUE;
4103         gtk_main_quit();
4104 }
4105
4106 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
4107                                          gboolean *cancelled)
4108 {
4109         *cancelled = TRUE;
4110         gtk_main_quit();
4111
4112         return TRUE;
4113 }
4114
4115 static void attach_property_key_pressed(GtkWidget *widget, GdkEventKey *event,
4116                                         gboolean *cancelled)
4117 {
4118         if (event && event->keyval == GDK_Escape) {
4119                 *cancelled = TRUE;
4120                 gtk_main_quit();
4121         }
4122 }
4123
4124 static void compose_exec_ext_editor(Compose *compose)
4125 {
4126         gchar tmp[64];
4127         pid_t pid;
4128         gint pipe_fds[2];
4129
4130         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%08x",
4131                    g_get_tmp_dir(), G_DIR_SEPARATOR, (gint)compose);
4132
4133         if (pipe(pipe_fds) < 0) {
4134                 perror("pipe");
4135                 return;
4136         }
4137
4138         if ((pid = fork()) < 0) {
4139                 perror("fork");
4140                 return;
4141         }
4142
4143         if (pid != 0) {
4144                 /* close the write side of the pipe */
4145                 close(pipe_fds[1]);
4146
4147                 compose->exteditor_file    = g_strdup(tmp);
4148                 compose->exteditor_pid     = pid;
4149                 compose->exteditor_readdes = pipe_fds[0];
4150
4151                 compose_set_ext_editor_sensitive(compose, FALSE);
4152
4153                 compose->exteditor_tag =
4154                         gdk_input_add(pipe_fds[0], GDK_INPUT_READ,
4155                                       compose_input_cb, compose);
4156         } else {        /* process-monitoring process */
4157                 pid_t pid_ed;
4158
4159                 if (setpgid(0, 0))
4160                         perror("setpgid");
4161
4162                 /* close the read side of the pipe */
4163                 close(pipe_fds[0]);
4164
4165                 if (compose_write_body_to_file(compose, tmp) < 0) {
4166                         fd_write(pipe_fds[1], "2\n", 2);
4167                         _exit(1);
4168                 }
4169
4170                 pid_ed = compose_exec_ext_editor_real(tmp);
4171                 if (pid_ed < 0) {
4172                         fd_write(pipe_fds[1], "1\n", 2);
4173                         _exit(1);
4174                 }
4175
4176                 /* wait until editor is terminated */
4177                 waitpid(pid_ed, NULL, 0);
4178
4179                 fd_write(pipe_fds[1], "0\n", 2);
4180
4181                 close(pipe_fds[1]);
4182                 _exit(0);
4183         }
4184 }
4185
4186 static gint compose_exec_ext_editor_real(const gchar *file)
4187 {
4188         static gchar *def_cmd = "emacs %s";
4189         gchar buf[1024];
4190         gchar *p;
4191         gchar **cmdline;
4192         pid_t pid;
4193
4194         g_return_val_if_fail(file != NULL, -1);
4195
4196         if ((pid = fork()) < 0) {
4197                 perror("fork");
4198                 return -1;
4199         }
4200
4201         if (pid != 0) return pid;
4202
4203         /* grandchild process */
4204
4205         if (setpgid(0, getppid()))
4206                 perror("setpgid");
4207
4208         if (prefs_common.ext_editor_cmd &&
4209             (p = strchr(prefs_common.ext_editor_cmd, '%')) &&
4210             *(p + 1) == 's' && !strchr(p + 2, '%')) {
4211                 g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, file);
4212         } else {
4213                 if (prefs_common.ext_editor_cmd)
4214                         g_warning(_("External editor command line is invalid: `%s'\n"),
4215                                   prefs_common.ext_editor_cmd);
4216                 g_snprintf(buf, sizeof(buf), def_cmd, file);
4217         }
4218
4219         cmdline = g_strsplit(buf, " ", 1024);
4220         execvp(cmdline[0], cmdline);
4221
4222         perror("execvp");
4223         g_strfreev(cmdline);
4224
4225         _exit(1);
4226 }
4227
4228 static gboolean compose_ext_editor_kill(Compose *compose)
4229 {
4230         pid_t pgid = compose->exteditor_pid * -1;
4231         gint ret;
4232
4233         ret = kill(pgid, 0);
4234
4235         if (ret == 0 || (ret == -1 && EPERM == errno)) {
4236                 AlertValue val;
4237                 gchar *msg;
4238
4239                 msg = g_strdup_printf
4240                         (_("The external editor is still working.\n"
4241                            "Force terminating the process?\n"
4242                            "process group id: %d"), -pgid);
4243                 val = alertpanel(_("Notice"), msg, _("Yes"), _("+No"), NULL);
4244                 g_free(msg);
4245
4246                 if (val == G_ALERTDEFAULT) {
4247                         gdk_input_remove(compose->exteditor_tag);
4248                         close(compose->exteditor_readdes);
4249
4250                         if (kill(pgid, SIGTERM) < 0) perror("kill");
4251                         waitpid(compose->exteditor_pid, NULL, 0);
4252
4253                         g_warning(_("Terminated process group id: %d"), -pgid);
4254                         g_warning(_("Temporary file: %s"),
4255                                   compose->exteditor_file);
4256
4257                         compose_set_ext_editor_sensitive(compose, TRUE);
4258
4259                         g_free(compose->exteditor_file);
4260                         compose->exteditor_file    = NULL;
4261                         compose->exteditor_pid     = -1;
4262                         compose->exteditor_readdes = -1;
4263                         compose->exteditor_tag     = -1;
4264                 } else
4265                         return FALSE;
4266         }
4267
4268         return TRUE;
4269 }
4270
4271 static void compose_input_cb(gpointer data, gint source,
4272                              GdkInputCondition condition)
4273 {
4274         gchar buf[3];
4275         Compose *compose = (Compose *)data;
4276         gint i = 0;
4277
4278         debug_print(_("Compose: input from monitoring process\n"));
4279
4280         gdk_input_remove(compose->exteditor_tag);
4281
4282         for (;;) {
4283                 if (read(source, &buf[i], 1) < 1) {
4284                         buf[0] = '3';
4285                         break;
4286                 }
4287                 if (buf[i] == '\n') {
4288                         buf[i] = '\0';
4289                         break;
4290                 }
4291                 i++;
4292                 if (i == sizeof(buf) - 1)
4293                         break;
4294         }
4295
4296         waitpid(compose->exteditor_pid, NULL, 0);
4297
4298         if (buf[0] == '0') {            /* success */
4299                 GtkSText *text = GTK_STEXT(compose->text);
4300
4301                 gtk_stext_freeze(text);
4302                 gtk_stext_set_point(text, 0);
4303                 gtk_stext_forward_delete(text, gtk_stext_get_length(text));
4304                 compose_insert_file(compose, compose->exteditor_file);
4305                 compose_changed_cb(NULL, compose);
4306                 gtk_stext_thaw(text);
4307
4308                 if (unlink(compose->exteditor_file) < 0)
4309                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
4310         } else if (buf[0] == '1') {     /* failed */
4311                 g_warning(_("Couldn't exec external editor\n"));
4312                 if (unlink(compose->exteditor_file) < 0)
4313                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
4314         } else if (buf[0] == '2') {
4315                 g_warning(_("Couldn't write to file\n"));
4316         } else if (buf[0] == '3') {
4317                 g_warning(_("Pipe read failed\n"));
4318         }
4319
4320         close(source);
4321
4322         compose_set_ext_editor_sensitive(compose, TRUE);
4323
4324         g_free(compose->exteditor_file);
4325         compose->exteditor_file    = NULL;
4326         compose->exteditor_pid     = -1;
4327         compose->exteditor_readdes = -1;
4328         compose->exteditor_tag     = -1;
4329 }
4330
4331 static void compose_set_ext_editor_sensitive(Compose *compose,
4332                                              gboolean sensitive)
4333 {
4334         GtkItemFactory *ifactory;
4335
4336         ifactory = gtk_item_factory_from_widget(compose->menubar);
4337
4338         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
4339         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
4340         menu_set_sensitive(ifactory, "/Message/Save to draft folder",
4341                            sensitive);
4342         menu_set_sensitive(ifactory, "/File/Insert file", sensitive);
4343         menu_set_sensitive(ifactory, "/File/Insert signature", sensitive);
4344         menu_set_sensitive(ifactory, "/Edit/Wrap long lines", sensitive);
4345         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
4346                            sensitive);
4347
4348         gtk_widget_set_sensitive(compose->text,          sensitive);
4349         gtk_widget_set_sensitive(compose->send_btn,      sensitive);
4350         gtk_widget_set_sensitive(compose->sendl_btn,     sensitive);
4351         gtk_widget_set_sensitive(compose->draft_btn,     sensitive);
4352         gtk_widget_set_sensitive(compose->insert_btn,    sensitive);
4353         gtk_widget_set_sensitive(compose->sig_btn,       sensitive);
4354         gtk_widget_set_sensitive(compose->exteditor_btn, sensitive);
4355         gtk_widget_set_sensitive(compose->linewrap_btn,  sensitive);
4356 }
4357
4358 static gint calc_cursor_xpos(GtkSText *text, gint extra, gint char_width)
4359 {
4360         gint cursor_pos;
4361
4362         cursor_pos = (text->cursor_pos_x - extra) / char_width;
4363         cursor_pos = MAX(cursor_pos, 0);
4364
4365         return cursor_pos;
4366 }
4367
4368 /* callback functions */
4369
4370 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
4371  * includes "non-client" (windows-izm) in calculation, so this calculation
4372  * may not be accurate.
4373  */
4374 static gboolean compose_edit_size_alloc(GtkEditable *widget,
4375                                         GtkAllocation *allocation,
4376                                         GtkSHRuler *shruler)
4377 {
4378         if (prefs_common.show_ruler) {
4379                 gint char_width;
4380                 gint line_width_in_chars;
4381
4382                 char_width = gtkut_get_font_width
4383                         (GTK_WIDGET(widget)->style->font);
4384                 line_width_in_chars =
4385                         (allocation->width - allocation->x) / char_width;
4386
4387                 /* got the maximum */
4388                 gtk_ruler_set_range(GTK_RULER(shruler),
4389                                     0.0, line_width_in_chars,
4390                                     calc_cursor_xpos(GTK_STEXT(widget),
4391                                                      allocation->x,
4392                                                      char_width),
4393                                     /*line_width_in_chars*/ char_width);
4394         }
4395
4396         return TRUE;
4397 }
4398
4399 static void toolbar_send_cb(GtkWidget *widget, gpointer data)
4400 {
4401         compose_send_cb(data, 0, NULL);
4402 }
4403
4404 static void toolbar_send_later_cb(GtkWidget *widget, gpointer data)
4405 {
4406         compose_send_later_cb(data, 0, NULL);
4407 }
4408
4409 static void toolbar_draft_cb(GtkWidget *widget, gpointer data)
4410 {
4411         compose_draft_cb(data, 0, NULL);
4412 }
4413
4414 static void toolbar_insert_cb(GtkWidget *widget, gpointer data)
4415 {
4416         compose_insert_file_cb(data, 0, NULL);
4417 }
4418
4419 static void toolbar_attach_cb(GtkWidget *widget, gpointer data)
4420 {
4421         compose_attach_cb(data, 0, NULL);
4422 }
4423
4424 static void toolbar_sig_cb(GtkWidget *widget, gpointer data)
4425 {
4426         Compose *compose = (Compose *)data;
4427
4428         compose_insert_sig(compose);
4429 }
4430
4431 static void toolbar_ext_editor_cb(GtkWidget *widget, gpointer data)
4432 {
4433         Compose *compose = (Compose *)data;
4434
4435         compose_exec_ext_editor(compose);
4436 }
4437
4438 static void toolbar_linewrap_cb(GtkWidget *widget, gpointer data)
4439 {
4440         Compose *compose = (Compose *)data;
4441
4442         compose_wrap_line(compose);
4443 }
4444
4445 static void toolbar_address_cb(GtkWidget *widget, gpointer data)
4446 {
4447         compose_address_cb(data, 0, NULL);
4448 }
4449
4450 static void select_account(Compose * compose, PrefsAccount * ac)
4451 {
4452                 compose->account = ac;
4453                 compose_set_title(compose);
4454
4455                 if (ac->protocol == A_NNTP) {
4456                         GtkItemFactory *ifactory;
4457                         GtkWidget *menuitem;
4458
4459                         ifactory = gtk_item_factory_from_widget(compose->menubar);
4460                         menu_set_sensitive(ifactory,
4461                                            "/Message/Followup to", TRUE);
4462
4463                         gtk_widget_show(compose->newsgroups_hbox);
4464                         gtk_widget_show(compose->newsgroups_entry);
4465                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4466                                                   1, 4);
4467
4468                         compose->use_to = FALSE;
4469                         compose->use_cc = FALSE;
4470
4471                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
4472                         gtk_check_menu_item_set_active
4473                                 (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
4474                         menu_set_sensitive(ifactory,
4475                                            "/Message/To", TRUE);
4476                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
4477                         gtk_check_menu_item_set_active
4478                                 (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
4479
4480                         gtk_widget_hide(compose->to_hbox);
4481                         gtk_widget_hide(compose->to_entry);
4482                         gtk_widget_hide(compose->cc_hbox);
4483                         gtk_widget_hide(compose->cc_entry);
4484
4485                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4486                                                   0, 0);
4487                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4488                                                   3, 0);
4489                 }
4490                 else {
4491                         GtkItemFactory *ifactory;
4492                         GtkWidget *menuitem;
4493
4494                         ifactory = gtk_item_factory_from_widget(compose->menubar);
4495                         menu_set_sensitive(ifactory,
4496                                            "/Message/Followup to", FALSE);
4497
4498                         gtk_entry_set_text(GTK_ENTRY(compose->newsgroups_entry), "");
4499                         gtk_widget_hide(compose->newsgroups_hbox);
4500                         gtk_widget_hide(compose->newsgroups_entry);
4501                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4502                                                   1, 0);
4503
4504                         compose->use_to = TRUE;
4505                         compose->use_cc = TRUE;
4506
4507                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
4508                         gtk_check_menu_item_set_active
4509                                 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4510                         menu_set_sensitive(ifactory,
4511                                            "/Message/To", FALSE);
4512                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
4513                         gtk_check_menu_item_set_active
4514                                 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4515
4516                         gtk_widget_show(compose->to_hbox);
4517                         gtk_widget_show(compose->to_entry);
4518                         gtk_widget_show(compose->cc_hbox);
4519                         gtk_widget_show(compose->cc_entry);
4520
4521                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4522                                                   0, 4);
4523                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
4524                                                   3, 4);
4525                 }
4526                 gtk_widget_queue_resize(compose->table_vbox);
4527 }
4528
4529 static void account_activated(GtkMenuItem *menuitem, gpointer data)
4530 {
4531         Compose *compose = (Compose *)data;
4532
4533         PrefsAccount *ac;
4534
4535         ac = (PrefsAccount *)gtk_object_get_user_data(GTK_OBJECT(menuitem));
4536         g_return_if_fail(ac != NULL);
4537
4538         if (ac != compose->account)
4539                 select_account(compose, ac);
4540 }
4541
4542 static void attach_selected(GtkCList *clist, gint row, gint column,
4543                             GdkEvent *event, gpointer data)
4544 {
4545         Compose *compose = (Compose *)data;
4546
4547         if (event && event->type == GDK_2BUTTON_PRESS)
4548                 compose_attach_property(compose);
4549 }
4550
4551 static void attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
4552                                   gpointer data)
4553 {
4554         Compose *compose = (Compose *)data;
4555         GtkCList *clist = GTK_CLIST(compose->attach_clist);
4556         gint row, column;
4557
4558         if (!event) return;
4559
4560         if (event->button == 3) {
4561                 if ((clist->selection && !clist->selection->next) ||
4562                     !clist->selection) {
4563                         gtk_clist_unselect_all(clist);
4564                         if (gtk_clist_get_selection_info(clist,
4565                                                          event->x, event->y,
4566                                                          &row, &column)) {
4567                                 gtk_clist_select_row(clist, row, column);
4568                                 gtkut_clist_set_focus_row(clist, row);
4569                         }
4570                 }
4571                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
4572                                NULL, NULL, event->button, event->time);
4573         }
4574 }
4575
4576 static void attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
4577                                gpointer data)
4578 {
4579         Compose *compose = (Compose *)data;
4580
4581         if (!event) return;
4582
4583         switch (event->keyval) {
4584         case GDK_Delete:
4585                 compose_attach_remove_selected(compose);
4586                 break;
4587         }
4588 }
4589
4590 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
4591 {
4592         Compose *compose = (Compose *)data;
4593         gint val;
4594
4595         val = compose_send(compose);
4596
4597         if (val == 0) gtk_widget_destroy(compose->window);
4598 }
4599
4600 static void compose_send_later_cb(gpointer data, guint action,
4601                                   GtkWidget *widget)
4602 {
4603         Compose *compose = (Compose *)data;
4604         gchar tmp[22];
4605         gchar *to, *newsgroups;
4606
4607         to = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4608         newsgroups = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
4609         if (*to == '\0' && *newsgroups == '\0') {
4610                 alertpanel_error(_("Recipient is not specified."));
4611                 return;
4612         }
4613
4614         g_snprintf(tmp, 22, "%s%ctmpmsg%d",
4615                    g_get_tmp_dir(), G_DIR_SEPARATOR, (gint)compose);
4616
4617         if (prefs_common.linewrap_at_send)
4618                 compose_wrap_line_all(compose);
4619
4620         if (compose_write_to_file(compose, tmp, FALSE) < 0 ||
4621             compose_queue(compose, tmp) < 0) {
4622                 alertpanel_error(_("Can't queue the message."));
4623                 return;
4624         }
4625
4626         if (prefs_common.savemsg) {
4627                 if (compose_save_to_outbox(compose, tmp) < 0)
4628                         alertpanel_error
4629                                 (_("Can't save the message to outbox."));
4630         }
4631
4632         if (unlink(tmp) < 0)
4633                 FILE_OP_ERROR(tmp, "unlink");
4634
4635         gtk_widget_destroy(compose->window);
4636 }
4637
4638 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
4639 {
4640         Compose *compose = (Compose *)data;
4641         FolderItem *draft;
4642         gchar *tmp;
4643
4644         draft = folder_get_default_draft();
4645
4646         tmp = g_strdup_printf("%s%cdraft.%d", g_get_tmp_dir(),
4647                               G_DIR_SEPARATOR, (gint)compose);
4648
4649         if (compose_write_to_file(compose, tmp, TRUE) < 0) {
4650                 g_free(tmp);
4651                 return;
4652         }
4653
4654         folder_item_scan(draft);
4655         if (folder_item_add_msg(draft, tmp, TRUE) < 0) {
4656                 unlink(tmp);
4657                 g_free(tmp);
4658                 return;
4659         }
4660         g_free(tmp);
4661
4662         if (compose->mode == COMPOSE_REEDIT) {
4663                 compose_remove_reedit_target(compose);
4664                 if (compose->targetinfo &&
4665                     compose->targetinfo->folder != draft)
4666                         folderview_update_item(compose->targetinfo->folder,
4667                                                TRUE);
4668         }
4669
4670         folder_item_scan(draft);
4671         folderview_update_item(draft, TRUE);
4672
4673         gtk_widget_destroy(compose->window);
4674 }
4675
4676 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
4677 {
4678         Compose *compose = (Compose *)data;
4679         GList *file_list;
4680
4681         file_list = filesel_select_multiple_files(_("Select file"), NULL);
4682
4683         if (file_list) {
4684                 GList *tmp;
4685
4686                 for ( tmp = file_list; tmp; tmp = tmp->next) {
4687                         gchar *file = (gchar *) tmp->data;
4688                         compose_attach_append(compose, file, MIME_UNKNOWN);
4689                         g_free(file);
4690                 }
4691                 g_list_free(file_list);
4692         }               
4693 }
4694
4695 static void compose_insert_file_cb(gpointer data, guint action,
4696                                    GtkWidget *widget)
4697 {
4698         Compose *compose = (Compose *)data;
4699         GList *file_list;
4700
4701         file_list = filesel_select_multiple_files(_("Select file"), NULL);
4702
4703         if (file_list) {
4704                 GList *tmp;
4705
4706                 for ( tmp = file_list; tmp; tmp = tmp->next) {
4707                         gchar *file = (gchar *) tmp->data;
4708                         compose_insert_file(compose, file);
4709                         g_free(file);
4710                 }
4711                 g_list_free(file_list);
4712         }
4713 }
4714
4715 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
4716                               gpointer data)
4717 {
4718         compose_close_cb(data, 0, NULL);
4719         return TRUE;
4720 }
4721
4722 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
4723 {
4724         Compose *compose = (Compose *)data;
4725         AlertValue val;
4726
4727         if (compose->exteditor_tag != -1) {
4728                 if (!compose_ext_editor_kill(compose))
4729                         return;
4730         }
4731
4732         if (compose->modified) {
4733                 val = alertpanel(_("Discard message"),
4734                                  _("This message has been modified. discard it?"),
4735                                  _("Discard"), _("to Draft"), _("Cancel"));
4736
4737                 switch (val) {
4738                 case G_ALERTDEFAULT:
4739                         break;
4740                 case G_ALERTALTERNATE:
4741                         compose_draft_cb(data, 0, NULL);
4742                         return;
4743                 default:
4744                         return;
4745                 }
4746         }
4747
4748         gtk_widget_destroy(compose->window);
4749 }
4750
4751 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
4752 {
4753         Compose *compose = (Compose *)data;
4754
4755         addressbook_open(compose);
4756 }
4757
4758 static void compose_ext_editor_cb(gpointer data, guint action,
4759                                   GtkWidget *widget)
4760 {
4761         Compose *compose = (Compose *)data;
4762
4763         compose_exec_ext_editor(compose);
4764 }
4765
4766 static void compose_destroy_cb(GtkWidget *widget, Compose *compose)
4767 {
4768         compose_destroy(compose);
4769 }
4770
4771 static void compose_cut_cb(Compose *compose)
4772 {
4773         if (compose->focused_editable &&
4774             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
4775                 gtk_editable_cut_clipboard
4776                         (GTK_EDITABLE(compose->focused_editable));
4777 }
4778
4779 static void compose_copy_cb(Compose *compose)
4780 {
4781         if (compose->focused_editable &&
4782             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
4783                 gtk_editable_copy_clipboard
4784                         (GTK_EDITABLE(compose->focused_editable));
4785 }
4786
4787 static void compose_paste_cb(Compose *compose)
4788 {
4789         if (compose->focused_editable &&
4790             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
4791                 gtk_editable_paste_clipboard
4792                         (GTK_EDITABLE(compose->focused_editable));
4793 }
4794
4795 static void compose_allsel_cb(Compose *compose)
4796 {
4797         if (compose->focused_editable &&
4798             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
4799                 gtk_editable_select_region
4800                         (GTK_EDITABLE(compose->focused_editable), 0, -1);
4801 }
4802
4803 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
4804 {
4805         if (GTK_IS_EDITABLE(widget))
4806                 compose->focused_editable = widget;
4807 }
4808
4809 static void compose_changed_cb(GtkEditable *editable, Compose *compose)
4810 {
4811         if (compose->modified == FALSE) {
4812                 compose->modified = TRUE;
4813                 compose_set_title(compose);
4814         }
4815 }
4816
4817 static void compose_button_press_cb(GtkWidget *widget, GdkEventButton *event,
4818                                     Compose *compose)
4819 {
4820         gtk_stext_set_point(GTK_STEXT(widget),
4821                            gtk_editable_get_position(GTK_EDITABLE(widget)));
4822 }
4823
4824 #if 0
4825 static void compose_key_press_cb(GtkWidget *widget, GdkEventKey *event,
4826                                  Compose *compose)
4827 {
4828         gtk_stext_set_point(GTK_STEXT(widget),
4829                            gtk_editable_get_position(GTK_EDITABLE(widget)));
4830 }
4831 #endif
4832
4833 static void compose_toggle_to_cb(gpointer data, guint action,
4834                                  GtkWidget *widget)
4835 {
4836         Compose *compose = (Compose *)data;
4837
4838         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4839                 gtk_widget_show(compose->to_hbox);
4840                 gtk_widget_show(compose->to_entry);
4841                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
4842                 compose->use_to = TRUE;
4843         } else {
4844                 gtk_widget_hide(compose->to_hbox);
4845                 gtk_widget_hide(compose->to_entry);
4846                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 0);
4847                 gtk_widget_queue_resize(compose->table_vbox);
4848                 compose->use_to = FALSE;
4849         }
4850
4851         if (addressbook_get_target_compose() == compose)
4852                 addressbook_set_target_compose(compose);
4853 }
4854
4855 static void compose_toggle_cc_cb(gpointer data, guint action,
4856                                  GtkWidget *widget)
4857 {
4858         Compose *compose = (Compose *)data;
4859
4860         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4861                 gtk_widget_show(compose->cc_hbox);
4862                 gtk_widget_show(compose->cc_entry);
4863                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 4);
4864                 compose->use_cc = TRUE;
4865         } else {
4866                 gtk_widget_hide(compose->cc_hbox);
4867                 gtk_widget_hide(compose->cc_entry);
4868                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 0);
4869                 gtk_widget_queue_resize(compose->table_vbox);
4870                 compose->use_cc = FALSE;
4871         }
4872
4873         if (addressbook_get_target_compose() == compose)
4874                 addressbook_set_target_compose(compose);
4875 }
4876
4877 static void compose_toggle_bcc_cb(gpointer data, guint action,
4878                                   GtkWidget *widget)
4879 {
4880         Compose *compose = (Compose *)data;
4881
4882         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4883                 gtk_widget_show(compose->bcc_hbox);
4884                 gtk_widget_show(compose->bcc_entry);
4885                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 4);
4886                 compose->use_bcc = TRUE;
4887         } else {
4888                 gtk_widget_hide(compose->bcc_hbox);
4889                 gtk_widget_hide(compose->bcc_entry);
4890                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 0);
4891                 gtk_widget_queue_resize(compose->table_vbox);
4892                 compose->use_bcc = FALSE;
4893         }
4894
4895         if (addressbook_get_target_compose() == compose)
4896                 addressbook_set_target_compose(compose);
4897 }
4898
4899 static void compose_toggle_replyto_cb(gpointer data, guint action,
4900                                       GtkWidget *widget)
4901 {
4902         Compose *compose = (Compose *)data;
4903
4904         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4905                 gtk_widget_show(compose->reply_hbox);
4906                 gtk_widget_show(compose->reply_entry);
4907                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 4);
4908                 compose->use_replyto = TRUE;
4909         } else {
4910                 gtk_widget_hide(compose->reply_hbox);
4911                 gtk_widget_hide(compose->reply_entry);
4912                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 0);
4913                 gtk_widget_queue_resize(compose->table_vbox);
4914                 compose->use_replyto = FALSE;
4915         }
4916 }
4917
4918 static void compose_toggle_followupto_cb(gpointer data, guint action,
4919                                          GtkWidget *widget)
4920 {
4921         Compose *compose = (Compose *)data;
4922
4923         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4924                 gtk_widget_show(compose->followup_hbox);
4925                 gtk_widget_show(compose->followup_entry);
4926                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 4);
4927                 compose->use_followupto = TRUE;
4928         } else {
4929                 gtk_widget_hide(compose->followup_hbox);
4930                 gtk_widget_hide(compose->followup_entry);
4931                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 0);
4932                 gtk_widget_queue_resize(compose->table_vbox);
4933                 compose->use_followupto = FALSE;
4934         }
4935 }
4936
4937 static void compose_toggle_attach_cb(gpointer data, guint action,
4938                                      GtkWidget *widget)
4939 {
4940         Compose *compose = (Compose *)data;
4941
4942         if (GTK_CHECK_MENU_ITEM(widget)->active) {
4943                 gtk_widget_ref(compose->edit_vbox);
4944
4945                 gtk_container_remove(GTK_CONTAINER(compose->vbox2),
4946                                      compose->edit_vbox);
4947                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
4948                 gtk_box_pack_start(GTK_BOX(compose->vbox2), compose->paned,
4949                                    TRUE, TRUE, 0);
4950                 gtk_widget_show(compose->paned);
4951
4952                 gtk_widget_unref(compose->edit_vbox);
4953                 gtk_widget_unref(compose->paned);
4954
4955                 compose->use_attach = TRUE;
4956         } else {
4957                 gtk_widget_ref(compose->paned);
4958                 gtk_widget_ref(compose->edit_vbox);
4959
4960                 gtk_container_remove(GTK_CONTAINER(compose->vbox2),
4961                                      compose->paned);
4962                 gtk_container_remove(GTK_CONTAINER(compose->paned),
4963                                      compose->edit_vbox);
4964                 gtk_box_pack_start(GTK_BOX(compose->vbox2),
4965                                    compose->edit_vbox, TRUE, TRUE, 0);
4966
4967                 gtk_widget_unref(compose->edit_vbox);
4968
4969                 compose->use_attach = FALSE;
4970         }
4971 }
4972
4973 #if USE_GPGME
4974 static void compose_toggle_sign_cb(gpointer data, guint action,
4975                                    GtkWidget *widget)
4976 {
4977         Compose *compose = (Compose *)data;
4978
4979         if (GTK_CHECK_MENU_ITEM(widget)->active)
4980                 compose->use_signing = TRUE;
4981         else
4982                 compose->use_signing = FALSE;
4983 }
4984
4985 static void compose_toggle_encrypt_cb(gpointer data, guint action,
4986                                       GtkWidget *widget)
4987 {
4988         Compose *compose = (Compose *)data;
4989
4990         if (GTK_CHECK_MENU_ITEM(widget)->active)
4991                 compose->use_encryption = TRUE;
4992         else
4993                 compose->use_encryption = FALSE;
4994 }
4995 #endif /* USE_GPGME */
4996
4997 static void compose_toggle_ruler_cb(gpointer data, guint action,
4998                                     GtkWidget *widget)
4999 {
5000         Compose *compose = (Compose *)data;
5001
5002         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5003                 gtk_widget_show(compose->ruler_hbox);
5004                 prefs_common.show_ruler = TRUE;
5005         } else {
5006                 gtk_widget_hide(compose->ruler_hbox);
5007                 gtk_widget_queue_resize(compose->edit_vbox);
5008                 prefs_common.show_ruler = FALSE;
5009         }
5010 }
5011
5012 static void compose_attach_drag_received_cb (GtkWidget          *widget,
5013                                              GdkDragContext     *drag_context,
5014                                              gint                x,
5015                                              gint                y,
5016                                              GtkSelectionData   *data,
5017                                              guint               info,
5018                                              guint               time,
5019                                              gpointer            user_data)
5020 {
5021         Compose *compose = (Compose *)user_data;
5022         GList *list, *tmp;
5023
5024         list = uri_list_extract_filenames((const gchar *)data->data);
5025         for (tmp = list; tmp != NULL; tmp = tmp->next)
5026                 compose_attach_append(compose, (const gchar *)tmp->data,
5027                                       MIME_UNKNOWN);
5028         list_free_strings(list);
5029         g_list_free(list);
5030 }
5031
5032 static void compose_insert_drag_received_cb (GtkWidget          *widget,
5033                                              GdkDragContext     *drag_context,
5034                                              gint                x,
5035                                              gint                y,
5036                                              GtkSelectionData   *data,
5037                                              guint               info,
5038                                              guint               time,
5039                                              gpointer            user_data)
5040 {
5041         Compose *compose = (Compose *)user_data;
5042         GList *list, *tmp;
5043
5044         list = uri_list_extract_filenames((const gchar *)data->data);
5045         for (tmp = list; tmp != NULL; tmp = tmp->next)
5046                 compose_insert_file(compose, (const gchar *)tmp->data);
5047         list_free_strings(list);
5048         g_list_free(list);
5049 }
5050
5051 static void to_activated(GtkWidget *widget, Compose *compose)
5052 {
5053         if (GTK_WIDGET_VISIBLE(compose->newsgroups_entry))
5054                 gtk_widget_grab_focus(compose->newsgroups_entry);
5055         else
5056                 gtk_widget_grab_focus(compose->subject_entry);
5057 }
5058
5059 static void newsgroups_activated(GtkWidget *widget, Compose *compose)
5060 {
5061         gtk_widget_grab_focus(compose->subject_entry);
5062 }
5063
5064 static void subject_activated(GtkWidget *widget, Compose *compose)
5065 {
5066         if (GTK_WIDGET_VISIBLE(compose->cc_entry))
5067                 gtk_widget_grab_focus(compose->cc_entry);
5068         else if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
5069                 gtk_widget_grab_focus(compose->bcc_entry);
5070         else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
5071                 gtk_widget_grab_focus(compose->reply_entry);
5072         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
5073                 gtk_widget_grab_focus(compose->followup_entry);
5074         else
5075                 gtk_widget_grab_focus(compose->text);
5076 }
5077
5078 static void cc_activated(GtkWidget *widget, Compose *compose)
5079 {
5080         if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
5081                 gtk_widget_grab_focus(compose->bcc_entry);
5082         else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
5083                 gtk_widget_grab_focus(compose->reply_entry);
5084         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
5085                 gtk_widget_grab_focus(compose->followup_entry);
5086         else
5087                 gtk_widget_grab_focus(compose->text);
5088 }
5089
5090 static void bcc_activated(GtkWidget *widget, Compose *compose)
5091 {
5092         if (GTK_WIDGET_VISIBLE(compose->reply_entry))
5093                 gtk_widget_grab_focus(compose->reply_entry);
5094         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
5095                 gtk_widget_grab_focus(compose->followup_entry);
5096         else
5097                 gtk_widget_grab_focus(compose->text);
5098 }
5099
5100 static void replyto_activated(GtkWidget *widget, Compose *compose)
5101 {
5102         if (GTK_WIDGET_VISIBLE(compose->followup_entry))
5103                 gtk_widget_grab_focus(compose->followup_entry);
5104         else
5105                 gtk_widget_grab_focus(compose->text);
5106 }
5107
5108 static void followupto_activated(GtkWidget *widget, Compose *compose)
5109 {
5110         gtk_widget_grab_focus(compose->text);
5111 }
5112
5113 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
5114                                              GtkWidget *widget)
5115 {
5116         Compose *compose = (Compose *)data;
5117
5118         if (GTK_CHECK_MENU_ITEM(widget)->active)
5119                 compose->return_receipt = TRUE;
5120         else
5121                 compose->return_receipt = FALSE;
5122 }
5123
5124 static gchar *compose_quote_fmt         (Compose        *compose,
5125                                          MsgInfo        *msginfo,
5126                                          const gchar    *fmt,
5127                                          const gchar    *qmark)
5128 {
5129         gchar * quote_str = NULL;
5130
5131         if (qmark != NULL) {
5132                 gchar * p;
5133
5134                 quote_fmt_init(msginfo, NULL);
5135                 quote_fmt_scan_string(qmark);
5136                 quote_fmtparse();
5137
5138                 p = quote_fmt_get_buffer();
5139                 if (p == NULL) {
5140                         alertpanel_error
5141                                 (_("Quote mark format error."));
5142                 }
5143                 else {
5144                         quote_str = alloca(strlen(p) + 1);
5145                         strcpy(quote_str, p);
5146                 }
5147         }
5148
5149         quote_fmt_init(msginfo, quote_str);
5150         quote_fmt_scan_string(fmt);
5151         quote_fmtparse();
5152
5153         if (quote_fmt_get_buffer() == NULL)
5154                 alertpanel_error
5155                         (_("Message reply/forward format error."));
5156
5157         return quote_fmt_get_buffer();
5158 }
5159
5160 static void template_apply_cb(gchar *s, gpointer data)
5161 {
5162         Compose *compose = (Compose*)data;
5163         GtkSText *text = GTK_STEXT(compose->text);
5164         gchar *quote_str;
5165         gchar *qmark;
5166         gchar *parsed_text;
5167         gchar *tmpl;
5168         gchar *old_tmpl = s;
5169
5170         if(!s) return;
5171         
5172         if(compose->replyinfo == NULL) {
5173                 gtk_stext_freeze(text);
5174                 gtk_stext_set_point(text, 0);
5175                 gtk_stext_forward_delete(text, gtk_stext_get_length(text));
5176                 gtk_stext_insert(text, NULL, NULL, NULL, s, -1);
5177                 gtk_stext_thaw(text);
5178                 g_free(old_tmpl);
5179                 return;
5180         }
5181
5182         parsed_text = g_new(gchar, strlen(s)*2 + 1);
5183         tmpl = parsed_text;
5184         while(*s) {
5185                 if (*s == '\n') {
5186                         *parsed_text++ = '\\';
5187                         *parsed_text++ = 'n';
5188                         s++;
5189                 } else {
5190                         *parsed_text++ = *s++;
5191                 }
5192         }
5193         *parsed_text = '\0';
5194
5195         if (prefs_common.quotemark && *prefs_common.quotemark)
5196                 qmark = prefs_common.quotemark;
5197         else
5198                 qmark = "> ";
5199
5200         quote_str = compose_quote_fmt(compose, compose->replyinfo, tmpl, qmark);
5201         if (quote_str != NULL) {
5202                 gtk_stext_freeze(text);
5203                 gtk_stext_set_point(text, 0);
5204                 gtk_stext_forward_delete(text, gtk_stext_get_length(text));
5205                 gtk_stext_insert(text, NULL, NULL, NULL, quote_str, -1);
5206                 gtk_stext_thaw(text);
5207         }
5208
5209         g_free(old_tmpl);
5210         g_free(tmpl);
5211 }
5212
5213 static void template_select_cb(gpointer data, guint action,
5214                                GtkWidget *widget)
5215 {
5216         template_select(&template_apply_cb, data);
5217 }