Add Sign/Encrypt toggle buttons to Compose toolbar.
[claws.git] / src / compose.c
index dfd0d2266d79606d59156c6dc3d9795f2308a9e9..4ff2363c962f2ad2aab16595afe5fb28f80d43f6 100644 (file)
 #include "timing.h"
 #include "autofaces.h"
 #include "spell_entry.h"
+#include "headers.h"
 
 enum
 {
@@ -489,8 +490,6 @@ static void compose_update_privacy_system_menu_item(Compose * compose, gboolean
 static void activate_privacy_system     (Compose *compose, 
                                          PrefsAccount *account,
                                         gboolean warn);
-static void compose_use_signing(Compose *compose, gboolean use_signing);
-static void compose_use_encryption(Compose *compose, gboolean use_encryption);
 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
                                         gpointer        data);
 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
@@ -1320,6 +1319,7 @@ static void compose_force_encryption(Compose *compose, PrefsAccount *account,
                if (privacy_avail && g_slist_length(privacy_avail)) {
                        privacy = (gchar *)(privacy_avail->data);
                }
+               g_slist_free_full(privacy_avail, g_free);
        }
        if (privacy != NULL) {
                if (system) {
@@ -2755,6 +2755,10 @@ void compose_toolbar_cb(gint action, gpointer data)
                compose_check_all(NULL, compose);
                break;
 #endif
+       case A_PRIVACY_SIGN:
+               break;
+       case A_PRIVACY_ENCRYPT:
+               break;
        default:
                break;
        }
@@ -2980,7 +2984,9 @@ static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
                if (msginfo->inreplyto && *msginfo->inreplyto)
                        compose->inreplyto = g_strdup(msginfo->inreplyto);
 
-               if (msginfo->msgid && *msginfo->msgid)
+               if (msginfo->msgid && *msginfo->msgid &&
+                               compose->folder != NULL &&
+                               compose->folder->stype ==  F_DRAFT)
                        compose->msgid = g_strdup(msginfo->msgid);
        } else {
                if (msginfo->msgid && *msginfo->msgid)
@@ -3755,7 +3761,7 @@ static gboolean compose_attach_append(Compose *compose, const gchar *file,
                g_free(file_from_uri);
                if (result)
                        return TRUE;
-               alertpanel_error("File %s doesn't exist\n", filename);
+               alertpanel_error("File %s doesn't exist or permission denied\n", filename);
                return FALSE;
        }
        if ((size = get_file_size(file)) < 0) {
@@ -3867,13 +3873,13 @@ static gboolean compose_attach_append(Compose *compose, const gchar *file,
        return TRUE;
 }
 
-static void compose_use_signing(Compose *compose, gboolean use_signing)
+void compose_use_signing(Compose *compose, gboolean use_signing)
 {
        compose->use_signing = use_signing;
        cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
 }
 
-static void compose_use_encryption(Compose *compose, gboolean use_encryption)
+void compose_use_encryption(Compose *compose, gboolean use_encryption)
 {
        compose->use_encryption = use_encryption;
        cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
@@ -4081,10 +4087,22 @@ static int compose_itemized_length(GtkTextBuffer *buffer,
        }
 
        clen = g_unichar_to_utf8(wc, ch);
-       if (clen != 1)
-               return 0;
-
-       if (!strchr("*-+", ch[0]))
+       if (!((clen == 1 && strchr("*-+", ch[0])) ||
+           (clen == 3 && (
+               wc == 0x2022 || /* BULLET */
+               wc == 0x2023 || /* TRIANGULAR BULLET */
+               wc == 0x2043 || /* HYPHEN BULLET */
+               wc == 0x204c || /* BLACK LEFTWARDS BULLET */
+               wc == 0x204d || /* BLACK RIGHTWARDS BULLET */
+               wc == 0x2219 || /* BULLET OPERATOR */
+               wc == 0x25d8 || /* INVERSE BULLET */
+               wc == 0x25e6 || /* WHITE BULLET */
+               wc == 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
+               wc == 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
+               wc == 0x2767 || /* ROTATED FLORAL HEART BULLET */
+               wc == 0x29be || /* CIRCLED WHITE BULLET */
+               wc == 0x29bf    /* CIRCLED BULLET */
+               ))))
                return 0;
 
        gtk_text_iter_forward_char(&iter);
@@ -5151,7 +5169,7 @@ static gboolean compose_check_entries(Compose *compose, gboolean check_everythin
                }
        }
 
-       if (!compose->batch && prefs_common.warn_sending_many_recipients == TRUE
+       if (!compose->batch && prefs_common.warn_sending_many_recipients_num > 0
                        && check_everything == TRUE) {
                GSList *list;
                gint cnt = 0;
@@ -5192,7 +5210,7 @@ static gboolean compose_check_entries(Compose *compose, gboolean check_everythin
                        g_free(message);
                        if (aval & G_ALERTDISABLE) {
                                aval &= ~G_ALERTDISABLE;
-                               prefs_common.warn_empty_subj = FALSE;
+                               prefs_common.warn_sending_many_recipients_num = 0;
                        }
                        if (aval != G_ALERTALTERNATE)
                                return FALSE;
@@ -5444,6 +5462,7 @@ static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
 
 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
 {
+       gchar date[RFC822_DATE_BUFFSIZE];
        gchar buf[BUFFSIZE];
        gchar *str;
        const gchar *entstr;
@@ -5456,10 +5475,10 @@ static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
 
        /* Resent-Date */
        if (prefs_common.hide_timezone)
-               get_rfc822_date_hide_tz(buf, sizeof(buf));
+               get_rfc822_date_hide_tz(date, sizeof(date));
        else
-               get_rfc822_date(buf, sizeof(buf));
-       err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
+               get_rfc822_date(date, sizeof(date));
+       err |= (fprintf(fp, "Resent-Date: %s\n", date) < 0);
 
        /* Resent-From */
        if (compose->account->name && *compose->account->name) {
@@ -5542,8 +5561,6 @@ static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
                        if (g_ascii_strncasecmp(buf, not_included[i],
                                                strlen(not_included[i])) == 0) {
                                skip = TRUE;
-                               g_free(buf);
-                               buf = NULL;
                                break;
                        }
                }
@@ -5587,7 +5604,7 @@ static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
                        goto error;
        }
 
-       if (err || ret == -1)
+       if (err)
                goto error;
 
        if (compose_redirect_write_headers(compose, fdest))
@@ -5611,7 +5628,7 @@ error:
 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
 {
        GtkTextBuffer *buffer;
-       GtkTextIter start, end;
+       GtkTextIter start, end, tmp;
        gchar *chars, *tmp_enc_file, *content;
        gchar *buf, *msg;
        const gchar *out_codeset;
@@ -5643,10 +5660,20 @@ static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gbool
        mimemsg->data.mem = compose_get_header(compose);
 
        /* Create text part MimeInfo */
-       /* get all composed text */
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
-       gtk_text_buffer_get_start_iter(buffer, &start);
        gtk_text_buffer_get_end_iter(buffer, &end);
+       tmp = end;
+
+       /* We make sure that there is a newline at the end. */
+       if (action == COMPOSE_WRITE_FOR_SEND && gtk_text_iter_backward_char(&tmp)) {
+               chars = gtk_text_buffer_get_text(buffer, &tmp, &end, FALSE);
+               if (*chars != '\n') {
+                       gtk_text_buffer_insert(buffer, &end, "\n", 1);
+               }
+       }
+
+       /* get all composed text */
+       gtk_text_buffer_get_start_iter(buffer, &start);
        chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
 
        out_codeset = conv_get_charset_str(compose->out_encoding);
@@ -6294,9 +6321,7 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent)
        if (!gtk_tree_model_get_iter_first(model, &iter))
                return 0;
        do {
-               gtk_tree_model_get(model, &iter,
-                                  COL_DATA, &ainfo,
-                                  -1);
+               gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
                
                if (!is_file_exist(ainfo->file)) {
                        gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
@@ -6325,11 +6350,11 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent)
                        type = g_strdup("application/octet-stream");
                }
 
-               subtype = strchr(type, '/') + 1;
-               *(subtype - 1) = '\0';
-               mimepart->type = procmime_get_media_type(type);
-               mimepart->subtype = g_strdup(subtype);
-               g_free(type);
+               subtype = strchr(type, '/') + 1;
+               *(subtype - 1) = '\0';
+               mimepart->type = procmime_get_media_type(type);
+               mimepart->subtype = g_strdup(subtype);
+               g_free(type);
 
                if (mimepart->type == MIMETYPE_MESSAGE && 
                    !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
@@ -6367,8 +6392,6 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent)
                                ainfo->encoding = ENC_BASE64;
                }
 
-               
-               
                procmime_encode_content(mimepart, ainfo->encoding);
 
                g_node_append(parent->node, mimepart->node);
@@ -6423,6 +6446,40 @@ static gchar *compose_quote_list_of_addresses(gchar *str)
        (compose->account->add_customhdr && \
         custom_header_find(compose->account->customhdr_list, header) != NULL)
 
+static const gchar *compose_untranslated_header_name(gchar *header_name)
+{
+       /* return the untranslated header name, if header_name is a known
+          header name, in either its translated or untranslated form, with
+          or without trailing colon. otherwise, returns header_name. */
+       gchar *translated_header_name;
+       gchar *translated_header_name_wcolon;
+       const gchar *untranslated_header_name;
+       const gchar *untranslated_header_name_wcolon;
+       gint i;
+
+       cm_return_val_if_fail(header_name != NULL, NULL);
+
+       for (i = 0; HEADERS[i].header_name != NULL; i++) {
+               untranslated_header_name = HEADERS[i].header_name;
+               untranslated_header_name_wcolon = HEADERS[i].header_name_w_colon;
+
+               translated_header_name = gettext(untranslated_header_name);
+               translated_header_name_wcolon = gettext(untranslated_header_name_wcolon);
+
+               if (!strcmp(header_name, untranslated_header_name) ||
+                       !strcmp(header_name, translated_header_name)) {
+                       return untranslated_header_name;
+               } else {
+                       if (!strcmp(header_name, untranslated_header_name_wcolon) ||
+                               !strcmp(header_name, translated_header_name_wcolon)) {
+                               return untranslated_header_name_wcolon;
+                       }
+               }
+       }
+       debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name);
+       return header_name;
+}
+
 static void compose_add_headerfield_from_headerlist(Compose *compose, 
                                                    GString *header, 
                                                    const gchar *fieldname,
@@ -6532,6 +6589,7 @@ static gchar *compose_get_manual_headers_info(Compose *compose)
 
 static gchar *compose_get_header(Compose *compose)
 {
+       gchar date[RFC822_DATE_BUFFSIZE];
        gchar buf[BUFFSIZE];
        const gchar *entry_str;
        gchar *str;
@@ -6549,10 +6607,10 @@ static gchar *compose_get_header(Compose *compose)
 
        /* Date */
        if (prefs_common.hide_timezone)
-               get_rfc822_date_hide_tz(buf, sizeof(buf));
+               get_rfc822_date_hide_tz(date, sizeof(date));
        else
-               get_rfc822_date(buf, sizeof(buf));
-       g_string_append_printf(header, "Date: %s\n", buf);
+               get_rfc822_date(date, sizeof(date));
+       g_string_append_printf(header, "Date: %s\n", date);
 
        /* From */
        
@@ -6781,16 +6839,22 @@ static gchar *compose_get_header(Compose *compose)
                Xstrdup_a(headervalue, entry_str, return NULL);
                subst_char(headervalue, '\r', ' ');
                subst_char(headervalue, '\n', ' ');
-               string = std_headers;
-               while (*string != NULL) {
-                       headername_trans = prefs_common_translated_header_name(*string);
-                       if (!strcmp(headername_trans, headername_wcolon))
-                               standard_header = TRUE;
-                       string++;
-               }
-               if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
-                       g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
-                               
+               g_strstrip(headervalue);
+               if (*headervalue != '\0') {
+                       string = std_headers;
+                       while (*string != NULL && !standard_header) {
+                               headername_trans = prefs_common_translated_header_name(*string);
+                               /* support mixed translated and untranslated headers */
+                               if (!strcmp(headername_trans, headername_wcolon) || !strcmp(*string, headername_wcolon))
+                                       standard_header = TRUE;
+                               string++;
+                       }
+                       if (!standard_header && !IS_IN_CUSTOM_HEADER(headername)) {
+                               /* store untranslated header name */
+                               g_string_append_printf(header, "%s %s\n",
+                                               compose_untranslated_header_name(headername_wcolon), headervalue);
+                       }
+               }                               
                g_free(headername);
                g_free(headername_wcolon);              
        }
@@ -7408,7 +7472,8 @@ static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
        FolderItem *dest;
        gchar * path;
 
-       dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
+       dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
+                       _("Select folder to save message to"));
        if (!dest) return;
 
        path = folder_item_get_identifier(dest);
@@ -8407,6 +8472,10 @@ static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
 
        cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
        cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
+       gtk_widget_set_sensitive(compose->toolbar->privacy_sign_btn, can_sign);
+       gtk_widget_set_sensitive(compose->toolbar->privacy_encrypt_btn, can_encrypt);
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_sign_btn), can_sign ? compose->use_signing : FALSE);
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_encrypt_btn), can_encrypt ? compose->use_encryption : FALSE);
 }
 
 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
