#include "intl.h"
#include "codeconv.h"
+#include "unmime.h"
#include "base64.h"
+#include "quoted-printable.h"
#include "utils.h"
#include "prefs_common.h"
-#include "unmime.h"
typedef enum
{
unmime_header(outbuf, str);
}
-#define MAX_ENCLEN 75
#define MAX_LINELEN 76
+#define MIMESEP_BEGIN "=?"
+#define MIMESEP_END "?="
#define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
-#if HAVE_LIBJCONV
+#define LBREAK_IF_REQUIRED(cond) \
+{ \
+ if (len - (destp - dest) < MAX_LINELEN) { \
+ *destp = '\0'; \
+ return; \
+ } \
+ \
+ if (cond) { \
+ if (destp > dest && isspace(*(destp - 1))) \
+ destp--; \
+ *destp++ = '\n'; \
+ *destp++ = ' '; \
+ left = MAX_LINELEN - 1; \
+ } \
+}
+
void conv_encode_header(gchar *dest, gint len, const gchar *src,
gint header_len)
{
- wchar_t *wsrc;
- wchar_t *wsrcp;
- gchar *destp;
- size_t line_len, mimehdr_len, mimehdr_begin_len;
- gchar *mimehdr_init = "=?";
- gchar *mimehdr_end = "?=";
- gchar *mimehdr_enctype = "?B?";
- const gchar *mimehdr_charset;
-
- /* g_print("src = %s\n", src); */
- mimehdr_charset = conv_get_outgoing_charset_str();
-
- /* convert to wide-character string */
- wsrcp = wsrc = strdup_mbstowcs(src);
- if (!wsrc) {
- g_warning("Can't convert string to wide characters.\n");
- strncpy2(dest, src, len);
- return;
+ const gchar *cur_encoding;
+ const gchar *out_encoding;
+ gint mimestr_len;
+ gchar *mimesep_enc;
+ gint left;
+ const gchar *srcp = src;
+ gchar *destp = dest;
+ gboolean use_base64;
+
+ if (MB_CUR_MAX > 1) {
+ use_base64 = TRUE;
+ mimesep_enc = "?B?";
+ } else {
+ use_base64 = FALSE;
+ mimesep_enc = "?Q?";
}
- mimehdr_len = strlen(mimehdr_init) + strlen(mimehdr_end) +
- strlen(mimehdr_charset) + strlen(mimehdr_enctype);
- mimehdr_begin_len = strlen(mimehdr_init) +
- strlen(mimehdr_charset) + strlen(mimehdr_enctype);
- line_len = header_len;
- destp = dest;
- *dest = '\0';
-
- while (*wsrcp) {
- wchar_t *wp, *wtmp, *wtmpp;
- gint nspc = 0;
- gboolean str_is_non_ascii;
-
- /* irresponsible buffer overrun check */
- if ((len - (destp - dest)) < (MAX_LINELEN + 1) * 2) break;
-
- /* encode string including space
- if non-ASCII string follows */
- if (is_next_nonascii(wsrcp)) {
- wp = wsrcp;
- while ((wp = find_wspace(wp)) != NULL)
- if (!is_next_nonascii(wp)) break;
- str_is_non_ascii = TRUE;
- } else {
- wp = find_wspace(wsrcp);
- str_is_non_ascii = FALSE;
- }
-
- if (wp != NULL) {
- wtmp = wcsndup(wsrcp, wp - wsrcp);
- wsrcp = wp + 1;
- while (iswspace(wsrcp[nspc])) nspc++;
- } else {
- wtmp = wcsdup(wsrcp);
- wsrcp += wcslen(wsrcp);
- }
-
- wtmpp = wtmp;
-
- do {
- gint tlen = 0;
- gchar *tmp; /* internal codeset */
- gchar *raw; /* converted, but not base64 encoded */
- register gchar *tmpp;
- gint raw_len;
-
- tmpp = tmp = g_malloc(wcslen(wtmpp) * MB_CUR_MAX + 1);
- *tmp = '\0';
- raw = g_strdup("");
- raw_len = 0;
-
- while (*wtmpp != (wchar_t)0) {
- gint mbl;
- gint dummy;
- gchar *raw_new = NULL;
- int raw_new_len = 0;
- const gchar *src_codeset;
-
- mbl = wctomb(tmpp, *wtmpp);
- if (mbl == -1) {
- g_warning("invalid wide character\n");
- wtmpp++;
- continue;
- }
- /* g_free(raw); */
- src_codeset = conv_get_current_charset_str();
- /* printf ("tmp = %s, tlen = %d, mbl\n",
- tmp, tlen, mbl); */
- if (jconv_alloc_conv(tmp, tlen + mbl,
- &raw_new, &raw_new_len,
- &src_codeset, 1,
- &dummy, mimehdr_charset)
- != 0) {
- g_warning("can't convert\n");
- tmpp[0] = '\0';
- wtmpp++;
- continue;
- }
- if (str_is_non_ascii) {
- gint dlen = mimehdr_len +
- B64LEN(raw_len);
- if ((line_len + dlen +
- (*(wtmpp + 1) ? 0 : nspc) +
- (line_len > 1 ? 1 : 0))
- > MAX_LINELEN) {
- g_free(raw_new);
- if (tlen == 0) {
- *destp++ = '\n';
- *destp++ = ' ';
- line_len = 1;
- continue;
- } else {
- *tmpp = '\0';
- break;
- }
- }
- } else if ((line_len + tlen + mbl +
- (*(wtmpp + 1) ? 0 : nspc) +
- (line_len > 1 ? 1 : 0))
- > MAX_LINELEN) {
- g_free(raw_new);
- if (1 + tlen + mbl +
- (*(wtmpp + 1) ? 0 : nspc)
- >= MAX_LINELEN) {
- *tmpp = '\0';
- break;
- }
- *destp++ = '\n';
- *destp++ = ' ';
- line_len = 1;
- continue;
- }
-
- tmpp += mbl;
- *tmpp = '\0';
-
- tlen += mbl;
-
- g_free(raw);
- raw = raw_new;
- raw_len = raw_new_len;
-
- wtmpp++;
- }
- /* g_print("tmp = %s, tlen = %d, mb_seqlen = %d\n",
- tmp, tlen, mb_seqlen); */
-
- if (tlen == 0 || raw_len == 0) {
- g_free(tmp);
- g_free(raw);
- continue;
- }
-
- if (line_len > 1 && destp > dest) {
- *destp++ = ' ';
- *destp = '\0';
- line_len++;
- }
-
- if (str_is_non_ascii) {
- g_snprintf(destp, len - strlen(dest), "%s%s%s",
- mimehdr_init, mimehdr_charset,
- mimehdr_enctype);
- destp += mimehdr_begin_len;
- line_len += mimehdr_begin_len;
+ cur_encoding = conv_get_current_charset_str();
+ out_encoding = conv_get_outgoing_charset_str();
+ if (!strcmp(out_encoding, "US-ASCII"))
+ out_encoding = "ISO-8859-1";
- base64_encode(destp, raw, raw_len);
- line_len += strlen(destp);
- destp += strlen(destp);
+ mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
+ strlen(mimesep_enc) + strlen(MIMESEP_END);
- strcpy(destp, mimehdr_end);
- destp += strlen(mimehdr_end);
- line_len += strlen(mimehdr_end);
- } else {
- strcpy(destp, tmp);
- line_len += strlen(destp);
- destp += strlen(destp);
- }
+ left = MAX_LINELEN - header_len;
- g_free(tmp);
- g_free(raw);
- /* g_print("line_len = %d\n\n", line_len); */
- } while (*wtmpp != (wchar_t)0);
+ while (*srcp) {
+ LBREAK_IF_REQUIRED(left <= 0);
- while (iswspace(*wsrcp)) {
- gint mbl;
-
- mbl = wctomb(destp, *wsrcp++);
- if (mbl != -1) {
- destp += mbl;
- line_len += mbl;
- }
+ while (isspace(*srcp)) {
+ *destp++ = *srcp++;
+ left--;
+ LBREAK_IF_REQUIRED(left <= 0);
}
- *destp = '\0';
-
- g_free(wtmp);
- }
- g_free(wsrc);
+ /* output as it is if the next word is ASCII string */
+ if (!is_next_nonascii(srcp)) {
+ gint word_len;
- /* g_print("dest = %s\n", dest); */
-}
-#else /* !HAVE_LIBJCONV */
-
-#define JIS_SEQLEN 3
-
-void conv_encode_header(gchar *dest, gint len, const gchar *src,
- gint header_len)
-{
- wchar_t *wsrc;
- wchar_t *wsrcp;
- gchar *destp;
- size_t line_len, mimehdr_len, mimehdr_begin_len;
- gchar *mimehdr_init = "=?";
- gchar *mimehdr_end = "?=";
- gchar *mimehdr_enctype = "?B?";
- const gchar *mimehdr_charset;
- gboolean do_conv = FALSE;
-
- /* g_print("src = %s\n", src); */
- mimehdr_charset = conv_get_outgoing_charset_str();
- if (strcmp(mimehdr_charset, "ISO-2022-JP") == 0)
- do_conv = TRUE;
- else if (strcmp(mimehdr_charset, "US-ASCII") == 0)
- mimehdr_charset = "ISO-8859-1";
-
- /* convert to wide-character string */
- wsrcp = wsrc = strdup_mbstowcs(src);
- if (!wsrc) {
- g_warning("Can't convert string to wide characters.\n");
- strncpy2(dest, src, len);
- return;
- }
-
- mimehdr_len = strlen(mimehdr_init) + strlen(mimehdr_end) +
- strlen(mimehdr_charset) + strlen(mimehdr_enctype);
- mimehdr_begin_len = strlen(mimehdr_init) +
- strlen(mimehdr_charset) + strlen(mimehdr_enctype);
- line_len = header_len;
- destp = dest;
- *dest = '\0';
-
- while (*wsrcp) {
- wchar_t *wp, *wtmp, *wtmpp;
- gint nspc = 0;
- gboolean str_is_non_ascii;
-
- /* irresponsible buffer overrun check */
- if ((len - (destp - dest)) < (MAX_LINELEN + 1) * 2) break;
-
- /* encode string including space
- if non-ASCII string follows */
- if (is_next_nonascii(wsrcp)) {
- wp = wsrcp;
- while ((wp = find_wspace(wp)) != NULL)
- if (!is_next_nonascii(wp)) break;
- str_is_non_ascii = TRUE;
- } else {
- wp = find_wspace(wsrcp);
- str_is_non_ascii = FALSE;
- }
+ word_len = get_next_word_len(srcp);
+ LBREAK_IF_REQUIRED(left < word_len);
+ while(*srcp && !isspace(*srcp)) {
+ *destp++ = *srcp++;
+ left--;
+ LBREAK_IF_REQUIRED(left <= 0);
+ }
- if (wp != NULL) {
- wtmp = wcsndup(wsrcp, wp - wsrcp);
- wsrcp = wp + 1;
- while (iswspace(wsrcp[nspc])) nspc++;
- } else {
- wtmp = wcsdup(wsrcp);
- wsrcp += wcslen(wsrcp);
+ continue;
}
- wtmpp = wtmp;
-
- do {
- gint prev_mbl = 1, tlen = 0, mb_seqlen = 0;
- gchar *tmp;
- register gchar *tmpp;
+ while (1) {
+ gint mb_len = 0;
+ gint cur_len = 0;
+ gchar *part_str;
+ gchar *out_str;
+ gchar *enc_str;
+ const gchar *p = srcp;
+ gint out_str_len;
+ gint out_enc_str_len;
+ gint mime_block_len;
+ gboolean cont = FALSE;
+
+ while (*p != '\0') {
+ if (isspace(*p) && !is_next_nonascii(p + 1))
+ break;
+
+ mb_len = mblen(p, MB_CUR_MAX);
+ if (mb_len < 0) {
+ g_warning("invalid multibyte character encountered\n");
+ break;
+ }
- tmpp = tmp = g_malloc(wcslen(wtmpp) * MB_CUR_MAX + 1);
- *tmp = '\0';
+ Xstrndup_a(part_str, srcp, cur_len + mb_len, );
+ out_str = conv_codeset_strdup
+ (part_str, cur_encoding, out_encoding);
+ out_str_len = strlen(out_str);
- while (*wtmpp != (wchar_t)0) {
- gint mbl;
+ if (use_base64)
+ out_enc_str_len = B64LEN(out_str_len);
+ else
+ out_enc_str_len =
+ qp_get_q_encoding_len(out_str);
- mbl = wctomb(tmpp, *wtmpp);
- if (mbl == -1) {
- g_warning("invalid wide character\n");
- wtmpp++;
- continue;
- }
+ g_free(out_str);
- /* length of KI + KO */
- if (do_conv && prev_mbl == 1 && mbl == 2)
- mb_seqlen += JIS_SEQLEN * 2;
-
- if (str_is_non_ascii) {
- gint dlen = mimehdr_len +
- B64LEN(tlen + mb_seqlen + mbl);
-
- if ((line_len + dlen +
- (*(wtmpp + 1) ? 0 : nspc) +
- (line_len > 1 ? 1 : 0))
- > MAX_LINELEN) {
- if (tlen == 0) {
- *destp++ = '\n';
- *destp++ = ' ';
- line_len = 1;
- mb_seqlen = 0;
- continue;
- } else {
- *tmpp = '\0';
- break;
- }
- }
- } else if ((line_len + tlen + mbl +
- (*(wtmpp + 1) ? 0 : nspc) +
- (line_len > 1 ? 1 : 0))
- > MAX_LINELEN) {
- if (1 + tlen + mbl +
- (*(wtmpp + 1) ? 0 : nspc)
- >= MAX_LINELEN) {
- *tmpp = '\0';
- break;
- }
- *destp++ = '\n';
- *destp++ = ' ';
- line_len = 1;
+ if (mimestr_len + out_enc_str_len <= left) {
+ cur_len += mb_len;
+ p += mb_len;
+ } else if (cur_len == 0) {
+ LBREAK_IF_REQUIRED(1);
continue;
+ } else {
+ cont = TRUE;
+ break;
}
-
- tmpp += mbl;
- *tmpp = '\0';
-
- tlen += mbl;
- prev_mbl = mbl;
-
- wtmpp++;
- }
- /* g_print("tmp = %s, tlen = %d, mb_seqlen = %d\n",
- tmp, tlen, mb_seqlen); */
-
- if (tlen == 0) {
- g_free(tmp);
- continue;
}
- if (line_len > 1 && destp > dest) {
- *destp++ = ' ';
- *destp = '\0';
- line_len++;
- }
+ if (cur_len > 0) {
+ Xstrndup_a(part_str, srcp, cur_len, );
+ out_str = conv_codeset_strdup
+ (part_str, cur_encoding, out_encoding);
+ out_str_len = strlen(out_str);
- if (str_is_non_ascii) {
- gchar *raw;
+ if (use_base64)
+ out_enc_str_len = B64LEN(out_str_len);
+ else
+ out_enc_str_len =
+ qp_get_q_encoding_len(out_str);
- raw = g_new(gchar, tlen + mb_seqlen + 1);
- if (do_conv)
- conv_euctojis(raw, tlen + mb_seqlen + 1,
- tmp);
+ Xalloca(enc_str, out_enc_str_len + 1, );
+ if (use_base64)
+ base64_encode(enc_str, out_str, out_str_len);
else
- strcpy(raw, tmp);
- g_snprintf(destp, len - strlen(dest), "%s%s%s",
- mimehdr_init, mimehdr_charset,
- mimehdr_enctype);
- destp += mimehdr_begin_len;
- line_len += mimehdr_begin_len;
-
- base64_encode(destp, raw, strlen(raw));
- line_len += strlen(destp);
- destp += strlen(destp);
-
- strcpy(destp, mimehdr_end);
- destp += strlen(mimehdr_end);
- line_len += strlen(mimehdr_end);
-
- g_free(raw);
- } else {
- strcpy(destp, tmp);
- line_len += strlen(destp);
- destp += strlen(destp);
- }
+ qp_q_encode(enc_str, out_str);
- g_free(tmp);
- /* g_print("line_len = %d\n\n", line_len); */
- } while (*wtmpp != (wchar_t)0);
+ g_free(out_str);
- while (iswspace(*wsrcp)) {
- gint mbl;
+ /* output MIME-encoded string block */
+ mime_block_len = mimestr_len + strlen(enc_str);
+ g_snprintf(destp, mime_block_len + 1,
+ MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
+ out_encoding, mimesep_enc, enc_str);
+ destp += mime_block_len;
+ srcp += cur_len;
- mbl = wctomb(destp, *wsrcp++);
- if (mbl != -1) {
- destp += mbl;
- line_len += mbl;
+ left -= mime_block_len;
}
- }
- *destp = '\0';
- g_free(wtmp);
- }
+ LBREAK_IF_REQUIRED(cont);
- g_free(wsrc);
+ if (cur_len == 0)
+ break;
+ }
+ }
- /* g_print("dest = %s\n", dest); */
+ *destp = '\0';
}
-#endif /* HAVE_LIBJCONV */
+
+#undef LBREAK_IF_REQUIRED
--- /dev/null
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <ctype.h>
+
+static gboolean get_hex_value(guchar *out, gchar c1, gchar c2);
+static void get_hex_str(gchar *out, guchar ch);
+
+gint qp_decode_line(gchar *str)
+{
+ gchar *inp = str, *outp = str;
+
+ while (*inp != '\0') {
+ if (*inp == '=') {
+ if (inp[1] && inp[2] &&
+ get_hex_value(outp, inp[1], inp[2]) == TRUE) {
+ inp += 3;
+ } else if (inp[1] == '\0' || isspace(inp[1])) {
+ /* soft line break */
+ break;
+ } else {
+ /* broken QP string */
+ *outp = *inp++;
+ }
+ } else {
+ *outp = *inp++;
+ }
+ outp++;
+ }
+
+ *outp = '\0';
+
+ return outp - str;
+}
+
+gint qp_decode_q_encoding(guchar *out, const gchar *in, gint inlen)
+{
+ const gchar *inp = in;
+ guchar *outp = out;
+
+ if (inlen < 0)
+ inlen = G_MAXINT;
+
+ while (inp - in < inlen && *inp != '\0') {
+ if (*inp == '=' && inp + 3 - in <= inlen) {
+ if (get_hex_value(outp, inp[1], inp[2]) == TRUE) {
+ inp += 3;
+ } else {
+ *outp = *inp++;
+ }
+ } else if (*inp == '_') {
+ *outp = ' ';
+ inp++;
+ } else {
+ *outp = *inp++;
+ }
+ outp++;
+ }
+
+ *outp = '\0';
+
+ return outp - out;
+}
+
+gint qp_get_q_encoding_len(const guchar *str)
+{
+ const guchar *inp = str;
+ gint len = 0;
+
+ while (*inp != '\0') {
+ if (*inp == 0x20)
+ len++;
+ else if (*inp == '=' || *inp == '?' || *inp == '_' ||
+ *inp < 32 || *inp > 127 || isspace(*inp))
+ len += 3;
+ else
+ len++;
+
+ inp++;
+ }
+
+ return len;
+}
+
+void qp_q_encode(gchar *out, const guchar *in)
+{
+ const guchar *inp = in;
+ gchar *outp = out;
+
+ while (*inp != '\0') {
+ if (*inp == 0x20)
+ *outp++ = '_';
+ else if (*inp == '=' || *inp == '?' || *inp == '_' ||
+ *inp < 32 || *inp > 127 || isspace(*inp)) {
+ *outp++ = '=';
+ get_hex_str(outp, *inp);
+ outp += 2;
+ } else
+ *outp++ = *inp;
+
+ inp++;
+ }
+
+ *outp = '\0';
+}
+
+#define HEX_TO_INT(val, hex) \
+{ \
+ gchar c = hex; \
+ \
+ if ('0' <= c && c <= '9') { \
+ val = c - '0'; \
+ } else if ('a' <= c && c <= 'f') { \
+ val = c - 'a' + 10; \
+ } else if ('A' <= c && c <= 'F') { \
+ val = c - 'A' + 10; \
+ } else { \
+ val = -1; \
+ } \
+}
+
+static gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
+{
+ gint hi, lo;
+
+ HEX_TO_INT(hi, c1);
+ HEX_TO_INT(lo, c2);
+
+ if (hi == -1 || lo == -1)
+ return FALSE;
+
+ *out = (hi << 4) + lo;
+ return TRUE;
+}
+
+#define INT_TO_HEX(hex, val) \
+{ \
+ if ((val) < 10) \
+ hex = '0' + (val); \
+ else \
+ hex = 'A' + (val) - 10; \
+}
+
+static void get_hex_str(gchar *out, guchar ch)
+{
+ gchar hex;
+
+ INT_TO_HEX(hex, ch >> 4);
+ *out++ = hex;
+ INT_TO_HEX(hex, ch & 0x0f);
+ *out++ = hex;
+}