542 lines
10 KiB
C
542 lines
10 KiB
C
/*
|
|
** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for
|
|
** distribution information.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "rfc822.h"
|
|
#include "rfc2047.h"
|
|
|
|
static const char rcsid[]="$Id$";
|
|
|
|
static const char xdigit[]="0123456789ABCDEF";
|
|
|
|
static char *rfc2047_search_quote(const char **ptr)
|
|
{
|
|
const char *p= *ptr;
|
|
char *s;
|
|
|
|
while (**ptr && **ptr != '?')
|
|
++ *ptr;
|
|
if ((s=malloc( *ptr - p + 1)) == 0)
|
|
return (0);
|
|
memcpy(s, p, *ptr-p);
|
|
s[*ptr - p]=0;
|
|
return (s);
|
|
}
|
|
|
|
static int nyb(int c)
|
|
{
|
|
const char *p;
|
|
|
|
c=toupper( (int)(unsigned char)c );
|
|
p=strchr(xdigit, c);
|
|
return (p ? p-xdigit:0);
|
|
}
|
|
|
|
static unsigned char decode64tab[256];
|
|
static int decode64tab_init=0;
|
|
|
|
static size_t decodebase64(char *ptr, size_t cnt)
|
|
{
|
|
size_t i, j;
|
|
char a,b,c;
|
|
size_t k;
|
|
|
|
if (!decode64tab_init)
|
|
{
|
|
for (i=0; i<256; i++) decode64tab[i]=0;
|
|
for (i=0; i<64; i++)
|
|
decode64tab[ (int)
|
|
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i])]=i;
|
|
decode64tab[ (int)'=' ] = 99;
|
|
}
|
|
|
|
i=cnt / 4;
|
|
i=i*4;
|
|
k=0;
|
|
for (j=0; j<i; j += 4)
|
|
{
|
|
int w=decode64tab[(int)(unsigned char)ptr[j]];
|
|
int x=decode64tab[(int)(unsigned char)ptr[j+1]];
|
|
int y=decode64tab[(int)(unsigned char)ptr[j+2]];
|
|
int z=decode64tab[(int)(unsigned char)ptr[j+3]];
|
|
|
|
a= (w << 2) | (x >> 4);
|
|
b= (x << 4) | (y >> 2);
|
|
c= (y << 6) | z;
|
|
ptr[k++]=a;
|
|
if ( ptr[j+2] != '=')
|
|
ptr[k++]=b;
|
|
if ( ptr[j+3] != '=')
|
|
ptr[k++]=c;
|
|
}
|
|
return (k);
|
|
}
|
|
|
|
/*
|
|
** This is the main rfc2047 decoding function. It receives rfc2047-encoded
|
|
** text, and a callback function. The callback function is repeatedly
|
|
** called, each time receiving a piece of decoded text. The decoded
|
|
** info includes a text fragment - string, string length arg - followed
|
|
** by the character set, followed by a context pointer that is received
|
|
** from the caller. If the callback function returns non-zero, rfc2047
|
|
** decoding terminates, returning the result code. Otherwise,
|
|
** rfc2047_decode returns 0 after a successfull decoding (-1 if malloc
|
|
** failed).
|
|
*/
|
|
|
|
int rfc2047_decode(const char *text, int (*func)(const char *, int,
|
|
const char *, void *),
|
|
void *arg)
|
|
{
|
|
int rc;
|
|
int had_last_word=0;
|
|
const char *p;
|
|
char *chset;
|
|
char *encoding;
|
|
char *enctext;
|
|
|
|
while (text && *text)
|
|
{
|
|
if (text[0] != '=' || text[1] != '?')
|
|
{
|
|
p=text;
|
|
while (*text)
|
|
{
|
|
if (text[0] == '=' && text[1] == '?')
|
|
break;
|
|
if (!isspace((int)(unsigned char)*text))
|
|
had_last_word=0;
|
|
++text;
|
|
}
|
|
if (text > p && !had_last_word)
|
|
{
|
|
rc=(*func)(p, text-p, 0, arg);
|
|
if (rc) return (rc);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
text += 2;
|
|
if ((chset=rfc2047_search_quote( &text )) == 0)
|
|
return (-1);
|
|
if (*text) ++text;
|
|
if ((encoding=rfc2047_search_quote( &text )) == 0)
|
|
{
|
|
free(chset);
|
|
return (-1);
|
|
}
|
|
if (*text) ++text;
|
|
if ((enctext=rfc2047_search_quote( &text )) == 0)
|
|
{
|
|
free(encoding);
|
|
free(chset);
|
|
return (-1);
|
|
}
|
|
if (*text == '?' && text[1] == '=')
|
|
text += 2;
|
|
if (strcmp(encoding, "Q") == 0 || strcmp(encoding, "q") == 0)
|
|
{
|
|
char *q, *r;
|
|
|
|
for (q=r=enctext; *q; )
|
|
{
|
|
int c;
|
|
|
|
if (*q == '=' && q[1] && q[2])
|
|
{
|
|
*r++ = (char)(
|
|
nyb(q[1])*16+nyb(q[2]));
|
|
q += 3;
|
|
continue;
|
|
}
|
|
|
|
c=*q++;
|
|
if (c == '_')
|
|
c=' ';
|
|
*r++ = c ;
|
|
}
|
|
*r=0;
|
|
}
|
|
else if (strcmp(encoding, "B") == 0 || strcmp(encoding, "b")==0)
|
|
{
|
|
enctext[decodebase64(enctext, strlen(enctext))]=0;
|
|
}
|
|
rc=(*func)(enctext, strlen(enctext), chset, arg);
|
|
free(enctext);
|
|
free(chset);
|
|
free(encoding);
|
|
if (rc) return (rc);
|
|
|
|
had_last_word=1; /* Ignore blanks between enc words */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
** rfc2047_decode_simple just strips out the rfc2047 decoding, throwing away
|
|
** the character set. This is done by calling rfc2047_decode twice, once
|
|
** to count the number of characters in the decoded text, the second time to
|
|
** actually do it.
|
|
*/
|
|
|
|
struct simple_info {
|
|
char *string;
|
|
int index;
|
|
const char *mychset;
|
|
} ;
|
|
|
|
static int count_simple(const char *txt, int len, const char *chset,
|
|
void *arg)
|
|
{
|
|
struct simple_info *iarg= (struct simple_info *)arg;
|
|
|
|
iarg->index += len;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int save_simple(const char *txt, int len, const char *chset,
|
|
void *arg)
|
|
{
|
|
struct simple_info *iarg= (struct simple_info *)arg;
|
|
|
|
memcpy(iarg->string+iarg->index, txt, len);
|
|
iarg->index += len;
|
|
return (0);
|
|
}
|
|
|
|
char *rfc2047_decode_simple(const char *text)
|
|
{
|
|
struct simple_info info;
|
|
|
|
info.index=1;
|
|
if (rfc2047_decode(text, &count_simple, &info))
|
|
return (0);
|
|
|
|
if ((info.string=malloc(info.index)) == 0) return (0);
|
|
info.index=0;
|
|
if (rfc2047_decode(text, &save_simple, &info))
|
|
{
|
|
free(info.string);
|
|
return (0);
|
|
}
|
|
info.string[info.index]=0;
|
|
return (info.string);
|
|
}
|
|
|
|
/*
|
|
** rfc2047_decode_enhanced is like simply, but prefixes the character set
|
|
** name before the text, in brackets.
|
|
*/
|
|
|
|
static int do_enhanced(const char *txt, int len, const char *chset,
|
|
void *arg,
|
|
int (*func)(const char *, int, const char *, void *)
|
|
)
|
|
{
|
|
int rc=0;
|
|
struct simple_info *info=(struct simple_info *)arg;
|
|
|
|
if (chset && info->mychset && strcmp(chset, info->mychset) == 0)
|
|
chset=0;
|
|
|
|
if (chset)
|
|
{
|
|
rc= (*func)(" [", 2, 0, arg);
|
|
if (rc == 0)
|
|
rc= (*func)(chset, strlen(chset), 0, arg);
|
|
if (rc == 0)
|
|
rc= (*func)("] ", 2, 0, arg);
|
|
}
|
|
|
|
if (rc == 0)
|
|
rc= (*func)(txt, len, 0, arg);
|
|
return (rc);
|
|
}
|
|
|
|
static int count_enhanced(const char *txt, int len, const char *chset,
|
|
void *arg)
|
|
{
|
|
return (do_enhanced(txt, len, chset, arg, &count_simple));
|
|
}
|
|
|
|
static int save_enhanced(const char *txt, int len, const char *chset,
|
|
void *arg)
|
|
{
|
|
return (do_enhanced(txt, len, chset, arg, &save_simple));
|
|
}
|
|
|
|
char *rfc2047_decode_enhanced(const char *text, const char *mychset)
|
|
{
|
|
struct simple_info info;
|
|
|
|
info.mychset=mychset;
|
|
info.index=1;
|
|
if (rfc2047_decode(text, &count_enhanced, &info))
|
|
return (0);
|
|
|
|
if ((info.string=malloc(info.index)) == 0) return (0);
|
|
info.index=0;
|
|
if (rfc2047_decode(text, &save_enhanced, &info))
|
|
{
|
|
free(info.string);
|
|
return (0);
|
|
}
|
|
info.string[info.index]=0;
|
|
return (info.string);
|
|
}
|
|
|
|
void rfc2047_print(const struct rfc822a *a,
|
|
const char *charset,
|
|
void (*print_func)(char, void *),
|
|
void (*print_separator)(const char *, void *), void *ptr)
|
|
{
|
|
rfc822_print_common(a, &rfc2047_decode_enhanced, charset,
|
|
print_func, print_separator, ptr);
|
|
}
|
|
|
|
static char *a_rfc2047_encode_str(const char *str, const char *charset);
|
|
|
|
static void rfc2047_encode_header_do(const struct rfc822a *a,
|
|
const char *charset,
|
|
void (*print_func)(char, void *),
|
|
void (*print_separator)(const char *, void *), void *ptr)
|
|
{
|
|
rfc822_print_common(a, &a_rfc2047_encode_str, charset,
|
|
print_func, print_separator, ptr);
|
|
}
|
|
|
|
/*
|
|
** When MIMEifying names from an RFC822 list of addresses, strip quotes
|
|
** before MIMEifying them, and add them afterwards.
|
|
*/
|
|
|
|
static char *a_rfc2047_encode_str(const char *str, const char *charset)
|
|
{
|
|
size_t l=strlen(str);
|
|
char *p, *s;
|
|
|
|
if (*str != '"' || str[l-1] != '"')
|
|
return (rfc2047_encode_str(str, charset));
|
|
|
|
p=malloc(l);
|
|
if (!p) return (0);
|
|
memcpy(p, str+1, l-2);
|
|
p[l-2]=0;
|
|
s=rfc2047_encode_str(p, charset);
|
|
free(p);
|
|
if (!s) return (0);
|
|
p=malloc(strlen(s)+3);
|
|
if (!p)
|
|
{
|
|
free(s);
|
|
return (0);
|
|
}
|
|
p[0]='"';
|
|
strcpy(p+1, s);
|
|
strcat(p, "\"");
|
|
free(s);
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void count(char c, void *p);
|
|
static void counts(const char *c, void *p);
|
|
static void save(char c, void *p);
|
|
static void saves(const char *c, void *p);
|
|
|
|
char *rfc2047_encode_header(const struct rfc822a *a,
|
|
const char *charset)
|
|
{
|
|
size_t l;
|
|
char *s, *p;
|
|
|
|
l=1;
|
|
rfc2047_encode_header_do(a, charset, &count, &counts, &l);
|
|
if ((s=malloc(l)) == 0) return (0);
|
|
p=s;
|
|
rfc2047_encode_header_do(a, charset, &save, &saves, &p);
|
|
*p=0;
|
|
return (s);
|
|
}
|
|
|
|
static void count(char c, void *p)
|
|
{
|
|
++*(size_t *)p;
|
|
}
|
|
|
|
static void counts(const char *c, void *p)
|
|
{
|
|
while (*c) count(*c++, p);
|
|
}
|
|
|
|
static void save(char c, void *p)
|
|
{
|
|
**(char **)p=c;
|
|
++*(char **)p;
|
|
}
|
|
|
|
static void saves(const char *c, void *p)
|
|
{
|
|
while (*c) save(*c++, p);
|
|
}
|
|
|
|
int rfc2047_encode_callback(const char *str, const char *charset,
|
|
int (*func)(const char *, size_t, void *), void *arg)
|
|
{
|
|
int rc;
|
|
|
|
while (*str)
|
|
{
|
|
size_t i, c;
|
|
|
|
for (i=0; str[i]; i++)
|
|
if ((str[i] & 0x80) || str[i] == '"')
|
|
break;
|
|
if (str[i] == 0)
|
|
return ( i ? (*func)(str, i, arg):0);
|
|
|
|
/* Find start of word */
|
|
|
|
while (i)
|
|
{
|
|
--i;
|
|
if (isspace((int)(unsigned char)str[i]))
|
|
{
|
|
++i;
|
|
break;
|
|
}
|
|
}
|
|
if (i)
|
|
{
|
|
rc= (*func)(str, i, arg);
|
|
if (rc) return (rc);
|
|
str += i;
|
|
}
|
|
|
|
/*
|
|
** Figure out when to stop MIME decoding. Consecutive
|
|
** MIME-encoded words are MIME-encoded together.
|
|
*/
|
|
|
|
i=0;
|
|
|
|
for (;;)
|
|
{
|
|
for ( ; str[i]; i++)
|
|
if (isspace((int)(unsigned char)str[i]))
|
|
break;
|
|
if (str[i] == 0)
|
|
break;
|
|
|
|
for (c=i; str[c] && isspace((int)(unsigned char)str[c]);
|
|
++c)
|
|
;
|
|
|
|
for (; str[c]; c++)
|
|
if (isspace((int)(unsigned char)str[c]) ||
|
|
(str[c] & 0x80) || str[c] == '"')
|
|
break;
|
|
|
|
if (str[c] == 0 || isspace((int)(unsigned char)str[c]))
|
|
break;
|
|
i=c;
|
|
}
|
|
|
|
/* Output mimeified text, insert spaces at 70+ character
|
|
** boundaries for line wrapping.
|
|
*/
|
|
|
|
c=0;
|
|
while (i)
|
|
{
|
|
if (c == 0)
|
|
{
|
|
if ( (rc=(*func)("=?", 2, arg)) != 0 ||
|
|
(rc=(*func)(charset, strlen(charset),
|
|
arg)) != 0 ||
|
|
(rc=(*func)("?Q?", 3, arg)) != 0)
|
|
return (rc);
|
|
c += strlen(charset)+5;
|
|
}
|
|
|
|
if ((*str & 0x80) || *str == '"')
|
|
{
|
|
char buf[3];
|
|
|
|
buf[0]='=';
|
|
buf[1]=xdigit[ ( *str >> 4) & 0x0F ];
|
|
buf[2]=xdigit[ *str & 0x0F ];
|
|
|
|
if ( (rc=(*func)(buf, 3, arg)) != 0)
|
|
return (rc);
|
|
c += 3;
|
|
++str;
|
|
--i;
|
|
}
|
|
else
|
|
{
|
|
size_t j;
|
|
|
|
for (j=0; j < i && !(str[j] & 0x80) &&
|
|
str[j] != '"'; j++)
|
|
if (j + c >= 70)
|
|
break;
|
|
if ( (rc=(*func)(str, j, arg)) != 0)
|
|
return (rc);
|
|
c += j;
|
|
str += j;
|
|
i -= j;
|
|
}
|
|
|
|
if (i == 0 || c >= 70)
|
|
{
|
|
if ( (rc=(*func)("?= ", i ? 3:2, arg)) != 0)
|
|
return (rc);
|
|
|
|
c=0;
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int count_char(const char *c, size_t l, void *p)
|
|
{
|
|
size_t *i=(size_t *)p;
|
|
|
|
*i += l;
|
|
return (0);
|
|
}
|
|
|
|
static int save_char(const char *c, size_t l, void *p)
|
|
{
|
|
char **s=(char **)p;
|
|
|
|
memcpy(*s, c, l);
|
|
*s += l;
|
|
return (0);
|
|
}
|
|
|
|
char *rfc2047_encode_str(const char *str, const char *charset)
|
|
{
|
|
size_t i=1;
|
|
char *s, *p;
|
|
|
|
(void)rfc2047_encode_callback(str, charset, &count_char, &i);
|
|
if ((s=malloc(i)) == 0) return (0);
|
|
p=s;
|
|
(void)rfc2047_encode_callback(str, charset, &save_char, &p);
|
|
*p=0;
|
|
return (s);
|
|
}
|