Improved wrapping on send to be smarter when quotation string is empty. Fix after...
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkitemfactory.h>
32 #include <gtk/gtkcheckmenuitem.h>
33 #include <gtk/gtkoptionmenu.h>
34 #include <gtk/gtkwidget.h>
35 #include <gtk/gtkclist.h>
36 #include <gtk/gtkctree.h>
37 #include <gtk/gtkvpaned.h>
38 #include <gtk/gtkentry.h>
39 #include <gtk/gtkeditable.h>
40 #include <gtk/gtkwindow.h>
41 #include <gtk/gtksignal.h>
42 #include <gtk/gtkvbox.h>
43 #include <gtk/gtkcontainer.h>
44 #include <gtk/gtkhandlebox.h>
45 #include <gtk/gtktoolbar.h>
46 #include <gtk/gtktable.h>
47 #include <gtk/gtkhbox.h>
48 #include <gtk/gtklabel.h>
49 #include <gtk/gtkscrolledwindow.h>
50 #include <gtk/gtkthemes.h>
51 #include <gtk/gtkdnd.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include <unistd.h>
57 #include <time.h>
58 /* #include <sys/utsname.h> */
59 #include <stdlib.h>
60 #include <sys/wait.h>
61 #include <signal.h>
62 #include <errno.h>
63
64 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
65 #  include <wchar.h>
66 #  include <wctype.h>
67 #endif
68
69
70 #include "gtkstext.h"
71
72 #include "intl.h"
73 #include "main.h"
74 #include "mainwindow.h"
75 #include "compose.h"
76 #include "addressbook.h"
77 #include "folderview.h"
78 #include "procmsg.h"
79 #include "menu.h"
80 #include "send.h"
81 #include "news.h"
82 #include "customheader.h"
83 #include "prefs_common.h"
84 #include "prefs_account.h"
85 #include "account.h"
86 #include "filesel.h"
87 #include "procheader.h"
88 #include "procmime.h"
89 #include "statusbar.h"
90 #include "about.h"
91 #include "base64.h"
92 #include "codeconv.h"
93 #include "utils.h"
94 #include "gtkutils.h"
95 #include "socket.h"
96 #include "alertpanel.h"
97 #include "manage_window.h"
98 #include "gtkshruler.h"
99 #include "folder.h"
100 #include "addr_compl.h"
101 #include "quote_fmt.h"
102 #include "template.h"
103
104 #if USE_GPGME
105 #  include "rfc2015.h"
106 #endif
107
108 typedef enum
109 {
110         COL_MIMETYPE = 0,
111         COL_SIZE     = 1,
112         COL_NAME     = 2
113 } AttachColumnPos;
114
115 #define N_ATTACH_COLS           3
116
117 #define B64_LINE_SIZE           57
118 #define B64_BUFFSIZE            77
119
120 #define MAX_REFERENCES_LEN      999
121
122 static GdkColor quote_color = {0, 0, 0, 0xbfff};
123
124 static GList *compose_list = NULL;
125
126 Compose *compose_generic_new                    (PrefsAccount   *account,
127                                                  const gchar    *to,
128                                                  FolderItem     *item);
129
130 static Compose *compose_create                  (PrefsAccount   *account,
131                                                  ComposeMode     mode);
132 static void compose_toolbar_create              (Compose        *compose,
133                                                  GtkWidget      *container);
134 static GtkWidget *compose_account_option_menu_create
135                                                 (Compose        *compose);
136 static void compose_set_template_menu           (Compose        *compose);
137 static void compose_template_apply              (Compose        *compose,
138                                                  Template       *tmpl);
139 static void compose_destroy                     (Compose        *compose);
140
141 static void compose_entries_set                 (Compose        *compose,
142                                                  const gchar    *mailto);
143 static gint compose_parse_header                (Compose        *compose,
144                                                  MsgInfo        *msginfo);
145 static gchar *compose_parse_references          (const gchar    *ref,
146                                                  const gchar    *msgid);
147
148 static gchar *compose_quote_fmt                 (Compose        *compose,
149                                                  MsgInfo        *msginfo,
150                                                  const gchar    *fmt,
151                                                  const gchar    *qmark);
152
153 static void compose_reply_set_entry             (Compose        *compose,
154                                                  MsgInfo        *msginfo,
155                                                  gboolean        to_all,
156                                                  gboolean        to_sender,
157                                                  gboolean
158                                                  followup_and_reply_to);
159 static void compose_reedit_set_entry            (Compose        *compose,
160                                                  MsgInfo        *msginfo);
161 static void compose_insert_sig                  (Compose        *compose);
162 static void compose_insert_file                 (Compose        *compose,
163                                                  const gchar    *file);
164 static void compose_attach_append               (Compose        *compose,
165                                                  const gchar    *file,
166                                                  ContentType     cnttype);
167 static void compose_attach_append_with_type(Compose *compose,
168                                             const gchar *file,
169                                             const gchar *type,
170                                             ContentType cnttype);
171 static void compose_wrap_line                   (Compose        *compose);
172 static void compose_wrap_line_all               (Compose        *compose);
173 static void compose_set_title                   (Compose        *compose);
174
175 static PrefsAccount *compose_current_mail_account(void);
176 /* static gint compose_send                     (Compose        *compose); */
177 static gint compose_write_to_file               (Compose        *compose,
178                                                  const gchar    *file,
179                                                  gboolean        is_draft);
180 static gint compose_write_body_to_file          (Compose        *compose,
181                                                  const gchar    *file);
182 static gint compose_save_to_outbox              (Compose        *compose,
183                                                  const gchar    *file);
184 static gint compose_remove_reedit_target        (Compose        *compose);
185 static gint compose_queue                       (Compose        *compose,
186                                                  gint           *msgnum,
187                                                  FolderItem     **item);
188 static void compose_write_attach                (Compose        *compose,
189                                                  FILE           *fp);
190 static gint compose_write_headers               (Compose        *compose,
191                                                  FILE           *fp,
192                                                  const gchar    *charset,
193                                                  EncodingType    encoding,
194                                                  gboolean        is_draft);
195
196 static void compose_convert_header              (gchar          *dest,
197                                                  gint            len,
198                                                  gchar          *src,
199                                                  gint            header_len);
200 static void compose_generate_msgid              (Compose        *compose,
201                                                  gchar          *buf,
202                                                  gint            len);
203
204 static void compose_attach_info_free            (AttachInfo     *ainfo);
205 static void compose_attach_remove_selected      (Compose        *compose);
206
207 static void compose_attach_property             (Compose        *compose);
208 static void compose_attach_property_create      (gboolean       *cancelled);
209 static void attach_property_ok                  (GtkWidget      *widget,
210                                                  gboolean       *cancelled);
211 static void attach_property_cancel              (GtkWidget      *widget,
212                                                  gboolean       *cancelled);
213 static gint attach_property_delete_event        (GtkWidget      *widget,
214                                                  GdkEventAny    *event,
215                                                  gboolean       *cancelled);
216 static void attach_property_key_pressed         (GtkWidget      *widget,
217                                                  GdkEventKey    *event,
218                                                  gboolean       *cancelled);
219
220 static void compose_exec_ext_editor             (Compose           *compose);
221 static gint compose_exec_ext_editor_real        (const gchar       *file);
222 static gboolean compose_ext_editor_kill         (Compose           *compose);
223 static void compose_input_cb                    (gpointer           data,
224                                                  gint               source,
225                                                  GdkInputCondition  condition);
226 static void compose_set_ext_editor_sensitive    (Compose           *compose,
227                                                  gboolean           sensitive);
228
229 static gint calc_cursor_xpos    (GtkSText       *text,
230                                  gint            extra,
231                                  gint            char_width);
232
233 static void compose_create_header_entry (Compose *compose);
234 static void compose_add_header_entry    (Compose *compose, gchar *header, gchar *text);
235
236 /* callback functions */
237
238 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
239                                          GtkAllocation  *allocation,
240                                          GtkSHRuler     *shruler);
241
242 static void toolbar_send_cb             (GtkWidget      *widget,
243                                          gpointer        data);
244 static void toolbar_send_later_cb       (GtkWidget      *widget,
245                                          gpointer        data);
246 static void toolbar_draft_cb            (GtkWidget      *widget,
247                                          gpointer        data);
248 static void toolbar_insert_cb           (GtkWidget      *widget,
249                                          gpointer        data);
250 static void toolbar_attach_cb           (GtkWidget      *widget,
251                                          gpointer        data);
252 static void toolbar_sig_cb              (GtkWidget      *widget,
253                                          gpointer        data);
254 static void toolbar_ext_editor_cb       (GtkWidget      *widget,
255                                          gpointer        data);
256 static void toolbar_linewrap_cb         (GtkWidget      *widget,
257                                          gpointer        data);
258 static void toolbar_address_cb          (GtkWidget      *widget,
259                                          gpointer        data);
260
261 static void select_account              (Compose        *compose,
262                                          PrefsAccount   *ac);
263
264 static void account_activated           (GtkMenuItem    *menuitem,
265                                          gpointer        data);
266
267 static void attach_selected             (GtkCList       *clist,
268                                          gint            row,
269                                          gint            column,
270                                          GdkEvent       *event,
271                                          gpointer        data);
272 static void attach_button_pressed       (GtkWidget      *widget,
273                                          GdkEventButton *event,
274                                          gpointer        data);
275 static void attach_key_pressed          (GtkWidget      *widget,
276                                          GdkEventKey    *event,
277                                          gpointer        data);
278
279 static void compose_send_cb             (gpointer        data,
280                                          guint           action,
281                                          GtkWidget      *widget);
282 static void compose_send_later_cb       (gpointer        data,
283                                          guint           action,
284                                          GtkWidget      *widget);
285
286 static void compose_draft_cb            (gpointer        data,
287                                          guint           action,
288                                          GtkWidget      *widget);
289
290 static void compose_attach_cb           (gpointer        data,
291                                          guint           action,
292                                          GtkWidget      *widget);
293 static void compose_insert_file_cb      (gpointer        data,
294                                          guint           action,
295                                          GtkWidget      *widget);
296
297 static void compose_close_cb            (gpointer        data,
298                                          guint           action,
299                                          GtkWidget      *widget);
300
301 static void compose_address_cb          (gpointer        data,
302                                          guint           action,
303                                          GtkWidget      *widget);
304 static void compose_template_activate_cb(GtkWidget      *widget,
305                                          gpointer        data);
306
307 static void compose_ext_editor_cb       (gpointer        data,
308                                          guint           action,
309                                          GtkWidget      *widget);
310
311 static gint compose_delete_cb           (GtkWidget      *widget,
312                                          GdkEventAny    *event,
313                                          gpointer        data);
314 static void compose_destroy_cb          (GtkWidget      *widget,
315                                          Compose        *compose);
316
317 static void compose_cut_cb              (Compose        *compose);
318 static void compose_copy_cb             (Compose        *compose);
319 static void compose_paste_cb            (Compose        *compose);
320 static void compose_allsel_cb           (Compose        *compose);
321
322 static void compose_grab_focus_cb       (GtkWidget      *widget,
323                                          Compose        *compose);
324
325 static void compose_changed_cb          (GtkEditable    *editable,
326                                          Compose        *compose);
327 static void compose_button_press_cb     (GtkWidget      *widget,
328                                          GdkEventButton *event,
329                                          Compose        *compose);
330 #if 0
331 static void compose_key_press_cb        (GtkWidget      *widget,
332                                          GdkEventKey    *event,
333                                          Compose        *compose);
334 #endif
335
336 #if 0
337 static void compose_toggle_to_cb        (gpointer        data,
338                                          guint           action,
339                                          GtkWidget      *widget);
340 static void compose_toggle_cc_cb        (gpointer        data,
341                                          guint           action,
342                                          GtkWidget      *widget);
343 static void compose_toggle_bcc_cb       (gpointer        data,
344                                          guint           action,
345                                          GtkWidget      *widget);
346 static void compose_toggle_replyto_cb   (gpointer        data,
347                                          guint           action,
348                                          GtkWidget      *widget);
349 static void compose_toggle_followupto_cb(gpointer        data,
350                                          guint           action,
351                                          GtkWidget      *widget);
352 static void compose_toggle_attach_cb    (gpointer        data,
353                                          guint           action,
354                                          GtkWidget      *widget);
355 #endif
356 static void compose_toggle_ruler_cb     (gpointer        data,
357                                          guint           action,
358                                          GtkWidget      *widget);
359 #if USE_GPGME
360 static void compose_toggle_sign_cb      (gpointer        data,
361                                          guint           action,
362                                          GtkWidget      *widget);
363 static void compose_toggle_encrypt_cb   (gpointer        data,
364                                          guint           action,
365                                          GtkWidget      *widget);
366 #endif
367 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
368                                              GtkWidget *widget);
369
370 static void compose_attach_drag_received_cb (GtkWidget          *widget,
371                                              GdkDragContext     *drag_context,
372                                              gint                x,
373                                              gint                y,
374                                              GtkSelectionData   *data,
375                                              guint               info,
376                                              guint               time,
377                                              gpointer            user_data);
378 static void compose_insert_drag_received_cb (GtkWidget          *widget,
379                                              GdkDragContext     *drag_context,
380                                              gint                x,
381                                              gint                y,
382                                              GtkSelectionData   *data,
383                                              guint               info,
384                                              guint               time,
385                                              gpointer            user_data);
386
387 #if 0
388 static void to_activated                (GtkWidget      *widget,
389                                          Compose        *compose);
390 static void newsgroups_activated        (GtkWidget      *widget,
391                                          Compose        *compose);
392 static void subject_activated           (GtkWidget      *widget,
393                                          Compose        *compose);
394 static void cc_activated                (GtkWidget      *widget,
395                                          Compose        *compose);
396 static void bcc_activated               (GtkWidget      *widget,
397                                          Compose        *compose);
398 static void replyto_activated           (GtkWidget      *widget,
399                                          Compose        *compose);
400 static void followupto_activated        (GtkWidget      *widget,
401                                          Compose        *compose);
402 #endif
403
404 static void compose_attach_parts        (Compose        *compose,
405                                          MsgInfo        *msginfo);
406
407 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
408                                   gboolean to_all,
409                                   gboolean ignore_replyto,
410                                   gboolean followup_and_reply_to);
411
412 void compose_headerentry_changed_cb        (GtkWidget          *entry,
413                                             ComposeHeaderEntry *headerentry);
414 void compose_headerentry_key_press_event_cb(GtkWidget          *entry,
415                                             GdkEventKey        *event,
416                                             ComposeHeaderEntry *headerentry);
417
418 static GtkItemFactoryEntry compose_popup_entries[] =
419 {
420         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
421         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
422         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
423         {N_("/_Property..."),   NULL, compose_attach_property, 0, NULL}
424 };
425
426 static GtkItemFactoryEntry compose_entries[] =
427 {
428         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
429         {N_("/_File/_Attach file"),             "<control>M", compose_attach_cb,      0, NULL},
430         {N_("/_File/_Insert file"),             "<control>I", compose_insert_file_cb, 0, NULL},
431         {N_("/_File/Insert si_gnature"),        "<control>G", compose_insert_sig,     0, NULL},
432         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
433         {N_("/_File/_Close"),                   "<alt>W", compose_close_cb, 0, NULL},
434
435         {N_("/_Edit"),             NULL, NULL, 0, "<Branch>"},
436         {N_("/_Edit/_Undo"),       "<control>Z", NULL, 0, NULL},
437         {N_("/_Edit/_Redo"),       "<control>Y", NULL, 0, NULL},
438         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
439         {N_("/_Edit/Cu_t"),        "<control>X", compose_cut_cb,    0, NULL},
440         {N_("/_Edit/_Copy"),       "<control>C", compose_copy_cb,   0, NULL},
441         {N_("/_Edit/_Paste"),      "<control>V", compose_paste_cb,  0, NULL},
442         {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
443         {N_("/_Edit/---"),         NULL, NULL, 0, "<Separator>"},
444         {N_("/_Edit/_Wrap current paragraph"), "<alt>L", compose_wrap_line, 0, NULL},
445         {N_("/_Edit/Wrap all long _lines"),
446                         "<shift><alt>L", compose_wrap_line_all, 0, NULL},
447         {N_("/_Edit/Edit with e_xternal editor"),
448                         "<alt>X", compose_ext_editor_cb, 0, NULL},
449
450         {N_("/_Message"),               NULL, NULL, 0, "<Branch>"},
451         {N_("/_Message/_Send"),         "<control>Return",
452                                         compose_send_cb, 0, NULL},
453         {N_("/_Message/Send _later"),   "<shift><alt>S",
454                                         compose_send_later_cb,  0, NULL},
455         {N_("/_Message/Save to _draft folder"),
456                                         "<alt>D", compose_draft_cb, 0, NULL},
457 #if 0 /* NEW COMPOSE GUI */
458         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
459         {N_("/_Message/_To"),           NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
460         {N_("/_Message/_Cc"),           NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
461         {N_("/_Message/_Bcc"),          NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
462         {N_("/_Message/_Reply to"),     NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
463         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
464         {N_("/_Message/_Followup to"),  NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
465         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
466         {N_("/_Message/_Attach"),       NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
467 #endif
468 #if USE_GPGME
469         {N_("/_Message/---"),           NULL, NULL, 0, "<Separator>"},
470         {N_("/_Message/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
471         {N_("/_Message/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
472 #endif /* USE_GPGME */
473         {N_("/_Message/---"),           NULL,           NULL,   0, "<Separator>"},
474         {N_("/_Message/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
475         {N_("/_Tool"),                  NULL, NULL, 0, "<Branch>"},
476         {N_("/_Tool/Show _ruler"),      NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
477         {N_("/_Tool/_Address book"),    "<alt>A", compose_address_cb , 0, NULL},
478         {N_("/_Tool/_Template"),        NULL, NULL, 0, "<Branch>"},
479         {N_("/_Help"),                  NULL, NULL, 0, "<LastBranch>"},
480         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
481 };
482
483 static GtkTargetEntry compose_mime_types[] =
484 {
485         {"text/uri-list", 0, 0}
486 };
487
488 Compose *compose_new(PrefsAccount *account)
489 {
490         return compose_generic_new(account, NULL, NULL);
491 }
492
493 Compose *compose_bounce(PrefsAccount *account, MsgInfo *msginfo)
494 {
495         Compose *c;
496         gchar *filename;
497         GtkItemFactory *ifactory;
498         
499         c = compose_generic_new(account, NULL, NULL);
500
501         filename = procmsg_get_message_file(msginfo);
502         if (filename == NULL)
503                 return NULL;
504
505         c->bounce_filename = filename;
506
507         if (msginfo->subject)
508                 gtk_entry_set_text(GTK_ENTRY(c->subject_entry),
509                                    msginfo->subject);
510         gtk_editable_set_editable(GTK_EDITABLE(c->subject_entry), FALSE);
511
512         compose_quote_fmt(c, msginfo, "%M", NULL);
513         gtk_editable_set_editable(GTK_EDITABLE(c->text), FALSE);
514
515         ifactory = gtk_item_factory_from_widget(c->popupmenu);
516         menu_set_sensitive(ifactory, "/Add...", FALSE);
517         menu_set_sensitive(ifactory, "/Remove", FALSE);
518         menu_set_sensitive(ifactory, "/Property...", FALSE);
519
520         ifactory = gtk_item_factory_from_widget(c->menubar);
521         menu_set_sensitive(ifactory, "/File/Insert file", FALSE);
522         menu_set_sensitive(ifactory, "/File/Attach file", FALSE);
523         menu_set_sensitive(ifactory, "/File/Insert signature", FALSE);
524         menu_set_sensitive(ifactory, "/Edit/Paste", FALSE);
525         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE);
526         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE);
527         menu_set_sensitive(ifactory, "/Edit/Edit with external editor", FALSE);
528         menu_set_sensitive(ifactory, "/Message/Attach", FALSE);
529 #if USE_GPGME
530         menu_set_sensitive(ifactory, "/Message/Sign", FALSE);
531         menu_set_sensitive(ifactory, "/Message/Encrypt", FALSE);
532 #endif
533         menu_set_sensitive(ifactory, "/Message/Request Return Receipt", FALSE);
534         menu_set_sensitive(ifactory, "/Tool/Template", FALSE);
535         
536         gtk_widget_set_sensitive(c->insert_btn, FALSE);
537         gtk_widget_set_sensitive(c->attach_btn, FALSE);
538         gtk_widget_set_sensitive(c->sig_btn, FALSE);
539         gtk_widget_set_sensitive(c->exteditor_btn, FALSE);
540         gtk_widget_set_sensitive(c->linewrap_btn, FALSE);
541
542         return c;
543 }
544
545 Compose *compose_new_with_recipient(PrefsAccount *account, const gchar *mailto)
546 {
547         return compose_generic_new(account, mailto, NULL);
548 }
549
550 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item)
551 {
552         return compose_generic_new(account, NULL, item);
553 }
554
555 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item)
556 {
557         Compose *compose;
558
559         if (item && item->prefs && item->prefs->enable_default_account)
560                 account = account_find_from_id(item->prefs->default_account);
561
562         if (!account) account = cur_account;
563         g_return_val_if_fail(account != NULL, NULL);
564
565         compose = compose_create(account, COMPOSE_NEW);
566         compose->replyinfo = NULL;
567
568         if (prefs_common.auto_sig)
569                 compose_insert_sig(compose);
570         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
571         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
572
573         if (account->protocol != A_NNTP) {
574                 if (mailto) {
575                         compose_entries_set(compose, mailto);
576
577                 } else if(item && item->prefs->enable_default_to) {
578                         compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
579                 }
580                 if (item && item->ret_rcpt) {
581                         GtkItemFactory *ifactory;
582                 
583                         ifactory = gtk_item_factory_from_widget(compose->menubar);
584                         menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
585                 }
586         } else {
587                 if (mailto) {
588                         compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
589                 }
590         }
591         gtk_widget_grab_focus(compose->subject_entry);
592
593         if (prefs_common.auto_exteditor)
594                 compose_exec_ext_editor(compose);
595
596         return compose;
597 }
598
599 #define CHANGE_FLAGS(msginfo) \
600 { \
601 if (msginfo->folder->folder->change_flags != NULL) \
602 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
603                                       msginfo->folder, \
604                                       msginfo); \
605 }
606
607 /*
608 Compose *compose_new_followup_and_replyto(PrefsAccount *account,
609                                            const gchar *followupto, gchar * to)
610 {
611         Compose *compose;
612
613         if (!account) account = cur_account;
614         g_return_val_if_fail(account != NULL, NULL);
615         g_return_val_if_fail(account->protocol != A_NNTP, NULL);
616
617         compose = compose_create(account, COMPOSE_NEW);
618
619         if (prefs_common.auto_sig)
620                 compose_insert_sig(compose);
621         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
622         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
623
624         compose_entry_append(compose, to, COMPOSE_TO);
625         compose_entry_append(compose, followupto, COMPOSE_NEWSGROUPS);
626         gtk_widget_grab_focus(compose->subject_entry);
627
628         return compose;
629 }
630 */
631
632 void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all,
633                    gboolean ignore_replyto)
634 {
635         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, FALSE);
636 }
637
638 void compose_followup_and_reply_to(MsgInfo *msginfo, gboolean quote,
639                                    gboolean to_all,
640                                    gboolean ignore_replyto)
641 {
642         compose_generic_reply(msginfo, quote, to_all, ignore_replyto, TRUE);
643 }
644
645 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
646                                   gboolean to_all,
647                                   gboolean ignore_replyto,
648                                   gboolean followup_and_reply_to)
649 {
650         Compose *compose;
651         PrefsAccount *account;
652         PrefsAccount *reply_account;
653         GtkSText *text;
654
655         g_return_if_fail(msginfo != NULL);
656         g_return_if_fail(msginfo->folder != NULL);
657
658         account = NULL;
659         /* select the account set in folderitem's property (if enabled) */
660         if (msginfo->folder->prefs && msginfo->folder->prefs->enable_default_account)
661                 account = account_find_from_id(msginfo->folder->prefs->default_account);
662         
663         /* select the account for the whole folder (IMAP / NNTP) */
664         if (!account)
665                 account = msginfo->folder->folder->account;
666
667         /* select account by to: and cc: header if enabled */
668         if (prefs_common.reply_account_autosel) {
669                 if (!account && msginfo->to) {
670                         gchar *to;
671                         Xstrdup_a(to, msginfo->to, return);
672                         extract_address(to);
673                         account = account_find_from_address(to);
674                 }
675                 if (!account) {
676                         gchar cc[BUFFSIZE];
677                         if(!get_header_from_msginfo(msginfo, cc, sizeof(cc), "CC:")) { /* Found a CC header */
678                                 extract_address(cc);
679                                 account = account_find_from_address(cc);
680                         }        
681                 }
682         }
683
684         /* select current account */
685         if (!account) account = cur_account;
686         g_return_if_fail(account != NULL);
687
688         if (ignore_replyto && account->protocol == A_NNTP &&
689             !followup_and_reply_to) {
690                 reply_account =
691                         account_find_from_address(account->address);
692                 if (!reply_account)
693                         reply_account = compose_current_mail_account();
694                 if (!reply_account)
695                         return;
696         } else
697                 reply_account = account;
698
699         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
700         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
701
702         CHANGE_FLAGS(msginfo);
703
704         compose = compose_create(account, COMPOSE_REPLY);
705         compose->replyinfo = procmsg_msginfo_copy(msginfo);
706
707 #if 0 /* NEW COMPOSE GUI */
708         if (followup_and_reply_to) {
709                 gtk_widget_show(compose->to_hbox);
710                 gtk_widget_show(compose->to_entry);
711                 gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
712                 compose->use_to = TRUE;
713         }
714 #endif
715         if (msginfo->folder && msginfo->folder->ret_rcpt) {
716                 GtkItemFactory *ifactory;
717         
718                 ifactory = gtk_item_factory_from_widget(compose->menubar);
719                 menu_set_toggle(ifactory, "/Message/Request Return Receipt", TRUE);
720         }
721
722         if (compose_parse_header(compose, msginfo) < 0) return;
723         compose_reply_set_entry(compose, msginfo, to_all, ignore_replyto,
724                                 followup_and_reply_to);
725
726         text = GTK_STEXT(compose->text);
727         gtk_stext_freeze(text);
728
729         if (quote) {
730                 gchar *qmark;
731                 gchar *quote_str;
732
733                 if (prefs_common.quotemark && *prefs_common.quotemark)
734                         qmark = prefs_common.quotemark;
735                 else
736                         qmark = "> ";
737
738                 quote_str = compose_quote_fmt(compose, msginfo,
739                                               prefs_common.quotefmt,
740                                               qmark);
741         }
742
743         if (prefs_common.auto_sig)
744                 compose_insert_sig(compose);
745         gtk_editable_set_position(GTK_EDITABLE(text), 0);
746         gtk_stext_set_point(text, 0);
747
748         if (quote && prefs_common.linewrap_quote)
749                 compose_wrap_line_all(compose);
750
751         gtk_stext_thaw(text);
752         gtk_widget_grab_focus(compose->text);
753
754         if (prefs_common.auto_exteditor)
755                 compose_exec_ext_editor(compose);
756 }
757
758
759 static gchar *procmime_get_file_name(MimeInfo *mimeinfo)
760 {
761         gchar *base;
762         gchar *filename;
763
764         g_return_val_if_fail(mimeinfo != NULL, NULL);
765
766         base = mimeinfo->filename ? mimeinfo->filename
767                 : mimeinfo->name ? mimeinfo->name : NULL;
768
769         if (MIME_TEXT_HTML == mimeinfo->mime_type && base == NULL){
770                 filename = g_strdup_printf("%s%smimetmp.%08x.html",
771                                            get_mime_tmp_dir(),
772                                            G_DIR_SEPARATOR_S,
773                                            (gint)mimeinfo);
774                 return filename;
775         }
776         else {
777                 base = base ? base : "";
778                 base = g_basename(base);
779                 if (*base == '\0') {
780                         filename = g_strdup_printf("%s%smimetmp.%08x",
781                                                    get_mime_tmp_dir(),
782                                                    G_DIR_SEPARATOR_S,
783                                                    (gint)mimeinfo);
784                         return filename;
785                 }
786         }
787
788         filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
789                                base, NULL);
790
791         return filename;
792 }
793
794 static gchar *mime_extract_file(gchar *source, MimeInfo *partinfo)
795 {
796         gchar *filename;
797
798         if (!partinfo) return NULL;
799
800         filename = procmime_get_file_name(partinfo);
801
802         if (procmime_get_part(filename, source, partinfo) < 0)
803                 alertpanel_error
804                         (_("Can't get the part of multipart message."));
805
806         return filename;
807 }
808
809 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
810 {
811         FILE *fp;
812         gchar *file;
813         MimeInfo *mimeinfo;
814         MsgInfo *tmpmsginfo;
815         gchar *p;
816         gchar *boundary;
817         gint boundary_len = 0;
818         gchar buf[BUFFSIZE];
819         glong fpos, prev_fpos;
820         gint npart;
821         gchar *source;
822         gchar *filename;
823
824         g_return_if_fail(msginfo != NULL);
825         
826 #if USE_GPGME
827         for (;;) {
828                 if ((fp = procmsg_open_message(msginfo)) == NULL) return;
829                 mimeinfo = procmime_scan_mime_header(fp);
830                 if (!mimeinfo) break;
831
832                 if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
833                     rfc2015_is_encrypted(mimeinfo)) {
834                         MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
835                 }
836                 if (MSG_IS_ENCRYPTED(msginfo->flags) &&
837                     !msginfo->plaintext_file  &&
838                     !msginfo->decryption_failed) {
839                         rfc2015_decrypt_message(msginfo, mimeinfo, fp);
840                         if (msginfo->plaintext_file &&
841                             !msginfo->decryption_failed) {
842                                 fclose(fp);
843                                 continue;
844                         }
845                 }
846                 
847                 break;
848         }
849 #else /* !USE_GPGME */
850         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
851         mimeinfo = procmime_scan_mime_header(fp);
852 #endif /* USE_GPGME */
853
854         fclose(fp);
855         if (!mimeinfo) return;
856         if (mimeinfo->mime_type == MIME_TEXT)
857                 return;
858
859         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
860
861         g_return_if_fail(mimeinfo != NULL);
862         g_return_if_fail(mimeinfo->mime_type != MIME_TEXT);
863
864         if (mimeinfo->mime_type == MIME_MULTIPART) {
865                 g_return_if_fail(mimeinfo->boundary != NULL);
866                 g_return_if_fail(mimeinfo->sub == NULL);
867         }
868         g_return_if_fail(fp != NULL);
869
870         boundary = mimeinfo->boundary;
871
872         if (boundary) {
873                 boundary_len = strlen(boundary);
874
875                 /* look for first boundary */
876                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL)
877                         if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
878                 if (!p) {
879                         fclose(fp);
880                         return;
881                 }
882         }
883
884         if ((fpos = ftell(fp)) < 0) {
885                 perror("ftell");
886                 fclose(fp);
887                 return;
888         }
889
890         for (npart = 0;; npart++) {
891                 MimeInfo *partinfo;
892                 gboolean eom = FALSE;
893
894                 prev_fpos = fpos;
895
896                 partinfo = procmime_scan_mime_header(fp);
897                 if (!partinfo) break;
898
899                 if (npart != 0)
900                         procmime_mimeinfo_insert(mimeinfo, partinfo);
901                 else
902                         procmime_mimeinfo_free(partinfo);
903
904                 /* look for next boundary */
905                 buf[0] = '\0';
906                 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
907                         if (IS_BOUNDARY(buf, boundary, boundary_len)) {
908                                 if (buf[2 + boundary_len]     == '-' &&
909                                     buf[2 + boundary_len + 1] == '-')
910                                         eom = TRUE;
911                                 break;
912                         }
913                 }
914                 if (p == NULL)
915                         eom = TRUE;     /* broken MIME message */
916                 fpos = ftell(fp);
917
918                 partinfo->size = fpos - prev_fpos - strlen(buf);
919
920                 if (eom) break;
921         }
922
923         source = procmsg_get_message_file_path(msginfo);
924
925         g_return_if_fail(mimeinfo != NULL);
926
927         if (!mimeinfo->main && mimeinfo->parent)
928                 {
929                         filename = mime_extract_file(source, mimeinfo);
930
931                         compose_attach_append_with_type(compose, filename,
932                                                         mimeinfo->content_type,
933                                                         mimeinfo->mime_type);
934
935                         g_free(filename);
936                 }
937
938         if (mimeinfo->sub && mimeinfo->sub->children)
939                 {
940                         filename = mime_extract_file(source, mimeinfo->sub);
941
942                         compose_attach_append_with_type(compose, filename,
943                                                         mimeinfo->content_type,
944                                                         mimeinfo->sub->mime_type);
945
946                         g_free(filename);
947                 }
948
949         if (mimeinfo->children) {
950                 MimeInfo *child;
951
952                 child = mimeinfo->children;
953                 while (child) {
954                         filename = mime_extract_file(source, child);
955
956                         compose_attach_append_with_type(compose, filename,
957                                                         child->content_type,
958                                                         child->mime_type);
959
960                         g_free(filename);
961
962                         child = child->next;
963                 }
964         }
965
966         fclose(fp);
967
968         procmime_mimeinfo_free_all(mimeinfo);
969 }
970
971
972 #define INSERT_FW_HEADER(var, hdr) \
973 if (msginfo->var && *msginfo->var) { \
974         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
975         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
976         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
977 }
978
979 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
980                          gboolean as_attach)
981 {
982         Compose *compose;
983         /*      PrefsAccount *account; */
984         GtkSText *text;
985
986         g_return_val_if_fail(msginfo != NULL, NULL);
987         g_return_val_if_fail(msginfo->folder != NULL, NULL);
988
989         account = msginfo->folder->folder->account;
990         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
991                 gchar *to;
992                 Xstrdup_a(to, msginfo->to, return NULL);
993                 extract_address(to);
994                 account = account_find_from_address(to);
995         }
996
997         if(!account && prefs_common.forward_account_autosel) {
998                 gchar cc[BUFFSIZE];
999                 if(!get_header_from_msginfo(msginfo,cc,sizeof(cc),"CC:")){ /* Found a CC header */
1000                         extract_address(cc);
1001                         account = account_find_from_address(cc);
1002                 }
1003         }
1004
1005         if (account == NULL) {
1006                 account = cur_account;
1007                 /*
1008                 account = msginfo->folder->folder->account;
1009                 if (!account) account = cur_account;
1010                 */
1011         }
1012         g_return_val_if_fail(account != NULL, NULL);
1013
1014         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
1015         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
1016         CHANGE_FLAGS(msginfo);
1017
1018         compose = compose_create(account, COMPOSE_FORWARD);
1019
1020         if (msginfo->subject && *msginfo->subject) {
1021                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Fw: ");
1022                 gtk_entry_append_text(GTK_ENTRY(compose->subject_entry),
1023                                       msginfo->subject);
1024         }
1025
1026         text = GTK_STEXT(compose->text);
1027         gtk_stext_freeze(text);
1028
1029         if (as_attach) {
1030                 gchar *msgfile;
1031
1032                 msgfile = procmsg_get_message_file_path(msginfo);
1033                 if (!is_file_exist(msgfile))
1034                         g_warning(_("%s: file not exist\n"), msgfile);
1035                 else
1036                         compose_attach_append(compose, msgfile,
1037                                               MIME_MESSAGE_RFC822);
1038
1039                 g_free(msgfile);
1040         } else {
1041                 gchar *qmark;
1042                 gchar *quote_str;
1043
1044                 if (prefs_common.fw_quotemark && *prefs_common.fw_quotemark)
1045                         qmark = prefs_common.fw_quotemark;
1046                 else
1047                         qmark = "> ";
1048
1049                 quote_str = compose_quote_fmt(compose, msginfo,
1050                                               prefs_common.fw_quotefmt, qmark);
1051                 compose_attach_parts(compose, msginfo);
1052         }
1053
1054         if (prefs_common.auto_sig)
1055                 compose_insert_sig(compose);
1056
1057         if (prefs_common.linewrap_quote)
1058                 compose_wrap_line_all(compose);
1059
1060         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1061         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1062
1063         gtk_stext_thaw(text);
1064 #if 0 /* NEW COMPOSE GUI */
1065         if (account->protocol != A_NNTP)
1066                 gtk_widget_grab_focus(compose->to_entry);
1067         else
1068                 gtk_widget_grab_focus(compose->newsgroups_entry);
1069 #endif
1070         gtk_widget_grab_focus(compose->header_last->entry);
1071
1072         if (prefs_common.auto_exteditor)
1073                 compose_exec_ext_editor(compose);
1074
1075         return compose;
1076 }
1077
1078 #undef INSERT_FW_HEADER
1079
1080 Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1081 {
1082         Compose *compose;
1083         GtkSText *text;
1084         GSList *msginfo;
1085         gchar *msgfile;
1086
1087         g_return_val_if_fail(msginfo_list != NULL, NULL);
1088         
1089         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1090                 if ( ((MsgInfo *)msginfo->data)->folder == NULL )
1091                         return NULL;
1092         }
1093
1094         if (account == NULL) {
1095                 account = cur_account;
1096                 /*
1097                 account = msginfo->folder->folder->account;
1098                 if (!account) account = cur_account;
1099                 */
1100         }
1101         g_return_val_if_fail(account != NULL, NULL);
1102
1103         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1104                 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1105                 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1106                 CHANGE_FLAGS(((MsgInfo *)msginfo->data));
1107         }
1108
1109         compose = compose_create(account, COMPOSE_FORWARD);
1110
1111         text = GTK_STEXT(compose->text);
1112         gtk_stext_freeze(text);
1113
1114         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1115                 msgfile = procmsg_get_message_file_path((MsgInfo *)msginfo->data);
1116                 if (!is_file_exist(msgfile))
1117                         g_warning(_("%s: file not exist\n"), msgfile);
1118                 else
1119                         compose_attach_append(compose, msgfile,
1120                                 MIME_MESSAGE_RFC822);
1121                 g_free(msgfile);
1122         }
1123
1124         if (prefs_common.auto_sig)
1125                 compose_insert_sig(compose);
1126
1127         if (prefs_common.linewrap_quote)
1128                 compose_wrap_line_all(compose);
1129
1130         gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
1131         gtk_stext_set_point(GTK_STEXT(compose->text), 0);
1132
1133         gtk_stext_thaw(text);
1134 #if 0 /* NEW COMPOSE GUI */
1135         if (account->protocol != A_NNTP)
1136                 gtk_widget_grab_focus(compose->to_entry);
1137         else
1138                 gtk_widget_grab_focus(compose->newsgroups_entry);
1139 #endif
1140
1141         return compose;
1142 }
1143
1144 void compose_reedit(MsgInfo *msginfo)
1145 {
1146         Compose *compose;
1147         PrefsAccount *account;
1148         GtkSText *text;
1149         FILE *fp;
1150         gchar buf[BUFFSIZE];
1151
1152         g_return_if_fail(msginfo != NULL);
1153         g_return_if_fail(msginfo->folder != NULL);
1154
1155         account = msginfo->folder->folder->account;
1156
1157         if(!account&& prefs_common.reedit_account_autosel) {
1158                 gchar from[BUFFSIZE];
1159                 if(!get_header_from_msginfo(msginfo,from,sizeof(from),"FROM:")){ /* Found a FROM header */
1160                         extract_address(from);
1161                         account = account_find_from_address(from);
1162                 }
1163         }
1164         if (!account) account = cur_account;
1165         g_return_if_fail(account != NULL);
1166
1167         compose = compose_create(account, COMPOSE_REEDIT);
1168         compose->targetinfo = procmsg_msginfo_copy(msginfo);
1169
1170         if (compose_parse_header(compose, msginfo) < 0) return;
1171         compose_reedit_set_entry(compose, msginfo);
1172
1173         text = GTK_STEXT(compose->text);
1174         gtk_stext_freeze(text);
1175
1176         if ((fp = procmime_get_first_text_content(msginfo)) == NULL)
1177                 g_warning(_("Can't get text part\n"));
1178         else {
1179                 while (fgets(buf, sizeof(buf), fp) != NULL)
1180                         gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1181                 fclose(fp);
1182         }
1183         compose_attach_parts(compose, msginfo);
1184
1185         gtk_stext_thaw(text);
1186         gtk_widget_grab_focus(compose->text);
1187
1188         if (prefs_common.auto_exteditor)
1189                 compose_exec_ext_editor(compose);
1190 }
1191
1192 GList *compose_get_compose_list(void)
1193 {
1194         return compose_list;
1195 }
1196
1197 void compose_entry_append(Compose *compose, const gchar *address,
1198                           ComposeEntryType type)
1199 {
1200         GtkEntry *entry;
1201         const gchar *text;
1202         gchar *header;
1203
1204         if (!address || *address == '\0') return;
1205
1206 #if 0 /* NEW COMPOSE GUI */
1207         switch (type) {
1208         case COMPOSE_CC:
1209                 entry = GTK_ENTRY(compose->cc_entry);
1210                 break;
1211         case COMPOSE_BCC:
1212                 entry = GTK_ENTRY(compose->bcc_entry);
1213                 break;
1214         case COMPOSE_NEWSGROUPS:
1215                 entry = GTK_ENTRY(compose->newsgroups_entry);
1216                 break;
1217         case COMPOSE_TO:
1218         default:
1219                 entry = GTK_ENTRY(compose->to_entry);
1220                 break;
1221         }
1222
1223         text = gtk_entry_get_text(entry);
1224         if (*text != '\0')
1225                 gtk_entry_append_text(entry, ", ");
1226         gtk_entry_append_text(entry, address);
1227 #endif
1228
1229         switch (type) {
1230         case COMPOSE_CC:
1231                 header = N_("Cc:");
1232                 break;
1233         case COMPOSE_BCC:
1234                 header = N_("Bcc:");
1235                 break;
1236         case COMPOSE_REPLYTO:
1237                 header = N_("Reply-To:");
1238                 break;
1239         case COMPOSE_NEWSGROUPS:
1240                 header = N_("Newsgroups:");
1241                 break;
1242         case COMPOSE_FOLLOWUPTO:
1243                 header = N_( "Followup-To:");
1244                 break;
1245         case COMPOSE_TO:
1246         default:
1247                 header = N_("To:");
1248                 break;
1249         }
1250         header = prefs_common.trans_hdr ? gettext(header) : header;
1251
1252         compose_add_header_entry(compose, header, (gchar *)address);
1253 }
1254
1255 static void compose_entries_set(Compose *compose, const gchar *mailto)
1256 {
1257         gchar *subject = NULL;
1258         gchar *to = NULL;
1259         gchar *cc = NULL;
1260         gchar *bcc = NULL;
1261         gchar *body = NULL;
1262         gchar *p;
1263         gchar *tmp_mailto;
1264
1265         Xstrdup_a(tmp_mailto, mailto, return);
1266
1267         to = tmp_mailto;
1268
1269         p = strchr(tmp_mailto, '?');
1270         if (p) {
1271                 *p = '\0';
1272                 p++;
1273         }
1274
1275         while (p) {
1276                 gchar *field, *value;
1277
1278                 field = p;
1279
1280                 p = strchr(p, '=');
1281                 if (!p) break;
1282                 *p = '\0';
1283                 p++;
1284
1285                 value = p;
1286
1287                 p = strchr(p, '&');
1288                 if (p) {
1289                         *p = '\0';
1290                         p++;
1291                 }
1292
1293                 if (*value == '\0') continue;
1294
1295                 if (!g_strcasecmp(field, "subject")) {
1296                         Xalloca(subject, strlen(value) + 1, return);
1297                         decode_uri(subject, value);
1298                 } else if (!g_strcasecmp(field, "cc")) {
1299                         cc = value;
1300                 } else if (!g_strcasecmp(field, "bcc")) {
1301                         bcc = value;
1302                 } else if (!g_strcasecmp(field, "body")) {
1303                         Xalloca(body, strlen(value) + 1, return);
1304                         decode_uri(body, value);
1305                 }
1306         }
1307
1308         if (to)
1309                 compose_entry_append(compose, to, COMPOSE_TO);
1310         if (subject)
1311                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
1312         if (cc)
1313                 compose_entry_append(compose, cc, COMPOSE_CC);
1314         if (bcc)
1315                 compose_entry_append(compose, bcc, COMPOSE_BCC);
1316         if (body) {
1317                 gtk_stext_insert(GTK_STEXT(compose->text),
1318                                 NULL, NULL, NULL, body, -1);
1319                 gtk_stext_insert(GTK_STEXT(compose->text),
1320                                 NULL, NULL, NULL, "\n", 1);
1321         }
1322 }
1323
1324 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1325 {
1326         static HeaderEntry hentry[] = {{"Reply-To:",       NULL, TRUE},
1327                                        {"Cc:",             NULL, FALSE},
1328                                        {"References:",     NULL, FALSE},
1329                                        {"Bcc:",            NULL, FALSE},
1330                                        {"Newsgroups:",     NULL, FALSE},
1331                                        {"Followup-To:",    NULL, FALSE},
1332                                        {"X-Mailing-List:", NULL, FALSE},
1333                                        {"X-BeenThere:",    NULL, FALSE},
1334                                        {NULL,              NULL, FALSE}};
1335
1336         enum
1337         {
1338                 H_REPLY_TO       = 0,
1339                 H_CC             = 1,
1340                 H_REFERENCES     = 2,
1341                 H_BCC            = 3,
1342                 H_NEWSGROUPS     = 4,
1343                 H_FOLLOWUP_TO    = 5,
1344                 H_X_MAILING_LIST = 6,
1345                 H_X_BEENTHERE    = 7
1346         };
1347
1348         FILE *fp;
1349
1350         g_return_val_if_fail(msginfo != NULL, -1);
1351
1352         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1353         procheader_get_header_fields(fp, hentry);
1354         fclose(fp);
1355
1356         if (hentry[H_REPLY_TO].body != NULL) {
1357                 conv_unmime_header_overwrite(hentry[H_REPLY_TO].body);
1358                 compose->replyto = hentry[H_REPLY_TO].body;
1359                 hentry[H_REPLY_TO].body = NULL;
1360         }
1361         if (hentry[H_CC].body != NULL) {
1362                 conv_unmime_header_overwrite(hentry[H_CC].body);
1363                 compose->cc = hentry[H_CC].body;
1364                 hentry[H_CC].body = NULL;
1365         }
1366         if (hentry[H_X_MAILING_LIST].body != NULL) {
1367                 /* this is good enough to parse debian-devel */
1368                 gchar *buf = g_malloc(strlen(hentry[H_X_MAILING_LIST].body) + 1);
1369                 g_return_val_if_fail(buf != NULL, -1 );
1370                 if (1 == sscanf(hentry[H_X_MAILING_LIST].body, "<%[^>]>", buf))
1371                         compose->mailinglist = g_strdup(buf);
1372                 g_free(buf);
1373                 g_free(hentry[H_X_MAILING_LIST].body);
1374                 hentry[H_X_MAILING_LIST].body = NULL ;
1375         }
1376         if (hentry[H_X_BEENTHERE].body != NULL) {
1377                 /* this is good enough to parse the sylpheed-claws lists */
1378                 gchar *buf = g_malloc(strlen(hentry[H_X_BEENTHERE].body) + 1);
1379                 g_return_val_if_fail(buf != NULL, -1 );
1380                 if (1 == sscanf(hentry[H_X_BEENTHERE].body, "%[^>]", buf))
1381                         compose->mailinglist = g_strdup(buf);
1382                 g_free(buf);
1383                 g_free(hentry[H_X_BEENTHERE].body);
1384                 hentry[H_X_BEENTHERE].body = NULL ;
1385         }
1386         if (hentry[H_REFERENCES].body != NULL) {
1387                 if (compose->mode == COMPOSE_REEDIT)
1388                         compose->references = hentry[H_REFERENCES].body;
1389                 else {
1390                         compose->references = compose_parse_references
1391                                 (hentry[H_REFERENCES].body, msginfo->msgid);
1392                         g_free(hentry[H_REFERENCES].body);
1393                 }
1394                 hentry[H_REFERENCES].body = NULL;
1395         }
1396         if (hentry[H_BCC].body != NULL) {
1397                 if (compose->mode == COMPOSE_REEDIT) {
1398                         conv_unmime_header_overwrite(hentry[H_BCC].body);
1399                         compose->bcc = hentry[H_BCC].body;
1400                 } else
1401                         g_free(hentry[H_BCC].body);
1402                 hentry[H_BCC].body = NULL;
1403         }
1404         if (hentry[H_NEWSGROUPS].body != NULL) {
1405                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
1406                 hentry[H_NEWSGROUPS].body = NULL;
1407         }
1408         if (hentry[H_FOLLOWUP_TO].body != NULL) {
1409                 conv_unmime_header_overwrite(hentry[H_FOLLOWUP_TO].body);
1410                 compose->followup_to = hentry[H_FOLLOWUP_TO].body;
1411                 hentry[H_FOLLOWUP_TO].body = NULL;
1412         }
1413
1414         if (compose->mode == COMPOSE_REEDIT && msginfo->inreplyto)
1415                 compose->inreplyto = g_strdup(msginfo->inreplyto);
1416         else if (compose->mode != COMPOSE_REEDIT &&
1417                  msginfo->msgid && *msginfo->msgid) {
1418                 compose->inreplyto = g_strdup(msginfo->msgid);
1419
1420                 if (!compose->references) {
1421                         if (msginfo->inreplyto && *msginfo->inreplyto)
1422                                 compose->references =
1423                                         g_strdup_printf("<%s>\n\t<%s>",
1424                                                         msginfo->inreplyto,
1425                                                         msginfo->msgid);
1426                         else
1427                                 compose->references =
1428                                         g_strconcat("<", msginfo->msgid, ">",
1429                                                     NULL);
1430                 }
1431         }
1432
1433         return 0;
1434 }
1435
1436 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1437 {
1438         GSList *ref_id_list, *cur;
1439         GString *new_ref;
1440         gchar *new_ref_str;
1441
1442         ref_id_list = references_list_append(NULL, ref);
1443         if (!ref_id_list) return NULL;
1444         if (msgid && *msgid)
1445                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1446
1447         for (;;) {
1448                 gint len = 0;
1449
1450                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
1451                         /* "<" + Message-ID + ">" + CR+LF+TAB */
1452                         len += strlen((gchar *)cur->data) + 5;
1453
1454                 if (len > MAX_REFERENCES_LEN) {
1455                         /* remove second message-ID */
1456                         if (ref_id_list && ref_id_list->next &&
1457                             ref_id_list->next->next) {
1458                                 g_free(ref_id_list->next->data);
1459                                 ref_id_list = g_slist_remove
1460                                         (ref_id_list, ref_id_list->next->data);
1461                         } else {
1462                                 slist_free_strings(ref_id_list);
1463                                 g_slist_free(ref_id_list);
1464                                 return NULL;
1465                         }
1466                 } else
1467                         break;
1468         }
1469
1470         new_ref = g_string_new("");
1471         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1472                 if (new_ref->len > 0)
1473                         g_string_append(new_ref, "\n\t");
1474                 g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1475         }
1476
1477         slist_free_strings(ref_id_list);
1478         g_slist_free(ref_id_list);
1479
1480         new_ref_str = new_ref->str;
1481         g_string_free(new_ref, FALSE);
1482
1483         return new_ref_str;
1484 }
1485
1486 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1487                                 const gchar *fmt, const gchar *qmark)
1488 {
1489         GtkSText *text = GTK_STEXT(compose->text);
1490         gchar *quote_str = NULL;
1491         gchar *buf;
1492         gchar *p, *lastp;
1493         gint len;
1494
1495         if (qmark != NULL) {
1496                 quote_fmt_init(msginfo, NULL);
1497                 quote_fmt_scan_string(qmark);
1498                 quote_fmt_parse();
1499
1500                 buf = quote_fmt_get_buffer();
1501                 if (buf == NULL)
1502                         alertpanel_error(_("Quote mark format error."));
1503                 else
1504                         Xstrdup_a(quote_str, buf, return NULL)
1505         }
1506
1507         quote_fmt_init(msginfo, quote_str);
1508         quote_fmt_scan_string(fmt);
1509         quote_fmt_parse();
1510
1511         buf = quote_fmt_get_buffer();
1512         if (buf == NULL) {
1513                 alertpanel_error(_("Message reply/forward format error."));
1514                 return NULL;
1515         }
1516
1517         gtk_stext_freeze(text);
1518         gtk_stext_set_point(text, 0);
1519         gtk_stext_forward_delete(text, gtk_stext_get_length(text));
1520
1521         for (p = buf; *p != '\0'; ) {
1522                 lastp = strchr(p, '\n');
1523                 len = lastp ? lastp - p + 1 : -1;
1524                 gtk_stext_insert(text, NULL, NULL, NULL, p, len);
1525                 if (lastp)
1526                         p = lastp + 1;
1527                 else
1528                         break;
1529         }
1530
1531         gtk_stext_thaw(text);
1532
1533         return buf;
1534 }
1535
1536 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1537                                     gboolean to_all, gboolean ignore_replyto,
1538                                     gboolean followup_and_reply_to)
1539 {
1540         GSList *cc_list;
1541         GSList *cur;
1542         gchar *from;
1543         GHashTable *to_table;
1544
1545         g_return_if_fail(compose->account != NULL);
1546         g_return_if_fail(msginfo != NULL);
1547
1548         if ((compose->account->protocol != A_NNTP) || followup_and_reply_to)
1549                 compose_entry_append(compose,
1550                                     ((compose->mailinglist && !ignore_replyto)
1551                                      ? compose->mailinglist
1552                                      : (compose->replyto && !ignore_replyto)
1553                                      ? compose->replyto
1554                                      : msginfo->from ? msginfo->from : ""),
1555                                      COMPOSE_TO);
1556         if (compose->account->protocol == A_NNTP) {
1557                 if (ignore_replyto)
1558                         compose_entry_append
1559                                 (compose, msginfo->from ? msginfo->from : "",
1560                                  COMPOSE_TO);
1561                 else
1562                         compose_entry_append
1563                                 (compose,
1564                                  compose->followup_to ? compose->followup_to
1565                                  : compose->newsgroups ? compose->newsgroups
1566                                  : "",
1567                                  COMPOSE_NEWSGROUPS);
1568         }
1569
1570         if (msginfo->subject && *msginfo->subject) {
1571                 gchar *buf, *buf2, *p;
1572
1573                 buf = g_strdup(msginfo->subject);
1574                 while (!strncasecmp(buf, "Re:", 3)) {
1575                         p = buf + 3;
1576                         while (isspace(*p)) p++;
1577                         memmove(buf, p, strlen(p) + 1);
1578                 }
1579
1580                 buf2 = g_strdup_printf("Re: %s", buf);
1581                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1582                 g_free(buf2);
1583                 g_free(buf);
1584         } else
1585                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
1586
1587         if (!to_all || compose->account->protocol == A_NNTP) return;
1588
1589         from = g_strdup(compose->replyto ? compose->replyto :
1590                         msginfo->from ? msginfo->from : "");
1591         extract_address(from);
1592
1593         cc_list = address_list_append(NULL, msginfo->to);
1594         cc_list = address_list_append(cc_list, compose->cc);
1595
1596         to_table = g_hash_table_new(g_str_hash, g_str_equal);
1597         g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1598         if (compose->account)
1599                 g_hash_table_insert(to_table, compose->account->address,
1600                                     GINT_TO_POINTER(1));
1601
1602         /* remove address on To: and that of current account */
1603         for (cur = cc_list; cur != NULL; ) {
1604                 GSList *next = cur->next;
1605
1606                 if (g_hash_table_lookup(to_table, cur->data) != NULL)
1607                         cc_list = g_slist_remove(cc_list, cur->data);
1608                 else
1609                         g_hash_table_insert(to_table, cur->data, cur);
1610
1611                 cur = next;
1612         }
1613         g_hash_table_destroy(to_table);
1614         g_free(from);
1615
1616         if (cc_list) {
1617                 for (cur = cc_list; cur != NULL; cur = cur->next)
1618                         compose_entry_append(compose, (gchar *)cur->data,
1619                                              COMPOSE_CC);
1620                 slist_free_strings(cc_list);
1621                 g_slist_free(cc_list);
1622         }
1623 }
1624
1625 #define SET_ENTRY(entry, str) \
1626 { \
1627         if (str && *str) \
1628                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
1629 }
1630
1631 #define SET_ADDRESS(type, str) \
1632 { \
1633         if (str && *str) \
1634                 compose_entry_append(compose, str, type); \
1635 }
1636
1637 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1638 {
1639         g_return_if_fail(msginfo != NULL);
1640
1641         SET_ENTRY(subject_entry, msginfo->subject);
1642         SET_ADDRESS(COMPOSE_TO, msginfo->to);
1643         SET_ADDRESS(COMPOSE_CC, compose->cc);
1644         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
1645         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
1646
1647 #if 0 /* NEW COMPOSE GUI */
1648         if (compose->bcc) {
1649                 GtkItemFactory *ifactory;
1650                 GtkWidget *menuitem;
1651
1652                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1653                 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Bcc");
1654                 gtk_check_menu_item_set_active
1655                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1656         }
1657         if (compose->replyto) {
1658                 GtkItemFactory *ifactory;
1659                 GtkWidget *menuitem;
1660
1661                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1662                 menuitem = gtk_item_factory_get_item
1663                         (ifactory, "/Message/Reply to");
1664                 gtk_check_menu_item_set_active
1665                         (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1666         }
1667 #endif
1668 }
1669
1670 #undef SET_ENTRY
1671 #undef SET_ADDRESS
1672
1673 static void compose_exec_sig(Compose *compose, gchar *sigfile)
1674 {
1675         FILE *tmpfp;
1676         pid_t thepid;
1677         gchar *sigtext;
1678         FILE  *sigprg;
1679         gchar  *buf;
1680         size_t buf_len = 128;
1681  
1682         if (strlen(sigfile) < 2)
1683           return;
1684  
1685         sigprg = popen(sigfile+1, "r");
1686         if (sigprg) {
1687
1688                 buf = g_malloc(buf_len);
1689
1690                 if (!buf) {
1691                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1692                         "Unable to insert signature (malloc failed)\n", -1);
1693
1694                         pclose(sigprg);
1695                         return;
1696                 }
1697
1698                 while (!feof(sigprg)) {
1699                         bzero(buf, buf_len);
1700                         fread(buf, buf_len-1, 1, sigprg);
1701                         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, buf, -1);
1702                 }
1703
1704                 g_free(buf);
1705                 pclose(sigprg);
1706         }
1707         else
1708         {
1709                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1710                 "Can't exec file: ", -1);
1711                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, \
1712                 sigfile+1, -1);
1713         }
1714 }
1715
1716 static void compose_insert_sig(Compose *compose)
1717 {
1718         gchar *sigfile;
1719
1720         if (compose->account && compose->account->sig_path)
1721                 sigfile = g_strdup(compose->account->sig_path);
1722         else {
1723                 sigfile = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1724                                       DEFAULT_SIGNATURE, NULL);
1725         }
1726
1727         if (!is_file_or_fifo_exist(sigfile) & (sigfile[0] != '|')) {
1728                 g_free(sigfile);
1729                 return;
1730         }
1731
1732         gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, "\n\n", 2);
1733         if (prefs_common.sig_sep) {
1734                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1735                                 prefs_common.sig_sep, -1);
1736                 gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
1737                                 "\n", 1);
1738         }
1739
1740         if (sigfile[0] == '|')
1741         {
1742                 compose_exec_sig(compose, sigfile);
1743         }
1744         else
1745         {
1746                 compose_insert_file(compose, sigfile);
1747         }
1748         g_free(sigfile);
1749 }
1750
1751 static void compose_insert_file(Compose *compose, const gchar *file)
1752 {
1753         GtkSText *text = GTK_STEXT(compose->text);
1754         gchar buf[BUFFSIZE];
1755         gint len;
1756         FILE *fp;
1757
1758         g_return_if_fail(file != NULL);
1759
1760         if ((fp = fopen(file, "r")) == NULL) {
1761                 FILE_OP_ERROR(file, "fopen");
1762                 return;
1763         }
1764
1765         gtk_stext_freeze(text);
1766
1767         while (fgets(buf, sizeof(buf), fp) != NULL) {
1768                 /* Strip <CR> if DOS/Windoze file, replace <CR> with <LF> if MAC file */
1769                 len = strlen(buf);
1770                 if (len > 1 && buf[len - 2] == '\r' && buf[len - 1] == '\n') {
1771                         buf[len - 2] = '\n';
1772                         buf[len - 1] = '\0';
1773                 } else {
1774                         while (--len > 0)
1775                                 if (buf[len] == '\r')
1776                                         buf[len] = '\n';
1777                 }
1778
1779                 gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
1780         }
1781
1782         gtk_stext_thaw(text);
1783
1784         fclose(fp);
1785 }
1786
1787 static void compose_attach_info(Compose * compose, AttachInfo * ainfo,
1788                                 ContentType cnttype)
1789 {
1790         gchar *text[N_ATTACH_COLS];
1791         gint row;
1792
1793         text[COL_MIMETYPE] = ainfo->content_type;
1794         text[COL_SIZE] = to_human_readable(ainfo->size);
1795         text[COL_NAME] = ainfo->name;
1796
1797         row = gtk_clist_append(GTK_CLIST(compose->attach_clist), text);
1798         gtk_clist_set_row_data(GTK_CLIST(compose->attach_clist), row, ainfo);
1799
1800         if (cnttype != MIME_MESSAGE_RFC822)
1801                 compose_changed_cb(NULL, compose);
1802 }
1803
1804 static void compose_attach_append_with_type(Compose *compose,
1805                                             const gchar *file,
1806                                             const gchar *type,
1807                                             ContentType cnttype)
1808 {
1809         AttachInfo *ainfo;
1810         off_t size;
1811
1812         if (!is_file_exist(file)) {
1813                 g_warning(_("File %s doesn't exist\n"), file);
1814                 return;
1815         }
1816         if ((size = get_file_size(file)) < 0) {
1817                 g_warning(_("Can't get file size of %s\n"), file);
1818                 return;
1819         }
1820         if (size == 0) {
1821                 alertpanel_notice(_("File %s is empty\n"), file);
1822                 return;
1823         }
1824 #if 0 /* NEW COMPOSE GUI */
1825         if (!compose->use_attach) {
1826                 GtkItemFactory *ifactory;
1827                 GtkWidget *menuitem;
1828
1829                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1830                 menuitem = gtk_item_factory_get_item(ifactory,
1831                                                      "/Message/Attach");
1832                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1833                                                TRUE);
1834         }
1835 #endif
1836         ainfo = g_new0(AttachInfo, 1);
1837         ainfo->file = g_strdup(file);
1838
1839         if (cnttype == MIME_MESSAGE_RFC822) {
1840                 ainfo->encoding = ENC_7BIT;
1841                 ainfo->name = g_strdup_printf(_("Message: %s"),
1842                                               g_basename(file));
1843         } else {
1844                 ainfo->encoding = ENC_BASE64;
1845                 ainfo->name = g_strdup(g_basename(file));
1846         }
1847
1848         ainfo->content_type = g_strdup(type);
1849         ainfo->size = size;
1850
1851         compose_attach_info(compose, ainfo, cnttype);
1852 }
1853
1854 static void compose_attach_append(Compose *compose, const gchar *file,
1855                                   ContentType cnttype)
1856 {
1857         AttachInfo *ainfo;
1858         gchar *text[N_ATTACH_COLS];
1859         off_t size;
1860         gint row;
1861
1862         if (!is_file_exist(file)) {
1863                 g_warning(_("File %s doesn't exist\n"), file);
1864                 return;
1865         }
1866         if ((size = get_file_size(file)) < 0) {
1867                 g_warning(_("Can't get file size of %s\n"), file);
1868                 return;
1869         }
1870         if (size == 0) {
1871                 alertpanel_notice(_("File %s is empty\n"), file);
1872                 return;
1873         }
1874 #if 0 /* NEW COMPOSE GUI */
1875         if (!compose->use_attach) {
1876                 GtkItemFactory *ifactory;
1877                 GtkWidget *menuitem;
1878
1879                 ifactory = gtk_item_factory_from_widget(compose->menubar);
1880                 menuitem = gtk_item_factory_get_item(ifactory,
1881                                                      "/Message/Attach");
1882                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1883                                                TRUE);
1884         }
1885 #endif
1886         ainfo = g_new0(AttachInfo, 1);
1887         ainfo->file = g_strdup(file);
1888
1889         if (cnttype == MIME_MESSAGE_RFC822) {
1890                 ainfo->content_type = g_strdup("message/rfc822");
1891                 ainfo->encoding = ENC_7BIT;
1892                 ainfo->name = g_strdup_printf(_("Message: %s"),
1893                                               g_basename(file));
1894         } else {
1895                 ainfo->content_type = procmime_get_mime_type(file);
1896                 if (!ainfo->content_type)
1897                         ainfo->content_type =
1898                                 g_strdup("application/octet-stream");
1899                 ainfo->encoding = ENC_BASE64;
1900                 ainfo->name = g_strdup(g_basename(file));
1901         }
1902         ainfo->size = size;
1903
1904         compose_attach_info(compose, ainfo, cnttype);
1905 }
1906
1907 static void compose_wrap_line(Compose *compose)
1908 {
1909         GtkSText *text = GTK_STEXT(compose->text);
1910         gint ch_len, last_ch_len;
1911         gchar cbuf[MB_LEN_MAX], last_ch;
1912         guint text_len;
1913         guint line_end;
1914         guint quoted;
1915         gint p_start, p_end;
1916         gint line_pos, cur_pos;
1917         gint line_len, cur_len;
1918
1919 #define GET_STEXT(pos)                                                        \
1920         if (text->use_wchar)                                                 \
1921                 ch_len = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, (pos))); \
1922         else {                                                               \
1923                 cbuf[0] = GTK_STEXT_INDEX(text, (pos));                       \
1924                 ch_len = 1;                                                  \
1925         }
1926
1927         gtk_stext_freeze(text);
1928
1929         text_len = gtk_stext_get_length(text);
1930
1931         /* check to see if the point is on the paragraph mark (empty line). */
1932         cur_pos = gtk_stext_get_point(text);
1933         GET_STEXT(cur_pos);
1934         if ((ch_len == 1 && *cbuf == '\n') || cur_pos == text_len) {
1935                 if (cur_pos == 0)
1936                         goto compose_end; /* on the paragraph mark */
1937                 GET_STEXT(cur_pos - 1);
1938                 if (ch_len == 1 && *cbuf == '\n')
1939                         goto compose_end; /* on the paragraph mark */
1940         }
1941
1942         /* find paragraph start. */
1943         line_end = quoted = 0;
1944         for (p_start = cur_pos; p_start >= 0; --p_start) {
1945                 GET_STEXT(p_start);
1946                 if (ch_len == 1 && *cbuf == '\n') {
1947                         if (quoted)
1948                                 goto compose_end; /* quoted part */
1949                         if (line_end) {
1950                                 p_start += 2;
1951                                 break;
1952                         }
1953                         line_end = 1;
1954                 } else {
1955                         if (ch_len == 1 && strchr(">:#", *cbuf))
1956                                 quoted = 1;
1957                         else if (ch_len != 1 || !isspace(*cbuf))
1958                                 quoted = 0;
1959
1960                         line_end = 0;
1961                 }
1962         }
1963         if (p_start < 0)
1964                 p_start = 0;
1965
1966         /* find paragraph end. */
1967         line_end = 0;
1968         for (p_end = cur_pos; p_end < text_len; p_end++) {
1969                 GET_STEXT(p_end);
1970                 if (ch_len == 1 && *cbuf == '\n') {
1971                         if (line_end) {
1972                                 p_end -= 1;
1973                                 break;
1974                         }
1975                         line_end = 1;
1976                 } else {
1977                         if (line_end && ch_len == 1 && strchr(">:#", *cbuf))
1978                                 goto compose_end; /* quoted part */
1979
1980                         line_end = 0;
1981                 }
1982         }
1983         if (p_end >= text_len)
1984                 p_end = text_len;
1985
1986         if (p_start >= p_end)
1987                 goto compose_end;
1988
1989         line_len = cur_len = 0;
1990         last_ch_len = 0;
1991         last_ch = '\0';
1992         line_pos = p_start;
1993         for (cur_pos = p_start; cur_pos < p_end; cur_pos++) {
1994                 guint space = 0;
1995
1996                 GET_STEXT(cur_pos);
1997
1998                 if (ch_len < 0) {
1999                         cbuf[0] = '\0';
2000                         ch_len = 1;
2001                 }
2002
2003                 if (ch_len == 1 && isspace(*cbuf))
2004                         space = 1;
2005
2006                 if (ch_len == 1 && *cbuf == '\n') {
2007                         guint replace = 0;
2008                         if (last_ch_len == 1 && !isspace(last_ch)) {
2009                                 if (cur_pos + 1 < p_end) {
2010                                         GET_STEXT(cur_pos + 1);
2011                                         if (ch_len == 1 && !isspace(*cbuf))
2012                                                 replace = 1;
2013                                 }
2014                         }
2015                         gtk_stext_set_point(text, cur_pos + 1);
2016                         gtk_stext_backward_delete(text, 1);
2017                         if (replace) {
2018                                 gtk_stext_set_point(text, cur_pos);
2019                                 gtk_stext_insert(text, NULL, NULL, NULL, " ", 1);
2020                                 space = 1;
2021                         }
2022                         else {
2023                                 p_end--;
2024                                 cur_pos--;
2025                                 continue;
2026                         }
2027                 }
2028
2029                 last_ch_len = ch_len;
2030                 last_ch = *cbuf;
2031
2032                 if (space) {
2033                         line_pos = cur_pos + 1;
2034                         line_len = cur_len + ch_len;
2035                 }
2036
2037                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2038                     line_len > 0) {
2039                         gint tlen = ch_len;
2040
2041                         GET_STEXT(line_pos - 1);
2042                         if (ch_len == 1 && isspace(*cbuf)) {
2043                                 gtk_stext_set_point(text, line_pos);
2044                                 gtk_stext_backward_delete(text, 1);
2045                                 p_end--;
2046                                 cur_pos--;
2047                                 line_pos--;
2048                                 cur_len--;
2049                                 line_len--;
2050                         }
2051                         ch_len = tlen;
2052
2053                         gtk_stext_set_point(text, line_pos);
2054                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2055                         p_end++;
2056                         cur_pos++;
2057                         line_pos++;
2058                         cur_len = cur_len - line_len + ch_len;
2059                         line_len = 0;
2060                         continue;
2061                 }
2062
2063                 if (ch_len > 1) {
2064                         line_pos = cur_pos + 1;
2065                         line_len = cur_len + ch_len;
2066                 }
2067                 cur_len += ch_len;
2068         }
2069
2070 compose_end:
2071         gtk_stext_thaw(text);
2072
2073 #undef GET_STEXT
2074 }
2075
2076 /* return indent length */
2077 static guint get_indent_length(GtkSText *text, guint start_pos, guint text_len)
2078 {
2079         gint indent_len = 0;
2080         gint i, ch_len;
2081         gchar cbuf[MB_LEN_MAX];
2082
2083         for (i = start_pos; i < text_len; i++) {
2084                 if (text->use_wchar)
2085                         ch_len = wctomb
2086                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, i));
2087                 else {
2088                         cbuf[0] = GTK_STEXT_INDEX(text, i);
2089                         ch_len = 1;
2090                 }
2091                 if (ch_len > 1)
2092                         break;
2093                 /* allow space, tab, > or | */
2094                 if (cbuf[0] != ' ' && cbuf[0] != '\t' &&
2095                     cbuf[0] != '>' && cbuf[0] != '|')
2096                         break;
2097                 indent_len++;
2098         }
2099
2100         return indent_len;
2101 }
2102
2103 /* insert quotation string when line was wrapped */
2104 static guint ins_quote(GtkSText *text, guint quote_len, guint indent_len,
2105                        guint prev_line_pos, guint text_len,
2106                        gchar *quote_fmt)
2107 {
2108         guint i, ins_len;
2109         gchar ch;
2110
2111         if (indent_len) {
2112                 for (i = 0; i < indent_len; i++) {
2113                         ch = GTK_STEXT_INDEX(text, prev_line_pos + i);
2114                         gtk_stext_insert(text, NULL, NULL, NULL, &ch, 1);
2115                 }
2116                 ins_len = indent_len;
2117         }
2118         else {
2119                 gtk_stext_insert(text, NULL, NULL, NULL, quote_fmt, quote_len);
2120                 ins_len = quote_len;
2121         }
2122
2123         return ins_len;
2124 }
2125
2126 #undef WRAP_DEBUG
2127 #ifdef WRAP_DEBUG
2128 /* Darko: used when I debug wrapping */
2129 void dump_text(GtkSText *text, int pos, int tlen, int breakoncr)
2130 {
2131         int i;
2132         char ch;
2133
2134         printf("%d [", pos);
2135         for (i = pos; i < tlen; i++) {
2136                 ch = GTK_STEXT_INDEX(text, i);
2137                 if (breakoncr && ch == '\n')
2138                         break;
2139                 printf("%c", ch);
2140         }
2141         printf("]\n");
2142 }
2143 #endif
2144
2145 static void compose_wrap_line_all(Compose *compose)
2146 {
2147         GtkSText *text = GTK_STEXT(compose->text);
2148         guint tlen;
2149         guint line_pos = 0, cur_pos = 0, p_pos = 0;
2150         gint line_len = 0, cur_len = 0;
2151         gint ch_len;
2152         gint is_new_line = 1, do_delete = 0;
2153         guint qlen = 0, i_len = 0;
2154         guint linewrap_quote = prefs_common.linewrap_quote;
2155         guint linewrap_len = prefs_common.linewrap_len;
2156         gchar *qfmt = prefs_common.quotemark;
2157         gchar cbuf[MB_LEN_MAX];
2158
2159         gtk_stext_freeze(text);
2160
2161         /* make text buffer contiguous */
2162         /* gtk_stext_compact_buffer(text); */
2163
2164         tlen = gtk_stext_get_length(text);
2165
2166         for (; cur_pos < tlen; cur_pos++) {
2167                 /* mark position of new line - needed for quotation wrap */
2168                 if (linewrap_quote && is_new_line) {
2169                         qlen = gtkut_text_str_compare
2170                                 (text, cur_pos, tlen, qfmt);
2171                         is_new_line = 0;
2172                         if (qlen)
2173                                 i_len = get_indent_length(text, cur_pos, tlen);
2174                         else
2175                                 i_len = 0;
2176                         p_pos = cur_pos;
2177 #ifdef WRAP_DEBUG
2178                         printf("new line i_len=%d qlen=%d p_pos=", i_len, qlen);
2179                         dump_text(text, p_pos, tlen, 1);
2180 #endif
2181                 }
2182
2183                 /* get character(s) at current position */
2184                 if (text->use_wchar)
2185                         ch_len = wctomb
2186                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2187                 else {
2188                         cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
2189                         ch_len = 1;
2190                 }
2191
2192                 /* fix line length for tabs */
2193                 if (ch_len == 1 && *cbuf == '\t') {
2194                         guint tab_width = text->default_tab_width;
2195                         guint tab_offset = line_len % tab_width;
2196
2197 #ifdef WRAP_DEBUG
2198                         printf("found tab at pos=%d line_len=%d ", cur_pos,
2199                                 line_len);
2200 #endif
2201                         if (tab_offset) {
2202                                 line_len += tab_width - tab_offset - 1;
2203                                 cur_len = line_len;
2204                         }
2205 #ifdef WRAP_DEBUG
2206                         printf("new_len=%d\n", line_len);
2207 #endif
2208                 }
2209
2210                 /* we have encountered line break */
2211                 if (ch_len == 1 && *cbuf == '\n') {
2212                         gint clen;
2213                         guint ilen;
2214                         gchar cb[MB_CUR_MAX];
2215
2216 #ifdef WRAP_DEBUG
2217                         printf("found CR at %d next line is ", cur_pos);
2218                         dump_text(text, cur_pos + 1, tlen, 1);
2219 #endif
2220                         /* if it's just quotation + newline skip it */
2221                         if (i_len && (cur_pos + 1 < tlen)) {
2222                                 /* check if text at new line matches indent */
2223                                 ilen =  gtkut_text_str_compare_n
2224                                         (text, cur_pos + 1, p_pos, i_len, tlen);
2225                                 if (cur_pos + ilen < tlen) {
2226                                         if (text->use_wchar)
2227                                                 clen = wctomb(cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos + ilen + 1));
2228                                         else {
2229                                                 cb[0] = GTK_STEXT_INDEX(text,
2230                                                             cur_pos + ilen + 1);
2231                                                 clen = 1;
2232                                         }
2233                                         /* no need to join the lines */
2234                                         if ((clen == 1) && (cb[0] == '\n'))
2235                                                 do_delete = 0;
2236                                 }
2237                         }
2238                         /* if it's just newline skip it */
2239                         else if (do_delete && (cur_pos + 1 < tlen)) {
2240                                 if (text->use_wchar)
2241                                         clen = wctomb(cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos + 1));
2242                                 else {
2243                                         cb[0] = GTK_STEXT_INDEX(text,
2244                                                                 cur_pos + 1);
2245                                         clen = 1;
2246                                 }
2247                                 /* no need to join the lines */
2248                                 if ((clen == 1) && (cb[0] == '\n'))
2249                                         do_delete = 0;
2250                         }
2251
2252 #ifdef WRAP_DEBUG
2253                         printf("qlen=%d l_len=%d wrap_len=%d do_del=%d\n",
2254                                 qlen, line_len, linewrap_len, do_delete);
2255 #endif
2256                         /* should we delete to perform smart wrapping */
2257                         if (line_len < linewrap_len && do_delete) {
2258                                 /* get rid of newline */
2259                                 gtk_stext_set_point(text, cur_pos);
2260                                 gtk_stext_forward_delete(text, 1);
2261                                 tlen--;
2262
2263                                 /* if text starts with quote fmt or with
2264                                    indent string, delete them */
2265                                 if (i_len) {
2266                                         ilen =  gtkut_text_str_compare_n
2267                                                 (text, cur_pos, p_pos, i_len,
2268                                                  tlen);
2269                                         if (ilen) {
2270                                                 gtk_stext_forward_delete(text,
2271                                                                          ilen);
2272                                                 tlen -= ilen;
2273                                         }
2274                                 } else if (qlen) {
2275                                         if (gtkut_text_str_compare
2276                                             (text, cur_pos, tlen, qfmt)) {
2277                                                 gtk_stext_forward_delete
2278                                                         (text, qlen);
2279                                                 tlen -= qlen;
2280                                         }
2281                                 }
2282
2283                                 if (text->use_wchar)
2284                                         clen = wctomb
2285                                                 (cb, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2286                                 else {
2287                                         cb[0] = GTK_STEXT_INDEX(text, cur_pos);
2288                                         clen = 1;
2289                                 }
2290
2291                                 /* insert space if it's alphanumeric */
2292                                 if ((cur_pos != line_pos) &&
2293                                     ((clen > 1) || isalnum(cb[0]))) {
2294                                         gtk_stext_insert(text, NULL, NULL,
2295                                                         NULL, " ", 1);
2296                                         /* gtk_text_compact_buffer(text); */
2297                                         tlen++;
2298                                 }
2299
2300                                 /* and start over with current line */
2301                                 cur_pos = p_pos - 1;
2302                                 line_pos = cur_pos;
2303                                 line_len = cur_len = 0;
2304                                 qlen = 0;
2305                                 do_delete = 0;
2306                                 is_new_line = 1;
2307 #ifdef WRAP_DEBUG
2308                                 printf("after delete l_pos=");
2309                                 dump_text(text, line_pos, tlen, 1);
2310 #endif
2311                                 continue;
2312                         }
2313
2314                         /* mark new line beginning */
2315                         line_pos = cur_pos + 1;
2316                         line_len = cur_len = 0;
2317                         qlen = 0;
2318                         do_delete = 0;
2319                         is_new_line = 1;
2320                         continue;
2321                 }
2322
2323                 if (ch_len < 0) {
2324                         cbuf[0] = '\0';
2325                         ch_len = 1;
2326                 }
2327
2328                 /* possible line break */
2329                 if (ch_len == 1 && isspace(*cbuf)) {
2330                         line_pos = cur_pos + 1;
2331                         line_len = cur_len + ch_len;
2332                 }
2333
2334                 /* are we over wrapping length set in preferences ? */
2335                 if (cur_len + ch_len > linewrap_len) {
2336                         gint clen;
2337
2338 #ifdef WRAP_DEBUG
2339                         printf("should wrap cur_pos=%d ", cur_pos);
2340                         dump_text(text, p_pos, tlen, 1);
2341                         dump_text(text, line_pos, tlen, 1);
2342 #endif
2343                         /* force wrapping if it is one long word but not URL */
2344                         if (p_pos + i_len == line_pos)
2345                                 if (!gtkut_text_is_uri_string
2346                                     (text, line_pos, tlen))
2347                                         line_pos = cur_pos - 1;
2348 #ifdef WRAP_DEBUG
2349                         printf("new line_pos=%d\n", line_pos);
2350 #endif
2351
2352                         /* if next character is space delete it */
2353                         if (text->use_wchar)
2354                                 clen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
2355                         else {
2356                                 cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
2357                                 clen = 1;
2358                         }
2359                         if (clen == 1 && isspace(*cbuf)) {
2360                                 if (p_pos + i_len != line_pos ||
2361                                     !gtkut_text_is_uri_string
2362                                         (text, line_pos, tlen)) {
2363                                         gtk_stext_set_point(text, line_pos);
2364                                         gtk_stext_backward_delete(text, 1);
2365                                         tlen--;
2366                                         cur_pos--;
2367                                         line_pos--;
2368                                         cur_len--;
2369                                         line_len--;
2370                                 }
2371                         }
2372
2373                         /* if it is URL at beginning of line don't wrap */
2374                         if (p_pos + i_len == line_pos &&
2375                             gtkut_text_is_uri_string(text, line_pos, tlen)) {
2376 #ifdef WRAP_DEBUG
2377                                 printf("found URL at ");
2378                                 dump_text(text, line_pos, tlen, 1);
2379 #endif
2380                                 continue;
2381                         }
2382
2383                         /* insert CR */
2384                         gtk_stext_set_point(text, line_pos);
2385                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2386                         /* gtk_stext_compact_buffer(text); */
2387                         tlen++;
2388                         line_pos++;
2389                         /* for loop will increase it */
2390                         cur_pos = line_pos - 1;
2391                         /* start over with current line */
2392                         is_new_line = 1;
2393                         line_len = 0;
2394                         cur_len = 0;
2395                         do_delete = 1;
2396 #ifdef WRAP_DEBUG
2397                         printf("after CR insert ");
2398                         dump_text(text, line_pos, tlen, 1);
2399                         dump_text(text, cur_pos, tlen, 1);
2400 #endif
2401
2402                         /* should we insert quotation ? */
2403                         if (linewrap_quote && qlen) {
2404                                 /* only if line is not already quoted  */
2405                                 if (!gtkut_text_str_compare
2406                                         (text, line_pos, tlen, qfmt)) {
2407                                         guint ins_len;
2408
2409                                         if (line_pos - p_pos > i_len) {
2410                                                 ins_len = ins_quote
2411                                                         (text, qlen, i_len,
2412                                                          p_pos, tlen, qfmt);
2413
2414                                                 /* gtk_stext_compact_buffer(text); */
2415                                                 tlen += ins_len;
2416                                         }
2417 #ifdef WRAP_DEBUG
2418                                         printf("after quote insert ");
2419                                         dump_text(text, line_pos, tlen, 1);
2420 #endif
2421                                 }
2422                         }
2423                         continue;
2424                 }
2425
2426                 if (ch_len > 1) {
2427                         line_pos = cur_pos + 1;
2428                         line_len = cur_len + ch_len;
2429                 }
2430                 /* advance to next character in buffer */
2431                 cur_len += ch_len;
2432         }
2433
2434         gtk_stext_thaw(text);
2435 }
2436
2437 #if 0
2438 static void compose_wrap_line_all(Compose *compose)
2439 {
2440         GtkText *text = GTK_STEXT(compose->text);
2441         guint text_len;
2442         guint line_pos = 0, cur_pos = 0;
2443         gint line_len = 0, cur_len = 0;
2444         gint ch_len;
2445         gchar cbuf[MB_LEN_MAX];
2446
2447         gtk_stext_freeze(text);
2448
2449         text_len = gtk_stext_get_length(text);
2450
2451         for (; cur_pos < text_len; cur_pos++) {
2452                 if (text->use_wchar)
2453                         ch_len = wctomb
2454                                 (cbuf, (wchar_t)GTK_STEXT_INDEX(text, cur_pos));
2455                 else {
2456                         cbuf[0] = GTK_STEXT_INDEX(text, cur_pos);
2457                         ch_len = 1;
2458                 }
2459
2460                 if (ch_len == 1 && *cbuf == '\n') {
2461                         line_pos = cur_pos + 1;
2462                         line_len = cur_len = 0;
2463                         continue;
2464                 }
2465
2466                 if (ch_len < 0) {
2467                         cbuf[0] = '\0';
2468                         ch_len = 1;
2469                 }
2470
2471                 if (ch_len == 1 && isspace(*cbuf)) {
2472                         line_pos = cur_pos + 1;
2473                         line_len = cur_len + ch_len;
2474                 }
2475
2476                 if (cur_len + ch_len > prefs_common.linewrap_len &&
2477                     line_len > 0) {
2478                         gint tlen;
2479
2480                         if (text->use_wchar)
2481                                 tlen = wctomb(cbuf, (wchar_t)GTK_STEXT_INDEX(text, line_pos - 1));
2482                         else {
2483                                 cbuf[0] = GTK_STEXT_INDEX(text, line_pos - 1);
2484                                 tlen = 1;
2485                         }
2486                         if (tlen == 1 && isspace(*cbuf)) {
2487                                 gtk_stext_set_point(text, line_pos);
2488                                 gtk_stext_backward_delete(text, 1);
2489                                 text_len--;
2490                                 cur_pos--;
2491                                 line_pos--;
2492                                 cur_len--;
2493                                 line_len--;
2494                         }
2495
2496                         gtk_stext_set_point(text, line_pos);
2497                         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
2498                         text_len++;
2499                         cur_pos++;
2500                         line_pos++;
2501                         cur_len = cur_len - line_len + ch_len;
2502                         line_len = 0;
2503                         continue;
2504                 }
2505
2506                 if (ch_len > 1) {
2507                         line_pos = cur_pos + 1;
2508                         line_len = cur_len + ch_len;
2509                 }
2510                 cur_len += ch_len;
2511         }
2512
2513         gtk_stext_thaw(text);
2514 }
2515 #endif
2516
2517 static void compose_set_title(Compose *compose)
2518 {
2519         gchar *str;
2520         gchar *edited;
2521
2522         edited = compose->modified ? _(" [Edited]") : "";
2523         if (compose->account && compose->account->address)
2524                 str = g_strdup_printf(_("%s - Compose message%s"),
2525                                       compose->account->address, edited);
2526         else
2527                 str = g_strdup_printf(_("Compose message%s"), edited);
2528         gtk_window_set_title(GTK_WINDOW(compose->window), str);
2529         g_free(str);
2530 }
2531
2532 /**
2533  * compose_current_mail_account:
2534  * 
2535  * Find a current mail account (the currently selected account, or the
2536  * default account, if a news account is currently selected).  If a
2537  * mail account cannot be found, display an error message.
2538  * 
2539  * Return value: Mail account, or NULL if not found.
2540  **/
2541 static PrefsAccount *
2542 compose_current_mail_account(void)
2543 {
2544         PrefsAccount *ac;
2545
2546         if (cur_account && cur_account->protocol != A_NNTP)
2547                 ac = cur_account;
2548         else {
2549                 ac = account_get_default();
2550                 if (!ac || ac->protocol == A_NNTP) {
2551                         alertpanel_error(_("Account for sending mail is not specified.\n"
2552                                            "Please select a mail account before sending."));
2553                         return NULL;
2554                 }
2555         }
2556         return ac;
2557 }
2558
2559 gboolean compose_check_for_valid_recipient(Compose *compose) {
2560         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
2561         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
2562         gboolean recipient_found = FALSE;
2563         GSList *list;
2564         gchar **strptr;
2565
2566         /* free to and newsgroup list */
2567         slist_free_strings(compose->to_list);
2568         g_slist_free(compose->to_list);
2569         compose->to_list = NULL;
2570                         
2571         slist_free_strings(compose->newsgroup_list);
2572         g_slist_free(compose->newsgroup_list);
2573         compose->newsgroup_list = NULL;
2574
2575         /* search header entries for to and newsgroup entries */
2576         for(list = compose->header_list; list; list = list->next) {
2577                 gchar *header;
2578                 gchar *entry;
2579                 header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry));
2580                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
2581                 g_strstrip(entry);
2582                 if(entry[0] != '\0') {
2583                         for(strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
2584                                 if(!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
2585                                         compose->to_list = address_list_append(compose->to_list, entry);
2586                                         recipient_found = TRUE;
2587                                 }
2588                         }
2589                         for(strptr = recipient_headers_news; *strptr != NULL; strptr++) {
2590                                 if(!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
2591                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
2592                                         recipient_found = TRUE;
2593                                 }
2594                         }
2595                 }
2596                 g_free(entry);
2597         }
2598         return recipient_found;
2599 }
2600
2601 gint compose_send(Compose *compose)
2602 {
2603         gint msgnum;
2604         FolderItem *folder;
2605         gint val;
2606
2607         val = compose_queue(compose, &msgnum, &folder);
2608         if (val) {
2609                 alertpanel_error(_("Could not queue message for sending"));
2610                 return -1;
2611         }
2612         
2613         val = procmsg_send_message_queue(folder_item_fetch_msg(folder, msgnum));
2614         if(!val) {
2615                 folder_item_remove_msg(folder, msgnum);
2616                 folderview_update_item(folder, TRUE);
2617         }
2618
2619         return val;
2620 }
2621
2622 #if 0 /* compose restructure */
2623 gint compose_send(Compose *compose)
2624 {
2625         gchar tmp[MAXPATHLEN + 1];
2626         gint ok = 0;
2627         static gboolean lock = FALSE;
2628
2629         if (lock) return 1;
2630
2631         g_return_val_if_fail(compose->account != NULL, -1);
2632         g_return_val_if_fail(compose->orig_account != NULL, -1);
2633
2634         lock = TRUE;
2635
2636         if(!compose_check_for_valid_recipient(compose)) {
2637                 alertpanel_error(_("Recipient is not specified."));
2638                 lock = FALSE;
2639                 return 1;
2640         }
2641
2642         /* write to temporary file */
2643         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
2644                    get_rc_dir(), G_DIR_SEPARATOR, (gint)compose);
2645
2646         if (prefs_common.linewrap_at_send)
2647                 compose_wrap_line_all(compose);
2648
2649         if (compose_write_to_file(compose, tmp, FALSE) < 0) {
2650                 lock = FALSE;
2651                 return -1;
2652         }
2653
2654         if (!compose->to_list && !compose->newsgroup_list) {
2655                 g_warning(_("can't get recipient list."));
2656                 unlink(tmp);
2657                 lock = FALSE;
2658                 return -1;
2659         }
2660
2661         if (compose->to_list) {
2662                 PrefsAccount *ac;
2663
2664 #if 0 /* NEW COMPOSE GUI */
2665                 if (compose->account->protocol != A_NNTP)
2666                         ac = compose->account;
2667                 else if (compose->orig_account->protocol != A_NNTP)
2668                         ac = compose->orig_account;
2669                 else if (cur_account && cur_account->protocol != A_NNTP)
2670                         ac = cur_account;
2671                 else {
2672                         ac = compose_current_mail_account();
2673                         if (!ac) {
2674                                 unlink(tmp);
2675                                 lock = FALSE;
2676                                 return -1;
2677                         }
2678                 }
2679 #endif
2680                 ac = compose->account;
2681
2682                 ok = send_message(tmp, ac, compose->to_list);
2683                 statusbar_pop_all();
2684         }
2685
2686         if (ok == 0 && compose->newsgroup_list) {
2687                 Folder *folder;
2688
2689                 if (compose->account->protocol == A_NNTP)
2690                         folder = FOLDER(compose->account->folder);
2691                 else
2692                         folder = FOLDER(compose->orig_account->folder);
2693
2694                 ok = news_post(folder, tmp);
2695                 if (ok < 0) {
2696                         alertpanel_error(_("Error occurred while posting the message to %s ."),
2697                                          compose->account->nntp_server);
2698                         unlink(tmp);
2699                         lock = FALSE;
2700                         return -1;
2701                 }
2702         }
2703
2704         /* queue message if failed to send */
2705         if (ok < 0) {
2706                 if (prefs_common.queue_msg) {
2707                         AlertValue val;
2708
2709                         val = alertpanel
2710                                 (_("Queueing"),
2711                                  _("Error occurred while sending the message.\n"
2712                                    "Put this message into queue folder?"),
2713                                  _("OK"), _("Cancel"), NULL);
2714                         if (G_ALERTDEFAULT == val) {
2715                                 ok = compose_queue(compose, tmp);
2716                                 if (ok < 0)
2717                                         alertpanel_error(_("Can't queue the message."));
2718                         }
2719                 } else
2720                         alertpanel_error(_("Error occurred while sending the message."));
2721         } else {
2722                 if (compose->mode == COMPOSE_REEDIT) {
2723                         compose_remove_reedit_target(compose);
2724                         if (compose->targetinfo)
2725                                 folderview_update_item
2726                                         (compose->targetinfo->folder, TRUE);
2727                 }
2728         }
2729
2730         /* save message to outbox */
2731         if (ok == 0 && prefs_common.savemsg) {
2732                 if (compose_save_to_outbox(compose, tmp) < 0)
2733                         alertpanel_error
2734                                 (_("Can't save the message to outbox."));
2735         }
2736
2737         unlink(tmp);
2738         lock = FALSE;
2739         return ok;
2740 }
2741 #endif
2742
2743 static gboolean compose_use_attach(Compose *compose) {
2744     return(gtk_clist_get_row_data(GTK_CLIST(compose->attach_clist), 0) != NULL);
2745 }
2746
2747 static gint compose_bounce_write_headers_from_headerlist(Compose *compose, 
2748                                                          FILE *fp)
2749 {
2750         gchar buf[BUFFSIZE];
2751         gchar *str;
2752         gboolean first_address;
2753         GSList *list;
2754         ComposeHeaderEntry *headerentry;
2755         gchar *headerentryname;
2756         gchar *header_w_colon;
2757         gchar *cc_hdr;
2758         gchar *to_hdr;
2759
2760         debug_print(_("Writing bounce header\n"));
2761
2762         header_w_colon = g_strconcat("To:", NULL);
2763         to_hdr = (prefs_common.trans_hdr ? gettext(header_w_colon) : header_w_colon);
2764         header_w_colon = g_strconcat("Cc:", NULL);
2765         cc_hdr = (prefs_common.trans_hdr ? gettext(header_w_colon) : header_w_colon);
2766         
2767         first_address = TRUE;
2768         for(list = compose->header_list; list; list = list->next) {
2769                 headerentry = ((ComposeHeaderEntry *)list->data);
2770                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
2771
2772                 if(g_strcasecmp(headerentryname, cc_hdr) == 0 
2773                    || g_strcasecmp(headerentryname, to_hdr) == 0) {
2774                         str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
2775                         Xstrdup_a(str, str, return -1);
2776                         g_strstrip(str);
2777                         if(str[0] != '\0') {
2778                                 compose_convert_header
2779                                         (buf, sizeof(buf), str,
2780                                         strlen("Resent-To") + 2);
2781                                 if(first_address) {
2782                                         fprintf(fp, "Resent-To: ");
2783                                         first_address = FALSE;
2784                                 } else {
2785                                         fprintf(fp, ",");
2786                                 }
2787                                 fprintf(fp, "%s", buf);
2788                         }
2789                 }
2790         }
2791         /* if(!first_address) { */
2792         fprintf(fp, "\n");
2793         /* } */
2794
2795         return(0);
2796 }
2797
2798 static gint compose_bounce_write_headers(Compose *compose, FILE *fp)
2799 {
2800         gchar buf[BUFFSIZE];
2801         gchar *str;
2802         /* struct utsname utsbuf; */
2803
2804         g_return_val_if_fail(fp != NULL, -1);
2805         g_return_val_if_fail(compose->account != NULL, -1);
2806         g_return_val_if_fail(compose->account->address != NULL, -1);
2807
2808         /* Date */
2809         get_rfc822_date(buf, sizeof(buf));
2810         fprintf(fp, "Resent-Date: %s\n", buf);
2811
2812         /* From */
2813         if (compose->account->name && *compose->account->name) {
2814                 compose_convert_header
2815                         (buf, sizeof(buf), compose->account->name,
2816                          strlen("From: "));
2817                 fprintf(fp, "Resent-From: %s <%s>\n",
2818                         buf, compose->account->address);
2819         } else
2820                 fprintf(fp, "Resent-From: %s\n", compose->account->address);
2821
2822         /* To */
2823         compose_bounce_write_headers_from_headerlist(compose, fp);
2824
2825         /* separator between header and body */
2826         fputs("\n", fp);
2827
2828         return 0;
2829 }
2830
2831 static gint compose_bounce_write_to_file(Compose *compose, const gchar *file)
2832 {
2833         FILE *fp;
2834         FILE *fdest;
2835         size_t len;
2836         gchar buf[BUFFSIZE];
2837
2838         if ((fp = fopen(compose->bounce_filename, "r")) == NULL) {
2839                 FILE_OP_ERROR(file, "fopen");
2840                 return -1;
2841         }
2842
2843         if ((fdest = fopen(file, "a+")) == NULL) {
2844                 FILE_OP_ERROR(file, "fopen");
2845                 fclose(fp);
2846                 return -1;
2847         }
2848
2849         /* chmod for security */
2850         if (change_file_mode_rw(fdest, file) < 0) {
2851                 FILE_OP_ERROR(file, "chmod");
2852                 g_warning(_("can't change file mode\n"));
2853         }
2854
2855         while (procheader_get_unfolded_line(buf, sizeof(buf), fp)) {
2856                 /* should filter returnpath, delivered-to */
2857                 if ((g_strncasecmp(buf, "Return-Path:",
2858                                    strlen("Return-Path:")) == 0)
2859                     || (g_strncasecmp(buf, "Delivered-To:",
2860                                       strlen("Delivered-To:")) == 0))
2861                         continue;
2862                 if (fputs(buf, fdest) == -1)
2863                         goto error;
2864                 if (fputs("\n", fdest) == -1)
2865                         goto error;
2866         }
2867
2868         compose_bounce_write_headers(compose, fdest);
2869
2870         while ((len = fread(buf, sizeof(gchar), BUFFSIZE, fp)) > 0) {
2871                 if (fwrite(buf, sizeof(gchar), len, fdest) == -1)
2872                         goto error;
2873         }
2874
2875         fclose(fdest);
2876         fclose(fp);
2877
2878         return 0;
2879  error:
2880         fclose(fdest);
2881         fclose(fp);
2882
2883         return -1;
2884 }
2885
2886 static gint compose_write_to_file(Compose *compose, const gchar *file,
2887                                   gboolean is_draft)
2888 {
2889         FILE *fp;
2890         size_t len;
2891         gchar *chars;
2892         gchar *buf;
2893         const gchar *out_codeset;
2894         EncodingType encoding;
2895
2896         if ((fp = fopen(file, "a+")) == NULL) {
2897                 FILE_OP_ERROR(file, "fopen");
2898                 return -1;
2899         }
2900
2901         /* chmod for security */
2902         if (change_file_mode_rw(fp, file) < 0) {
2903                 FILE_OP_ERROR(file, "chmod");
2904                 g_warning(_("can't change file mode\n"));
2905         }
2906
2907         /* get all composed text */
2908         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
2909         len = strlen(chars);
2910         if (is_ascii_str(chars)) {
2911                 buf = g_strdup(chars);
2912                 out_codeset = CS_US_ASCII;
2913                 encoding = ENC_7BIT;
2914         } else {
2915                 const gchar *src_codeset;
2916
2917                 out_codeset = conv_get_outgoing_charset_str();
2918                 if (!strcasecmp(out_codeset, CS_US_ASCII))
2919                         out_codeset = CS_ISO_8859_1;
2920                 encoding = procmime_get_encoding_for_charset(out_codeset);
2921
2922                 src_codeset = conv_get_current_charset_str();
2923                 /* if current encoding is US-ASCII, set it the same as
2924                    outgoing one to prevent code conversion failure */
2925                 if (!strcasecmp(src_codeset, CS_US_ASCII))
2926                         src_codeset = out_codeset;
2927
2928                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
2929                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
2930
2931                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
2932                 if (!buf) {
2933                         g_free(chars);
2934                         fclose(fp);
2935                         unlink(file);
2936                         alertpanel_error(_("Can't convert the codeset of the message."));
2937                         return -1;
2938                 }
2939         }
2940         g_free(chars);
2941
2942         /* write headers */
2943         if (compose_write_headers
2944                 (compose, fp, out_codeset, encoding, is_draft) < 0) {
2945                 g_warning(_("can't write headers\n"));
2946                 fclose(fp);
2947                 /* unlink(file); */
2948                 g_free(buf);
2949                 return -1;
2950         }
2951
2952         if (compose_use_attach(compose)) {
2953 #if USE_GPGME
2954             /* This prolog message is ignored by mime software and
2955              * because it would make our signing/encryption task
2956              * tougher, we don't emit it in that case */
2957             if (!compose->use_signing && !compose->use_encryption)
2958 #endif
2959                 fputs("This is a multi-part message in MIME format.\n", fp);
2960
2961                 fprintf(fp, "\n--%s\n", compose->boundary);
2962                 fprintf(fp, "Content-Type: text/plain; charset=%s\n",
2963                         out_codeset);
2964                 fprintf(fp, "Content-Transfer-Encoding: %s\n",
2965                         procmime_get_encoding_str(encoding));
2966                 fputc('\n', fp);
2967         }
2968
2969         /* write body */
2970         len = strlen(buf);
2971         if (encoding == ENC_BASE64) {
2972                 gchar outbuf[B64_BUFFSIZE];
2973                 gint i, l;
2974
2975                 for (i = 0; i < len; i += B64_LINE_SIZE) {
2976                         l = MIN(B64_LINE_SIZE, len - i);
2977                         to64frombits(outbuf, buf + i, l);
2978                         fputs(outbuf, fp);
2979                         fputc('\n', fp);
2980                 }
2981         } else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
2982                 FILE_OP_ERROR(file, "fwrite");
2983                 fclose(fp);
2984                 unlink(file);
2985                 g_free(buf);
2986                 return -1;
2987         }
2988         g_free(buf);
2989
2990         if (compose_use_attach(compose))
2991                 compose_write_attach(compose, fp);
2992
2993         if (fclose(fp) == EOF) {
2994                 FILE_OP_ERROR(file, "fclose");
2995                 unlink(file);
2996                 return -1;
2997         }
2998
2999 #if USE_GPGME
3000         if (compose->use_signing) {
3001                 if (rfc2015_sign(file, compose->account) < 0) {
3002                         unlink(file);
3003                         return -1;
3004                 }
3005         }
3006         if (compose->use_encryption) {
3007                 if (rfc2015_encrypt(file, compose->to_list) < 0) {
3008                         unlink(file);
3009                         return -1;
3010                 }
3011         }
3012 #endif /* USE_GPGME */
3013
3014         return 0;
3015 }
3016
3017 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
3018 {
3019         FILE *fp;
3020         size_t len;
3021         gchar *chars;
3022
3023         if ((fp = fopen(file, "w")) == NULL) {
3024                 FILE_OP_ERROR(file, "fopen");
3025                 return -1;
3026         }
3027
3028         /* chmod for security */
3029         if (change_file_mode_rw(fp, file) < 0) {
3030                 FILE_OP_ERROR(file, "chmod");
3031                 g_warning(_("can't change file mode\n"));
3032         }
3033
3034         chars = gtk_editable_get_chars(GTK_EDITABLE(compose->text), 0, -1);
3035
3036         /* write body */
3037         len = strlen(chars);
3038         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
3039                 FILE_OP_ERROR(file, "fwrite");
3040                 g_free(chars);
3041                 fclose(fp);
3042                 unlink(file);
3043                 return -1;
3044         }
3045
3046         g_free(chars);
3047
3048         if (fclose(fp) == EOF) {
3049                 FILE_OP_ERROR(file, "fclose");
3050                 unlink(file);
3051                 return -1;
3052         }
3053         return 0;
3054 }
3055
3056 static gint compose_save_to_outbox(Compose *compose, const gchar *file)
3057 {
3058         FolderItem *outbox;
3059         gchar *path;
3060         gint num;
3061         FILE *fp;
3062
3063         debug_print(_("saving sent message...\n"));
3064
3065         outbox = folder_get_default_outbox();
3066         path = folder_item_get_path(outbox);
3067         if (!is_dir_exist(path))
3068                 make_dir_hier(path);
3069
3070         folder_item_scan(outbox);
3071         if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
3072                 g_free(path);
3073                 g_warning(_("can't save message\n"));
3074                 return -1;
3075         }
3076
3077         if ((fp = procmsg_open_mark_file(path, TRUE)) == NULL)
3078                 g_warning(_("can't open mark file\n"));
3079         else {
3080                 MsgInfo newmsginfo;
3081
3082                 newmsginfo.msgnum = num;
3083                 newmsginfo.flags.perm_flags = 0;
3084                 newmsginfo.flags.tmp_flags = 0;
3085                 procmsg_write_flags(&newmsginfo, fp);
3086                 fclose(fp);
3087         }
3088         g_free(path);
3089
3090         return 0;
3091 }
3092
3093 static gint compose_remove_reedit_target(Compose *compose)
3094 {
3095         FolderItem *item;
3096         MsgInfo *msginfo = compose->targetinfo;
3097
3098         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
3099         if (!msginfo) return -1;
3100
3101         item = msginfo->folder;
3102         g_return_val_if_fail(item != NULL, -1);
3103
3104         folder_item_scan(item);
3105         if (procmsg_msg_exist(msginfo) &&
3106             (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
3107                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
3108                         g_warning(_("can't remove the old message\n"));
3109                         return -1;
3110                 }
3111         }
3112
3113         return 0;
3114 }
3115
3116
3117 static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
3118 {
3119         FolderItem *queue;
3120         gchar *tmpfilename, *queue_path;
3121         FILE *fp, *src_fp;
3122         GSList *cur;
3123         gchar buf[BUFFSIZE];
3124         gint num;
3125         static gboolean lock = FALSE;
3126         PrefsAccount *mailac = NULL, *newsac = NULL;
3127         
3128         debug_print(_("queueing message...\n"));
3129         g_return_val_if_fail(compose->account != NULL, -1);
3130         g_return_val_if_fail(compose->orig_account != NULL, -1);
3131
3132         lock = TRUE;
3133         
3134         if(!compose_check_for_valid_recipient(compose)) {
3135                 alertpanel_error(_("Recipient is not specified."));
3136                 lock = FALSE;
3137                 return -1;
3138         }
3139                                                                         
3140         if (!compose->to_list && !compose->newsgroup_list) {
3141                 g_warning(_("can't get recipient list."));
3142                 lock = FALSE;
3143                 return -1;
3144         }
3145
3146         if (prefs_common.linewrap_at_send)
3147                 compose_wrap_line_all(compose);
3148                         
3149         /* write to temporary file */
3150         tmpfilename = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
3151                                       G_DIR_SEPARATOR, (gint)compose);
3152         if ((fp = fopen(tmpfilename, "w")) == NULL) {
3153                 FILE_OP_ERROR(tmpfilename, "fopen");
3154                 g_free(tmpfilename);
3155                 return -1;
3156         }
3157         if (change_file_mode_rw(fp, tmpfilename) < 0) {
3158                 FILE_OP_ERROR(tmpfilename, "chmod");
3159                 g_warning(_("can't change file mode\n"));
3160         }
3161
3162         if(compose->to_list) {
3163                 if (compose->account->protocol != A_NNTP)
3164                         mailac = compose->account;
3165                 else if (compose->orig_account->protocol != A_NNTP)
3166                         mailac = compose->orig_account;
3167                 else if (cur_account && cur_account->protocol != A_NNTP)
3168                         mailac = cur_account;
3169                 else if (!(mailac = compose_current_mail_account())) {
3170                         unlink(tmpfilename);
3171                         lock = FALSE;
3172                         alertpanel_error(_("No account for sending mails available!"));
3173                         return -1;
3174                 }
3175         }
3176
3177         if(compose->newsgroup_list) {
3178                 if (compose->account->protocol == A_NNTP)
3179                         newsac = compose->account;
3180                 else if(!(newsac = compose->orig_account) || (newsac->protocol != A_NNTP)) {
3181                         unlink(tmpfilename);
3182                         lock = FALSE;
3183                         alertpanel_error(_("No account for posting news available!"));
3184                         return -1;
3185                 }                       
3186         }
3187
3188         /* queueing variables */
3189         fprintf(fp, "AF:\n");
3190         fprintf(fp, "NF:0\n");
3191         fprintf(fp, "PS:10\n");
3192         fprintf(fp, "SRH:1\n");
3193         fprintf(fp, "SFN:\n");
3194         fprintf(fp, "DSR:\n");
3195         if (compose->msgid)
3196                 fprintf(fp, "MID:<%s>\n", compose->msgid);
3197         else
3198                 fprintf(fp, "MID:\n");
3199         fprintf(fp, "CFG:\n");
3200         fprintf(fp, "PT:0\n");
3201         fprintf(fp, "S:%s\n", compose->account->address);
3202         fprintf(fp, "RQ:\n");
3203         if (mailac)
3204                 fprintf(fp, "SSV:%s\n", mailac->smtp_server);
3205         else
3206                 fprintf(fp, "SSV:\n");
3207         if (newsac)
3208                 fprintf(fp, "NSV:%s\n", newsac->nntp_server);
3209         else
3210                 fprintf(fp, "NSV:\n");
3211         fprintf(fp, "SSH:\n");
3212         /* write recepient list */
3213         fprintf(fp, "R:");
3214         if(compose->to_list) {
3215                 fprintf(fp, "<%s>", (gchar *)compose->to_list->data);
3216                 for (cur = compose->to_list->next; cur != NULL; cur = cur->next)
3217                         fprintf(fp, ",<%s>", (gchar *)cur->data);
3218         }
3219         fprintf(fp, "\n");
3220         /* write newsgroup list */
3221         fprintf(fp, "NG:");
3222         if(compose->newsgroup_list) {
3223                 fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data);
3224                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
3225                         fprintf(fp, ",%s", (gchar *)cur->data);
3226         }
3227         fprintf(fp, "\n");
3228         /* Sylpheed account IDs */
3229         if(mailac) {
3230                 fprintf(fp, "MAID:%d\n", mailac->account_id);
3231         }
3232         if(newsac) {
3233                 fprintf(fp, "NAID:%d\n", newsac->account_id);
3234         }
3235         fprintf(fp, "\n");
3236         fclose(fp);
3237
3238         if (compose->bounce_filename != NULL) {
3239                 if (compose_bounce_write_to_file(compose, tmpfilename) < 0) {
3240                         unlink(tmpfilename);
3241                         lock = FALSE;
3242                         return -1;
3243                 }
3244         }
3245         else {
3246                 if (compose_write_to_file(compose, tmpfilename, FALSE) < 0) {
3247                         unlink(tmpfilename);
3248                         lock = FALSE;
3249                         return -1;
3250                 }
3251         }
3252                                                 
3253         /* queue message */
3254         queue = folder_get_default_queue();
3255
3256         folder_item_scan(queue);
3257         queue_path = folder_item_get_path(queue);
3258         if (!is_dir_exist(queue_path))
3259                 make_dir_hier(queue_path);
3260         if ((num = folder_item_add_msg(queue, tmpfilename, TRUE)) < 0) {
3261                 g_warning(_("can't queue the message\n"));
3262                 unlink(tmpfilename);
3263                 g_free(tmpfilename);
3264                 g_free(queue_path);
3265                 return -1;
3266         }
3267         g_free(tmpfilename);
3268
3269         if (compose->mode == COMPOSE_REEDIT) {
3270                 compose_remove_reedit_target(compose);
3271                 if (compose->targetinfo &&
3272                     compose->targetinfo->folder != queue)
3273                         folderview_update_item
3274                                 (compose->targetinfo->folder, TRUE);
3275         }
3276
3277         if ((fp = procmsg_open_mark_file(queue_path, TRUE)) == NULL)
3278                 g_warning(_("can't open mark file\n"));
3279         else {
3280                 MsgInfo newmsginfo;
3281
3282                 newmsginfo.msgnum = num;
3283                 newmsginfo.flags.perm_flags = 0;
3284                 newmsginfo.flags.tmp_flags = 0;
3285                 procmsg_write_flags(&newmsginfo, fp);