@@ -8754,7 +8823,7 @@ static void compose_template_apply_fields_error(const gchar *header)
        tr = g_strdup(C_("'%s' stands for a header name",
                                  "Template '%s' format error."));
        text = g_strdup_printf(tr, prefs_common_translated_header_name(header));
-       alertpanel_error(text);
+       alertpanel_error("%s", text);
 
        g_free(text);
        g_free(tr);
@@ -8996,20 +9065,26 @@ static void compose_attach_update_label(Compose *compose)
        gint i = 1;
        gchar *text;
        GtkTreeModel *model;
-       
-       if(compose == NULL)
+       goffset total_size;
+       AttachInfo *ainfo;
+
+       if (compose == NULL)
                return;
-               
+
        model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
-       if(!gtk_tree_model_get_iter_first(model, &iter)) {
+       if (!gtk_tree_model_get_iter_first(model, &iter)) {
                gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
                return;
        }
-       
-       while(gtk_tree_model_iter_next(model, &iter))
+
+       gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
+       total_size = ainfo->size;
+       while(gtk_tree_model_iter_next(model, &iter)) {
+               gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
+               total_size += ainfo->size;
                i++;
-       
-       text = g_strdup_printf("(%d)", i);
+       }
+       text = g_strdup_printf(" (%d/%s)", i, to_human_readable(total_size));
        gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
        g_free(text);
 }
@@ -11317,6 +11392,7 @@ static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
        Compose *compose = (Compose *)data;
 
        compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_sign_btn), compose->use_signing);
 }
 
 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
@@ -11324,6 +11400,7 @@ static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
        Compose *compose = (Compose *)data;
 
        compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_encrypt_btn), compose->use_encryption);
 }
 
 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
@@ -12096,15 +12173,15 @@ static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
 {
        MsgInfo *newmsginfo;
        GSList *list;
-       gchar buf[BUFFSIZE];
+       gchar date[RFC822_DATE_BUFFSIZE];
 
        cm_return_val_if_fail( compose != NULL, NULL );
 
        newmsginfo = procmsg_msginfo_new();
 
        /* date is now */
-       get_rfc822_date(buf, sizeof(buf));
-       newmsginfo->date = g_strdup(buf);
+       get_rfc822_date(date, sizeof(date));
+       newmsginfo->date = g_strdup(date);
 
        /* from */
        if (compose->from_name) {