cdesktopenv/cde/programs/dtmail/libDtMail/RFC/RFCBodyPart.C

1086 lines
26 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
*+SNOTICE
*
*
* $TOG: RFCBodyPart.C /main/16 1998/04/06 13:27:40 mgreess $
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special
* restrictions in a confidential disclosure agreement bertween
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
* document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
* Sun's specific written approval. This documment and all copies
* and derivative works thereof must be returned or destroyed at
* Sun's request.
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*
*+ENOTICE
*/
#ifndef I_HAVE_NO_IDENT
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Dt/Dts.h>
#include <DtMail/DtMail.hh>
#include "RFCImpl.hh"
#include <DtMail/Threads.hh>
// For CHARSET
//-------------------------------------
// HACK ALERT
// Any code change within "For CHARSET" should be changed in
// RFCBodyPart and Session because the same methods are duplicated
// in both of these classes.
// See RFCImpl.hh or DtMail/DtMail.hh for more explanation.
//-------------------------------------
#include <locale.h>
#include <time.h>
#include <Dt/LocaleXlate.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#ifndef True
#define True 1
#endif
#ifndef False
#define False 0
#endif
#if defined(SunOS) && (SunOS < 55)
extern "C" {
#endif
#include <iconv.h>
#if defined(SunOS) && (SunOS < 55)
}
#endif
// End of For CHARSET
RFCBodyPart::RFCBodyPart(DtMailEnv & error,
DtMail::Message * parent,
const char * start,
const int len,
RFCEnvelope * body_env)
: DtMail::BodyPart(error, parent)
{
_body_lock = MutexInit();
_body_start = _body_text = start;
_body_len = len > 0 ? len : 0;
// If we have no body to start, then we will want to create
// a body envelope. We do this to make sure we can always
// set headers for the body part.
//
if (_body_start == NULL) {
_body_env = new RFCEnvelope(error, parent, NULL, 0);
_my_env = DTM_TRUE;
}
else {
_body_env = body_env;
_my_env = _body_env ? DTM_FALSE : DTM_TRUE;
}
_body = NULL;
_body_decoded_len = 0;
_body_type = NULL;
_must_free_body = DTM_FALSE;
}
RFCBodyPart::~RFCBodyPart(void)
{
if (_my_env == DTM_TRUE) {
delete _body_env;
}
if (_must_free_body) {
free(_body);
}
if (_body_type) {
free(_body_type);
}
}
void
RFCBodyPart::getContents(DtMailEnv & error,
const void ** contents,
unsigned long * length,
char ** type,
char ** name,
int * mode,
char ** description)
{
// First things first, for each possible return, zero out the
// pointer so that if the caller does not check for errors it
// will be caught by a null dereference
//
if (name)
*name = (char *)0;
if (description)
*description = (char *)0;
if (type)
*type = (char *)0;
if (contents)
*contents = (void *)0;
// The caller can ask for a hodge podge of information.
// The only real rule here is that you can not ask for
// the contents without requesting the length.
if (contents && !length) {
error.setError(DTME_OperationInvalid);
return;
}
// No need to clear the error here because it should be clear already.
// error.clear();
// MIME currently doesn't have body part names and RFC
// doesn't let us at arbitrary message headers. Name
// therefore can not be retrieved.
//
if (name) {
*name = getName(error);
if (error.isSet()) {
// don't care about the error condition returned by getName()
// because it returns a valid string no matter what error
// condition occurs.
error.clear();
}
}
// Ditto for mode.
//
if (mode) {
*mode = 0;
}
if (description) {
// getDescription should not be passed a DtMailEnv object.
// Neither implementation of getDescription (MIME or V3)
// sets the error before returning. Besides being unnecessary,
// it requires the caller of getDescription to check the error
// status upon its return.
//
*description = getDescription(error);
if (error.isSet()) {
// Don't care about the error condition returned by getDescription
// because it returns a valid string no matter what error
// condition occurs.
error.clear();
}
}
// Types for DtMail are in the Dt name space. RFC returns
// mime types. We will have to convert from the RFC name
// space to the Dt name space.
//
if (type) {
if (!_body_type) {
getDtType(error);
// Do we want to propogate this error back up to the
// function that called us? If not, we need to call
// error.clear() before returning.
if (error.isSet()) {
return;
}
}
*type = strdup(_body_type);
}
if (length) {
*length = getLength(error);
if (error.isSet()) {
// propogate the error back up to the caller
return;
}
}
if (contents) {
*contents = getBody(error);
if (error.isSet()) {
// propogate the error back up to the caller
return;
}
}
}
void
RFCBodyPart::setContents(DtMailEnv & error,
const void * contents,
const unsigned long length,
const char * type,
const char * name,
const int, // mode,
const char *) // description)
{
error.clear();
MutexLock lock_scope(_body_lock);
if (name) {
setName(error, name);
}
if (contents && !length) {
error.setError(DTME_OperationInvalid);
return;
}
if (!contents && !length) {
if (_body && _must_free_body) {
free(_body);
_body = NULL;
_must_free_body = DTM_FALSE;
}
_body_decoded_len = _body_len = 0;
if (_body_type) {
free(_body_type);
_body_type = NULL;
}
// _must_free_body = DTM_FALSE;
return;
}
if (contents) {
if (_body && _must_free_body) {
free(_body);
_body = NULL;
}
_body = (char *)malloc((int)length);
memcpy(_body, contents, (int)length);
_body_decoded_len = _body_len = (int)length;
_must_free_body = DTM_TRUE;
// Reset the type. We don't know what it is!
if (_body_type) {
free(_body_type);
_body_type = NULL;
}
}
if (type) {
if (_body_type)
free(_body_type);
_body_type = strdup(type);
}
}
void
RFCBodyPart::lockContents(DtMailEnv & error, const DtMailLock)
{
error.clear();
}
void
RFCBodyPart::unlockContents(DtMailEnv & error)
{
error.clear();
}
void
RFCBodyPart::getHeader(DtMailEnv & error,
const char * name,
const DtMailBoolean abstract,
DtMailValueSeq & value)
{
error.clear();
// If this is not our envelope, then we will not set the flag.
//
if (_my_env == DTM_FALSE) {
error.setError(DTME_NotSupported);
return;
}
if (_body_env == (RFCEnvelope *)NULL) {
error.setError(DTME_NoObjectValue);
return;
}
_body_env->getHeader(error, name, abstract, value);
}
void
RFCBodyPart::setFlag(DtMailEnv & error,
DtMailBodyPartState state)
{
error.clear();
// If this is not our envelope, then we will not set the flag.
//
if (_my_env == DTM_FALSE) {
error.setError(DTME_NotSupported);
return;
}
DtMailEnv my_error;
time_t now;
char str_time[40];
switch (state) {
case DtMailBodyPartDeletePending:
now = time(NULL);
sprintf(str_time, "%08lX", (long)now);
_body_env->setHeader(my_error, RFCDeleteHeader, DTM_TRUE, str_time);
break;
default:
error.setError(DTME_OperationInvalid);
}
}
void
RFCBodyPart::resetFlag(DtMailEnv & error,
DtMailBodyPartState state)
{
error.clear();
// If this is not our envelope, then we will not set the flag.
//
if (_my_env == DTM_FALSE) {
error.setError(DTME_NotSupported);
return;
}
DtMailEnv my_error;
switch (state) {
case DtMailBodyPartDeletePending:
_body_env->removeHeader(my_error, RFCDeleteHeader);
break;
default:
error.setError(DTME_OperationInvalid);
}
}
DtMailBoolean
RFCBodyPart::flagIsSet(DtMailEnv & error,
DtMailBodyPartState state)
{
error.clear();
// If this is not our envelope, then we will not set the flag.
//
if (_my_env == DTM_FALSE) {
error.setError(DTME_NotSupported);
return(DTM_FALSE);
}
DtMailEnv my_error;
DtMailValueSeq value;
DtMailBoolean answer;
switch (state) {
case DtMailBodyPartDeletePending:
_body_env->getHeader(my_error, RFCDeleteHeader, DTM_FALSE, value);
if (my_error.isNotSet()) {
answer = DTM_TRUE;
}
else {
answer = DTM_FALSE;
}
break;
default:
error.setError(DTME_OperationInvalid);
return(DTM_FALSE);
}
return(answer);
}
time_t
RFCBodyPart::getDeleteTime(DtMailEnv & error)
{
time_t delete_time = 0;
DtMailValueSeq value;
_body_env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
if (error.isNotSet()) {
delete_time = (time_t) strtol(*(value[0]), NULL, 16);
}
error.clear();
return(delete_time);
}
void
RFCBodyPart::adjustBodyPartsLocation(char * start)
{
MutexLock lock_scope(_body_lock);
_body_text = (_body_text - _body_start) + start;
_body_start = start;
if (_must_free_body == DTM_FALSE) {
//_body = (char *)_body_text;
_body = NULL;
}
if (_body_env && _my_env == DTM_TRUE) {
// CMVC bug 2807
// start points at the body part separator. Need to
// Skip separator. Put in a sanity check until we know
// this is the right fix
if (*start != '-' && *(start + 1) != '-') {
fprintf(
stderr,
"RFCBodyPart::adjustBodyPartLocation(%.20s): Not a separator\n",
start);
} else {
while (*start != '\n')
start++;
start++;
}
// End Of fix for 2807
_body_env->adjustHeaderLocation(start, (int)(_body_text-_body_start));
}
}
DtMailBoolean
RFCBodyPart::isTerm(const char * start)
{
if (*start == '\n' || (*start == '\r' && *(start + 1) == '\n')) {
return(DTM_TRUE);
}
else {
return(DTM_FALSE);
}
}
const void *
RFCBodyPart::getBody(DtMailEnv & error)
{
error.clear();
if (!_body) {
loadBody(error);
if (error.isSet()) {
return(NULL);
}
}
return(_body);
}
// For CHARSET
/*
* Wrapper functions taken from libHelp/CEUtil.c
*
* We took these functions and renamed them because
* 1. Originally these are called _DtHelpCeXlate* and thus they are private
* to libHelp and not exported to outside of libHelp.
* 2. When these functions are moved to another library, then users of these
* functions would only need to link with a different library. The caller
* doesn't have to modify code.
*/
static const char *DfltStdCharset = "us-ascii";
static const char *DfltStdLang = "C";
static char MyPlatform[_DtPLATFORM_MAX_LEN+1];
static _DtXlateDb MyDb = NULL;
static char MyProcess = False;
static char MyFirst = True;
static int ExecVer;
static int CompVer;
/******************************************************************************
* Function: static int OpenLcxDb ()
*
* Parameters: none
*
* Return Value: 0: ok
* -1: error
*
* errno Values:
*
* Purpose: Opens the Ce-private Lcx database
*
*****************************************************************************/
int
RFCBodyPart::OpenLcxDb (void)
{
time_t time1 = 0;
time_t time2 = 0;
while (MyProcess == True)
{
/* if time out, return */
if (time(&time2) == (time_t)-1)
return -1;
if (time1 == 0)
time1 = time2;
else if (time2 - time1 >= (time_t)30)
return -1;
}
if (MyFirst == True)
{
MyProcess = True;
if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
_DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
{
_DtLcxCloseDb(&MyDb);
MyDb = NULL;
}
MyFirst = False;
MyProcess = False;
}
return (MyDb == NULL ? -1 : 0 );
}
/******************************************************************************
* Function: int DtXlateOpToStdLocale(char *operation, char *opLocale,
* char **ret_stdLocale,
* char **ret_stdLang, char **ret_stdSet)
*
* Parameters:
* operation Operation associated with the locale value
* opLocale An operation-specific locale string
* ret_locale Returns the std locale
* Caller must free this string.
* ret_stdLang Returns the std language & territory string.
* Caller must free this string.
* ret_stdSet Returns the std code set string.
* Caller must free this string.
*
* Return Value:
*
* Purpose: Gets the standard locale given an operation and its locale
*
*****************************************************************************/
void
RFCBodyPart::DtXlateOpToStdLocale (
char *operation,
char *opLocale,
char **ret_stdLocale,
char **ret_stdLang,
char **ret_stdSet)
{
int result = OpenLcxDb();
if (result == 0) {
(void) _DtLcxXlateOpToStd(
MyDb, MyPlatform, CompVer,
operation, opLocale,
ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
}
/* if failed, give default values */
if (ret_stdLocale != NULL && (result != 0 || *ret_stdLocale == NULL))
{
*ret_stdLocale =
(char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
}
if (ret_stdLang != NULL && (result != 0 || *ret_stdLang == NULL))
*ret_stdLang = (char *)strdup(DfltStdLang);
if (ret_stdSet != NULL && (result != 0 || *ret_stdSet == NULL))
*ret_stdSet = (char *)strdup(DfltStdCharset);
}
/******************************************************************************
* Function: int DtXlateStdToOpLocale ( char *operation, char *stdLocale,
* char *dflt_opLocale, char **ret_opLocale)
*
* Parameters:
* operation operation whose locale value will be retrieved
* stdLocale standard locale value
* dflt_opLocale operation-specific locale-value
* This is the default value used in error case
* ret_opLocale operation-specific locale-value placed here
* Caller must free this string.
*
* Return Value:
*
* Purpose: Gets an operation-specific locale string given the standard string
*
*****************************************************************************/
void
RFCBodyPart::DtXlateStdToOpLocale (
char *operation,
char *stdLocale,
char *dflt_opLocale,
char **ret_opLocale)
{
int result = this->OpenLcxDb();
if (ret_opLocale)
*ret_opLocale = NULL;
if (result == 0)
{
(void) _DtLcxXlateStdToOp(
MyDb, MyPlatform, CompVer,
operation,
stdLocale, NULL, NULL, NULL,
ret_opLocale);
}
/* if translation fails, use a default value */
if (ret_opLocale && (result != 0 || *ret_opLocale == NULL))
{
if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
}
}
/******************************************************************************
* Function: int DtXlateStdToOpCodeset (
* char *operation,
* char *stdCodeset,
* char *dflt_opCodeset,
* char **ret_opCodeset)
*
* Parameters:
* operation operation whose codeset value will be retrieved
* stdCodeset standard codeset value
* dflt_opCodeset operation-specific codeset-value
* This is the default value used in error case
* ret_opCodeset operation-specific codeset-value placed here
* Caller must free this string.
*
* Return Value:
*
* Purpose: Gets an operation-specific locale string given the standard string
*
*****************************************************************************/
void
RFCBodyPart::DtXlateStdToOpCodeset (
char *operation,
char *stdCodeset,
char *dflt_opCodeset,
char **ret_opCodeset)
{
int result = this->OpenLcxDb();
if (ret_opCodeset)
*ret_opCodeset = NULL;
if (result == 0)
{
(void) _DtLcxXlateStdToOp(
MyDb, MyPlatform, CompVer,
operation,
NULL, NULL, stdCodeset, NULL,
ret_opCodeset);
}
/* if translation fails, use a default value */
if (ret_opCodeset && (result != 0 || *ret_opCodeset == NULL))
{
if (dflt_opCodeset) *ret_opCodeset = (char *)strdup(dflt_opCodeset);
else if (stdCodeset) *ret_opCodeset = (char *)strdup(stdCodeset);
}
}
void
RFCBodyPart::DtXlateMimeToIconv(
const char *mimeId,
const char *defaultCommonCS,
const char *defaultIconvCS,
char **ret_commonCS,
char **ret_platformIconv)
{
int exists = -1;
this->OpenLcxDb();
exists = _DtLcxXlateOpToStd(
MyDb, MyPlatform, CompVer,
DtLCX_OPER_MIME, mimeId,
NULL, NULL, ret_commonCS, NULL);
if (exists == -1)
{
exists = _DtLcxXlateOpToStd(
MyDb, "CDE", 0,
DtLCX_OPER_MIME, mimeId,
NULL, NULL, ret_commonCS, NULL);
if (exists == -1)
*ret_commonCS = (char *)strdup(defaultCommonCS);
}
exists = _DtLcxXlateStdToOp(
MyDb, MyPlatform, CompVer,
DtLCX_OPER_ICONV3,
NULL, NULL, *ret_commonCS, NULL,
ret_platformIconv);
if (exists == -1)
*ret_platformIconv = (char *)strdup(defaultIconvCS);
}
void
RFCBodyPart::DtXlateLocaleToMime(
const char * locale,
const char * defaultCommonCS,
const char * defaultMimeCS,
char ** ret_mimeCS)
{
char * commonCS = NULL;
this->OpenLcxDb();
/* look for platform-specific locale to CDE translation */
_DtLcxXlateOpToStd(
MyDb, MyPlatform, CompVer,
DtLCX_OPER_SETLOCALE, locale,
NULL, NULL, &commonCS, NULL);
if (!commonCS)
commonCS = (char *)strdup(defaultCommonCS);
/* look for platform-specific MIME types; by default, there is none */
_DtLcxXlateStdToOp(
MyDb, MyPlatform, CompVer,
DtLCX_OPER_MIME,
NULL, NULL, commonCS, NULL,
ret_mimeCS);
if (!(*ret_mimeCS))
{
_DtLcxXlateStdToOp(
MyDb, "CDE", 0,
DtLCX_OPER_MIME,
NULL, NULL, commonCS, NULL,
ret_mimeCS);
if (!(*ret_mimeCS))
*ret_mimeCS = (char *)strdup(defaultMimeCS);
}
if (commonCS)
free(commonCS);
}
// Return iconv name of the given codeset.
// If iconv name does not exist, return NULL.
char *
RFCBodyPart::csToConvName(char *cs)
{
int exists = -1;
char *commonCS = NULL;
char *convName = NULL;
char *ret_target = NULL;
this->OpenLcxDb();
// Convert charset to upper case first because charset table is
// case sensitive.
if (cs)
{
int len_cs = strlen(cs);
for (int num_cs = 0; num_cs < len_cs; num_cs++)
*(cs+num_cs) = toupper(*(cs+num_cs));
}
exists = _DtLcxXlateOpToStd(
MyDb, MyPlatform, CompVer,
DtLCX_OPER_MIME, cs,
NULL, NULL, &commonCS, NULL);
if (exists == -1) {
exists = _DtLcxXlateOpToStd(
MyDb, "CDE", 0,
DtLCX_OPER_MIME, cs,
NULL, NULL, &commonCS, NULL);
if (exists == -1)
return NULL;
}
DtXlateStdToOpCodeset(DtLCX_OPER_INTERCHANGE_CODESET,
commonCS,
NULL,
&ret_target);
DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
ret_target,
NULL,
&convName);
if ( ret_target )
free( ret_target );
if ( commonCS )
free( commonCS );
// Workaround for libDtHelp
// Case of no iconv name for a particular locale, eg. C,
// check for empty string.
if ( convName != NULL )
{
if ( strlen(convName) > 0 )
return convName;
else
free( convName );
}
return NULL;
}
// Return current locale's iconv name.
char *
RFCBodyPart::locToConvName()
{
char *ret_locale = NULL;
char *ret_lang = NULL;
char *ret_codeset = NULL;
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
setlocale(LC_CTYPE, NULL),
&ret_locale,
&ret_lang,
&ret_codeset);
if (ret_codeset) {
free(ret_codeset);
ret_codeset = NULL;
}
if (ret_lang) {
free(ret_lang);
ret_lang = NULL;
}
DtXlateStdToOpLocale(DtLCX_OPER_ICONV3,
ret_locale,
NULL,
&ret_codeset);
if (ret_locale)
free(ret_locale);
// Workaround for libDtHelp
// Case of no iconv name for a particular locale, eg. C,
// check for empty string.
if ( ret_codeset != NULL ) {
if (strlen(ret_codeset) > 0)
return ret_codeset;
else
free(ret_codeset);
}
return NULL;
}
// Return target codeset's iconv name.
char *
RFCBodyPart::targetConvName()
{
char *ret_locale = NULL;
char *ret_lang = NULL;
char *ret_codeset = NULL;
char *ret_target = NULL;
char *ret_convName = NULL;
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
setlocale(LC_CTYPE, NULL),
&ret_locale,
&ret_lang,
&ret_codeset);
DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
ret_locale,
NULL,
&ret_target);
// Or do I call csToConvName() here??
DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
ret_target,
NULL,
&ret_convName);
if (ret_locale)
free(ret_locale);
if (ret_lang)
free(ret_lang);
if (ret_codeset)
free(ret_codeset);
if (ret_target)
free(ret_target);
// Workaround for libDtHelp
// Case of no iconv name for a particular locale, eg. C,
// check for empty string.
if ( ret_convName != NULL )
{
if (strlen(ret_convName) > 0)
return ret_convName;
else
free(ret_convName);
}
return NULL;
}
// Return target codeset's MIME (tag) name.
char *
RFCBodyPart::targetTagName()
{
char *ret_locale = NULL;
char *ret_lang = NULL;
char *ret_codeset = NULL;
char *ret_target = NULL;
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
setlocale(LC_CTYPE, NULL),
&ret_locale,
&ret_lang,
&ret_codeset);
DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
ret_locale,
NULL,
&ret_target);
DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
ret_target,
NULL,
&ret_codeset);
if (ret_locale)
free(ret_locale);
if (ret_lang)
free(ret_lang);
if (ret_target)
free(ret_target);
return ret_codeset;
}
// Given a message text and codesets
// Convert message text from one codeset to another
// Return 1 if conversion is successful else return 0.
int
RFCBodyPart::csConvert(char **bp, unsigned long &bp_len, int free_bp,
char *from_cs, char *to_cs)
{
DtMailEnv error;
iconv_t cd;
size_t ileft = (size_t) bp_len, oleft = (size_t) bp_len, ret = 0;
#if defined(_aix) || defined(sun) || defined(CSRG_BASED)
const char *ip = (const char *) *bp;
#else
char *ip = *bp;
#endif
char *op = NULL;
char *op_start = NULL;
int mb_ret = 0;
size_t delta;
if ( *bp == NULL || **bp == '\0' || bp_len <= 0 )
return 0;
if ( to_cs == NULL || from_cs == NULL )
return 0;
if ( (cd = iconv_open(to_cs, from_cs)) == (iconv_t) -1 ) {
switch (errno) {
case EINVAL:
error.logError(DTM_FALSE,
"DtMail: Conversion from %s to %s is not supported.\n",
from_cs, to_cs);
break;
} // end of switch statement
return 0;
}
// Caller will set _must_free_body to DTM_TRUE if this routine
// succeeds. Then this space will be freed appropriately.
// Add 1 to buffer size for null terminator.
op_start = op = (char *)calloc((unsigned int) bp_len + 1, sizeof(char));
// When ileft finally reaches 0, the conversion still might not be
// complete. Here's why we also need to check for E2BIG: Let's
// say we're converting from eucJP to ISO-2022-JP, and there's just
// enough room in the output buffer for the last input character,
// but not enough room for the trailing "ESC ( B" (for switching
// back to ASCII). In that case, iconv() will convert the last
// input character, decrement ileft to zero, and then set errno to
// E2BIG to tell us that it still needs more room for the "ESC ( B".
errno = 0;
while ( ileft > 0 || errno == E2BIG ) {
errno = 0;
ret = iconv(cd, &ip, &ileft, &op, &oleft);
if ( ret == (size_t) -1 ) {
switch (errno) {
case E2BIG: // increase output buffer size
delta = ileft ? ileft : 3;
bp_len += delta;
op_start = (char *)realloc(
(char *)op_start,
(unsigned int) bp_len + 1);
op = op_start + bp_len - delta - oleft;
oleft += delta;
// realloc does not clear out unused space.
// Therefore, garbage shows up in output buffer.
memset(op, 0, oleft + 1);
break;
case EILSEQ: // input byte does not belong to input codeset
case EINVAL: // invalid input
mb_ret = mblen(ip, MB_LEN_MAX);
if ( (mb_ret > 0) && (oleft >= mb_ret) ) {
strncat(op_start, ip, mb_ret);
ip += mb_ret;
op += mb_ret;
oleft -= mb_ret;
ileft -= mb_ret;
mb_ret = 0;
} else {
// mb_ret is either 0 or -1 at this point,
// then skip one byte
// and try conversion again.
ip++;
ileft--;
}
break;
case EBADF: // bad conversion descriptor
break;
} // end of switch statement
}
} // end of while loop
iconv_close(cd);
// Is this necessary?? Is _body_decode_len == strlen(_body)??
// Or can _body_decode_len contain spaces??
// Check to see if a body had been allocated by prior decoding.
if (free_bp) {
free(*bp);
}
*bp = op_start;
bp_len = strlen(*bp);
return 1;
}
// End of For CHARSET