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 (was hurtta+elm@ozone.FMI.FI) * * Partially based on mime_encode.c, which is initially * written by: Michael Elkins , 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 : "")); 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 : "", lang ? lang : "")); *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 : "", lang ? lang : "")); *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 : "", lang ? lang : "")); *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 : "", 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: */