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