Created autoconf/automake rules for the quote format parser.
[claws.git] / src / compose.c
index fe5ed023b09684772559b09b55782b8cd918c3cf..7f133f78505ec5f8ba720dbd0f350bee214d24b8 100644 (file)
 #  include "rfc2015.h"
 #endif
 
+/* #include "quote_fmt_parse.h" */
+
 typedef enum
 {
        COL_MIMETYPE = 0,
@@ -140,7 +142,8 @@ static gchar *compose_quote_parse_fmt               (Compose        *compose,
                                                 const gchar    *fmt);
 static void compose_reply_set_entry            (Compose        *compose,
                                                 MsgInfo        *msginfo,
-                                                gboolean        to_all);
+                                                gboolean        to_all,
+                                                gboolean        to_author);
 static void compose_reedit_set_entry           (Compose        *compose,
                                                 MsgInfo        *msginfo);
 static void compose_insert_sig                 (Compose        *compose);
@@ -152,6 +155,7 @@ static void compose_attach_append           (Compose        *compose,
 static void compose_wrap_line                  (Compose        *compose);
 static void compose_set_title                  (Compose        *compose);
 
+static PrefsAccount *compose_current_mail_account(void);
 /* static gint compose_send                    (Compose        *compose); */
 static gint compose_write_to_file              (Compose        *compose,
                                                 const gchar    *file,
@@ -364,6 +368,13 @@ static void replyto_activated              (GtkWidget      *widget,
                                         Compose        *compose);
 static void followupto_activated       (GtkWidget      *widget,
                                         Compose        *compose);
+static void compose_attach_parts(Compose * compose,
+                                MsgInfo * msginfo);
+
+static gchar *compose_quote_fmt                (Compose        *compose,
+                                        MsgInfo        *msginfo,
+                                        const gchar    *fmt,
+                                        const gchar    * qmark);
 
 static GtkItemFactoryEntry compose_popup_entries[] =
 {
@@ -440,7 +451,7 @@ Compose * compose_new_with_recipient(PrefsAccount *account, const gchar *to)
        Compose *compose;
 
        if (!account) account = cur_account;
-       g_return_if_fail(account != NULL);
+       g_return_val_if_fail(account != NULL, NULL);
 
        compose = compose_create(account);
        compose->mode = COMPOSE_NEW;
@@ -462,10 +473,20 @@ Compose * compose_new_with_recipient(PrefsAccount *account, const gchar *to)
        return compose;
 }
 
-void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all)
+#define CHANGE_FLAGS(msginfo) \
+{ \
+if (msginfo->folder->folder->change_flags != NULL) \
+msginfo->folder->folder->change_flags(msginfo->folder->folder, \
+                                     msginfo->folder, \
+                                     msginfo); \
+}
+
+void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all,
+                  gboolean to_author)
 {
        Compose *compose;
        PrefsAccount *account;
+       PrefsAccount *reply_account;
        GtkSText *text;
 
        g_return_if_fail(msginfo != NULL);
@@ -475,14 +496,26 @@ void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all)
        if (!account) account = cur_account;
        g_return_if_fail(account != NULL);
 
+       if (to_author && account->protocol == A_NNTP) {
+               reply_account =
+                       account_find_mail_from_address(account->address);
+               if (!reply_account)
+                       reply_account = compose_current_mail_account();
+               if (!reply_account)
+                       return;
+       } else
+               reply_account = account;
+
        MSG_UNSET_FLAGS(msginfo->flags, MSG_FORWARDED);
        MSG_SET_FLAGS(msginfo->flags, MSG_REPLIED);
 
-       compose = compose_create(account);
+       CHANGE_FLAGS(msginfo);
+
+       compose = compose_create(reply_account);
        compose->mode = COMPOSE_REPLY;
 
        if (compose_parse_header(compose, msginfo) < 0) return;
-       compose_reply_set_entry(compose, msginfo, to_all);
+       compose_reply_set_entry(compose, msginfo, to_all, to_author);
 
        text = GTK_STEXT(compose->text);
        gtk_stext_freeze(text);
