Mercurial > hg > Applications > mh
view sbr/ml_exthdr.c @ 17:76d91e545ea8 default tip
addrsbr and dtimep fix
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 24 Nov 2014 14:49:25 +0900 |
parents | 6bc439d68ff9 |
children |
line wrap: on
line source
/* ml_exthdr.c - supporting RFC-2047 (Non-ASCII Mail Headers) */ /* by takada@seraph.ntt.jp */ /* arranged by MH-plus project */ #ifdef MIME_HEADERS #include "../h/mh.h" #include <stdio.h> #include <ctype.h> #define MAXWIDTH 75 static char b64_to_alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static unsigned char alpha_to_b64[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,62, 0, 0, 0,63, 52,53,54,55, 56,57,58,59, 60,61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25, 0, 0, 0, 0, 0, 0,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static unsigned char alpha_to_qpr[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0,10,11,12, 13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,10,11,12, 13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static char *text_headers[] = { /* unstructured headers */ "Subject", "Comments", "Content-Description", NULL }; static int structured = 1; static int nameoutput = 1; static char *mm_charset = NULL; #ifdef JAPAN extern int japan_environ; #endif /* JAPAN */ static int ml_to_mmh(); static int mmh_to_ml(); static int bin_to_b64(); static int bin_to_qpr(); static int b64_to_bin(); static int qpr_to_bin(); /* * Encode multilingual string into RFC-2047 format */ char * exthdr_encode(input, output, width, component) char *input, *output, *component; int width; { char **p; structured = 1; /* almost headers are structured. */ if (uprf(component, "X-")) /* user defined headers are assumed to be unstructured. */ structured = 0; else { for (p = text_headers; *p; p++) if (uleq(component, *p)) { structured = 0; break; } } nameoutput = strlen(component); if (nameoutput < 1) nameoutput = 1; if (nameoutput > 36) nameoutput = 36; /* avoid endless loop */ (void) ml_to_mmh(input, output, width); return(output); } #define ASCII 0 #define JISX0208 1 #define DSGNT_ASCII() \ {\ if (kanji_pos != ASCII) {\ *bp++ = '\033'; *bp++ = '('; *bp++ = 'B';\ kanji_pos = ASCII;\ }\ } #define DSGNT_JISX0208() \ {\ if (kanji_pos != JISX0208) {\ *bp++ = '\033'; *bp++ = '$'; *bp++ = 'B';\ kanji_pos = JISX0208;\ }\ } #define isspecials(c) \ ((c) == '(' || (c) == ')' || (c) == '<' || (c) == '>' || (c) == '@' \ || (c) == ',' || (c) == ';' || (c) == ':' || (c) == '\\' || (c) == '"' \ || (c) == '.' || (c) == '[' || (c) == ']') #ifdef JAPAN #define PUT_ENCODED_WORD()\ {\ int x;\ *bp = '\0';\ if (japan_environ) {\ op = copy("=?ISO-2022-JP?B?", op); width += 16;\ x = bin_to_b64(buf, op); op += x; width += x;\ } else {\ op = copy("=?", op); width += 2;\ op = copy(mm_charset, op); width += strlen(mm_charset);\ op = copy("?Q?", op); width += 3;\ x = bin_to_qpr(buf, op); op += x; width += x;\ }\ op = copy("?=", op); width += 2;\ mmcontain = 1;\ bp = buf;\ } #else /* JAPAN */ #define PUT_ENCODED_WORD()\ {\ int x;\ *bp = '\0';\ op = copy("=?", op); width += 2;\ op = copy(mm_charset, op); width += strlen(mm_charset);\ op = copy("?Q?", op); width += 3;\ x = bin_to_qpr(buf, op); op += x; width += x;\ op = copy("?=", op); width += 2;\ mmcontain = 1;\ bp = buf;\ } #endif /* JAPAN */ #define qpr_len(c) \ ((c == ' ' || isalnum(c) \ || c == '!' || c == '*' || c == '+' || c == '-' || c == '/' \ || (!structured && !isspace(c) && !iscntrl(c) && !((c) & 0x80) \ && c != '=' && c != '?' && c != '_')) \ ? 1 : 3) static int encoded_length(buf, bp, kanji_pos, p) char *buf, *bp, *p; int kanji_pos; { int len; char *cp, c; #ifdef JAPAN if (japan_environ) { len = bp - buf; if (p) { if (ml_ismlptr(p)) { if (kanji_pos != JISX0208) len += 3+2+3; else len += 2+3; } else len++; } return 16 /* strlen("=?ISO-2022-JP?B?") */ + ((len+2)/3)*4 + 2; } #endif /* JAPAN */ len = 2 + strlen(mm_charset) + 3 + 2; cp = buf; while (cp < bp) { c = *cp++; len += qpr_len(c); } if (p) { c = *p; len += qpr_len(c); } return len; } static int parse_specials(encodep, ctextp, dpp, epp, ppp) int *encodep, *ctextp; char **dpp, **epp, **ppp; { char *pp = *ppp, *xp; unsigned char c, pair = '\0'; if (*pp == '"') { /* encoded-word never appear within a quoted-string */ while ((c = *++pp)) { if (c == '"') { pp++; break; } if (c == '\\' && *++pp) pp++; #ifdef JAPAN else if (japan_environ) { if (ml_ismlptr(pp)) { pp++; *encodep = 1; } } #endif /* JAPAN */ else if (*pp & 0x80) *encodep = 1; } *ppp = pp; return 0; } if (*pp == '[') { /* encoded-word never appear within a domain-literal */ while ((c = *++pp)) { if (c == ']') { pp++; break; } if (c == '\\' && *++pp) pp++; #ifdef JAPAN else if (japan_environ) { if (ml_ismlptr(pp)) { pp++; *encodep = 1; } } #endif /* JAPAN */ else if (*pp & 0x80) *encodep = 1; } *ppp = pp; return 0; } if (*encodep) *epp = *dpp = pp; if (((c = *pp) == '<' || c == '(') && *epp < pp) return 1; switch (c) { case '(': while (*pp == '(') { pp++; (*ctextp)++; } xp = pp; while ((c = *pp) && !isspace(c) && c != '(' && c != ')') { #ifdef JAPAN if (japan_environ ? ml_ismlptr(pp) : (*pp & 0x80)) { #else /* JAPAN */ if (*pp & 0x80) { #endif /* JAPAN */ pp = xp; break; } pp++; if (c == '\\' && *pp) pp++; } if (c != ')') { *ppp = pp; return 1; } /* else fall */ case ')': do { if (*ctextp > 0) (*ctextp)--; } while (*++pp == ')'); *ppp = pp; if (*ctextp || (*pp != ',' && *pp != '>' && *pp != ':' && *pp != ';')) return 1; *encodep = 0; pp--; /* tricky */ /* fall */ case '.': if (*encodep) { (*ppp)++; return 1; } /* else fall */ case '<': case '@': xp = pp; while ((c = *++pp)) { #ifdef JAPAN if (japan_environ ? ml_ismlptr(pp) : (*pp & 0x80)) { #else /* JAPAN */ if (*pp & 0x80) { #endif /* JAPAN */ pp = xp + 1; break; } if (c == '\\' && *++pp) pp++; else if (c == pair) pair = '\0'; else if (!pair) { if (c == '"') pair = '"'; else if (c == '[') pair = ']'; else if (c == '.' || c == '@' || (**epp == '<' && (c == ',' || c == ':'))) xp = pp; else if (isspecials(c)) { break; } } } if (*pp == '>') { /* fall */ case '>': case ':': case ']': pp++; } if (*pp == ';') { /* fall */ case ';': pp++; } if (*pp == ',') { /* fall */ case ',': pp++; } *ppp = pp; return 1; } /* NOT REACHABLE */ return 0; } static int ml_to_mmh(ibuf, obuf, width) char *ibuf, *obuf; int width; { int kanji_pos, encode, mmcontain, ctext, extra; char *ip = ibuf; char *op = obuf; char buf[BUFSIZ], *bp = buf; char *cp, *dp, *ep, *pp, *saveip; unsigned char c1, c2; #ifdef JAPAN if (japan_environ) mm_charset = "ISO-2022-JP"; else #endif /* JAPAN */ if (!(mm_charset = getenv("MM_CHARSET"))) mm_charset = m_find("MM-Charset"); if (!mm_charset || !*mm_charset) mm_charset = "UNKNOWN-8BIT"; kanji_pos = ASCII; mmcontain = ctext = 0; while (*ip) { pp = ip; while (isspace(*pp & 0xff)) pp++; cp = dp = pp; for (;;) { ep = pp; encode = 0; if (structured) { while ((c1 = *pp) && !isspace(c1)) { if (c1 == '\\') { if (*++pp) pp++; } else if (ctext ? (c1 == '(' || c1 == ')') : isspecials(c1)) { if (parse_specials(&encode, &ctext, &dp, &ep, &pp)) goto start_encode; } #ifdef JAPAN else if (japan_environ) { if (ml_ismlptr(pp)) { pp++; encode = 1; } } #endif /* JAPAN */ else if (c1 & 0x80) encode = 1; pp++; } } else { /* unstructured */ while ((c1 = *pp) && !isspace(c1)) { #ifdef JAPAN if (japan_environ) { if (ml_ismlptr(pp)) { pp++; encode = 1; } } else #endif /* JAPAN */ if (c1 & 0x80) encode = 1; pp++; } } if (!encode) break; dp = pp; while (isspace(*pp & 0xff)) pp++; } /* * ip -- cp : white spaces * cp -- dp : to encode (some blocks) * dp -- ep : white spaces * ep -- pp : not to encode (one block) */ start_encode: if (cp == dp) cp = dp = ip; /* make cp < dp whenever ip < cp */ /* lines containing encoded-word have MAXWIDTH limit (cf. RFC-2047) */ if (mmcontain && width + (cp - ip) > MAXWIDTH) { ip = cp; sprintf(op, "\n%*s", width = nameoutput, ""); op += nameoutput + 1; mmcontain = 0; } else { char *xp = op; while (ip < cp) { if ((*op++ = *ip++) == '\n') mmcontain = width = 0; else width++; } if (cp < dp) { if (xp == op && op > obuf && *(op-1) != '(') { *op++ = ' '; width++; } if (width + encoded_length(buf, bp, kanji_pos, cp) > MAXWIDTH) { sprintf(xp, "\n%*s", width = nameoutput, ""); op = xp + nameoutput + 1; mmcontain = 0; } } } saveip = ip; extra = 0; redo_encode: while (ip < dp) { if (*ip == '\n') { ip++; } else #ifdef JAPAN if (japan_environ && (*ip & 0x80)) { if (ml_ismlptr(ip)) { if (width + encoded_length(buf, bp, kanji_pos, ip) + extra > MAXWIDTH) { DSGNT_ASCII(); if (bp > buf) PUT_ENCODED_WORD(); sprintf(op, "\n%*s", width = nameoutput, ""); op += nameoutput + 1; saveip = ip; } DSGNT_JISX0208(); *bp++ = *ip++ & 0x7f; *bp++ = *ip++ & 0x7f; } else { ip++; /* skip */ } } else #endif /* JAPAN */ { DSGNT_ASCII(); if (width + encoded_length(buf, bp, kanji_pos, ip) + extra > MAXWIDTH) { if (bp > buf) PUT_ENCODED_WORD(); sprintf(op, "\n%*s", width = nameoutput, ""); op += nameoutput + 1; saveip = ip; } *bp++ = *ip++; } } DSGNT_ASCII(); if (bp > buf) { if (dp == ep && !extra && (*ep == ')' || *ep == ',' || *ep == ':' || *ep == '>' || *ep == ';') /* kinsoku :-) */ && width + encoded_length(buf, bp, kanji_pos, NULL) + (extra = (*ep != ')' ? 1 : 0) + (pp - ip)) > MAXWIDTH) { ip = saveip; bp = buf; kanji_pos = ASCII; goto redo_encode; /* redo with new "extra" */ } PUT_ENCODED_WORD(); } if (mmcontain && width + (ep - ip) > MAXWIDTH) { ip = ep; if (ep < pp) { sprintf(op, "\n%*s", width = nameoutput, ""); op += nameoutput + 1; mmcontain = 0; } else if (dp < ep) *op++ = *(ep - 1); /* end of string */ } else { char *xp = op; while (ip < ep) { if ((*op++ = *ip++) == '\n') mmcontain = width = 0; else width++; } if (ep < pp) { if (xp == op && cp < dp && *ep != ')') { *op++ = ' '; width++; } if ((mmcontain && width + (pp - ip) > MAXWIDTH) || ((*ep == '(' || *ep == '<') /* kinsoku :-) */ && *ep == *(pp-1) && *pp && !isspace(*pp & 0xff) && width + (pp - ip) + (*(pp-1) != '(' ? 1 : 0) + encoded_length(buf, bp, kanji_pos, pp) > MAXWIDTH)) { sprintf(xp, "\n%*s", width = nameoutput, ""); op = xp + nameoutput + 1; mmcontain = 0; } } } while (ip < pp) { if ((*op = *ip++) & 0x80) continue; /* skip */ op++; width++; } } *op = '\0'; return (op - obuf); } static int bin_to_b64(in, out) char *in, *out; { char *oldout = out; char c1, c2, c3; while (c1 = *in++) { *out++ = b64_to_alpha[c1 >> 2]; c2 = *in++; *out++ = b64_to_alpha[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; if (c2) { c3 = *in++; *out++ = b64_to_alpha[((c2 & 0x0f) << 2) | ((c3 & 0xc0) >> 6)]; if (c3) { *out++ = b64_to_alpha[c3 & 0x3f]; } else { *out++ = '='; break; } } else { *out++ = '='; *out++ = '='; break; } } *out = '\0'; return(out - oldout); } static int bin_to_qpr(in, out) char *in, *out; { char *oldout = out; unsigned char c1; while (c1 = *in++) { if (c1 == ' ') *out++ = '_'; else if (isalnum(c1) || c1 == '!' || c1 == '*' || c1 == '+' || c1 == '-' || c1 == '/') *out++ = c1; else if (!structured && !isspace(c1) && !iscntrl(c1) && !((c1) & 0x80) && c1 != '=' && c1 != '?' && c1 != '_') *out++ = c1; else { sprintf(out, "=%02X", c1); out += 3; } } *out = '\0'; return(out - oldout); } /* * Decode multilingual string from RFC-2047 format */ char * exthdr_decode(input, output) char *input, *output; { char *cp = input; char *op = output; char *first, *last; int in, out; last = cp; first = op; while (*cp) { char *tmp = cp; if (uprf(cp, "=?") && /* NOTE: The first "?=" of "=?ISO-2022-JP?Q?=1B$B ... ?=" is not the end of encoded-word. */ (tmp = index(tmp+2, '?')) && (tmp = index(tmp+1, '?')) && ((in = stringdex("?=", tmp + 1) + tmp + 1 - cp) >= 0) && ((out = mmh_to_ml(cp, cp+in+1, op)) >= 0)) { cp += in + 2; op += out; last = cp; if (!*cp) { break; } else if (isspace(*cp & 0xff) && *(cp+1)) while (*++cp && isspace(*cp & 0xff)) ; } else { #ifdef JAPAN if (first < op) { *op = '\0'; (void) ml_conv(first); op = first + strlen(first); } #endif /* JAPAN */ if (*last == '\n' && last+1 < cp) { *op++ = ' '; last = cp; } while (last < cp) *op++ = *last++; *op++ = *cp++; last = cp; first = op; } } #ifdef JAPAN if (first < op) { *op = '\0'; (void) ml_conv(first); op = first + strlen(first); } #endif /* JAPAN */ while (last < cp) *op++ = *last++; *op = '\0'; return(output); } static int mmh_to_ml(bp, ep, op) char *bp, *ep, *op; { char *ip, *cp, *tmp, buf[BUFSIZ], encode; int result; #ifdef JAPAN if (japan_environ) mm_charset = "iso-2022-jp"; else #endif /* JAPAN */ mm_charset = getenv("MM_CHARSET"); ip = bp; if ((ip > ep) || strncmp(ip, "=?", 2)) return(-1); ip += 2; if ((ip > ep) || ((cp = index(ip, '?')) == NULL)) return(-1); if (cp - ip >= BUFSIZ) return(-1); strncpy(buf, ip, cp-ip); buf[cp-ip] = 0; if ((tmp = index(buf, '*')) != NULL) { /* =?ISO-2022-JP*ja-JP?B?......?= is valid. (cf. RFC-2231) */ *tmp++ = '\0'; for (;;) { int i; if (!isalpha(*tmp)) return(-1); for (i = 1; i < 8 && isalpha(*(tmp+i)); i++) ; tmp += i; if (*tmp == '\0') break; if (*tmp++ != '-') return(-1); } } if (uleq(buf, "utf-8")) { int i; ml_conv(buf); for(i=0;i<BUFSIZ-1 && ip <= ep;i++) *op++ = *ip++; if (i==BUFSIZ-1) *op++=0; return i; } if (!uleq(buf, "us-ascii") && (!mm_charset || !uleq(buf, mm_charset))) return(-1); ip = cp + 1; if (ip > ep) return(-1); if ((*ip == 'B') || (*ip == 'b')) encode = 'B'; else if ((*ip == 'Q') || (*ip == 'q')) encode = 'Q'; else return(-1); ip++; if ((ip > ep) || (*ip != '?')) return(-1); ip++; if ((ip > ep) || ((cp = index(ip, '?')) == NULL)) return(-1); if (cp - ip >= BUFSIZ) return(-1); strncpy(buf, ip, cp-ip); buf[cp-ip] = 0; ip = cp + 1; if ((ip != ep) || (*ip != '=')) return(-1); if (encode == 'B') { (void) b64_to_bin(buf, op); } else { (void) qpr_to_bin(buf, op); } return(strlen(op)); } static int b64_to_bin(in, out) char *in, *out; { char *oldout = out; unsigned char c1, c2, c3, c4; while (c1 = *in++) { if ((c2 = *in++) && (c3 = *in++) && (c4 = *in++)) { if ((c1 == '=') || (c2 == '=')) { break; } else { c1 = alpha_to_b64[c1]; c2 = alpha_to_b64[c2]; } *out++ = (c1 << 2) | ((c2 & 0x30) >> 4); if (c3 == '=') { break; } else { c3 = alpha_to_b64[c3]; } *out++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); if (c4 == '=') { break; } else { c4 = alpha_to_b64[c4]; } *out++ = ((c3 & 0x03) << 6) | c4 ; } else { break; } } *out = '\0'; return(out - oldout); } static int qpr_to_bin(in, out) char *in, *out; { char *oldout = out; unsigned char c1, c2, c3; while (c1 = *in++) { if ((c1 == '=') && (c2 = *in++) && (c3 = *in++)) { c2 = alpha_to_qpr[c2]; c3 = alpha_to_qpr[c3]; *out++ = (c2 << 4) | c3 ; } else if (c1 == '_') { *out++ = ' '; } else { *out++ = c1; } } *out = '\0'; return(out - oldout); } #endif /* MIME_HEADERS */