static char rcsid[] = "@(#)$Id: hdrencode.c,v 1.14 2006/05/22 19:17:14 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.14 $ $State: Exp $
*
* Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
*
* Partially based on mime_encode.c, which is initially
* written by: Michael Elkins <elkins@aero.org>, 1995
*****************************************************************************/
#include "headers.h"
#include "s_me.h"
DEBUG_VAR(Debug,__FILE__,"mime");
char hexchars[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F',
};
static char *us2s P_((unsigned char *str));
static char *us2s(str)
unsigned char *str;
{
return (char *)str;
}
/* Need also encode special characters is comments and phrases:
\ " ( ) < > and so on
*/
static char * hdr_tencode P_((char *buffer, const char *cs, int *Elen,
const char *lang));
static char * hdr_tencode(buffer,cs,Elen,lang)
char *buffer;
CONST char *cs;
int *Elen;
CONST char *lang;
{
char * ret = NULL;
int bad = 0;
char * p1, *work;
int clen,llen=0;
int l;
int i = 0;
if (!cs || NULL != strpbrk(cs," \t\r\n()\"?*'="))
cs = "UNKNOWN-8BIT";
clen = strlen(cs);
if (lang && NULL != strpbrk(lang," \t\r\n()\"?*'="))
lang = NULL;
if (lang)
llen = strlen(lang);
for (p1 = buffer; *p1; p1++) {
if ((*p1 < '0' || *p1 > '9') &&
(*p1 < 'a' || *p1 > 'z') &&
(*p1 < 'A' || *p1 > 'Z') &&
'-' != *p1 && ' ' != *p1 &&
'+' != *p1)
bad++;
}
l = (p1 - buffer) + 3 * bad;
work = safe_malloc(l+1);
for (p1 = buffer; *p1 && i < l; p1++) {
if (' ' == *p1) {
work[i++] = '_';
} else if ((*p1 < '0' || *p1 > '9') &&
(*p1 < 'a' || *p1 > 'z') &&
(*p1 < 'A' || *p1 > 'Z') &&
'-' != *p1 && '+' != *p1) {
unsigned char val = *p1;
work[i++] = '=';
work[i++] = hexchars[val / 16];
work[i++] = hexchars[val % 16];
} else
work[i++] = *p1;
/* We not test len (i+clen > 70) here --
caller should do splitting
*/
}
if (i > 0) {
work[i] = '\0';
ret = strmcat(ret,"=?");
ret = strmcat(ret,cs);
if (lang) {
ret = strmcat(ret,"*");
ret = strmcat(ret,lang);
}
ret = strmcat(ret,"?Q?");
ret = strmcat(ret,work);
ret = strmcat(ret,"?=");
*Elen = i + clen + 8;
if (lang)
*Elen += llen + 1;
DPRINT(Debug,32,(&Debug,
"hdr_tencode=%s (len=%d)\n",
ret,*Elen));
} else
*Elen = 0;
free(work);
return ret;
}
static char * hdr_encode P_((const struct string *buffer));
static char * hdr_encode(buffer)
CONST struct string *buffer;
{
char * cname = buffer->string_type->MIME_name ?
buffer->string_type->MIME_name :
"UNKNOWN-8BIT";
CONST char * lang = get_string_lang(buffer);
int overhead = strlen(cname) + 8 + (lang ? strlen(lang) + 1 : 0);
int splitlen = 75 - overhead;
int blen = string_len(buffer);
char * ret = NULL;
int X;
DPRINT(Debug,32,(&Debug,
" (hdr_encode cname=%s, lang=%s)",
cname,lang ? lang : "<none>"));
splitlen -= splitlen/8; /* Rough estimate */
if (splitlen < 1) /* Should not happen .... */
splitlen = 1;
for (X = 0; X < blen; ) {
struct string * split = NULL;
char *tmp,*tmp2;
int oldX = X;
int Elen;
/* We are splitting on struct string and not result stream
because we no not want split on middle of UTF-8 character
or ISO 2022 escape sequnces
*/
restart:
split = clip_from_string(buffer,&X,splitlen);
tmp = us2s(stream_from_string(split,0,NULL));
tmp2 = hdr_tencode(tmp,cname,&Elen,lang);
if (Elen > 75 && splitlen > 3) {
int rawlen = Elen - overhead; /* Estimate */
int newlen;
/* ratio: rawlen / splitlen
target: 75 - overhead
new splitlen = target / ratio
*/
if (overhead < 70 && rawlen > 1)
newlen = ( 75 - overhead ) * splitlen / rawlen;
else
newlen = splitlen -2;
DPRINT(Debug,32,(&Debug,
"hdr_encode restarting split_len=%d -> %d",
splitlen,newlen));
if (newlen >= splitlen)
newlen = splitlen -2;
splitlen = newlen;
if (splitlen < 1)
splitlen = 1;
DPRINT(Debug,32,(&Debug, " (really %d)\n",
splitlen));
X = oldX;
free(tmp);
free(tmp2);
free_string(&split);
goto restart;
}
if (ret)
ret = strmcat(ret," ");
ret = strmcat(ret,tmp2);
free(tmp);
free(tmp2);
free_string(&split);
}
if (!ret)
ret = safe_strdup("");
return ret;
}
static char * hdr_comment_quote P_((char *str));
static char * hdr_comment_quote(str)
char *str;
{
char * ret;
int bad = 0;
char * p1;
int l;
int i = 0;
for (p1 = str; *p1; p1++)
if ('\\' == *p1 ||
'(' == *p1 ||
')' == *p1)
bad++;
l = (p1 - str) + bad;
ret = safe_malloc(l+1);
for (p1 = str; *p1 && i < l; p1++) {
if ('\\' == *p1 ||
'(' == *p1 ||
')' == *p1)
ret[i++] = '\\';
ret[i++] = *p1;
}
ret[i] = '\0';
return ret;
}
static char * hdr_phrase P_((const struct string *buffer,
charset_t defcharset, int enmime,
int *is_encoded));
static char * hdr_phrase(buffer,defcharset,enmime, is_encoded)
CONST struct string *buffer;
charset_t defcharset;
int enmime;
int *is_encoded;
{
char * ret = NULL;
CONST char * lang = get_string_lang(buffer);
struct string *temp;
char * tmp;
int bad = 0;
int hi = 0;
char * p1;
CONST char * A = get_string_MIME_name(buffer);
DPRINT(Debug,32,(&Debug," (hdr_phrase cs=%s lang=%s) ",
A ? A : "<none>",
lang ? lang : "<none>"));
*is_encoded = 0;
if (!enmime)
temp = convert_string(defcharset,buffer,1);
else
temp = ascify_string(buffer);
tmp = us2s(stream_from_string(temp,0,NULL));
for (p1 = tmp; *p1; p1++) {
if ((*p1 < '0' || *p1 > '9') &&
(*p1 < 'a' || *p1 > 'z') &&
(*p1 < 'A' || *p1 > 'Z') &&
'-' != *p1 && ' ' != *p1)
bad++;
if (*p1 & 128)
hi++;
}
if (!bad && ( !enmime ||
!lang &&
((temp->string_type->MIME_name &&
0 == istrcmp(temp->string_type->MIME_name,"US-ASCII")) ||
charset_ok_p(temp->string_type))))
ret = strmcat(ret,tmp);
else if (!enmime ||
!lang &&
(!hi && ( (temp->string_type->MIME_name &&
0 == istrcmp(temp->string_type->MIME_name,
"US-ASCII")) ||
charset_ok_p(temp->string_type)))) {
int l = (p1-tmp) + bad + 2;
int i = 0;
char * work = safe_malloc(l+1);
work[i++] = '"';
for (p1 = tmp; *p1 && i < l-1; p1++) {
if ('\\' == *p1 || '"' == *p1)
work[i++] = '\\';
work[i++] = *p1;
}
work[i++] = '"';
work[i] = '\0';
ret = strmcat(ret,work);
free(work);
*is_encoded = -1;
} else {
/* We do not use hdr_tencode() and tmp here because
charset may be UTF-8 charset or some ISO 2022 variant
and we need do splitting for them also correctly
*/
char * work = hdr_encode(temp);
ret = strmcat(ret,work);
free(work);
*is_encoded = 1;
}
free(tmp);
free_string(&temp);
return ret;
}
static char * hdr_comment P_((const struct string *buffer,
charset_t defcharset, int enmime,
int *is_encoded));
static char * hdr_comment(buffer,defcharset,enmime, is_encoded)
CONST struct string *buffer;
charset_t defcharset;
int enmime;
int *is_encoded;
{
char * ret;
CONST char * lang = get_string_lang(buffer);
CONST char * A = get_string_MIME_name(buffer);
DPRINT(Debug,32,(&Debug," (hdr_comment cs=%s lang=%s) ",
A ? A : "<none>",
lang ? lang : "<none>"));
*is_encoded = 0;
if (!enmime) {
struct string *temp = convert_string(defcharset,buffer,1);
char *tmp = us2s(stream_from_string(temp,0,NULL));
ret = hdr_comment_quote(tmp);
free_string(&temp);
free(tmp);
} else {
char *A, *B;
struct string *temp = ascify_string(buffer);
ret = NULL;
if (!lang &&
temp->string_type->MIME_name &&
0 == istrcmp(temp->string_type->MIME_name,"US-ASCII")) {
char *tmp = us2s(stream_from_string(temp,0,NULL));
if (NULL != (A = strstr(tmp,"=?")) &&
NULL != (B = strstr(tmp,"?=")) &&
B > A) {
/* oops */
} else
ret = hdr_comment_quote(tmp);
free(tmp);
}
if (!ret) {
ret = hdr_encode(temp);
*is_encoded = 1;
}
free_string(&temp);
}
return ret;
}
static char * hdr_text P_((const struct string *buffer,
charset_t defcharset, int enmime,
int *is_encoded));
static char * hdr_text(buffer,defcharset,enmime,is_encoded)
CONST struct string *buffer;
charset_t defcharset;
int enmime;
int *is_encoded;
{
char * ret;
CONST char * lang = get_string_lang(buffer);
CONST char * A = get_string_MIME_name(buffer);
DPRINT(Debug,32,(&Debug," (hdr_text cs=%s lang=%s) ",
A ? A : "<none>",
lang ? lang : "<none>"));
*is_encoded = 0;
if (!enmime) {
struct string *temp = convert_string(defcharset,buffer,1);
ret = us2s(stream_from_string(temp,0,NULL));
free_string(&temp);
} else {
char *A, *B;
struct string *temp = ascify_string(buffer);
ret = NULL;
if (!lang &&
temp->string_type->MIME_name &&
0 == istrcmp(temp->string_type->MIME_name,"US-ASCII")) {
ret = us2s(stream_from_string(temp,0,NULL));
if (NULL != (A = strstr(ret,"=?")) &&
NULL != (B = strstr(ret,"?=")) &&
B > A) {
free(ret);
ret = NULL;
}
}
if (!ret) {
ret = hdr_encode(temp);
*is_encoded = 1;
}
free_string(&temp);
}
return ret;
}
/* *is_encoded = 1 ... mime encoded
*is_encoded = -1 quoted phrase
*/
/* class is one of HDR_PHRASE, HDR_COMMENT, HDR_TEXT */
char * string_to_hdr(class,buffer,defcharset,enmime,is_encoded)
int class;
CONST struct string *buffer;
charset_t defcharset;
int enmime;
int *is_encoded;
{
char * ret = NULL;
int IS_ENCODED = 0;
switch(class) {
case HDR_PHRASE:
ret = hdr_phrase(buffer,defcharset,enmime,&IS_ENCODED);
break;
case HDR_COMMENT:
ret = hdr_comment(buffer,defcharset,enmime,&IS_ENCODED);
break;
case HDR_TEXT:
ret = hdr_text(buffer,defcharset,enmime,&IS_ENCODED);
break;
}
DPRINT(Debug,30,(&Debug,
"string_to_hdr=%s (class=%d, buffer=%p, defcharset=%p '%s', enmime=%d) .. encoded=%d\n",
ret,
class,buffer,
defcharset,
defcharset->MIME_name ? defcharset->MIME_name : "<none>",
enmime,IS_ENCODED));
if (is_encoded)
*is_encoded = IS_ENCODED;
return ret;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1