@@ -494,11 +527,27 @@ void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all)
                if ((fp = procmime_get_text_part(msginfo)) == NULL)
                        g_warning(_("Can't get text part\n"));
                else {
+                       gchar * qmark;
+
+                       if (prefs_common.quotemark && *prefs_common.quotemark)
+                               qmark = prefs_common.quotemark;
+                       else
+                               qmark = "> ";
+
+                       quote_str = compose_quote_fmt(compose, msginfo,
+                                                     prefs_common.quotefmt,
+                                                     qmark);
+
+                       /*
                        quote_str = compose_quote_parse_fmt
                                (compose, msginfo, prefs_common.quotefmt);
-                       gtk_stext_insert(text, NULL, NULL, NULL, quote_str, -1);
-                       g_free(quote_str);
-                       compose_quote_file(compose, msginfo, fp);
+                       */
+
+                       if (quote_str != NULL)
+                               gtk_stext_insert(text, NULL, NULL, NULL,
+                                                quote_str, -1);
+                       /*                      g_free(quote_str); */
+                       /* compose_quote_file(compose, msginfo, fp); */
                        fclose(fp);
                }
        }
@@ -512,6 +561,208 @@ void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all)
        gtk_widget_grab_focus(compose->text);
 }
 
+
+static gchar *procmime_get_file_name(MimeInfo *mimeinfo)
+{
+       gchar *base;
+       gchar *filename;
+
+       g_return_val_if_fail(mimeinfo != NULL, NULL);
+
+       base = mimeinfo->filename ? mimeinfo->filename
+               : mimeinfo->name ? mimeinfo->name : NULL;
+
+       if (MIME_TEXT_HTML == mimeinfo->mime_type && base == NULL)
+               base = "mimetmp.html";
+       else {
+               base = base ? base : "mimetmp";
+               base = g_basename(base);
+               if (*base == '\0') base = "mimetmp";
+       }
+
+       filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S,
+                              base, NULL);
+
+       return filename;
+}
+
+static gchar * mime_extract_file(gchar * source, MimeInfo *partinfo)
+{
+       gchar *filename;
+
+       if (!partinfo) return;
+
+       filename = procmime_get_file_name(partinfo);
+
+       if (procmime_get_part(filename, source, partinfo) < 0)
+               alertpanel_error
+                       (_("Can't get the part of multipart message."));
+
+       return filename;
+}
+
+static void compose_attach_parts(Compose * compose,
+                                MsgInfo * msginfo)
+{
+
+       FILE *fp;
+       gchar *file;
+       MimeInfo *mimeinfo;
+       MsgInfo *tmpmsginfo;
+       gchar *p;
+       gchar *boundary;
+       gint boundary_len = 0;
+       gchar buf[BUFFSIZE];
+       glong fpos, prev_fpos;
+       gint npart;
+       gchar * source;
+       gchar * filename;
+
+       g_return_if_fail(msginfo != NULL);
+       
+#if USE_GPGME
+       for (;;) {
+               if ((fp = procmsg_open_message(msginfo)) == NULL) return;
+               mimeinfo = procmime_scan_mime_header(fp);
+               if (!mimeinfo) break;
+
+               if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
+                   rfc2015_is_encrypted(mimeinfo)) {
+                       MSG_SET_FLAGS(msginfo->flags, MSG_ENCRYPTED);
+               }
+               if (MSG_IS_ENCRYPTED(msginfo->flags) &&
+                   !msginfo->plaintext_file  &&
+                   !msginfo->decryption_failed) {
+                       rfc2015_decrypt_message(msginfo, mimeinfo, fp);
+                       if (msginfo->plaintext_file &&
+                           !msginfo->decryption_failed) {
+                               fclose(fp);
+                               continue;
+                       }
+               }
+               
+               break;
+       }
+#else /* !USE_GPGME */
+       if ((fp = procmsg_open_message(msginfo)) == NULL) return;
+       mimeinfo = procmime_scan_mime_header(fp);
+#endif /* USE_GPGME */
+
+       fclose(fp);
+       if (!mimeinfo) return;
+       if (mimeinfo->mime_type == MIME_TEXT)
+               return;
+
+       if ((fp = procmsg_open_message(msginfo)) == NULL) return;
+
+       g_return_if_fail(mimeinfo != NULL);
+       g_return_if_fail(mimeinfo->mime_type != MIME_TEXT);
+
+       if (mimeinfo->mime_type == MIME_MULTIPART) {
+               g_return_if_fail(mimeinfo->boundary != NULL);
+               g_return_if_fail(mimeinfo->sub == NULL);
+       }
+       g_return_if_fail(fp != NULL);
+
+       boundary = mimeinfo->boundary;
+
+       if (boundary) {
+               boundary_len = strlen(boundary);
+
+               /* look for first boundary */
+               while ((p = fgets(buf, sizeof(buf), fp)) != NULL)
+                       if (IS_BOUNDARY(buf, boundary, boundary_len)) break;
+               if (!p) {
+                       fclose(fp);
+                       return;
+               }
+       }
+
+       if ((fpos = ftell(fp)) < 0) {
+               perror("ftell");
+               fclose(fp);
+               return;
+       }
+
+       for (npart = 0;; npart++) {
+               MimeInfo *partinfo;
+               gboolean eom = FALSE;
+
+               prev_fpos = fpos;
+
+               partinfo = procmime_scan_mime_header(fp);
+               if (!partinfo) break;
+
+               if (npart != 0)
+                       procmime_mimeinfo_insert(mimeinfo, partinfo);
+               else
+                       procmime_mimeinfo_free(partinfo);
+
+               /* look for next boundary */
+               buf[0] = '\0';
+               while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
+                       if (IS_BOUNDARY(buf, boundary, boundary_len)) {
+                               if (buf[2 + boundary_len]     == '-' &&
+                                   buf[2 + boundary_len + 1] == '-')
+                                       eom = TRUE;
+                               break;
+                       }
+               }
+               if (p == NULL)
+                       eom = TRUE;     /* broken MIME message */
+               fpos = ftell(fp);
+
+               partinfo->size = fpos - prev_fpos - strlen(buf);
+
+               if (eom) break;
+       }
+
+       source = procmsg_get_message_file_path(msginfo);
+
+       g_return_if_fail(mimeinfo != NULL);
+
+       if (!mimeinfo->main && mimeinfo->parent)
+               {
+                       filename = mime_extract_file(source, mimeinfo);
+
+                       compose_attach_append(compose, filename,
+                                             mimeinfo->mime_type);
+
+                       g_free(filename);
+               }
+
+       if (mimeinfo->sub && mimeinfo->sub->children)
+               {
+                       filename = mime_extract_file(source, mimeinfo->sub);
+
+                       compose_attach_append(compose, filename,
+                                             mimeinfo->sub->mime_type);
+
+                       g_free(filename);
+               }
+
+       if (mimeinfo->children) {
+               MimeInfo *child;
+
+               child = mimeinfo->children;
+               while (child) {
+                       filename = mime_extract_file(source, child);
+
+                       compose_attach_append(compose, filename,
+                                             child->mime_type);
+
+                       g_free(filename);
+
+                       child = child->next;
+               }
+       }
+
+       fclose(fp);
+
+       procmime_mimeinfo_free_all(mimeinfo);
+}
+
+
 #define INSERT_FW_HEADER(var, hdr) \
 if (msginfo->var && *msginfo->var) { \
        gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
@@ -528,18 +779,20 @@ Compose * compose_forward(PrefsAccount * account, MsgInfo *msginfo,
        FILE *fp;
        gchar buf[BUFFSIZE];
 
-       g_return_if_fail(msginfo != NULL);
-       g_return_if_fail(msginfo->folder != NULL);
+       g_return_val_if_fail(msginfo != NULL, NULL);
+       g_return_val_if_fail(msginfo->folder != NULL, NULL);
 
        if (account == NULL) {
                account = msginfo->folder->folder->account;
                if (!account) account = cur_account;
        }
-       g_return_if_fail(account != NULL);
+       g_return_val_if_fail(account != NULL, NULL);
 
        MSG_UNSET_FLAGS(msginfo->flags, MSG_REPLIED);
        MSG_SET_FLAGS(msginfo->flags, MSG_FORWARDED);
 
+       CHANGE_FLAGS(msginfo);
+
        compose = compose_create(account);
        compose->mode = COMPOSE_FORWARD;
 
@@ -564,25 +817,32 @@ Compose * compose_forward(PrefsAccount * account, MsgInfo *msginfo,
 
                g_free(msgfile);
        } else {
+               FILE *fp;
+               gchar *quote_str;
+
                if ((fp = procmime_get_text_part(msginfo)) == NULL)
                        g_warning(_("Can't get text part\n"));
                else {
-                       /* insert header */
-                       gtk_stext_insert(text, NULL, NULL, NULL,
-                                       _("\n\nBegin forwarded message:\n\n"), -1);
-                       INSERT_FW_HEADER(date,       "Date: ");
-                       INSERT_FW_HEADER(from,       "From: ");
-                       INSERT_FW_HEADER(to,         "To: ");
-                       INSERT_FW_HEADER(newsgroups, "Newsgroups: ");
-                       INSERT_FW_HEADER(subject,    "Subject: ");
-                       gtk_stext_insert(text, NULL, NULL, NULL, "\n\n", 2);
-
-                       /* forward body */
-                       while (fgets(buf, sizeof(buf), fp) != NULL)
+                       gchar * qmark;
+
+                       if (prefs_common.fw_quotemark &&
+                           *prefs_common.fw_quotemark)
+                               qmark = prefs_common.fw_quotemark;
+                       else
+                               qmark = "> ";
+
+                       quote_str = compose_quote_fmt(compose, msginfo,
+                                                     prefs_common.fw_quotefmt,
+                                                     qmark);
+
+                       if (quote_str != NULL)
                                gtk_stext_insert(text, NULL, NULL, NULL,
-                                               buf, -1);
+                                                quote_str, -1);
+
                        fclose(fp);
                }
+
+               compose_attach_parts(compose, msginfo);
        }
 
        if (prefs_common.auto_sig)
@@ -633,6 +893,7 @@ void compose_reedit(MsgInfo *msginfo)
                        gtk_stext_insert(text, NULL, NULL, NULL, buf, -1);
                fclose(fp);
        }
