home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Mail / mailapp-utilities-2.1-MIHS / Source / iso_convert.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-12-17  |  6.6 KB  |  265 lines

  1. /* -*-C-*-
  2. *******************************************************************************
  3. *
  4. * File:         iso_convert.c
  5. * RCS:          iso_convert.c,v 1.1 1997/12/17 16:45:03 tom Exp
  6. * Description:  some iso header conversions.
  7. * Author:       Tom Hageman <tom@basil.icce.rug.nl>
  8. * Created:      Tue Dec 16 18:15:26 1997 (extracted/expanded from appnmail)
  9. * Modified:     
  10. * Language:     C
  11. * Package:      mailapp-utilities
  12. * Status:       Exp.
  13. *
  14. * (C) Copyright 1997, but otherwise this file is perfect freeware.
  15. *
  16. *******************************************************************************
  17. */
  18.  
  19. #import <stdio.h>
  20. #import <string.h>
  21. #import <ctype.h>
  22. #import <regex.h>
  23. #import "optutil.h"
  24. #import "iso_convert.h"
  25.  
  26. #import "iso2next.h"
  27. /* Tables in there are expected to be in the "recode --header" format. */
  28. /* XXX maybe we'd better use "recode --header --strict"? */
  29.  
  30. size_t decode_quoted_printable(char *buf, const char *str, size_t len, int *err)
  31. {
  32.    register char *d = buf;
  33.    register const char *s = str, *e = s + len;
  34.    int errors = 0;
  35.  
  36.    while (s < e)
  37.    {
  38.       if (*s == '=')
  39.       {
  40.      if (s >= e - 2) ++errors;
  41.      else
  42.      {
  43.         //  parse "=XX" where XX is a 2-digit hex number
  44.         unsigned c = 0;
  45.  
  46.         sscanf(s+1, "%2x", &c);
  47.         if (c >= 256) ++errors;
  48.         else
  49.         {
  50.            s += 3;
  51.            *d++ = c;
  52.            continue;
  53.         }
  54.      }
  55.       }
  56.       else if (*s == '_')  /* special-case for Q-P headers [rfc2047 4.2(2)] */
  57.       {
  58.      s += 1;
  59.      *d++ = '\x20';
  60.      continue;
  61.       }
  62.       *d++ = *s++;
  63.    }
  64.    if (err) (*err) += errors;
  65.    return (d - buf);
  66. }
  67.  
  68. size_t decode_base64(char *buf, const char *str, size_t len, int *err)
  69. {
  70.    static char b64[256] = {0}, initialized = 0; 
  71.    register char *d = buf;
  72.    register const char *s = str, *e = s + len;
  73.    int errors = 0;
  74.    int i;
  75.    char b[4];
  76.  
  77.    if (!initialized)
  78.    {
  79.       static const unsigned char c64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  80.  
  81.       for (i = 0;  i < sizeof(b64);  i++) b64[i] = -1;
  82.       for (i = 0;  i < 64;  i++) b64[c64[i]] = i;
  83.       ++initialized;
  84.    }
  85.  
  86.    i = 0;
  87.    /* Ignore `=' padding at the end. */
  88.    while (s < e && e[-1] == '=') --e;
  89.  
  90.    while (s < e)
  91.    {
  92.       if ((signed)(b[i++] = b64[(unsigned char)*s++]) < 0) ++errors;
  93.  
  94.       if ((i &= 3) == 0)
  95.       {
  96.      *d++ = (b[0] << 2) | (b[1] >> 4);
  97.      *d++ = (b[1] << 4) | (b[2] >> 2);
  98.      *d++ = (b[2] << 6) | (b[3]);
  99.       }
  100.    }
  101.    switch (i)
  102.    {
  103.    case 0:
  104.       break;
  105.    case 1:
  106.       *d++ = (b[0] << 2);
  107.       break;
  108.    case 2:
  109.       *d++ = (b[0] << 2) | (b[1] >> 4);
  110.       *d++ = (b[1] << 4);
  111.    case 3:
  112.       *d++ = (b[0] << 2) | (b[1] >> 4);
  113.       *d++ = (b[1] << 4) | (b[2] >> 2);
  114.       *d++ = (b[2] << 6);
  115.       break;
  116.    }
  117.    if (err) (*err) += errors;
  118.    return (d - buf);
  119. }
  120.  
  121. static size_t convert_table(char *buf, const char *str, size_t len, int *err, const unsigned char *table)
  122. {
  123.    register char *d = buf;
  124.    register const char *s = str, *e = s + len;
  125.    int errors = 0;
  126.  
  127.    while (s < e)
  128.    {
  129.       if ((*d++ = table[(unsigned char)*s++]) == '\0')
  130.       {
  131.      ++errors;
  132.      d[-1] = '?';    // Avoid null characters in string.
  133.       }
  134.    }
  135.    if (err) (*err) += errors;
  136.    return (d - buf);
  137. }
  138.  
  139.  
  140. typedef size_t (*decode_function)(char *buf, const char *str, size_t len, int *err);
  141. typedef size_t (*convert_function)(char *buf, const char *str, size_t len, int *err, const unsigned char *table);
  142.  
  143. int iso_convert(char *line)
  144. {
  145.    static const struct
  146.    {
  147.       const char *name;
  148.       const unsigned char *contents;
  149.       convert_function convert;
  150.    }
  151.    c_table[] = {
  152.       /* The contents of these tables are defined in "iso2next.h".
  153.      If you add one there, don't forget to add it here too. */
  154.       { "iso-8859-1", latin1_to_next, convert_table },
  155.       { "iso-8859-2", latin2_to_next, convert_table },
  156.       /* {{this could be expanded with a dedicated iso-2022-jp to EUC filter,
  157.       if we were so inclined.  However, this can also be solved externally,
  158.           with the following rule near the top of your ~/.procmailrc:
  159.  
  160.         :0fh
  161.         * =\?iso-2022-jp\?
  162.         | nkf -e -m
  163.  
  164.       nkf -- Network Kanji code conversion Filter (v1.6 or better.)
  165.      }} */
  166.    };
  167.    static const struct
  168.    {
  169.       char tag;
  170.       decode_function decode;
  171.    }
  172.    d_table[] = {
  173.       { 'Q', decode_quoted_printable },
  174.       { 'B', decode_base64 },
  175.    };
  176.    static struct regex *isore = 0;
  177.    int errors = 0;
  178.    const char *name = "";
  179.    
  180.    if (!isore) isore = re_compile("=?\\([^?]*\\)?\\([A-Za-z]\\)?",0);
  181.  
  182.    while (re_match(line, isore) > 0)
  183.    {
  184.       const unsigned char *tt = NULL;
  185.       convert_function convert = NULL;
  186.       decode_function decode = NULL;
  187.       int i, namelen;
  188.       char coding = isore->braslist[1][0];
  189.       char *s, *t, *p;
  190.       size_t len;
  191.  
  192.       name = isore->braslist[0];
  193.       namelen = (isore->braelist[0] - isore->braslist[0]);
  194.       for (i = 0;  i < sizeof(c_table)/sizeof(c_table[0]);  i++)
  195.       {
  196.      if (strncasecmp(c_table[i].name, name, namelen) == 0 &&
  197.          c_table[i].name[namelen] == 0)
  198.      {
  199.         convert = c_table[i].convert;
  200.         name = c_table[i].name;
  201.         tt = c_table[i].contents;
  202.         break;
  203.      }
  204.       }
  205.       for (i = 0;  i < sizeof(d_table)/sizeof(d_table[0]);  i++)
  206.       {
  207.      if (d_table[i].tag == toupper(coding))
  208.      {
  209.         decode = d_table[i].decode;
  210.         break;
  211.      }
  212.       }
  213.  
  214.       s = (char *)isore->end;
  215.  
  216.       if ((t = strstr(s, "?=")) == NULL)
  217.       {
  218.      fprintf(stderr, "%s: warning: Missing terminating `?=' in MIME 8-bit header `%s'\n", progname(), line);
  219.      len = strlen(s);
  220.       }
  221.       else
  222.       {
  223.      len = t - s;
  224.       }
  225.  
  226.       /* Ignore whitespace between `encoded-words' [RFC2047 6.2] */
  227.       for (p = line;  p < isore->start && isspace(*p);  p++) ;
  228.  
  229.       if (p < isore->start)
  230.       {
  231.      /* Just to be sure, assume iso-latin1 encoding outside match. */
  232.      convert_table(line, line, (isore->start - line), &errors, latin1_to_next);
  233.      line = (char *)isore->start;
  234.       }
  235.  
  236.       if (tt == NULL)
  237.       {
  238.      fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%.*s' is unsupported\n", progname(), namelen, name);
  239.      len += s - line;
  240.       }
  241.       else if (decode == NULL)
  242.       {
  243.      fprintf(stderr, "%s: warning: MIME header transfer-encoding `%c' is unsupported\n", progname(), coding);
  244.      len += s - line;
  245.       }
  246.       else
  247.       {
  248.      len = (*decode)(line, s, len, &errors);
  249.      len = (*convert)(line, line, len, &errors, tt);
  250.      strcpy(line + len, t ? t  + 2 : "");
  251.       }
  252.       line = line + len;
  253.    }
  254.  
  255.    /* Just to be sure, assume iso-latin1 encoding outside match. */
  256.    convert_table(line, line, strlen(line), &errors, latin1_to_next);
  257.  
  258.    if (errors > 0)
  259.    {
  260.       fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%s' incomplete conversion\n", progname(), name);
  261.       return -1;
  262.    }
  263.    return 0;
  264. }
  265.