Mozilla/mozilla/mailnews/db/msgdb/src/nsMailDatabase.cpp
bienvenu%netscape.com af30301e84 add ability to import msf files
git-svn-id: svn://10.0.0.236/trunk@22404 18797224-902f-48f8-a5cc-f745e15eee43
1999-02-28 22:00:50 +00:00

498 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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) 1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "msgCore.h"
#include "nsMailDatabase.h"
#include "nsDBFolderInfo.h"
#include "nsMsgLocalFolderHdrs.h"
#include "nsFileStream.h"
#include "nsLocalFolderSummarySpec.h"
#include "nsFileSpec.h"
nsMailDatabase::nsMailDatabase(nsFilePath& folder)
: m_master(nsnull), m_reparse(PR_FALSE), m_folderName(folder), m_folderStream(nsnull)
{
}
nsMailDatabase::~nsMailDatabase()
{
}
/* static */ nsresult nsMailDatabase::Open(nsFilePath &dbName, PRBool create, nsMailDatabase** pMessageDB,
PRBool upgrading /*=PR_FALSE*/)
{
nsMailDatabase *mailDB;
int statResult;
struct stat st;
PRBool newFile = PR_FALSE;
nsLocalFolderSummarySpec summarySpec(dbName);
nsDBFolderInfo *folderInfo = NULL;
*pMessageDB = NULL;
mailDB = (nsMailDatabase *) FindInCache(dbName);
if (mailDB)
{
*pMessageDB = mailDB;
mailDB->AddRef();
return(NS_OK);
}
// if the old summary doesn't exist, we're creating a new one.
if (stat ((const char *) summarySpec, &st) && create)
newFile = PR_TRUE;
nsFilePath dbPath(summarySpec);
mailDB = new nsMailDatabase(dbPath);
if (!mailDB)
return NS_ERROR_OUT_OF_MEMORY;
// stat file before we open the db, because if we've latered
// any messages, handling latered will change time stamp on
// folder file.
statResult = stat ((const char *) summarySpec, &st);
nsresult err = mailDB->OpenMDB((const char *) summarySpec, create);
if (NS_SUCCEEDED(err))
{
folderInfo = mailDB->GetDBFolderInfo();
if (folderInfo == NULL)
{
err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
else
{
// if opening existing file, make sure summary file is up to date.
// if caller is upgrading, don't return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE so the caller
// can pull out the transfer info for the new db.
if (!newFile && !statResult && !upgrading)
{
PRInt32 numNewMessages;
folderInfo->GetNumNewMessages(&numNewMessages);
if (folderInfo->m_folderSize != st.st_size ||
folderInfo->m_folderDate != st.st_mtime || numNewMessages < 0)
err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
// compare current version of db versus filed out version info.
if (mailDB->GetCurVersion() != folderInfo->GetDiskVersion())
err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
if (err != NS_OK)
{
mailDB->Close();
mailDB = NULL;
}
}
if (err != NS_OK || newFile)
{
// if we couldn't open file, or we have a blank one, and we're supposed
// to upgrade, updgrade it.
if (newFile && !upgrading) // caller is upgrading, and we have empty summary file,
{ // leave db around and open so caller can upgrade it.
err = NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
}
else if (err != NS_OK)
{
*pMessageDB = NULL;
delete mailDB;
}
}
if (err == NS_OK || err == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
{
*pMessageDB = mailDB;
GetDBCache()->AppendElement(mailDB);
// if (err == NS_OK)
// mailDB->HandleLatered();
}
return err;
}
/* static */ nsresult nsMailDatabase::CloneInvalidDBInfoIntoNewDB(nsFilePath &pathName, nsMailDatabase** pMailDB)
{
nsresult ret = NS_OK;
return ret;
}
nsresult nsMailDatabase::OnNewPath (nsFilePath &newPath)
{
nsresult ret = NS_OK;
return ret;
}
nsresult nsMailDatabase::DeleteMessages(nsMsgKeyArray &nsMsgKeys, nsIDBChangeListener *instigator)
{
nsresult ret = NS_OK;
m_folderStream = new nsIOFileStream(nsFileSpec(m_dbName));
ret = nsMsgDatabase::DeleteMessages(nsMsgKeys, instigator);
if (m_folderStream)
delete m_folderStream;
m_folderStream = NULL;
SetFolderInfoValid(m_folderName, 0, 0);
return ret;
}
// Helper routine - lowest level of flag setting
PRBool nsMailDatabase::SetHdrFlag(nsMsgHdr *msgHdr, PRBool bSet, MsgFlags flag)
{
nsIOFileStream *fileStream = NULL;
PRBool ret = PR_FALSE;
if (nsMsgDatabase::SetHdrFlag(msgHdr, bSet, flag))
{
UpdateFolderFlag(msgHdr, bSet, flag, &fileStream);
if (fileStream != NULL)
{
delete fileStream;
SetFolderInfoValid(m_folderName, 0, 0);
}
ret = PR_TRUE;
}
return ret;
}
#ifdef XP_MAC
extern PRFileDesc *gIncorporateFID;
extern const char* gIncorporatePath;
#endif // XP_MAC
// ### should move this into some utils class...
int msg_UnHex(char C)
{
return ((C >= '0' && C <= '9') ? C - '0' :
((C >= 'A' && C <= 'F') ? C - 'A' + 10 :
((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)));
}
// We let the caller close the file in case he's updating a lot of flags
// and we don't want to open and close the file every time through.
// As an experiment, try caching the fid in the db as m_folderFile.
// If this is set, use it but don't return *pFid.
void nsMailDatabase::UpdateFolderFlag(nsMsgHdr *mailHdr, PRBool bSet,
MsgFlags flag, nsIOFileStream **ppFileStream)
{
static char buf[30];
nsIOFileStream *fileStream = (m_folderStream) ? m_folderStream : *ppFileStream;
//#ifdef GET_FILE_STUFF_TOGETHER
#ifdef XP_MAC
// This is a horrible hack and we should make sure we don't need it anymore.
// It has to do with multiple people having the same file open, I believe, but the
// mac file system only has one handle, and they compete for the file position.
// Prevent closing the file from under the incorporate stuff. #82785.
int32 savedPosition = -1;
if (!fid && gIncorporatePath && !XP_STRCMP(m_folderName, gIncorporatePath))
{
fid = gIncorporateFID;
savedPosition = ftell(gIncorporateFID); // so we can restore it.
}
#endif // XP_MAC
PRUint32 offset;
(void)mailHdr->GetStatusOffset(&offset);
if (offset > 0)
{
if (fileStream == NULL)
{
fileStream = new nsIOFileStream(nsFileSpec(m_folderName));
}
if (fileStream)
{
PRUint32 msgOffset;
(void)mailHdr->GetMessageOffset(&msgOffset);
PRUint32 position = offset + msgOffset;
PR_ASSERT(offset < 10000);
fileStream->seek(position);
buf[0] = '\0';
if (fileStream->readline(buf, sizeof(buf)))
{
if (strncmp(buf, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN) == 0 &&
strncmp(buf + X_MOZILLA_STATUS_LEN, ": ", 2) == 0 &&
strlen(buf) > X_MOZILLA_STATUS_LEN + 6)
{
PRUint32 flags;
(void)mailHdr->GetFlags(&flags);
if (!(flags & MSG_FLAG_EXPUNGED))
{
int i;
char *p = buf + X_MOZILLA_STATUS_LEN + 2;
for (i=0, flags = 0; i<4; i++, p++)
{
flags = (flags << 4) | msg_UnHex(*p);
}
PRUint32 curFlags;
(void)mailHdr->GetFlags(&curFlags);
flags = (flags & MSG_FLAG_QUEUED) |
(curFlags & ~MSG_FLAG_RUNTIME_ONLY);
}
else
{
flags &= ~MSG_FLAG_RUNTIME_ONLY;
}
fileStream->seek(position);
// We are filing out old Cheddar flags here
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT, flags);
fileStream->write(buf, PL_strlen(buf));
// time to upate x-mozilla-status2
position = fileStream->tell();
fileStream->seek(position + LINEBREAK_LEN);
if (fileStream->readline(buf, sizeof(buf)))
{
if (strncmp(buf, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN) == 0 &&
strncmp(buf + X_MOZILLA_STATUS2_LEN, ": ", 2) == 0 &&
strlen(buf) > X_MOZILLA_STATUS2_LEN + 10)
{
PRUint32 dbFlags;
(void)mailHdr->GetFlags(&dbFlags);
dbFlags &= (MSG_FLAG_MDN_REPORT_NEEDED | MSG_FLAG_MDN_REPORT_SENT | MSG_FLAG_TEMPLATE);
fileStream->seek(position + LINEBREAK_LEN);
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT, dbFlags);
fileStream->write(buf, PL_strlen(buf));
}
}
} else
{
printf("Didn't find %s where expected at position %ld\n"
"instead, found %s.\n",
X_MOZILLA_STATUS, (long) position, buf);
SetReparse(TRUE);
}
}
else
{
printf("Couldn't read old status line at all at position %ld\n",
(long) position);
SetReparse(TRUE);
}
#ifdef XP_MAC
// Restore the file position
if (savedPosition >= 0)
XP_FileSeek(fid, savedPosition, SEEK_SET);
#endif
}
else
{
printf("Couldn't open mail folder for update%s!\n", m_folderName);
PR_ASSERT(PR_FALSE);
}
}
//#endif // GET_FILE_STUFF_TOGETHER
#ifdef XP_MAC
if (!m_folderStream && fid != gIncorporateFID)
#else
if (!m_folderStream)
#endif
*ppFileStream = fileStream; // This tells the caller that we opened the file, and please to close it.
}
/* static */ nsresult nsMailDatabase::SetSummaryValid(PRBool valid)
{
nsresult ret = NS_OK;
struct stat st;
if (stat(m_dbName, &st))
return NS_MSG_ERROR_FOLDER_MISSING;
if (valid)
{
m_dbFolderInfo->SetFolderSize(st.st_size);
m_dbFolderInfo->SetFolderDate(st.st_mtime);
}
else
{
m_dbFolderInfo->SetFolderDate(0); // that ought to do the trick.
}
return ret;
}
nsresult nsMailDatabase::GetFolderName(nsString &folderName)
{
folderName = m_folderName;
return NS_OK;
}
// The master is needed to find the folder info corresponding to the db.
// Perhaps if we passed in the folder info when we opened the db,
// we wouldn't need the master. I don't remember why we sometimes need to
// get from the db to the folder info, but it's probably something like
// some poor soul who has a db pointer but no folderInfo.
MSG_FolderInfo *nsMailDatabase::GetFolderInfo()
{
PR_ASSERT(PR_FALSE);
return NULL;
}
// for offline imap queued operations
// these are in the base mail class (presumably) because offline moves between online and offline
// folders can cause these operations to be stored in local mail folders.
nsresult nsMailDatabase::ListAllOfflineOpIds(nsMsgKeyArray &outputIds)
{
nsresult ret = NS_OK;
return ret;
}
int nsMailDatabase::ListAllOfflineDeletes(nsMsgKeyArray &outputIds)
{
nsresult ret = NS_OK;
return ret;
}
nsresult nsMailDatabase::GetOfflineOpForKey(nsMsgKey opKey, PRBool create, nsOfflineImapOperation **)
{
nsresult ret = NS_OK;
return ret;
}
nsresult nsMailDatabase::AddOfflineOp(nsOfflineImapOperation *op)
{
nsresult ret = NS_OK;
return ret;
}
nsresult DeleteOfflineOp(nsMsgKey opKey)
{
nsresult ret = NS_OK;
return ret;
}
nsresult SetSourceMailbox(nsOfflineImapOperation *op, const char *mailbox, nsMsgKey key)
{
nsresult ret = NS_OK;
return ret;
}
nsresult nsMailDatabase::GetIdsWithNoBodies (nsMsgKeyArray &bodylessIds)
{
nsresult ret = NS_OK;
return ret;
}
/* static */
nsresult nsMailDatabase::SetFolderInfoValid(nsFilePath &folderName, int num, int numunread)
{
struct stat st;
nsLocalFolderSummarySpec summarySpec(folderName);
nsFilePath summaryPath(summarySpec);
nsresult err;
if (stat((const char*) folderName, &st))
return NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
// should we have type safe downcast methods again?
nsMailDatabase *pMessageDB = (nsMailDatabase *) nsMailDatabase::FindInCache(summaryPath);
if (pMessageDB == NULL)
{
pMessageDB = new nsMailDatabase(summaryPath);
// ### this does later stuff (marks latered messages unread), which may be a problem
err = pMessageDB->OpenMDB(summaryPath, FALSE);
if (err != NS_OK)
{
delete pMessageDB;
pMessageDB = NULL;
}
}
else
pMessageDB->AddRef();
if (pMessageDB == NULL)
{
printf("Exception opening summary file\n");
return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
{
pMessageDB->m_dbFolderInfo->m_folderSize = st.st_size;
pMessageDB->m_dbFolderInfo->m_folderDate = st.st_mtime;
pMessageDB->m_dbFolderInfo->ChangeNumVisibleMessages(num);
pMessageDB->m_dbFolderInfo->ChangeNumNewMessages(numunread);
pMessageDB->m_dbFolderInfo->ChangeNumMessages(num);
}
pMessageDB->Close();
return NS_OK;
}
// This is used to remember that the db is out of sync with the mail folder
// and needs to be regenerated.
void nsMailDatabase::SetReparse(PRBool reparse)
{
m_reparse = reparse;
}
#ifdef DEBUG // strictly for testing purposes
nsresult nsMailDatabase::PrePopulate()
{
nsMsgHdr *newHdr;
PRTime resultTime, intermediateResult, microSecondsPerSecond;
resultTime = PR_Now();
time_t resDate;
LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
LL_DIV(intermediateResult, resultTime, microSecondsPerSecond);
LL_L2I(resDate, intermediateResult);
nsresult res = CreateNewHdr(1, &newHdr);
newHdr->SetAuthor("bird@celtics.com (Larry Bird)");
newHdr->SetSubject("Why the Lakers suck");
newHdr->SetDate(resDate);
newHdr->SetRecipients("riley@heat.com (Pat Riley)", FALSE);
AddNewHdrToDB (newHdr, PR_TRUE);
newHdr->Release();
res = CreateNewHdr(2, &newHdr);
newHdr->SetAuthor("shaq@brick.com (Shaquille O'Neal)");
newHdr->SetSubject("Anyone here know how to shoot free throws?");
newHdr->SetDate(resDate);
AddNewHdrToDB (newHdr, PR_TRUE);
newHdr->Release();
res = CreateNewHdr(3, &newHdr);
newHdr->SetAuthor("dj@celtics.com (Dennis Johnson)");
newHdr->SetSubject("Has anyone seen my jump shot?");
newHdr->SetDate(resDate);
AddNewHdrToDB (newHdr, PR_TRUE);
newHdr->Release();
res = CreateNewHdr(4, &newHdr);
newHdr->SetAuthor("sichting@celtics.com (Jerry Sichting)");
newHdr->SetSubject("Tips for fighting 7' 4\" guys");
newHdr->SetDate(resDate);
AddNewHdrToDB (newHdr, PR_TRUE);
newHdr->Release();
return NS_OK;
}
#endif