aboutsummaryrefslogblamecommitdiffstats
path: root/camel/gmime-rfc2047.c
blob: 2a90c1869e92f2916e3a2d9e65266fde689f84c1 (plain) (tree)




























































































































































































































                                                                                         
/* 
 * gmime-rfc2047.c: implemention of RFC2047
 *
 * Copyright (C) 1999 Robert Brady <rwb197@ecs.soton.ac.uk>
 *
 * 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 <stdio.h>
#include <ctype.h>
#include <unicode.h>
#include <string.h>

#include "gmime-rfc2047.h"

#define NOT_RANKED -1

/* base64 code from tin. This should be changed to use the base64 code Miguel comitted */

const char base64_alphabet[64] =
{
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};

static unsigned char base64_rank[256];
static int base64_rank_table_built;
static void build_base64_rank_table (void);

static int hexval(gchar c) {
  if (isdigit(c)) return c-'0';
  c = tolower(c);
  return c - 'a' + 10;
}

static void decode_quoted(const gchar *text, gchar *to) {
  while (*text) {
    if (*text == '=') {
      gchar a = hexval(text[1]);
      gchar b = hexval(text[2]);
      int c = (a << 4) + b;
      *to = c;
      to++;
      text+=3;
    } else if (*text == '_') {
      *to = ' ';
      to++;
      text++;
    } else {
      *to = *text;
      to++;
      text++;
    }
  }
  *to = 0;
}

static void decode_base64(const gchar *what,  gchar *where) {
  unsigned short pattern = 0;
  int bits = 0;
  int delimiter = '=';
  gchar x;
  gchar *t = where;
  int Q = 0;
  while (*what != delimiter) {
    x = base64_rank[(unsigned char)(*what++)];
    if (x == NOT_RANKED)
      continue;
    pattern <<= 6;
    pattern |= x;
    bits += 6;
    if (bits >= 8) {
      x = (pattern >> (bits - 8)) & 0xff;
      *t++ = x;
      Q++;
      bits -= 8;
    }
  }
  *t = 0;
}

static void
build_base64_rank_table (
    void)
{
    int i;

    if (!base64_rank_table_built) {
        for (i = 0; i < 256; i++)
            base64_rank[i] = NOT_RANKED;
        for (i = 0; i < 64; i++)
            base64_rank[(int) base64_alphabet[i]] = i;
        base64_rank_table_built = 1;
    }
}

gchar *gmime_rfc2047_decode(const gchar *data, const gchar *into_what) {
  gchar buffer[4096] /* FIXME : constant sized buffer */, *b = buffer;

  build_base64_rank_table();

  while (*data) {

    /* If we encounter an error we just break out of the loop and copy the rest
     * of the text as-is */
     
    if (*data=='=') {
      data++;
      if (*data=='?') {
    gchar *charset, *encoding, *text, *end;
    gchar dc[4096];
    charset = data+1;
    encoding = strchr(charset, '?');

    if (!encoding) break;
    encoding++;
        text = strchr(encoding, '?');
    if (!text) break;
    text++;
    end = strstr(text, "?=");
    if (!end) break;
    end++;

    *(encoding-1)=0;
    *(text-1)=0;
    *(end-1)=0;

    if (strcasecmp(encoding, "q")==0) {
      decode_quoted(text, dc);
    } else if (strcasecmp(encoding, "b")==0) {
      decode_base64(text, dc);
    } else {
      /* What to do here? */
      break;
    }

    {
      int f;
      iconv_t i;
      const gchar *d2 = dc;
      int l = strlen(d2), l2 = 4000;

      i = unicode_iconv_open(into_what, charset);
      if (!i) 
         break;
       
      unicode_iconv(i, &d2, &l, &b, &l2);

      unicode_iconv_close(i);
      data = end;
    }
      }
    }
    else {
      *b = *data;
      b++;
    }

    data++;

  }

  while (*data) {
    *b = *data;
    b++;
    data++;
  }

  *b = 0;

  return g_strdup(buffer);
}

gchar *rfc2047_encode(const gchar *string, const gchar *charset) {
  gchar buffer[4096] /* FIXME : constant sized buffer */;
  gchar *b = buffer;
  const gchar *s = string;
  int not_ascii = 0, not_latin1 = 0;
  while (*s) {
    if (*s <= 20 || *s >= 0x7f || *s == '=') { not_ascii = 1; }
    s++;
  }
  
  if (!not_ascii) {
    b += sprintf(b, "%s", string);
  }
  
  else {
    b += sprintf(b, "=?%s?Q?", charset);
    s = string;
    while (*s) {
      if (*s == ' ') b += sprintf(b, "_");
      else if (*s < 0x20 || *s >= 0x7f || *s == '=' || *s == '?' || *s == '_') {
    b += sprintf(b, "=%2x", *s);
      } else {
    b += sprintf(b, "%c", *s);
      }
      s++;
    }
    b += sprintf(b, "?=");
  }

  *b = 0;

  return g_strdup(buffer);
}