aab357d525f34d25869410f5f5fb449bd131b154
[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 "quote_fmt.h"
102 #include "template.h"
103
104 #if USE_GPGME
105 #  include "rfc2015.h"
106 #endif
107
108 typedef enum
109 {
110         COL_MIMETYPE = 0,
111         COL_SIZE     = 1,
112         COL_NAME     = 2
113 } AttachColumnPos;
114
115 #define N_ATTACH_COLS           3
116
117 #define B64_LINE_SIZE           57
118 #define B64_BUFFSIZE            77
119
120 #define MAX_REFERENCES_LEN      999
121
122 static GdkColor quote_color = {0, 0, 0, 0xbfff};
123
124 static GList *compose_list = NULL;
125
126 Compose *compose_generic_new                    (PrefsAccount   *account,
127                                                  const gchar    *to,
128                                                  FolderItem     *item);
129
130 static Compose *compose_create                  (PrefsAccount   *account,
131                                                  ComposeMode     mode);
132 static void compose_toolbar_create              (Compose        *compose,
133                                                  GtkWidget      *container);
134 static GtkWidget *compose_account_option_menu_create
135                                                 (Compose        *compose);
136 static void compose_set_template_menu           (Compose        *compose);
137 static void compose_template_apply              (Compose        *compose,
138                                                  Template       *tmpl);
139 static void compose_destroy                     (Compose        *compose);
140
141 static void compose_entries_set                 (Compose        *compose,
142                                                  const gchar    *mailto);
143 static gint compose_parse_header                (Compose        *compose,
144                                                  MsgInfo        *msginfo);
145 static gchar *compose_parse_references          (const gchar    *ref,
146                                                  const gchar    *msgid);
147
148 static gchar *compose_quote_fmt                 (Compose        *compose,
149                                                  MsgInfo        *msginfo,
150                                                  const gchar    *fmt,
151                                                  const gchar    *qmark);
152
153 static void compose_reply_set_entry             (Compose        *compose,
154                                                  MsgInfo        *msginfo,
155                                                  gboolean        to_all,
156                                                  gboolean        to_sender,
157                                                  gboolean
158                                                  followup_and_reply_to);
159 static void compose_reedit_set_entry            (Compose        *compose,
160                                                  MsgInfo        *msginfo);
161 static void compose_insert_sig                  (Compose        *compose);
162 static void compose_insert_file                 (Compose        *compose,
163                                                  const gchar    *file);
164 static void compose_attach_append               (Compose        *compose,
165                                                  const gchar    *file,
166                                                  ContentType     cnttype);
167 static void compose_attach_append_with_type(Compose *compose,
168                                             const gchar *file,
169                                             const gchar *type,
170                                             ContentType cnttype);
171 static void compose_wrap_line                   (Compose        *compose);
172 static void compose_wrap_line_all               (Compose        *compose);
173 static void compose_set_title                   (Compose        *compose);
174
175 static PrefsAccount *compose_current_mail_account(void);
176 /* static gint compose_send                     (Compose        *compose); */
177 static gint compose_write_to_file               (Compose        *compose,
178                                                  const gchar    *file,
179                                                  gboolean        is_draft);
180 static gint compose_write_body_to_file          (Compose        *compose,
181                                                  const gchar    *file);
182 static gint compose_save_to_outbox              (Compose        *compose,
183                                                  const gchar    *file);
184 static gint compose_remove_reedit_target        (Compose        *compose);
185 static gint compose_queue                       (Compose        *compose,
186                                                  gint           *msgnum,
187                                                  FolderItem     **item);
188 static void compose_write_attach                (Compose        *compose,
189                                                  FILE           *fp);
190 static gint compose_write_headers               (Compose        *compose,
191                                                  FILE           *fp,
192                                                  const gchar    *charset,
193                                                  EncodingType    encoding,
194                                                  gboolean        is_draft);
195
196 static void compose_convert_header              (gchar          *dest,
197                                                  gint            len,
198                                                  gchar          *src,
199                                                  gint            header_len);
200 static void compose_generate_msgid              (Compose        *compose,
201                                                  gchar          *buf,
202                                                  gint            len);
203
204 static void compose_attach_info_free            (AttachInfo     *ainfo);
205 static void compose_attach_remove_selected      (Compose        *compose);
206
207 static void compose_attach_property             (Compose        *compose);
208 static void compose_attach_property_create      (gboolean       *cancelled);
209 static void attach_property_ok                  (GtkWidget      *widget,
210                                                  gboolean       *cancelled);
211 static void attach_property_cancel              (GtkWidget      *widget,
212                                                  gboolean       *cancelled);
213 static gint attach_property_delete_event        (GtkWidget      *widget,
214                                                  GdkEventAny    *event,
215                                                  gboolean       *cancelled);
216 static void attach_property_key_pressed         (GtkWidget      *widget,
217                                                  GdkEventKey    *event,
218                                                  gboolean       *cancelled);
219
220 static void compose_exec_ext_editor             (Compose           *compose);
221 static gint compose_exec_ext_editor_real        (const gchar       *file);
222 static gboolean compose_ext_editor_kill         (Compose           *compose);
223 static void compose_input_cb                    (gpointer           data,
224                                                  gint               source,
225                                                  GdkInputCondition  condition);
226 static void compose_set_ext_editor_sensitive    (Compose           *compose,
227                                                  gboolean           sensitive);
228
229 static gint calc_cursor_xpos    (GtkSText       *text,
230                                  gint            extra,
231                                  gint            char_width);
232
233 static void compose_create_header_entry (Compose *compose);
234 static void compose_add_header_entry    (Compose *compose, gchar *header, gchar *text);
235
236 /* callback functions */
237
238 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
239                                          GtkAllocation  *allocation,
240                                          GtkSHRuler     *shruler);
241
242 static void toolbar_send_cb             (GtkWidget      *widget,
243                                          gpointer        data);
244 static void toolbar_send_later_cb       (GtkWidget      *widget,
245                                          gpointer        data);
246 static void toolbar_draft_cb            (GtkWidget      *widget,
247                                          gpointer        data);
248 static void toolbar_insert_cb           (GtkWidget      *widget,
249                                          gpointer        data);
250 static void toolbar_attach_cb           (GtkWidget      *widget,
251                                          gpointer        data);
252 static void toolbar_sig_cb              (GtkWidget      *widget,
253                                          gpointer        data);
254 static void toolbar_ext_editor_cb       (GtkWidget      *widget,
255                                          gpointer        data);
256 static void toolbar_linewrap_cb         (GtkWidget      *widget,
257                                          gpointer        data);
258 static void toolbar_address_cb          (GtkWidget      *widget,
259                                          gpointer        data);
260
261 static void select_account              (Compose        *compose,
262                                          PrefsAccount   *ac);
263
264 static void account_activated           (GtkMenuItem    *menuitem,
265                                          gpointer        data);
266
267 static void attach_selected             (GtkCList       *clist,
268                                          gint            row,
269                                          gint            column,
270                                          GdkEvent       *event,
271                                          gpointer        data);
272 static void attach_button_pressed       (GtkWidget      *widget,
273                                          GdkEventButton *event,
274                                          gpointer        data);
275 static void attach_key_pressed          (GtkWidget      *widget,
276                                          GdkEventKey    *event,
277                                          gpointer        data);
278
279 static void compose_send_cb             (gpointer        data,
280                                          guint           action,
281                                          GtkWidget      *widget);
282 static void compose_send_later_cb       (gpointer        data,
283                                          guint           action,
284                                          GtkWidget      *widget);
285
286 static void compose_draft_cb            (gpointer        data,
287                                          guint           action,
288                                          GtkWidget      *widget);
289
290 static void compose_attach_cb           (gpointer        data,
291                                          guint           action,
292                                          GtkWidget      *widget);
293 static void compose_insert_file_cb      (gpointer        data,
294                                          guint           action,
295                                          GtkWidget      *widget);
296
297 static void compose_close_cb            (gpointer        data,
298                                          guint           action,
299                                          GtkWidget      *widget);
300
301 static void compose_address_cb          (gpointer        data,
302                                          guint           action,
303                                          GtkWidget      *widget);
304 static void compose_template_activate_cb(GtkWidget      *widget,
305                                          gpointer        data);
306
307 static void compose_ext_editor_cb       (gpointer        data,
308                                          guint           action,
309                                          GtkWidget      *widget);
310
311 static gint compose_delete_cb           (GtkWidget      *widget,
312                                          GdkEventAny    *event,
313                                          gpointer        data);
314 static void compose_destroy_cb          (GtkWidget      *widget,
315                                          Compose        *compose);
316
317 static void compose_cut_cb              (Compose        *compose);
318 static void compose_copy_cb             (Compose        *compose);
319 static void compose_paste_cb            (Compose        *compose);
320 static void compose_allsel_cb           (Compose        *compose);
321
322 static void compose_grab_focus_cb       (GtkWidget      *widget,
323                                          Compose        *compose);
324
325 static void compose_changed_cb          (GtkEditable    *editable,
326                                          Compose        *compose);
327 static void compose_button_press_cb     (GtkWidget      *widget,
328                                          GdkEventButton *event,
329                                          Compose        *compose);
330 #if 0
331 static void compose_key_press_cb        (GtkWidget      *widget,
332                                          GdkEventKey    *event,
333                                          Compose        *compose);
334 #endif
335
336 #if 0
337 static void compose_toggle_to_cb        (gpointer        data,
338                                          guint           action,
339                                          GtkWidget      *widget);
340 static void compose_toggle_cc_cb        (gpointer        data,
341                                          guint           action,
342                                          GtkWidget      *widget);
343 static void compose_toggle_bcc_cb       (gpointer        data,
344                                          guint           action,
345                                          GtkWidget      *widget);
346 static void compose_toggle_replyto_cb   (gpointer        data,
347                                          guint           action,
348                                          GtkWidget      *widget);
349 static void compose_toggle_followupto_cb(gpointer        data,
350                                          guint           action,
351                                          GtkWidget      *widget);
352 static void compose_toggle_attach_cb    (gpointer        data,
353                                          guint           action,
354                                          GtkWidget      *widget);
355 #endif
356 static void compose_toggle_ruler_cb     (gpointer        data,
357                                          guint           action,
358                                          GtkWidget      *widget);
359 #if USE_GPGME
360 static void compose_toggle_sign_cb      (gpointer        data,
361                                          guint           action,
362                                          GtkWidget      *widget);
363 static void compose_toggle_encrypt_cb   (gpointer        data,
364                                          guint           action,
365                                          GtkWidget      *widget);
366 #endif
367 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
368                                              GtkWidget *widget);
369
370 static void compose_attach_drag_received_cb (GtkWidget          *widget,
371                                              GdkDragContext     *drag_context,
372                                              gint                x,
373                                              gint                y,
374                                              GtkSelectionData   *data,
375                                              guint               info,
376                                              guint               time,
377                                              gpointer            user_data);
378 static void compose_insert_drag_received_cb (GtkWidget          *widget,
379                                              GdkDragContext     *drag_context,
380                                              gint                x,
381                                              gint                y,
382                                              GtkSelectionData   *data,
383                                              guint               info,
384                                              guint               time,
385                                              gpointer            user_data);
386
387 #if 0
388 static void to_activated                (GtkWidget      *widget,
389                                          Compose        *compose);
390 static void newsgroups_activated        (GtkWidget      *widget,
391                                          Compose        *compose);
392 static void subject_activated           (GtkWidget      *widget,
393                                          Compose        *compose);
394 static void cc_activated                (GtkWidget      *widget,
395                                          Compose        *compose);
396 static void bcc_activated               (GtkWidget      *widget,
397                                          Compose        *compose);
398 static void replyto_activated           (GtkWidget      *widget,
399                                          Compose        *compose);
400 static void followupto_activated        (GtkWidget      *widget,
401                                          Compose        *compose);
402 #endif
403
404 static void compose_attach_parts        (Compose        *compose,
405                                          MsgInfo        *msginfo);
406
407 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
408                                   gboolean to_all,
409                                   gboolean ignore_replyto,
410                                   gboolean followup_and_reply_to);
411
412 void compose_headerentry_changed_cb        (GtkWidget          *entry,
413                                             ComposeHeaderEntry *headerentry);
414 void compose_headerentry_key_press_event_cb(GtkWidget          *entry,
415                                             GdkEventKey        *event,
416                                             ComposeHeaderEntry *headerentry);
417
418 static GtkItemFactoryEntry compose_popup_entries[] =
419 {
420         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
421         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
422         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
423         {N_("/_Property..."),   NULL, compose_attach_property, 0, NULL}
424 };
425
426 static GtkItemFactoryEntry compose_entries[] =
427 {
428         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
429         {N_("/_File/_Attach file"),             "<control>M", compose_attach_cb,      0, NULL},
430         {N_("/_File/_Insert file"),             "<control>I", compose_insert_file_cb, 0, NULL},
431         {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig,     0, NULL},
432         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
433         {N_("/_File/_Close"),                   "<alt>W", compose_close_cb, 0, NULL},
434
435         {N_("/_Edit"),             NULL, NULL, 0, "<Branch>"},
436         {N_("/_Edit/_Undo"),       "<control>Z", NULL, 0, NULL},
437         {N_("/_Edit/_Redo"),       "<control>Y", NULL, 0, NULL},
438         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
439         {N_("/_Edit/Cu_t"),        "<control>X", compose_cut_cb,    0, NULL},
440         {N_("/_Edit/_Copy"),       "<control>C", compose_copy_cb,   0, NULL},
441         {N_("/_Edit/_Paste"),      "<control>V", compose_paste_cb,  0, NULL},
442         {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
443         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
444         {N_("/_Edit/_Wrap current paragraph"), "<alt>L", compose_wrap_line, 0, NULL},
445         {N_("/_Edit/Wrap all long _lines"),
446                         "<shift><alt>L", compose_wrap_line_all, 0, NULL},
447         {N_("/_Edit/Edit with e_xternal editor"),
448                         "<alt>X", compose_ext_editor_cb, 0, NULL},
449
450         {N_("/_Message"),               NULL, NULL, 0, "<Branch>"},
451         {N_("/_Message/_Send"),         "<control>Return",
452                                         compose_send_cb, 0, NULL},
453         {N_("/_Message/Send _later"),   "<shift><alt>S",
454                                         compose_send_later_cb,  0, NULL},
455         {N_("/_Message/Save to _draft folder"),
456                                         "<alt>D", compose_draft_cb, 0, NULL},
457 #if 0 /* NEW COMPOSE GUI */
458         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
459         {N_("/_Message/_To"),           NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
460         {N_("/_Message/_Cc"),           NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
461         {N_("/_Message/_Bcc"),          NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
462         {N_("/_Message/_Reply to"),     NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
463         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
464         {N_("/_Message/_Followup to"),  NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
465         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
466         {N_("/_Message/_Attach"),       NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
467 #endif
468 #if USE_GPGME
469         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
470         {N_("/_Message/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
471         {N_("/_Message/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
472 #endif /* USE_GPGME */
473         {N_("/_Message/---"),           NULL,           NULL,   0, "<Separator>"},
474         {N_("/_Message/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
475         {N_("/_Tool"),                  NULL, NULL, 0, "<Branch>"},
476         {N_("/_Tool/Show _ruler"),      NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
477         {N_("/_Tool/_Address book"),    "<alt>A", compose_address_cb , 0, NULL},
478         {N_("/_Tool/_Template"),        NULL, NULL, 0, "<Branch>"},
479         {N_("/_Help"),                  NULL, NULL, 0, "<LastBranch>"},
480         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
481 };
482
483 static GtkTargetEntry compose_mime_types[] =
484 {
485         {"text/uri-list", 0, 0}
486 };
487
488 Compose *compose_new(PrefsAccount *account)
489 {
490         return compose_generic_new(account, NULL, NULL);
491 }
492
493 Compose *compose_bounce(PrefsAccount *account, MsgInfo *msginfo)
494 {
495         Compose *c;
496         gchar *filename;
497         GtkItemFactory *ifactory;
498         
499         c = compose_generic_new(account, NULL, NULL);
500
501         filename = procmsg_get_message_file(msginfo);
502         if (filename == NULL)
503                 return NULL;
504
505         c->bounce_filename = filename;
506
507         if (msginfo->subject)
508                 gtk_entry_set_text(GTK_ENTRY(c->subject_entry),
509                                    msginfo->subject);
510         gtk_editable_set_editable(GTK_EDITABLE(c->subject_entry), FALSE);
511
512         compose_quote_fmt(c, msginfo, "%M", NULL);
513         gtk_editable_set_editable(GTK_EDITABLE(c->text), FALSE);
514
515         ifactory = gtk_item_factory_from_widget(c->popupmenu);
516         menu_set_sensitive(ifactory, "/Add...", FALSE);
517         menu_set_sensitive(ifactory, "/Remove", FALSE);
518         menu_set_sensitive(ifactory, "/Property...", FALSE);
519
520         ifactory = gtk_item_factory_from_widget(c->menubar);
521         menu_set_sensitive(ifactory, "/File/Insert file", FALSE);
522         menu_set_sensitive(ifactory, "/File/Attach file", FALSE);
523         menu_set_sensitive(ifactory, "/File/Insert signature", FALSE);
524         menu_set_sensitive(ifactory, "/Edit/Paste", FALSE);
525         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE);
526         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE);
527         menu_set_sensitive(ifactory, "/Edit/Edit with external editor", FALSE);
528         menu_set_sensitive(ifactory, "/Message/Attach", FALSE);
529 #if USE_GPGME
530         menu_set_sensitive(ifactory, "/Message/Sign", FALSE);
531         menu_set_sensitive(ifactory, "/Message/Encrypt", FALSE);
532 #endif
533         menu_set_sensitive(ifactory, "/Message/Request Return Receipt", FALSE);
534         menu_set_sensitive(ifactory, "/Tool/Template", FALSE);
535         
536         gtk_widget_set_sensitive(c->insert_btn, FALSE);
537         gtk_widget_set_sensitive(c->attach_btn, FALSE);
538         gtk_widget_set_sensitive(c->sig_btn, FALSE);
539         gtk_widget_set_sensitive(c->exteditor_btn, FALSE);
540         gtk_widget_set_sensitive(c->linewrap_btn, FALSE);
541
542         return c;
543 }
544
545 Compose *compose_new_with_recipient(PrefsAccount *account, const gchar *mailto)
546 {
547         return compose_generic_new(account, mailto, NULL);
548 }
549
550 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item)
551 {
552         return compose_generic_new(account, NULL, item);
553 }
554
555 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item)
556 {
557         Compose *compose;
558
559         if (item && item->prefs && item->prefs->enable_default_account)
560                 account = account_find_from_id(item->prefs->default_account);
561
562         if (!account) account = cur_account;
563         g_return_val_if_fail(account != NULL, NULL);
564
565         compose = compose_create(account, COMPOSE_NEW);
566         compose->replyinfo = NULL;
567
568         if (prefs_common.auto_sig)
569                 compose_insert_sig(compose);
570         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
571         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
572
573         if (account->protocol != A_NNTP) {
574                 if (mailto) {
575                         compose_entries_set(compose, mailto);
576
577                 } else if(item && item->prefs->enable_default_to) {
578                         compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
579                 }
580                 if (item && item->ret_rcpt) {
581                         GtkItemFactory *ifactory;
582                 
583                         ifactory = gtk_item_factory_from_widget(compose->menubar);
584                         menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
585                 }
586         } else {
587                 if (mailto) {
588                         compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
589                 }
590         }
591         gtk_widget_grab_focus(compose->subject_entry);
592
593         if (prefs_common.auto_exteditor)
594                 compose_exec_ext_editor(compose);
595
596         return compose;
597 }
598
599 #define CHANGE_FLAGS(msginfo) \
600 { \
601 if (msginfo->folder->folder->change_flags != NULL) \
602 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
603                                       msginfo->folder, \
604                                       msginfo); \
605 }
606
607 /*
608 Compose *compose_new_followup_and_replyto(PrefsAccount *account,
609                                            const gchar *followupto, gchar * to)
610 {
611         Compose *compose;
612
613         if (!account) account = cur_account;
614         g_return_val_if_fail(account != NULL, NULL);
615         g_return_val_if_fail(account->protocol != A_NNTP, NULL);
616
617         compose = compose_create(account, COMPOSE_NEW);
618
619         if (prefs_common.auto_sig)
620                 compose_insert_sig(compose);
621         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
622         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
623
624         compose_entry_append(compose, to, COMPOSE_TO);
625         compose_entry_append(compose, followupto, COMPOSE_NEWSGROUPS);
626         gtk_widget_grab_focus(compose->subject_entry);
627
628         return compose;
629 }
630 */
631
632 void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all,
633                    gboolean ignore_replyto)
634 {
635         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, FALSE);
636 }
637
638 void compose_followup_and_reply_to(MsgInfo *msginfo, gboolean quote,
639                                    gboolean to_all,
640                                    gboolean ignore_replyto)
641 {
642         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, TRUE);
643 }
644
645 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
646                                   gboolean to_all,
647                                   gboolean ignore_replyto,
648                                   gboolean followup_and_reply_to)
649 {
650         Compose *compose;
651         PrefsAccount *account;
652         PrefsAccount *reply_account;
653         GtkSText *text;
654
655         g_return_if_fail(msginfo != NULL);
656         g_return_if_fail(msginfo->folder != NULL);
657
658         account = NULL;
659         /* select the account set in folderitem's property (if enabled) */
660         if (msginfo->folder->prefs && msginfo->folder->prefs->enable_default_account)
661                 account = account_find_from_id(msginfo->folder->prefs->default_account);
662         
663         /* select the account for the whole folder (IMAP / NNTP) */
664         if (!account)
665                 account = msginfo->folder->folder->account;
666
667         /* select account by to: and cc: header if enabled */
668         if (prefs_common.reply_account_autosel) {
669                 if (!account && msginfo->to) {
670                         gchar *to;
671                         Xstrdup_a(to, msginfo->to, return);
672                         extract_address(to);
673                         account = account_find_from_address(to);
674                 }
675                 if (!account) {
676                         gchar cc[BUFFSIZE];
677                         if(!get_header_from_msginfo(msginfo, cc, sizeof(cc), "CC:")) { /* Found a CC header */
678                                 extract_address(cc);
679                                 account = account_find_from_address(cc);
680                         }        
681                 }
682         }
683
684         /* select current account */
685         if (!account) account = cur_account;
686         g_return_if_fail(account != NULL);
687
688         if (ignore_replyto && account->protocol == A_NNTP &&
689             !followup_and_reply_to) {
690                 reply_account =
691                         account_find_from_address(account->address);
692                 if (!reply_account)
693                         reply_account = compose_current_mail_account();
694                 if (!reply_account)
695                         return;
696         } else
697                 reply_account = account;
698
699         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
700         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
701
702         CHANGE_FLAGS(msginfo);
703
704         compose = compose_create(account, COMPOSE_REPLY);
705         compose->replyinfo = procmsg_msginfo_copy(msginfo);
706
707 #if 0 /* NEW COMPOSE GUI */
708         if (followup_and_reply_to) {
709                 gtk_widget_show(compose->to_hbox);
710                 gtk_widget_show(compose->to_entry);
711                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
712                 compose->use_to = TRUE;
713         }
714 #endif
715         if (msginfo->folder && msginfo->folder->ret_rcpt) {
716                 GtkItemFactory *ifactory;
717         
718                 ifactory = gtk_item_factory_from_widget(compose->menubar);
719                 menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
720         }
721
722         if (compose_parse_header(compose, msginfo) < 0) return;
723         compose_reply_set_entry(compose, msginfo, to_all, ignore_replyto,
724                                 followup_and_reply_to);
725
726         text = GTK_STEXT(compose->text);
727         gtk_stext_freeze(text);
728
729         if (quote) {
730                 gchar *qmark;
731                 gchar *quote_str;
732
733                 if (prefs_common.quotemark && *prefs_common.quotemark)
734                         qmark = prefs_common.quotemark;
735                 else
736                         qmark = "> ";
737
738                 quote_str = compose_quote_fmt(compose, msginfo,
739                                               prefs_common.quotefmt,
740                                               qmark);
741         }
742
743         if (prefs_common.auto_sig)
744                 compose_insert_sig(compose);
745         gtk_editable_set_position(GTK_EDITABLE(text), 0);
746         gtk_stext_set_point(text, 0);
747
748         if (quote && prefs_common.linewrap_quote)
749                 compose_wrap_line_all(compose);
750
751         gtk_stext_thaw(text);
752         gtk_widget_grab_focus(compose->text);
753
754         if (prefs_common.auto_exteditor)
755                 compose_exec_ext_editor(compose);
756 }
757
758
759 static gchar *procmime_get_file_name(MimeInfo *mimeinfo)
760 {
761         gchar *base;
762         gchar *filename;
763
764         g_return_val_if_fail(mimeinfo != NULL, NULL);
765
766         base = mimeinfo->filename ? mimeinfo->filename
767                 : mimeinfo->name ? mimeinfo->name : NULL;
768
769         if (MIME_TEXT_HTML == mimeinfo->mime_type && base == NULL){
770                 filename = g_strdup_printf("%s%smimetmp.%08x.html",
771                                            get_mime_tmp_dir(),
772                                            G_DIR_SEPARATOR_S,
773                                            (gint)mimeinfo);
774                 return filename;
775         }
776         else {
777                 base = base ? base : "";
778                 base = g_basename(base);
779                 if (*base == '\0') {
780                         filename = g_strdup_printf("%s%smimetmp.%08x",
781                                                    get_mime_tmp_dir(),
782                                                    G_DIR_SEPARATOR_S,
783                                                    (gint)mimeinfo);
784                         return filename;
785                 }
786         }
787
788         filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
789                                base, NULL);
790
791         return filename;
792 }
793
794 static gchar *mime_extract_file(gchar *source, MimeInfo *partinfo)
795 {
796         gchar *filename;
797
798         if (!partinfo) return NULL;
799
800         filename = procmime_get_file_name(partinfo);
801
802         if (procmime_get_part(filename, source, partinfo) < 0)
803                 alertpanel_error
804                         (_("Can't get the part of multipart message."));
805
806         return filename;
807 }
808
809 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
810 {
811         FILE *fp;
812         gchar *file;
813         MimeInfo *mimeinfo;
814         MsgInfo *tmpmsginfo;
815         gchar *p;
816         gchar *boundary;
817         gint boundary_len = 0;
818         gchar buf[BUFFSIZE];
819         glong fpos, prev_fpos;
820         gint npart;
821         gchar *source;
822         gchar *filename;
823
824         g_return_if_fail(msginfo != NULL);
825         
826 #if USE_GPGME
827         for (;;) {
828                 if ((fp = procmsg_open_message(msginfo)) == NULL) return;
829                 mimeinfo = procmime_scan_mime_header(fp);
830                 if (!mimeinfo) break;
831
832                 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
833                     rfc2015_is_encrypted(mimeinfo)) {
834                         MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
835                 }
836                 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
837                     !msginfo->plaintext_file  &&
838                     !msginfo->decryption_failed) {
839                         rfc2015_decrypt_message(msginfo, mimeinfo, fp);
840                         if (msginfo->plaintext_file &&
841                             !msginfo->decryption_failed) {
842                                 fclose(fp);
843                                 continue;
844                         }
845                 }
846                 
847                 break;
848         }
849 #else /* !USE_GPGME */
850         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
851         mimeinfo = procmime_scan_mime_header(fp);
852 #endif /* USE_GPGME */
853
854         fclose(fp);
855         if (!mimeinfo) return;
856         if (mimeinfo->mime_type == MIME_TEXT)
857                 return;
858
859         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
860
861         g_return_if_fail(mimeinfo != NULL);
862         g_return_if_fail(mimeinfo->mime_type != MIME_TEXT);
863
864         if (mimeinfo->mime_type == MIME_MULTIPART) {
865                 g_return_if_fail(mimeinfo->boundary != NULL);
866                 g_return_if_fail(mimeinfo->sub == NULL);
867         }
868         g_return_if_fail(fp != NULL);
869
870         boundary = mimeinfo->boundary;
871
872         if (boundary) {
873                 boundary_len = strlen(boundary);
874
875                 /* look for first boundary */
876                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL)
877                         if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
878                 if (!p) {
879                         fclose(fp);
880                         return;
881                 }
882         }
883
884         if ((fpos = ftell(fp)) < 0) {
885                 perror("ftell");
886                 fclose(fp);
887                 return;
888         }
889
890         for (npart = 0;; npart++) {
891                 MimeInfo *partinfo;
892                 gboolean eom = FALSE;
893
894                 prev_fpos = fpos;
895
896                 partinfo = procmime_scan_mime_header(fp);
897                 if (!partinfo) break;
898
899                 if (npart != 0)
900                         procmime_mimeinfo_insert(mimeinfo, partinfo);
901                 else
902                         procmime_mimeinfo_free(partinfo);
903
904                 /* look for next boundary */
905                 buf[0] = '\0';
906                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
907                         if (IS_BOUNDARY(buf, boundary, boundary_len)) {
908                                 if (buf[2 + boundary_len]     == '-' &&
909                                     buf[2 + boundary_len + 1] == '-')
910                                         eom = TRUE;
911                                 break;
912                         }
913                 }
914                 if (p == NULL)
915                         eom = TRUE;     /* broken MIME message */
916                 fpos = ftell(fp);
917
918                 partinfo->size = fpos - prev_fpos - strlen(buf);
919
920                 if (eom) break;
921         }
922
923         source = procmsg_get_message_file_path(msginfo);
924
925         g_return_if_fail(mimeinfo != NULL);
926
927         if (!mimeinfo->main && mimeinfo->parent)
928                 {
929                         filename = mime_extract_file(source, mimeinfo);
930
931                         compose_attach_append_with_type(compose, filename,
932                                                         mimeinfo->content_type,
933                                                         mimeinfo->mime_type);
934
935                         g_free(filename);
936                 }
937
938         if (mimeinfo->sub && mimeinfo->sub->children)
939                 {
940                         filename = mime_extract_file(source, mimeinfo->sub);
941
942                         compose_attach_append_with_type(compose, filename,
943                                                         mimeinfo->content_type,
944                                                         mimeinfo->sub->mime_type);
945
946                         g_free(filename);
947                 }
948
949         if (mimeinfo->children) {
950                 MimeInfo *child;
951
952                 child = mimeinfo->children;
953                 while (child) {
954                         filename = mime_extract_file(source, child);
955
956                         compose_attach_append_with_type(compose, filename,
957                                                         child->content_type,
958                                                         child->mime_type);
959
960                         g_free(filename);
961
962                         child = child->next;
963                 }
964         }
965
966         fclose(fp);
967
968         procmime_mimeinfo_free_all(mimeinfo);
969 }
970
971
972 #define INSERT_FW_HEADER(var, hdr) \
973 if (msginfo->var && *msginfo->var) { \
974         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
975         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
976         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
977 }
978
979 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
980                          gboolean as_attach)
981 {
982         Compose *compose;
983         /*      PrefsAccount *account; */
984         GtkSText *text;
985
986         g_return_val_if_fail(msginfo != NULL, NULL);
987         g_return_val_if_fail(msginfo->folder != NULL, NULL);
988
989         account = msginfo->folder->folder->account;
990         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
991                 gchar *to;
992                 Xstrdup_a(to, msginfo->to, return NULL);
993                 extract_address(to);
994                 account = account_find_from_address(to);
995         }
996
997         if(!account && prefs_common.forward_account_autosel) {
998                 gchar cc[BUFFSIZE];
999                 if(!get_header_from_msginfo(msginfo,cc,sizeof(cc),"CC:")){ /* Found a CC header */
1000                         extract_address(cc);
1001                         account = account_find_from_address(cc);
1002                 }
1003         }
1004
1005         if (account == NULL) {
1006                 account = cur_account;
1007                 /*
1008                 account = msginfo->folder->folder->account;
1009                 if (!account) account = cur_account;
1010                 */
1011         }
1012         g_return_val_if_fail(account != NULL, NULL);
1013
1014         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
1015         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
1016         CHANGE_FLAGS(msginfo);
1017
1018         compose = compose_create(account, COMPOSE_FORWARD);
1019
1020         if (msginfo->subject && *msginfo->subject) {
1021                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Fw: ");
1022                 gtk_entry_append_text(GTK_ENTRY(compose->subject_entry),
1023                                       msginfo->subject);
1024         }
1025
1026         text = GTK_STEXT(compose->text);
1027         gtk_stext_freeze(text);
1028
1029         if (as_attach) {
1030                 gchar *msgfile;
1031
1032                 msgfile = procmsg_get_message_file_path(msginfo);
1033                 if (!is_file_exist(msgfile))
1034                         g_warning(_("%s: file not exist\n"), msgfile);
1035                 else
1036                         compose_attach_append(compose, msgfile,
1037                                               MIME_MESSAGE_RFC822);
1038
1039                 g_free(msgfile);
1040         } else {
1041                 gchar *qmark;
1042                 gchar *quote_str;
1043
1044                 if (prefs_common.fw_quotemark && *prefs_common.fw_quotemark)
1045                         qmark = prefs_common.fw_quotemark;
1046                 else
1047                         qmark = "> ";
1048
1049                 quote_str = compose_quote_fmt(compose, msginfo,
1050                                               prefs_common.fw_quotefmt, qmark);
1051                 compose_attach_parts(compose, msginfo);
1052         }
1053
1054         if (prefs_common.auto_sig)
1055                 compose_insert_sig(compose);
1056
1057         if (prefs_common.linewrap_quote)
1058                 compose_wrap_line_all(compose);
1059
1060         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1061         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1062
1063         gtk_stext_thaw(text);
1064 #if 0 /* NEW COMPOSE GUI */
1065         if (account->protocol != A_NNTP)
1066                 gtk_widget_grab_focus(compose->to_entry);
1067         else
1068                 gtk_widget_grab_focus(compose->newsgroups_entry);
1069 #endif
1070         gtk_widget_grab_focus(compose->header_last->entry);
1071
1072         if (prefs_common.auto_exteditor)
1073                 compose_exec_ext_editor(compose);
1074
1075         return compose;
1076 }
1077
1078 #undef INSERT_FW_HEADER
1079
1080 Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1081 {
1082         Compose *compose;
1083         GtkSText *text;
1084         GSList *msginfo;
1085         gchar *msgfile;
1086
1087         g_return_val_if_fail(msginfo_list != NULL, NULL);
1088         
1089         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1090                 if ( ((MsgInfo *)msginfo->data)->folder == NULL )
1091                         return NULL;
1092         }
1093
1094         if (account == NULL) {
1095                 account = cur_account;
1096                 /*
1097                 account = msginfo->folder->folder->account;
1098                 if (!account) account = cur_account;
1099                 */
1100         }
1101         g_return_val_if_fail(account != NULL, NULL);
1102
1103         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1104                 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1105                 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1106                 CHANGE_FLAGS(((MsgInfo *)msginfo->data));
1107         }
1108
1109         compose = compose_create(account, COMPOSE_FORWARD);
1110
1111         text = GTK_STEXT(compose->text);
1112         gtk_stext_freeze(text);
1113
1114         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1115                 msgfile = procmsg_get_message_file_path((MsgInfo *)msginfo->data);
1116                 if (!is_file_exist(msgfile))
1117                         g_warning(_("%s: file not exist\n"), msgfile);
1118                 else
1119                         compose_attach_append(compose, msgfile,
1120                                 MIME_MESSAGE_RFC822);
1121                 g_free(msgfile);
1122         }
1123
1124         if (prefs_common.auto_sig)
1125                 compose_insert_sig(compose);
1126
1127         if (prefs_common.linewrap_quote)
1128                 compose_wrap_line_all(compose);
1129
1130         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1131         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1132
1133         gtk_stext_thaw(text);
1134 #if 0 /* NEW COMPOSE GUI */
1135         if (account->protocol != A_NNTP)
1136                 gtk_widget_grab_focus(compose->to_entry);
1137         else
1138                 gtk_widget_grab_focus(compose->newsgroups_entry);
1139 #endif
1140
1141         return compose;
1142 }
1143
1144 void compose_reedit(MsgInfo *msginfo)
1145 {
1146         Compose *compose;
1147         PrefsAccount *account;
1148         GtkSText *text;
1149         FILE *fp;
1150         gchar buf[BUFFSIZE];
1151
1152         g_return_if_fail(msginfo != NULL);
1153         g_return_if_fail(msginfo->folder != NULL);
1154
1155         account = msginfo->folder->folder->account;
1156
1157         if(!account&& prefs_common.reedit_account_autosel) {
1158                 gchar from[BUFFSIZE];
1159                 if(!get_header_from_msginfo(msginfo,from,sizeof(from),"FROM:")){ /* Found a FROM header */
1160                         extract_address(from);
1161                         account = account_find_from_address(from);
1162                 }
1163         }
1164         if (!account) account = cur_account;
1165         g_return_if_fail(account != NULL);
1166
1167         compose = compose_create(account, COMPOSE_REEDIT);
1168         compose->targetinfo = procmsg_msginfo_copy(msginfo);
1169
1170         if (compose_parse_header(compose, msginfo) < 0) return;
1171         compose_reedit_set_entry(compose, msginfo);
1172
1173         text = GTK_STEXT(compose->text);
1174         gtk_stext_freeze(text);
1175
1176         if ((fp = procmime_get_first_text_content(msginfo)) == NULL)
1177                 g_warning(_("Can't get text part\n"));
1178         else {
1179                 while (fgets(buf, sizeof(buf), fp) != NULL)
1180                         gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1181                 fclose(fp);
1182         }
1183         compose_attach_parts(compose, msginfo);
1184
1185         gtk_stext_thaw(text);
1186         gtk_widget_grab_focus(compose->text);
1187
1188         if (prefs_common.auto_exteditor)
1189                 compose_exec_ext_editor(compose);
1190 }
1191
1192 GList *compose_get_compose_list(void)
1193 {
1194         return compose_list;
1195 }
1196
1197 void compose_entry_append(Compose *compose, const gchar *address,
1198                           ComposeEntryType type)
1199 {
1200         GtkEntry *entry;
1201         const gchar *text;
1202         gchar *header;
1203
1204         if (!address || *address == '\0') return;
1205
1206 #if 0 /* NEW COMPOSE GUI */
1207         switch (type) {
1208         case COMPOSE_CC:
1209                 entry = GTK_ENTRY(compose->cc_entry);
1210                 break;
1211         case COMPOSE_BCC:
1212                 entry = GTK_ENTRY(compose->bcc_entry);
1213                 break;
1214         case COMPOSE_NEWSGROUPS:
1215                 entry = GTK_ENTRY(compose->newsgroups_entry);
1216                 break;
1217         case COMPOSE_TO:
1218         default:
1219                 entry = GTK_ENTRY(compose->to_entry);
1220                 break;
1221         }
1222
1223         text = gtk_entry_get_text(entry);
1224         if (*text != '\0')
1225                 gtk_entry_append_text(entry, ", ");
1226         gtk_entry_append_text(entry, address);
1227 #endif
1228
1229         switch (type) {
1230         case COMPOSE_CC:
1231                 header = N_("Cc:");
1232                 break;
1233         case COMPOSE_BCC:
1234                 header = N_("Bcc:");
1235                 break;
1236         case COMPOSE_REPLYTO:
1237                 header = N_("Reply-To:");
1238                 break;
1239         case COMPOSE_NEWSGROUPS:
1240                 header = N_("Newsgroups:");
1241                 break;
1242         case COMPOSE_FOLLOWUPTO:
1243                 header = N_( "Followup-To:");
1244                 break;
1245         case COMPOSE_TO:
1246         default:
1247                 header = N_("To:");
1248                 break;
1249         }
1250         header = prefs_common.trans_hdr ? gettext(header) : header;
1251
1252         compose_add_header_entry(compose, header, (gchar *)address);
1253 }
1254
1255 static void compose_entries_set(Compose *compose, const gchar *mailto)
1256 {
1257         gchar *subject = NULL;
1258         gchar *to = NULL;
1259         gchar *cc = NULL;
1260         gchar *bcc = NULL;
1261         gchar *body = NULL;
1262         gchar *p;
1263         gchar *tmp_mailto;
1264
1265         Xstrdup_a(tmp_mailto, mailto, return);
1266
1267         to = tmp_mailto;
1268
1269         p = strchr(tmp_mailto, '?');
1270         if (p) {
1271                 *p = '\0';
1272                 p++;
1273         }
1274
1275         while (p) {
1276                 gchar *field, *value;
1277
1278                 field = p;
1279
1280                 p = strchr(p, '=');
1281                 if (!p) break;
1282                 *p = '\0';
1283                 p++;
1284
1285                 value = p;
1286
1287                 p = strchr(p, '&');
1288                 if (p) {
1289                         *p = '\0';
1290                         p++;
1291                 }
1292
1293                 if (*value == '\0') continue;
1294
1295                 if (!g_strcasecmp(field, "subject")) {
1296                         Xalloca(subject, strlen(value) + 1, return);
1297                         decode_uri(subject, value);
1298                 } else if (!g_strcasecmp(field, "cc")) {
1299                         cc = value;
1300                 } else if (!g_strcasecmp(field, "bcc")) {
1301                         bcc = value;
1302                 } else if (!g_strcasecmp(field, "body")) {
1303                         Xalloca(body, strlen(value) + 1, return);
1304                         decode_uri(body, value);
1305                 }
1306         }
1307
1308         if (to)
1309                 compose_entry_append(compose, to, COMPOSE_TO);
1310         if (subject)
1311                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
1312         if (cc)
1313                 compose_entry_append(compose, cc, COMPOSE_CC);
1314         if (bcc)
1315                 compose_entry_append(compose, bcc, COMPOSE_BCC);
1316         if (body) {
1317                 gtk_stext_insert(GTK_STEXT(compose->text),
1318                                 NULL, NULL, NULL, body, -1);
1319                 gtk_stext_insert(GTK_STEXT(compose->text),
1320                                 NULL, NULL, NULL, "\n", 1);
1321         }
1322 }
1323
1324 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1325 {
1326         static HeaderEntry hentry[] = {{"Reply-To:",       NULL, TRUE},
1327                                        {"Cc:",             NULL, FALSE},
1328                                        {"References:",     NULL, FALSE},
1329                                        {"Bcc:",            NULL, FALSE},
1330                                        {"Newsgroups:",     NULL, FALSE},
1331                                        {"Followup-To:",    NULL, FALSE},
1332                                        {"X-Mailing-List:", NULL, FALSE},
1333                                        {"X-BeenThere:",    NULL, FALSE},
1334                                        {NULL,              NULL, FALSE}};
1335
1336         enum
1337         {
1338                 H_REPLY_TO       = 0,
1339                 H_CC             = 1,
1340                 H_REFERENCES     = 2,
1341                 H_BCC            = 3,
1342                 H_NEWSGROUPS     = 4,
1343                 H_FOLLOWUP_TO    = 5,
1344                 H_X_MAILING_LIST = 6,
1345                 H_X_BEENTHERE    = 7
1346         };
1347
1348         FILE *fp;
1349
1350         g_return_val_if_fail(msginfo != NULL, -1);
1351
1352         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1353         procheader_get_header_fields(fp, hentry);
1354         fclose(fp);
1355
1356         if (hentry[H_REPLY_TO].body != NULL) {
1357                 conv_unmime_header_overwrite(hentry[H_REPLY_TO].body);
1358                 compose->replyto = hentry[H_REPLY_TO].body;
1359                 hentry[H_REPLY_TO].body = NULL;
1360         }
1361         if (hentry[H_CC].body != NULL) {
1362                 conv_unmime_header_overwrite(hentry[H_CC].body);
1363                 compose->cc = hentry[H_CC].body;
1364                 hentry[H_CC].body = NULL;
1365         }
1366         if (hentry[H_X_MAILING_LIST].body != NULL) {
1367                 /* this is good enough to parse debian-devel */
1368                 gchar *buf = g_malloc(strlen(hentry[H_X_MAILING_LIST].body) + 1);
1369                 g_return_val_if_fail(buf != NULL, -1 );
1370                 if (1 == sscanf(hentry[H_X_MAILING_LIST].body, "<%[^>]>", buf))
1371                         compose->mailinglist = g_strdup(buf);
1372                 g_free(buf);
1373                 g_free(hentry[H_X_MAILING_LIST].body);
1374                 hentry[H_X_MAILING_LIST].body = NULL ;
1375         }
1376         if (hentry[H_X_BEENTHERE].body != NULL) {
1377                 /* this is good enough to parse the sylpheed-claws lists */
1378                 gchar *buf = g_malloc(strlen(hentry[H_X_BEENTHERE].body) + 1);
1379                 g_return_val_if_fail(buf != NULL, -1 );
1380                 if (1 == sscanf(hentry[H_X_BEENTHERE].body, "%[^>]", buf))
1381                         compose->mailinglist = g_strdup(buf);
1382                 g_free(buf);
1383                 g_free(hentry[H_X_BEENTHERE].body);
1384                 hentry[H_X_BEENTHERE].body = NULL ;
1385         }
1386         if (hentry[H_REFERENCES].body != NULL) {
1387                 if (compose->mode == COMPOSE_REEDIT)
1388                         compose->references = hentry[H_REFERENCES].body;
1389                 else {
1390                         compose->references = compose_parse_references
1391                                 (hentry[H_REFERENCES].body, msginfo->msgid);
1392                         g_free(hentry[H_REFERENCES].body);
1393                 }
1394                 hentry[H_REFERENCES].body = NULL;
1395         }
1396         if (hentry[H_BCC].body != NULL) {
1397                 if (compose->mode == COMPOSE_REEDIT) {
1398                         conv_unmime_header_overwrite(hentry[H_BCC].body);
1399                         compose->bcc = hentry[H_BCC].body;
1400                 } else
1401                         g_free(hentry[H_BCC].body);
1402                 hentry[H_BCC].body = NULL;
1403         }
1404         if (hentry[H_NEWSGROUPS].body != NULL) {
1405                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
1406                 hentry[H_NEWSGROUPS].body = NULL;
1407         }
1408         if (hentry[H_FOLLOWUP_TO].body != NULL) {
1409                 conv_unmime_header_overwrite(hentry[H_FOLLOWUP_TO].body);
1410                 compose->followup_to = hentry[H_FOLLOWUP_TO].body;
1411                 hentry[H_FOLLOWUP_TO].body = NULL;
1412         }
1413
1414         if (compose->mode == COMPOSE_REEDIT && msginfo->inreplyto)
1415                 compose->inreplyto = g_strdup(msginfo->inreplyto);
1416         else if (compose->mode != COMPOSE_REEDIT &&
1417                  msginfo->msgid && *msginfo->msgid) {
1418                 compose->inreplyto = g_strdup(msginfo->msgid);
1419
1420                 if (!compose->references) {
1421                         if (msginfo->inreplyto && *msginfo->inreplyto)
1422                                 compose->references =
1423                                         g_strdup_printf("<%s>\n\t<%s>",
1424                                                         msginfo->inreplyto,
1425                                                         msginfo->msgid);
1426                         else
1427                                 compose->references =
1428                                         g_strconcat("<", msginfo->msgid, ">",
1429                                                     NULL);
1430                 }
1431         }
1432
1433         return 0;
1434 }
1435
1436 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1437 {
1438         GSList *ref_id_list, *cur;
1439         GString *new_ref;
1440         gchar *new_ref_str;
1441
1442         ref_id_list = references_list_append(NULL, ref);
1443         if (!ref_id_list) return NULL;
1444         if (msgid && *msgid)
1445                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1446
1447         for (;;) {
1448                 gint len = 0;
1449
1450                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
1451                         /* "<" + Message-ID + ">" + CR+LF+TAB */
1452                         len += strlen((gchar *)cur->data) + 5;
1453
1454                 if (len > MAX_REFERENCES_LEN) {
1455                         /* remove second message-ID */
1456                         if (ref_id_list && ref_id_list->next &&
1457                             ref_id_list->next->next) {
1458                                 g_free(ref_id_list->next->data);
1459                                 ref_id_list = g_slist_remove
1460                                         (ref_id_list, ref_id_list->next->data);
1461                         } else {
1462                                 slist_free_strings(ref_id_list);
1463                                 g_slist_free(ref_id_list);
1464                                 return NULL;
1465                         }
1466                 } else
1467                         break;
1468         }
1469
1470         new_ref = g_string_new("");
1471         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1472                 if (new_ref->len > 0)
1473                         g_string_append(new_ref, "\n\t");
1474                 g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1475         }
1476
1477         slist_free_strings(ref_id_list);
1478         g_slist_free(ref_id_list);
1479
1480         new_ref_str = new_ref->str;
1481         g_string_free(new_ref, FALSE);
1482
1483         return new_ref_str;
1484 }
1485
1486 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1487                                 const gchar *fmt, const gchar *qmark)
1488 {
1489         GtkSText *text = GTK_STEXT(compose->text);
1490         gchar *quote_str = NULL;
1491         gchar *buf;
1492         gchar *p, *lastp;
1493         gint len;
1494
1495         if (qmark != NULL) {
1496                 quote_fmt_init(msginfo, NULL);
1497                 quote_fmt_scan_string(qmark);
1498                 quote_fmt_parse();
1499
1500                 buf = quote_fmt_get_buffer();
1501                 if (buf == NULL)
1502                         alertpanel_error(_("Quote mark format error."));
1503                 else
1504                         Xstrdup_a(quote_str, buf, return NULL)
1505         }
1506
1507         quote_fmt_init(msginfo, quote_str);
1508         quote_fmt_scan_string(fmt);
1509         quote_fmt_parse();
1510
1511         buf = quote_fmt_get_buffer();
1512         if (buf == NULL) {
1513                 alertpanel_error(_("Message reply/forward format error."));
1514                 return NULL;
1515         }
1516
1517         gtk_stext_freeze(text);
1518         gtk_stext_set_point(text, 0);
1519         gtk_stext_forward_delete(text, gtk_stext_get_length(text));
1520
1521         for (p = buf; *p != '\0'; ) {
1522                 lastp = strchr(p, '\n');
1523                 len = lastp ? lastp - p + 1 : -1;
1524                 gtk_stext_insert(text, NULL, NULL, NULL, p, len);
1525                 if (lastp)
1526                         p = lastp + 1;
1527                 else
1528                         break;
1529         }
1530
1531         gtk_stext_thaw(text);
1532
1533         return buf;
1534 }
1535
1536 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1537                                     gboolean to_all, gboolean ignore_replyto,
1538                                     gboolean followup_and_reply_to)
1539 {
1540         GSList *cc_list;
1541         GSList *cur;
1542         gchar *from;
1543         GHashTable *to_table;
1544
1545         g_return_if_fail(compose->account != NULL);
1546         g_return_if_fail(msginfo != NULL);
1547
1548         if ((compose->account->protocol != A_NNTP) || followup_and_reply_to)
1549                 compose_entry_append(compose,
1550                                     ((compose->mailinglist && !ignore_replyto)
1551                                      ? compose->mailinglist
1552                                      : (compose->replyto && !ignore_replyto)
1553                                      ? compose->replyto
1554                                      : msginfo->from ? msginfo->from : ""),
1555                                      COMPOSE_TO);
1556         if (compose->account->protocol == A_NNTP) {
1557                 if (ignore_replyto)
1558                         compose_entry_append
1559                                 (compose, msginfo->from ? msginfo->from : "",
1560                                  COMPOSE_TO);
1561                 else
1562                         compose_entry_append
1563                                 (compose,
1564                                  compose->followup_to ? compose->followup_to
1565                                  : compose->newsgroups ? compose->newsgroups
1566                                  : "",
1567                                  COMPOSE_NEWSGROUPS);
1568         }
1569
1570         if (msginfo->subject && *msginfo->subject) {
1571                 gchar *buf, *buf2, *p;
1572
1573                 buf = g_strdup(msginfo->subject);
1574                 while (!strncasecmp(buf, "Re:", 3)) {
1575                         p = buf + 3;
1576                         while (isspace(*p)) p++;
1577                         memmove(buf, p, strlen(p) + 1);
1578                 }
1579
1580                 buf2 = g_strdup_printf("Re: %s", buf);
1581                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1582                 g_free(buf2);
1583                 g_free(buf);
1584         } else
1585                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
1586
1587         if (!to_all || compose->account->protocol == A_NNTP) return;
1588
1589         from = g_strdup(compose->replyto ? compose->replyto :
1590                         msginfo->from ? msginfo->from : "");
1591         extract_address(from);
1592
1593         cc_list = address_list_append(NULL, msginfo->to);
1594         cc_list = address_list_append(cc_list, compose->cc);
1595
1596         to_table = g_hash_table_new(g_str_hash, g_str_equal);
1597         g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1598         if (compose->account)
1599                 g_hash_table_insert(to_table, compose->account->address,
1600                                     GINT_TO_POINTER(1));
1601
1602         /* remove address on To: and that of current account */
1603         for (cur = cc_list; cur != NULL; ) {
1604                 GSList *next = cur->next;
1605
1606                 if (g_hash_table_lookup(to_table, cur->data) != NULL)
1607                         cc_list = g_slist_remove(cc_list, cur->data);
1608                 else
1609                         g_hash_table_insert(to_table, cur->data, cur);
1610
1611                 cur = next;
1612         }
1613         g_hash_table_destroy(to_table);
1614         g_free(from);
1615
1616         if (cc_list) {
1617                 for (cur = cc_list; cur != NULL; cur = cur->next)
1618                         compose_entry_append(compose, (gchar *)cur->data,
1619                                              COMPOSE_CC);
1620                 slist_free_strings(cc_list);
1621                 g_slist_free(cc_list);
1622         }
1623 }
1624
1625 #define SET_ENTRY(entry, str) \
1626 { \
1627         if (str && *str) \
1628                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
1629 }
1630
1631 #define SET_ADDRESS(type, str) \
1632 { \
1633         if (str && *str) \
1634                 compose_entry_append(compose, str, type); \
1635 }
1636
1637 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1638 {
1639         g_return_if_fail(msginfo != NULL);
1640
1641         SET_ENTRY(subject_entry, msginfo->subject);
1642         SET_ADDRESS(COMPOSE_TO, msginfo->to);
1643         SET_ADDRESS(COMPOSE_CC, compose->cc);
1644         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
1645         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
1646
1647 #if 0 /* NEW COMPOSE GUI */
1648         if (compose->bcc) {
1649                 GtkItemFactory *ifactory;
1650                 GtkWidget *menuitem;
1651
1652                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1653                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Bcc");
1654                 gtk_check_menu_item_set_active
1655                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1656         }
1657         if (compose->replyto) {
1658                 GtkItemFactory *ifactory;
1659                 GtkWidget *menuitem;
1660
1661                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1662                 menuitem = gtk_item_factory_get_item
1663                         (ifactory, "/Message/Reply to");
1664                 gtk_check_menu_item_set_active
1665                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1666         }
1667 #endif
1668 }
1669
1670 #undef SET_ENTRY
1671 #undef SET_ADDRESS
1672
1673 static void compose_exec_sig(Compose *compose, gchar *sigfile)
1674 {
1675         FILE *tmpfp;
1676         pid_t thepid;
1677         gchar *sigtext;
1678         FILE  *sigprg;
1679         gchar  *buf;
1680         size_t buf_len = 128;
1681  
1682         if (strlen(sigfile) < 2)
1683           return;
1684  
1685         sigprg = popen(sigfile+1, "r");
1686         if (sigprg) {
1687
1688                 buf = g_malloc(buf_len);
1689
1690                 if (!buf) {
1691                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1692                         "Unable to insert signature (malloc failed)\n", -1);
1693
1694                         pclose(sigprg);
1695                         return;
1696                 }
1697
1698                 while (!feof(sigprg)) {
1699                         bzero(buf, buf_len);
1700                         fread(buf, buf_len-1, 1, sigprg);
1701                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, buf, -1);
1702                 }
1703
1704                 g_free(buf);
1705                 pclose(sigprg);
1706         }
1707         else
1708         {
1709                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1710                 "Can't exec file: ", -1);
1711                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1712                 sigfile+1, -1);
1713         }
1714 }
1715
1716 static void compose_insert_sig(Compose *compose)
1717 {
1718         gchar *sigfile;
1719
1720         if (compose->account && compose->account->sig_path)
1721                 sigfile = g_strdup(compose->account->sig_path);
1722         else {
1723                 sigfile = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1724                                       DEFAULT_SIGNATURE, NULL);
1725         }
1726
1727         if (!is_file_or_fifo_exist(sigfile) & (sigfile[0] != '|')) {
1728                 g_free(sigfile);
1729                 return;
1730         }
1731
1732         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, "\n\n", 2);
1733         if (prefs_common.sig_sep) {
1734                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1735                                 prefs_common.sig_sep, -1);
1736                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1737                                 "\n", 1);
1738         }
1739
1740         if (sigfile[0] == '|')
1741         {
1742                 compose_exec_sig(compose, sigfile);
1743         }
1744         else
1745         {
1746                 compose_insert_file(compose, sigfile);
1747         }
1748         g_free(sigfile);
1749 }
1750
1751 static void compose_insert_file(Compose *compose, const gchar *file)
1752 {
1753         GtkSText *text = GTK_STEXT(compose->text);
1754         gchar buf[BUFFSIZE];
1755         gint len;
1756         FILE *fp;
1757
1758         g_return_if_fail(file != NULL);
1759
1760         if ((fp = fopen(file, "r")) == NULL) {
1761                 FILE_OP_ERROR(file, "fopen");
1762                 return;
1763         }
1764
1765         gtk_stext_freeze(text);
1766
1767         while (fgets(buf, sizeof(buf), fp) != NULL) {
1768                 /* Strip <CR> if DOS/Windoze file, replace <CR> with <LF> if MAC file */
1769                 len = strlen(buf);
1770                 if (len > 1 && buf[len - 2] == '\r' && buf[len - 1] == '\n') {
1771                         buf[len - 2] = '\n';
1772                         buf[len - 1] = '\0';
1773                 } else {
1774                         while (--len > 0)
1775                                 if (buf[len] == '\r')
1776                                         buf[len] = '\n';
1777                 }
1778
1779                 gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1780         }
1781
1782         gtk_stext_thaw(text);
1783
1784         fclose(fp);
1785 }
1786
1787 static void compose_attach_info(Compose * compose, AttachInfo * ainfo,
1788                                 ContentType cnttype)
1789 {
1790         gchar *text[N_ATTACH_COLS];
1791         gint row;
1792
1793         text[COL_MIMETYPE] = ainfo->content_type;
1794         text[COL_SIZE] = to_human_readable(ainfo->size);
1795         text[COL_NAME] = ainfo->name;
1796
1797         row = gtk_clist_append(GTK_CLIST(compose->attach_clist), text);
1798         gtk_clist_set_row_data(GTK_CLIST(compose->attach_clist), row, ainfo);
1799
1800         if (cnttype != MIME_MESSAGE_RFC822)
1801                 compose_changed_cb(NULL, compose);
1802 }
1803
1804 static void compose_attach_append_with_type(Compose *compose,
1805                                             const gchar *file,
1806                                             const gchar *type,
1807                                             ContentType cnttype)
1808 {
1809         AttachInfo *ainfo;
1810         off_t size;
1811
1812         if (!is_file_exist(file)) {
1813                 g_warning(_("File %s doesn't exist\n"), file);
1814                 return;
1815         }
1816         if ((size = get_file_size(file)) < 0) {
1817                 g_warning(_("Can't get file size of %s\n"), file);
1818                 return;
1819         }
1820         if (size == 0) {
1821                 alertpanel_notice(_("File %s is empty\n"), file);
1822                 return;
1823         }
1824 #if 0 /* NEW COMPOSE GUI */
1825         if (!compose->use_attach) {
1826                 GtkItemFactory *ifactory;
1827                 GtkWidget *menuitem;
1828
1829                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1830                 menuitem = gtk_item_factory_get_item(ifactory,
1831                                                      "/Message/Attach");
1832                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1833                                                TRUE);
1834         }
1835 #endif
1836         ainfo = g_new0(AttachInfo, 1);
1837         ainfo->file = g_strdup(file);
1838
1839         if (cnttype == MIME_MESSAGE_RFC822) {
1840                 ainfo->encoding = ENC_7BIT;
1841                 ainfo->name = g_strdup_printf(_("Message: %s"),
1842                                               g_basename(file));
1843         } else {
1844                 ainfo->encoding = ENC_BASE64;
1845                 ainfo->name = g_strdup(g_basename(file));
1846         }
1847
1848         ainfo->content_type = g_strdup(type);
1849         ainfo->size = size;
1850
1851         compose_attach_info(compose, ainfo, cnttype);
1852 }
1853
1854 static void compose_attach_append(Compose *compose, const gchar *file,
1855                                   ContentType cnttype)
1856 {
1857         AttachInfo *ainfo;
1858         gchar *text[N_ATTACH_COLS];
1859         off_t size;
1860         gint row;
1861
1862         if (!is_file_exist(file)) {
1863                 g_warning(_("File %s doesn't exist\n"), file);
1864                 return;
1865         }
1866         if ((size = get_file_size(file)) < 0) {
1867                 g_warning(_("Can't get file size of %s\n"), file);
1868                 return;
1869         }
1870         if (size == 0) {
1871                 alertpanel_notice(_("File %s is empty\n"), file);
1872                 return;
1873         }
1874 #if 0 /* NEW COMPOSE GUI */
1875         if (!compose->use_attach) {
1876                 GtkItemFactory *ifactory;
1877                 GtkWidget *menuitem;
1878
1879                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1880                 menuitem = gtk_item_factory_get_item(ifactory,
1881                                                      "/Message/Attach");
1882                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1883                                                TRUE);
1884         }
1885 #endif
1886         ainfo = g_new0(AttachInfo, 1);
1887         ainfo->file = g_strdup(file);
1888
1889         if (cnttype == MIME_MESSAGE_RFC822) {
1890                 ainfo->content_type = g_strdup("message/rfc822");
1891                 ainfo->encoding = ENC_7BIT;
1892                 ainfo->name = g_strdup_printf(_("Message: %s"),
1893                                               g_basename(file));
1894         } else {
1895                 ainfo->content_type = procmime_get_mime_type(file);
1896                 if (!ainfo->content_type)
1897                         ainfo->content_type =
1898                                 g_strdup("application/octet-stream");
1899                 ainfo->encoding = ENC_BASE64;
1900                 ainfo->name = g_strdup(g_basename(file));
1901         }
1902         ainfo->size = size;
1903
1904         compose_attach_info(compose, ainfo, cnttype);
1905 }
1906
1907 static void compose_wrap_line(Compose *compose)
1908 {
1909         GtkSText *text = GTK_STEXT(compose->text);
1910         gint ch_len, last_ch_len;
1911         gchar cbuf[MB_LEN_MAX], last_ch;
1912         guint text_len;
1913         guint line_end;
1914         guint quoted;
1915         gint p_start, p_end;
1916         gint line_pos, cur_pos;
1917         gint line_len, cur_len;
1918
1919 #define GET_STEXT(pos)                                                        \
1920         if (text->use_wchar)                                                 \
1921                 ch_len = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, (pos))); \
1922         else {                                                               \
1923                 cbuf[0] = GTK_STEXT_INDEX(text, (pos));                       \
1924                 ch_len = 1;                                                  \
1925         }
1926
1927         gtk_stext_freeze(text);
1928
1929         text_len = gtk_stext_get_length(text);
1930
1931         /* check to see if the point is on the paragraph mark (empty line). */
1932         cur_pos = gtk_stext_get_point(text);
1933         GET_STEXT(cur_pos);
1934         if ((ch_len == 1 && *cbuf == '\n') || cur_pos == text_len) {
1935                 if (cur_pos == 0)
1936                         goto compose_end; /* on the paragraph mark */
1937                 GET_STEXT(cur_pos - 1);
1938                 if (ch_len == 1 && *cbuf == '\n')
1939                         goto compose_end; /* on the paragraph mark */
1940         }
1941
1942         /* find paragraph start. */
1943         line_end = quoted = 0;
1944         for (p_start = cur_pos; p_start >= 0; --p_start) {
1945                 GET_STEXT(p_start);
1946                 if (ch_len == 1 && *cbuf == '\n') {
1947                         if (quoted)
1948                                 goto compose_end; /* quoted part */
1949                         if (line_end) {
1950                                 p_start += 2;
1951                                 break;
1952                         }
1953                         line_end = 1;
1954                 } else {
1955                         if (ch_len == 1 && strchr(">:#", *cbuf))
1956                                 quoted = 1;
1957                         else if (ch_len != 1 || !isspace(*cbuf))
1958                                 quoted = 0;
1959
1960                         line_end = 0;
1961                 }
1962         }
1963         if (p_start < 0)
1964                 p_start = 0;
1965
1966         /* find paragraph end. */
1967         line_end = 0;
1968         for (p_end = cur_pos; p_end < text_len; p_end++) {
1969                 GET_STEXT(p_end);
1970                 if (ch_len == 1 && *cbuf == '\n') {
1971                         if (line_end) {
1972                                 p_end -= 1;
1973                                 break;
1974                         }
1975                         line_end = 1;
1976                 } else {
1977                         if (line_end && ch_len == 1 && strchr(">:#", *cbuf))
1978                                 goto compose_end; /* quoted part */
1979
1980                         line_end = 0;
1981                 }
1982         }
1983         if (p_end >= text_len)
1984                 p_end = text_len;
1985
1986         if (p_start >= p_end)
1987                 goto compose_end;
1988
1989         line_len = cur_len = 0;
1990         last_ch_len = 0;
1991         last_ch = '\0';
1992         line_pos = p_start;
1993         for (cur_pos = p_start; cur_pos < p_end; cur_pos++) {
1994                 guint space = 0;
1995
1996                 GET_STEXT(cur_pos);
1997
1998                 if (ch_len < 0) {
1999                         cbuf[0] = '\0';
2000                         ch_len = 1;
2001                 }
2002
2003                 if (ch_len == 1 && isspace(*cbuf))
2004                         space = 1;
2005
2006                 if (ch_len == 1 && *cbuf == '\n') {
2007                         guint replace = 0;
2008                         if (last_ch_len == 1 && !isspace(last_ch)) {
2009                                 if (cur_pos + 1 < p_end) {
2010                                         GET_STEXT(cur_pos + 1);
2011                                         if (ch_len == 1 && !isspace(*cbuf))
2012                                                 replace = 1;
2013                                 }
2014                         }
2015                         gtk_stext_set_point(text, cur_pos + 1);
2016                         gtk_stext_backward_delete(text, 1);
2017                         if (replace) {
2018                                 gtk_stext_set_point(text, cur_pos);
2019                                 gtk_stext_insert(text, NULL, NULL, NULL, " ", 1);
2020                                 space = 1;
2021                         }
2022                         else {
2023                                 p_end--;
2024                                 cur_pos--;
2025                                 continue;
2026                         }
2027                 }
2028
2029                 last_ch_len = ch_len;
2030                 last_ch = *cbuf;
2031
2032                 if (space) {
2033                         line_pos = cur_pos + 1;
2034                         line_len = cur_len + ch_len;
2035                 }
2036
2037                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2038                     line_len > 0) {
2039                         gint tlen = ch_len;
2040
2041                         GET_STEXT(line_pos - 1);
2042                         if (ch_len == 1 && isspace(*cbuf)) {
2043                                 gtk_stext_set_point(text, line_pos);
2044                                 gtk_stext_backward_delete(text, 1);
2045                                 p_end--;
2046                                 cur_pos--;
2047                                 line_pos--;
2048                                 cur_len--;
2049                                 line_len--;
2050                         }
2051                         ch_len = tlen;
2052
2053                         gtk_stext_set_point(text, line_pos);
2054                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2055                         p_end++;
2056                         cur_pos++;
2057                         line_pos++;
2058                         cur_len = cur_len - line_len + ch_len;
2059                         line_len = 0;
2060                         continue;
2061                 }
2062
2063                 if (ch_len > 1) {
2064                         line_pos = cur_pos + 1;
2065                         line_len = cur_len + ch_len;
2066                 }
2067                 cur_len += ch_len;
2068         }
2069
2070 compose_end:
2071         gtk_stext_thaw(text);
2072
2073 #undef GET_STEXT
2074 }
2075
2076 /* return indent length */
2077 static guint get_indent_length(GtkSText *text, guint start_pos, guint text_len)
2078 {
2079         gint indent_len = 0;
2080         gint i, ch_len;
2081         gchar cbuf[MB_LEN_MAX];
2082
2083         for (i = start_pos; i < text_len; i++) {
2084                 if (text->use_wchar)
2085                         ch_len = wctomb
2086                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, i));
2087                 else {
2088                         cbuf[0] = GTK_STEXT_INDEX(text, i);
2089                         ch_len = 1;
2090                 }
2091                 if (ch_len > 1)
2092                         break;
2093                 /* allow space, tab, > or | */
2094                 if (cbuf[0] != ' ' && cbuf[0] != '\t' &&
2095                     cbuf[0] != '>' && cbuf[0] != '|')
2096                         break;
2097                 indent_len++;
2098         }
2099
2100         return indent_len;
2101 }
2102
2103 /* insert quotation string when line was wrapped */
2104 static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
2105                        guint prev_line_pos, guint text_len,
2106                        gchar *quote_fmt)
2107 {
2108         guint i, ins_len;
2109         gchar ch;
2110
2111         if (indent_len) {
2112                 for (i = 0; i < indent_len; i++) {
2113                         ch = GTK_STEXT_INDEX(text, prev_line_pos + i);
2114                         gtk_stext_insert(text, NULL, NULL, NULL, &ch, 1);
2115                 }
2116                 ins_len = indent_len;
2117         }
2118         else {
2119                 gtk_stext_insert(text, NULL, NULL, NULL, quote_fmt, quote_len);
2120                 ins_len = quote_len;
2121         }
2122
2123         return ins_len;
2124 }
2125
2126 #undef WRAP_DEBUG
2127 #ifdef WRAP_DEBUG
2128 /* Darko: used when I debug wrapping */
2129 void dump_text(GtkSText *text, int pos, int tlen, int breakoncr)
2130 {
2131         int i;
2132         char ch;
2133
2134         printf("%d [", pos);
2135         for (i = pos; i < tlen; i++) {
2136                 ch = GTK_STEXT_INDEX(text, i);
2137                 if (breakoncr && ch == '\n')
2138                         break;
2139                 printf("%c", ch);
2140         }
2141         printf("]\n");
2142 }
2143 #endif
2144
2145 static void compose_wrap_line_all(Compose *compose)
2146 {
2147         GtkSText *text = GTK_STEXT(compose->text);
2148         guint tlen;
2149         guint line_pos = 0, cur_pos = 0, p_pos = 0;
2150         gint line_len = 0, cur_len = 0;
2151         gint ch_len;
2152         gint is_new_line = 1, do_delete = 0;
2153         guint qlen = 0, i_len = 0;
2154         guint linewrap_quote = prefs_common.linewrap_quote;
2155         guint linewrap_len = prefs_common.linewrap_len;
2156         gchar *qfmt = prefs_common.quotemark;
2157         gchar cbuf[MB_LEN_MAX];
2158
2159         gtk_stext_freeze(text);
2160
2161         /* make text buffer contiguous */
2162         /* gtk_stext_compact_buffer(text); */
2163
2164         tlen = gtk_stext_get_length(text);
2165
2166         for (; cur_pos < tlen; cur_pos++) {
2167                 /* mark position of new line - needed for quotation wrap */
2168                 if (linewrap_quote && is_new_line) {
2169                         qlen = gtkut_text_str_compare
2170                                 (text, cur_pos, tlen, qfmt);
2171                         is_new_line = 0;
2172                         if (qlen)
2173                                 i_len = get_indent_length(text, cur_pos, tlen);
2174                         else
2175                                 i_len = 0;
2176                         p_pos = cur_pos;
2177 #ifdef WRAP_DEBUG
2178                         printf("new line i_len=%d qlen=%d p_pos=", i_len, qlen);
2179                         dump_text(text, p_pos, tlen, 1);
2180 #endif
2181                 }
2182
2183                 /* get character(s) at current position */
2184                 if (text->use_wchar)
2185                         ch_len = wctomb
2186                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2187                 else {
2188                         cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
2189                         ch_len = 1;
2190                 }
2191
2192                 /* fix line length for tabs */
2193                 if (ch_len == 1 && *cbuf == '\t') {
2194                         guint tab_width = text->default_tab_width;
2195                         guint tab_offset = line_len % tab_width;
2196
2197 #ifdef WRAP_DEBUG
2198                         printf("found tab at pos=%d line_len=%d ", cur_pos,
2199                                 line_len);
2200 #endif
2201                         if (tab_offset) {
2202                                 line_len += tab_width - tab_offset - 1;
2203                                 cur_len = line_len;
2204                         }
2205 #ifdef WRAP_DEBUG
2206                         printf("new_len=%d\n", line_len);
2207 #endif
2208                 }
2209
2210                 /* we have encountered line break */
2211                 if (ch_len == 1 && *cbuf == '\n') {
2212                         gint clen;
2213                         guint ilen;
2214                         gchar cb[MB_CUR_MAX];
2215
2216 #ifdef WRAP_DEBUG
2217                         printf("found CR at %d next line is ", cur_pos);
2218                         dump_text(text, cur_pos + 1, tlen, 1);
2219 #endif
2220                         /* if it's just quotation + newline skip it */
2221                         if (i_len && (cur_pos + 1 < tlen)) {
2222                                 /* check if text at new line matches indent */
2223                                 ilen =  gtkut_text_str_compare_n
2224                                         (text, cur_pos + 1, p_pos, i_len, tlen);
2225                                 if (cur_pos + ilen < tlen) {
2226                                         if (text->use_wchar)
2227                                                 clen = wctomb(cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos + ilen + 1));
2228                                         else {
2229                                                 cb[0] = GTK_STEXT_INDEX(text,
2230                                                             cur_pos + ilen + 1);
2231                                                 clen = 1;
2232                                         }
2233                                         /* no need to join the lines */
2234                                         if ((clen == 1) && (cb[0] == '\n'))
2235                                                 do_delete = 0;
2236                                 }
2237                         }
2238                         /* if it's just newline skip it */
2239                         else if (do_delete && (cur_pos + 1 < tlen)) {
2240                                 if (text->use_wchar)
2241                                         clen = wctomb(cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos + 1));
2242                                 else {
2243                                         cb[0] = GTK_STEXT_INDEX(text,
2244                                                                 cur_pos + 1);
2245                                         clen = 1;
2246                                 }
2247                                 /* no need to join the lines */
2248                                 if ((clen == 1) && (cb[0] == '\n'))
2249                                         do_delete = 0;
2250                         }
2251
2252 #ifdef WRAP_DEBUG
2253                         printf("qlen=%d l_len=%d wrap_len=%d do_del=%d\n",
2254                                 qlen, line_len, linewrap_len, do_delete);
2255 #endif
2256                         /* should we delete to perform smart wrapping */
2257                         if (line_len < linewrap_len && do_delete) {
2258                                 /* get rid of newline */
2259                                 gtk_stext_set_point(text, cur_pos);
2260                                 gtk_stext_forward_delete(text, 1);
2261                                 tlen--;
2262
2263                                 /* if text starts with quote fmt or with
2264                                    indent string, delete them */
2265                                 if (i_len) {
2266                                         ilen =  gtkut_text_str_compare_n
2267                                                 (text, cur_pos, p_pos, i_len,
2268                                                  tlen);
2269                                         if (ilen) {
2270                                                 gtk_stext_forward_delete(text,
2271                                                                          ilen);
2272                                                 tlen -= ilen;
2273                                         }
2274                                 } else if (qlen) {
2275                                         if (gtkut_text_str_compare
2276                                             (text, cur_pos, tlen, qfmt)) {
2277                                                 gtk_stext_forward_delete
2278                                                         (text, qlen);
2279                                                 tlen -= qlen;
2280                                         }
2281                                 }
2282
2283                                 if (text->use_wchar)
2284                                         clen = wctomb
2285                                                 (cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2286                                 else {
2287                                         cb[0] = GTK_STEXT_INDEX(text, cur_pos);
2288                                         clen = 1;
2289                                 }
2290
2291                                 /* insert space if it's alphanumeric */
2292                                 if ((cur_pos != line_pos) &&
2293                                     ((clen > 1) || isalnum(cb[0]))) {
2294                                         gtk_stext_insert(text, NULL, NULL,
2295                                                         NULL, " ", 1);
2296                                         /* gtk_text_compact_buffer(text); */
2297                                         tlen++;
2298                                 }
2299
2300                                 /* and start over with current line */
2301                                 cur_pos = p_pos - 1;
2302                                 line_pos = cur_pos;
2303                                 line_len = cur_len = 0;
2304                                 qlen = 0;
2305                                 do_delete = 0;
2306                                 is_new_line = 1;
2307 #ifdef WRAP_DEBUG
2308                                 printf("after delete l_pos=");
2309                                 dump_text(text, line_pos, tlen, 1);
2310 #endif
2311                                 continue;
2312                         }
2313
2314                         /* mark new line beginning */
2315                         line_pos = cur_pos + 1;
2316                         line_len = cur_len = 0;
2317                         qlen = 0;
2318                         do_delete = 0;
2319                         is_new_line = 1;
2320                         continue;
2321                 }
2322
2323                 if (ch_len < 0) {
2324                         cbuf[0] = '\0';
2325                         ch_len = 1;
2326                 }
2327
2328                 /* possible line break */
2329                 if (ch_len == 1 && isspace(*cbuf)) {
2330                         line_pos = cur_pos + 1;
2331                         line_len = cur_len + ch_len;
2332                 }
2333
2334                 /* are we over wrapping length set in preferences ? */
2335                 if (cur_len + ch_len > linewrap_len) {
2336                         gint clen;
2337
2338 #ifdef WRAP_DEBUG
2339                         printf("should wrap cur_pos=%d ", cur_pos);
2340                         dump_text(text, p_pos, tlen, 1);
2341                         dump_text(text, line_pos, tlen, 1);
2342 #endif
2343                         /* force wrapping if it is one long word but not URL */
2344                         if (p_pos + i_len == line_pos)
2345                                 if (!gtkut_text_is_uri_string
2346                                     (text, line_pos, tlen))
2347                                         line_pos = cur_pos - 1;
2348 #ifdef WRAP_DEBUG
2349                         printf("new line_pos=%d\n", line_pos);
2350 #endif
2351
2352                         /* if next character is space delete it */
2353                         if (text->use_wchar)
2354                                 clen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
2355                         else {
2356                                 cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
2357                                 clen = 1;
2358                         }
2359                         if (clen == 1 && isspace(*cbuf)) {
2360                                 if (p_pos + i_len != line_pos ||
2361                                     !gtkut_text_is_uri_string
2362                                         (text, line_pos, tlen)) {
2363                                         gtk_stext_set_point(text, line_pos);
2364                                         gtk_stext_backward_delete(text, 1);
2365                                         tlen--;
2366                                         cur_pos--;
2367                                         line_pos--;
2368                                         cur_len--;
2369                                         line_len--;
2370                                 }
2371                         }
2372
2373                         /* if it is URL at beginning of line don't wrap */
2374                         if (p_pos + i_len == line_pos &&
2375                             gtkut_text_is_uri_string(text, line_pos, tlen)) {
2376 #ifdef WRAP_DEBUG
2377                                 printf("found URL at ");
2378                                 dump_text(text, line_pos, tlen, 1);
2379 #endif
2380                                 continue;
2381                         }
2382
2383                         /* insert CR */
2384                         gtk_stext_set_point(text, line_pos);
2385                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2386                         /* gtk_stext_compact_buffer(text); */
2387                         tlen++;
2388                         line_pos++;
2389                         /* for loop will increase it */
2390                         cur_pos = line_pos - 1;
2391                         /* start over with current line */
2392                         is_new_line = 1;
2393                         line_len = 0;
2394                         cur_len = 0;
2395                         do_delete = 1;
2396 #ifdef WRAP_DEBUG
2397                         printf("after CR insert ");
2398                         dump_text(text, line_pos, tlen, 1);
2399                         dump_text(text, cur_pos, tlen, 1);
2400 #endif
2401
2402                         /* should we insert quotation ? */
2403                         if (linewrap_quote && qlen) {
2404                                 /* only if line is not already quoted  */
2405                                 if (!gtkut_text_str_compare
2406                                         (text, line_pos, tlen, qfmt)) {
2407                                         guint ins_len;
2408
2409                                         if (line_pos - p_pos > i_len) {
2410                                                 ins_len = ins_quote
2411                                                         (text, qlen, i_len,
2412                                                          p_pos, tlen, qfmt);
2413
2414                                                 /* gtk_stext_compact_buffer(text); */
2415                                                 tlen += ins_len;
2416                                         }
2417 #ifdef WRAP_DEBUG
2418                                         printf("after quote insert ");
2419                                         dump_text(text, line_pos, tlen, 1);
2420 #endif
2421                                 }
2422                         }
2423                         continue;
2424                 }
2425
2426                 if (ch_len > 1) {
2427                         line_pos = cur_pos + 1;
2428                         line_len = cur_len + ch_len;
2429                 }
2430                 /* advance to next character in buffer */
2431                 cur_len += ch_len;
2432         }
2433
2434         gtk_stext_thaw(text);
2435 }
2436
2437 #if 0
2438 static void compose_wrap_line_all(Compose *compose)
2439 {
2440         GtkText *text = GTK_STEXT(compose->text);
2441         guint text_len;
2442         guint line_pos = 0, cur_pos = 0;
2443         gint line_len = 0, cur_len = 0;
2444         gint ch_len;
2445         gchar cbuf[MB_LEN_MAX];
2446
2447         gtk_stext_freeze(text);
2448
2449         text_len = gtk_stext_get_length(text);
2450
2451         for (; cur_pos < text_len; cur_pos++) {
2452                 if (text->use_wchar)
2453                         ch_len = wctomb
2454                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2455                 else {
2456                         cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
2457                         ch_len = 1;
2458                 }
2459
2460                 if (ch_len == 1 && *cbuf == '\n') {
2461                         line_pos = cur_pos + 1;
2462                         line_len = cur_len = 0;
2463                         continue;
2464                 }
2465
2466                 if (ch_len < 0) {
2467                         cbuf[0] = '\0';
2468                         ch_len = 1;
2469                 }
2470
2471                 if (ch_len == 1 && isspace(*cbuf)) {
2472                         line_pos = cur_pos + 1;
2473                         line_len = cur_len + ch_len;
2474                 }
2475
2476                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2477                     line_len > 0) {
2478                         gint tlen;
2479
2480                         if (text->use_wchar)
2481                                 tlen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
2482                         else {
2483                                 cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
2484                                 tlen = 1;
2485                         }
2486                         if (tlen == 1 && isspace(*cbuf)) {
2487                                 gtk_stext_set_point(text, line_pos);
2488                                 gtk_stext_backward_delete(text, 1);
2489                                 text_len--;
2490                                 cur_pos--;
2491                                 line_pos--;
2492                                 cur_len--;
2493                                 line_len--;
2494                         }
2495
2496                         gtk_stext_set_point(text, line_pos);
2497                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2498                         text_len++;
2499                         cur_pos++;
2500                         line_pos++;
2501                         cur_len = cur_len - line_len + ch_len;
2502                         line_len = 0;
2503                         continue;
2504                 }
2505
2506                 if (ch_len > 1) {
2507                         line_pos = cur_pos + 1;
2508                         line_len = cur_len + ch_len;
2509                 }
2510                 cur_len += ch_len;
2511         }
2512
2513         gtk_stext_thaw(text);
2514 }
2515 #endif
2516
2517 static void compose_set_title(Compose *compose)
2518 {
2519         gchar *str;
2520         gchar *edited;
2521
2522         edited = compose->modified ? _(" [Edited]") : "";
2523         if (compose->account && compose->account->address)
2524                 str = g_strdup_printf(_("%s - Compose message%s"),
2525                                       compose->account->address, edited);
2526         else
2527                 str = g_strdup_printf(_("Compose message%s"), edited);
2528         gtk_window_set_title(GTK_WINDOW(compose->window), str);
2529         g_free(str);
2530 }
2531
2532 /**
2533  * compose_current_mail_account:
2534  * 
2535  * Find a current mail account (the currently selected account, or the
2536  * default account, if a news account is currently selected).  If a
2537  * mail account cannot be found, display an error message.
2538  * 
2539  * Return value: Mail account, or NULL if not found.
2540  **/
2541 static PrefsAccount *
2542 compose_current_mail_account(void)
2543 {
2544         PrefsAccount *ac;
2545
2546         if (cur_account && cur_account->protocol != A_NNTP)
2547                 ac = cur_account;
2548         else {
2549                 ac = account_get_default();
2550                 if (!ac || ac->protocol == A_NNTP) {
2551                         alertpanel_error(_("Account for sending mail is not specified.\n"
2552                                            "Please select a mail account before sending."));
2553                         return NULL;
2554                 }
2555         }
2556         return ac;
2557 }
2558
2559 gboolean compose_check_for_valid_recipient(Compose *compose) {
2560         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
2561         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
2562         gboolean recipient_found = FALSE;
2563         GSList *list;
2564         gchar **strptr;
2565
2566         /* free to and newsgroup list */
2567         slist_free_strings(compose->to_list);
2568         g_slist_free(compose->to_list);
2569         compose->to_list = NULL;
2570                         
2571         slist_free_strings(compose->newsgroup_list);
2572         g_slist_free(compose->newsgroup_list);
2573         compose->newsgroup_list = NULL;
2574
2575         /* search header entries for to and newsgroup entries */
2576         for(list = compose->header_list; list; list = list->next) {
2577                 gchar *header;
2578                 gchar *entry;
2579                 header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry));
2580                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
2581                 g_strstrip(entry);
2582                 if(entry[0] != '\0') {
2583                         for(strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
2584                                 if(!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
2585                                         compose->to_list = address_list_append(compose->to_list, entry);
2586                                         recipient_found = TRUE;
2587                                 }
2588                         }
2589                         for(strptr = recipient_headers_news; *strptr != NULL; strptr++) {
2590                                 if(!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
2591                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
2592                                         recipient_found = TRUE;
2593                                 }
2594                         }
2595                 }
2596                 g_free(entry);
2597         }
2598         return recipient_found;
2599 }
2600
2601 gint compose_send(Compose *compose)
2602 {
2603         gint msgnum;
2604         FolderItem *folder;
2605         gint val;
2606
2607         val = compose_queue(compose, &msgnum, &folder);
2608         if (val) {
2609                 alertpanel_error(_("Could not queue message for sending"));
2610                 return -1;
2611         }
2612         
2613         val = procmsg_send_message_queue(folder_item_fetch_msg(folder, msgnum));
2614         if(!val) {
2615                 folder_item_remove_msg(folder, msgnum);
2616                 folderview_update_item(folder, TRUE);
2617         }
2618
2619         return val;
2620 }
2621
2622 #if 0 /* compose restructure */
2623 gint compose_send(Compose *compose)
2624 {
2625         gchar tmp[MAXPATHLEN + 1];
2626         gint ok = 0;
2627         static gboolean lock = FALSE;
2628
2629         if (lock) return 1;
2630
2631         g_return_val_if_fail(compose->account != NULL, -1);
2632         g_return_val_if_fail(compose->orig_account != NULL, -1);
2633
2634         lock = TRUE;
2635
2636         if(!compose_check_for_valid_recipient(compose)) {
2637                 alertpanel_error(_("Recipient is not specified."));
2638                 lock = FALSE;
2639                 return 1;
2640         }
2641
2642         /* write to temporary file */
2643         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
2644                    get_rc_dir(), G_DIR_SEPARATOR, (gint)compose);
2645
2646         if (prefs_common.linewrap_at_send)
2647                 compose_wrap_line_all(compose);
2648
2649         if (compose_write_to_file(compose, tmp, FALSE) < 0) {
2650                 lock = FALSE;
2651                 return -1;
2652         }
2653
2654         if (!compose->to_list && !compose->newsgroup_list) {
2655                 g_warning(_("can't get recipient list."));
2656                 unlink(tmp);
2657                 lock = FALSE;
2658                 return -1;
2659         }
2660
2661         if (compose->to_list) {
2662                 PrefsAccount *ac;
2663
2664 #if 0 /* NEW COMPOSE GUI */
2665                 if (compose->account->protocol != A_NNTP)
2666                         ac = compose->account;
2667                 else if (compose->orig_account->protocol != A_NNTP)
2668                         ac = compose->orig_account;
2669                 else if (cur_account && cur_account->protocol != A_NNTP)
2670                         ac = cur_account;
2671                 else {
2672                         ac = compose_current_mail_account();
2673                         if (!ac) {
2674                                 unlink(tmp);
2675                                 lock = FALSE;
2676                                 return -1;
2677                         }
2678                 }
2679 #endif
2680                 ac = compose->account;
2681
2682                 ok = send_message(tmp, ac, compose->to_list);
2683                 statusbar_pop_all();
2684         }
2685
2686         if (ok == 0 && compose->newsgroup_list) {
2687                 Folder *folder;
2688
2689                 if (compose->account->protocol == A_NNTP)
2690                         folder = FOLDER(compose->account->folder);
2691                 else
2692                         folder = FOLDER(compose->orig_account->folder);
2693
2694                 ok = news_post(folder, tmp);
2695                 if (ok < 0) {
2696                         alertpanel_error(_("Error occurred while posting the message to %s ."),
2697                                          compose->account->nntp_server);
2698                         unlink(tmp);
2699                         lock = FALSE;
2700                         return -1;
2701                 }
2702         }
2703
2704         /* queue message if failed to send */
2705         if (ok < 0) {
2706                 if (prefs_common.queue_msg) {
2707                         AlertValue val;
2708
2709                         val = alertpanel
2710                                 (_("Queueing"),
2711                                  _("Error occurred while sending the message.\n"
2712                                    "Put this message into queue folder?"),
2713                                  _("OK"), _("Cancel"), NULL);
2714                         if (G_ALERTDEFAULT == val) {
2715                                 ok = compose_queue(compose, tmp);
2716                                 if (ok < 0)
2717                                         alertpanel_error(_("Can't queue the message."));
2718                         }
2719                 } else
2720                         alertpanel_error(_("Error occurred while sending the message."));
2721         } else {
2722                 if (compose->mode == COMPOSE_REEDIT) {
2723                         compose_remove_reedit_target(compose);
2724                         if (compose->targetinfo)
2725                                 folderview_update_item
2726                                         (compose->targetinfo->folder, TRUE);
2727                 }
2728         }
2729
2730         /* save message to outbox */
2731         if (ok == 0 && prefs_common.savemsg) {
2732                 if (compose_save_to_outbox(compose, tmp) < 0)
2733                         alertpanel_error
2734                                 (_("Can't save the message to outbox."));
2735         }
2736
2737         unlink(tmp);
2738         lock = FALSE;
2739         return ok;
2740 }
2741 #endif
2742
2743 static gboolean compose_use_attach(Compose *compose) {
2744     return(gtk_clist_get_row_data(GTK_CLIST(compose->attach_clist), 0) != NULL);
2745 }
2746
2747 static gint compose_bounce_write_headers_from_headerlist(Compose *compose, 
2748                                                          FILE *fp)
2749 {
2750         gchar buf[BUFFSIZE];
2751         gchar *str;
2752         gboolean first_address;
2753         GSList *list;
2754         ComposeHeaderEntry *headerentry;
2755         gchar *headerentryname;
2756         gchar *header_w_colon;
2757         gchar *cc_hdr;
2758         gchar *to_hdr;
2759
2760         debug_print(_("Writing bounce header\n"));
2761
2762         header_w_colon = g_strconcat("To:", NULL);
2763         to_hdr = (prefs_common.trans_hdr ? gettext(header_w_colon) : header_w_colon);
2764         header_w_colon = g_strconcat("Cc:", NULL);
2765         cc_hdr = (prefs_common.trans_hdr ? gettext(header_w_colon) : header_w_colon);
2766         
2767         first_address = TRUE;
2768         for(list = compose->header_list; list; list = list->next) {
2769                 headerentry = ((ComposeHeaderEntry *)list->data);
2770                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
2771
2772                 if(g_strcasecmp(headerentryname, cc_hdr) == 0 
2773                    || g_strcasecmp(headerentryname, to_hdr) == 0) {
2774                         str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
2775                         Xstrdup_a(str, str, return -1);
2776                         g_strstrip(str);
2777                         if(str[0] != '\0') {
2778                                 compose_convert_header
2779                                         (buf, sizeof(buf), str,
2780                                         strlen("Resent-To") + 2);
2781                                 if(first_address) {
2782                                         fprintf(fp, "Resent-To: ");
2783                                         first_address = FALSE;
2784                                 } else {
2785                                         fprintf(fp, ",");
2786                                 }
2787                                 fprintf(fp, "%s", buf);
2788                         }
2789                 }
2790         }
2791         /* if(!first_address) { */
2792         fprintf(fp, "\n");
2793         /* } */
2794
2795         return(0);
2796 }
2797
2798 static gint compose_bounce_write_headers(Compose *compose, FILE *fp)
2799 {
2800         gchar buf[BUFFSIZE];
2801         gchar *str;
2802         /* struct utsname utsbuf; */
2803
2804         g_return_val_if_fail(fp != NULL, -1);
2805         g_return_val_if_fail(compose->account != NULL, -1);
2806         g_return_val_if_fail(compose->account->address != NULL, -1);
2807
2808         /* Date */
2809         get_rfc822_date(buf, sizeof(buf));
2810         fprintf(fp, "Resent-Date: %s\n", buf);
2811
2812         /* From */
2813         if (compose->account->name && *compose->account->name) {
2814                 compose_convert_header
2815                         (buf, sizeof(buf), compose->account->name,
2816                          strlen("From: "));
2817                 fprintf(fp, "Resent-From: %s <%s>\n",
2818                         buf, compose->account->address);
2819         } else
2820                 fprintf(fp, "Resent-From: %s\n", compose->account->address);
2821
2822         /* To */
2823         compose_bounce_write_headers_from_headerlist(compose, fp);
2824
2825         /* separator between header and body */
2826         fputs("\n", fp);
2827
2828         return 0;
2829 }
2830
2831 static gint compose_bounce_write_to_file(Compose *compose, const gchar *file)
2832 {
2833         FILE *fp;
2834         FILE *fdest;
2835         size_t len;
2836         gchar buf[BUFFSIZE];
2837
2838         if ((fp = fopen(compose->bounce_filename, "r")) == NULL) {
2839                 FILE_OP_ERROR(file, "fopen");
2840                 return -1;
2841         }
2842
2843         if ((fdest = fopen(file, "a+")) == NULL) {
2844                 FILE_OP_ERROR(file, "fopen");
2845                 fclose(fp);
2846                 return -1;
2847         }
2848
2849         /* chmod for security */
2850         if (change_file_mode_rw(fdest, file) < 0) {
2851                 FILE_OP_ERROR(file, "chmod");
2852                 g_warning(_("can't change file mode\n"));
2853         }
2854
2855         while (procheader_get_unfolded_line(buf, sizeof(buf), fp)) {
2856                 /* should filter returnpath, delivered-to */
2857                 if ((g_strncasecmp(buf, "Return-Path:",
2858                                    strlen("Return-Path:")) == 0)
2859                     || (g_strncasecmp(buf, "Delivered-To:",
2860                                       strlen("Delivered-To:")) == 0))
2861                         continue;
2862                 if (fputs(buf, fdest) == -1)
2863                         goto error;
2864                 if (fputs("\n", fdest) == -1)
2865                         goto error;
2866         }
2867
2868         compose_bounce_write_headers(compose, fdest);
2869
2870         while ((len = fread(buf, sizeof(gchar), BUFFSIZE, fp)) > 0) {
2871                 if (fwrite(buf, sizeof(gchar), len, fdest) == -1)
2872                         goto error;
2873         }
2874
2875         fclose(fdest);
2876         fclose(fp);
2877
2878         return 0;
2879  error:
2880         fclose(fdest);
2881         fclose(fp);
2882
2883         return -1;
2884 }
2885
2886 static gint compose_write_to_file(Compose *compose, const gchar *file,
2887                                   gboolean is_draft)
2888 {
2889         FILE *fp;
2890         size_t len;
2891         gchar *chars;
2892         gchar *buf;
2893         const gchar *out_codeset;
2894         EncodingType encoding;
2895
2896         if ((fp = fopen(file, "a+")) == NULL) {
2897                 FILE_OP_ERROR(file, "fopen");
2898                 return -1;
2899         }
2900
2901         /* chmod for security */
2902         if (change_file_mode_rw(fp, file) < 0) {
2903                 FILE_OP_ERROR(file, "chmod");
2904                 g_warning(_("can't change file mode\n"));
2905         }
2906
2907         /* get all composed text */
2908         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
2909         len = strlen(chars);
2910         if (is_ascii_str(chars)) {
2911                 buf = g_strdup(chars);
2912                 out_codeset = CS_US_ASCII;
2913                 encoding = ENC_7BIT;
2914         } else {
2915                 const gchar *src_codeset;
2916
2917                 out_codeset = conv_get_outgoing_charset_str();
2918                 if (!strcasecmp(out_codeset, CS_US_ASCII))
2919                         out_codeset = CS_ISO_8859_1;
2920                 encoding = procmime_get_encoding_for_charset(out_codeset);
2921
2922                 src_codeset = conv_get_current_charset_str();
2923                 /* if current encoding is US-ASCII, set it the same as
2924                    outgoing one to prevent code conversion failure */
2925                 if (!strcasecmp(src_codeset, CS_US_ASCII))
2926                         src_codeset = out_codeset;
2927
2928                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
2929                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
2930
2931                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
2932                 if (!buf) {
2933                         g_free(chars);
2934                         fclose(fp);
2935                         unlink(file);
2936                         alertpanel_error(_("Can't convert the codeset of the message."));
2937                         return -1;
2938                 }
2939         }
2940         g_free(chars);
2941
2942         /* write headers */
2943         if (compose_write_headers
2944                 (compose, fp, out_codeset, encoding, is_draft) < 0) {
2945                 g_warning(_("can't write headers\n"));
2946                 fclose(fp);
2947                 /* unlink(file); */
2948                 g_free(buf);
2949                 return -1;
2950         }
2951
2952         if (compose_use_attach(compose)) {
2953 #if USE_GPGME
2954             /* This prolog message is ignored by mime software and
2955              * because it would make our signing/encryption task
2956              * tougher, we don't emit it in that case */
2957             if (!compose->use_signing && !compose->use_encryption)
2958 #endif
2959                 fputs("This is a multi-part message in MIME format.\n", fp);
2960
2961                 fprintf(fp, "\n--%s\n", compose->boundary);
2962                 fprintf(fp, "Content-Type: text/plain; charset=%s\n",
2963                         out_codeset);
2964                 fprintf(fp, "Content-Transfer-Encoding: %s\n",
2965                         procmime_get_encoding_str(encoding));
2966                 fputc('\n', fp);
2967         }
2968
2969         /* write body */
2970         len = strlen(buf);
2971         if (encoding == ENC_BASE64) {
2972                 gchar outbuf[B64_BUFFSIZE];
2973                 gint i, l;
2974
2975                 for (i = 0; i < len; i += B64_LINE_SIZE) {
2976                         l = MIN(B64_LINE_SIZE, len - i);
2977                         to64frombits(outbuf, buf + i, l);
2978                         fputs(outbuf, fp);
2979                         fputc('\n', fp);
2980                 }
2981         } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
2982                 FILE_OP_ERROR(file, "fwrite");
2983                 fclose(fp);
2984                 unlink(file);
2985                 g_free(buf);
2986                 return -1;
2987         }
2988         g_free(buf);
2989
2990         if (compose_use_attach(compose))
2991                 compose_write_attach(compose, fp);
2992
2993         if (fclose(fp) == EOF) {
2994                 FILE_OP_ERROR(file, "fclose");
2995                 unlink(file);
2996                 return -1;
2997         }
2998
2999 #if USE_GPGME
3000         if (compose->use_signing) {
3001                 if (rfc2015_sign(file, compose->account) < 0) {
3002                         unlink(file);
3003                         return -1;
3004                 }
3005         }
3006         if (compose->use_encryption) {
3007                 if (rfc2015_encrypt(file, compose->to_list) < 0) {
3008                         unlink(file);
3009                         return -1;
3010                 }
3011         }
3012 #endif /* USE_GPGME */
3013
3014         return 0;
3015 }
3016
3017 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
3018 {
3019         FILE *fp;
3020         size_t len;
3021         gchar *chars;
3022
3023         if ((fp = fopen(file, "w")) == NULL) {
3024                 FILE_OP_ERROR(file, "fopen");
3025                 return -1;
3026         }
3027
3028         /* chmod for security */
3029         if (change_file_mode_rw(fp, file) < 0) {
3030                 FILE_OP_ERROR(file, "chmod");
3031                 g_warning(_("can't change file mode\n"));
3032         }
3033
3034         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
3035
3036         /* write body */
3037         len = strlen(chars);
3038         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
3039                 FILE_OP_ERROR(file, "fwrite");
3040                 g_free(chars);
3041                 fclose(fp);
3042                 unlink(file);
3043                 return -1;
3044         }
3045
3046         g_free(chars);
3047
3048         if (fclose(fp) == EOF) {
3049                 FILE_OP_ERROR(file, "fclose");
3050                 unlink(file);
3051                 return -1;
3052         }
3053         return 0;
3054 }
3055
3056 static gint compose_save_to_outbox(Compose *compose, const gchar *file)
3057 {
3058         FolderItem *outbox;
3059         gchar *path;
3060         gint num;
3061         FILE *fp;
3062
3063         debug_print(_("saving sent message...\n"));
3064
3065         outbox = folder_get_default_outbox();
3066         path = folder_item_get_path(outbox);
3067         if (!is_dir_exist(path))
3068                 make_dir_hier(path);
3069
3070         folder_item_scan(outbox);
3071         if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
3072                 g_free(path);
3073                 g_warning(_("can't save message\n"));
3074                 return -1;
3075         }
3076
3077         if ((fp = procmsg_open_mark_file(path, TRUE)) == NULL)
3078                 g_warning(_("can't open mark file\n"));
3079         else {
3080                 MsgInfo newmsginfo;
3081
3082                 newmsginfo.msgnum = num;
3083                 newmsginfo.flags.perm_flags = 0;
3084                 newmsginfo.flags.tmp_flags = 0;
3085                 procmsg_write_flags(&newmsginfo, fp);
3086                 fclose(fp);
3087         }
3088         g_free(path);
3089
3090         return 0;
3091 }
3092
3093 static gint compose_remove_reedit_target(Compose *compose)
3094 {
3095         FolderItem *item;
3096         MsgInfo *msginfo = compose->targetinfo;
3097
3098         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
3099         if (!msginfo) return -1;
3100
3101         item = msginfo->folder;
3102         g_return_val_if_fail(item != NULL, -1);
3103
3104         folder_item_scan(item);
3105         if (procmsg_msg_exist(msginfo) &&
3106             (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
3107                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
3108                         g_warning(_("can't remove the old message\n"));
3109                         return -1;
3110                 }
3111         }
3112
3113         return 0;
3114 }
3115
3116
3117 static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
3118 {
3119         FolderItem *queue;
3120         gchar *tmpfilename, *queue_path;
3121         FILE *fp, *src_fp;
3122         GSList *cur;
3123         gchar buf[BUFFSIZE];
3124         gint num;
3125         static gboolean lock = FALSE;
3126         PrefsAccount *mailac = NULL, *newsac = NULL;
3127         
3128         debug_print(_("queueing message...\n"));
3129         g_return_val_if_fail(compose->account != NULL, -1);
3130         g_return_val_if_fail(compose->orig_account != NULL, -1);
3131
3132         lock = TRUE;
3133         
3134         if(!compose_check_for_valid_recipient(compose)) {
3135                 alertpanel_error(_("Recipient is not specified."));
3136                 lock = FALSE;
3137                 return -1;
3138         }
3139                                                                         
3140         if (!compose->to_list && !compose->newsgroup_list) {
3141                 g_warning(_("can't get recipient list."));
3142                 lock = FALSE;
3143                 return -1;
3144         }
3145
3146         if (prefs_common.linewrap_at_send)
3147                 compose_wrap_line_all(compose);
3148                         
3149         /* write to temporary file */
3150         tmpfilename = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
3151                                       G_DIR_SEPARATOR, (gint)compose);
3152         if ((fp = fopen(tmpfilename, "w")) == NULL) {
3153                 FILE_OP_ERROR(tmpfilename, "fopen");
3154                 g_free(tmpfilename);
3155                 return -1;
3156         }
3157         if (change_file_mode_rw(fp, tmpfilename) < 0) {
3158                 FILE_OP_ERROR(tmpfilename, "chmod");
3159                 g_warning(_("can't change file mode\n"));
3160         }
3161
3162         if(compose->to_list) {
3163                 if (compose->account->protocol != A_NNTP)
3164                         mailac = compose->account;
3165                 else if (compose->orig_account->protocol != A_NNTP)
3166                         mailac = compose->orig_account;
3167                 else if (cur_account && cur_account->protocol != A_NNTP)
3168                         mailac = cur_account;
3169                 else if (!(mailac = compose_current_mail_account())) {
3170                         unlink(tmpfilename);
3171                         lock = FALSE;
3172                         alertpanel_error(_("No account for sending mails available!"));
3173                         return -1;
3174                 }
3175         }
3176
3177         if(compose->newsgroup_list) {
3178                 if (compose->account->protocol == A_NNTP)
3179                         newsac = compose->account;
3180                 else if(!(newsac = compose->orig_account) || (newsac->protocol != A_NNTP)) {
3181                         unlink(tmpfilename);
3182                         lock = FALSE;
3183                         alertpanel_error(_("No account for posting news available!"));
3184                         return -1;
3185                 }                       
3186         }
3187
3188         /* queueing variables */
3189         fprintf(fp, "AF:\n");
3190         fprintf(fp, "NF:0\n");
3191         fprintf(fp, "PS:10\n");
3192         fprintf(fp, "SRH:1\n");
3193         fprintf(fp, "SFN:\n");
3194         fprintf(fp, "DSR:\n");
3195         if (compose->msgid)
3196                 fprintf(fp, "MID:<%s>\n", compose->msgid);
3197         else
3198                 fprintf(fp, "MID:\n");
3199         fprintf(fp, "CFG:\n");
3200         fprintf(fp, "PT:0\n");
3201         fprintf(fp, "S:%s\n", compose->account->address);
3202         fprintf(fp, "RQ:\n");
3203         if (mailac)
3204                 fprintf(fp, "SSV:%s\n", mailac->smtp_server);
3205         else
3206                 fprintf(fp, "SSV:\n");
3207         if (newsac)
3208                 fprintf(fp, "NSV:%s\n", newsac->nntp_server);
3209         else
3210                 fprintf(fp, "NSV:\n");
3211         fprintf(fp, "SSH:\n");
3212         /* write recepient list */
3213         fprintf(fp, "R:");
3214         if(compose->to_list) {
3215                 fprintf(fp, "<%s>", (gchar *)compose->to_list->data);
3216                 for (cur = compose->to_list->next; cur != NULL; cur = cur->next)
3217                         fprintf(fp, ",<%s>", (gchar *)cur->data);
3218         }
3219         fprintf(fp, "\n");
3220         /* write newsgroup list */
3221         fprintf(fp, "NG:");
3222         if(compose->newsgroup_list) {
3223                 fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data);
3224                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
3225                         fprintf(fp, ",%s", (gchar *)cur->data);
3226         }
3227         fprintf(fp, "\n");
3228         /* Sylpheed account IDs */
3229         if(mailac) {
3230                 fprintf(fp, "MAID:%d\n", mailac->account_id);
3231         }
3232         if(newsac) {
3233                 fprintf(fp, "NAID:%d\n", newsac->account_id);
3234         }
3235         fprintf(fp, "\n");
3236         fclose(fp);
3237
3238         if (compose->bounce_filename != NULL) {
3239                 if (compose_bounce_write_to_file(compose, tmpfilename) < 0) {
3240                         unlink(tmpfilename);
3241                         lock = FALSE;
3242                         return -1;
3243                 }
3244         }
3245         else {
3246                 if (compose_write_to_file(compose, tmpfilename, FALSE) < 0) {
3247                         unlink(tmpfilename);
3248                         lock = FALSE;
3249                         return -1;
3250                 }
3251         }
3252                                                 
3253         /* queue message */
3254         queue = folder_get_default_queue();
3255
3256         folder_item_scan(queue);
3257         queue_path = folder_item_get_path(queue);
3258         if (!is_dir_exist(queue_path))
3259                 make_dir_hier(queue_path);
3260         if ((num = folder_item_add_msg(queue, tmpfilename, TRUE)) < 0) {
3261                 g_warning(_("can't queue the message\n"));
3262                 unlink(tmpfilename);
3263                 g_free(tmpfilename);
3264                 g_free(queue_path);
3265                 return -1;
3266         }
3267         g_free(tmpfilename);
3268
3269         if (compose->mode == COMPOSE_REEDIT) {
3270                 compose_remove_reedit_target(compose);
3271                 if (compose->targetinfo &&
3272                     compose->targetinfo->folder != queue)
3273                         folderview_update_item
3274                                 (compose->targetinfo->folder, TRUE);
3275         }
3276
3277         if ((fp = procmsg_open_mark_file(queue_path, TRUE)) == NULL)
3278                 g_warning(_("can't open mark file\n"));
3279         else {
3280                 MsgInfo newmsginfo;
3281
3282                 newmsginfo.msgnum = num;
3283                 newmsginfo.flags.perm_flags = 0;
3284                 newmsginfo.flags.tmp_flags = 0;
3285                 procmsg_write_flags(&newmsginfo, fp);
3286                 fclose(fp);
3287         }
3288         g_free(queue_path);
3289
3290         folder_item_scan(queue);
3291         folderview_update_item(queue, TRUE);
3292
3293         if((msgnum != NULL) && (item != NULL)) {
3294                 *msgnum = num;
3295                 *item = queue;
3296         }
3297
3298         return 0;
3299 }
3300
3301 static void compose_write_attach(Compose *compose, FILE *fp)
3302 {
3303         AttachInfo *ainfo;
3304         GtkCList *clist = GTK_CLIST(compose->attach_clist);
3305         gint row;
3306         FILE *attach_fp;
3307         gchar filename[BUFFSIZE];
3308         gint len;
3309
3310         for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
3311              row++) {
3312                 if ((attach_fp = fopen(ainfo->file, "r")) == NULL) {
3313                         g_warning(_("Can't open file %s\n"), ainfo->file);
3314                         continue;
3315                 }
3316
3317                 fprintf(fp, "\n--%s\n", compose->boundary);
3318
3319                 if (!strcmp2(ainfo->content_type, "message/rfc822")) {
3320                         fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
3321                         fprintf(fp, "Content-Disposition: inline\n");
3322                 } else {
3323                         conv_encode_header(filename, sizeof(filename),
3324                                            ainfo->name, 12);
3325                         fprintf(fp, "Content-Type: %s;\n"
3326                                     " name=\"%s\"\n",
3327                                 ainfo->content_type, filename);
3328                         fprintf(fp, "Content-Disposition: attachment;\n"
3329                                     " filename=\"%s\"\n", filename);
3330                 }
3331
3332                 fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
3333                         procmime_get_encoding_str(ainfo->encoding));
3334
3335                 if (ainfo->encoding == ENC_7BIT) {
3336                         gchar buf[BUFFSIZE];
3337
3338                         while (fgets(buf, sizeof(buf), attach_fp) != NULL) {
3339                                 strcrchomp(buf);
3340                                 fputs(buf, fp);
3341                         }
3342                 } else {
3343                         gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
3344
3345                         while ((len = fread(inbuf, sizeof(gchar),
3346                                             B64_LINE_SIZE, attach_fp))
3347                                == B64_LINE_SIZE) {
3348                                 to64frombits(outbuf, inbuf, B64_LINE_SIZE);
3349                                 fputs(outbuf, fp);
3350                                 fputc('\n', fp);
3351                         }
3352                         if (len > 0 && feof(attach_fp)) {
3353                                 to64frombits(outbuf, inbuf, len);
3354                                 fputs(outbuf, fp);
3355                                 fputc('\n', fp);
3356                         }
3357                 }
3358
3359                 fclose(attach_fp);
3360         }
3361
3362         fprintf(fp, "\n--%s--\n", compose->boundary);
3363 }
3364
3365 #define IS_IN_CUSTOM_HEADER(header) \
3366         (compose->account->add_customhdr && \
3367          custom_header_find(compose->account->customhdr_list, header) != NULL)
3368
3369 static gint compose_write_headers_from_headerlist(Compose *compose, 
3370                                                   FILE *fp, 
3371                                                   gchar *header)
3372 {
3373         gchar buf[BUFFSIZE];
3374         gchar *str, *header_w_colon, *trans_hdr;
3375         gboolean first_address;
3376         GSList *list;
3377         ComposeHeaderEntry *headerentry;
3378         gchar * headerentryname;
3379
3380         if (IS_IN_CUSTOM_HEADER(header)) {
3381                 return 0;
3382         }
3383
3384         debug_print(_("Writing %s-header\n"), header);
3385
3386         header_w_colon = g_strconcat(header, ":", NULL);
3387         trans_hdr = (prefs_common.trans_hdr ? gettext(header_w_colon) : header_w_colon);
3388
3389         first_address = TRUE;
3390         for(list = compose->header_list; list; list = list->next) {
3391                 headerentry = ((ComposeHeaderEntry *)list->data);
3392                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
3393
3394                 if(!g_strcasecmp(trans_hdr, headerentryname)) {
3395                         str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
3396                         Xstrdup_a(str, str, return -1);
3397                         g_strstrip(str);
3398                         if(str[0] != '\0') {
3399                                 compose_convert_header
3400                                         (buf, sizeof(buf), str,
3401                                         strlen(header) + 2);
3402                                 if(first_address) {
3403                                         fprintf(fp, "%s: ", header);
3404                                         first_address = FALSE;
3405                                 } else {
3406                                         fprintf(fp, ",");
3407                                 }
3408                                 fprintf(fp, "%s", buf);
3409                         }
3410                 }
3411         }
3412         if(!first_address) {
3413                 fprintf(fp, "\n");
3414         }
3415
3416         g_free(header_w_colon);
3417
3418         return(0);
3419 }
3420
3421 static gint compose_write_headers(Compose *compose, FILE *fp,
3422                                   const gchar *charset, EncodingType encoding,
3423                                   gboolean is_draft)
3424 {
3425         gchar buf[BUFFSIZE];
3426         gchar *str;
3427         /* struct utsname utsbuf; */
3428
3429         g_return_val_if_fail(fp != NULL, -1);
3430         g_return_val_if_fail(charset != NULL, -1);
3431         g_return_val_if_fail(compose->account != NULL, -1);
3432         g_return_val_if_fail(compose->account->address != NULL, -1);
3433
3434         /* Date */
3435         if (compose->account->add_date) {
3436                 get_rfc822_date(buf, sizeof(buf));
3437                 fprintf(fp, "Date: %s\n", buf);
3438         }
3439
3440         /* From */
3441         if (!IS_IN_CUSTOM_HEADER("From")) {
3442                 if (compose->account->name && *compose->account->name) {
3443                         compose_convert_header
3444                                 (buf, sizeof(buf), compose->account->name,
3445                                  strlen("From: "));
3446                         fprintf(fp, "From: %s <%s>\n",
3447                                 buf, compose->account->address);
3448                 } else
3449                         fprintf(fp, "From: %s\n", compose->account->address);
3450         }
3451         
3452         /* To */
3453         compose_write_headers_from_headerlist(compose, fp, "To");
3454 #if 0 /* NEW COMPOSE GUI */
3455         if (compose->use_to) {
3456                 str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3457                 if (*str != '\0') {
3458                         Xstrdup_a(str, str, return -1);
3459                         g_strstrip(str);
3460                         if (*str != '\0') {
3461                                 compose->to_list = address_list_append
3462                                         (compose->to_list, str);
3463                                 if (!IS_IN_CUSTOM_HEADER("To")) {
3464                                         compose_convert_header
3465                                                 (buf, sizeof(buf), str,
3466                                                  strlen("To: "));
3467                                         fprintf(fp, "To: %s\n", buf);
3468                                 }
3469                         }
3470                 }
3471         }
3472 #endif
3473
3474         /* Newsgroups */
3475         compose_write_headers_from_headerlist(compose, fp, "Newsgroups");
3476 #if 0 /* NEW COMPOSE GUI */
3477         str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
3478         if (*str != '\0') {
3479                 Xstrdup_a(str, str, return -1);
3480                 g_strstrip(str);
3481                 remove_space(str);
3482                 if (*str != '\0') {
3483                         compose->newsgroup_list =
3484                                 newsgroup_list_append(compose->newsgroup_list,
3485                                                       str);
3486                         if (!IS_IN_CUSTOM_HEADER("Newsgroups")) {
3487                                 compose_convert_header(buf, sizeof(buf), str,
3488                                                        strlen("Newsgroups: "));
3489                                 fprintf(fp, "Newsgroups: %s\n", buf);
3490                         }
3491                 }
3492         }
3493 #endif
3494         /* Cc */
3495         compose_write_headers_from_headerlist(compose, fp, "Cc");
3496 #if 0 /* NEW COMPOSE GUI */
3497         if (compose->use_cc) {
3498                 str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3499                 if (*str != '\0') {
3500                         Xstrdup_a(str, str, return -1);
3501                         g_strstrip(str);
3502                         if (*str != '\0') {
3503                                 compose->to_list = address_list_append
3504                                         (compose->to_list, str);
3505                                 if (!IS_IN_CUSTOM_HEADER("Cc")) {
3506                                         compose_convert_header
3507                                                 (buf, sizeof(buf), str,
3508                                                  strlen("Cc: "));
3509                                         fprintf(fp, "Cc: %s\n", buf);
3510                                 }
3511                         }
3512                 }
3513         }
3514 #endif
3515         /* Bcc */
3516         compose_write_headers_from_headerlist(compose, fp, "Bcc");
3517 #if 0 /* NEW COMPOSE GUI */
3518         if (compose->use_bcc) {
3519                 str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3520                 if (*str != '\0') {
3521                         Xstrdup_a(str, str, return -1);
3522                         g_strstrip(str);
3523                         if (*str != '\0') {
3524                                 compose->to_list = address_list_append
3525                                         (compose->to_list, str);
3526                                 compose_convert_header(buf, sizeof(buf), str,
3527                                                        strlen("Bcc: "));
3528                                 fprintf(fp, "Bcc: %s\n", buf);
3529                         }
3530                 }
3531         }
3532 #endif
3533
3534         /* Subject */
3535         str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3536         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
3537                 Xstrdup_a(str, str, return -1);
3538                 g_strstrip(str);
3539                 if (*str != '\0') {
3540                         compose_convert_header(buf, sizeof(buf), str,
3541                                                strlen("Subject: "));
3542                         fprintf(fp, "Subject: %s\n", buf);
3543                 }
3544         }
3545
3546         /* Message-ID */
3547         if (compose->account->gen_msgid) {
3548                 compose_generate_msgid(compose, buf, sizeof(buf));
3549                 fprintf(fp, "Message-Id: <%s>\n", buf);
3550                 compose->msgid = g_strdup(buf);
3551         }
3552
3553         /* In-Reply-To */
3554         if (compose->inreplyto && compose->to_list)
3555                 fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
3556
3557         /* References */
3558         if (compose->references)
3559                 fprintf(fp, "References: %s\n", compose->references);
3560
3561         /* Followup-To */
3562         compose_write_headers_from_headerlist(compose, fp, "Followup-To");
3563 #if 0 /* NEW COMPOSE GUI */
3564         if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
3565                 str = gtk_entry_get_text(GTK_ENTRY(compose->followup_entry));
3566                 if (*str != '\0') {
3567                         Xstrdup_a(str, str, return -1);
3568                         g_strstrip(str);
3569                         remove_space(str);
3570                         if (*str != '\0') {
3571                                 compose_convert_header(buf, sizeof(buf), str,
3572                                                        strlen("Followup-To: "));
3573                                 fprintf(fp, "Followup-To: %s\n", buf);
3574                         }
3575                 }
3576         }
3577 #endif
3578         /* Reply-To */
3579         compose_write_headers_from_headerlist(compose, fp, "Reply-To");
3580 #if 0 /* NEW COMPOSE GUI */
3581         if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
3582                 str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
3583                 if (*str != '\0') {
3584                         Xstrdup_a(str, str, return -1);
3585                         g_strstrip(str);
3586                         if (*str != '\0') {
3587                                 compose_convert_header(buf, sizeof(buf), str,
3588                                                        strlen("Reply-To: "));
3589                                 fprintf(fp, "Reply-To: %s\n", buf);
3590                         }
3591                 }
3592         }
3593 #endif
3594         /* Organization */
3595         if (compose->account->organization &&
3596             !IS_IN_CUSTOM_HEADER("Organization")) {
3597                 compose_convert_header(buf, sizeof(buf),
3598                                        compose->account->organization,
3599                                        strlen("Organization: "));
3600                 fprintf(fp, "Organization: %s\n", buf);
3601         }
3602
3603         /* Program version and system info */
3604         /* uname(&utsbuf); */
3605         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
3606                 fprintf(fp, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
3607                         prog_version,
3608                         gtk_major_version, gtk_minor_version, gtk_micro_version,
3609                         HOST_ALIAS);
3610                         /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
3611         }
3612         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
3613                 fprintf(fp, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
3614                         prog_version,
3615                         gtk_major_version, gtk_minor_version, gtk_micro_version,
3616                         HOST_ALIAS);
3617                         /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
3618         }
3619
3620         /* custom headers */
3621         if (compose->account->add_customhdr) {
3622                 GSList *cur;
3623
3624                 for (cur = compose->account->customhdr_list; cur != NULL;
3625                      cur = cur->next) {
3626                         CustomHeader *chdr = (CustomHeader *)cur->data;
3627
3628                         if (strcasecmp(chdr->name, "Date")         != 0 &&
3629                             strcasecmp(chdr->name, "From")         != 0 &&
3630                             strcasecmp(chdr->name, "To")           != 0 &&
3631                          /* strcasecmp(chdr->name, "Sender")       != 0 && */
3632                             strcasecmp(chdr->name, "Message-Id")   != 0 &&
3633                             strcasecmp(chdr->name, "In-Reply-To")  != 0 &&
3634                             strcasecmp(chdr->name, "References")   != 0 &&
3635                             strcasecmp(chdr->name, "Mime-Version") != 0 &&
3636                             strcasecmp(chdr->name, "Content-Type") != 0 &&
3637                             strcasecmp(chdr->name, "Content-Transfer-Encoding")
3638                             != 0) {
3639                                 compose_convert_header
3640                                         (buf, sizeof(buf),
3641                                          chdr->value ? chdr->value : "",
3642                                          strlen(chdr->name) + 2);
3643                                 fprintf(fp, "%s: %s\n", chdr->name, buf);
3644                         }
3645                 }
3646         }
3647
3648         /* MIME */
3649         fprintf(fp, "Mime-Version: 1.0\n");
3650         if (compose_use_attach(compose)) {
3651                 get_rfc822_date(buf, sizeof(buf));
3652                 subst_char(buf, ' ', '_');
3653                 subst_char(buf, ',', '_');
3654                 compose->boundary = g_strdup_printf("Multipart_%s_%08x",
3655                                                     buf, (guint)compose);
3656                 fprintf(fp,
3657                         "Content-Type: multipart/mixed;\n"
3658                         " boundary=\"%s\"\n", compose->boundary);
3659         } else {
3660                 fprintf(fp, "Content-Type: text/plain; charset=%s\n", charset);
3661                 fprintf(fp, "Content-Transfer-Encoding: %s\n",
3662                         procmime_get_encoding_str(encoding));
3663         }
3664
3665         /* Request Return Receipt */
3666         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
3667                 if (compose->return_receipt) {
3668                         if (compose->account->name
3669                             && *compose->account->name) {
3670                                 compose_convert_header(buf, sizeof(buf), compose->account->name, strlen("Disposition-Notification-To: "));
3671                                 fprintf(fp, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
3672                         } else
3673                                 fprintf(fp, "Disposition-Notification-To: %s\n", compose->account->address);
3674                 }
3675         }
3676
3677         /* separator between header and body */
3678         fputs("\n", fp);
3679
3680         return 0;
3681 }
3682
3683 #undef IS_IN_CUSTOM_HEADER
3684
3685 static void compose_convert_header(gchar *dest, gint len, gchar *src,
3686                                    gint header_len)
3687 {
3688         g_return_if_fail(src != NULL);
3689         g_return_if_fail(dest != NULL);
3690
3691         if (len < 1) return;
3692
3693         remove_return(src);
3694
3695         if (is_ascii_str(src)) {
3696                 strncpy2(dest, src, len);
3697                 dest[len - 1] = '\0';
3698                 return;
3699         } else
3700                 conv_encode_header(dest, len, src, header_len);
3701 }
3702
3703 static void compose_generate_msgid(Compose *compose, gchar *buf, gint len)
3704 {
3705         struct tm *lt;
3706         time_t t;
3707         gchar *addr;
3708
3709         t = time(NULL);
3710         lt = localtime(&t);
3711
3712         if (compose->account && compose->account->address &&
3713             *compose->account->address) {
3714                 if (strchr(compose->account->address, '@'))
3715                         addr = g_strdup(compose->account->address);
3716                 else
3717                         addr = g_strconcat(compose->account->address, "@",
3718                                            get_domain_name(), NULL);
3719         } else
3720                 addr = g_strconcat(g_get_user_name(), "@", get_domain_name(),
3721                                    NULL);
3722
3723         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x.%s",
3724                    lt->tm_year + 1900, lt->tm_mon + 1,
3725                    lt->tm_mday, lt->tm_hour,
3726                    lt->tm_min, lt->tm_sec,
3727                    (guint)random(), addr);
3728
3729         debug_print(_("generated Message-ID: %s\n"), buf);
3730
3731         g_free(addr);
3732 }
3733
3734
3735 static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox,
3736                                     GtkWidget **entry, gint *count,
3737                                     const gchar *label_str,
3738                                     gboolean is_addr_entry)
3739 {
3740         GtkWidget *label;
3741
3742         if (GTK_TABLE(table)->nrows < (*count) + 1)
3743                 gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2);
3744
3745         *hbox = gtk_hbox_new(FALSE, 0);
3746         label = gtk_label_new
3747                 (prefs_common.trans_hdr ? gettext(label_str) : label_str);
3748         gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0);
3749         gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1,
3750                          GTK_FILL, 0, 2, 0);
3751         *entry = gtk_entry_new_with_max_length(MAX_ENTRY_LENGTH);
3752         gtk_table_attach
3753                 (GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
3754 #if 0 /* NEW COMPOSE GUI */
3755         if (GTK_TABLE(table)->nrows > (*count) + 1)
3756                 gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4);
3757 #endif
3758
3759         if (is_addr_entry)
3760                 address_completion_register_entry(GTK_ENTRY(*entry));
3761
3762         (*count)++;
3763 }
3764
3765 static void compose_create_header_entry(Compose *compose) {
3766         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
3767
3768         GtkWidget *combo;
3769         GtkWidget *entry;
3770         GList *combo_list = NULL;
3771         gchar **string, *header;
3772         ComposeHeaderEntry *headerentry;
3773
3774         headerentry = g_new0(ComposeHeaderEntry, 1);
3775
3776         /* Combo box */
3777         combo = gtk_combo_new();
3778         string = headers; 
3779         while(*string != NULL) {
3780             combo_list = g_list_append(combo_list, (prefs_common.trans_hdr ? gettext(*string) : *string));
3781             string++;
3782         }
3783         gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_list);
3784         g_list_free(combo_list);
3785         gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(combo)->entry), FALSE);
3786         gtk_widget_show(combo);
3787         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1, compose->header_nextrow, compose->header_nextrow+1, GTK_SHRINK, GTK_FILL, 0, 0);
3788         if(compose->header_last) {      
3789                 header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
3790         } else {
3791                 switch(compose->account->protocol) {
3792                         case A_NNTP:
3793                                 header = _("Newsgroups:");
3794                                 break;
3795                         default:
3796                                 header = _("To:");
3797                                 break;
3798                 }                                                                   
3799         }
3800         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), header);
3801
3802         /* Entry field */
3803         entry = gtk_entry_new(); 
3804         gtk_widget_show(entry);
3805         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2, compose->header_nextrow, compose->header_nextrow+1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
3806
3807         gtk_signal_connect(GTK_OBJECT(entry), "key-press-event", GTK_SIGNAL_FUNC(compose_headerentry_key_press_event_cb), headerentry);
3808         gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(compose_headerentry_changed_cb), headerentry);
3809
3810         address_completion_register_entry(GTK_ENTRY(entry));
3811
3812         headerentry->compose = compose;
3813         headerentry->combo = combo;
3814         headerentry->entry = entry;
3815         headerentry->headernum = compose->header_nextrow;
3816
3817         compose->header_nextrow++;
3818         compose->header_last = headerentry;
3819 }
3820
3821 static void compose_add_header_entry(Compose *compose, gchar *header, gchar *text) {
3822         ComposeHeaderEntry *last_header;
3823         
3824         last_header = compose->header_last;
3825         
3826         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header->combo)->entry), header);
3827         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
3828 }
3829
3830 static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
3831 {
3832         Compose   *compose;
3833         GtkWidget *window;
3834         GtkWidget *vbox;
3835         GtkWidget *menubar;
3836         GtkWidget *handlebox;
3837
3838         GtkWidget *notebook;
3839
3840         GtkWidget *vbox2;
3841
3842         GtkWidget *table_vbox;
3843         GtkWidget *label;
3844         GtkWidget *from_optmenu_hbox;
3845 #if 0 /* NEW COMPOSE GUI */
3846         GtkWidget *to_entry;
3847         GtkWidget *to_hbox;
3848         GtkWidget *newsgroups_entry;
3849         GtkWidget *newsgroups_hbox;
3850 #endif
3851         GtkWidget *header_scrolledwin;
3852         GtkWidget *header_table;
3853         GtkWidget *subject_entry;
3854 #if 0 /* NEW COMPOSE GUI */
3855         GtkWidget *cc_entry;
3856         GtkWidget *cc_hbox;
3857         GtkWidget *bcc_entry;
3858         GtkWidget *bcc_hbox;
3859         GtkWidget *reply_entry;
3860         GtkWidget *reply_hbox;
3861         GtkWidget *followup_entry;
3862         GtkWidget *followup_hbox;
3863 #endif
3864         GtkWidget *paned;
3865
3866         GtkWidget *attach_scrwin;
3867         GtkWidget *attach_clist;
3868
3869         GtkWidget *edit_vbox;
3870         GtkWidget *ruler_hbox;
3871         GtkWidget *ruler;
3872         GtkWidget *scrolledwin;
3873         GtkWidget *text;
3874
3875         GtkWidget *table;
3876         GtkWidget *hbox;
3877
3878         gchar *titles[] = {_("MIME type"), _("Size"), _("Name")};
3879         guint n_menu_entries;
3880         GtkStyle  *style, *new_style;
3881         GdkColormap *cmap;
3882         GdkColor color[1];
3883         gboolean success[1];
3884         GdkFont   *font;
3885         GtkWidget *popupmenu;
3886         GtkWidget *menuitem;
3887         GtkItemFactory *popupfactory;
3888         GtkItemFactory *ifactory;
3889         GtkWidget *tmpl_menu;
3890         gint n_entries;
3891         gint count = 0;
3892         gint i;
3893
3894 #if USE_PSPELL
3895         GtkPspell * gtkpspell = NULL;
3896 #endif
3897
3898         g_return_val_if_fail(account != NULL, NULL);
3899
3900         debug_print(_("Creating compose window...\n"));
3901         compose = g_new0(Compose, 1);
3902
3903         compose->account = account;
3904         compose->orig_account = account;
3905
3906         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3907         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
3908         gtk_widget_set_usize(window, -1, prefs_common.compose_height);
3909         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
3910                            GTK_SIGNAL_FUNC(compose_delete_cb), compose);
3911         gtk_signal_connect(GTK_OBJECT(window), "destroy",
3912                            GTK_SIGNAL_FUNC(compose_destroy_cb), compose);
3913         gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
3914                            GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
3915         gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
3916                            GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
3917         gtk_widget_realize(window);
3918
3919         gtkut_widget_set_composer_icon(window);
3920
3921         vbox = gtk_vbox_new(FALSE, 0);
3922         gtk_container_add(GTK_CONTAINER(window), vbox);
3923
3924         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
3925         menubar = menubar_create(window, compose_entries,
3926                                  n_menu_entries, "<Compose>", compose);
3927         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
3928
3929         handlebox = gtk_handle_box_new();
3930         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
3931
3932         compose_toolbar_create(compose, handlebox);
3933
3934         vbox2 = gtk_vbox_new(FALSE, 2);
3935         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
3936         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
3937
3938         table_vbox = gtk_vbox_new(FALSE, 0);
3939         gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0);
3940         gtk_container_set_border_width(GTK_CONTAINER(table_vbox),
3941                                        BORDER_WIDTH * 2);
3942
3943         /* Notebook */
3944         notebook = gtk_notebook_new();
3945         gtk_widget_set_usize(notebook, -1, 180);
3946         gtk_widget_show(notebook);
3947
3948         /* header labels and entries */
3949         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
3950         gtk_widget_show(header_scrolledwin);
3951         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3952         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), header_scrolledwin, gtk_label_new(_("Header")));
3953
3954         header_table = gtk_table_new(2, 2, FALSE);
3955         gtk_widget_show(header_table);
3956         gtk_container_set_border_width(GTK_CONTAINER(header_table), 2);
3957         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
3958         count = 0;
3959
3960         /* option menu for selecting accounts */
3961         hbox = gtk_hbox_new(FALSE, 0);
3962         label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
3963         gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3964         gtk_table_attach(GTK_TABLE(header_table), hbox, 0, 1, count, count + 1,
3965                          GTK_FILL, 0, 2, 0);
3966         from_optmenu_hbox = compose_account_option_menu_create(compose);
3967         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
3968                                   1, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
3969 #if 0 /* NEW COMPOSE GUI */
3970         gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
3971 #endif
3972         count++;
3973
3974         /* Subject */
3975         compose_add_entry_field(header_table, &hbox, &subject_entry, &count,
3976                                 "Subject:", FALSE);
3977
3978         compose->header_table = header_table;
3979         compose->header_list = NULL;
3980         compose->header_nextrow = count;
3981
3982         compose_create_header_entry(compose);
3983
3984 #if 0 /* NEW COMPOSE GUI */
3985         compose_add_entry_field(table, &to_hbox, &to_entry, &count,
3986                                 "To:", TRUE); 
3987         gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
3988         compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry,
3989                                 &count, "Newsgroups:", FALSE);
3990         gtk_table_set_row_spacing(GTK_TABLE(table), 1, 4);
3991
3992         gtk_table_set_row_spacing(GTK_TABLE(table), 2, 4);
3993
3994         compose_add_entry_field(table, &cc_hbox, &cc_entry, &count,
3995                                 "Cc:", TRUE);
3996         gtk_table_set_row_spacing(GTK_TABLE(table), 3, 4);
3997         compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count,
3998                                 "Bcc:", TRUE);
3999         gtk_table_set_row_spacing(GTK_TABLE(table), 4, 4);
4000         compose_add_entry_field(table, &reply_hbox, &reply_entry, &count,
4001                                 "Reply-To:", TRUE);
4002         gtk_table_set_row_spacing(GTK_TABLE(table), 5, 4);
4003         compose_add_entry_field(table, &followup_hbox, &followup_entry, &count,
4004                                 "Followup-To:", FALSE);
4005         gtk_table_set_row_spacing(GTK_TABLE(table), 6, 4);
4006
4007         gtk_table_set_col_spacings(GTK_TABLE(table), 4);
4008
4009         gtk_signal_connect(GTK_OBJECT(to_entry), "activate",
4010                            GTK_SIGNAL_FUNC(to_activated), compose);
4011         gtk_signal_connect(GTK_OBJECT(newsgroups_entry), "activate",
4012                            GTK_SIGNAL_FUNC(newsgroups_activated), compose);
4013         gtk_signal_connect(GTK_OBJECT(subject_entry), "activate",
4014                            GTK_SIGNAL_FUNC(subject_activated), compose);
4015         gtk_signal_connect(GTK_OBJECT(cc_entry), "activate",
4016                            GTK_SIGNAL_FUNC(cc_activated), compose);
4017         gtk_signal_connect(GTK_OBJECT(bcc_entry), "activate",
4018                            GTK_SIGNAL_FUNC(bcc_activated), compose);
4019         gtk_signal_connect(GTK_OBJECT(reply_entry), "activate",
4020                            GTK_SIGNAL_FUNC(replyto_activated), compose);
4021         gtk_signal_connect(GTK_OBJECT(followup_entry), "activate",
4022                            GTK_SIGNAL_FUNC(followupto_activated), compose);
4023
4024         gtk_signal_connect(GTK_OBJECT(subject_entry), "grab_focus",
4025                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4026         gtk_signal_connect(GTK_OBJECT(to_entry), "grab_focus",
4027                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4028         gtk_signal_connect(GTK_OBJECT(newsgroups_entry), "grab_focus",
4029                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4030         gtk_signal_connect(GTK_OBJECT(cc_entry), "grab_focus",
4031                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4032         gtk_signal_connect(GTK_OBJECT(bcc_entry), "grab_focus",
4033                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4034         gtk_signal_connect(GTK_OBJECT(reply_entry), "grab_focus",
4035                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4036         gtk_signal_connect(GTK_OBJECT(followup_entry), "grab_focus",
4037                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4038 #endif
4039
4040         /* attachment list */
4041         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
4042         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), attach_scrwin, gtk_label_new(_("Attachments")));
4043         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
4044                                        GTK_POLICY_AUTOMATIC,
4045                                        GTK_POLICY_ALWAYS);
4046         gtk_widget_set_usize(attach_scrwin, -1, 80);
4047
4048         attach_clist = gtk_clist_new_with_titles(N_ATTACH_COLS, titles);
4049         gtk_clist_set_column_justification(GTK_CLIST(attach_clist), COL_SIZE,
4050                                            GTK_JUSTIFY_RIGHT);
4051         gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_MIMETYPE, 240);
4052         gtk_clist_set_column_width(GTK_CLIST(attach_clist), COL_SIZE, 64);
4053         gtk_clist_set_selection_mode(GTK_CLIST(attach_clist),
4054                                      GTK_SELECTION_EXTENDED);
4055         for (i = 0; i < N_ATTACH_COLS; i++)
4056                 GTK_WIDGET_UNSET_FLAGS
4057                         (GTK_CLIST(attach_clist)->column[i].button,
4058                          GTK_CAN_FOCUS);
4059         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
4060
4061         gtk_signal_connect(GTK_OBJECT(attach_clist), "select_row",
4062                            GTK_SIGNAL_FUNC(attach_selected), compose);
4063         gtk_signal_connect(GTK_OBJECT(attach_clist), "button_press_event",
4064                            GTK_SIGNAL_FUNC(attach_button_pressed), compose);
4065         gtk_signal_connect(GTK_OBJECT(attach_clist), "key_press_event",
4066                            GTK_SIGNAL_FUNC(attach_key_pressed), compose);
4067
4068         /* drag and drop */
4069         gtk_drag_dest_set(attach_clist,
4070                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
4071                           GDK_ACTION_COPY);
4072         gtk_signal_connect(GTK_OBJECT(attach_clist), "drag_data_received",
4073                            GTK_SIGNAL_FUNC(compose_attach_drag_received_cb),
4074                            compose);
4075
4076
4077         edit_vbox = gtk_vbox_new(FALSE, 0);
4078 #if 0 /* NEW COMPOSE GUI */
4079         gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0);
4080 #endif
4081
4082         /* ruler */
4083         ruler_hbox = gtk_hbox_new(FALSE, 0);
4084         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
4085
4086         ruler = gtk_shruler_new();
4087         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
4088         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
4089                            BORDER_WIDTH + 1);
4090         gtk_widget_set_usize(ruler_hbox, 1, -1);
4091
4092         /* text widget */
4093         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
4094         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
4095                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4096         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
4097         gtk_widget_set_usize(scrolledwin, prefs_common.compose_width, -1);
4098
4099         text = gtk_stext_new(gtk_scrolled_window_get_hadjustment
4100                             (GTK_SCROLLED_WINDOW(scrolledwin)),
4101                             gtk_scrolled_window_get_vadjustment
4102                             (GTK_SCROLLED_WINDOW(scrolledwin)));
4103         GTK_STEXT(text)->default_tab_width = 8;
4104         gtk_stext_set_editable(GTK_STEXT(text), TRUE);
4105
4106         if (prefs_common.block_cursor) {
4107                 GTK_STEXT(text)->cursor_type = STEXT_CURSOR_BLOCK;
4108         }
4109         
4110         if (prefs_common.smart_wrapping) {      
4111                 gtk_stext_set_word_wrap(GTK_STEXT(text), TRUE);
4112                 gtk_stext_set_wrap_rmargin(GTK_STEXT(text), prefs_common.linewrap_len);
4113         }               
4114
4115         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
4116
4117         gtk_signal_connect(GTK_OBJECT(text), "changed",
4118                            GTK_SIGNAL_FUNC(compose_changed_cb), compose);
4119         gtk_signal_connect(GTK_OBJECT(text), "grab_focus",
4120                            GTK_SIGNAL_FUNC(compose_grab_focus_cb), compose);
4121         gtk_signal_connect_after(GTK_OBJECT(text), "button_press_event",
4122                                  GTK_SIGNAL_FUNC(compose_button_press_cb),
4123                                  compose);
4124 #if 0
4125         gtk_signal_connect_after(GTK_OBJECT(text), "key_press_event",
4126                                  GTK_SIGNAL_FUNC(compose_key_press_cb),
4127                                  compose);
4128 #endif
4129         gtk_signal_connect_after(GTK_OBJECT(text), "size_allocate",
4130                                  GTK_SIGNAL_FUNC(compose_edit_size_alloc),
4131                                  ruler);
4132
4133         /* drag and drop */
4134         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 1,
4135                           GDK_ACTION_COPY);
4136         gtk_signal_connect(GTK_OBJECT(text), "drag_data_received",
4137                            GTK_SIGNAL_FUNC(compose_insert_drag_received_cb),
4138                            compose);
4139 #if USE_PSPELL
4140         if (prefs_common.enable_pspell) {
4141                 gtkpspell = gtkpspell_new_with_config(gtkpspellconfig,
4142                                                       prefs_common.pspell_path,
4143                                                       prefs_common.dictionary,
4144                                                       PSPELL_FASTMODE,
4145                                                       conv_get_current_charset_str());
4146                 if (gtkpspell == NULL)
4147                         prefs_common.enable_pspell = FALSE;
4148                 else
4149                         gtkpspell_attach(gtkpspell, GTK_STEXT(text));
4150         }
4151 #endif
4152         gtk_widget_show_all(vbox);
4153
4154         /* pane between attach clist and text */
4155         paned = gtk_vpaned_new();
4156         gtk_container_add(GTK_CONTAINER(vbox2), paned);
4157         gtk_paned_add1(GTK_PANED(paned), notebook);
4158         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
4159         gtk_widget_show_all(paned);
4160
4161         style = gtk_widget_get_style(text);
4162
4163         /* workaround for the slow down of GtkText when using Pixmap theme */
4164         if (style->engine) {
4165                 GtkThemeEngine *engine;
4166
4167                 engine = style->engine;
4168                 style->engine = NULL;
4169                 new_style = gtk_style_copy(style);
4170                 style->engine = engine;
4171         } else
4172                 new_style = gtk_style_copy(style);
4173
4174         if (prefs_common.textfont) {
4175                 CharSet charset;
4176
4177                 charset = conv_get_current_charset();
4178                 if (MB_CUR_MAX == 1) {
4179                         gchar *fontstr, *p;
4180
4181                         Xstrdup_a(fontstr, prefs_common.textfont, );
4182                         if (fontstr && (p = strchr(fontstr, ',')) != NULL)
4183                                 *p = '\0';
4184                         font = gdk_font_load(fontstr);
4185                 } else
4186                         font = gdk_fontset_load(prefs_common.textfont);
4187                 if (font) {
4188                         gdk_font_unref(new_style->font);
4189                         new_style->font = font;
4190                 }
4191         }
4192
4193         gtk_widget_set_style(text, new_style);
4194
4195         color[0] = quote_color;
4196         cmap = gdk_window_get_colormap(window->window);
4197         gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
4198         if (success[0] == FALSE) {
4199                 g_warning("Compose: color allocation failed.\n");
4200                 style = gtk_widget_get_style(text);
4201                 quote_color = style->black;
4202         }
4203
4204         n_entries = sizeof(compose_popup_entries) /
4205                 sizeof(compose_popup_entries[0]);
4206         popupmenu = menu_create_items(compose_popup_entries, n_entries,
4207                                       "<Compose>", &popupfactory,
4208                                       compose);
4209
4210         ifactory = gtk_item_factory_from_widget(menubar);
4211         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
4212         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
4213
4214         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tool/Template");
4215 #if 0 /* NEW COMPOSE GUI */
4216         if (account->protocol == A_NNTP) {
4217                 gtk_widget_hide(to_hbox);
4218                 gtk_widget_hide(to_entry);
4219                 gtk_widget_hide(cc_hbox);
4220                 gtk_widget_hide(cc_entry);
4221                 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
4222                 gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
4223         } else {
4224                 gtk_widget_hide(newsgroups_hbox);
4225                 gtk_widget_hide(newsgroups_entry);
4226                 gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
4227
4228                 menu_set_sensitive(ifactory, "/Message/Followup to", FALSE);
4229         }
4230 #endif
4231
4232         switch (prefs_common.toolbar_style) {
4233         case TOOLBAR_NONE:
4234                 gtk_widget_hide(handlebox);
4235                 break;
4236         case TOOLBAR_ICON:
4237                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4238                                       GTK_TOOLBAR_ICONS);
4239                 break;
4240         case TOOLBAR_TEXT:
4241                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4242                                       GTK_TOOLBAR_TEXT);
4243                 break;
4244         case TOOLBAR_BOTH:
4245                 gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar),
4246                                       GTK_TOOLBAR_BOTH);
4247                 break;
4248         }
4249
4250         gtk_widget_show(window);
4251
4252         address_completion_start(window);
4253
4254         compose->window        = window;
4255         compose->vbox          = vbox;
4256         compose->menubar       = menubar;
4257         compose->handlebox     = handlebox;
4258
4259         compose->vbox2         = vbox2;
4260
4261         compose->table_vbox       = table_vbox;
4262         compose->table            = NULL;
4263 #if 0 /* NEW COMPOSE GUI */
4264         compose->table            = table;
4265         compose->to_hbox          = to_hbox;
4266         compose->to_entry         = to_entry;
4267         compose->newsgroups_hbox  = newsgroups_hbox;
4268         compose->newsgroups_entry = newsgroups_entry;
4269 #endif
4270         compose->subject_entry    = subject_entry;
4271 #if 0 /* NEW COMPOSE GUI */
4272         compose->cc_hbox          = cc_hbox;
4273         compose->cc_entry         = cc_entry;
4274         compose->bcc_hbox         = bcc_hbox;
4275         compose->bcc_entry        = bcc_entry;
4276         compose->reply_hbox       = reply_hbox;
4277         compose->reply_entry      = reply_entry;
4278         compose->followup_hbox    = followup_hbox;
4279         compose->followup_entry   = followup_entry;
4280 #endif
4281         compose->paned = paned;
4282
4283         compose->attach_scrwin = attach_scrwin;
4284         compose->attach_clist  = attach_clist;
4285
4286         compose->edit_vbox     = edit_vbox;
4287         compose->ruler_hbox    = ruler_hbox;
4288         compose->ruler         = ruler;
4289         compose->scrolledwin   = scrolledwin;
4290         compose->text          = text;
4291
4292         compose->focused_editable = NULL;
4293
4294         compose->popupmenu    = popupmenu;
4295         compose->popupfactory = popupfactory;
4296
4297         compose->tmpl_menu = tmpl_menu;
4298
4299         compose->mode = mode;
4300
4301         compose->targetinfo = NULL;
4302         compose->replyinfo  = NULL;
4303
4304         compose->replyto     = NULL;
4305         compose->mailinglist = NULL;
4306         compose->cc          = NULL;
4307         compose->bcc         = NULL;
4308         compose->followup_to = NULL;
4309         compose->inreplyto   = NULL;
4310         compose->references  = NULL;
4311         compose->msgid       = NULL;
4312         compose->boundary    = NULL;
4313
4314 #if USE_GPGME
4315         compose->use_signing    = FALSE;
4316         compose->use_encryption = FALSE;
4317 #endif /* USE_GPGME */
4318
4319         compose->modified = FALSE;
4320
4321         compose->return_receipt = FALSE;
4322
4323         compose->to_list        = NULL;
4324         compose->newsgroup_list = NULL;
4325
4326         compose->exteditor_file    = NULL;
4327         compose->exteditor_pid     = -1;
4328         compose->exteditor_readdes = -1;
4329         compose->exteditor_tag     = -1;
4330
4331         compose->bounce_filename = NULL;
4332
4333         compose_set_title(compose);
4334
4335 #if 0 /* NEW COMPOSE GUI */
4336         compose->use_bcc        = FALSE;
4337         compose->use_replyto    = FALSE;
4338         compose->use_followupto = FALSE;
4339 #endif
4340
4341 #if USE_PSPELL
4342         compose->gtkpspell      = gtkpspell;
4343 #endif
4344
4345 #if 0 /* NEW COMPOSE GUI */
4346         if (account->protocol != A_NNTP) {
4347                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
4348                 gtk_check_menu_item_set_active
4349                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4350                 gtk_widget_set_sensitive(menuitem, FALSE);
4351                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
4352                 gtk_check_menu_item_set_active
4353                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4354                 gtk_widget_set_sensitive(menuitem, FALSE);
4355         }
4356 #endif
4357         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT) {
4358                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
4359 #if 0 /* NEW COMPOSE GUI */
4360                 compose->use_cc = TRUE;
4361                 gtk_entry_set_text(GTK_ENTRY(cc_entry), account->auto_cc);
4362                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
4363                 gtk_check_menu_item_set_active
4364                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4365 #endif
4366         }
4367         if (account->set_autobcc) {
4368                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
4369 #if 0 /* NEW COMPOSE GUI */
4370                 compose->use_bcc = TRUE;
4371                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Bcc");
4372                 gtk_check_menu_item_set_active
4373                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4374                 if (account->auto_bcc && mode != COMPOSE_REEDIT)
4375                         gtk_entry_set_text(GTK_ENTRY(bcc_entry),
4376                                            account->auto_bcc);
4377 #endif
4378         }
4379         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT) {
4380                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
4381 #if 0 /* NEW COMPOSE GUI */
4382                 compose->use_replyto = TRUE;
4383                 menuitem = gtk_item_factory_get_item(ifactory,
4384                                                      "/Message/Reply to");
4385                 gtk_check_menu_item_set_active
4386                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
4387                 gtk_entry_set_text(GTK_ENTRY(reply_entry),
4388                                    account->auto_replyto);
4389 #endif
4390         }
4391         if (account->protocol != A_NNTP) {
4392                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), _("To:"));
4393         } else {
4394                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), _("Newsgroups:"));
4395         }
4396
4397         menuitem = gtk_item_factory_get_item(ifactory, "/Tool/Show ruler");
4398         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
4399                                        prefs_common.show_ruler);
4400
4401 #if USE_GPGME
4402         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Sign");
4403         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
4404                                        prefs_common.default_sign);
4405         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Encrypt");
4406         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
4407                                        prefs_common.default_encrypt);
4408 #endif /* USE_GPGME */
4409
4410         addressbook_set_target_compose(compose);
4411         compose_set_template_menu(compose);
4412
4413         compose_list = g_list_append(compose_list, compose);
4414
4415 #if 0 /* NEW COMPOSE GUI */
4416         compose->use_to         = FALSE;
4417         compose->use_cc         = FALSE;
4418         compose->use_attach     = TRUE;
4419 #endif
4420
4421 #if 0 /* NEW COMPOSE GUI */
4422         if (!compose->use_bcc) {
4423                 gtk_widget_hide(bcc_hbox);
4424                 gtk_widget_hide(bcc_entry);
4425                 gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0);
4426         }
4427         if (!compose->use_replyto) {
4428                 gtk_widget_hide(reply_hbox);
4429                 gtk_widget_hide(reply_entry);
4430                 gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0);
4431         }
4432         if (!compose->use_followupto) {
4433                 gtk_widget_hide(followup_hbox);
4434                 gtk_widget_hide(followup_entry);
4435                 gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0);
4436         }
4437 #endif
4438         if (!prefs_common.show_ruler)
4439                 gtk_widget_hide(ruler_hbox);
4440
4441         select_account(compose, account);
4442
4443         return compose;
4444 }
4445
4446 #include "pixmaps/stock_mail_send.xpm"
4447 #include "pixmaps/stock_mail_send_queue.xpm"
4448 #include "pixmaps/stock_mail.xpm"
4449 #include "pixmaps/stock_paste.xpm"
4450 #include "pixmaps/stock_mail_attach.xpm"
4451 #include "pixmaps/stock_mail_compose.xpm"
4452 #include "pixmaps/linewrap.xpm"
4453 #include "pixmaps/tb_address_book.xpm"
4454
4455 #define CREATE_TOOLBAR_ICON(xpm_d) \
4456 { \
4457         icon = gdk_pixmap_create_from_xpm_d(container->window, &mask, \
4458                                             &container->style->white, \
4459                                             xpm_d); \
4460         icon_wid = gtk_pixmap_new(icon, mask); \
4461 }
4462
4463 static void compose_toolbar_create(Compose *compose, GtkWidget *container)
4464 {
4465         GtkWidget *toolbar;
4466         GdkPixmap *icon;
4467         GdkBitmap *mask;
4468         GtkWidget *icon_wid;
4469         GtkWidget *send_btn;
4470         GtkWidget *sendl_btn;
4471         GtkWidget *draft_btn;
4472         GtkWidget *insert_btn;
4473         GtkWidget *attach_btn;
4474         GtkWidget *sig_btn;
4475         GtkWidget *exteditor_btn;
4476         GtkWidget *linewrap_btn;
4477         GtkWidget *addrbook_btn;
4478
4479         toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
4480                                   GTK_TOOLBAR_BOTH);
4481         gtk_container_add(GTK_CONTAINER(container), toolbar);
4482         gtk_container_set_border_width(GTK_CONTAINER(container), 2);
4483         gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
4484         gtk_toolbar_set_space_style(GTK_TOOLBAR(toolbar),
4485                                     GTK_TOOLBAR_SPACE_LINE);
4486
4487         CREATE_TOOLBAR_ICON(stock_mail_send_xpm);
4488         send_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4489                                            _("Send"),
4490                                            _("Send message"),
4491                                            "Send",
4492                                            icon_wid, toolbar_send_cb, compose);
4493
4494         CREATE_TOOLBAR_ICON(stock_mail_send_queue_xpm);
4495         /* CREATE_TOOLBAR_ICON(tb_mail_queue_send_xpm); */
4496         sendl_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4497                                            _("Send later"),
4498                                            _("Put into queue folder and send later"),
4499                                            "Send later",
4500                                            icon_wid, toolbar_send_later_cb,
4501                                            compose);
4502
4503         CREATE_TOOLBAR_ICON(stock_mail_xpm);
4504         draft_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4505                                             _("Draft"),
4506                                             _("Save to draft folder"),
4507                                             "Draft",
4508                                             icon_wid, toolbar_draft_cb,
4509                                             compose);
4510
4511         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4512
4513         CREATE_TOOLBAR_ICON(stock_paste_xpm);
4514         insert_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4515                                              _("Insert"),
4516                                              _("Insert file"),
4517                                              "Insert",
4518                                              icon_wid, toolbar_insert_cb,
4519                                              compose);
4520
4521         CREATE_TOOLBAR_ICON(stock_mail_attach_xpm);
4522         attach_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4523                                              _("Attach"),
4524                                              _("Attach file"),
4525                                              "Attach",
4526                                              icon_wid, toolbar_attach_cb,
4527                                              compose);
4528
4529         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4530
4531         CREATE_TOOLBAR_ICON(stock_mail_xpm);
4532         sig_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4533                                           _("Signature"),
4534                                           _("Insert signature"),
4535                                           "Signature",
4536                                           icon_wid, toolbar_sig_cb, compose);
4537
4538         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4539
4540         CREATE_TOOLBAR_ICON(stock_mail_compose_xpm);
4541         exteditor_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4542                                                 _("Editor"),
4543                                                 _("Edit with external editor"),
4544                                                 "Editor",
4545                                                 icon_wid,
4546                                                 toolbar_ext_editor_cb,
4547                                                 compose);
4548
4549         CREATE_TOOLBAR_ICON(linewrap_xpm);
4550         linewrap_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4551                                                _("Linewrap"),
4552                                                _("Wrap current paragraph"),
4553                                                "Linewrap",
4554                                                icon_wid,
4555                                                toolbar_linewrap_cb,
4556                                                compose);
4557
4558         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
4559
4560         CREATE_TOOLBAR_ICON(tb_address_book_xpm);
4561         addrbook_btn = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
4562                                                _("Address"),
4563                                                _("Address book"),
4564                                                "Address",
4565                                                icon_wid, toolbar_address_cb,
4566                                                compose);
4567
4568         compose->toolbar       = toolbar;
4569         compose->send_btn      = send_btn;
4570         compose->sendl_btn     = sendl_btn;
4571         compose->draft_btn     = draft_btn;
4572         compose->insert_btn    = insert_btn;
4573         compose->attach_btn    = attach_btn;
4574         compose->sig_btn       = sig_btn;
4575         compose->exteditor_btn = exteditor_btn;
4576         compose->linewrap_btn  = linewrap_btn;
4577         compose->addrbook_btn  = addrbook_btn;
4578
4579         gtk_widget_show_all(toolbar);
4580 }
4581
4582 #undef CREATE_TOOLBAR_ICON
4583
4584 static GtkWidget *compose_account_option_menu_create(Compose *compose)
4585 {
4586         GList *accounts;
4587         GtkWidget *hbox;
4588         GtkWidget *optmenu;
4589         GtkWidget *menu;
4590         gint num = 0, def_menu = 0;
4591
4592         accounts = account_get_list();
4593         g_return_val_if_fail(accounts != NULL, NULL);
4594
4595         hbox = gtk_hbox_new(FALSE, 0);
4596         optmenu = gtk_option_menu_new();
4597         gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
4598         menu = gtk_menu_new();
4599
4600         for (; accounts != NULL; accounts = accounts->next, num++) {
4601                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
4602                 GtkWidget *menuitem;
4603                 gchar *name;
4604
4605                 if (ac == compose->account) def_menu = num;
4606
4607                 name = g_strdup_printf("%s: %s <%s>",
4608                                        ac->account_name, ac->name, ac->address);
4609                 MENUITEM_ADD(menu, menuitem, name, ac);
4610                 g_free(name);
4611                 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
4612                                    GTK_SIGNAL_FUNC(account_activated),
4613                                    compose);
4614         }
4615
4616         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
4617         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
4618
4619         return hbox;
4620 }
4621
4622 static void compose_set_template_menu(Compose *compose)
4623 {
4624         GSList *tmpl_list, *cur;
4625         GtkWidget *menu;
4626         GtkWidget *item;
4627
4628         tmpl_list = template_get_config();
4629
4630         menu = gtk_menu_new();
4631
4632         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
4633                 Template *tmpl = (Template *)cur->data;
4634
4635                 item = gtk_menu_item_new_with_label(tmpl->name);
4636                 gtk_menu_append(GTK_MENU(menu), item);
4637                 gtk_signal_connect(GTK_OBJECT(item), "activate",
4638                                    GTK_SIGNAL_FUNC(compose_template_activate_cb),
4639                                    compose);
4640                 gtk_object_set_data(GTK_OBJECT(item), "template", tmpl);
4641                 gtk_widget_show(item);
4642         }
4643
4644         gtk_widget_show(menu);
4645         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
4646 }
4647
4648 void compose_reflect_prefs_all(void)
4649 {
4650         GList *cur;
4651         Compose *compose;
4652
4653         for (cur = compose_list; cur != NULL; cur = cur->next) {
4654                 compose = (Compose *)cur->data;
4655                 compose_set_template_menu(compose);
4656         }
4657 }
4658
4659 static void compose_template_apply(Compose *compose, Template *tmpl)
4660 {
4661         gchar *qmark;
4662         gchar *parsed_str;
4663
4664         if (!tmpl || !tmpl->value) return;
4665
4666         gtk_stext_freeze(GTK_STEXT(compose->text));
4667         
4668         if (tmpl->subject)
4669                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
4670                                    tmpl->subject);
4671
4672         if (compose->replyinfo == NULL) {
4673                 MsgInfo dummyinfo;
4674
4675                 memset(&dummyinfo, 0, sizeof(MsgInfo));
4676                 parsed_str = compose_quote_fmt(compose, &dummyinfo,
4677                                                tmpl->value, NULL);
4678         } else {
4679                 if (prefs_common.quotemark && *prefs_common.quotemark)
4680                         qmark = prefs_common.quotemark;
4681                 else
4682                         qmark = "> ";
4683
4684                 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
4685                                                tmpl->value, qmark);
4686         }
4687
4688         if (parsed_str && prefs_common.auto_sig)
4689                 compose_insert_sig(compose);
4690
4691         gtk_stext_thaw(GTK_STEXT(compose->text));
4692 }
4693
4694 static void compose_destroy(Compose *compose)
4695 {
4696         gint row;
4697         GtkCList *clist = GTK_CLIST(compose->attach_clist);
4698         AttachInfo *ainfo;
4699
4700         /* NOTE: address_completion_end() does nothing with the window
4701          * however this may change. */
4702         address_completion_end(compose->window);
4703
4704         slist_free_strings(compose->to_list);
4705         g_slist_free(compose->to_list);
4706         slist_free_strings(compose->newsgroup_list);
4707         g_slist_free(compose->newsgroup_list);
4708         slist_free_strings(compose->header_list);
4709         g_slist_free(compose->header_list);
4710
4711         procmsg_msginfo_free(compose->targetinfo);
4712         procmsg_msginfo_free(compose->replyinfo);
4713
4714         g_free(compose->replyto);
4715         g_free(compose->cc);
4716         g_free(compose->bcc);
4717         g_free(compose->newsgroups);
4718         g_free(compose->followup_to);
4719
4720         g_free(compose->inreplyto);
4721         g_free(compose->references);
4722         g_free(compose->msgid);
4723         g_free(compose->boundary);
4724
4725         if (compose->bounce_filename)
4726                 g_free(compose->bounce_filename);
4727
4728         g_free(compose->exteditor_file);
4729
4730         for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
4731              row++)
4732                 compose_attach_info_free(ainfo);
4733
4734         if (addressbook_get_target_compose() == compose)
4735                 addressbook_set_target_compose(NULL);
4736
4737         prefs_common.compose_width = compose->scrolledwin->allocation.width;
4738         prefs_common.compose_height = compose->window->allocation.height;
4739
4740         gtk_widget_destroy(compose->paned);
4741
4742         g_free(compose);
4743
4744         compose_list = g_list_remove(compose_list, compose);
4745 }
4746
4747 static void compose_attach_info_free(AttachInfo *ainfo)
4748 {
4749         g_free(ainfo->file);
4750         g_free(ainfo->content_type);
4751         g_free(ainfo->name);
4752         g_free(ainfo);
4753 }
4754
4755 static void compose_attach_remove_selected(Compose *compose)
4756 {
4757         GtkCList *clist = GTK_CLIST(compose->attach_clist);
4758         AttachInfo *ainfo;
4759         gint row;
4760
4761         while (clist->selection != NULL) {
4762                 row = GPOINTER_TO_INT(clist->selection->data);
4763                 ainfo = gtk_clist_get_row_data(clist, row);
4764                 compose_attach_info_free(ainfo);
4765                 gtk_clist_remove(clist, row);
4766         }
4767 }
4768
4769 static struct _AttachProperty
4770 {
4771         GtkWidget *window;
4772         GtkWidget *mimetype_entry;
4773         GtkWidget *encoding_optmenu;
4774         GtkWidget *path_entry;
4775         GtkWidget *filename_entry;
4776         GtkWidget *ok_btn;
4777         GtkWidget *cancel_btn;
4778 } attach_prop;
4779
4780 static void compose_attach_property(Compose *compose)
4781 {
4782         GtkCList *clist = GTK_CLIST(compose->attach_clist);
4783         AttachInfo *ainfo;
4784         gint row;
4785         GtkOptionMenu *optmenu;
4786         static gboolean cancelled;
4787
4788         if (!clist->selection) return;
4789         row = GPOINTER_TO_INT(clist->selection->data);
4790
4791         ainfo = gtk_clist_get_row_data(clist, row);
4792         if (!ainfo) return;
4793
4794         if (!attach_prop.window)
4795                 compose_attach_property_create(&cancelled);
4796         gtk_widget_grab_focus(attach_prop.ok_btn);
4797         gtk_widget_show(attach_prop.window);
4798         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
4799
4800         optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu);
4801         if (ainfo->encoding == ENC_UNKNOWN)
4802                 gtk_option_menu_set_history(optmenu, ENC_BASE64);
4803         else
4804                 gtk_option_menu_set_history(optmenu, ainfo->encoding);
4805
4806         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
4807                            ainfo->content_type ? ainfo->content_type : "");
4808         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
4809                            ainfo->file ? ainfo->file : "");
4810         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
4811                            ainfo->name ? ainfo->name : "");
4812
4813         for (;;) {
4814                 gchar *text;
4815                 gchar *cnttype = NULL;
4816                 gchar *file = NULL;
4817                 off_t size = 0;
4818                 GtkWidget *menu;
4819                 GtkWidget *menuitem;
4820
4821                 cancelled = FALSE;
4822                 gtk_main();
4823
4824                 if (cancelled == TRUE) {
4825                         gtk_widget_hide(attach_prop.window);
4826                         break;
4827                 }
4828
4829                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
4830                 if (*text != '\0') {
4831                         gchar *p;
4832
4833                         text = g_strstrip(g_strdup(text));
4834                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
4835                                 cnttype = g_strdup(text);
4836                                 g_free(text);
4837                         } else {
4838                                 alertpanel_error(_("Invalid MIME type."));
4839                                 g_free(text);
4840                                 continue;
4841                         }
4842                 }
4843
4844                 menu = gtk_option_menu_get_menu(optmenu);
4845                 menuitem = gtk_menu_get_active(GTK_MENU(menu));
4846                 ainfo->encoding = GPOINTER_TO_INT
4847                         (gtk_object_get_user_data(GTK_OBJECT(menuitem)));
4848
4849                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
4850                 if (*text != '\0') {
4851                         if (is_file_exist(text) &&
4852                             (size = get_file_size(text)) > 0)
4853                                 file = g_strdup(text);
4854                         else {
4855                                 alertpanel_error
4856                                         (_("File doesn't exist or is empty."));
4857                                 g_free(cnttype);
4858                                 continue;
4859                         }
4860                 }
4861
4862                 text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
4863                 if (*text != '\0') {
4864                         g_free(ainfo->name);
4865                         ainfo->name = g_strdup(text);
4866                 }
4867
4868                 if (cnttype) {
4869                         g_free(ainfo->content_type);
4870                         ainfo->content_type = cnttype;
4871                 }
4872                 if (file) {
4873                         g_free(ainfo->file);
4874                         ainfo->file = file;
4875                 }
4876                 if (size)
4877                         ainfo->size = size;
4878
4879                 gtk_clist_set_text(clist, row, COL_MIMETYPE,
4880                                    ainfo->content_type);
4881                 gtk_clist_set_text(clist, row, COL_SIZE,
4882                                    to_human_readable(ainfo->size));
4883                 gtk_clist_set_text(clist, row, COL_NAME, ainfo->name);
4884
4885                 gtk_widget_hide(attach_prop.window);
4886                 break;
4887         }
4888 }
4889
4890 #define SET_LABEL_AND_ENTRY(str, entry, top) \
4891 { \
4892         label = gtk_label_new(str); \
4893         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
4894                          GTK_FILL, 0, 0, 0); \
4895         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
4896  \
4897         entry = gtk_entry_new(); \
4898         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
4899                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
4900 }
4901
4902 static void compose_attach_property_create(gboolean *cancelled)
4903 {
4904         GtkWidget *window;
4905         GtkWidget *vbox;
4906         GtkWidget *table;
4907         GtkWidget *label;
4908         GtkWidget *mimetype_entry;
4909         GtkWidget *hbox;
4910         GtkWidget *optmenu;
4911         GtkWidget *optmenu_menu;
4912         GtkWidget *menuitem;
4913         GtkWidget *path_entry;
4914         GtkWidget *filename_entry;
4915         GtkWidget *hbbox;
4916         GtkWidget *ok_btn;
4917         GtkWidget *cancel_btn;
4918         GList     *mime_type_list, *strlist;
4919
4920         debug_print("Creating attach_property window...\n");
4921
4922         window = gtk_window_new(GTK_WINDOW_DIALOG);
4923         gtk_widget_set_usize(window, 480, -1);
4924         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
4925         gtk_window_set_title(GTK_WINDOW(window), _("Property"));
4926         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
4927         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
4928         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
4929                            GTK_SIGNAL_FUNC(attach_property_delete_event),
4930                            cancelled);
4931         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
4932                            GTK_SIGNAL_FUNC(attach_property_key_pressed),
4933                            cancelled);
4934
4935         vbox = gtk_vbox_new(FALSE, 8);
4936         gtk_container_add(GTK_CONTAINER(window), vbox);
4937
4938         table = gtk_table_new(4, 2, FALSE);
4939         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
4940         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
4941         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
4942
4943         label = gtk_label_new(_("MIME type")); 
4944         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
4945                          GTK_FILL, 0, 0, 0); 
4946         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
4947         mimetype_entry = gtk_combo_new(); 
4948         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
4949                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
4950                          
4951         /* stuff with list */
4952         mime_type_list = procmime_get_mime_type_list();
4953         strlist = NULL;
4954         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
4955                 MimeType *type = (MimeType *) mime_type_list->data;
4956                 strlist = g_list_append(strlist, 
4957                                 g_strdup_printf("%s/%s",
4958                                         type->type, type->sub_type));
4959         }
4960         
4961         gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry), strlist);
4962
4963         for (mime_type_list = strlist; mime_type_list != NULL; 
4964                 mime_type_list = mime_type_list->next)
4965                 g_free(mime_type_list->data);
4966         g_list_free(strlist);
4967                          
4968         mimetype_entry = GTK_COMBO(mimetype_entry)->entry;                       
4969
4970         label = gtk_label_new(_("Encoding"));
4971         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
4972                          GTK_FILL, 0, 0, 0);
4973         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
4974
4975         hbox = gtk_hbox_new(FALSE, 0);
4976         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
4977                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
4978
4979         optmenu = gtk_option_menu_new();
4980         gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
4981
4982         optmenu_menu = gtk_menu_new();
4983         MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT);
4984         gtk_widget_set_sensitive(menuitem, FALSE);
4985         MENUITEM_ADD(optmenu_menu, menuitem, "8bit", ENC_8BIT);
4986         gtk_widget_set_sensitive(menuitem, FALSE);
4987         MENUITEM_ADD(optmenu_menu, menuitem, "quoted-printable", ENC_QUOTED_PRINTABLE);
4988         gtk_widget_set_sensitive(menuitem, FALSE);
4989         MENUITEM_ADD(optmenu_menu, menuitem, "base64", ENC_BASE64);
4990
4991         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
4992
4993         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
4994         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
4995
4996         gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
4997                                 &cancel_btn, _("Cancel"), NULL, NULL);
4998         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
4999         gtk_widget_grab_default(ok_btn);
5000
5001         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
5002                            GTK_SIGNAL_FUNC(attach_property_ok),
5003                            cancelled);
5004         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
5005                            GTK_SIGNAL_FUNC(attach_property_cancel),
5006                            cancelled);
5007
5008         gtk_widget_show_all(vbox);
5009
5010         attach_prop.window           = window;
5011         attach_prop.mimetype_entry   = mimetype_entry;
5012         attach_prop.encoding_optmenu = optmenu;
5013         attach_prop.path_entry       = path_entry;
5014         attach_prop.filename_entry   = filename_entry;
5015         attach_prop.ok_btn           = ok_btn;
5016         attach_prop.cancel_btn       = cancel_btn;
5017 }
5018
5019 #undef SET_LABEL_AND_ENTRY
5020
5021 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
5022 {
5023         *cancelled = FALSE;
5024         gtk_main_quit();
5025 }
5026
5027 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
5028 {
5029         *cancelled = TRUE;
5030         gtk_main_quit();
5031 }
5032
5033 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
5034                                          gboolean *cancelled)
5035 {
5036         *cancelled = TRUE;
5037         gtk_main_quit();
5038
5039         return TRUE;
5040 }
5041
5042 static void attach_property_key_pressed(GtkWidget *widget, GdkEventKey *event,
5043                                         gboolean *cancelled)
5044 {
5045         if (event && event->keyval == GDK_Escape) {
5046                 *cancelled = TRUE;
5047                 gtk_main_quit();
5048         }
5049 }
5050
5051 static void compose_exec_ext_editor(Compose *compose)
5052 {
5053         gchar tmp[64];
5054         pid_t pid;
5055         gint pipe_fds[2];
5056
5057         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%08x",
5058                    g_get_tmp_dir(), G_DIR_SEPARATOR, (gint)compose);
5059
5060         if (pipe(pipe_fds) < 0) {
5061                 perror("pipe");
5062                 return;
5063         }
5064
5065         if ((pid = fork()) < 0) {
5066                 perror("fork");
5067                 return;
5068         }
5069
5070         if (pid != 0) {
5071                 /* close the write side of the pipe */
5072                 close(pipe_fds[1]);
5073
5074                 compose->exteditor_file    = g_strdup(tmp);
5075                 compose->exteditor_pid     = pid;
5076                 compose->exteditor_readdes = pipe_fds[0];
5077
5078                 compose_set_ext_editor_sensitive(compose, FALSE);
5079
5080                 compose->exteditor_tag =
5081                         gdk_input_add(pipe_fds[0], GDK_INPUT_READ,
5082                                       compose_input_cb, compose);
5083         } else {        /* process-monitoring process */
5084                 pid_t pid_ed;
5085
5086                 if (setpgid(0, 0))
5087                         perror("setpgid");
5088
5089                 /* close the read side of the pipe */
5090                 close(pipe_fds[0]);
5091
5092                 if (compose_write_body_to_file(compose, tmp) < 0) {
5093                         fd_write(pipe_fds[1], "2\n", 2);
5094                         _exit(1);
5095                 }
5096
5097                 pid_ed = compose_exec_ext_editor_real(tmp);
5098                 if (pid_ed < 0) {
5099                         fd_write(pipe_fds[1], "1\n", 2);
5100                         _exit(1);
5101                 }
5102
5103                 /* wait until editor is terminated */
5104                 waitpid(pid_ed, NULL, 0);
5105
5106                 fd_write(pipe_fds[1], "0\n", 2);
5107
5108                 close(pipe_fds[1]);
5109                 _exit(0);
5110         }
5111 }
5112
5113 static gint compose_exec_ext_editor_real(const gchar *file)
5114 {
5115         static gchar *def_cmd = "emacs %s";
5116         gchar buf[1024];
5117         gchar *p;
5118         gchar **cmdline;
5119         pid_t pid;
5120
5121         g_return_val_if_fail(file != NULL, -1);
5122
5123         if ((pid = fork()) < 0) {
5124                 perror("fork");
5125                 return -1;
5126         }
5127
5128         if (pid != 0) return pid;
5129
5130         /* grandchild process */
5131
5132         if (setpgid(0, getppid()))
5133                 perror("setpgid");
5134
5135         if (prefs_common.ext_editor_cmd &&
5136             (p = strchr(prefs_common.ext_editor_cmd, '%')) &&
5137             *(p + 1) == 's' && !strchr(p + 2, '%')) {
5138                 g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, file);
5139         } else {
5140                 if (prefs_common.ext_editor_cmd)
5141                         g_warning(_("External editor command line is invalid: `%s'\n"),
5142                                   prefs_common.ext_editor_cmd);
5143                 g_snprintf(buf, sizeof(buf), def_cmd, file);
5144         }
5145
5146         cmdline = g_strsplit(buf, " ", 1024);
5147         execvp(cmdline[0], cmdline);
5148
5149         perror("execvp");
5150         g_strfreev(cmdline);
5151
5152         _exit(1);
5153 }
5154
5155 static gboolean compose_ext_editor_kill(Compose *compose)
5156 {
5157         pid_t pgid = compose->exteditor_pid * -1;
5158         gint ret;
5159
5160         ret = kill(pgid, 0);
5161
5162         if (ret == 0 || (ret == -1 && EPERM == errno)) {
5163                 AlertValue val;
5164                 gchar *msg;
5165
5166                 msg = g_strdup_printf
5167                         (_("The external editor is still working.\n"
5168                            "Force terminating the process?\n"
5169                            "process group id: %d"), -pgid);
5170                 val = alertpanel(_("Notice"), msg, _("Yes"), _("+No"), NULL);
5171                 g_free(msg);
5172
5173                 if (val == G_ALERTDEFAULT) {
5174                         gdk_input_remove(compose->exteditor_tag);
5175                         close(compose->exteditor_readdes);
5176
5177                         if (kill(pgid, SIGTERM) < 0) perror("kill");
5178                         waitpid(compose->exteditor_pid, NULL, 0);
5179
5180                         g_warning(_("Terminated process group id: %d"), -pgid);
5181                         g_warning(_("Temporary file: %s"),
5182                                   compose->exteditor_file);
5183
5184                         compose_set_ext_editor_sensitive(compose, TRUE);
5185
5186                         g_free(compose->exteditor_file);
5187                         compose->exteditor_file    = NULL;
5188                         compose->exteditor_pid     = -1;
5189                         compose->exteditor_readdes = -1;
5190                         compose->exteditor_tag     = -1;
5191                 } else
5192                         return FALSE;
5193         }
5194
5195         return TRUE;
5196 }
5197
5198 static void compose_input_cb(gpointer data, gint source,
5199                              GdkInputCondition condition)
5200 {
5201         gchar buf[3];
5202         Compose *compose = (Compose *)data;
5203         gint i = 0;
5204
5205         debug_print(_("Compose: input from monitoring process\n"));
5206
5207         gdk_input_remove(compose->exteditor_tag);
5208
5209         for (;;) {
5210                 if (read(source, &buf[i], 1) < 1) {
5211                         buf[0] = '3';
5212                         break;
5213                 }
5214                 if (buf[i] == '\n') {
5215                         buf[i] = '\0';
5216                         break;
5217                 }
5218                 i++;
5219                 if (i == sizeof(buf) - 1)
5220                         break;
5221         }
5222
5223         waitpid(compose->exteditor_pid, NULL, 0);
5224
5225         if (buf[0] == '0') {            /* success */
5226                 GtkSText *text = GTK_STEXT(compose->text);
5227
5228                 gtk_stext_freeze(text);
5229                 gtk_stext_set_point(text, 0);
5230                 gtk_stext_forward_delete(text, gtk_stext_get_length(text));
5231                 compose_insert_file(compose, compose->exteditor_file);
5232                 compose_changed_cb(NULL, compose);
5233                 gtk_stext_thaw(text);
5234
5235                 if (unlink(compose->exteditor_file) < 0)
5236                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
5237         } else if (buf[0] == '1') {     /* failed */
5238                 g_warning(_("Couldn't exec external editor\n"));
5239                 if (unlink(compose->exteditor_file) < 0)
5240                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
5241         } else if (buf[0] == '2') {
5242                 g_warning(_("Couldn't write to file\n"));
5243         } else if (buf[0] == '3') {
5244                 g_warning(_("Pipe read failed\n"));
5245         }
5246
5247         close(source);
5248
5249         compose_set_ext_editor_sensitive(compose, TRUE);
5250
5251         g_free(compose->exteditor_file);
5252         compose->exteditor_file    = NULL;
5253         compose->exteditor_pid     = -1;
5254         compose->exteditor_readdes = -1;
5255         compose->exteditor_tag     = -1;
5256 }
5257
5258 static void compose_set_ext_editor_sensitive(Compose *compose,
5259                                              gboolean sensitive)
5260 {
5261         GtkItemFactory *ifactory;
5262
5263         ifactory = gtk_item_factory_from_widget(compose->menubar);
5264
5265         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
5266         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
5267         menu_set_sensitive(ifactory, "/Message/Save to draft folder",
5268                            sensitive);
5269         menu_set_sensitive(ifactory, "/File/Insert file", sensitive);
5270         menu_set_sensitive(ifactory, "/File/Insert signature", sensitive);
5271         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
5272         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
5273         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
5274                            sensitive);
5275
5276         gtk_widget_set_sensitive(compose->text,          sensitive);
5277         gtk_widget_set_sensitive(compose->send_btn,      sensitive);
5278         gtk_widget_set_sensitive(compose->sendl_btn,     sensitive);
5279         gtk_widget_set_sensitive(compose->draft_btn,     sensitive);
5280         gtk_widget_set_sensitive(compose->insert_btn,    sensitive);
5281         gtk_widget_set_sensitive(compose->sig_btn,       sensitive);
5282         gtk_widget_set_sensitive(compose->exteditor_btn, sensitive);
5283         gtk_widget_set_sensitive(compose->linewrap_btn,  sensitive);
5284 }
5285
5286 static gint calc_cursor_xpos(GtkSText *text, gint extra, gint char_width)
5287 {
5288         gint cursor_pos;
5289
5290         cursor_pos = (text->cursor_pos_x - extra) / char_width;
5291         cursor_pos = MAX(cursor_pos, 0);
5292
5293         return cursor_pos;
5294 }
5295
5296 /* callback functions */
5297
5298 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
5299  * includes "non-client" (windows-izm) in calculation, so this calculation
5300  * may not be accurate.
5301  */
5302 static gboolean compose_edit_size_alloc(GtkEditable *widget,
5303                                         GtkAllocation *allocation,
5304                                         GtkSHRuler *shruler)
5305 {
5306         if (prefs_common.show_ruler) {
5307                 gint char_width;
5308                 gint line_width_in_chars;
5309
5310                 char_width = gtkut_get_font_width
5311                         (GTK_WIDGET(widget)->style->font);
5312                 line_width_in_chars =
5313                         (allocation->width - allocation->x) / char_width;
5314
5315                 /* got the maximum */
5316                 gtk_ruler_set_range(GTK_RULER(shruler),
5317                                     0.0, line_width_in_chars,
5318                                     calc_cursor_xpos(GTK_STEXT(widget),
5319                                                      allocation->x,
5320                                                      char_width),
5321                                     /*line_width_in_chars*/ char_width);
5322         }
5323
5324         return TRUE;
5325 }
5326
5327 static void toolbar_send_cb(GtkWidget *widget, gpointer data)
5328 {
5329         compose_send_cb(data, 0, NULL);
5330 }
5331
5332 static void toolbar_send_later_cb(GtkWidget *widget, gpointer data)
5333 {
5334         compose_send_later_cb(data, 0, NULL);
5335 }
5336
5337 static void toolbar_draft_cb(GtkWidget *widget, gpointer data)
5338 {
5339         compose_draft_cb(data, 0, NULL);
5340 }
5341
5342 static void toolbar_insert_cb(GtkWidget *widget, gpointer data)
5343 {
5344         compose_insert_file_cb(data, 0, NULL);
5345 }
5346
5347 static void toolbar_attach_cb(GtkWidget *widget, gpointer data)
5348 {
5349         compose_attach_cb(data, 0, NULL);
5350 }
5351
5352 static void toolbar_sig_cb(GtkWidget *widget, gpointer data)
5353 {
5354         Compose *compose = (Compose *)data;
5355
5356         compose_insert_sig(compose);
5357 }
5358
5359 static void toolbar_ext_editor_cb(GtkWidget *widget, gpointer data)
5360 {
5361         Compose *compose = (Compose *)data;
5362
5363         compose_exec_ext_editor(compose);
5364 }
5365
5366 static void toolbar_linewrap_cb(GtkWidget *widget, gpointer data)
5367 {
5368         Compose *compose = (Compose *)data;
5369
5370         compose_wrap_line(compose);
5371 }
5372
5373 static void toolbar_address_cb(GtkWidget *widget, gpointer data)
5374 {
5375         compose_address_cb(data, 0, NULL);
5376 }
5377
5378 static void select_account(Compose * compose, PrefsAccount * ac)
5379 {
5380         compose->account = ac;
5381         compose_set_title(compose);
5382
5383 #if 0 /* NEW COMPOSE GUI */
5384                 if (ac->protocol == A_NNTP) {
5385                         GtkItemFactory *ifactory;
5386                         GtkWidget *menuitem;
5387
5388                         ifactory = gtk_item_factory_from_widget(compose->menubar);
5389                         menu_set_sensitive(ifactory,
5390                                            "/Message/Followup to", TRUE);
5391                         gtk_widget_show(compose->newsgroups_hbox);
5392                         gtk_widget_show(compose->newsgroups_entry);
5393                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5394                                                   1, 4);
5395
5396                         compose->use_to = FALSE;
5397                         compose->use_cc = FALSE;
5398
5399                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
5400                         gtk_check_menu_item_set_active
5401                                 (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
5402
5403                         menu_set_sensitive(ifactory,
5404                                            "/Message/To", TRUE);
5405                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
5406                         gtk_check_menu_item_set_active
5407                                 (GTK_CHECK_MENU_ITEM(menuitem), FALSE);
5408
5409                         gtk_widget_hide(compose->to_hbox);
5410                         gtk_widget_hide(compose->to_entry);
5411                         gtk_widget_hide(compose->cc_hbox);
5412                         gtk_widget_hide(compose->cc_entry);
5413                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5414                                                   0, 0);
5415                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5416                                                   3, 0);
5417                 }
5418                 else {
5419                         GtkItemFactory *ifactory;
5420                         GtkWidget *menuitem;
5421
5422                         ifactory = gtk_item_factory_from_widget(compose->menubar);
5423                         menu_set_sensitive(ifactory,
5424                                            "/Message/Followup to", FALSE);
5425                         gtk_entry_set_text(GTK_ENTRY(compose->newsgroups_entry), "");
5426                         gtk_widget_hide(compose->newsgroups_hbox);
5427                         gtk_widget_hide(compose->newsgroups_entry);
5428                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5429                                                   1, 0);
5430
5431                         compose->use_to = TRUE;
5432                         compose->use_cc = TRUE;
5433
5434                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
5435                         gtk_check_menu_item_set_active
5436                                 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
5437                         menu_set_sensitive(ifactory,
5438                                            "/Message/To", FALSE);
5439                         menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
5440                         gtk_check_menu_item_set_active
5441                                 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
5442                         gtk_widget_show(compose->to_hbox);
5443                         gtk_widget_show(compose->to_entry);
5444                         gtk_widget_show(compose->cc_hbox);
5445                         gtk_widget_show(compose->cc_entry);
5446
5447                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5448                                                   0, 4);
5449                         gtk_table_set_row_spacing(GTK_TABLE(compose->table),
5450                                                   3, 4);
5451                 }
5452                 gtk_widget_queue_resize(compose->table_vbox);
5453 #endif
5454 }
5455
5456 static void account_activated(GtkMenuItem *menuitem, gpointer data)
5457 {
5458         Compose *compose = (Compose *)data;
5459
5460         PrefsAccount *ac;
5461
5462         ac = (PrefsAccount *)gtk_object_get_user_data(GTK_OBJECT(menuitem));
5463         g_return_if_fail(ac != NULL);
5464
5465         if (ac != compose->account)
5466                 select_account(compose, ac);
5467 }
5468
5469 static void attach_selected(GtkCList *clist, gint row, gint column,
5470                             GdkEvent *event, gpointer data)
5471 {
5472         Compose *compose = (Compose *)data;
5473
5474         if (event && event->type == GDK_2BUTTON_PRESS)
5475                 compose_attach_property(compose);
5476 }
5477
5478 static void attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
5479                                   gpointer data)
5480 {
5481         Compose *compose = (Compose *)data;
5482         GtkCList *clist = GTK_CLIST(compose->attach_clist);
5483         gint row, column;
5484
5485         if (!event) return;
5486
5487         if (event->button == 3) {
5488                 if ((clist->selection && !clist->selection->next) ||
5489                     !clist->selection) {
5490                         gtk_clist_unselect_all(clist);
5491                         if (gtk_clist_get_selection_info(clist,
5492                                                          event->x, event->y,
5493                                                          &row, &column)) {
5494                                 gtk_clist_select_row(clist, row, column);
5495                                 gtkut_clist_set_focus_row(clist, row);
5496                         }
5497                 }
5498                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
5499                                NULL, NULL, event->button, event->time);
5500         }
5501 }
5502
5503 static void attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
5504                                gpointer data)
5505 {
5506         Compose *compose = (Compose *)data;
5507
5508         if (!event) return;
5509
5510         switch (event->keyval) {
5511         case GDK_Delete:
5512                 compose_attach_remove_selected(compose);
5513                 break;
5514         }
5515 }
5516
5517 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
5518 {
5519         Compose *compose = (Compose *)data;
5520         gint val;
5521
5522         val = compose_send(compose);
5523         if (val == 0) gtk_widget_destroy(compose->window);
5524 }
5525
5526 static void compose_send_later_cb(gpointer data, guint action,
5527                                   GtkWidget *widget)
5528 {
5529         Compose *compose = (Compose *)data;
5530         gint val;
5531
5532         val = compose_queue(compose, NULL, NULL);
5533         if (!val) gtk_widget_destroy(compose->window);
5534 }
5535
5536 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
5537 {
5538         Compose *compose = (Compose *)data;
5539         FolderItem *draft;
5540         gchar *tmp;
5541
5542         draft = folder_get_default_draft();
5543
5544         tmp = g_strdup_printf("%s%cdraft.%d", g_get_tmp_dir(),
5545                               G_DIR_SEPARATOR, (gint)compose);
5546
5547         if (compose_write_to_file(compose, tmp, TRUE) < 0) {
5548                 g_free(tmp);
5549                 return;
5550         }
5551
5552         folder_item_scan(draft);
5553         if (folder_item_add_msg(draft, tmp, TRUE) < 0) {
5554                 unlink(tmp);
5555                 g_free(tmp);
5556                 return;
5557         }
5558         g_free(tmp);
5559
5560         if (compose->mode == COMPOSE_REEDIT) {
5561                 compose_remove_reedit_target(compose);
5562                 if (compose->targetinfo &&
5563                     compose->targetinfo->folder != draft)
5564                         folderview_update_item(compose->targetinfo->folder,
5565                                                TRUE);
5566         }
5567
5568         folder_item_scan(draft);
5569         folderview_update_item(draft, TRUE);
5570
5571         gtk_widget_destroy(compose->window);
5572 }
5573
5574 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
5575 {
5576         Compose *compose = (Compose *)data;
5577         GList *file_list;
5578
5579         if (compose->bounce_filename != NULL)
5580                 return;
5581
5582         file_list = filesel_select_multiple_files(_("Select file"), NULL);
5583
5584         if (file_list) {
5585                 GList *tmp;
5586
5587                 for ( tmp = file_list; tmp; tmp = tmp->next) {
5588                         gchar *file = (gchar *) tmp->data;
5589                         compose_attach_append(compose, file, MIME_UNKNOWN);
5590                         g_free(file);
5591                 }
5592                 g_list_free(file_list);
5593         }               
5594 }
5595
5596 static void compose_insert_file_cb(gpointer data, guint action,
5597                                    GtkWidget *widget)
5598 {
5599         Compose *compose = (Compose *)data;
5600         GList *file_list;
5601
5602         file_list = filesel_select_multiple_files(_("Select file"), NULL);
5603
5604         if (file_list) {
5605                 GList *tmp;
5606
5607                 for ( tmp = file_list; tmp; tmp = tmp->next) {
5608                         gchar *file = (gchar *) tmp->data;
5609                         compose_insert_file(compose, file);
5610                         g_free(file);
5611                 }
5612                 g_list_free(file_list);
5613         }
5614 }
5615
5616 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
5617                               gpointer data)
5618 {
5619         compose_close_cb(data, 0, NULL);
5620         return TRUE;
5621 }
5622
5623 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
5624 {
5625         Compose *compose = (Compose *)data;
5626         AlertValue val;
5627
5628         if (compose->exteditor_tag != -1) {
5629                 if (!compose_ext_editor_kill(compose))
5630                         return;
5631         }
5632
5633         if (compose->modified) {
5634                 val = alertpanel(_("Discard message"),
5635                                  _("This message has been modified. discard it?"),
5636                                  _("Discard"), _("to Draft"), _("Cancel"));
5637
5638                 switch (val) {
5639                 case G_ALERTDEFAULT:
5640                         break;
5641                 case G_ALERTALTERNATE:
5642                         compose_draft_cb(data, 0, NULL);
5643                         return;
5644                 default:
5645                         return;
5646                 }
5647         }
5648 #if USE_PSPELL
5649         if (compose->gtkpspell) {
5650                 gtkpspell_detach(compose->gtkpspell);
5651                 compose->gtkpspell = gtkpspell_delete(compose->gtkpspell);
5652         }
5653 #endif
5654         gtk_widget_destroy(compose->window);
5655 }
5656
5657 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
5658 {
5659         Compose *compose = (Compose *)data;
5660
5661         addressbook_open(compose);
5662 }
5663
5664 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
5665 {
5666         Compose *compose = (Compose *)data;
5667         Template *tmpl;
5668
5669         tmpl = gtk_object_get_data(GTK_OBJECT(widget), "template");
5670         g_return_if_fail(tmpl != NULL);
5671
5672         compose_template_apply(compose, tmpl);
5673 }
5674
5675 static void compose_ext_editor_cb(gpointer data, guint action,
5676                                   GtkWidget *widget)
5677 {
5678         Compose *compose = (Compose *)data;
5679
5680         compose_exec_ext_editor(compose);
5681 }
5682
5683 static void compose_destroy_cb(GtkWidget *widget, Compose *compose)
5684 {
5685         compose_destroy(compose);
5686 }
5687
5688 static void compose_cut_cb(Compose *compose)
5689 {
5690         if (compose->focused_editable &&
5691             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
5692                 gtk_editable_cut_clipboard
5693                         (GTK_EDITABLE(compose->focused_editable));
5694 }
5695
5696 static void compose_copy_cb(Compose *compose)
5697 {
5698         if (compose->focused_editable &&
5699             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
5700                 gtk_editable_copy_clipboard
5701                         (GTK_EDITABLE(compose->focused_editable));
5702 }
5703
5704 static void compose_paste_cb(Compose *compose)
5705 {
5706         if (compose->focused_editable &&
5707             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
5708                 gtk_editable_paste_clipboard
5709                         (GTK_EDITABLE(compose->focused_editable));
5710 }
5711
5712 static void compose_allsel_cb(Compose *compose)
5713 {
5714         if (compose->focused_editable &&
5715             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
5716                 gtk_editable_select_region
5717                         (GTK_EDITABLE(compose->focused_editable), 0, -1);
5718 }
5719
5720 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
5721 {
5722         if (GTK_IS_EDITABLE(widget))
5723                 compose->focused_editable = widget;
5724 }
5725
5726 static void compose_changed_cb(GtkEditable *editable, Compose *compose)
5727 {
5728         if (compose->modified == FALSE) {
5729                 compose->modified = TRUE;
5730                 compose_set_title(compose);
5731         }
5732 }
5733
5734 static void compose_button_press_cb(GtkWidget *widget, GdkEventButton *event,
5735                                     Compose *compose)
5736 {
5737         gtk_stext_set_point(GTK_STEXT(widget),
5738                            gtk_editable_get_position(GTK_EDITABLE(widget)));
5739 }
5740
5741 #if 0
5742 static void compose_key_press_cb(GtkWidget *widget, GdkEventKey *event,
5743                                  Compose *compose)
5744 {
5745         gtk_stext_set_point(GTK_STEXT(widget),
5746                            gtk_editable_get_position(GTK_EDITABLE(widget)));
5747 }
5748 #endif
5749
5750 #if 0 /* NEW COMPOSE GUI */
5751 static void compose_toggle_to_cb(gpointer data, guint action,
5752                                  GtkWidget *widget)
5753 {
5754         Compose *compose = (Compose *)data;
5755
5756         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5757                 gtk_widget_show(compose->to_hbox);
5758                 gtk_widget_show(compose->to_entry);
5759                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
5760                 compose->use_to = TRUE;
5761         } else {
5762                 gtk_widget_hide(compose->to_hbox);
5763                 gtk_widget_hide(compose->to_entry);
5764                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 0);
5765                 gtk_widget_queue_resize(compose->table_vbox);
5766                 compose->use_to = FALSE;
5767         }
5768
5769         if (addressbook_get_target_compose() == compose)
5770                 addressbook_set_target_compose(compose);
5771 }
5772
5773 static void compose_toggle_cc_cb(gpointer data, guint action,
5774                                  GtkWidget *widget)
5775 {
5776         Compose *compose = (Compose *)data;
5777
5778         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5779                 gtk_widget_show(compose->cc_hbox);
5780                 gtk_widget_show(compose->cc_entry);
5781                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 4);
5782                 compose->use_cc = TRUE;
5783         } else {
5784                 gtk_widget_hide(compose->cc_hbox);
5785                 gtk_widget_hide(compose->cc_entry);
5786                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 0);
5787                 gtk_widget_queue_resize(compose->table_vbox);
5788                 compose->use_cc = FALSE;
5789         }
5790
5791         if (addressbook_get_target_compose() == compose)
5792                 addressbook_set_target_compose(compose);
5793 }
5794
5795 static void compose_toggle_bcc_cb(gpointer data, guint action,
5796                                   GtkWidget *widget)
5797 {
5798         Compose *compose = (Compose *)data;
5799
5800         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5801                 gtk_widget_show(compose->bcc_hbox);
5802                 gtk_widget_show(compose->bcc_entry);
5803                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 4);
5804                 compose->use_bcc = TRUE;
5805         } else {
5806                 gtk_widget_hide(compose->bcc_hbox);
5807                 gtk_widget_hide(compose->bcc_entry);
5808                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 0);
5809                 gtk_widget_queue_resize(compose->table_vbox);
5810                 compose->use_bcc = FALSE;
5811         }
5812
5813         if (addressbook_get_target_compose() == compose)
5814                 addressbook_set_target_compose(compose);
5815 }
5816
5817 static void compose_toggle_replyto_cb(gpointer data, guint action,
5818                                       GtkWidget *widget)
5819 {
5820         Compose *compose = (Compose *)data;
5821
5822         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5823                 gtk_widget_show(compose->reply_hbox);
5824                 gtk_widget_show(compose->reply_entry);
5825                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 4);
5826                 compose->use_replyto = TRUE;
5827         } else {
5828                 gtk_widget_hide(compose->reply_hbox);
5829                 gtk_widget_hide(compose->reply_entry);
5830                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 0);
5831                 gtk_widget_queue_resize(compose->table_vbox);
5832                 compose->use_replyto = FALSE;
5833         }
5834 }
5835
5836 static void compose_toggle_followupto_cb(gpointer data, guint action,
5837                                          GtkWidget *widget)
5838 {
5839         Compose *compose = (Compose *)data;
5840
5841         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5842                 gtk_widget_show(compose->followup_hbox);
5843                 gtk_widget_show(compose->followup_entry);
5844                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 4);
5845                 compose->use_followupto = TRUE;
5846         } else {
5847                 gtk_widget_hide(compose->followup_hbox);
5848                 gtk_widget_hide(compose->followup_entry);
5849                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 0);
5850                 gtk_widget_queue_resize(compose->table_vbox);
5851                 compose->use_followupto = FALSE;
5852         }
5853 }
5854
5855 static void compose_toggle_attach_cb(gpointer data, guint action,
5856                                      GtkWidget *widget)
5857 {
5858         Compose *compose = (Compose *)data;
5859
5860         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5861                 gtk_widget_ref(compose->edit_vbox);
5862
5863                 gtk_container_remove(GTK_CONTAINER(compose->vbox2),
5864                                      compose->edit_vbox);
5865                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
5866                 gtk_box_pack_start(GTK_BOX(compose->vbox2), compose->paned,
5867                                    TRUE, TRUE, 0);
5868                 gtk_widget_show(compose->paned);
5869
5870                 gtk_widget_unref(compose->edit_vbox);
5871                 gtk_widget_unref(compose->paned);
5872
5873                 compose->use_attach = TRUE;
5874         } else {
5875                 gtk_widget_ref(compose->paned);
5876                 gtk_widget_ref(compose->edit_vbox);
5877
5878                 gtk_container_remove(GTK_CONTAINER(compose->vbox2),
5879                                      compose->paned);
5880                 gtk_container_remove(GTK_CONTAINER(compose->paned),
5881                                      compose->edit_vbox);
5882                 gtk_box_pack_start(GTK_BOX(compose->vbox2),
5883                                    compose->edit_vbox, TRUE, TRUE, 0);
5884
5885                 gtk_widget_unref(compose->edit_vbox);
5886
5887                 compose->use_attach = FALSE;
5888         }
5889 }
5890 #endif
5891
5892 #if USE_GPGME
5893 static void compose_toggle_sign_cb(gpointer data, guint action,
5894                                    GtkWidget *widget)
5895 {
5896         Compose *compose = (Compose *)data;
5897
5898         if (GTK_CHECK_MENU_ITEM(widget)->active)
5899                 compose->use_signing = TRUE;
5900         else
5901                 compose->use_signing = FALSE;
5902 }
5903
5904 static void compose_toggle_encrypt_cb(gpointer data, guint action,
5905                                       GtkWidget *widget)
5906 {
5907         Compose *compose = (Compose *)data;
5908
5909         if (GTK_CHECK_MENU_ITEM(widget)->active)
5910                 compose->use_encryption = TRUE;
5911         else
5912                 compose->use_encryption = FALSE;
5913 }
5914 #endif /* USE_GPGME */
5915
5916 static void compose_toggle_ruler_cb(gpointer data, guint action,
5917                                     GtkWidget *widget)
5918 {
5919         Compose *compose = (Compose *)data;
5920
5921         if (GTK_CHECK_MENU_ITEM(widget)->active) {
5922                 gtk_widget_show(compose->ruler_hbox);
5923                 prefs_common.show_ruler = TRUE;
5924         } else {
5925                 gtk_widget_hide(compose->ruler_hbox);
5926                 gtk_widget_queue_resize(compose->edit_vbox);
5927                 prefs_common.show_ruler = FALSE;
5928         }
5929 }
5930
5931 static void compose_attach_drag_received_cb (GtkWidget          *widget,
5932                                              GdkDragContext     *drag_context,
5933                                              gint                x,
5934                                              gint                y,
5935                                              GtkSelectionData   *data,
5936                                              guint               info,
5937                                              guint               time,
5938                                              gpointer            user_data)
5939 {
5940         Compose *compose = (Compose *)user_data;
5941         GList *list, *tmp;
5942
5943         list = uri_list_extract_filenames((const gchar *)data->data);
5944         for (tmp = list; tmp != NULL; tmp = tmp->next)
5945                 compose_attach_append(compose, (const gchar *)tmp->data,
5946                                       MIME_UNKNOWN);
5947         list_free_strings(list);
5948         g_list_free(list);
5949 }
5950
5951 static void compose_insert_drag_received_cb (GtkWidget          *widget,
5952                                              GdkDragContext     *drag_context,
5953                                              gint                x,
5954                                              gint                y,
5955                                              GtkSelectionData   *data,
5956                                              guint               info,
5957                                              guint               time,
5958                                              gpointer            user_data)
5959 {
5960         Compose *compose = (Compose *)user_data;
5961         GList *list, *tmp;
5962
5963         list = uri_list_extract_filenames((const gchar *)data->data);
5964         for (tmp = list; tmp != NULL; tmp = tmp->next)
5965                 compose_insert_file(compose, (const gchar *)tmp->data);
5966         list_free_strings(list);
5967         g_list_free(list);
5968 }
5969
5970 #if 0 /* NEW COMPOSE GUI */
5971 static void to_activated(GtkWidget *widget, Compose *compose)
5972 {
5973         if (GTK_WIDGET_VISIBLE(compose->newsgroups_entry))
5974                 gtk_widget_grab_focus(compose->newsgroups_entry);
5975         else
5976                 gtk_widget_grab_focus(compose->subject_entry);
5977 }
5978
5979 static void newsgroups_activated(GtkWidget *widget, Compose *compose)
5980 {
5981         gtk_widget_grab_focus(compose->subject_entry);
5982 }
5983
5984 static void subject_activated(GtkWidget *widget, Compose *compose)
5985 {
5986         if (GTK_WIDGET_VISIBLE(compose->cc_entry))
5987                 gtk_widget_grab_focus(compose->cc_entry);
5988         else if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
5989                 gtk_widget_grab_focus(compose->bcc_entry);
5990         else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
5991                 gtk_widget_grab_focus(compose->reply_entry);
5992         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
5993                 gtk_widget_grab_focus(compose->followup_entry);
5994         else
5995                 gtk_widget_grab_focus(compose->text);
5996 }
5997
5998 static void cc_activated(GtkWidget *widget, Compose *compose)
5999 {
6000         if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
6001                 gtk_widget_grab_focus(compose->bcc_entry);
6002         else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
6003                 gtk_widget_grab_focus(compose->reply_entry);
6004         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
6005                 gtk_widget_grab_focus(compose->followup_entry);
6006         else
6007                 gtk_widget_grab_focus(compose->text);
6008 }
6009
6010 static void bcc_activated(GtkWidget *widget, Compose *compose)
6011 {
6012         if (GTK_WIDGET_VISIBLE(compose->reply_entry))
6013                 gtk_widget_grab_focus(compose->reply_entry);
6014         else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
6015                 gtk_widget_grab_focus(compose->followup_entry);
6016         else
6017                 gtk_widget_grab_focus(compose->text);
6018 }
6019
6020 static void replyto_activated(GtkWidget *widget, Compose *compose)
6021 {
6022         if (GTK_WIDGET_VISIBLE(compose->followup_entry))
6023                 gtk_widget_grab_focus(compose->followup_entry);
6024         else
6025                 gtk_widget_grab_focus(compose->text);
6026 }
6027
6028 static void followupto_activated(GtkWidget *widget, Compose *compose)
6029 {
6030         gtk_widget_grab_focus(compose->text);
6031 }
6032 #endif
6033
6034 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
6035                                              GtkWidget *widget)
6036 {
6037         Compose *compose = (Compose *)data;
6038
6039         if (GTK_CHECK_MENU_ITEM(widget)->active)
6040                 compose->return_receipt = TRUE;
6041         else
6042                 compose->return_receipt = FALSE;
6043 }
6044
6045 void compose_headerentry_key_press_event_cb(GtkWidget *entry,
6046                                             GdkEventKey *event,
6047                                             ComposeHeaderEntry *headerentry)
6048 {
6049         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
6050             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
6051             !(event->state & GDK_MODIFIER_MASK) &&
6052             (event->keyval == GDK_BackSpace) &&
6053             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
6054                 gtk_container_remove
6055                         (GTK_CONTAINER(headerentry->compose->header_table),
6056                          headerentry->combo);
6057                 gtk_container_remove
6058                         (GTK_CONTAINER(headerentry->compose->header_table),
6059                          headerentry->entry);
6060                 headerentry->compose->header_list =
6061                         g_slist_remove(headerentry->compose->header_list,
6062                                        headerentry);
6063                 g_free(headerentry);
6064         }
6065 }
6066
6067 void compose_headerentry_changed_cb(GtkWidget *entry,
6068                                     ComposeHeaderEntry *headerentry)
6069 {
6070         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
6071                 headerentry->compose->header_list =
6072                         g_slist_append(headerentry->compose->header_list,
6073                                        headerentry);
6074                 compose_create_header_entry(headerentry->compose);
6075                 gtk_signal_disconnect_by_func
6076                         (GTK_OBJECT(entry),
6077                          GTK_SIGNAL_FUNC(compose_headerentry_changed_cb),
6078                          headerentry);
6079         }
6080 }