mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
1472 lines
38 KiB
C
1472 lines
38 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/* mime2fun.c */
|
|
/* Function related to MIME-2 support */
|
|
|
|
#include "intlpriv.h"
|
|
#include "prefapi.h"
|
|
|
|
/*
|
|
"The character set names may be up to 40 characters taken from the
|
|
printable characters of US-ASCII. However, no distinction is made
|
|
between use of upper and lower case letters." [RFC 1700]
|
|
*/
|
|
typedef struct {
|
|
int16 csid_key;
|
|
int16 csid_target;
|
|
} cs_csid_map_t;
|
|
|
|
|
|
/* Maps a window encoding to the MIME encoding used for posting
|
|
* news and internet email. String is for the MIME charset string.
|
|
|
|
Currently, this table is only used for HEADER ENCODING!!!!
|
|
It is not used for HEADER decoding!!!!
|
|
It is not used for Body Decoding or Encoding!!!!
|
|
NOTE: We need to change this from win_csid base to doc_csid base
|
|
|
|
*/
|
|
|
|
PRIVATE cs_csid_map_t cs_mime_csidmap_tbl[] = {
|
|
{CS_ASCII, CS_ASCII },
|
|
|
|
{CS_LATIN1, CS_LATIN1 },
|
|
|
|
{CS_JIS, CS_JIS },
|
|
{CS_SJIS, CS_JIS },
|
|
{CS_EUCJP, CS_JIS, },
|
|
|
|
{CS_KSC_8BIT, CS_KSC_8BIT },
|
|
|
|
{CS_GB_8BIT, CS_GB_8BIT },
|
|
|
|
{CS_BIG5, CS_BIG5 },
|
|
{CS_CNS_8BIT, CS_BIG5 },
|
|
|
|
{CS_MAC_ROMAN, CS_LATIN1 },
|
|
|
|
{CS_LATIN2, CS_LATIN2 },
|
|
{CS_MAC_CE, CS_LATIN2 },
|
|
{CS_CP_1250, CS_LATIN2 },
|
|
|
|
{CS_8859_5, CS_KOI8_R },
|
|
{CS_KOI8_R, CS_KOI8_R },
|
|
{CS_MAC_CYRILLIC, CS_KOI8_R },
|
|
{CS_CP_1251, CS_KOI8_R },
|
|
|
|
{CS_8859_7, CS_8859_7 },
|
|
{CS_CP_1253, CS_8859_7 },
|
|
{CS_MAC_GREEK, CS_8859_7 },
|
|
|
|
{CS_8859_9, CS_8859_9 },
|
|
{CS_MAC_TURKISH,CS_8859_9 },
|
|
|
|
{CS_UTF8, CS_UTF8 },
|
|
{CS_UTF7, CS_UTF7 },
|
|
{CS_UCS2, CS_UTF7 },
|
|
{CS_UCS2_SWAP, CS_UTF7 },
|
|
{0, 0, }
|
|
};
|
|
|
|
#define MAXLINELEN 72
|
|
#define IS_MAIL_SEPARATOR(p) ((*(p) == ',' || *(p) == ' ' || *(p) == '\"' || *(p) == ':' || \
|
|
*(p) == '(' || *(p) == ')' || *(p) == '\\' || (unsigned char)*p < 0x20))
|
|
|
|
/*
|
|
Prototype for Private Function
|
|
*/
|
|
PRIVATE void intlmime_init_csidmap();
|
|
PRIVATE XP_Bool intlmime_only_ascii_str(const char *s);
|
|
PRIVATE char * intlmime_encode_mail_address(int wincsid, const char *src, CCCDataObject obj,
|
|
int maxLineLen);
|
|
PRIVATE char * intlmime_encode_next8bitword(int wincsid, char *src);
|
|
PRIVATE int16 intlmime_get_outgoing_mime_csid(int16);
|
|
PRIVATE int16 intlmime_map_csid(cs_csid_map_t *csmimep, int16 csid_key);
|
|
|
|
/* we should consider replace this base64 decodeing and encoding function with a better one */
|
|
PRIVATE int intlmime_decode_base64 (const char *in, char *out);
|
|
PRIVATE char * intlmime_decode_qp(char *in);
|
|
PRIVATE int intlmime_encode_base64 (const char *in, char *out);
|
|
PRIVATE char * intlmime_decode_base64_buf(char *subject);
|
|
PRIVATE char * intlmime_encode_base64_buf(char *subject, size_t size);
|
|
PRIVATE char * intlmime_encode_qp_buf(char *subject);
|
|
|
|
|
|
PRIVATE XP_Bool intlmime_is_hz(const char *header);
|
|
PRIVATE XP_Bool intlmime_is_mime_part2_header(const char *header);
|
|
PRIVATE XP_Bool intlmime_is_iso_2022_xxx(const char *, int16 );
|
|
PRIVATE char * intl_decode_mime_part2_str(const char *, int , XP_Bool );
|
|
PRIVATE char * intl_DecodeMimePartIIStr(const char *, int16 , XP_Bool );
|
|
PRIVATE char * intl_EncodeMimePartIIStr(char *subject, int16 wincsid, XP_Bool bUseMime, int maxLineLen);
|
|
|
|
|
|
#if 0
|
|
/* Prototype of callback routine invoked by prefapi when the pref value changes
|
|
* Win16 build fails if this is declared as static.
|
|
*/
|
|
MODULE_PRIVATE
|
|
int PR_CALLBACK intlmime_get_mail_strictly_mime(const char * newpref, void * data);
|
|
#endif
|
|
|
|
/* We probably should change these into private instead of PUBLIC */
|
|
PUBLIC char *DecodeBase64Buffer(char *subject);
|
|
PUBLIC char *EncodeBase64Buffer(char *subject, size_t size);
|
|
|
|
|
|
/* 4.0: Made Encode & Decode public for use by libpref; added size param.
|
|
*/
|
|
PUBLIC char *EncodeBase64Buffer(char *subject, size_t size)
|
|
{
|
|
/* This function should be obsolete */
|
|
/* We should not make this public in libi18n */
|
|
/* We should use the new Base64 Encoder wrote by jwz in libmime */
|
|
return intlmime_encode_base64_buf(subject, size);
|
|
}
|
|
|
|
PUBLIC char *DecodeBase64Buffer(char *subject)
|
|
{
|
|
/* This function should be obsolete */
|
|
/* We should not make this public in libi18n */
|
|
/* We should use the new Base64 Decoder wrote by jwz in libmime */
|
|
return intlmime_decode_base64_buf(subject);
|
|
}
|
|
|
|
#if defined(MOZ_MAIL_COMPOSE) || defined(MOZ_MAIL_NEWS)
|
|
|
|
/*
|
|
Implementation
|
|
*/
|
|
/*
|
|
INTL_DefaultMailCharSetID,
|
|
*/
|
|
PUBLIC int16 INTL_DefaultMailCharSetID(int16 csid)
|
|
{
|
|
int16 retcsid;
|
|
csid &= ~CS_AUTO;
|
|
|
|
intlmime_init_csidmap();
|
|
retcsid = intlmime_map_csid(cs_mime_csidmap_tbl, csid);
|
|
|
|
return retcsid;
|
|
}
|
|
|
|
PUBLIC int16 INTL_DefaultNewsCharSetID(int16 csid)
|
|
{
|
|
|
|
if (csid == 0)
|
|
csid = INTL_DefaultDocCharSetID(0);
|
|
csid &= ~CS_AUTO;
|
|
if (csid == CS_KSC_8BIT)
|
|
return CS_KSC_8BIT;
|
|
intlmime_init_csidmap();
|
|
return intlmime_map_csid(cs_mime_csidmap_tbl, csid);
|
|
}
|
|
|
|
PUBLIC
|
|
char *INTL_DecodeMimePartIIStr(const char *header, int16 wincsid, XP_Bool dontConvert)
|
|
{
|
|
return intl_DecodeMimePartIIStr(header, wincsid, dontConvert);
|
|
}
|
|
PUBLIC
|
|
char *INTL_EncodeMimePartIIStr(char *subject, int16 wincsid, XP_Bool bUseMime)
|
|
{
|
|
return intl_EncodeMimePartIIStr(subject, wincsid, bUseMime, MAXLINELEN);
|
|
}
|
|
#endif /* MOZ_MAIL_COMPOSE || MOZ_MAIL_NEWS */
|
|
|
|
#ifdef MOZ_MAIL_NEWS
|
|
/* This is a routine used to re-encode subject lines for use in the summary file.
|
|
The reason why we specify a different length here is because we are not encoding
|
|
the string for use in a mail message, but rather want to stuff as much content
|
|
into the subject string as possible. */
|
|
PUBLIC
|
|
char *INTL_EncodeMimePartIIStr_VarLen(char *subject, int16 wincsid, XP_Bool bUseMime, int encodedWordSize)
|
|
{
|
|
return intl_EncodeMimePartIIStr(subject, wincsid, bUseMime, encodedWordSize);
|
|
}
|
|
#endif /* MOZ_MAIL_NEWS */
|
|
|
|
|
|
#if defined(MOZ_MAIL_COMPOSE) || defined(MOZ_MAIL_NEWS)
|
|
/* some utility function used by this file */
|
|
PRIVATE XP_Bool intlmime_only_ascii_str(const char *s)
|
|
{
|
|
for(; *s; s++)
|
|
if(*s & 0x80)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
#endif /* MOZ_MAIL_COMPOSE || MOZ_MAIL_NEWS */
|
|
|
|
#ifdef MOZ_MAIL_NEWS
|
|
PRIVATE void intlmime_update_csidmap(int16 csid_key, int16 csid_target)
|
|
{
|
|
cs_csid_map_t * mapp;
|
|
|
|
for(mapp = cs_mime_csidmap_tbl ;
|
|
mapp->csid_key != 0 ;
|
|
mapp++)
|
|
{
|
|
if (mapp->csid_key == csid_key)
|
|
mapp->csid_target = csid_target;
|
|
}
|
|
}
|
|
#endif /* MOZ_MAIL_NEWS */
|
|
|
|
#if defined(MOZ_MAIL_COMPOSE) || defined(MOZ_MAIL_NEWS)
|
|
PRIVATE void
|
|
intlmime_init_csidmap()
|
|
{
|
|
static XP_Bool initialized = FALSE;
|
|
|
|
if(initialized)
|
|
return;
|
|
|
|
/* Speical Hack for Cyrllic */
|
|
/* We need to know wheater we should send KOI8-R or ISO-8859-5 */
|
|
|
|
{
|
|
static const char* pref_mailcharset_cyrillic = "intl.mailcharset.cyrillic";
|
|
static const char* pref_mailcharset_korean = "intl.mailcharset.korean";
|
|
char *mailcharset_pref = NULL;
|
|
int16 mailcsid_pref = CS_UNKNOWN;
|
|
|
|
if( PREF_NOERROR == PREF_CopyCharPref(pref_mailcharset_cyrillic,
|
|
&mailcharset_pref))
|
|
{
|
|
mailcsid_pref = INTL_CharSetNameToID(mailcharset_pref);
|
|
XP_FREE(mailcharset_pref);
|
|
|
|
if(CS_UNKNOWN != mailcsid_pref)
|
|
{
|
|
intlmime_update_csidmap(CS_KOI8_R, mailcsid_pref);
|
|
intlmime_update_csidmap(CS_8859_5, mailcsid_pref);
|
|
intlmime_update_csidmap(CS_MAC_CYRILLIC, mailcsid_pref);
|
|
intlmime_update_csidmap(CS_CP_1251, mailcsid_pref);
|
|
}
|
|
}
|
|
if( PREF_NOERROR == PREF_CopyCharPref(pref_mailcharset_korean,
|
|
&mailcharset_pref))
|
|
{
|
|
mailcsid_pref = INTL_CharSetNameToID(mailcharset_pref);
|
|
XP_FREE(mailcharset_pref);
|
|
|
|
if(CS_UNKNOWN != mailcsid_pref)
|
|
{
|
|
intlmime_update_csidmap(CS_KSC_8BIT, mailcsid_pref);
|
|
}
|
|
}
|
|
}
|
|
|
|
initialized = TRUE;
|
|
}
|
|
|
|
PRIVATE int16
|
|
intlmime_map_csid(cs_csid_map_t *mapp, int16 csid_key)
|
|
{
|
|
for( ; mapp->csid_key != 0 ; mapp++)
|
|
{
|
|
if (csid_key == mapp->csid_key)
|
|
return(mapp->csid_target);
|
|
}
|
|
return(csid_key); /* causes no conversion */
|
|
}
|
|
|
|
PRIVATE int16
|
|
intlmime_get_outgoing_mime_csid(int16 win_csid)
|
|
{
|
|
win_csid &= ~CS_AUTO;
|
|
intlmime_init_csidmap();
|
|
return intlmime_map_csid(cs_mime_csidmap_tbl, win_csid);
|
|
}
|
|
|
|
PRIVATE char *intlmime_decode_qp(char *in)
|
|
{
|
|
int i = 0, length;
|
|
char token[3];
|
|
char *out, *dest = 0;
|
|
|
|
out = dest = (char *)XP_ALLOC(strlen(in)+1);
|
|
if (dest == NULL)
|
|
return NULL;
|
|
memset(out, 0, strlen(in)+1);
|
|
length = strlen(in);
|
|
while (length > 0 || i != 0)
|
|
{
|
|
while (i < 3 && length > 0)
|
|
{
|
|
token [i++] = *in;
|
|
in++;
|
|
length--;
|
|
}
|
|
|
|
if (i < 3)
|
|
{
|
|
/* Didn't get enough for a complete token.
|
|
If it might be a token, unread it.
|
|
Otherwise, just dump it.
|
|
*/
|
|
strncpy (out, token, i);
|
|
break;
|
|
}
|
|
i = 0;
|
|
|
|
if (token [0] == '=')
|
|
{
|
|
unsigned char c = 0;
|
|
if (token[1] >= '0' && token[1] <= '9')
|
|
c = token[1] - '0';
|
|
else if (token[1] >= 'A' && token[1] <= 'F')
|
|
c = token[1] - ('A' - 10);
|
|
else if (token[1] >= 'a' && token[1] <= 'f')
|
|
c = token[1] - ('a' - 10);
|
|
else if (token[1] == CR || token[1] == LF)
|
|
{
|
|
/* =\n means ignore the newline. */
|
|
if (token[1] == CR && token[2] == LF)
|
|
; /* swallow all three chars */
|
|
else
|
|
{
|
|
in--; /* put the third char back */
|
|
length++;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* = followed by something other than hex or newline -
|
|
pass it through unaltered, I guess. (But, if
|
|
this bogus token happened to occur over a buffer
|
|
boundary, we can't do this, since we don't have
|
|
space for it. Oh well. Forget it.) */
|
|
if (in > out) *out++ = token[0];
|
|
if (in > out) *out++ = token[1];
|
|
if (in > out) *out++ = token[2];
|
|
continue;
|
|
}
|
|
|
|
/* Second hex digit */
|
|
c = (c << 4);
|
|
if (token[2] >= '0' && token[2] <= '9')
|
|
c += token[2] - '0';
|
|
else if (token[2] >= 'A' && token[2] <= 'F')
|
|
c += token[2] - ('A' - 10);
|
|
else if (token[2] >= 'a' && token[2] <= 'f')
|
|
c += token[2] - ('a' - 10);
|
|
else
|
|
{
|
|
/* We got =xy where "x" was hex and "y" was not, so
|
|
treat that as a literal "=", x, and y. (But, if
|
|
this bogus token happened to occur over a buffer
|
|
boundary, we can't do this, since we don't have
|
|
space for it. Oh well. Forget it.) */
|
|
if (in > out) *out++ = token[0];
|
|
if (in > out) *out++ = token[1];
|
|
if (in > out) *out++ = token[2];
|
|
continue;
|
|
}
|
|
|
|
*out++ = (char) c;
|
|
}
|
|
else
|
|
{
|
|
*out++ = token [0];
|
|
|
|
token[0] = token[1];
|
|
token[1] = token[2];
|
|
i = 2;
|
|
}
|
|
}
|
|
/* take care of special underscore case */
|
|
for (out = dest; *out; out++)
|
|
if (*out == '_') *out = ' ';
|
|
return dest;
|
|
}
|
|
|
|
#endif /* MOZ_MAIL_COMPOSE || MOZ_MAIL_NEWS */
|
|
|
|
PRIVATE int intlmime_decode_base64 (const char *in, char *out)
|
|
{
|
|
/* reads 4, writes 3. */
|
|
int j;
|
|
unsigned long num = 0;
|
|
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
unsigned char c;
|
|
if (in[j] >= 'A' && in[j] <= 'Z') c = in[j] - 'A';
|
|
else if (in[j] >= 'a' && in[j] <= 'z') c = in[j] - ('a' - 26);
|
|
else if (in[j] >= '0' && in[j] <= '9') c = in[j] - ('0' - 52);
|
|
else if (in[j] == '+') c = 62;
|
|
else if (in[j] == '/') c = 63;
|
|
else if (in[j] == '=') c = 0;
|
|
else
|
|
{
|
|
/* abort (); */
|
|
strcpy(out, in); /* I hate abort */
|
|
return 0;
|
|
}
|
|
num = (num << 6) | c;
|
|
}
|
|
|
|
*out++ = (unsigned char) (num >> 16);
|
|
*out++ = (unsigned char) ((num >> 8) & 0xFF);
|
|
*out++ = (unsigned char) (num & 0xFF);
|
|
return 1;
|
|
}
|
|
|
|
PRIVATE char *intlmime_decode_base64_buf(char *subject)
|
|
{
|
|
char *output = 0;
|
|
char *pSrc, *pDest ;
|
|
int i ;
|
|
|
|
StrAllocCopy(output, subject); /* Assume converted text are always less than source text */
|
|
|
|
pSrc = subject;
|
|
pDest = output ;
|
|
for (i = strlen(subject); i > 3; i -= 4)
|
|
{
|
|
if (intlmime_decode_base64(pSrc, pDest) == 0)
|
|
{
|
|
pSrc += 4;
|
|
pDest += 4;
|
|
}
|
|
else
|
|
{
|
|
pSrc += 4;
|
|
pDest += 3;
|
|
}
|
|
}
|
|
|
|
*pDest = '\0';
|
|
return output;
|
|
}
|
|
|
|
#if defined(MOZ_MAIL_COMPOSE) || defined(MOZ_MAIL_NEWS)
|
|
|
|
PRIVATE char *intlmime_convert_but_no_decode(const char *header, int16 mailcsid, int16 wincsid)
|
|
{
|
|
char* tmpbuf = NULL, *convbuf = NULL;
|
|
CCCDataObject obj;
|
|
CCCFunc cvtfunc;
|
|
/* Copy buf to tmpbuf, this guarantee the convresion won't overwrite the origional buffer and */
|
|
/* It will always return something it any conversion occcur */
|
|
StrAllocCopy(tmpbuf, header);
|
|
|
|
if(tmpbuf == NULL)
|
|
return NULL;
|
|
|
|
obj = INTL_CreateCharCodeConverter();
|
|
if (obj == NULL)
|
|
return NULL;
|
|
INTL_GetCharCodeConverter(mailcsid, wincsid, obj);
|
|
convbuf = NULL;
|
|
cvtfunc = INTL_GetCCCCvtfunc(obj);
|
|
if (cvtfunc)
|
|
convbuf = (char*)cvtfunc(obj, (unsigned char*)tmpbuf, (int32)XP_STRLEN((char*)tmpbuf));
|
|
XP_FREE(obj);
|
|
|
|
/* if the conversion which use the origional buffer
|
|
them we return the tmpbuf */
|
|
if(convbuf == NULL)
|
|
return tmpbuf;
|
|
|
|
/* if the conversion return a different buffer, we free the
|
|
origional one and return the one return from conversion */
|
|
if(convbuf != tmpbuf)
|
|
XP_FREE(tmpbuf);
|
|
return convbuf;
|
|
}
|
|
/*
|
|
intlmime_is_hz: it is CS_HZ
|
|
*/
|
|
PRIVATE XP_Bool intlmime_is_hz(const char *header)
|
|
{
|
|
return (XP_STRSTR(header, "~{") ? TRUE : FALSE);
|
|
}
|
|
/*
|
|
intlmime_is_iso_2022_xxx: it is statefule encoding with esc
|
|
*/
|
|
PRIVATE XP_Bool intlmime_is_iso_2022_xxx(const char *header, int16 mailcsid)
|
|
{
|
|
return (((mailcsid & STATEFUL) && (XP_STRCHR(header, '\033'))) ? TRUE : FALSE);
|
|
}
|
|
/*
|
|
intlmime_is_mime_part2_header:
|
|
*/
|
|
PRIVATE XP_Bool intlmime_is_mime_part2_header(const char *header)
|
|
{
|
|
return ((
|
|
XP_STRSTR(header, "=?") &&
|
|
(
|
|
XP_STRSTR(header, "?q?") ||
|
|
XP_STRSTR(header, "?Q?") ||
|
|
XP_STRSTR(header, "?b?") ||
|
|
XP_STRSTR(header, "?B?")
|
|
)
|
|
) ? TRUE : FALSE );
|
|
}
|
|
|
|
extern char *strip_continuations(char *original);
|
|
extern unsigned char *XP_WordWrapWithPrefix(int charset, unsigned char *str,
|
|
int maxColumn, int checkQuoting,
|
|
const char *prefix, int addCRLF);
|
|
|
|
PRIVATE
|
|
char *intl_decode_mime_part2_str(const char *header, int wincsid, XP_Bool dontConvert)
|
|
{
|
|
char *work_buf = NULL;
|
|
char *output_p = NULL;
|
|
char *retbuff = NULL;
|
|
char *p, *q, *decoded_text;
|
|
char *begin; /* tracking pointer for where we are in the work buffer */
|
|
int16 csid = 0;
|
|
int ret = 0;
|
|
|
|
|
|
StrAllocCopy(work_buf, header); /* temporary buffer */
|
|
StrAllocCopy(retbuff, header);
|
|
|
|
if (work_buf == NULL || retbuff == NULL)
|
|
return NULL;
|
|
|
|
output_p = retbuff;
|
|
begin = work_buf;
|
|
|
|
while (*begin != '\0')
|
|
{
|
|
char * output_text;
|
|
|
|
/* GetCharset(); */
|
|
p = strstr(begin, "=?");
|
|
if (p == NULL)
|
|
break; /* exit the loop because the rest are not encoded */
|
|
*p = '\0';
|
|
/* skip strings don't need conversion */
|
|
strncpy(output_p, begin, p - begin);
|
|
output_p += p - begin;
|
|
|
|
p += 2;
|
|
begin = p;
|
|
|
|
q = strchr(p, '?'); /* Get charset info */
|
|
if (q == NULL)
|
|
break; /* exit the loop because there are no charset info */
|
|
*q++ = '\0';
|
|
csid = INTL_CharSetNameToID(p);
|
|
if (csid == CS_UNKNOWN)
|
|
{
|
|
/*
|
|
* @@@ may want to use context's default doc_csid in the future
|
|
*/
|
|
break; /* exit the loop because we don't know the charset */
|
|
}
|
|
|
|
if (*(q+1) == '?' &&
|
|
(*q == 'Q' || *q == 'q' || *q == 'B' || *q == 'b'))
|
|
{
|
|
p = strstr(q+2, "?=");
|
|
if(p != NULL)
|
|
*p = '\0';
|
|
if(*q == 'Q' || *q == 'q')
|
|
decoded_text = intlmime_decode_qp(q+2);
|
|
else
|
|
decoded_text = intlmime_decode_base64_buf(q+2);
|
|
}
|
|
else
|
|
break; /* exit the loop because we don't know the encoding method */
|
|
|
|
begin = (p != NULL) ? p + 2 : (q + strlen(q));
|
|
|
|
if (decoded_text == NULL)
|
|
break; /* exit the loop because we have problem to decode */
|
|
|
|
ret = 1;
|
|
if ((! dontConvert) && (csid != wincsid))
|
|
output_text = (char *)intlmime_convert_but_no_decode(decoded_text, csid, (int16)wincsid);
|
|
else
|
|
output_text = (char *)decoded_text;
|
|
|
|
XP_ASSERT(output_text != NULL);
|
|
XP_STRCPY(output_p, (char *)output_text);
|
|
output_p += strlen(output_text);
|
|
|
|
if (output_text != decoded_text)
|
|
XP_FREE(output_text);
|
|
XP_FREE(decoded_text);
|
|
}
|
|
XP_STRCPY(output_p, (char *)begin); /* put the tail back */
|
|
|
|
if (work_buf)
|
|
XP_FREE(work_buf);
|
|
|
|
if (ret)
|
|
{
|
|
return retbuff;
|
|
}
|
|
else
|
|
{
|
|
XP_FREE(retbuff);
|
|
return NULL; /* null means no conversion */
|
|
}
|
|
}
|
|
|
|
/* PRIVATE */
|
|
/* char* intl_strip_crlftab(char* str) */
|
|
/* { */
|
|
/* char* out, *in; */
|
|
/* if(str) { */
|
|
/* for(out = in = str; *in != NULL; in++) */
|
|
/* if((*in != CR) && (*in != LF) && (*in != TAB)) */
|
|
/* *out++ = *in; */
|
|
/* *out = NULL; */
|
|
/* } */
|
|
/* return str; */
|
|
/* } */
|
|
|
|
|
|
/*
|
|
IntlDecodeMimePartIIStr
|
|
This functions converts mail charset to Window charset for subject
|
|
Syntax: =?MimeCharset?[B|Q]?text?=
|
|
MimeCharset = ISO-2022-JP
|
|
ISO-8859-1
|
|
ISO-8859-?
|
|
....
|
|
?B? : Base64 Encoding (used for multibyte encoding)
|
|
?Q? : Quote Printable Encoding (used for single byte encoding)
|
|
|
|
eg. for Japanese mail, it looks like
|
|
=?ISO-2022-JP?B? ........ ?=
|
|
*/
|
|
/* IMPORTANT NOTE: */
|
|
/* Return NULL in this interface only mean ther are no conversion */
|
|
/* It does not mean the conversion is store in the origional buffer */
|
|
/* and the length is not change. This is differ from other conversion routine */
|
|
|
|
|
|
PRIVATE
|
|
char *intl_DecodeMimePartIIStr(const char *header, int16 wincsid, XP_Bool dontConvert)
|
|
{
|
|
int16 mailcsid = INTL_DefaultMailCharSetID(wincsid);
|
|
XP_Bool no8bitdata = TRUE;
|
|
|
|
if (header == 0 || *header == '\0')
|
|
return NULL;
|
|
if (wincsid == 0) /* Use global if undefined */
|
|
wincsid = INTL_DefaultWinCharSetID(0);
|
|
|
|
no8bitdata = intlmime_only_ascii_str(header);
|
|
|
|
/* Start Special Case Handling */
|
|
if(! dontConvert)
|
|
{
|
|
/* Need to do conversion in here if necessary */
|
|
if(! no8bitdata)
|
|
{
|
|
/* Special Case 1: 8 Bit */
|
|
/* then we assume it is not mime part 2 encoding, we convert from the internet encoding to wincsid */
|
|
if(wincsid == CS_UTF8)
|
|
return strip_continuations(intlmime_convert_but_no_decode(header, CS_UTF8, (int16)wincsid));
|
|
else
|
|
return strip_continuations(intlmime_convert_but_no_decode(header, mailcsid, (int16)wincsid));
|
|
}
|
|
else
|
|
{
|
|
/* 7bit- It could be MIME Part 2 Header */
|
|
if ((wincsid == CS_GB_8BIT) && (intlmime_is_hz(header)) )
|
|
{
|
|
/* Special Case 2: HZ */
|
|
/* for subject list pane, if it's GB, we only do HZ conversion */
|
|
return strip_continuations(intlmime_convert_but_no_decode(header, CS_GB_8BIT, CS_GB_8BIT));
|
|
}
|
|
else if((wincsid == CS_UTF8) &&
|
|
(! intlmime_is_mime_part2_header(header)))
|
|
{
|
|
/* Special Case 3: UTF8, no mime2 */
|
|
return strip_continuations(intlmime_convert_but_no_decode(header, CS_UTF8, CS_UTF8));
|
|
}
|
|
else if(intlmime_is_iso_2022_xxx(header, mailcsid) &&
|
|
(! intlmime_is_mime_part2_header(header)))
|
|
{
|
|
return strip_continuations(intlmime_convert_but_no_decode(header, mailcsid, wincsid));
|
|
}
|
|
}
|
|
}
|
|
/* Handle only Mime Part 2 after this point */
|
|
return strip_continuations(intl_decode_mime_part2_str(header, wincsid, dontConvert));
|
|
}
|
|
#endif /* MOZ_MAIL_COMPOSE || MOZ_MAIL_NEWS */
|
|
|
|
|
|
|
|
PRIVATE char basis_64[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
PRIVATE int intlmime_encode_base64 (const char *in, char *out)
|
|
{
|
|
unsigned char c1, c2, c3;
|
|
c1 = in[0];
|
|
c2 = in[1];
|
|
c3 = in[2];
|
|
|
|
*out++ = basis_64[c1>>2];
|
|
*out++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
|
|
|
|
*out++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
|
|
*out++ = basis_64[c3 & 0x3F];
|
|
return 1;
|
|
}
|
|
|
|
PRIVATE char *intlmime_encode_base64_buf(char *subject, size_t size)
|
|
{
|
|
char *output = 0;
|
|
char *pSrc, *pDest ;
|
|
int i ;
|
|
|
|
output = (char *)XP_ALLOC(size * 4 / 3 + 4);
|
|
if (output == NULL)
|
|
return NULL;
|
|
|
|
pSrc = subject;
|
|
pDest = output ;
|
|
for (i = size; i >= 3; i -= 3)
|
|
{
|
|
if (intlmime_encode_base64(pSrc, pDest) == 0) /* error */
|
|
{
|
|
pSrc += 3;
|
|
pDest += 3;
|
|
}
|
|
else
|
|
{
|
|
pSrc += 3;
|
|
pDest += 4;
|
|
}
|
|
}
|
|
/* in case (i % 3 ) == 1 or 2 */
|
|
if(i > 0)
|
|
{
|
|
char in[3];
|
|
int j;
|
|
in[0] = in[1] = in[2] ='\0';
|
|
for(j=0;j<i;j++)
|
|
in[j] = *pSrc++;
|
|
intlmime_encode_base64(in, pDest);
|
|
for(j=i+1;j<4;j++)
|
|
pDest[j] = '=';
|
|
pDest += 4;
|
|
}
|
|
*pDest = '\0';
|
|
return output;
|
|
}
|
|
|
|
#if defined(MOZ_MAIL_COMPOSE) || defined(MOZ_MAIL_NEWS)
|
|
PRIVATE char *intlmime_encode_qp_buf(char *subject)
|
|
{
|
|
char *output = 0;
|
|
unsigned char *p, *pDest ;
|
|
int i, n, len ;
|
|
|
|
if (subject == NULL || *subject == '\0')
|
|
return NULL;
|
|
len = strlen(subject);
|
|
output = XP_ALLOC(len * 3 + 1);
|
|
if (output == NULL)
|
|
return NULL;
|
|
|
|
p = (unsigned char*)subject;
|
|
pDest = (unsigned char*)output ;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
/* XP_IS_ALPHA(*p) || XP_IS_DIGIT(*p)) */
|
|
if ((*p < 0x80) &&
|
|
(((*p >= 'a') && (*p <= 'z')) ||
|
|
((*p >= 'A') && (*p <= 'Z')) ||
|
|
((*p >= '0') && (*p <= '9')))
|
|
)
|
|
*pDest = *p;
|
|
else
|
|
{
|
|
*pDest++ = '=';
|
|
n = (*p & 0xF0) >> 4; /* high byte */
|
|
if (n < 10)
|
|
*pDest = '0' + n;
|
|
else
|
|
*pDest = 'A' + n - 10;
|
|
pDest ++ ;
|
|
|
|
n = *p & 0x0F; /* low byte */
|
|
if (n < 10)
|
|
*pDest = '0' + n;
|
|
else
|
|
*pDest = 'A' + n - 10;
|
|
}
|
|
|
|
p ++;
|
|
pDest ++;
|
|
}
|
|
|
|
*pDest = '\0';
|
|
return output;
|
|
}
|
|
|
|
PRIVATE char *intlmime_encode_next8bitword(int wincsid, char *src)
|
|
{
|
|
char *p;
|
|
XP_Bool non_ascii = FALSE;
|
|
if (src == NULL)
|
|
return NULL;
|
|
p = src;
|
|
while (*p == ' ')
|
|
p ++ ;
|
|
while ( *p )
|
|
{
|
|
if ((unsigned char) *p > 0x7F)
|
|
non_ascii = TRUE;
|
|
if ( IS_MAIL_SEPARATOR(p) )
|
|
{
|
|
break;
|
|
}
|
|
p = INTL_NextChar(wincsid, p);
|
|
}
|
|
|
|
if (non_ascii)
|
|
return p;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
lock then length of input buffer, so the return value is less than iThreshold bytes
|
|
*/
|
|
PRIVATE int ResetLen( int iThreshold, const char* buffer, int16 wincsid )
|
|
{
|
|
const char *begin, *end, *tmp;
|
|
|
|
tmp = begin = end = buffer;
|
|
XP_ASSERT( iThreshold > 1 );
|
|
XP_ASSERT( buffer != NULL );
|
|
while( ( end - begin ) <= iThreshold ){
|
|
tmp = end;
|
|
if (!(*end))
|
|
break;
|
|
end = INTL_NextChar( wincsid, (char*)end );
|
|
}
|
|
|
|
XP_ASSERT( tmp > begin );
|
|
return tmp - begin;
|
|
}
|
|
|
|
PRIVATE char * intlmime_encode_mail_address(int wincsid, const char *src, CCCDataObject obj,
|
|
int maxLineLen)
|
|
{
|
|
char *begin, *end;
|
|
char *retbuf = NULL, *srcbuf = NULL;
|
|
char sep = '\0';
|
|
char *sep_p = NULL;
|
|
char *name;
|
|
int retbufsize;
|
|
int line_len = 0;
|
|
int srclen;
|
|
int default_iThreshold;
|
|
int iThreshold; /* how many bytes we can convert from the src */
|
|
int iEffectLen; /* the maximum length we can convert from the src */
|
|
XP_Bool bChop = FALSE;
|
|
XP_Bool is_being_used_in_email_summary_file = (maxLineLen > 120);
|
|
CCCFunc cvtfunc = NULL;
|
|
|
|
if (obj)
|
|
cvtfunc = INTL_GetCCCCvtfunc(obj);
|
|
|
|
if (src == NULL || *src == '\0')
|
|
return NULL;
|
|
/* make a copy, so don't need touch original buffer */
|
|
StrAllocCopy(srcbuf, src);
|
|
if (srcbuf == NULL)
|
|
return NULL;
|
|
begin = srcbuf;
|
|
|
|
name = (char *)INTL_CsidToCharsetNamePt(intlmime_get_outgoing_mime_csid ((int16)wincsid));
|
|
default_iThreshold = iEffectLen = ( maxLineLen - XP_STRLEN( name ) - 7 ) * 3 / 4;
|
|
iThreshold = default_iThreshold;
|
|
|
|
|
|
/* allocate enough buffer for conversion, this way it can avoid
|
|
do another memory allocation which is expensive
|
|
*/
|
|
|
|
retbufsize = XP_STRLEN(srcbuf) * 3 + MAX_CSNAME + 8;
|
|
retbuf = XP_ALLOC(retbufsize);
|
|
if (retbuf == NULL) /* Give up if not enough memory */
|
|
{
|
|
XP_FREE(srcbuf);
|
|
return NULL;
|
|
}
|
|
|
|
*retbuf = '\0';
|
|
|
|
srclen = XP_STRLEN(srcbuf);
|
|
while (begin < (srcbuf + srclen))
|
|
{ /* get block of data between commas */
|
|
char *p, *q;
|
|
char *buf1, *buf2;
|
|
int len, newsize, convlen, retbuflen;
|
|
XP_Bool non_ascii;
|
|
|
|
retbuflen = XP_STRLEN(retbuf);
|
|
end = NULL;
|
|
if (is_being_used_in_email_summary_file) {
|
|
} else {
|
|
/* scan for separator, conversion happens on 8bit
|
|
word between separators
|
|
*/
|
|
if (IS_MAIL_SEPARATOR(begin))
|
|
{ /* skip white spaces and separator */
|
|
q = begin;
|
|
while ( IS_MAIL_SEPARATOR(q) )
|
|
q ++ ;
|
|
sep = *(q - 1);
|
|
sep_p = (q - 1);
|
|
*(q - 1) = '\0';
|
|
end = q - 1;
|
|
}
|
|
else
|
|
{
|
|
sep = '\0';
|
|
/* scan for next separator */
|
|
non_ascii = FALSE;
|
|
for (q = begin; *q;)
|
|
{
|
|
if ((unsigned char) *q > 0x7F)
|
|
non_ascii = TRUE;
|
|
if ( IS_MAIL_SEPARATOR(q) )
|
|
{
|
|
if ((*q == ' ') && (non_ascii == TRUE))
|
|
{
|
|
while ((p = intlmime_encode_next8bitword(wincsid, q)) != NULL)
|
|
{
|
|
if (p == NULL)
|
|
break;
|
|
q = p;
|
|
if (*p != ' ')
|
|
break;
|
|
}
|
|
}
|
|
sep = *q;
|
|
sep_p = q;
|
|
*q = '\0';
|
|
end = q;
|
|
break;
|
|
}
|
|
q = INTL_NextChar(wincsid, q);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get the to_be_converted_buffer's len */
|
|
len = XP_STRLEN(begin);
|
|
|
|
if ( !intlmime_only_ascii_str(begin) )
|
|
{
|
|
if (obj && cvtfunc)
|
|
{
|
|
/*
|
|
the 30 lenght is calculated as follows (I think)
|
|
total: 30 = 7 + 11 + 8 + 4
|
|
--------------------------------------
|
|
Mime Part II tags: 7 = "=?...?B?...?="
|
|
Charset name: 11 = "iso-2022-jp"
|
|
JIS excape seq. 8 = "<ESC>$B" + "<ESC>(B" * 4/3
|
|
space for one char 4 = 2 * 4/3 rounded up to nearest 4
|
|
Brian Stell 10/97
|
|
*/
|
|
if( ( maxLineLen - line_len < 30 ) || bChop ){
|
|
/* chop first, then continue */
|
|
buf1 = retbuf + retbuflen;
|
|
*buf1++ = CR; *buf1++ = LF; *buf1++ = '\t';
|
|
line_len = 0;
|
|
retbuflen += 3;
|
|
*buf1 = '\0';
|
|
bChop = FALSE;
|
|
iThreshold = default_iThreshold;
|
|
}
|
|
/* iEffectLen - the max byte-string length of JIS ( converted form S-JIS )
|
|
name - such as "iso-2022-jp", the encoding name, MUST be shorter than 23 bytes
|
|
7 - is the "=?:?:?=" */
|
|
iEffectLen = ( maxLineLen - line_len - XP_STRLEN( name ) - 7 ) * 3 / 4;
|
|
while( TRUE ){
|
|
int iBufLen; /* converted buffer's length, not BASE64 */
|
|
if( len > iThreshold )
|
|
len = ResetLen( iThreshold, begin, (int16)wincsid );
|
|
|
|
if ( iThreshold <= 1 )
|
|
{
|
|
/* Certain trashed mailboxes were causing an
|
|
** infinite loop at this point, so we need a way of
|
|
** getting out of trouble.
|
|
**
|
|
** BEFORE: iThreshold was becoming 1, then 0, and
|
|
** we were looping indefinitely.
|
|
** AFTER: Now, first there will be
|
|
** an assert in the previous call to ResetLen,
|
|
** then we'll do that again on the repeat pass,
|
|
** then we'll exit more or less gracefully.
|
|
** - bug #83204, an oldie but goodie.
|
|
** - jrm 98/03/25
|
|
*/
|
|
return NULL;
|
|
}
|
|
buf1 = (char *) cvtfunc(obj, (unsigned char *)begin, len);
|
|
iBufLen = XP_STRLEN( buf1 );
|
|
XP_ASSERT( iBufLen > 0 );
|
|
|
|
/* recal iThreshold each time based on last experience */
|
|
iThreshold = len * iEffectLen / iBufLen;
|
|
if( iBufLen > iEffectLen ){
|
|
/* the converted buffer is too large, we have to
|
|
1. free the buffer;
|
|
2. redo again based on the new iThreshold
|
|
*/
|
|
bChop = TRUE; /* append CRLFTAB */
|
|
if (buf1 && (buf1 != begin)){
|
|
XP_FREE(buf1);
|
|
buf1 = NULL;
|
|
}
|
|
} else {
|
|
end = begin + len - 1;
|
|
break;
|
|
}
|
|
}
|
|
if (bChop && (NULL!=sep_p)) {
|
|
*sep_p = sep; /* we are length limited so we do not need this */
|
|
sep = '\0'; /* artifical terminator. So, restore the original character */
|
|
sep_p = NULL;
|
|
}
|
|
|
|
if (!buf1)
|
|
{
|
|
XP_FREE(srcbuf);
|
|
XP_FREE(retbuf);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
buf1 = XP_ALLOC(len + 1);
|
|
if (!buf1)
|
|
{
|
|
XP_FREE(srcbuf);
|
|
XP_FREE(retbuf);
|
|
return NULL;
|
|
}
|
|
XP_MEMCPY(buf1, begin, len);
|
|
*(buf1 + len) = '\0';
|
|
}
|
|
|
|
if (wincsid & MULTIBYTE)
|
|
{
|
|
/* converts to Base64 Encoding */
|
|
buf2 = (char *)intlmime_encode_base64_buf(buf1, strlen(buf1));
|
|
}
|
|
else
|
|
{
|
|
/* Converts to Quote Printable Encoding */
|
|
buf2 = (char *)intlmime_encode_qp_buf(buf1);
|
|
}
|
|
|
|
|
|
if (buf1 && (buf1 != begin))
|
|
XP_FREE(buf1);
|
|
|
|
if (buf2 == NULL) /* QUIT if memory allocation failed */
|
|
{
|
|
XP_FREE(srcbuf);
|
|
XP_FREE(retbuf);
|
|
return NULL;
|
|
}
|
|
|
|
/* realloc memory for retbuff if necessary,
|
|
7: =?...?B?..?=, 3: CR LF TAB */
|
|
convlen = XP_STRLEN(buf2) + XP_STRLEN(name) + 7;
|
|
newsize = convlen + retbuflen + 3 + 2; /* 2:SEP '\0', 3:CRLFTAB */
|
|
|
|
if (newsize > retbufsize)
|
|
{
|
|
char *tempbuf;
|
|
tempbuf = XP_REALLOC(retbuf, newsize);
|
|
if (tempbuf == NULL) /* QUIT, if not enough memory left */
|
|
{
|
|
XP_FREE(buf2);
|
|
XP_FREE(srcbuf);
|
|
XP_FREE(retbuf);
|
|
return NULL;
|
|
}
|
|
retbuf = tempbuf;
|
|
retbufsize = newsize;
|
|
}
|
|
/* buf1 points to end of current retbuf */
|
|
buf1 = retbuf + retbuflen;
|
|
|
|
if ((line_len > 10) &&
|
|
((line_len + convlen) > maxLineLen))
|
|
{
|
|
*buf1++ = CR;
|
|
*buf1++ = LF;
|
|
*buf1++ = '\t';
|
|
line_len = 0;
|
|
iThreshold = default_iThreshold;
|
|
}
|
|
*buf1 = '\0';
|
|
|
|
/* Add encoding tag for base62 and QP */
|
|
XP_STRCAT(buf1, "=?");
|
|
XP_STRCAT(buf1, name );
|
|
if(wincsid & MULTIBYTE)
|
|
XP_STRCAT(buf1, "?B?");
|
|
else
|
|
XP_STRCAT(buf1, "?Q?");
|
|
XP_STRCAT(buf1, buf2);
|
|
XP_STRCAT(buf1, "?=");
|
|
|
|
line_len += convlen + 1; /* 1: SEP */
|
|
|
|
XP_FREE(buf2); /* free base64 buffer */
|
|
}
|
|
else /* if no 8bit data in the block */
|
|
{
|
|
newsize = retbuflen + len + 2 + 3; /* 2: ',''\0', 3: CRLFTAB */
|
|
if (newsize > retbufsize)
|
|
{
|
|
char *tempbuf;
|
|
tempbuf = XP_REALLOC(retbuf, newsize);
|
|
if (tempbuf == NULL)
|
|
{
|
|
XP_FREE(srcbuf);
|
|
XP_FREE(retbuf);
|
|
return NULL;
|
|
}
|
|
retbuf = tempbuf;
|
|
retbufsize = newsize;
|
|
}
|
|
buf1 = retbuf + retbuflen;
|
|
|
|
if ((line_len > 10) &&
|
|
((line_len + len) > maxLineLen))
|
|
{
|
|
*buf1++ = CR;
|
|
*buf1++ = LF;
|
|
*buf1++ = '\t';
|
|
line_len = 0;
|
|
iThreshold = default_iThreshold;
|
|
}
|
|
/* copy buffer from begin to buf1 stripping CRLFTAB */
|
|
for (p = begin; *p; p++)
|
|
{
|
|
if (*p == CR || *p == LF || *p == TAB)
|
|
len --;
|
|
else
|
|
*buf1++ = *p;
|
|
}
|
|
*buf1 = '\0';
|
|
line_len += len + 1; /* 1: SEP */
|
|
}
|
|
|
|
buf1 = buf1 + XP_STRLEN(buf1);
|
|
if (sep == CR || sep == LF || sep == TAB) /* strip CR,LF,TAB */
|
|
*buf1 = '\0';
|
|
else
|
|
{
|
|
*buf1 = sep;
|
|
*(buf1+1) = '\0';
|
|
}
|
|
|
|
if (end == NULL)
|
|
break;
|
|
begin = end + 1;
|
|
}
|
|
if (srcbuf)
|
|
XP_FREE(srcbuf);
|
|
return retbuf;
|
|
}
|
|
|
|
/*
|
|
Latin1, latin2:
|
|
Source --> Quote Printable --> Encoding Info
|
|
Japanese:
|
|
EUC,JIS,SJIS --> JIS --> Base64 --> Encoding Info
|
|
Others:
|
|
No conversion
|
|
flag: 0: 8bit on
|
|
1: mime_use_quoted_printable_p
|
|
return: NULL if no conversion occured
|
|
|
|
*/
|
|
|
|
PRIVATE
|
|
char *intl_EncodeMimePartIIStr(char *subject, int16 wincsid, XP_Bool bUseMime, int maxLineLen)
|
|
{
|
|
int iSrcLen;
|
|
unsigned char *buf = NULL; /* Initial to NULL */
|
|
int16 mail_csid;
|
|
CCCDataObject obj = NULL;
|
|
char *name;
|
|
CCCFunc cvtfunc = NULL;
|
|
|
|
if (subject == NULL || *subject == '\0')
|
|
return NULL;
|
|
|
|
iSrcLen = XP_STRLEN(subject);
|
|
if (wincsid == 0)
|
|
wincsid = INTL_DefaultWinCharSetID(0) ;
|
|
|
|
mail_csid = intlmime_get_outgoing_mime_csid ((int16)wincsid);
|
|
name = (char *)INTL_CsidToCharsetNamePt(mail_csid);
|
|
|
|
/* check to see if subject are all ascii or not */
|
|
if(intlmime_only_ascii_str(subject))
|
|
return (char *) XP_WordWrapWithPrefix(mail_csid, (unsigned char *)
|
|
subject, maxLineLen, 0, " ", 1);
|
|
|
|
if (mail_csid != wincsid)
|
|
{
|
|
obj = INTL_CreateCharCodeConverter();
|
|
if (obj == NULL)
|
|
return 0;
|
|
/* setup converter from wincsid --> mail_csid */
|
|
INTL_GetCharCodeConverter((int16)wincsid, mail_csid, obj) ;
|
|
cvtfunc = INTL_GetCCCCvtfunc(obj);
|
|
}
|
|
/* Erik said in the case of STATEFUL mail encoding, we should FORCE it to use */
|
|
/* MIME Part2 to get ride of ESC in To: and CC: field, which may introduce more trouble */
|
|
if((bUseMime) || (mail_csid & STATEFUL))/* call intlmime_encode_mail_address */
|
|
{
|
|
buf = (unsigned char *)intlmime_encode_mail_address(wincsid, subject, obj, maxLineLen);
|
|
if(buf == (unsigned char*)subject) /* no encoding, set return value to NULL */
|
|
buf = NULL;
|
|
}
|
|
else
|
|
{ /* 8bit, just do conversion if necessary */
|
|
/* In this case, since the conversion routine may reuse the origional buffer */
|
|
/* We better allocate one first- We don't want to reuse the origional buffer */
|
|
|
|
if ((mail_csid != wincsid) && (cvtfunc))
|
|
{
|
|
char* newbuf = NULL;
|
|
/* Copy buf to newbuf */
|
|
StrAllocCopy(newbuf, subject);
|
|
if(newbuf != NULL)
|
|
{
|
|
buf = (unsigned char *)cvtfunc(obj, (unsigned char*)newbuf, iSrcLen);
|
|
if(buf != (unsigned char*)newbuf)
|
|
XP_FREE(newbuf);
|
|
/* time for wrapping long line */
|
|
if (buf)
|
|
{
|
|
newbuf = (char*) buf;
|
|
buf = XP_WordWrapWithPrefix(mail_csid, (unsigned char *)
|
|
newbuf, maxLineLen, 0, " ", 1);
|
|
|
|
if (buf != (unsigned char*) newbuf)
|
|
XP_FREE(newbuf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (obj)
|
|
XP_FREE(obj);
|
|
return (char*)buf;
|
|
|
|
/* IMPORTANT NOTE: */
|
|
/* Return NULL in this interface only mean ther are no conversion */
|
|
/* It does not mean the conversion is store in the origional buffer */
|
|
/* and the length is not change. This is differ from other conversion routine */
|
|
}
|
|
#endif /* MOZ_MAIL_COMPOSE || MOZ_MAIL_NEWS */
|
|
|
|
#ifdef MOZ_MAIL_NEWS
|
|
#if 0
|
|
PUBLIC XP_Bool INTL_FindMimePartIIStr(int16 csid, XP_Bool searchcasesensitive, const char *mimepart2str,const char *s2)
|
|
{
|
|
XP_Bool onlyAscii;
|
|
char *ret = NULL;
|
|
char *s1 = (char*)mimepart2str;
|
|
char *conv;
|
|
if((s2 == NULL) || (*s2 == '\0')) /* if search for NULL string, return TRUE */
|
|
return TRUE;
|
|
if((s1 == NULL) || (*s1 == '\0')) /* if string is NULL, return FALSE */
|
|
return FALSE;
|
|
|
|
conv= IntlDecodeMimePartIIStr(mimepart2str, csid, FALSE);
|
|
if(conv)
|
|
s1 = conv;
|
|
onlyAscii = intlmime_only_ascii_str(s1) && intlmime_only_ascii_str(s2);
|
|
if(onlyAscii) /* for performance reason, let's call the ANSI C routine for ascii only case */
|
|
{
|
|
if(searchcasesensitive)
|
|
ret= strstr( s1, s2);
|
|
else
|
|
ret= strcasestr(s1, s2);
|
|
}
|
|
else
|
|
{
|
|
if(searchcasesensitive)
|
|
ret= INTL_Strstr(csid, s1, s2);
|
|
else
|
|
ret= INTL_Strcasestr(csid, s1, s2);
|
|
}
|
|
if(conv != mimepart2str)
|
|
XP_FREE(conv);
|
|
return (ret != NULL); /* return TRUE if it find something */
|
|
}
|
|
#endif
|
|
/*
|
|
NNTP XPAT I18N Support
|
|
INTL_FormatNNTPXPATInNonRFC1522Format and INTL_FormatNNTPXPATInRFC1522Format
|
|
return the a new string to the caller
|
|
that could be send to NNTP server for Mail Header Search (use XPAT)
|
|
The caller must free the return string.
|
|
*/
|
|
|
|
/* This function use the same buffer it pass in, it strip the leading and trialling ISO-2022 ESC */
|
|
PRIVATE void intl_strip_leading_and_trial_iso_2022_esc (char* iso_2022_str);
|
|
PRIVATE char *intl_xpat_escape ( char *str);
|
|
|
|
#define ISO_2022_I_CODE(c) ((0x20 <= (c)) && ((c) <= 0x2F))
|
|
#define ISO_2022_F_CODE(c) ((0x30 <= (c)) && ((c) <= 0x7E))
|
|
|
|
PRIVATE void intl_strip_leading_and_trial_iso_2022_esc(char* iso_2022_str)
|
|
{
|
|
char* inp = iso_2022_str;
|
|
char* outp = iso_2022_str;
|
|
char* lastescp = NULL;
|
|
|
|
/* strip leading Escape */
|
|
if(ESC == *inp)
|
|
{
|
|
for(inp++ ;((*inp) && (ISO_2022_I_CODE(*inp))); /* void */)
|
|
inp++; /* Skip I Code */
|
|
if(ISO_2022_F_CODE(*inp)) /* Skip F Code */
|
|
inp++;
|
|
}
|
|
|
|
for( ; (0 != *inp); inp++, outp++) /* copy data including esc */
|
|
{
|
|
*outp = *inp;
|
|
if(ESC == *outp) /* remember the last position of esc */
|
|
lastescp = outp;
|
|
}
|
|
*outp = '\0'; /* NULL terminate */
|
|
|
|
/* strip trialling Escape if necessary */
|
|
if(lastescp)
|
|
{
|
|
char* esc_p;
|
|
for(esc_p = lastescp + 1; ((*esc_p) && (ISO_2022_I_CODE(*esc_p))); /* void */ )
|
|
esc_p++; /* Skip I Code */
|
|
|
|
if(ISO_2022_F_CODE(*esc_p)) /* Skip F Code */
|
|
esc_p++;
|
|
|
|
if('\0' == *esc_p) /* if it point to our NULL terminate, it is the trialling esp, we take it out */
|
|
{ /* otherwise, it is the esc in the middle, ignore it */
|
|
*lastescp = '\0';
|
|
}
|
|
}
|
|
}
|
|
#define BETWEEN_A_Z(c) (('A' <= (c)) && ((c) <= 'Z'))
|
|
#define BETWEEN_a_z(c) (('a' <= (c)) && ((c) <= 'z'))
|
|
#define BETWEEN_0_9(c) (('0' <= (c)) && ((c) <= '9'))
|
|
/*
|
|
Escape Everything except 0-9 A-Z a-z
|
|
"Common NNTP Extensions" and wildmat(3) does not state clearly what NEED TO BE Escape.
|
|
I look like the * ? [ \ need to be escape ,
|
|
But by trial and error I also find out ^ and $ need to be escape.
|
|
That's why I just do this ESCAPE MORE THAN WE NEEDED untill we figure out what relly NEED TO BE Escaped
|
|
*/
|
|
#define XPAT_NEED_ESCAPE(c) (! (BETWEEN_A_Z(c) || BETWEEN_a_z(c) || BETWEEN_0_9(c)))
|
|
PRIVATE char *intl_xpat_escape( char *str)
|
|
{
|
|
char *result = NULL;
|
|
/* max escaped length is one extra characters for every character in the str. */
|
|
char *scratchBuf = (char*) XP_ALLOC (2*XP_STRLEN(str) + 1);
|
|
if(scratchBuf)
|
|
{
|
|
char *scratchPtr = scratchBuf;
|
|
char ch;
|
|
while ('\0' != (ch = *str++))
|
|
{
|
|
if (XPAT_NEED_ESCAPE(ch))
|
|
*scratchPtr++ = '\\';
|
|
*scratchPtr++ = ch;
|
|
}
|
|
*scratchPtr = '\0';
|
|
result = XP_STRDUP (scratchBuf); /* realloc down to smaller size */
|
|
XP_FREE (scratchBuf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
INTL_FormatNNTPXPATInNonRFC1522Format
|
|
1. Convert the data from wincsid to newscsid
|
|
2. Strip Out leading Esc Sequence and trialing Esc Sequence
|
|
3. Always return memory unless memory is not enough. Never have side effect on the pass-in buffer
|
|
*/
|
|
|
|
PUBLIC unsigned char* INTL_FormatNNTPXPATInNonRFC1522Format(int16 wincsid, unsigned char* searchString)
|
|
{
|
|
char* temp = NULL;
|
|
char* conv = NULL;
|
|
char* xpat_escape = NULL;
|
|
StrAllocCopy(temp, (char*) searchString);
|
|
XP_ASSERT(temp); /* Should only come here if Memory Not Enough */
|
|
if(NULL == temp)
|
|
return NULL;
|
|
|
|
/* Convert text from wincsid to newscsid */
|
|
if(NULL != (conv = (char*)INTL_ConvertLineWithoutAutoDetect(wincsid, INTL_DefaultNewsCharSetID(wincsid), (unsigned char*)temp, XP_STRLEN((char*)temp))))
|
|
XP_FREE(temp); /* If the conversion do use the same buffer, free the origional one */
|
|
else
|
|
conv = temp;
|
|
intl_strip_leading_and_trial_iso_2022_esc(conv);
|
|
|
|
/* Do XPAT escape */
|
|
xpat_escape = intl_xpat_escape(conv);
|
|
if(NULL != conv)
|
|
XP_FREE(conv);
|
|
return (unsigned char*) xpat_escape;
|
|
}
|
|
|
|
#if 0
|
|
|
|
PUBLIC unsigned char* INTL_FormatNNTPXPATInRFC1522Format(int16 wincsid, unsigned char* searchString)
|
|
{
|
|
/* Temp Implementation untill we really support it : Just make a duplication. */
|
|
char* result = NULL;
|
|
StrAllocCopy(result, (char*) searchString);
|
|
XP_ASSERT(result); /* Should only come here if Memory Not Enough */
|
|
return (unsigned char*) result;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* MOZ_MAIL_NEWS */
|