netscape-revival
lib/libmsg/ap_decod.c
/* -*- Mode: C; tab-width: 4 -*-
*
* apple_double_decode.c
*
* ---------------------
*
* Codes for decoding Apple Single/Double object parts.
*
* 05aug95 mym Created.
* 25sep95 mym Add support to write to binhex encoding on non-mac system.
*/
#include "msg.h"
#include "appledbl.h"
#include "ad_codes.h"
#include "m_binhex.h"
extern int MK_UNABLE_TO_OPEN_TMP_FILE;
extern int MK_MIME_ERROR_WRITING_FILE;
#ifndef APPLICATION_APPLEFILE
#define APPLICATION_APPLEFILE "application/applefile"
#endif
/*
** Static functions.
*/
PRIVATE int from_decoder(appledouble_decode_object* p_ap_decode_obj,
char *buff,
int buff_size,
uint32 *in_count);
PRIVATE int from_64(appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size);
PRIVATE int from_qp(appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size);
PRIVATE int from_none(appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size);
PRIVATE int decoder_seek(appledouble_decode_object* p_ap_decode_obj,
int seek_pos,
int start_pos);
/*
** fetch_a_line
** -------------
**
** get a line from the in stream..
*/
int fetch_a_line(
appledouble_decode_object* p_ap_decode_obj,
char *buff)
{
int i, left;
char *p, c = 0;
if (p_ap_decode_obj->s_leftover == 0 &&
p_ap_decode_obj->s_inbuff <= p_ap_decode_obj->pos_inbuff)
{
*buff = '\0';
return errEOB;
}
if (p_ap_decode_obj->s_leftover)
{
for (p = p_ap_decode_obj->b_leftover, i = p_ap_decode_obj->s_leftover; i>0; i--)
*buff++ = *p++;
p_ap_decode_obj->s_leftover = 0;
}
p = p_ap_decode_obj->inbuff + p_ap_decode_obj->pos_inbuff;
left = p_ap_decode_obj->s_inbuff - p_ap_decode_obj->pos_inbuff;
for (i = 0; i < left; )
{
c = *p++; i++;
if (c == CR && *p == LF)
{
p++; i++; /* make sure skip both LF & CR */
}
if (c == LF || c == CR)
break;
*buff++ = c;
}
p_ap_decode_obj->pos_inbuff += i;
if (i == left && c != LF && c != CR)
{
/*
** we meet the buff end before we can terminate the string,
** save the string to the left_over buff.
*/
p_ap_decode_obj->s_leftover = i;
for (p = p_ap_decode_obj->b_leftover; i>0; i--)
*p++ = *(buff-i);
return errEOB;
}
*buff = '\0';
return NOERR;
}
void parse_param(
char *p,
char **param, /* the param */
char **define, /* the defination. */
char **np) /* next position. */
{
while (*p == ' ' || *p == '\"' || *p == ';') p++;
*param = p;
while (*p != ' ' && *p != '=' ) p++;
if (*p == ' ')
*define = p+1;
else
*define = p+2;
while (*p && *p != ';') p++;
if (*p == ';')
*np = p + 1;
else
*np = p;
}
int ap_seek_part_start(
appledouble_decode_object* p_ap_decode_obj)
{
int status;
char newline[256];
while (1)
{
status = fetch_a_line(p_ap_decode_obj, newline);
if(status != NOERR)
break;
if (newline[0] == '\0' && p_ap_decode_obj->boundary0 != NULL)
return errDone;
if (!XP_STRNCASECMP(newline, "--", 2))
{
/* we meet the start seperator, copy it and it will be our boundary */
p_ap_decode_obj->boundary0 = XP_STRDUP(newline+2);
return errDone;
}
}
return status;
}
int ParseFileHeader(
appledouble_decode_object *p_ap_decode_obj)
{
int status;
int i;
char newline[256], *p;
char *param, *define;
while (1)
{
status = fetch_a_line(p_ap_decode_obj, newline);
if (newline[0] == '\0')
break; /* we get the end of a defination section. */
p = newline;
while (1)
{
parse_param(p, ¶m, &define, &p);
/*
** we only care about these params.
*/
if (!XP_STRNCASECMP(param, "Content-Type:", 13))
{
if (!XP_STRNCASECMP(define, MULTIPART_APPLEDOUBLE,
XP_STRLEN(MULTIPART_APPLEDOUBLE)) ||
!XP_STRNCASECMP(define, MULTIPART_HEADER_SET,
XP_STRLEN(MULTIPART_HEADER_SET)))
p_ap_decode_obj->messagetype = kAppleDouble;
else
p_ap_decode_obj->messagetype = kGeneralMine;
}
else if (!XP_STRNCASECMP(param, "boundary=", 9))
{
for (i = 0; *define && *define != '\"'; )
p_ap_decode_obj->boundary0[i++] = *define++;
p_ap_decode_obj->boundary0[i] = '\0';
}
else if (!XP_STRNCASECMP(param, "Content-Disposition:", 20))
{
if (!XP_STRNCASECMP(define, "inline", 5))
p_ap_decode_obj->deposition = kInline;
else
p_ap_decode_obj->deposition = kDontCare;
}
else if (!XP_STRNCASECMP(param, "filename=", 9))
{
for (i = 0, p=define; *p && *p != '\"'; )
p_ap_decode_obj->fname[i++] = *p++;
p_ap_decode_obj->fname[i] = '\0';
}
if (*p == '\0')
break;
}
}
return NOERR;
}
int ap_seek_to_boundary(
appledouble_decode_object *p_ap_decode_obj,
XP_Bool firstime)
{
int status = NOERR;
char buff[256];
while (status == NOERR)
{
status = fetch_a_line(p_ap_decode_obj, buff);
if (status != NOERR)
break;
if ((!XP_STRNCASECMP(buff, "--", 2) &&
!XP_STRNCASECMP( buff+2,
p_ap_decode_obj->boundary0,
XP_STRLEN(p_ap_decode_obj->boundary0)))
||!XP_STRNCASECMP( buff,
p_ap_decode_obj->boundary0,
XP_STRLEN(p_ap_decode_obj->boundary0)))
{
TRACEMSG(("Found boundary: %s", p_ap_decode_obj->boundary0));
status = errDone;
break;
}
}
if (firstime && status == errEOB)
status = NOERR; /* so we can do it again. */
return status;
}
int ap_parse_header(
appledouble_decode_object *p_ap_decode_obj,
XP_Bool firstime)
{
int status, i;
char newline[256], *p;
char *param, *define;
if (firstime)
{
/* do the clean ups. */
p_ap_decode_obj->encoding = kEncodeNone;
p_ap_decode_obj->which_part = kFinishing;
}
while (1)
{
status = fetch_a_line(p_ap_decode_obj, newline);
if (status != NOERR)
return status; /* a possible end of buff happened. */
if (newline[0] == '\0')
break; /* we get the end of a defination section. */
p = newline;
while (1)
{
parse_param(p, ¶m, &define, &p);
/*
** we only care about these params.
*/
if (!XP_STRNCASECMP(param, "Content-Type:", 13))
{
if (!XP_STRNCASECMP(define, APPLICATION_APPLEFILE, 21))
p_ap_decode_obj->which_part = kHeaderPortion;
else
{
p_ap_decode_obj->which_part = kDataPortion;
if (!XP_STRNCASECMP(define, "text/plain", 10))
p_ap_decode_obj->is_binary = FALSE;
else
p_ap_decode_obj->is_binary = TRUE;
}
}
else if (!XP_STRNCASECMP(param, "Content-Transfer-Encoding:",26))
{
if (!XP_STRNCASECMP(define, "base64", 6))
p_ap_decode_obj->encoding = kEncodeBase64;
else if (!XP_STRNCASECMP(define, "quoted-printable", 16))
p_ap_decode_obj->encoding = kEncodeQP;
else
p_ap_decode_obj->encoding = kEncodeNone;
}
else if (!XP_STRNCASECMP(param, "Content-Disposition:", 20))
{
if (!XP_STRNCASECMP(define, "inline", 5))
p_ap_decode_obj->deposition = kInline;
else
p_ap_decode_obj->deposition = kDontCare;
}
else if (!XP_STRNCASECMP(param, "filename=", 9))
{
if (p_ap_decode_obj->fname[0] == '\0')
{
for (i = 0; *define && *define != '\"'; )
p_ap_decode_obj->fname[i++] = *define++;
p_ap_decode_obj->fname[i] = '\0';
}
}
if (*p == '\0')
break;
}
}
return errDone;
}
#if 0
PRIVATE void
copy_filename(MWContext* context, char* newFile, void* closure)
{
XP_STRCPY(closure, newFile);
}
#endif
/*
** decode the head portion.
*/
int ap_decode_file_infor(appledouble_decode_object *p_ap_decode_obj)
{
ap_header head;
ap_entry entries[NUM_ENTRIES + 1];
int i, j;
int st_pt;
uint32 in_count;
int status;
char name[64];
st_pt = p_ap_decode_obj->pos_inbuff;
/*
** Read & verify header
*/
status = from_decoder(
p_ap_decode_obj,
(char *) &head,
26, /* sizeof (head), */
&in_count);
if (status != NOERR)
return status;
if (p_ap_decode_obj->is_apple_single)
{
if (ntohl(head.magic) != APPLESINGLE_MAGIC)
return errVersion;
}
else
{
if(ntohl(head.magic) != APPLEDOUBLE_MAGIC)
return errVersion;
}
if (ntohl(head.version) != VERSION)
{
return errVersion;
}
/* read entries */
head.entries = ntohs(head.entries);
for (i = j = 0; i < head.entries; ++i)
{
status = from_decoder(
p_ap_decode_obj,
(char *) (entries + j),
sizeof (ap_entry),
&in_count);
if (status != NOERR)
return errDecoding;
/*
** correct the byte order now.
*/
entries[j].id = ntohl(entries[j].id);
entries[j].offset = ntohl(entries[j].offset);
entries[j].length = ntohl(entries[j].length);
/*
** only care about these entries...
*/
if (j < NUM_ENTRIES)
switch (entries[j].id)
{
case ENT_NAME:
case ENT_FINFO:
case ENT_DATES:
case ENT_COMMENT:
case ENT_RFORK:
case ENT_DFORK:
++j;
break;
}
}
/* read name */
for (i = 0; i < j && entries[i].id != ENT_NAME; ++i)
;
if (i == j)
return errDecoding;
status = decoder_seek(
p_ap_decode_obj,
entries[i].offset,
st_pt);
if (status != NOERR)
return status;
if (entries[i].length > 63)
entries[i].length = 63;
status = from_decoder(
p_ap_decode_obj,
p_ap_decode_obj->fname,
entries[i].length,
&in_count);
if (status != NOERR)
return status;
p_ap_decode_obj->fname[in_count] = '\0';
/* P_String version of the file name. */
XP_STRCPY((char *)name+1, p_ap_decode_obj->fname);
name[0] = in_count;
if (p_ap_decode_obj->write_as_binhex)
{
/*
** fill out the simple the binhex head.
*/
binhex_header head;
myFInfo myFInfo;
status = (*p_ap_decode_obj->binhex_stream->put_block)
(p_ap_decode_obj->binhex_stream->data_object,
name,
name[0] + 2);
if (status != NOERR)
return status;
/* get finder info */
for (i = 0; i < j && entries[i].id != ENT_FINFO; ++i)
;
if (i < j)
{
status = decoder_seek(p_ap_decode_obj,
entries[i].offset,
st_pt);
if (status != NOERR)
return status;
status = from_decoder(p_ap_decode_obj,
(char *) &myFInfo,
sizeof (myFInfo),
&in_count);
if (status != NOERR)
return status;
}
head.type = myFInfo.fdType;
head.creator = myFInfo.fdCreator;
head.flags = myFInfo.fdFlags;
for (i = 0; i < j && entries[i].id != ENT_DFORK; ++i)
;
if (i < j && entries[i].length != 0)
{
head.dlen = entries[i].length; /* set the data fork length */
}
else
{
head.dlen = 0;
}
for (i = 0; i < j && entries[i].id != ENT_RFORK; ++i)
;
if (i < j && entries[i].length != 0)
{
head.rlen = entries[i].length; /* set the resource fork length */
}
else
{
head.rlen = 0;
}
/*
** and the dlen, rlen is in the host byte order, correct it if needed ...
*/
head.dlen = htonl(head.dlen);
head.rlen = htonl(head.rlen);
/*
** then encode them in binhex.
*/
status = (*p_ap_decode_obj->binhex_stream->put_block)
(p_ap_decode_obj->binhex_stream->data_object,
(char*)&head,
sizeof(binhex_header));
if (status != NOERR)
return status;
/*
** after we have done with the header, end the binhex header part.
*/
status = (*p_ap_decode_obj->binhex_stream->put_block)
(p_ap_decode_obj->binhex_stream->data_object,
NULL,
0);
}
else
{
#ifdef XP_MAC
ap_dates dates;
HFileInfo *fpb;
CInfoPBRec cipbr;
IOParam vinfo;
GetVolParmsInfoBuffer vp;
DTPBRec dtp;
char comment[256];
char filename[64];
StandardFileReply reply;
XP_STRCPY(filename+1, p_ap_decode_obj->fname);
filename[0] = XP_STRLEN(p_ap_decode_obj->fname);
/* CALL fe_PromtGetFileName get a new name */
#if 0
if (FE_PromptForFileName(NULL,
"Save decoded file as:",
filename,
FALSE,
FALSE,
copy_filename,
filename) == -1)
{
return -1;
}
/*
** Convert it to fspec now.
*/
if (my_FSSpecFromPathname(filename, &fspec) != NOERR)
{
return -1;
}
#endif
StandardPutFile("\pSave decoded file as:",
(const unsigned char*)filename,
&reply);
if (!reply.sfGood)
{
return errUsrCancel;
}
XP_MEMCPY(p_ap_decode_obj->fname,
reply.sfFile.name+1,
*(reply.sfFile.name)+1);
p_ap_decode_obj->fname[*(reply.sfFile.name)] = '\0';
p_ap_decode_obj->vRefNum = reply.sfFile.vRefNum;
p_ap_decode_obj->dirId = reply.sfFile.parID;
/* create & get info for file */
HDelete(reply.sfFile.vRefNum,
reply.sfFile.parID,
reply.sfFile.name);
#define DONT_CARE_TYPE 0x3f3f3f3f
if (HCreate(reply.sfFile.vRefNum,
reply.sfFile.parID,
reply.sfFile.name,
DONT_CARE_TYPE,
DONT_CARE_TYPE) != NOERR)
{
return errFileOpen;
}
fpb = (HFileInfo *) &cipbr;
fpb->ioVRefNum = reply.sfFile.vRefNum;
fpb->ioDirID = reply.sfFile.parID;
fpb->ioNamePtr = reply.sfFile.name;
fpb->ioFDirIndex = 0;
PBGetCatInfoSync(&cipbr);
/* get finder info */
for (i = 0; i < j && entries[i].id != ENT_FINFO; ++i)
;
if (i < j)
{
status = decoder_seek(p_ap_decode_obj,
entries[i].offset,
st_pt);
if (status != NOERR)
return status;
status = from_decoder(p_ap_decode_obj,
(char *) &fpb->ioFlFndrInfo,
sizeof (FInfo),
&in_count);
if (status != NOERR)
return status;
status = from_decoder(p_ap_decode_obj,
(char *) &fpb->ioFlXFndrInfo,
sizeof (FXInfo),
&in_count);
if (status != NOERR)
return status;
fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */
}
/*
** get file date info
*/
for (i = 0; i < j && entries[i].id != ENT_DATES; ++i)
;
if (i < j)
{
status = decoder_seek(p_ap_decode_obj,
entries[i].offset,
st_pt);
if (status != NOERR)
return status;
status = from_decoder(p_ap_decode_obj,
(char *) &dates,
sizeof (dates),
&in_count);
if (status != NOERR)
return status;
fpb->ioFlCrDat = dates.create - CONVERT_TIME;
fpb->ioFlMdDat = dates.modify - CONVERT_TIME;
fpb->ioFlBkDat = dates.backup - CONVERT_TIME;
}
/*
** update info
*/
fpb->ioDirID = fpb->ioFlParID;
PBSetCatInfoSync(&cipbr);
/*
** get comment & save it
*/
for (i = 0; i < j && entries[i].id != ENT_COMMENT; ++i)
;
if (i < j && entries[i].length != 0)
{
memset((void *) &vinfo, '\0', sizeof (vinfo));
vinfo.ioVRefNum = fpb->ioVRefNum;
vinfo.ioBuffer = (Ptr) &vp;
vinfo.ioReqCount = sizeof (vp);
if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == NOERR &&
((vp.vMAttrib >> bHasDesktopMgr) & 1))
{
memset((void *) &dtp, '\0', sizeof (dtp));
dtp.ioVRefNum = fpb->ioVRefNum;
if (PBDTGetPath(&dtp) == NOERR)
{
if (entries[i].length > 255)
entries[i].length = 255;
status = decoder_seek(p_ap_decode_obj,
entries[i].offset,
st_pt);
if (status != NOERR)
return status;
status = from_decoder(p_ap_decode_obj,
comment,
entries[i].length,
&in_count);
if (status != NOERR)
return status;
dtp.ioDTBuffer = (Ptr) comment;
dtp.ioNamePtr = fpb->ioNamePtr;
dtp.ioDirID = fpb->ioDirID;
dtp.ioDTReqCount = entries[i].length;
if (PBDTSetCommentSync(&dtp) == NOERR)
{
PBDTFlushSync(&dtp);
}
}
}
}
#else
/*
** in non-mac system, creating a data fork file will be it.
*/
#endif
}
/*
** Get the size the data fork.
*/
for (i = 0; i < j && entries[i].id != ENT_DFORK; ++i)
;
if (i < j && entries[i].length != 0)
{
p_ap_decode_obj->dksize = entries[i].length;
}
else
p_ap_decode_obj->dksize = 0;
/*
** Seek to the position to the start of the resource place.
*/
for (i = 0; i < j && entries[i].id != ENT_RFORK; ++i)
;
if (i < j && entries[i].length != 0)
{
status = decoder_seek(p_ap_decode_obj,
entries[i].offset,
st_pt);
p_ap_decode_obj->rksize = entries[i].length;
}
else
p_ap_decode_obj->rksize = 0;
/*
** Prepare a tempfile to hold the resource fork decoded by the decoder,
** because in binhex, resource fork appears after the data fork!!!
*/
if (p_ap_decode_obj->write_as_binhex)
{
if (p_ap_decode_obj->rksize != 0)
{
/* we need a temp file to hold all the resource data, because the */
p_ap_decode_obj->tmpfname
= WH_TempName(xpTemporary, "apmail");
p_ap_decode_obj->tmpfd
= XP_FileOpen(p_ap_decode_obj->tmpfname,
xpTemporary,
XP_FILE_TRUNCATE_BIN);
if (p_ap_decode_obj->tmpfd == NULL)
return errFileOpen;
}
}
return NOERR;
}
/*
** ap_decode_process_header
**
**
*/
int ap_decode_process_header(
appledouble_decode_object* p_ap_decode_obj,
XP_Bool firstime)
{
uint32 in_count;
int status = NOERR;
char wr_buff[1024];
if (firstime)
{
status = ap_decode_file_infor(p_ap_decode_obj);
if (status != NOERR)
return status;
if (p_ap_decode_obj->rksize > 0)
{
#ifdef XP_MAC
if(!p_ap_decode_obj->write_as_binhex)
{
Str63 fname;
short refNum;
fname[0] = XP_STRLEN(p_ap_decode_obj->fname);
XP_STRCPY((char*)fname+1, p_ap_decode_obj->fname);
if (HOpenRF(p_ap_decode_obj->vRefNum,
p_ap_decode_obj->dirId,
fname,
fsWrPerm,
&refNum) != NOERR)
{
return (errFileOpen);
}
p_ap_decode_obj->fileId = refNum;
}
#endif
}
else
{
status = errDone;
}
}
/*
** Time to continue decoding all the resource data.
*/
while (status == NOERR && p_ap_decode_obj->rksize > 0)
{
in_count = MIN(1024, p_ap_decode_obj->rksize);
status = from_decoder(p_ap_decode_obj,
wr_buff,
in_count,
&in_count);
if (p_ap_decode_obj->write_as_binhex)
{
/*
** Write to the temp file first, because the resource fork appears after
** the data fork in the binhex encoding.
*/
if (XP_FileWrite(wr_buff,
in_count,
p_ap_decode_obj->tmpfd) != in_count)
{
status = errFileWrite;
break;
}
p_ap_decode_obj->data_size += in_count;
}
else
{
#ifdef XP_MAC
long howMuch = in_count;
if (FSWrite(p_ap_decode_obj->fileId,
&howMuch,
wr_buff) != NOERR)
{
status = errFileWrite;
break;
}
#else
/* ====== Write nothing in a non mac file system ============ */
#endif
}
p_ap_decode_obj->rksize -= in_count;
}
if (p_ap_decode_obj->rksize <= 0 || status == errEOP)
{
if (p_ap_decode_obj->write_as_binhex)
{
/*
** No more resource data, but we are not done
** with tempfile yet, just seek back to the start point,
** -- ready for a readback later
*/
if (p_ap_decode_obj->tmpfd)
XP_FileSeek(p_ap_decode_obj->tmpfd, 0L, 1);
}
#ifdef XP_MAC
else if (p_ap_decode_obj->fileId) /* close the resource fork of the macfile */
{
FSClose(p_ap_decode_obj->fileId);
p_ap_decode_obj->fileId = 0;
}
#endif
if (!p_ap_decode_obj->is_apple_single)
{
p_ap_decode_obj->left = 0;
p_ap_decode_obj->state64 = 0;
}
status = errDone;
}
return status;
}
int ap_decode_process_data(
appledouble_decode_object* p_ap_decode_obj,
XP_Bool firstime)
{
char wr_buff[1024];
uint32 in_count;
int status = NOERR;
int retval = NOERR;
if (firstime)
{
if (!p_ap_decode_obj->write_as_binhex)
{
#ifdef XP_MAC
char *filename;
FSSpec fspec;
fspec.vRefNum = p_ap_decode_obj->vRefNum;
fspec.parID = p_ap_decode_obj->dirId;
fspec.name[0] = XP_STRLEN(p_ap_decode_obj->fname);
XP_STRCPY((char*)fspec.name+1, p_ap_decode_obj->fname);
filename = my_PathnameFromFSSpec(&fspec);
if (p_ap_decode_obj->is_binary)
p_ap_decode_obj->fd =
XP_FileOpen(filename+7, xpURL, XP_FILE_TRUNCATE_BIN);
else
p_ap_decode_obj->fd =
XP_FileOpen(filename+7, xpURL, XP_FILE_TRUNCATE);
XP_FREE(filename);
#else
if (p_ap_decode_obj->is_binary)
p_ap_decode_obj->fd =
XP_FileOpen(p_ap_decode_obj->fname, xpURL, XP_FILE_TRUNCATE_BIN);
else
p_ap_decode_obj->fd =
XP_FileOpen(p_ap_decode_obj->fname, xpURL, XP_FILE_TRUNCATE);
#endif
}
else
{
; /* == don't need do anything to binhex stream, it is ready already == */
}
}
if (p_ap_decode_obj->is_apple_single &&
p_ap_decode_obj->dksize == 0)
{
/* if no data in apple single, we already done then. */
status = errDone;
}
while (status == NOERR && retval == NOERR)
{
retval = from_decoder(p_ap_decode_obj,
wr_buff,
1024,
&in_count);
if (p_ap_decode_obj->is_apple_single) /* we know the data fork size in */
p_ap_decode_obj->dksize -= in_count; /* apple single, use it to decide the end */
if (p_ap_decode_obj->write_as_binhex)
status = (*p_ap_decode_obj->binhex_stream->put_block)
(p_ap_decode_obj->binhex_stream->data_object,
wr_buff,
in_count);
else
status = XP_FileWrite(wr_buff,
in_count,
p_ap_decode_obj->fd) == in_count ? NOERR : errFileWrite;
if (retval == errEOP || /* for apple double, we meet the boundary */
( p_ap_decode_obj->is_apple_single &&
p_ap_decode_obj->dksize <= 0)) /* for apple single, we know it is ending */
{
status = errDone;
break;
}
}
if (status == errDone)
{
if (p_ap_decode_obj->write_as_binhex)
{
/* CALL with data == NULL && size == 0 to end a part object in binhex encoding */
status = (*p_ap_decode_obj->binhex_stream->put_block)
(p_ap_decode_obj->binhex_stream->data_object,
NULL,
0);
if (status != NOERR)
return status;
}
else if (p_ap_decode_obj->fd)
{
XP_FileClose(p_ap_decode_obj->fd);
p_ap_decode_obj->fd = 0;
}
status = errDone;
}
return status;
}
/*
** Fill the data from the decoder stream.
*/
PRIVATE int from_decoder(
appledouble_decode_object* p_ap_decode_obj,
char *buff,
int buff_size,
uint32 *in_count)
{
int status;
switch (p_ap_decode_obj->encoding)
{
case kEncodeQP:
status = from_qp(p_ap_decode_obj,
buff,
buff_size,
in_count);
break;
case kEncodeBase64:
status = from_64(p_ap_decode_obj,
buff,
buff_size,
in_count);
break;
case kEncodeNone:
default:
status = from_none(p_ap_decode_obj,
buff,
buff_size,
in_count);
break;
}
return status;
}
/*
** decoder_seek
**
** simulate a stream seeking on the encoded stream.
*/
PRIVATE int decoder_seek(
appledouble_decode_object* p_ap_decode_obj,
int seek_pos,
int start_pos)
{
char tmp[1024];
int status = NOERR;
uint32 in_count;
/*
** force a reset on the in buffer.
*/
p_ap_decode_obj->state64 = 0;
p_ap_decode_obj->left = 0;
p_ap_decode_obj->pos_inbuff = start_pos;
while (seek_pos > 0)
{
status = from_decoder(p_ap_decode_obj,
tmp,
MIN(1024, seek_pos),
&in_count);
if (status != NOERR)
break;
seek_pos -= in_count;
}
return status;
}
#define XX 127
/*
* Table for decoding base64
*/
static char index_64[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};
#define CHAR64(c) (index_64[(unsigned char)(c)])
#define EndOfBuff(p) ((p)->pos_inbuff >= (p)->s_inbuff)
PRIVATE int fetch_next_char_64(
appledouble_decode_object* p_ap_decode_obj)
{
char c;
c = p_ap_decode_obj->inbuff[p_ap_decode_obj->pos_inbuff++];
if (c == '-')
--p_ap_decode_obj->pos_inbuff; /* put back */
while (c == LF || c == CR) /* skip the CR character. */
{
if (EndOfBuff(p_ap_decode_obj))
{
c = 0;
break;
}
c = p_ap_decode_obj->inbuff[p_ap_decode_obj->pos_inbuff++];
if (c == '-')
{
--p_ap_decode_obj->pos_inbuff; /* put back */
}
}
return (int)c;
}
PRIVATE int from_64(
appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size)
{
int i, j, buf[4];
int c1, c2, c3, c4;
(*real_size) = 0;
/*
** decode 4 by 4s characters
*/
for (i = p_ap_decode_obj->state64; i<4; i++)
{
if (EndOfBuff(p_ap_decode_obj))
{
p_ap_decode_obj->state64 = i;
break;
}
if ((p_ap_decode_obj->c[i] = fetch_next_char_64(p_ap_decode_obj)) == 0)
break;
}
if (i != 4)
{
/*
** not enough data to fill the decode buff.
*/
return errEOB; /* end of buff */
}
while (size > 0)
{
c1 = p_ap_decode_obj->c[0];
c2 = p_ap_decode_obj->c[1];
c3 = p_ap_decode_obj->c[2];
c4 = p_ap_decode_obj->c[3];
if (c1 == '-' || c2 == '-' || c3 == '-' || c4 == '-')
{
return errEOP; /* we meet the part boundary. */
}
if (c1 == '=' || c2 == '=')
{
return errDecoding;
}
c1 = CHAR64(c1);
c2 = CHAR64(c2);
buf[0] = ((c1<<2) | ((c2&0x30)>>4));
if (c3 != '=')
{
c3 = CHAR64(c3);
buf[1] = (((c2&0x0F) << 4) | ((c3&0x3C) >> 2));
if (c4 != '=')
{
c4 = CHAR64(c4);
buf[2] = (((c3&0x03) << 6) | c4);
}
else
{
if (p_ap_decode_obj->left == 0)
{
*buff++ = buf[0]; (*real_size)++;
}
*buff++ = buf[1]; (*real_size)++;
return errEOP;
}
}
else
{
*buff++ = *buf;
(*real_size)++;
return errEOP; /* we meet the the end */
}
/*
** copy the content
*/
for (j = p_ap_decode_obj->left; j<3; )
{
*buff++ = buf[j++];
(*real_size)++;
if (--size <= 0)
break;
}
p_ap_decode_obj->left = j % 3;
if (size <=0)
{
if (j == 3)
p_ap_decode_obj->state64 = 0; /* See if we used up all data, */
/* ifnot, keep the data, */
/* we need it for next time. */
else
p_ap_decode_obj->state64 = 4;
break;
}
/*
** fetch the next 4 character group.
*/
for (i = 0; i < 4; i++)
{
if (EndOfBuff(p_ap_decode_obj))
break;
if ((p_ap_decode_obj->c[i] = fetch_next_char_64(p_ap_decode_obj)) == 0)
break;
}
p_ap_decode_obj->state64 = i % 4;
if (i != 4)
break; /* some kind of end of buff met.*/
}
/*
** decide the size and status.
*/
return EndOfBuff(p_ap_decode_obj) ? errEOB : NOERR;
}
/*
* Table for decoding hexadecimal in quoted-printable
*/
static char index_hex[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};
#define HEXCHAR(c) (index_hex[(unsigned char)(c)])
#define NEXT_CHAR(p) ((int)((p)->inbuff[(p)->pos_inbuff++]))
/*
** quoted printable decode, as defined in RFC 1521, page18 - 20
*/
PRIVATE int from_qp(
appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size)
{
char c;
int c1, c2;
*real_size = 0;
if (p_ap_decode_obj->c[0] == '=')
{
/*
** continue with the last time's left over.
*/
p_ap_decode_obj->c[0] = 0;
c1 = p_ap_decode_obj->c[1]; p_ap_decode_obj->c[1] = 0;
if ( c1 == 0)
{
c1 = NEXT_CHAR(p_ap_decode_obj);
c2 = NEXT_CHAR(p_ap_decode_obj);
}
else
{
c2 = NEXT_CHAR(p_ap_decode_obj);
}
c = HEXCHAR(c1) << 4 | HEXCHAR(c2);
size --;
*buff ++ = c;
(*real_size) ++;
}
/*
** Then start to work on the new data
*/
while (size > 0)
{
if (EndOfBuff(p_ap_decode_obj))
break;
c1 = NEXT_CHAR(p_ap_decode_obj);
if (c1 == '=')
{
if (EndOfBuff(p_ap_decode_obj))
{
p_ap_decode_obj->c[0] = c1;
break;
}
c1 = NEXT_CHAR(p_ap_decode_obj);
if (c1 != '\n')
{
/*
** Rule #2
*/
c1 = HEXCHAR(c1);
if (EndOfBuff(p_ap_decode_obj))
{
p_ap_decode_obj->c[0] = '=';
p_ap_decode_obj->c[1] = c1;
break;
}
c2 = NEXT_CHAR(p_ap_decode_obj);
c2 = HEXCHAR(c2);
c = c1 << 4 | c2;
if (c != '\r')
{
size --;
*buff++ = c;
(*real_size)++;
}
}
else
{
/* ignore the line break -- soft line break, rule #5 */
}
}
else
{
if (c1 == CR || c1 == LF)
{
if (p_ap_decode_obj->pos_inbuff < p_ap_decode_obj->s_inbuff)
{
if (p_ap_decode_obj->boundary0 &&
(!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff,
"--",
2)
&&
!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff+2,
p_ap_decode_obj->boundary0,
XP_STRLEN(p_ap_decode_obj->boundary0))))
{
return errEOP;
}
}
}
/*
** general 8bits case, Rule #1
*/
size -- ;
*buff++ = c1;
(*real_size) ++;
}
}
return EndOfBuff(p_ap_decode_obj) ? errEOB : NOERR;
}
/*
** from_none
**
** plain text transfer.
*/
PRIVATE int from_none(
appledouble_decode_object* p_ap_decode_obj,
char *buff,
int size,
uint32 *real_size)
{
char c;
int i, status = NOERR;
int left = p_ap_decode_obj->s_inbuff - p_ap_decode_obj->pos_inbuff;
int total = MIN(size, left);
for (i = 0; i < total; i++)
{
*buff ++ = c = NEXT_CHAR(p_ap_decode_obj);
if (c == CR || c == LF)
{
/* make sure the next thing is not a boundary string */
if (p_ap_decode_obj->pos_inbuff < p_ap_decode_obj->s_inbuff)
{
if (p_ap_decode_obj->boundary0 &&
(!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff,
"--",
2)
&&
!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff+2,
p_ap_decode_obj->boundary0,
XP_STRLEN(p_ap_decode_obj->boundary0))))
{
status = errEOP;
break;
}
}
}
}
*real_size = i;
if (status == NOERR)
status = (left == i) ? errEOB : status;
return status;
}