Logo Search packages:      
Sourcecode: w3mmee version File versions  Download package

mimehead.c

/* 
 * MIME header support by Akinori ITO
 */

#include <sys/types.h>
#include "Str.h"
#ifdef HAVE_MOE
#include <mb.h>
extern Str conv_mem2mbStr(const char *s, size_t n, const char **p_cs, const char *op, ...);
extern Str conv_str2mbStr(const char *s, const char **p_cs, const char *op, ...);
extern Str conv_Str2mbStr(Str s, const char **p_cs, const char *op, ...);
#endif

#define LINELEN 4096

#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)
#ifndef HAVE_MOE
#define J_CHARSET "ISO-2022-JP"
#endif

#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)
{
    unsigned char c[4];
    char *wp = *ww;
    char d[3];
    int i, n_pad;
    Str ap = Strnew_size(strlen(wp));

    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) {
            *ww = wp;
            return ap;
          }
      }
      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++) {
          Strcat_char(ap, d[i]);
      }
      if (n_pad || *wp == '\0' || *wp == '?')
          break;
    }
    *ww = wp;
    return ap;
}

Str
decodeU(char **ww)
{
    unsigned char c1, c2;
    char *w = *ww;
    int n, i;
    Str a;

    if (*w <= 0x20 || *w >= 0x60)
      return Strnew_size(0);
    n = *w - 0x20;
    a = Strnew_size(n);
    for (w++, i = 2; *w != '\0' && n; n--) {
      c1 = (w[0] - 0x20) % 0x40;
      c2 = (w[1] - 0x20) % 0x40;
      Strcat_char(a, (c1 << i) | (c2 >> (6 - i)));
      if (i == 6) {
          w += 2;
          i = 2;
      }
      else {
          w++;
          i += 2;
      }
    }
    return a;
}

/* 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)
{
    char *w = *ww;
    Str a = Strnew_size(strlen(w));

    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;
            Strcat_char(a, ha2d(*w, *(w + 1)));
            w++;
          }
      }
      else
          Strcat_char(a, *w);
    }
    *ww = w;
    return a;
}

Str
decodeWord(char **ow)
{
    char *p, *w = *ow;
    char method;
    Str a = Strnew();
    Str charset;

    if (*w != '=' || *(w + 1) != '?')
      goto convert_fail;
    w += 2;
    for (charset = Strnew(); *w != '?'; w++) {
      if (*w == '\0')
          goto convert_fail;
      Strcat_char(charset, *w);
    }
#ifndef HAVE_MOE
    if (Strcasecmp_charp(charset, J_CHARSET) != 0) {
      /* NOT ISO-2022-JP encoding ... don't convert */
      goto convert_fail;
    }
#endif
    w++;
    method = *(w++);
    if (*w != '?')
      goto convert_fail;
    w++;
    p = w;
    switch (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 HAVE_MOE
    a = conv_Str2mbStr(a, NULL, "@|", charset->ptr, MB_FLAG_DISCARD_NOTPREFERED_CHAR);
#endif
    return a;

  convert_fail:
    return Strnew();
}

/* 
 * convert MIME encoded string to the original one
 */
Str
decodeMIME(char *orgstr)
{
    char *org = orgstr;
    char *org0, *p, *beg;
    Str cnv = Strnew_size(strlen(orgstr));

    for (beg = org ; *org ;) {
      if (*org == '=' && *(org + 1) == '?') {
          if (beg < org) {
#ifdef HAVE_MOE
            Strcat(cnv, conv_mem2mbStr(beg, org - beg, NULL, "|", MB_FLAG_DISCARD_NOTPREFERED_CHAR));
#else
            Strcat_charp_n(cnv, beg, org - beg);
#endif
          }
        nextEncodeWord:
          p = org;
          Strcat(cnv, decodeWord(&org));
          if (org == p) {     /* Convert failure */
#ifdef HAVE_MOE
            Strcat(cnv, conv_str2mbStr(org, NULL, "|", MB_FLAG_DISCARD_NOTPREFERED_CHAR));
#else
            Strcat_charp(cnv, org);
#endif
            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;
          }
          beg = org;
      }
      else
          ++org;
    }
    if (beg < org) {
#ifdef HAVE_MOE
      Strcat(cnv, conv_mem2mbStr(beg, org - beg, NULL, "|", MB_FLAG_DISCARD_NOTPREFERED_CHAR));
#else
      Strcat_charp_n(cnv, beg, org - beg);
#endif
    }
    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;
}

Generated by  Doxygen 1.6.0   Back to index