+       compose_attach_parts(compose, msginfo);
 
        gtk_stext_thaw(text);
        gtk_widget_grab_focus(compose->text);
@@ -811,6 +1072,7 @@ static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
        return new_ref_str;
 }
 
+/*
 static void compose_quote_file(Compose *compose, MsgInfo *msginfo, FILE *fp)
 {
        GtkSText *text = GTK_STEXT(compose->text);
@@ -825,7 +1087,7 @@ static void compose_quote_file(Compose *compose, MsgInfo *msginfo, FILE *fp)
        gint str_len;
        gint ch_len;
 
-       /* if (prefs_common.enable_color) qcolor = &quote_color; */
+       // if (prefs_common.enable_color) qcolor = &quote_color;
        if (prefs_common.quotemark && *prefs_common.quotemark)
                qmark = prefs_common.quotemark;
        else
@@ -911,7 +1173,9 @@ static void compose_quote_file(Compose *compose, MsgInfo *msginfo, FILE *fp)
 
        g_free(quote_str);
 }
+*/
 
+/*
 static gchar *compose_quote_parse_fmt(Compose *compose, MsgInfo *msginfo,
                                      const gchar *fmt)
 {
@@ -955,7 +1219,7 @@ static gchar *compose_quote_parse_fmt(Compose *compose, MsgInfo *msginfo,
                                str = msginfo->from;
                                sp++;
                                break;
-                       case 'I':       /* initial */
+                       case 'I':
                                if (!msginfo->fromname) {sp++; break;}
                                p = msginfo->fromname;
                                tmp[0] = tmp[1] = tmp[2] = '\0';
