]> granicus.if.org Git - neomutt/commitdiff
Enable editing of multipart/multilingual mails
authorYubin Ruan <ablacktshirt@gmail.com>
Sun, 1 Apr 2018 13:20:01 +0000 (21:20 +0800)
committerRichard Russon <rich@flatcap.org>
Sun, 1 Apr 2018 13:20:01 +0000 (21:20 +0800)
compose.c
functions.h
handler.c
mutt/mime.c
mutt/mime.h
opcodes.h
sendlib.c

index 0ff704c6673cf25f9b9dfbef3ad15f15ee73dbb2..0acf0043992a4c3eb0f87c3ac312e452ca08ae0c 100644 (file)
--- a/compose.c
+++ b/compose.c
@@ -1171,6 +1171,104 @@ int mutt_compose_menu(struct Header *msg, char *fcc, size_t fcclen,
         menu->redraw = REDRAW_INDEX;
         break;
 
+      case OP_COMPOSE_GROUP_LINGUAL:
+      {
+        if (menu->tagged < 2)
+        {
+          mutt_error(
+              _("Grouping multilingual requires at least 2 tagged messages."));
+          break;
+        }
+
+        /* traverse to see whether all the parts have Content-Language: set */
+        struct Body *b = msg->content;
+        int tagged_with_lang_num = 0;
+        for (i = 0; b; b = b->next)
+          if (b->tagged && b->language && *b->language)
+            tagged_with_lang_num++;
+
+        if (menu->tagged != tagged_with_lang_num)
+        {
+          if (mutt_yesorno(
+                  _("Not all parts have Content-Language: set, continue?"), MUTT_YES) != MUTT_YES)
+          {
+            mutt_message(_("Not sending this message."));
+            break;
+          }
+        }
+
+        struct Body *group = mutt_body_new();
+        group->type = TYPEMULTIPART;
+        group->subtype = mutt_str_strdup("multilingual");
+        group->disposition = DISPINLINE;
+
+        struct Body *alts = NULL;
+        /* group tagged message into a multipart/multilingual */
+        struct Body *bptr = msg->content;
+        for (i = 0; bptr;)
+        {
+          if (bptr->tagged)
+          {
+            bptr->tagged = false;
+            bptr->disposition = DISPINLINE;
+
+            /* for first match, set group desc according to match */
+#define LINGUAL_TAG "Multilingual part for \"%s\""
+            if (!group->description)
+            {
+              char *p = bptr->description ? bptr->description : bptr->filename;
+              if (p)
+              {
+                group->description =
+                    mutt_mem_calloc(1, strlen(p) + strlen(LINGUAL_TAG) + 1);
+                sprintf(group->description, LINGUAL_TAG, p);
+              }
+            }
+
+            /* append bptr to the alts list,
+             * and remove from the msg->content list */
+            if (alts == NULL)
+            {
+              group->parts = alts = bptr;
+              bptr = bptr->next;
+              alts->next = NULL;
+            }
+            else
+            {
+              alts->next = bptr;
+              bptr = bptr->next;
+              alts = alts->next;
+              alts->next = NULL;
+            }
+
+            for (int j = i; j < actx->idxlen - 1; j++)
+            {
+              actx->idx[j] = actx->idx[j + 1];
+              actx->idx[j + 1] = NULL; /* for debug reason */
+            }
+            actx->idxlen--;
+          }
+          else
+          {
+            bptr = bptr->next;
+            i++;
+          }
+        }
+
+        group->next = NULL;
+        mutt_generate_boundary(&group->parameter);
+
+        /* if no group desc yet, make one up */
+        if (!group->description)
+          group->description = mutt_str_strdup("unknown multilingual group");
+
+        struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
+        gptr->content = group;
+        update_idx(menu, actx, gptr);
+      }
+        menu->redraw = REDRAW_INDEX;
+        break;
+
       case OP_COMPOSE_ATTACH_FILE:
       {
         char *prompt = _("Attach file");
@@ -1423,6 +1521,23 @@ int mutt_compose_menu(struct Header *msg, char *fcc, size_t fcclen,
         mutt_message_hook(NULL, msg, MUTT_SEND2HOOK);
         break;
 
+      case OP_COMPOSE_EDIT_LANGUAGE:
+        CHECK_COUNT;
+        buf[0] = 0; /* clear buffer first */
+        if (CURATTACH->content->language)
+          mutt_str_strfcpy(buf, CURATTACH->content->language, sizeof(buf));
+        if ((mutt_get_field("Content-Language: ", buf, sizeof(buf), 0) == 0) &&
+            mutt_mime_valid_language(buf))
+        {
+          CURATTACH->content->language = mutt_str_strdup(buf);
+          menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
+          mutt_clear_error();
+        }
+        else
+          mutt_error(_("Invalid language, default to none."));
+        mutt_message_hook(NULL, msg, MUTT_SEND2HOOK);
+        break;
+
       case OP_COMPOSE_EDIT_ENCODING:
         CHECK_COUNT;
         mutt_str_strfcpy(buf, ENCODING(CURATTACH->content->encoding), sizeof(buf));
index 17e590000731beab5e103d68e56473dde4cb87e2..d3ab8c29203190d41adf77d0c0934920db933fb6 100644 (file)
@@ -436,6 +436,7 @@ const struct Binding OpCompose[] = { /* map: compose */
 #endif
   { "edit-from",             OP_COMPOSE_EDIT_FROM,           "\033f" },
   { "edit-headers",          OP_COMPOSE_EDIT_HEADERS,        "E" },
+  { "edit-language",         OP_COMPOSE_EDIT_LANGUAGE,       "\014" },
   { "edit-message",          OP_COMPOSE_EDIT_MESSAGE,        "e" },
   { "edit-mime",             OP_COMPOSE_EDIT_MIME,           "m" },
 #ifdef USE_NNTP
@@ -452,6 +453,7 @@ const struct Binding OpCompose[] = { /* map: compose */
   { "forget-passphrase",     OP_FORGET_PASSPHRASE,           "\006" },
   { "get-attachment",        OP_COMPOSE_GET_ATTACHMENT,      "G" },
   { "group-alternatives",    OP_COMPOSE_GROUP_ALTS,          "&" },
+  { "group-multilingual",    OP_COMPOSE_GROUP_LINGUAL,       "^" },
   { "ispell",                OP_COMPOSE_ISPELL,              "i" },
 #ifdef MIXMASTER
   { "mix",                   OP_COMPOSE_MIX,                 "M" },
index 60c55e706feab46499439ada3cbafb139f4e7fac..077a962024ddaf109517df195974087d3244cefc 100644 (file)
--- a/handler.c
+++ b/handler.c
@@ -1189,7 +1189,7 @@ static int multilingual_handler(struct Body *a, struct State *s)
   struct Body *b = NULL;
   bool mustfree = false;
   int rc = 0;
-  struct Body *first_multilingual_part = NULL;
+  struct Body *first_part = NULL;
   struct Body *zxx_part = NULL;
   char *lang;
 
@@ -1226,8 +1226,8 @@ static int multilingual_handler(struct Body *a, struct State *s)
     {
       if (mutt_can_decode(b))
       {
-        if (b->language && !first_multilingual_part)
-          first_multilingual_part = b;
+        if (!first_part)
+          first_part = b;
 
         if (b->language && (mutt_str_strcmp("zxx", b->language) == 0))
           zxx_part = b;
@@ -1264,7 +1264,7 @@ static int multilingual_handler(struct Body *a, struct State *s)
     if (zxx_part)
       mutt_body_handler(zxx_part, s);
     else
-      mutt_body_handler(first_multilingual_part, s);
+      mutt_body_handler(first_part, s);
   }
 
   if (mustfree)
index fb99cb7ce879be0668b8324de09f2e2afd7bffc0..f65c39324d58f17e76a70a2023066f925d3017ef 100644 (file)
  */
 
 #include "config.h"
+#include <stdbool.h>
 #include "mime.h"
+#include "memory.h"
+#include "string2.h"
 
 // clang-format off
 /**
@@ -43,6 +46,36 @@ const int IndexHex[128] = {
     -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
 };
+
+/**
+ * BodyLanguages - Common MIME body languages
+ */
+const char *const BodyLanguages[] = {
+  "cs-cz",
+  "da", "da-dk",
+  "de", "de-at", "de-ch", "de-de",
+  "el", "el-gr",
+  "en", "en-au", "en-ca", "en-dk", "en-fi", "en-gb", "en-id", "en-ie", "en-il",
+  "en-in", "en-my", "en-no", "en-nz", "en-ph", "en-sg", "en-th", "en-us", "en-ww",
+  "en-xa", "en-za",
+  "es", "es-ar", "es-cl", "es-co", "es-es", "es-la", "es-mx", "es-pr", "es-us",
+  "fi", "fi-fi",
+  "fr", "fr-be", "fr-ca", "fr-ch", "fr-fr", "fr-lu",
+  "he", "he-il",
+  "hu", "hu-hu",
+  "it", "it-it",
+  "ja", "ja-jp",
+  "ko", "ko-kr",
+  "nl", "nl-be", "nl-nl",
+  "no", "no-no",
+  "pl", "pl-pl",
+  "pt", "pt-br", "pt-pt",
+  "ru", "ru-ru",
+  "sl", "sl-sl",
+  "sv", "sv-se",
+  "tr", "tr-tr",
+  "zh", "zh-cn", "zh-hk", "zh-tw",
+};
 // clang-format on
 
 /**
@@ -65,3 +98,21 @@ const char *const BodyEncodings[] = {
  * MimeSpecials - Characters that need special treatment in MIME
  */
 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
+
+/**
+ * mutt_mime_valid_language - Is it a valid language code?
+ * @param lang Language code to check (e.g., en)
+ * @retval true Valid language code
+ *
+ * @note Currently this checking does not strictly adhere to RFC3282 and
+ * RFC5646. See #BodyLanguages for all supported languages.
+ */
+bool mutt_mime_valid_language(const char *lang)
+{
+  for (size_t i = 0; i < mutt_array_size(BodyLanguages); i++)
+  {
+    if (mutt_str_strcasecmp(lang, BodyLanguages[i]) == 0)
+      return true;
+  }
+  return false;
+}
index 12e2aa1bda5cacf5dc6392ac7cfdbc386e3e20fe..0051b86908f5ad6dad56a4ec776746c14c7cef6c 100644 (file)
@@ -23,6 +23,8 @@
 #ifndef _MUTT_MIME_H
 #define _MUTT_MIME_H
 
+#include <stdbool.h>
+
 /**
  * enum ContentType - Content-Type
  */
@@ -70,8 +72,11 @@ enum ContentDisposition
 extern const int IndexHex[128];
 extern const char *const BodyTypes[];
 extern const char *const BodyEncodings[];
+extern const char *const BodyLanguages[];
 extern const char MimeSpecials[];
 
+bool mutt_mime_valid_language(const char *lang);
+
 #define hexval(c) IndexHex[(unsigned int) (c)]
 
 #define is_multipart(x)                                                             \
index 5ca79bb79a20f2582d747ea4592f54cdd43ccb10..ac891da54530d227da66c47236bebd523330bae3 100644 (file)
--- a/opcodes.h
+++ b/opcodes.h
@@ -56,6 +56,7 @@
   _fmt(OP_COMPOSE_EDIT_FOLLOWUP_TO,       N_("edit the Followup-To field")) \
   _fmt(OP_COMPOSE_EDIT_FROM,              N_("edit the from field")) \
   _fmt(OP_COMPOSE_EDIT_HEADERS,           N_("edit the message with headers")) \
+  _fmt(OP_COMPOSE_EDIT_LANGUAGE,          N_("edit the Content-Language of the attachment")) \
   _fmt(OP_COMPOSE_EDIT_MESSAGE,           N_("edit the message")) \
   _fmt(OP_COMPOSE_EDIT_MIME,              N_("edit attachment using mailcap entry")) \
   _fmt(OP_COMPOSE_EDIT_NEWSGROUPS,        N_("edit the newsgroups list")) \
@@ -65,6 +66,7 @@
   _fmt(OP_COMPOSE_EDIT_X_COMMENT_TO,      N_("edit the X-Comment-To field")) \
   _fmt(OP_COMPOSE_GET_ATTACHMENT,         N_("get a temporary copy of an attachment")) \
   _fmt(OP_COMPOSE_GROUP_ALTS,             N_("group tagged attachments as multipart/alternative")) \
+  _fmt(OP_COMPOSE_GROUP_LINGUAL,          N_("group tagged attachments as multipart/multilingual")) \
   _fmt(OP_COMPOSE_ISPELL,                 N_("run ispell on the message")) \
   _fmt(OP_COMPOSE_MOVE_DOWN,              N_("move an attachment down in the attachment list")) \
   _fmt(OP_COMPOSE_MOVE_UP,                N_("move an attachment up in the attachment list")) \
index 76cb6841df3174092c618bd22348299e07cab334..e044725fa965b717706f74094083fe51d2aa3dbf 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -355,6 +355,9 @@ int mutt_write_mime_header(struct Body *a, FILE *f)
 
   fputc('\n', f);
 
+  if (a->language)
+    fprintf(f, "Content-Language: %s\n", a->language);
+
   if (a->description)
     fprintf(f, "Content-Description: %s\n", a->description);