mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 00:55:37 +00:00
519 lines
13 KiB
C++
519 lines
13 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.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "nsOE5File.h"
|
|
#include "nsCRT.h"
|
|
#include "OEDebugLog.h"
|
|
|
|
|
|
#define kIndexGrowBy 100
|
|
#define kSignatureSize 12
|
|
#define kDontSeek 0xFFFFFFFF
|
|
|
|
static char *gSig =
|
|
"\xCF\xAD\x12\xFE\xC5\xFD\x74\x6F\x66\xE3\xD1\x11";
|
|
|
|
|
|
PRBool nsOE5File::VerifyLocalMailFile( nsIFileSpec *pFile)
|
|
{
|
|
char sig[kSignatureSize];
|
|
|
|
if (!ReadBytes( pFile, sig, 0, kSignatureSize)) {
|
|
return( PR_FALSE);
|
|
}
|
|
|
|
PRBool result = PR_TRUE;
|
|
|
|
for (int i = 0; (i < kSignatureSize) && result; i++) {
|
|
if (sig[i] != gSig[i]) {
|
|
result = PR_FALSE;
|
|
}
|
|
}
|
|
|
|
char storeName[14];
|
|
if (!ReadBytes( pFile, storeName, 0x24C1, 12))
|
|
result = PR_FALSE;
|
|
|
|
storeName[12] = 0;
|
|
|
|
if (nsCRT::strcasecmp( "LocalStore", storeName))
|
|
result = PR_FALSE;
|
|
|
|
return( result);
|
|
}
|
|
|
|
PRBool nsOE5File::IsLocalMailFile( nsIFileSpec *pFile)
|
|
{
|
|
nsresult rv;
|
|
PRBool isFile = PR_FALSE;
|
|
|
|
rv = pFile->IsFile( &isFile);
|
|
if (NS_FAILED( rv) || !isFile)
|
|
return( PR_FALSE);
|
|
|
|
rv = pFile->OpenStreamForReading();
|
|
if (NS_FAILED( rv))
|
|
return( PR_FALSE);
|
|
|
|
PRBool result = VerifyLocalMailFile( pFile);
|
|
pFile->CloseStream();
|
|
|
|
return( result);
|
|
}
|
|
|
|
PRBool nsOE5File::ReadIndex( nsIFileSpec *pFile, PRUint32 **ppIndex, PRUint32 *pSize)
|
|
{
|
|
*ppIndex = nsnull;
|
|
*pSize = 0;
|
|
|
|
char signature[4];
|
|
if (!ReadBytes( pFile, signature, 0, 4))
|
|
return( PR_FALSE);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
if (signature[i] != gSig[i]) {
|
|
IMPORT_LOG0( "*** Outlook 5.0 dbx file signature doesn't match\n");
|
|
return( PR_FALSE);
|
|
}
|
|
}
|
|
|
|
PRUint32 offset = 0x00e4;
|
|
PRUint32 indexStart = 0;
|
|
if (!ReadBytes( pFile, &indexStart, offset, 4)) {
|
|
IMPORT_LOG0( "*** Unable to read offset to index start\n");
|
|
return( PR_FALSE);
|
|
}
|
|
|
|
PRUint32Array array;
|
|
array.count = 0;
|
|
array.alloc = kIndexGrowBy;
|
|
array.pIndex = new PRUint32[kIndexGrowBy];
|
|
|
|
PRUint32 next = ReadMsgIndex( pFile, indexStart, &array);
|
|
while (next) {
|
|
next = ReadMsgIndex( pFile, next, &array);
|
|
}
|
|
|
|
if (array.count) {
|
|
*pSize = array.count;
|
|
*ppIndex = array.pIndex;
|
|
return( PR_TRUE);
|
|
}
|
|
|
|
delete [] array.pIndex;
|
|
return( PR_FALSE);
|
|
}
|
|
|
|
|
|
PRUint32 nsOE5File::ReadMsgIndex( nsIFileSpec *file, PRUint32 offset, PRUint32Array *pArray)
|
|
{
|
|
// Record is:
|
|
// 4 byte marker
|
|
// 4 byte unknown
|
|
// 4 byte nextSubIndex
|
|
// 4 byte (parentIndex?)
|
|
// 2 bytes unknown
|
|
// 1 byte length - # of entries in this record
|
|
// 1 byte unknown
|
|
// 4 byte unknown
|
|
// length records consisting of 3 longs
|
|
// 1 - pointer to record
|
|
// 2 - child index pointer
|
|
// 3 - number of records in child
|
|
|
|
PRUint32 marker;
|
|
|
|
if (!ReadBytes( file, &marker, offset, 4))
|
|
return( 0);
|
|
|
|
if (marker != offset)
|
|
return( 0);
|
|
|
|
|
|
PRUint32 vals[3];
|
|
|
|
if (!ReadBytes( file, vals, offset + 4, 12))
|
|
return( 0);
|
|
|
|
|
|
PRUint8 len[4];
|
|
if (!ReadBytes( file, len, offset + 16, 4))
|
|
return( 0);
|
|
|
|
|
|
|
|
PRUint32 cnt = (PRUint32) len[1];
|
|
cnt *= 3;
|
|
PRUint32 *pData = new PRUint32[cnt];
|
|
|
|
if (!ReadBytes( file, pData, offset + 24, cnt * 4)) {
|
|
delete [] pData;
|
|
return( 0);
|
|
}
|
|
|
|
PRUint32 next;
|
|
PRUint32 indexOffset;
|
|
PRUint32 * pRecord = pData;
|
|
PRUint32 * pNewIndex;
|
|
|
|
for (PRUint8 i = 0; i < (PRUint8)len[1]; i++, pRecord += 3) {
|
|
indexOffset = pRecord[0];
|
|
|
|
if (pArray->count >= pArray->alloc) {
|
|
pNewIndex = new PRUint32[ pArray->alloc + kIndexGrowBy];
|
|
nsCRT::memcpy( pNewIndex, pArray->pIndex, (pArray->alloc * 4));
|
|
(pArray->alloc) += kIndexGrowBy;
|
|
delete [] pArray->pIndex;
|
|
pArray->pIndex = pNewIndex;
|
|
}
|
|
|
|
/*
|
|
We could do some checking here if we wanted -
|
|
make sure the index is within the file,
|
|
make sure there isn't a duplicate index, etc.
|
|
*/
|
|
|
|
pArray->pIndex[pArray->count] = indexOffset;
|
|
(pArray->count)++;
|
|
|
|
|
|
|
|
next = pRecord[1];
|
|
if (next)
|
|
while ((next = ReadMsgIndex( file, next, pArray)) != 0);
|
|
}
|
|
delete pData;
|
|
|
|
// return the pointer to the next subIndex
|
|
return( vals[1]);
|
|
}
|
|
|
|
PRBool nsOE5File::IsFromLine( char *pLine, PRUint32 len)
|
|
{
|
|
if (len >= 5) {
|
|
if ( (*pLine == 'F') && (pLine[1] = 'r') && (pLine[2] == 'o') && (pLine[3] == 'm') && (pLine[4] == ' '))
|
|
return( PR_TRUE);
|
|
}
|
|
|
|
return( PR_FALSE);
|
|
}
|
|
|
|
// Anything over 16K will be assumed BAD, BAD, BAD!
|
|
#define kMailboxBufferSize 0x4000
|
|
const char *nsOE5File::m_pFromLineSep = "From ????@???? 1 Jan 1965 00:00:00\x0D\x0A";
|
|
|
|
nsresult nsOE5File::ImportMailbox( PRUint32 *pBytesDone, PRBool *pAbort, nsString& name, nsIFileSpec *inFile, nsIFileSpec *pDestination, PRUint32 *pCount)
|
|
{
|
|
nsresult rv;
|
|
PRInt32 msgCount = 0;
|
|
if (pCount)
|
|
*pCount = 0;
|
|
|
|
rv = inFile->OpenStreamForReading();
|
|
if (NS_FAILED( rv)) return( rv);
|
|
rv = pDestination->OpenStreamForWriting();
|
|
if (NS_FAILED( rv)) {
|
|
inFile->CloseStream();
|
|
return( rv);
|
|
}
|
|
|
|
PRUint32 * pIndex;
|
|
PRUint32 indexSize;
|
|
|
|
if (!ReadIndex( inFile, &pIndex, &indexSize)) {
|
|
IMPORT_LOG1( "No messages found in mailbox: %S\n", name.GetUnicode());
|
|
inFile->CloseStream();
|
|
pDestination->CloseStream();
|
|
return( NS_OK);
|
|
}
|
|
|
|
char * pBuffer = new char[kMailboxBufferSize];
|
|
if (!(*pAbort))
|
|
ConvertIndex( inFile, pBuffer, pIndex, indexSize);
|
|
|
|
PRUint32 block[4];
|
|
PRInt32 sepLen = (PRInt32) nsCRT::strlen( m_pFromLineSep);
|
|
PRInt32 written;
|
|
|
|
/*
|
|
Each block is:
|
|
marker - matches file offset
|
|
block length
|
|
text length in block
|
|
pointer to next block. (0 if end)
|
|
|
|
So what we do for each message is:
|
|
Read the first block
|
|
Write out the From message separator if the message doesn't already
|
|
start with one.
|
|
Write out all subsequent blocks of text keeping track of the last
|
|
2 bytes written of the message.
|
|
if the last 2 bytes aren't and end of line then write one out before
|
|
proceeding to the next message.
|
|
*/
|
|
|
|
PRUint32 didBytes = 0;
|
|
char last2[2] = {0, 0};
|
|
PRUint32 next;
|
|
rv = NS_OK;
|
|
for (PRUint32 i = 0; (i < indexSize) && !(*pAbort); i++) {
|
|
if (pIndex[i]) {
|
|
if (ReadBytes( inFile, block, pIndex[i], 16) &&
|
|
(block[0] == pIndex[i]) &&
|
|
(block[2] < kMailboxBufferSize) &&
|
|
(ReadBytes( inFile, pBuffer, kDontSeek, block[2]))) {
|
|
// write out the from separator.
|
|
if (!IsFromLine( pBuffer, block[2])) {
|
|
rv = pDestination->Write( m_pFromLineSep, sepLen, &written);
|
|
// FIXME: Do I need to check the return value of written???
|
|
if (NS_FAILED( rv))
|
|
break;
|
|
last2[0] = 13;
|
|
last2[1] = 10;
|
|
}
|
|
else {
|
|
last2[0] = 0;
|
|
last2[1] = 0;
|
|
}
|
|
rv = WriteMessageBuffer( pDestination, pBuffer, block[2], last2);
|
|
didBytes += block[2];
|
|
if (NS_FAILED( rv))
|
|
break;
|
|
next = block[3];
|
|
while (next && !(*pAbort)) {
|
|
if (ReadBytes( inFile, block, next, 16) &&
|
|
(block[0] == next) &&
|
|
(block[2] < kMailboxBufferSize) &&
|
|
(ReadBytes( inFile, pBuffer, kDontSeek, block[2]))) {
|
|
if (block[2])
|
|
rv = WriteMessageBuffer( pDestination, pBuffer, block[2], last2);
|
|
didBytes += block[2];
|
|
if (NS_FAILED( rv))
|
|
break;
|
|
next = block[3];
|
|
}
|
|
else {
|
|
IMPORT_LOG2( "Error reading message from %S at 0x%lx\n", name.GetUnicode(), pIndex[i]);
|
|
pDestination->Write( "\x0D\x0A", 2, &written);
|
|
next = 0;
|
|
last2[0] = 13;
|
|
last2[1] = 10;
|
|
}
|
|
}
|
|
if (NS_FAILED( rv) || (*pAbort))
|
|
break;
|
|
if ((last2[0] != 13) || (last2[1] != 10)) {
|
|
pDestination->Write( "\x0D\x0A", 2, &written);
|
|
last2[0] = 13;
|
|
last2[1] = 10;
|
|
}
|
|
|
|
msgCount++;
|
|
if (pCount)
|
|
*pCount = msgCount;
|
|
if (pBytesDone)
|
|
*pBytesDone = didBytes;
|
|
}
|
|
else {
|
|
// Error reading message, should this be logged???
|
|
IMPORT_LOG2( "Error reading message from %S at 0x%lx\n", name.GetUnicode(), pIndex[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
delete [] pBuffer;
|
|
inFile->CloseStream();
|
|
pDestination->CloseStream();
|
|
|
|
|
|
return( rv);
|
|
}
|
|
|
|
|
|
#define ISFROMLINE( pChar, i) (*(pChar + i) == 'F') && (*(pChar + i + 1) == 'r') && \
|
|
(*(pChar + i + 2) == 'o') && (*(pChar + i + 3) == 'm') && \
|
|
(*(pChar + i + 4) == ' ')
|
|
|
|
|
|
nsresult nsOE5File::WriteMessageBuffer( nsIFileSpec *stream, char *pBuffer, PRUint32 size, char *last2)
|
|
{
|
|
if (!size)
|
|
return( NS_OK);
|
|
|
|
PRInt32 written;
|
|
nsresult rv = NS_OK;
|
|
|
|
//check the very beginning of the buffer to make sure it's not a from
|
|
// line
|
|
if ((size > 4) && ((last2[0] = 0x0D) && (last2[1] == 0x0A)) || (last2[1] == 0x0D)) {
|
|
if ((last2[1] == 0x0D) && (size > 5) && (*pBuffer == 0x0A) && ISFROMLINE( pBuffer, 1)) {
|
|
rv = stream->Write( pBuffer, 1, &written);
|
|
pBuffer++;
|
|
size--;
|
|
if (NS_SUCCEEDED( rv))
|
|
rv = stream->Write( ">", 1, &written);
|
|
}
|
|
else if ((last2[0] == 0x0D) && ISFROMLINE( pBuffer, 0)) {
|
|
rv = stream->Write( ">", 1, &written);
|
|
}
|
|
if (NS_FAILED( rv))
|
|
return( rv);
|
|
}
|
|
|
|
// examine the rest of the buffer for from problems!
|
|
PRUint32 cnt = 0;
|
|
char * pChar = pBuffer;
|
|
if (size > 6) {
|
|
while (cnt < (size - 6)) {
|
|
if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A) && ISFROMLINE( pChar, 2)) {
|
|
rv = stream->Write( pBuffer, cnt + 2, &written);
|
|
pBuffer += (cnt + 2);
|
|
size -= (cnt + 2);
|
|
pChar += 2;
|
|
cnt = 0;
|
|
if (NS_SUCCEEDED( rv)) {
|
|
rv = stream->Write( ">", 1, &written);
|
|
}
|
|
if (NS_FAILED( rv))
|
|
return( rv);
|
|
}
|
|
else {
|
|
cnt++;
|
|
pChar++;
|
|
}
|
|
}
|
|
}
|
|
|
|
rv = stream->Write( pBuffer, (PRInt32) size, &written);
|
|
|
|
if (NS_SUCCEEDED( rv)) {
|
|
if (size > 1) {
|
|
last2[0] = *(pBuffer + size - 2);
|
|
last2[1] = *(pBuffer + size - 1);
|
|
}
|
|
else {
|
|
last2[0] = last2[1];
|
|
last2[1] = *pBuffer;
|
|
}
|
|
}
|
|
|
|
return( rv);
|
|
}
|
|
|
|
/*
|
|
A message index record consists of:
|
|
4 byte marker - matches record offset
|
|
4 bytes size - size of data after this header
|
|
2 bytes unknown
|
|
1 bytes - number of attributes
|
|
1 byte unknown
|
|
Each attribute is a 4 byte value with the 1st byte being the tag
|
|
and the remaing 3 bytes being data. The data is either a direct
|
|
offset of an offset within the message index that points to the
|
|
data for the tag.
|
|
|
|
Current known tags are:
|
|
0x02 - a time value
|
|
0x04 - text offset pointer, the data is the offset after the attribute
|
|
of a 4 byte pointer to the message text
|
|
0x05 - offset to truncated subject
|
|
0x08 - offste to subject
|
|
0x0D - offset to from
|
|
0x0E - offset to from addresses
|
|
0x13 - offset to to name
|
|
0x45 - offset to to address
|
|
0x80 - msgId
|
|
0x84 - direct text offset, direct pointer to message text
|
|
*/
|
|
|
|
void nsOE5File::ConvertIndex( nsIFileSpec *pFile, char *pBuffer, PRUint32 *pIndex, PRUint32 size)
|
|
{
|
|
// for each index record, get the actual message offset! If there is a problem
|
|
// just record the message offset as 0 and the message reading code
|
|
// can log that error information.
|
|
|
|
PRUint8 recordHead[12];
|
|
PRUint32 marker;
|
|
PRUint32 recordSize;
|
|
PRUint32 numAttrs;
|
|
PRUint32 offset;
|
|
PRUint32 attrIndex;
|
|
PRUint32 attrOffset;
|
|
PRUint8 tag;
|
|
PRUint32 tagData;
|
|
|
|
for (PRUint32 i = 0; i < size; i++) {
|
|
offset = 0;
|
|
if (ReadBytes( pFile, recordHead, pIndex[i], 12)) {
|
|
nsCRT::memcpy( &marker, recordHead, 4);
|
|
nsCRT::memcpy( &recordSize, recordHead + 4, 4);
|
|
numAttrs = (PRUint32) recordHead[10];
|
|
if ((marker == pIndex[i]) && (recordSize < kMailboxBufferSize) && ((numAttrs * 4) <= recordSize)) {
|
|
if (ReadBytes( pFile, pBuffer, kDontSeek, recordSize)) {
|
|
attrOffset = 0;
|
|
for (attrIndex = 0; attrIndex < numAttrs; attrIndex++, attrOffset += 4) {
|
|
tag = (PRUint8) pBuffer[attrOffset];
|
|
if (tag == (PRUint8) 0x84) {
|
|
tagData = 0;
|
|
nsCRT::memcpy( &tagData, pBuffer + attrOffset + 1, 3);
|
|
offset = tagData;
|
|
break;
|
|
}
|
|
else if (tag == (PRUint8) 0x04) {
|
|
tagData = 0;
|
|
nsCRT::memcpy( &tagData, pBuffer + attrOffset + 1, 3);
|
|
if (((numAttrs * 4) + tagData + 4) <= recordSize)
|
|
nsCRT::memcpy( &offset, pBuffer + (numAttrs * 4) + tagData, 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pIndex[i] = offset;
|
|
}
|
|
}
|
|
|
|
|
|
PRBool nsOE5File::ReadBytes( nsIFileSpec *stream, void *pBuffer, PRUint32 offset, PRUint32 bytes)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (offset != kDontSeek) {
|
|
rv = stream->Seek( offset);
|
|
if (NS_FAILED( rv))
|
|
return( PR_FALSE);
|
|
}
|
|
|
|
if (!bytes)
|
|
return( PR_TRUE);
|
|
|
|
PRInt32 cntRead;
|
|
char * pReadTo = (char *)pBuffer;
|
|
rv = stream->Read( &pReadTo, bytes, &cntRead);
|
|
if (NS_FAILED( rv) || ((PRUint32)cntRead != bytes))
|
|
return( PR_FALSE);
|
|
return( PR_TRUE);
|
|
|
|
}
|
|
|