Patch from <http://www.j10n.org/files/w3m-cvs-1.1055-win64gc.patch>, [w3m-dev:04469] on 2013-10-14.
401 lines
6.9 KiB
C
401 lines
6.9 KiB
C
/* $Id: mimehead.c,v 1.10 2003/10/05 18:52:51 ukai Exp $ */
|
|
/*
|
|
* MIME header support by Akinori ITO
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include "fm.h"
|
|
#include "myctype.h"
|
|
#include "Str.h"
|
|
|
|
#define MIME_ENCODED_LINE_LIMIT 80
|
|
#define MIME_ENCODED_WORD_LENGTH_OFFSET 18
|
|
#define MIME_ENCODED_WORD_LENGTH_ESTIMATION(x) \
|
|
(((x)+2)*4/3+MIME_ENCODED_WORD_LENGTH_OFFSET)
|
|
#define MIME_DECODED_WORD_LENGTH_ESTIMATION(x) \
|
|
(((x)-MIME_ENCODED_WORD_LENGTH_OFFSET)/4*3)
|
|
#define J_CHARSET "ISO-2022-JP"
|
|
|
|
#define BAD_BASE64 255
|
|
|
|
static
|
|
unsigned char
|
|
c2e(char x)
|
|
{
|
|
if ('A' <= x && x <= 'Z')
|
|
return (x) - 'A';
|
|
if ('a' <= x && x <= 'z')
|
|
return (x) - 'a' + 26;
|
|
if ('0' <= x && x <= '9')
|
|
return (x) - '0' + 52;
|
|
if (x == '+')
|
|
return 62;
|
|
if (x == '/')
|
|
return 63;
|
|
return BAD_BASE64;
|
|
}
|
|
|
|
static
|
|
int
|
|
ha2d(char x, char y)
|
|
{
|
|
int r = 0;
|
|
|
|
if ('0' <= x && x <= '9')
|
|
r = x - '0';
|
|
else if ('A' <= x && x <= 'F')
|
|
r = x - 'A' + 10;
|
|
else if ('a' <= x && x <= 'f')
|
|
r = x - 'a' + 10;
|
|
|
|
r <<= 4;
|
|
|
|
if ('0' <= y && y <= '9')
|
|
r += y - '0';
|
|
else if ('A' <= y && y <= 'F')
|
|
r += y - 'A' + 10;
|
|
else if ('a' <= y && y <= 'f')
|
|
r += y - 'a' + 10;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
Str
|
|
decodeB(char **ww)
|
|
{
|
|
struct growbuf gb;
|
|
|
|
growbuf_init(&gb);
|
|
decodeB_to_growbuf(&gb, ww);
|
|
return growbuf_to_Str(&gb);
|
|
}
|
|
|
|
void
|
|
decodeB_to_growbuf(struct growbuf *gb, char **ww)
|
|
{
|
|
unsigned char c[4];
|
|
char *wp = *ww;
|
|
char d[3];
|
|
int i, n_pad;
|
|
|
|
growbuf_reserve(gb, strlen(wp) + 1);
|
|
n_pad = 0;
|
|
while (1) {
|
|
for (i = 0; i < 4; i++) {
|
|
c[i] = *(wp++);
|
|
if (*wp == '\0' || *wp == '?') {
|
|
i++;
|
|
for (; i < 4; i++) {
|
|
c[i] = '=';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (c[3] == '=') {
|
|
n_pad++;
|
|
c[3] = 'A';
|
|
if (c[2] == '=') {
|
|
n_pad++;
|
|
c[2] = 'A';
|
|
}
|
|
}
|
|
for (i = 0; i < 4; i++) {
|
|
c[i] = c2e(c[i]);
|
|
if (c[i] == BAD_BASE64) {
|
|
goto last;
|
|
}
|
|
}
|
|
d[0] = ((c[0] << 2) | (c[1] >> 4));
|
|
d[1] = ((c[1] << 4) | (c[2] >> 2));
|
|
d[2] = ((c[2] << 6) | c[3]);
|
|
for (i = 0; i < 3 - n_pad; i++) {
|
|
GROWBUF_ADD_CHAR(gb, d[i]);
|
|
}
|
|
if (n_pad || *wp == '\0' || *wp == '?')
|
|
break;
|
|
}
|
|
last:
|
|
growbuf_reserve(gb, gb->length + 1);
|
|
gb->ptr[gb->length] = '\0';
|
|
*ww = wp;
|
|
return;
|
|
}
|
|
|
|
Str
|
|
decodeU(char **ww)
|
|
{
|
|
struct growbuf gb;
|
|
|
|
growbuf_init(&gb);
|
|
decodeU_to_growbuf(&gb, ww);
|
|
return growbuf_to_Str(&gb);
|
|
}
|
|
|
|
void
|
|
decodeU_to_growbuf(struct growbuf *gb, char **ww)
|
|
{
|
|
unsigned char c1, c2;
|
|
char *w = *ww;
|
|
int n, i;
|
|
|
|
if (*w <= 0x20 || *w >= 0x60)
|
|
return;
|
|
n = *w - 0x20;
|
|
growbuf_reserve(gb, n + 1);
|
|
for (w++, i = 2; *w != '\0' && n; n--) {
|
|
c1 = (w[0] - 0x20) % 0x40;
|
|
c2 = (w[1] - 0x20) % 0x40;
|
|
gb->ptr[gb->length++] = (c1 << i) | (c2 >> (6 - i));
|
|
if (i == 6) {
|
|
w += 2;
|
|
i = 2;
|
|
}
|
|
else {
|
|
w++;
|
|
i += 2;
|
|
}
|
|
}
|
|
gb->ptr[gb->length] = '\0';
|
|
return;
|
|
}
|
|
|
|
/* RFC2047 (4.2. The "Q" encoding) */
|
|
Str
|
|
decodeQ(char **ww)
|
|
{
|
|
char *w = *ww;
|
|
Str a = Strnew_size(strlen(w));
|
|
|
|
for (; *w != '\0' && *w != '?'; w++) {
|
|
if (*w == '=') {
|
|
w++;
|
|
Strcat_char(a, ha2d(*w, *(w + 1)));
|
|
w++;
|
|
}
|
|
else if (*w == '_') {
|
|
Strcat_char(a, ' ');
|
|
}
|
|
else
|
|
Strcat_char(a, *w);
|
|
}
|
|
*ww = w;
|
|
return a;
|
|
}
|
|
|
|
/* RFC2045 (6.7. Quoted-Printable Content-Transfer-Encoding) */
|
|
Str
|
|
decodeQP(char **ww)
|
|
{
|
|
struct growbuf gb;
|
|
|
|
growbuf_init(&gb);
|
|
decodeQP_to_growbuf(&gb, ww);
|
|
return growbuf_to_Str(&gb);
|
|
}
|
|
|
|
void
|
|
decodeQP_to_growbuf(struct growbuf *gb, char **ww)
|
|
{
|
|
char *w = *ww;
|
|
|
|
growbuf_reserve(gb, strlen(w) + 1);
|
|
for (; *w != '\0'; w++) {
|
|
if (*w == '=') {
|
|
w++;
|
|
if (*w == '\n' || *w == '\r' || *w == ' ' || *w == '\t') {
|
|
while (*w != '\n' && *w != '\0')
|
|
w++;
|
|
if (*w == '\0')
|
|
break;
|
|
}
|
|
else {
|
|
if (*w == '\0' || *(w + 1) == '\0')
|
|
break;
|
|
gb->ptr[gb->length++] = ha2d(*w, *(w + 1));
|
|
w++;
|
|
}
|
|
}
|
|
else
|
|
gb->ptr[gb->length++] = *w;
|
|
}
|
|
gb->ptr[gb->length] = '\0';
|
|
*ww = w;
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_M17N
|
|
Str
|
|
decodeWord(char **ow, wc_ces * charset)
|
|
#else
|
|
Str
|
|
decodeWord0(char **ow)
|
|
#endif
|
|
{
|
|
#ifdef USE_M17N
|
|
wc_ces c;
|
|
#endif
|
|
char *p, *w = *ow;
|
|
char method;
|
|
Str a = Strnew();
|
|
Str tmp = Strnew();
|
|
|
|
if (*w != '=' || *(w + 1) != '?')
|
|
goto convert_fail;
|
|
w += 2;
|
|
for (; *w != '?'; w++) {
|
|
if (*w == '\0')
|
|
goto convert_fail;
|
|
Strcat_char(tmp, *w);
|
|
}
|
|
#ifdef USE_M17N
|
|
c = wc_guess_charset(tmp->ptr, 0);
|
|
if (!c)
|
|
goto convert_fail;
|
|
#else
|
|
if (strcasecmp(tmp->ptr, "ISO-8859-1") != 0 && strcasecmp(tmp->ptr, "US_ASCII") != 0)
|
|
/* NOT ISO-8859-1 encoding ... don't convert */
|
|
goto convert_fail;
|
|
#endif
|
|
w++;
|
|
method = *(w++);
|
|
if (*w != '?')
|
|
goto convert_fail;
|
|
w++;
|
|
p = w;
|
|
switch (TOUPPER(method)) {
|
|
case 'B':
|
|
a = decodeB(&w);
|
|
break;
|
|
case 'Q':
|
|
a = decodeQ(&w);
|
|
break;
|
|
default:
|
|
goto convert_fail;
|
|
}
|
|
if (p == w)
|
|
goto convert_fail;
|
|
if (*w == '?') {
|
|
w++;
|
|
if (*w == '=')
|
|
w++;
|
|
}
|
|
*ow = w;
|
|
#ifdef USE_M17N
|
|
*charset = c;
|
|
#endif
|
|
return a;
|
|
|
|
convert_fail:
|
|
return Strnew();
|
|
}
|
|
|
|
/*
|
|
* convert MIME encoded string to the original one
|
|
*/
|
|
#ifdef USE_M17N
|
|
Str
|
|
decodeMIME(Str orgstr, wc_ces * charset)
|
|
#else
|
|
Str
|
|
decodeMIME0(Str orgstr)
|
|
#endif
|
|
{
|
|
char *org = orgstr->ptr, *endp = org + orgstr->length;
|
|
char *org0, *p;
|
|
Str cnv = NULL;
|
|
|
|
#ifdef USE_M17N
|
|
*charset = 0;
|
|
#endif
|
|
while (org < endp) {
|
|
if (*org == '=' && *(org + 1) == '?') {
|
|
if (cnv == NULL) {
|
|
cnv = Strnew_size(orgstr->length);
|
|
Strcat_charp_n(cnv, orgstr->ptr, org - orgstr->ptr);
|
|
}
|
|
nextEncodeWord:
|
|
p = org;
|
|
Strcat(cnv, decodeWord(&org, charset));
|
|
if (org == p) { /* Convert failure */
|
|
Strcat_charp(cnv, org);
|
|
return cnv;
|
|
}
|
|
org0 = org;
|
|
SPCRLoop:
|
|
switch (*org0) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
org0++;
|
|
goto SPCRLoop;
|
|
case '=':
|
|
if (org0[1] == '?') {
|
|
org = org0;
|
|
goto nextEncodeWord;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (cnv != NULL)
|
|
Strcat_char(cnv, *org);
|
|
org++;
|
|
}
|
|
}
|
|
if (cnv == NULL)
|
|
return orgstr;
|
|
return cnv;
|
|
}
|
|
|
|
/* encoding */
|
|
|
|
static char Base64Table[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
|
|
Str
|
|
encodeB(char *a)
|
|
{
|
|
unsigned char d[3];
|
|
unsigned char c1, c2, c3, c4;
|
|
int i, n_pad;
|
|
Str w = Strnew();
|
|
|
|
while (1) {
|
|
if (*a == '\0')
|
|
break;
|
|
n_pad = 0;
|
|
d[1] = d[2] = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
d[i] = a[i];
|
|
if (a[i] == '\0') {
|
|
n_pad = 3 - i;
|
|
break;
|
|
}
|
|
}
|
|
c1 = d[0] >> 2;
|
|
c2 = (((d[0] << 4) | (d[1] >> 4)) & 0x3f);
|
|
if (n_pad == 2) {
|
|
c3 = c4 = 64;
|
|
}
|
|
else if (n_pad == 1) {
|
|
c3 = ((d[1] << 2) & 0x3f);
|
|
c4 = 64;
|
|
}
|
|
else {
|
|
c3 = (((d[1] << 2) | (d[2] >> 6)) & 0x3f);
|
|
c4 = (d[2] & 0x3f);
|
|
}
|
|
Strcat_char(w, Base64Table[c1]);
|
|
Strcat_char(w, Base64Table[c2]);
|
|
Strcat_char(w, Base64Table[c3]);
|
|
Strcat_char(w, Base64Table[c4]);
|
|
if (n_pad)
|
|
break;
|
|
a += 3;
|
|
}
|
|
return w;
|
|
}
|