@@ -1066,9 +1330,10 @@ static gchar *compose_quote_parse_fmt(Compose *compose, MsgInfo *msginfo,
 
        return ext_str;
 }
+*/
 
 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
-                                   gboolean to_all)
+                                   gboolean to_all, gboolean to_author)
 {
        GSList *cc_list;
        GSList *cur;
@@ -1080,8 +1345,9 @@ static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
 
        if (compose->account->protocol != A_NNTP)
                gtk_entry_set_text(GTK_ENTRY(compose->to_entry),
-                                  compose->replyto ? compose->replyto
-                                  : msginfo->from ? msginfo->from : "");
+                                  ( (compose->replyto && !to_author) 
+                                    ? compose->replyto
+                                    : msginfo->from ? msginfo->from : ""));
        if (compose->account->protocol == A_NNTP)
                gtk_entry_set_text(GTK_ENTRY(compose->newsgroups_entry),
                                   compose->followup_to ? compose->followup_to
@@ -1387,6 +1653,33 @@ static void compose_set_title(Compose *compose)
        g_free(str);
 }
 
+/**
+ * compose_current_mail_account:
+ * 
+ * Find a current mail account (the currently selected account, or the
+ * default account, if a news account is currently selected).  If a
+ * mail account cannot be found, display an error message.
+ * 
+ * Return value: Mail account, or NULL if not found.
+ **/
+static PrefsAccount *
+compose_current_mail_account(void)
+{
+       PrefsAccount *ac;
+
+       if (cur_account && cur_account->protocol != A_NNTP)
+               ac = cur_account;
+       else {
+               ac = account_get_default();
+               if (!ac || ac->protocol == A_NNTP) {
+                       alertpanel_error(_("Account for sending mail is not specified.\n"
+                                          "Please select a mail account before sending."));
+                       return NULL;
+               }
+       }
+       return ac;
+}
+
 gint compose_send(Compose *compose)
 {
        gchar tmp[MAXPATHLEN + 1];
@@ -1435,13 +1728,9 @@ gint compose_send(Compose *compose)
                        ac = compose->account;
                else if (compose->orig_account->protocol != A_NNTP)
                        ac = compose->orig_account;
-               else if (cur_account && cur_account->protocol != A_NNTP)
-                       ac = cur_account;
                else {
-                       ac = account_get_default();
-                       if (!ac || ac->protocol == A_NNTP) {
-                               alertpanel_error(_("Account for sending mail is not specified.\n"
-                                                  "Please select a mail account before sending."));
+                       ac = compose_current_mail_account();
+                       if (!ac) {
                                unlink(tmp);
                                lock = FALSE;
                                return -1;
@@ -4120,3 +4409,39 @@ static void compose_toggle_return_receipt_cb(gpointer data, guint action,
        else
                compose->return_receipt = FALSE;
 }
+
+static gchar *compose_quote_fmt                (Compose        *compose,
+                                        MsgInfo        *msginfo,
+                                        const gchar    *fmt,
+                                        const gchar    *qmark)
+{
+       gchar * quote_str = NULL;
+
+       if (qmark != NULL) {
+               gchar * p;
+
+               quote_fmt_init(msginfo, NULL);
+               quote_fmt_scan_string(qmark);
+               quote_fmtparse();
+
+               p = quote_fmt_get_buffer();
+               if (p == NULL) {
+                       alertpanel_error
+                               (_("Quote mark format error."));
+               }
+               else {
+                       quote_str = alloca(strlen(p) + 1);
+                       strcpy(quote_str, p);
+               }
+       }
+
+       quote_fmt_init(msginfo, quote_str);
+       quote_fmt_scan_string(fmt);
+       quote_fmtparse();
+
+       if (quote_fmt_get_buffer() == NULL)
+               alertpanel_error
+                       (_("Message reply/forward format error."));
+
+       return quote_fmt_get_buffer();
+}