📄 memotransfer.c
字号:
/******************************************************************************
*
* Copyright (c) 1995-2003 PalmSource, Inc. All rights reserved.
*
* File: MemoTransfer.c
*
* Release: Palm OS 5 SDK (68K) R3.
*
* Description:
* Memo Book routines to transfer records.
*
*****************************************************************************/
#include <StringMgr.h>
#include <TextMgr.h>
#include <UIResources.h>
#include <Category.h>
#include <Form.h>
#include <ErrorMgr.h>
#include <TraceMgr.h>
#include <PalmLocale.h>
#include <PalmUtils.h>
#include "MemoDB.h"
#include "MemoMain.h"
#include "MemoRsc.h"
/***********************************************************************
*
* Internal Constants
*
***********************************************************************/
#define identifierLengthMax 40
#define mimeVersionString "MIME-Version: 1.0\015\012"
#define mimeMultipartString "Content-type: multipart/mixed;"
#define mimeBoundaryString "boundary="
#define memoSuffix ("." memoExtension)
#define simpleBoundary "simple boundary"
#define delimiter "--" simpleBoundary
#define crlf "\015\012"
#define importBufferMaxLength 80
#define stringZLen -1 // pass to WriteFunc to calculate strlen
// Stream interface to exgsockets to optimize performance
#define maxStreamBuf 512 // MUST BE LARGER than importBufferMaxLength
typedef struct StreamType {
ExgSocketPtr socket;
UInt16 pos;
UInt16 len;
UInt16 bufSize;
Char buf[maxStreamBuf];
} StreamType;
/***********************************************************************
*
* Internal Functions
*
***********************************************************************/
static void PrvMemoImportMimeCleanup(DmOpenRef dbP, UInt32 firstRecordID, void* inputStream, UInt16 numRecordsReceived, UInt16* numRecordsReceivedP);
static void PrvStreamInit(StreamType *streamP, ExgSocketPtr exgSocketP);
static void PrvStreamFlush(StreamType *streamP);
static UInt32 PrvStreamWrite(StreamType *streamP, const Char * stringP, Int32 length, Err *errP);
static UInt32 PrvStreamRead(StreamType * streamP, Char *bufP, UInt32 length, Err *errP);
static ExgSocketPtr PrvStreamSocket(StreamType *streamP);
static UInt32 PrvReadFunction(const void * stream, Char * bufferP, UInt32 length);
static UInt32 PrvWriteFunction(void * stream, const Char * const bufferP, Int32 length);
static void PrvTransferCleanFileName(Char* ioFileName);
static void PrvSetDescriptionAndFilename(Char * textP, Char **descriptionPP, MemHandle *descriptionHP, Char **filenamePP, MemHandle *filenameHP, const Char * const prefix);
static Err PrvMemoSendRecordTryCatch (DmOpenRef dbP, Int16 recordNum, MemoDBRecordPtr recordP, ExgSocketPtr exgSocketP);
static Err PrvMemoSendCategoryTryCatch (DmOpenRef dbP, UInt16 categoryNum, ExgSocketPtr exgSocketP, UInt16 index);
static void PrvMemoSetGoToParams (DmOpenRef dbP, ExgSocketPtr exgSocketP, UInt32 uniqueID);
static Err PrvReadThroughCRLF(ReadFunctionF inputFunc, void * inputStreamP, Char * bufferP, UInt16 * bufferLengthP);
static void PrvMemoImportFinishRecord(DmOpenRef dbP, UInt16 indexNew, MemHandle *newRecordHPtr, UInt16 *newRecordSizePtr, void * inputStream);
/************************************************************
*
* FUNCTION: MemoImportMime
*
* DESCRIPTION: Import a Mime record.
*
* PARAMETERS:
* dbP - pointer to the database to add the record to
* inputStream - pointer to where to import the record from
* inputFunc - function to get input from the stream
* obeyUniqueIDs - true to obey any unique ids if possible
* beginAlreadyRead - whether the begin statement has been read
* numRecordsRecievedP - number of records received
*
* RETURNS: true if the input was read
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* rsf 4/24/97 Initial Revision
* bob 01/26/98 re-wrote MIME parser part to get delimiters right
* grant 6/25/99 Return count of records received in numRecordsReceivedP.
* kwk 06/25/99 Moved return out of ErrTry block.
* FPa 11/22/00 Fixed ErrTry/Catch/Throw problems
*
*************************************************************/
extern Boolean
MemoImportMime(DmOpenRef dbR, void * inputStream, ReadFunctionF inputFunc,
Boolean UNUSED_PARAM(obeyUniqueIDs), Boolean UNUSED_PARAM(beginAlreadyRead), UInt16 *numRecordsReceivedP, Char* descriptionP, UInt16 descriptionSize)
{
char *c;
char boundaryString[69+2] = "";
MemHandle newRecordH = NULL;
Char * newRecordP;
MemHandle newHandle; // Used to follow resized records which move
UInt16 indexNew = dmMaxRecordIndex;
DmOpenRef dbP = dbR;
Err err = 0;
Char buffer[importBufferMaxLength + 1];
UInt16 bufferLength = 0;
UInt16 charsRead;
UInt16 charsToWrite;
UInt16 newRecordSize = 0;
Char * nextCrChr;
int addCr;
Char * boundaryP;
Char * boundaryEndP;
UInt16 numRecordsReceived = 0;
UInt32 firstRecordID = 0;
// Keep the buffer always null terminated so we can use string functions on it.
buffer[importBufferMaxLength] = nullChr;
// Read chars into the buffer
charsRead = inputFunc( inputStream, buffer, importBufferMaxLength - bufferLength);
bufferLength += charsRead;
buffer[bufferLength] = nullChr;
if (charsRead == 0)
{
*numRecordsReceivedP = 0;
return false;
}
// An error happens usually due to no memory. It's easier just to
// catch the error. If an error happens, we remove the last record.
// Then we throw a second time so the caller receives it and displays a message.
ErrTry
{
// MIME start, find MIME ID and version
if (StrNCompareAscii(buffer, mimeVersionString, StrLen(mimeVersionString)) == 0)
{
// Remove the MIME header
MemMove(buffer, &buffer[StrLen(mimeVersionString)], bufferLength - StrLen(mimeVersionString));
bufferLength -= StrLen(mimeVersionString);
// Read chars into the buffer
charsRead = inputFunc( inputStream, &buffer[bufferLength], importBufferMaxLength - bufferLength);
bufferLength += charsRead;
buffer[bufferLength] = nullChr;
// scan header for a multi-part identifier
// skip anything else until we get an entirely blank line
do {
if (StrNCompareAscii(buffer, mimeMultipartString, StrLen(mimeMultipartString)) == 0)
{
// found a multi-part header, parse out the boundary string
// PREVIEW Aba: Here we know that the memo is multipart => several memos
if (descriptionP)
{
MemHandle headerStringH;
Char* headerStringP;
headerStringH = DmGetResource(strRsc, FindMemoHeaderStr);
headerStringP = MemHandleLock(headerStringH);
StrCopy(descriptionP, headerStringP);
MemHandleUnlock(headerStringH);
DmReleaseResource(headerStringH);
return true;
}
boundaryP = StrStr(buffer, mimeBoundaryString);
boundaryP += StrLen(mimeBoundaryString);
// Remove the boundary stuff so we can read in more into the buffer
MemMove(buffer, boundaryP, &buffer[bufferLength] - boundaryP);
bufferLength = (&buffer[bufferLength] - boundaryP);
// Read chars into the buffer
charsRead = inputFunc( inputStream, &buffer[bufferLength], importBufferMaxLength - bufferLength);
bufferLength += charsRead;
buffer[bufferLength] = nullChr;
boundaryP = buffer;
if (*boundaryP == '"')
{
boundaryP++;
boundaryEndP = StrChr(boundaryP, '"');
}
else
{
boundaryEndP = StrChr(boundaryP, crChr);
}
if (boundaryEndP == NULL)
{
ErrThrow(exgErrBadData);
}
boundaryString[0] = '-';
boundaryString[1] = '-';
MemMove(&boundaryString[2], boundaryP, boundaryEndP - boundaryP);
boundaryString[boundaryEndP - boundaryP + 2] = nullChr;
c = StrChr(boundaryEndP, crChr);
if (c == NULL)
{
ErrThrow(exgErrBadData);
}
c += sizeOf7BitChar(crChr) + sizeOf7BitChar(linefeedChr);
// Remove the boundary stuff so we can read in more into the buffer
MemMove(buffer, c, &buffer[bufferLength] - c);
bufferLength = (&buffer[bufferLength] - c);
}
else
{
// just an ordinary header line, skip it
err = PrvReadThroughCRLF(inputFunc, inputStream, buffer, &bufferLength);
if (err)
ErrThrow(err);
}
// Read chars into the buffer
charsRead = inputFunc( inputStream, &buffer[bufferLength], importBufferMaxLength - bufferLength);
bufferLength += charsRead;
buffer[bufferLength] = nullChr;
// stop at blank line by itself or EOF
} while (buffer[0] != crChr && buffer[0] != nullChr);
// We've now parsed the MIME header. Preamble, segments, and postamble below.
} // end of MIME parser
do {
// find the boundary and remove it, along with any header info in the body part
if (*boundaryString != nullChr)
{
// Keep reading until we find a boundary
while (buffer[0] != nullChr && StrNCompareAscii(buffer, boundaryString, StrLen(boundaryString)) != 0)
{
err = PrvReadThroughCRLF(inputFunc, inputStream, buffer, &bufferLength);
if (err)
ErrThrow(err);
}
// Remove the boundary by removing all text until the end of the line.
err = PrvReadThroughCRLF(inputFunc, inputStream, buffer, &bufferLength);
if (err)
ErrThrow(err);
while (buffer[0] != nullChr && buffer[0] != crChr)
{
err = PrvReadThroughCRLF(inputFunc, inputStream, buffer, &bufferLength);
if (err)
ErrThrow(err);
}
err = PrvReadThroughCRLF(inputFunc, inputStream, buffer, &bufferLength);
if (err)
ErrThrow(err);
}
// could be that everything was header, and we're out of data.
// weird error, but MemHandle it.
if (bufferLength == 0)
{
ErrThrow(exgErrBadData);
}
addCr = 0;
while (bufferLength > 0 &&
(*boundaryString == nullChr || StrNCompareAscii(buffer, boundaryString, StrLen(boundaryString)) != 0))
{
// find CR or end of buffer
nextCrChr = StrChr(buffer, crChr);
if (nextCrChr != NULL)
charsToWrite = nextCrChr - buffer;
else
charsToWrite = bufferLength;
// PREVIEW Aba: Here we have the first line and we can exit
if (descriptionP)
{
if (charsToWrite >= descriptionSize)
charsToWrite = descriptionSize - 1;
StrNCopy(descriptionP, buffer, charsToWrite);
descriptionP[charsToWrite] = '\0';
return true;
}
// if we're going to overflow record, close it out (leave room for terminating null)
if (newRecordSize + charsToWrite + addCr > memoMaxLength)
{
// since we try to stop parsing at each CR, and most records from other sources (MIME)
// should have a CR at least every 76 characters, we probably don't have to worry about
// word wrap. Still, beaming a lot of just plain text could break records on random
// boundaries...
PrvMemoImportFinishRecord(dbP, indexNew, &newRecordH, &newRecordSize, inputStream);
addCr = 0;
numRecordsReceived++;
}
// Make a record if we need one
if (newRecordH == NULL)
{
indexNew = dmMaxRecordIndex;
newRecordH = DmNewRecord(dbP, (UInt16 *)&indexNew, bufferLength);
if (newRecordH == 0)
ErrThrow(exgMemError);
newRecordSize = 0;
}
// Write the buffer out to the record
newHandle = DmResizeRecord(dbP, indexNew, newRecordSize + charsToWrite + addCr);
if (newHandle)
newRecordH = newHandle;
else
ErrThrow(exgMemError);
newRecordP = MemHandleLock(newRecordH);
if (addCr != 0)
DmWrite(newRecordP, newRecordSize++, "\n", 1);
DmWrite(newRecordP, newRecordSize, buffer, charsToWrite);
newRecordSize += charsToWrite;
MemHandleUnlock(newRecordH);
// Remove the chars written so we can read more into the buffer
if (nextCrChr != NULL)
{
if (charsToWrite < importBufferMaxLength-1)
{
MemMove(buffer, nextCrChr+2, bufferLength-(charsToWrite+2)); // delete LF
bufferLength -= charsToWrite+2;
}
else
// CR/LF was split by end of buffer, so DON'T delete the CR, catch it next time 'round
{
MemMove(buffer, nextCrChr, bufferLength-(charsToWrite)); // don't delete CR or LF
bufferLength -= charsToWrite;
nextCrChr = NULL;
}
}
else
buffer[bufferLength = 0] = nullChr;
// Now read more
charsRead = inputFunc( inputStream, &buffer[bufferLength], importBufferMaxLength - bufferLength);
bufferLength += charsRead;
buffer[bufferLength] = nullChr;
if (nextCrChr != NULL)
addCr = 1;
else
addCr = 0;
} // end of segment parser
// Set the category for the record
if (PrvStreamSocket(inputStream)->appData) {
UInt16 attr;
Err err;
UInt16 categoryID = PrvStreamSocket(inputStream)->appData;
// Get the attributes
err = DmRecordInfo(dbP, indexNew, &attr, NULL, NULL);
// Set them to include the category, and mark the record dirty
if ((attr & dmRecAttrCategoryMask) != categoryID) {
attr &= ~dmRecAttrCategoryMask;
attr |= categoryID | dmRecAttrDirty;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -