mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 10:10:31 +00:00
2127 lines
58 KiB
C++
2127 lines
58 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.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) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "rosetta.h"
|
|
#include "msg.h"
|
|
#include "xp.h"
|
|
#include "prsembst.h"
|
|
#include "mailhdr.h"
|
|
#include "maildb.h"
|
|
#include "msgfpane.h"
|
|
#include "msgfinfo.h"
|
|
#include "xp_time.h"
|
|
#include HG03067
|
|
#include "msgdbvw.h"
|
|
#include "grpinfo.h"
|
|
#include "msg_filt.h"
|
|
#include "msg_srch.h"
|
|
#include "pmsgsrch.h"
|
|
#include "pmsgfilt.h"
|
|
#include "xplocale.h"
|
|
#include "msgprefs.h"
|
|
#include "msgurlq.h"
|
|
#include "jsmsg.h"
|
|
#include "libi18n.h"
|
|
#include "msgimap.h"
|
|
#include "imaphost.h"
|
|
#include "msgmdn.h"
|
|
#include "prefapi.h"
|
|
|
|
extern "C"
|
|
{
|
|
#include "xpgetstr.h"
|
|
extern int MK_MSG_FOLDER_UNREADABLE;
|
|
extern int MK_OUT_OF_MEMORY;
|
|
extern int MK_MSG_NON_MAIL_FILE_READ_QUESTION;
|
|
extern int MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
extern int MK_MSG_FOLDER_UNREADABLE;
|
|
extern int MK_MSG_REPARSE_FOLDER;
|
|
}
|
|
|
|
ParseMailboxState::ParseMailboxState(const char *mailboxName)
|
|
{
|
|
m_mailDB = NULL;
|
|
m_msgDBView = NULL;
|
|
m_mailboxName = XP_STRDUP(mailboxName);
|
|
m_mailMaster = NULL;
|
|
m_folder = NULL;
|
|
m_pane = NULL;
|
|
m_obuffer = NULL;
|
|
m_obuffer_size = 0;
|
|
m_ibuffer = NULL;
|
|
m_ibuffer_size = 0;
|
|
m_ibuffer_fp = 0;
|
|
m_graph_progress_total = 0;
|
|
m_graph_progress_received = 0;
|
|
m_updateAsWeGo = FALSE;
|
|
m_ignoreNonMailFolder = FALSE;
|
|
m_isRealMailFolder = TRUE;
|
|
m_file = 0;
|
|
m_context = NULL;
|
|
// OK, it's bad to allocate one of these in the constructor.
|
|
// But this needs to be a pointer so that we can replace it
|
|
// with a different parser as required.
|
|
m_parseMsgState = new ParseMailMessageState;
|
|
}
|
|
|
|
ParseMailboxState::~ParseMailboxState()
|
|
{
|
|
XP_FREE(m_mailboxName);
|
|
// make sure we don't have the folder locked.
|
|
if (m_folder && m_folder->TestSemaphore(this))
|
|
m_folder->ReleaseSemaphore(this);
|
|
if (m_parseMsgState)
|
|
delete m_parseMsgState;
|
|
}
|
|
|
|
void ParseMailboxState::SetMailMessageParseState(ParseMailMessageState *mailMessageState)
|
|
{
|
|
// delete old one.
|
|
if (m_parseMsgState)
|
|
delete m_parseMsgState;
|
|
|
|
m_parseMsgState = mailMessageState;
|
|
}
|
|
|
|
void ParseMailboxState::UpdateStatusText ()
|
|
{
|
|
char *leafName = XP_STRRCHR (m_mailboxName, '/');
|
|
if (!leafName)
|
|
leafName = m_mailboxName;
|
|
else
|
|
leafName++;
|
|
NET_UnEscape(leafName);
|
|
char *upgrading = XP_GetString (MK_MSG_REPARSE_FOLDER);
|
|
int progressLength = XP_STRLEN(upgrading) + XP_STRLEN(leafName) + 1;
|
|
char *progress = new char [progressLength];
|
|
PR_snprintf (progress, progressLength, upgrading, leafName);
|
|
FE_Progress (m_context, progress);
|
|
delete [] progress;
|
|
}
|
|
|
|
void ParseMailboxState::UpdateProgressPercent ()
|
|
{
|
|
XP_ASSERT(m_context != NULL);
|
|
XP_ASSERT(m_graph_progress_total != 0);
|
|
if ((m_context) && (m_graph_progress_total != 0))
|
|
{
|
|
MSG_SetPercentProgress(m_context, m_graph_progress_received, m_graph_progress_total);
|
|
}
|
|
}
|
|
|
|
int ParseMailboxState::BeginOpenFolderSock(const char *folder_name,
|
|
const char * /*message_id*/ , int32 /* msgnum */,
|
|
void **folder_ptr)
|
|
{
|
|
XP_StatStruct folderst;
|
|
// get the semaphore for the folder.
|
|
int status = (m_folder) ? m_folder->AcquireSemaphore(this) : 0;
|
|
if (status)
|
|
{
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace ("ParseMailboxState::BeginOpenFolderSock: failed to acquire semaphore for %s", folder_name);
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
if (XP_Stat (folder_name, &folderst, xpMailFolder))
|
|
{
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace ("ParseMailboxState::BeginOpenFolderSock: couldn't stat %s", folder_name);
|
|
#endif
|
|
return MK_MSG_FOLDER_UNREADABLE;
|
|
}
|
|
m_file = XP_FileOpen(folder_name, xpMailFolder, XP_FILE_READ_BIN);
|
|
if (!m_file) {
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("ParseMailboxState::BeginOpenFolderSock: couldn't open %s", folder_name);
|
|
#endif
|
|
return MK_MSG_FOLDER_UNREADABLE;
|
|
}
|
|
|
|
HG82220
|
|
// assign this so libnet will call CloseFolderSock.
|
|
if (folder_ptr != NULL)
|
|
*folder_ptr = this;
|
|
/* The folder file is now open, and netlib will call us as it reads
|
|
chunks of it. Set up the buffers, etc. */
|
|
|
|
status = BeginParsingFolder(0);
|
|
if (status < 0)
|
|
{
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace ("ParseMailboxState::BeginOpenFolderSock: BeginParsing %s returned %d", folder_name, status);
|
|
#endif
|
|
return status;
|
|
}
|
|
m_graph_progress_total = folderst.st_size;
|
|
|
|
UpdateStatusText ();
|
|
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace ("ParseMailboxState::BeginOpenFolderSock: returned WAITING_FOR_CONNECTION");
|
|
#endif
|
|
return(MK_WAITING_FOR_CONNECTION);
|
|
}
|
|
|
|
int ParseMailboxState::BeginParsingFolder(int32 startPos)
|
|
{
|
|
m_obuffer_size = 10240;
|
|
m_parsingDone = FALSE;
|
|
m_obuffer = (char *) XP_ALLOC (m_obuffer_size);
|
|
if (! m_obuffer)
|
|
{
|
|
return MK_OUT_OF_MEMORY;
|
|
}
|
|
|
|
m_parseMsgState->Init(startPos);
|
|
return 0;
|
|
}
|
|
|
|
int ParseMailboxState::ParseBlock(const char *block, int32 length)
|
|
{
|
|
return msg_LineBuffer (block, length, &m_ibuffer, &m_ibuffer_size, &m_ibuffer_fp, FALSE,
|
|
#ifdef XP_OS2
|
|
(int32 (_Optlink*) (char*,uint32,void*))
|
|
#endif
|
|
LineBufferCallback, this);
|
|
}
|
|
|
|
/* This function works on what MSG_BeginOpenFolder
|
|
* starts. This function can return MK_WAITING_FOR_CONNECTION
|
|
* as many times as it needs and will be called again
|
|
* after yeilding to user events until it returns
|
|
* MK_CONNECTED or a negative error code.
|
|
*/
|
|
int ParseMailboxState::ParseMoreFolderSock(const char* folder_name,
|
|
const char* /* message_id */,
|
|
int32 /* msgnum */, void** /* folder_ptr */)
|
|
{
|
|
int status;
|
|
if (! m_file)
|
|
{
|
|
#ifdef MBOX_DEBUG
|
|
fprintf(real_stderr, "MSG_FinishOpenFolderSock: no file??\n");
|
|
#endif
|
|
return MK_MSG_FOLDER_UNREADABLE;
|
|
}
|
|
|
|
/* Read the next chunk of data from the file. */
|
|
status = XP_FileRead (m_obuffer, m_obuffer_size, m_file);
|
|
#ifdef MBOX_DEBUG
|
|
fprintf(real_stderr, "ParseMoreFolderSock: parsed %d of %s\n",
|
|
status, folder_name);
|
|
#endif
|
|
|
|
if (status > 0 &&
|
|
m_graph_progress_total > 0 &&
|
|
m_graph_progress_received == 0)
|
|
{
|
|
/* This is the first block from the file. Check to see if this
|
|
looks like a mail file. */
|
|
const char *s = m_obuffer;
|
|
const char *end = s + m_obuffer_size;
|
|
while (s < end && XP_IS_SPACE(*s))
|
|
s++;
|
|
if ((end - s) < 20 || !msg_IsEnvelopeLine(s, end - s))
|
|
{
|
|
char buf[500];
|
|
PR_snprintf (buf, sizeof(buf),
|
|
XP_GetString(MK_MSG_NON_MAIL_FILE_READ_QUESTION),
|
|
folder_name);
|
|
m_isRealMailFolder = FALSE;
|
|
if (m_ignoreNonMailFolder)
|
|
return MK_CONNECTED;
|
|
else if (!FE_Confirm (m_context, buf))
|
|
return -1; /* #### NOT_A_MAIL_FILE */
|
|
}
|
|
}
|
|
|
|
if (m_graph_progress_total > 0)
|
|
{
|
|
if (status > 0)
|
|
m_graph_progress_received += status;
|
|
MSG_SetPercentProgress (m_context, m_graph_progress_received, m_graph_progress_total);
|
|
}
|
|
|
|
if (status < 0)
|
|
{
|
|
return status;
|
|
}
|
|
else if (status == 0)
|
|
{
|
|
HG22067
|
|
DoneParsingFolder();
|
|
m_parsingDone = TRUE;
|
|
return MK_CONNECTED;
|
|
}
|
|
else
|
|
{
|
|
status = ParseBlock(m_obuffer, status);
|
|
if (status < 0)
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
return(MK_WAITING_FOR_CONNECTION);
|
|
}
|
|
|
|
void ParseMailboxState::CloseFolderSock(const char* /*folder_name*/, const char* /*message_id*/,
|
|
int32 /*msgnum*/, void* /*folder_ptr*/)
|
|
|
|
{
|
|
if (m_file)
|
|
XP_FileClose(m_file);
|
|
|
|
FREEIF (m_ibuffer);
|
|
FREEIF (m_obuffer);
|
|
m_obuffer_size = 0;
|
|
m_ibuffer_size = 0;
|
|
if (m_msgDBView != NULL && m_parsingDone && !m_updateAsWeGo)
|
|
{
|
|
uint32 viewCount;
|
|
|
|
m_msgDBView->NoteStartChange(0, 0, MSG_NotifyAll);
|
|
m_msgDBView->Init(&viewCount);
|
|
m_msgDBView->Sort(m_msgDBView->GetSortType(), m_msgDBView->GetSortOrder());
|
|
m_msgDBView->NoteEndChange(0, 0, MSG_NotifyAll);
|
|
if (m_pane)
|
|
FE_PaneChanged(m_pane, FALSE, MSG_PaneNotifyFolderLoaded, (uint32) m_folder);
|
|
}
|
|
if (!m_parsingDone)
|
|
{
|
|
if (m_mailDB != NULL)
|
|
{
|
|
m_mailDB->Close();
|
|
m_mailDB = NULL;
|
|
}
|
|
|
|
// If we've failed to create a summary file, don't leave the DB lying around
|
|
if (m_parseMsgState)
|
|
XP_FileRemove (m_mailboxName, xpMailFolderSummary);
|
|
}
|
|
}
|
|
|
|
|
|
void ParseMailboxState::DoneParsingFolder()
|
|
{
|
|
if (m_ibuffer_fp > 0)
|
|
{
|
|
m_parseMsgState->ParseFolderLine(m_ibuffer, m_ibuffer_fp);
|
|
m_ibuffer_fp = 0;
|
|
}
|
|
PublishMsgHeader();
|
|
|
|
if (m_mailDB != NULL) // finished parsing, so flush db folder info
|
|
UpdateDBFolderInfo();
|
|
|
|
if (m_folder != NULL)
|
|
m_folder->SummaryChanged();
|
|
|
|
FreeBuffers();
|
|
}
|
|
|
|
void ParseMailboxState::FreeBuffers()
|
|
{
|
|
/* We're done reading the folder - we don't need these things
|
|
any more. */
|
|
FREEIF (m_ibuffer);
|
|
m_ibuffer_size = 0;
|
|
FREEIF (m_obuffer);
|
|
m_obuffer_size = 0;
|
|
}
|
|
|
|
void ParseMailboxState::UpdateDBFolderInfo()
|
|
{
|
|
UpdateDBFolderInfo(m_mailDB, m_mailboxName);
|
|
}
|
|
|
|
// update folder info in db so we know not to reparse.
|
|
void ParseMailboxState::UpdateDBFolderInfo(MailDB *mailDB, const char *mailboxName)
|
|
{
|
|
XP_StatStruct folderst;
|
|
DBFolderInfo *folderInfo = mailDB->m_dbFolderInfo;
|
|
|
|
if (!XP_Stat (mailboxName, &folderst, xpMailFolder))
|
|
{
|
|
folderInfo->m_folderDate = folderst.st_mtime;
|
|
folderInfo->m_folderSize = folderst.st_size;
|
|
folderInfo->m_parsedThru = folderst.st_size;
|
|
// folderInfo->setDirty(); DMB TODO
|
|
}
|
|
mailDB->Commit();
|
|
// m_mailDB->Close();
|
|
}
|
|
|
|
// By default, do nothing
|
|
void ParseMailboxState::FolderTypeSpecificTweakMsgHeader(MailMessageHdr * /* tweakMe */)
|
|
{
|
|
}
|
|
|
|
// Tell the world about the message header (add to db, and view, if any)
|
|
int32 ParseMailboxState::PublishMsgHeader()
|
|
{
|
|
m_parseMsgState->FinishHeader();
|
|
if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
FolderTypeSpecificTweakMsgHeader(m_parseMsgState->m_newMsgHdr);
|
|
|
|
if (m_parseMsgState->m_newMsgHdr->GetFlags() & kExpunged)
|
|
{
|
|
DBFolderInfo *folderInfo = m_mailDB->m_dbFolderInfo;
|
|
folderInfo->m_expunged_bytes += m_parseMsgState->m_newMsgHdr->GetByteLength();
|
|
if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
}
|
|
else if (m_mailDB != NULL)
|
|
{
|
|
m_mailDB->AddHdrToDB(m_parseMsgState->m_newMsgHdr, NULL, m_updateAsWeGo);
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
else
|
|
XP_ASSERT(FALSE); // should have a DB, no?
|
|
}
|
|
else if (m_mailDB)
|
|
{
|
|
DBFolderInfo *folderInfo = m_mailDB->m_dbFolderInfo;
|
|
folderInfo->m_expunged_bytes += m_parseMsgState->m_position - m_parseMsgState->m_envelope_pos;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ParseMailboxState::AbortNewHeader()
|
|
{
|
|
if (m_parseMsgState->m_newMsgHdr && m_mailDB)
|
|
{
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
}
|
|
|
|
ParseMailMessageState *ParseMailboxState::GetMsgState()
|
|
{
|
|
return m_parseMsgState;
|
|
}
|
|
|
|
/* static */
|
|
int32 ParseMailboxState::LineBufferCallback(char *line, uint32 lineLength,
|
|
void *closure)
|
|
{
|
|
ParseMailboxState *parseState = (ParseMailboxState *) closure;
|
|
|
|
return parseState->ParseFolderLine(line, lineLength);
|
|
}
|
|
|
|
int32 ParseMailboxState::ParseFolderLine(const char *line, uint32 lineLength)
|
|
{
|
|
int status = 0;
|
|
|
|
if (m_mailDB && m_mailDB->GetDB())
|
|
{
|
|
m_parseMsgState->SetMailDB(m_mailDB);
|
|
}
|
|
// mailbox parser needs to do special stuff when it finds an envelope
|
|
// after parsing a message body. So do that.
|
|
if (line[0] == 'F' && msg_IsEnvelopeLine(line, lineLength))
|
|
{
|
|
// **** This used to be
|
|
// XP_ASSERT (m_parseMsgState->m_state == MBOX_PARSE_BODY);
|
|
// **** I am not sure this is a right thing to do. This happens when
|
|
// going online, downloading a message while playing back append
|
|
// draft/template offline operation. We are mixing MBOX_PARSE_BODY &&
|
|
// MBOX_PARSE_HEADERS state. **** jt
|
|
|
|
XP_ASSERT (m_parseMsgState->m_state == MBOX_PARSE_BODY ||
|
|
m_parseMsgState->m_state == MBOX_PARSE_HEADERS); /* else folder corrupted */
|
|
PublishMsgHeader();
|
|
m_parseMsgState->Clear();
|
|
status = m_parseMsgState->StartNewEnvelope(line, lineLength);
|
|
if (status < 0)
|
|
return status;
|
|
}
|
|
// otherwise, the message parser can handle it completely.
|
|
else if (m_mailDB != NULL) // if no DB, do we need to parse at all?
|
|
return m_parseMsgState->ParseFolderLine(line, lineLength);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ParseMailMessageState::ParseMailMessageState()
|
|
{
|
|
m_envelope = NULL;
|
|
m_headers = NULL;
|
|
m_headers_size = 0;
|
|
m_envelope_size = 0;
|
|
m_mailDB = NULL;
|
|
m_position = 0;
|
|
m_IgnoreXMozillaStatus = FALSE;
|
|
m_state = MBOX_PARSE_BODY;
|
|
Clear();
|
|
}
|
|
|
|
ParseMailMessageState::~ParseMailMessageState()
|
|
{
|
|
FREEIF(m_envelope);
|
|
FREEIF(m_headers);
|
|
ClearAggregateHeader (m_toList);
|
|
ClearAggregateHeader (m_ccList);
|
|
}
|
|
|
|
void ParseMailMessageState::Init(uint32 fileposition)
|
|
{
|
|
m_state = MBOX_PARSE_BODY;
|
|
m_position = fileposition;
|
|
m_newMsgHdr = NULL;
|
|
HG98330
|
|
}
|
|
|
|
void ParseMailMessageState::Clear()
|
|
{
|
|
m_headers_fp = 0;
|
|
m_envelope_fp = 0;
|
|
m_headers_size = 0;
|
|
m_message_id.length = 0;
|
|
m_references.length = 0;
|
|
m_date.length = 0;
|
|
m_from.length = 0;
|
|
m_sender.length = 0;
|
|
m_newsgroups.length = 0;
|
|
m_subject.length = 0;
|
|
m_status.length = 0;
|
|
m_mozstatus.length = 0;
|
|
m_mozstatus2.length = 0;
|
|
m_envelope_from.length = 0;
|
|
m_envelope_date.length = 0;
|
|
m_priority.length = 0;
|
|
m_mdn_dnt.length = 0;
|
|
m_return_path.length = 0;
|
|
m_mdn_original_recipient.length = 0;
|
|
m_body_lines = 0;
|
|
m_newMsgHdr = NULL;
|
|
m_envelope_pos = 0;
|
|
ClearAggregateHeader (m_toList);
|
|
ClearAggregateHeader (m_ccList);
|
|
}
|
|
|
|
int ParseMailMessageState::GrowHeaders(uint32 desired_size)
|
|
{
|
|
return (((desired_size) >= m_headers_size) ?
|
|
msg_GrowBuffer ((desired_size), sizeof(char), 1024,
|
|
&m_headers, &m_headers_size)
|
|
: 0);
|
|
}
|
|
|
|
int ParseMailMessageState::GrowEnvelope(uint32 desired_size)
|
|
{
|
|
return (((desired_size) >= m_envelope_size) ?
|
|
msg_GrowBuffer ((desired_size), sizeof(char), 255,
|
|
&m_envelope, &m_envelope_size)
|
|
: 0);
|
|
}
|
|
|
|
int32 ParseMailMessageState::ParseFolderLine(const char *line, uint32 lineLength)
|
|
{
|
|
int status = 0;
|
|
|
|
if (m_state == MBOX_PARSE_HEADERS)
|
|
{
|
|
if (EMPTY_MESSAGE_LINE(line))
|
|
{
|
|
/* End of headers. Now parse them. */
|
|
status = ParseHeaders();
|
|
if (status < 0)
|
|
return status;
|
|
|
|
status = FinalizeHeaders();
|
|
if (status < 0)
|
|
return status;
|
|
m_state = MBOX_PARSE_BODY;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, this line belongs to a header. So append it to the
|
|
header data, and stay in MBOX `MIME_PARSE_HEADERS' state.
|
|
*/
|
|
status = GrowHeaders (lineLength + m_headers_fp + 1);
|
|
if (status < 0) return status;
|
|
XP_MEMCPY (m_headers + m_headers_fp, line, lineLength);
|
|
m_headers_fp += lineLength;
|
|
}
|
|
}
|
|
else if ( m_state == MBOX_PARSE_BODY)
|
|
{
|
|
m_body_lines++;
|
|
}
|
|
|
|
m_position += lineLength;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ParseMailMessageState::SetMailDB(MailDB *mailDB)
|
|
{
|
|
m_mailDB = mailDB;
|
|
}
|
|
|
|
// We've found the start of the next message, so finish this one off.
|
|
void ParseMailMessageState::FinishHeader()
|
|
{
|
|
if (m_newMsgHdr)
|
|
{
|
|
m_newMsgHdr->SetMessageKey(m_envelope_pos);
|
|
m_newMsgHdr->SetByteLength(m_position - m_envelope_pos);
|
|
m_newMsgHdr->SetMessageSize(m_position - m_envelope_pos); // dmb - no longer number of lines.
|
|
m_newMsgHdr->SetLineCount(m_body_lines);
|
|
}
|
|
}
|
|
|
|
struct message_header *ParseMailMessageState::GetNextHeaderInAggregate (XPPtrArray &list)
|
|
{
|
|
// When parsing a message with multiple To or CC header lines, we're storing each line in a
|
|
// list, where the list represents the "aggregate" total of all the header. Here we get a new
|
|
// line for the list
|
|
|
|
struct message_header *header = (struct message_header*) XP_CALLOC (1, sizeof(struct message_header));
|
|
list.Add (header);
|
|
return header;
|
|
}
|
|
|
|
void ParseMailMessageState::GetAggregateHeader (XPPtrArray &list, struct message_header *outHeader)
|
|
{
|
|
// When parsing a message with multiple To or CC header lines, we're storing each line in a
|
|
// list, where the list represents the "aggregate" total of all the header. Here we combine
|
|
// all the lines together, as though they were really all found on the same line
|
|
|
|
struct message_header *header = NULL;
|
|
int length = 0;
|
|
int i;
|
|
|
|
// Count up the bytes required to allocate the aggregated header
|
|
for (i = 0; i < list.GetSize(); i++)
|
|
{
|
|
header = (struct message_header*) list.GetAt(i);
|
|
length += (header->length + 1); //+ for ","
|
|
XP_ASSERT(header->length == XP_STRLEN(header->value));
|
|
}
|
|
|
|
if (length > 0)
|
|
{
|
|
char *value = (char*) XP_ALLOC (length + 1); //+1 for null term
|
|
if (value)
|
|
{
|
|
// Catenate all the To lines together, separated by commas
|
|
value[0] = '\0';
|
|
int size = list.GetSize();
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
header = (struct message_header*) list.GetAt(i);
|
|
XP_STRCAT (value, header->value);
|
|
if (i + 1 < size)
|
|
XP_STRCAT (value, ",");
|
|
}
|
|
outHeader->length = length;
|
|
outHeader->value = value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outHeader->length = 0;
|
|
outHeader->value = NULL;
|
|
}
|
|
}
|
|
|
|
void ParseMailMessageState::ClearAggregateHeader (XPPtrArray &list)
|
|
{
|
|
// Reset the aggregate headers. Free only the message_header struct since
|
|
// we don't own the value pointer
|
|
|
|
for (int i = 0; i < list.GetSize(); i++)
|
|
XP_FREE ((struct message_header*) list.GetAt(i));
|
|
list.RemoveAll();
|
|
}
|
|
|
|
// We've found a new envelope to parse.
|
|
int ParseMailMessageState::StartNewEnvelope(const char *line, uint32 lineLength)
|
|
{
|
|
m_envelope_pos = m_position;
|
|
m_state = MBOX_PARSE_HEADERS;
|
|
m_position += lineLength;
|
|
m_headerstartpos = m_position;
|
|
return ParseEnvelope (line, lineLength);
|
|
}
|
|
|
|
/* largely taken from mimehtml.c, which does similar parsing, sigh...
|
|
*/
|
|
int ParseMailMessageState::ParseHeaders ()
|
|
{
|
|
char *buf = m_headers;
|
|
char *buf_end = buf + m_headers_fp;
|
|
while (buf < buf_end)
|
|
{
|
|
char *colon = XP_STRCHR (buf, ':');
|
|
char *end;
|
|
char *value = 0;
|
|
struct message_header *header = 0;
|
|
|
|
if (! colon)
|
|
break;
|
|
|
|
end = colon;
|
|
while (end > buf && (*end == ' ' || *end == '\t'))
|
|
end--;
|
|
|
|
switch (buf [0])
|
|
{
|
|
case 'C': case 'c':
|
|
if (!strncasecomp ("CC", buf, end - buf))
|
|
header = GetNextHeaderInAggregate(m_ccList);
|
|
break;
|
|
case 'D': case 'd':
|
|
if (!strncasecomp ("Date", buf, end - buf))
|
|
header = &m_date;
|
|
else if (!strncasecomp("Disposition-Notification-To", buf, end - buf))
|
|
header = &m_mdn_dnt;
|
|
break;
|
|
case 'F': case 'f':
|
|
if (!strncasecomp ("From", buf, end - buf))
|
|
header = &m_from;
|
|
break;
|
|
case 'M': case 'm':
|
|
if (!strncasecomp ("Message-ID", buf, end - buf))
|
|
header = &m_message_id;
|
|
break;
|
|
case 'N': case 'n':
|
|
if (!strncasecomp ("Newsgroups", buf, end - buf))
|
|
header = &m_newsgroups;
|
|
break;
|
|
case 'O': case 'o':
|
|
if (!strncasecomp ("Original-Recipient", buf, end - buf))
|
|
header = &m_mdn_original_recipient;
|
|
break;
|
|
case 'R': case 'r':
|
|
if (!strncasecomp ("References", buf, end - buf))
|
|
header = &m_references;
|
|
else if (!strncasecomp ("Return-Path", buf, end - buf))
|
|
header = &m_return_path;
|
|
// treat conventional Return-Receipt-To as MDN
|
|
// Disposition-Notification-To
|
|
else if (!strncasecomp ("Return-Receipt-To", buf, end - buf))
|
|
header = &m_mdn_dnt;
|
|
break;
|
|
case 'S': case 's':
|
|
if (!strncasecomp ("Subject", buf, end - buf))
|
|
header = &m_subject;
|
|
else if (!strncasecomp ("Sender", buf, end - buf))
|
|
header = &m_sender;
|
|
else if (!strncasecomp ("Status", buf, end - buf))
|
|
header = &m_status;
|
|
break;
|
|
case 'T': case 't':
|
|
if (!strncasecomp ("To", buf, end - buf))
|
|
header = GetNextHeaderInAggregate(m_toList);
|
|
break;
|
|
case 'X':
|
|
if (X_MOZILLA_STATUS2_LEN == end - buf &&
|
|
!strncasecomp(X_MOZILLA_STATUS2, buf, end - buf) &&
|
|
!m_IgnoreXMozillaStatus)
|
|
header = &m_mozstatus2;
|
|
else if ( X_MOZILLA_STATUS_LEN == end - buf &&
|
|
!strncasecomp(X_MOZILLA_STATUS, buf, end - buf) && !m_IgnoreXMozillaStatus)
|
|
header = &m_mozstatus;
|
|
// we could very well care what the priority header was when we
|
|
// remember its value. If so, need to remember it here. Also,
|
|
// different priority headers can appear in the same message,
|
|
// but we only rememeber the last one that we see.
|
|
else if (!strncasecomp("X-Priority", buf, end - buf)
|
|
|| !strncasecomp("Priority", buf, end - buf))
|
|
header = &m_priority;
|
|
break;
|
|
}
|
|
|
|
buf = colon + 1;
|
|
while (*buf == ' ' || *buf == '\t')
|
|
buf++;
|
|
|
|
value = buf;
|
|
if (header)
|
|
header->value = value;
|
|
|
|
SEARCH_NEWLINE:
|
|
while (*buf != 0 && *buf != CR && *buf != LF)
|
|
buf++;
|
|
|
|
if (buf+1 >= buf_end)
|
|
;
|
|
/* If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. */
|
|
else if (buf+2 < buf_end &&
|
|
(buf[0] == CR && buf[1] == LF) &&
|
|
(buf[2] == ' ' || buf[2] == '\t'))
|
|
{
|
|
buf += 3;
|
|
goto SEARCH_NEWLINE;
|
|
}
|
|
/* If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
|
|
the header either. */
|
|
else if ((buf[0] == CR || buf[0] == LF) &&
|
|
(buf[1] == ' ' || buf[1] == '\t'))
|
|
{
|
|
buf += 2;
|
|
goto SEARCH_NEWLINE;
|
|
}
|
|
|
|
if (header)
|
|
header->length = buf - header->value;
|
|
|
|
if (*buf == CR || *buf == LF)
|
|
{
|
|
char *last = buf;
|
|
if (*buf == CR && buf[1] == LF)
|
|
buf++;
|
|
buf++;
|
|
*last = 0; /* short-circuit const, and null-terminate header. */
|
|
}
|
|
|
|
if (header)
|
|
{
|
|
/* More const short-circuitry... */
|
|
/* strip leading whitespace */
|
|
while (XP_IS_SPACE (*header->value))
|
|
header->value++, header->length--;
|
|
/* strip trailing whitespace */
|
|
while (header->length > 0 &&
|
|
XP_IS_SPACE (header->value [header->length - 1]))
|
|
((char *) header->value) [--header->length] = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ParseMailMessageState::ParseEnvelope (const char *line, uint32 line_size)
|
|
{
|
|
const char *end;
|
|
char *s;
|
|
int status = 0;
|
|
|
|
status = GrowEnvelope (line_size + 1);
|
|
if (status < 0) return status;
|
|
XP_MEMCPY (m_envelope, line, line_size);
|
|
m_envelope_fp = line_size;
|
|
m_envelope [line_size] = 0;
|
|
end = m_envelope + line_size;
|
|
s = m_envelope + 5;
|
|
|
|
while (s < end && XP_IS_SPACE (*s))
|
|
s++;
|
|
m_envelope_from.value = s;
|
|
while (s < end && !XP_IS_SPACE (*s))
|
|
s++;
|
|
m_envelope_from.length = s - m_envelope_from.value;
|
|
|
|
while (s < end && XP_IS_SPACE (*s))
|
|
s++;
|
|
m_envelope_date.value = s;
|
|
m_envelope_date.length = (uint16) (line_size - (s - m_envelope));
|
|
while (XP_IS_SPACE (m_envelope_date.value [m_envelope_date.length - 1]))
|
|
m_envelope_date.length--;
|
|
|
|
/* #### short-circuit const */
|
|
((char *) m_envelope_from.value) [m_envelope_from.length] = 0;
|
|
((char *) m_envelope_date.value) [m_envelope_date.length] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C"
|
|
{
|
|
char *strip_continuations(char *original);
|
|
int16 INTL_DefaultMailToWinCharSetID(int16 csid);
|
|
char *INTL_EncodeMimePartIIStr_VarLen(char *subject, int16 wincsid, XP_Bool bUseMime,
|
|
int encodedWordLen);
|
|
}
|
|
|
|
static char *
|
|
msg_condense_mime2_string(char *sourceStr)
|
|
{
|
|
int16 string_csid = CS_DEFAULT;
|
|
int16 win_csid = CS_DEFAULT;
|
|
|
|
char *returnVal = XP_STRDUP(sourceStr);
|
|
if (!returnVal)
|
|
return NULL;
|
|
|
|
// If sourceStr has a MIME-2 encoded word in it, get the charset
|
|
// name/ID from the first encoded word.
|
|
char *p = XP_STRSTR(returnVal, "=?");
|
|
if (p)
|
|
{
|
|
p += 2;
|
|
char *q = XP_STRCHR(p, '?');
|
|
if (q) *q = '\0';
|
|
string_csid = INTL_CharSetNameToID(p);
|
|
win_csid = INTL_DocToWinCharSetID(string_csid);
|
|
if (q) *q = '?';
|
|
|
|
// Decode any MIME-2 encoded strings, to save the overhead.
|
|
char *cvt = INTL_DecodeMimePartIIStr(returnVal, win_csid, FALSE);
|
|
if (cvt)
|
|
{
|
|
if (cvt != returnVal)
|
|
{
|
|
XP_FREEIF(returnVal);
|
|
returnVal = cvt;
|
|
}
|
|
// MIME-2 decoding occurred, so re-encode into large encoded words
|
|
cvt = INTL_EncodeMimePartIIStr_VarLen(returnVal, win_csid, TRUE,
|
|
MSG_MAXSUBJECTLENGTH - 2);
|
|
if (cvt && (cvt != returnVal))
|
|
{
|
|
XP_FREE(returnVal); // encoding happened, deallocate decoded text
|
|
returnVal = strip_continuations(cvt); // and remove CR+LF+spaces that occur
|
|
}
|
|
// else returnVal == cvt, in which case nothing needs to be done
|
|
}
|
|
else
|
|
// no MIME-2 decoding occurred, so strip CR+LF+spaces ourselves
|
|
strip_continuations(returnVal);
|
|
}
|
|
else if (returnVal)
|
|
strip_continuations(returnVal);
|
|
|
|
return returnVal;
|
|
}
|
|
|
|
int ParseMailMessageState::InternSubject (struct message_header *header)
|
|
{
|
|
char *key;
|
|
uint32 L;
|
|
MSG_DBHandle db = (m_mailDB) ? m_mailDB->GetDB() : 0;
|
|
|
|
if (!header || header->length == 0)
|
|
{
|
|
m_newMsgHdr->SetSubject("", db);
|
|
return 0;
|
|
}
|
|
|
|
XP_ASSERT (header->length == (short) XP_STRLEN (header->value));
|
|
|
|
key = (char *) header->value; /* #### const evilness */
|
|
|
|
L = header->length;
|
|
|
|
|
|
/* strip "Re: " */
|
|
if (msg_StripRE((const char **) &key, &L))
|
|
{
|
|
m_newMsgHdr->SetFlags(m_newMsgHdr->GetFlags() | kHasRe);
|
|
}
|
|
|
|
// if (!*key) return 0; /* To catch a subject of "Re:" */
|
|
|
|
// Condense the subject text into as few MIME-2 encoded words as possible.
|
|
char *condensedKey = msg_condense_mime2_string(key);
|
|
|
|
m_newMsgHdr->SetSubject(condensedKey ? condensedKey : key, db);
|
|
XP_FREEIF(condensedKey);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Like mbox_intern() but for headers which contain email addresses:
|
|
we extract the "name" component of the first address, and discard
|
|
the rest. */
|
|
int ParseMailMessageState::InternRfc822 (struct message_header *header,
|
|
char **ret_name)
|
|
{
|
|
char *s;
|
|
|
|
if (!header || header->length == 0)
|
|
return 0;
|
|
|
|
XP_ASSERT (header->length == (short) XP_STRLEN (header->value));
|
|
XP_ASSERT (ret_name != NULL);
|
|
/* #### The mallocs here might be a performance bottleneck... */
|
|
s = MSG_ExtractRFC822AddressName (header->value);
|
|
if (! s)
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
*ret_name = s;
|
|
return 0;
|
|
}
|
|
|
|
int ParseMailMessageState::FinalizeHeaders()
|
|
{
|
|
int status = 0;
|
|
struct message_header *sender;
|
|
struct message_header *recipient;
|
|
struct message_header *subject;
|
|
struct message_header *id;
|
|
struct message_header *references;
|
|
struct message_header *date;
|
|
struct message_header *statush;
|
|
struct message_header *mozstatus;
|
|
struct message_header *mozstatus2;
|
|
struct message_header *priority;
|
|
struct message_header *ccList;
|
|
struct message_header *mdn_dnt;
|
|
|
|
HG23277
|
|
const char *s;
|
|
uint32 flags = 0;
|
|
uint32 delta = 0;
|
|
MSG_PRIORITY priorityFlags = MSG_PriorityNotSet;
|
|
|
|
MSG_DBHandle db = (m_mailDB) ? m_mailDB->GetDB() : 0;
|
|
if (!db) // if we don't have a valid db, skip the header.
|
|
return 0;
|
|
#ifdef _USRDLL
|
|
return(0);
|
|
#endif
|
|
|
|
struct message_header to;
|
|
GetAggregateHeader (m_toList, &to);
|
|
struct message_header cc;
|
|
GetAggregateHeader (m_ccList, &cc);
|
|
|
|
sender = (m_from.length ? &m_from :
|
|
m_sender.length ? &m_sender :
|
|
m_envelope_from.length ? &m_envelope_from :
|
|
0);
|
|
recipient = (to.length ? &to :
|
|
cc.length ? &cc :
|
|
m_newsgroups.length ? &m_newsgroups :
|
|
sender);
|
|
ccList = (cc.length ? &cc : 0);
|
|
subject = (m_subject.length ? &m_subject : 0);
|
|
id = (m_message_id.length ? &m_message_id : 0);
|
|
references = (m_references.length ? &m_references : 0);
|
|
statush = (m_status.length ? &m_status : 0);
|
|
mozstatus = (m_mozstatus.length ? &m_mozstatus : 0);
|
|
mozstatus2 = (m_mozstatus2.length ? &m_mozstatus2 : 0);
|
|
date = (m_date.length ? &m_date :
|
|
m_envelope_date.length ? &m_envelope_date :
|
|
0);
|
|
priority = (m_priority.length ? &m_priority : 0);
|
|
mdn_dnt = (m_mdn_dnt.length ? &m_mdn_dnt : 0);
|
|
|
|
if (mozstatus)
|
|
{
|
|
if (strlen(mozstatus->value) == 4)
|
|
{
|
|
#define UNHEX(C) \
|
|
((C >= '0' && C <= '9') ? C - '0' : \
|
|
((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
|
|
((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
|
|
int i;
|
|
for (i=0,s=mozstatus->value ; i<4 ; i++,s++)
|
|
{
|
|
flags = (flags << 4) | UNHEX(*s);
|
|
}
|
|
// strip off and remember priority bits.
|
|
flags &= ~MSG_FLAG_RUNTIME_ONLY;
|
|
priorityFlags = (MSG_PRIORITY) ((flags & MSG_FLAG_PRIORITIES) >> 13);
|
|
flags &= ~MSG_FLAG_PRIORITIES;
|
|
/* We trust the X-Mozilla-Status line to be the smartest in almost
|
|
all things. One exception, however, is the HAS_RE flag. Since
|
|
we just parsed the subject header anyway, we expect that parsing
|
|
to be smartest. (After all, what if someone just went in and
|
|
edited the subject line by hand?) */
|
|
}
|
|
delta = (m_headerstartpos +
|
|
(mozstatus->value - m_headers) -
|
|
(2 + X_MOZILLA_STATUS_LEN) /* 2 extra bytes for ": ". */
|
|
) - m_envelope_pos;
|
|
}
|
|
|
|
if (mozstatus2)
|
|
{
|
|
uint32 flags2 = 0;
|
|
sscanf(mozstatus2->value, " %lx ", &flags2);
|
|
flags |= flags2;
|
|
}
|
|
|
|
if (!(flags & MSG_FLAG_EXPUNGED)) // message was deleted, don't bother creating a hdr.
|
|
{
|
|
m_newMsgHdr = new MailMessageHdr; // TODO - should be try catch?
|
|
if (m_newMsgHdr)
|
|
{
|
|
if (m_newMsgHdr->GetFlags() & kHasRe)
|
|
flags |= MSG_FLAG_HAS_RE;
|
|
else
|
|
flags &= ~MSG_FLAG_HAS_RE;
|
|
|
|
if (mdn_dnt && !(m_newMsgHdr->GetFlags() & kIsRead) &&
|
|
!(m_newMsgHdr->GetFlags() & kMDNSent))
|
|
flags |= MSG_FLAG_MDN_REPORT_NEEDED;
|
|
|
|
MessageDB::ConvertPublicFlagsToDBFlags(&flags);
|
|
m_newMsgHdr->SetFlags(flags);
|
|
if (priorityFlags != MSG_PriorityNotSet)
|
|
m_newMsgHdr->SetPriority(priorityFlags);
|
|
|
|
if (delta < 0xffff)
|
|
{ /* Only use if fits in 16 bits. */
|
|
m_newMsgHdr->SetStatusOffset((uint16) delta);
|
|
if (!m_IgnoreXMozillaStatus) // imap doesn't care about X-MozillaStatus
|
|
XP_ASSERT(m_newMsgHdr->GetStatusOffset() < 10000); /* ### Debugging hack */
|
|
}
|
|
m_newMsgHdr->SetAuthor(sender->value, db);
|
|
if (recipient == &m_newsgroups)
|
|
{
|
|
/* In the case where the recipient is a newsgroup, truncate the string
|
|
at the first comma. This is used only for presenting the thread list,
|
|
and newsgroup lines tend to be long and non-shared, and tend to bloat
|
|
the string table. So, by only showing the first newsgroup, we can
|
|
reduce memory and file usage at the expense of only showing the one
|
|
group in the summary list, and only being able to sort on the first
|
|
group rather than the whole list. It's worth it. */
|
|
char *s;
|
|
XP_ASSERT (recipient->length == (uint16) XP_STRLEN (recipient->value));
|
|
s = XP_STRCHR (recipient->value, ',');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
recipient->length = XP_STRLEN (recipient->value);
|
|
}
|
|
m_newMsgHdr->SetRecipients(recipient->value, db, FALSE);
|
|
}
|
|
else
|
|
{
|
|
// note that we're now setting the whole recipient list,
|
|
// not just the pretty name of the first recipient.
|
|
m_newMsgHdr->SetRecipients(recipient->value, db, TRUE);
|
|
}
|
|
if (ccList)
|
|
m_newMsgHdr->SetCCList(ccList->value, db);
|
|
status = InternSubject (subject);
|
|
if (status >= 0)
|
|
{
|
|
|
|
HG92923
|
|
/* Take off <> around message ID. */
|
|
if (id->value[0] == '<')
|
|
id->value++, id->length--;
|
|
if (id->value[id->length-1] == '>')
|
|
((char *) id->value) [id->length-1] = 0, id->length--; /* #### const */
|
|
|
|
m_newMsgHdr->SetMessageId(id->value, db);
|
|
|
|
if (!mozstatus && statush)
|
|
{
|
|
/* Parse a little bit of the Berkeley Mail status header. */
|
|
for (s = statush->value; *s; s++)
|
|
switch (*s)
|
|
{
|
|
case 'R': case 'r':
|
|
m_newMsgHdr->SetFlags(m_newMsgHdr->GetFlags() | MSG_FLAG_READ);
|
|
break;
|
|
case 'D': case 'd':
|
|
/* msg->flags |= MSG_FLAG_EXPUNGED; ### Is this reasonable? */
|
|
break;
|
|
case 'N': case 'n':
|
|
case 'U': case 'u':
|
|
m_newMsgHdr->SetFlags(m_newMsgHdr->GetFlags() & ~MSG_FLAG_READ);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (references != NULL)
|
|
m_newMsgHdr->SetReferences(references->value, db);
|
|
if (date)
|
|
m_newMsgHdr->SetDate(XP_ParseTimeString (date->value, FALSE));
|
|
|
|
if (priority)
|
|
m_newMsgHdr->SetPriority(priority->value);
|
|
else if (priorityFlags == MSG_PriorityNotSet)
|
|
m_newMsgHdr->SetPriority(MSG_NoPriority);
|
|
}
|
|
}
|
|
else
|
|
status = MK_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
status = 0;
|
|
|
|
//### why is this stuff const?
|
|
char *tmp = (char*) to.value;
|
|
XP_FREEIF(tmp);
|
|
tmp = (char*) cc.value;
|
|
XP_FREEIF(tmp);
|
|
|
|
return status;
|
|
}
|
|
|
|
int ParseNewMailState::MarkFilteredMessageRead(MailMessageHdr *msgHdr)
|
|
{
|
|
if (m_mailDB)
|
|
m_mailDB->MarkHdrRead(msgHdr, TRUE, NULL);
|
|
else
|
|
msgHdr->OrFlags(kIsRead);
|
|
return 0;
|
|
}
|
|
|
|
int ParseNewMailState::MoveIncorporatedMessage(MailMessageHdr *mailHdr,
|
|
MailDB *sourceDB,
|
|
char *destFolder,
|
|
MSG_Filter *filter)
|
|
{
|
|
int err = 0;
|
|
XP_File destFid;
|
|
XP_File sourceFid = m_file;
|
|
|
|
// Make sure no one else is writing into this folder
|
|
MSG_FolderInfo *lockedFolder = m_mailMaster->FindMailFolder (destFolder, FALSE /*create*/);
|
|
if (lockedFolder && (err = lockedFolder->AcquireSemaphore (this)) != 0)
|
|
return err;
|
|
|
|
if (sourceFid == 0)
|
|
{
|
|
sourceFid = XP_FileOpen(m_mailboxName,
|
|
xpMailFolder, XP_FILE_READ_BIN);
|
|
}
|
|
XP_ASSERT(sourceFid != 0);
|
|
if (sourceFid == 0)
|
|
{
|
|
#ifdef DEBUG_bienvenu
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
if (lockedFolder)
|
|
lockedFolder->ReleaseSemaphore (this);
|
|
|
|
return MK_MSG_FOLDER_UNREADABLE; // ### dmb
|
|
}
|
|
|
|
XP_FileSeek (sourceFid, mailHdr->GetMessageOffset(), SEEK_SET);
|
|
int newMsgPos;
|
|
|
|
destFid = XP_FileOpen(destFolder, xpMailFolder, XP_FILE_APPEND_BIN);
|
|
|
|
if (!destFid)
|
|
{
|
|
#ifdef DEBUG_bienvenu
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
if (lockedFolder)
|
|
lockedFolder->ReleaseSemaphore (this);
|
|
XP_FileClose (sourceFid);
|
|
return MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
}
|
|
|
|
if (!XP_FileSeek (destFid, 0, SEEK_END))
|
|
{
|
|
newMsgPos = ftell (destFid);
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
if (lockedFolder)
|
|
lockedFolder->ReleaseSemaphore (this);
|
|
XP_FileClose (destFid);
|
|
XP_FileClose (sourceFid);
|
|
return MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
}
|
|
|
|
HG98373
|
|
uint32 length = mailHdr->GetByteLength();
|
|
|
|
m_ibuffer_size = 10240;
|
|
m_ibuffer = NULL;
|
|
|
|
while (!m_ibuffer && (m_ibuffer_size >= 512))
|
|
{
|
|
m_ibuffer = (char *) XP_ALLOC(m_ibuffer_size);
|
|
if (m_ibuffer == NULL)
|
|
m_ibuffer_size /= 2;
|
|
}
|
|
XP_ASSERT(m_ibuffer != NULL);
|
|
while ((length > 0) && m_ibuffer)
|
|
{
|
|
uint32 nRead = XP_FileRead (m_ibuffer, length > m_ibuffer_size ? m_ibuffer_size : length, sourceFid);
|
|
if (nRead == 0)
|
|
break;
|
|
|
|
// we must monitor the number of bytes actually written to the file. (mscott)
|
|
if (XP_FileWrite (m_ibuffer, nRead, destFid) != nRead)
|
|
{
|
|
XP_FileClose(sourceFid);
|
|
XP_FileClose(destFid);
|
|
|
|
// truncate destination file in case message was partially written
|
|
XP_FileTruncate(destFolder,xpMailFolder,newMsgPos);
|
|
|
|
if (lockedFolder)
|
|
lockedFolder->ReleaseSemaphore(this);
|
|
|
|
return MK_MSG_ERROR_WRITING_MAIL_FOLDER; // caller (ApplyFilters) currently ignores error conditions
|
|
}
|
|
|
|
length -= nRead;
|
|
}
|
|
|
|
XP_ASSERT(length == 0);
|
|
|
|
// if we have made it this far then the message has successfully been written to the new folder
|
|
// now add the header to the mailDb.
|
|
|
|
MailDB *mailDb = NULL;
|
|
// don't force upgrade in place
|
|
MsgERR msgErr = MailDB::Open (destFolder, TRUE, &mailDb);
|
|
if (eSUCCESS == msgErr)
|
|
{
|
|
MailMessageHdr *newHdr = new MailMessageHdr();
|
|
if (newHdr)
|
|
{
|
|
newHdr->CopyFromMsgHdr (mailHdr, sourceDB->GetDB(), mailDb->GetDB());
|
|
// set new byte offset, since the offset in the old file is certainly wrong
|
|
newHdr->SetMessageKey (newMsgPos);
|
|
newHdr->OrFlags(kNew);
|
|
|
|
msgErr = mailDb->AddHdrToDB (newHdr, NULL, m_updateAsWeGo);
|
|
delete newHdr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mailDb)
|
|
{
|
|
mailDb->Close();
|
|
mailDb = NULL;
|
|
}
|
|
}
|
|
|
|
XP_FileClose(sourceFid);
|
|
XP_FileClose(destFid);
|
|
int truncRet = XP_FileTruncate(m_mailboxName, xpMailFolder, mailHdr->GetMessageOffset());
|
|
XP_ASSERT(truncRet >= 0);
|
|
|
|
if (lockedFolder)
|
|
lockedFolder->ReleaseSemaphore (this);
|
|
|
|
// tell outgoing parser that we've truncated the Inbox
|
|
m_parseMsgState->Init(mailHdr->GetMessageOffset());
|
|
MSG_FolderInfo *folder = m_mailMaster->FindMailFolder(destFolder, FALSE);
|
|
|
|
if (folder)
|
|
folder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW);
|
|
|
|
if (mailDb != NULL)
|
|
{
|
|
// update the folder size so we won't reparse.
|
|
UpdateDBFolderInfo(mailDb, destFolder);
|
|
if (folder != NULL)
|
|
folder->SummaryChanged();
|
|
|
|
mailDb->Close();
|
|
}
|
|
// We are logging the hit with the old mailHdr, which should work, as long
|
|
// as LogRuleHit doesn't assume the new hdr.
|
|
if (m_filterList->IsLoggingEnabled())
|
|
LogRuleHit(filter, mailHdr);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ParseNewMailState::ParseNewMailState(MSG_Master *master, MSG_FolderInfoMail
|
|
*folder) :
|
|
ParseMailboxState(folder->GetPathname())
|
|
{
|
|
SetMaster(master);
|
|
if (MSG_FilterList::Open(master, filterInbox, NULL, folder, &m_filterList)
|
|
!= FilterError_Success)
|
|
m_filterList = NULL;
|
|
if (m_filterList)
|
|
{
|
|
const char *folderName = NULL;
|
|
int32 int_pref = 0;
|
|
PREF_GetIntPref("mail.incorporate.return_receipt", &int_pref);
|
|
if (int_pref == 1)
|
|
{
|
|
MSG_FolderInfo *folderInfo = NULL;
|
|
int status = 0;
|
|
char *defaultFolderName =
|
|
msg_MagicFolderName(master->GetPrefs(),
|
|
MSG_FOLDER_FLAG_SENTMAIL, &status);
|
|
if (defaultFolderName)
|
|
{
|
|
folderInfo = master->FindMailFolder(defaultFolderName, FALSE);
|
|
if (folderInfo && folderInfo->GetMailFolderInfo())
|
|
folderName = folderInfo->GetMailFolderInfo()->GetPathname();
|
|
XP_FREE(defaultFolderName);
|
|
}
|
|
}
|
|
|
|
if (folderName)
|
|
{
|
|
MSG_Filter *newFilter = new MSG_Filter(filterInboxRule, "receipt");
|
|
if (newFilter)
|
|
{
|
|
MSG_Rule *rule = NULL;
|
|
MSG_SearchValue value;
|
|
newFilter->SetDescription("incorporate mdn report");
|
|
newFilter->SetEnabled(TRUE);
|
|
newFilter->GetRule(&rule);
|
|
newFilter->SetFilterList(m_filterList);
|
|
value.attribute = attribOtherHeader;
|
|
value.u.string = "multipart/report";
|
|
rule->AddTerm(attribOtherHeader, opContains,
|
|
&value, TRUE, "Content-Type");
|
|
value.u.string = "disposition-notification";
|
|
rule->AddTerm(attribOtherHeader, opContains,
|
|
&value, TRUE, "Content-Type");
|
|
#if 0
|
|
value.u.string = "delivery-status";
|
|
rule->AddTerm(attribOtherHeader, opContains,
|
|
&value, FALSE, "Content-Type");
|
|
#endif
|
|
rule->SetAction(acMoveToFolder, (void*)folderName);
|
|
m_filterList->InsertFilterAt(0, newFilter);
|
|
}
|
|
}
|
|
}
|
|
m_logFile = NULL;
|
|
m_usingTempDB = FALSE;
|
|
m_tmpdbName = NULL;
|
|
m_disableFilters = FALSE;
|
|
}
|
|
|
|
ParseNewMailState::~ParseNewMailState()
|
|
{
|
|
if (m_filterList != NULL)
|
|
MSG_CancelFilterList(m_filterList);
|
|
if (m_logFile != NULL)
|
|
XP_FileClose(m_logFile);
|
|
if (m_mailDB)
|
|
m_mailDB->Close();
|
|
if (m_usingTempDB)
|
|
{
|
|
XP_FileRemove(m_tmpdbName, xpMailFolderSummary);
|
|
}
|
|
FREEIF(m_tmpdbName);
|
|
JSFilter_cleanup();
|
|
}
|
|
|
|
void ParseNewMailState::LogRuleHit(MSG_Filter *filter, MailMessageHdr *msgHdr)
|
|
{
|
|
char *filterName = "";
|
|
time_t date;
|
|
char dateStr[40]; /* 30 probably not enough */
|
|
MSG_RuleActionType actionType;
|
|
MSG_Rule *rule;
|
|
void *value;
|
|
MSG_DBHandle db = (m_mailDB) ? m_mailDB->GetDB() : 0;
|
|
XPStringObj author;
|
|
XPStringObj subject;
|
|
|
|
if (m_logFile == NULL)
|
|
m_logFile = XP_FileOpen("", xpMailFilterLog, XP_FILE_APPEND); // will this create?
|
|
|
|
filter->GetName(&filterName);
|
|
if (filter->GetRule(&rule) != FilterError_Success)
|
|
return;
|
|
rule->GetAction(&actionType, &value);
|
|
date = msgHdr->GetDate();
|
|
XP_StrfTime(m_context, dateStr, sizeof(dateStr), XP_DATE_TIME_FORMAT,
|
|
localtime(&date));
|
|
msgHdr->GetAuthor(author, db);
|
|
msgHdr->GetSubject(subject, TRUE, db);
|
|
if (m_logFile)
|
|
{
|
|
XP_FilePrintf(m_logFile, "Applied filter \"%s\" to message from %s - %s at %s\n", filterName,
|
|
(const char *) author, (const char *) subject, dateStr);
|
|
char *actionStr = rule->GetActionStr(actionType);
|
|
char *actionValue = "";
|
|
if (actionType == acMoveToFolder)
|
|
actionValue = (char *) value;
|
|
XP_FilePrintf(m_logFile, "Action = %s %s\n\n", actionStr, actionValue);
|
|
if (actionType == acMoveToFolder)
|
|
{
|
|
XPStringObj msgId;
|
|
msgHdr->GetMessageId(msgId, db);
|
|
XP_FilePrintf(m_logFile, "mailbox:%s?id=%s\n", value, (const char *) msgId);
|
|
}
|
|
}
|
|
}
|
|
|
|
MSG_FolderInfoMail *ParseNewMailState::GetTrashFolder()
|
|
{
|
|
MSG_FolderInfo *foundTrash = NULL;
|
|
GetMaster()->GetLocalMailFolderTree()->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH, &foundTrash, 1);
|
|
return foundTrash ? foundTrash->GetMailFolderInfo() : (MSG_FolderInfoMail *)NULL;
|
|
}
|
|
|
|
void ParseNewMailState::ApplyFilters(XP_Bool *pMoved)
|
|
{
|
|
MSG_Filter *filter;
|
|
int32 filterCount = 0;
|
|
XP_Bool msgMoved = FALSE;
|
|
MsgERR err = eSUCCESS;
|
|
|
|
MailMessageHdr *msgHdr = GetCurrentMsg();
|
|
if (m_filterList != NULL)
|
|
m_filterList->GetFilterCount(&filterCount);
|
|
|
|
for (MSG_FilterIndex filterIndex = 0; filterIndex < filterCount; filterIndex++)
|
|
{
|
|
if (m_filterList->GetFilterAt(filterIndex, &filter) == FilterError_Success)
|
|
{
|
|
if (!filter->GetEnabled())
|
|
continue;
|
|
|
|
if (filter->GetType() == filterInboxJavaScript)
|
|
{
|
|
if (JSMailFilter_execute(this, filter, msgHdr, m_mailDB, &msgMoved) == SearchError_Success)
|
|
break;
|
|
}
|
|
else if (filter->GetType() == filterInboxRule)
|
|
{
|
|
MSG_Rule *rule;
|
|
MSG_SearchError matchTermStatus;
|
|
|
|
if (filter->GetRule(&rule) == FilterError_Success)
|
|
{
|
|
{ // put this in its own block so scope will get destroyed
|
|
// before we apply the actions, so folder file will get closed.
|
|
MSG_ScopeTerm scope (NULL, scopeMailFolder, m_folder);
|
|
|
|
char * headers = GetMsgState()->m_headers;
|
|
uint32 headersSize = GetMsgState()->m_headers_fp;
|
|
|
|
matchTermStatus = msg_SearchOfflineMail::MatchTermsForFilter(
|
|
msgHdr, rule->GetTermList(), &scope, m_mailDB, headers, headersSize);
|
|
|
|
}
|
|
if (matchTermStatus == SearchError_Success)
|
|
{
|
|
MSG_RuleActionType actionType;
|
|
void *value = NULL;
|
|
// look at action - currently handle move
|
|
XP_Trace("got a rule hit!\n");
|
|
if (rule->GetAction(&actionType, &value) == FilterError_Success)
|
|
{
|
|
XP_Bool isRead = msgHdr->GetFlags() & kIsRead;
|
|
switch (actionType)
|
|
{
|
|
case acDelete:
|
|
{
|
|
MSG_IMAPFolderInfoMail *imapFolder = m_folder->GetIMAPFolderInfoMail();
|
|
XP_Bool serverIsImap = GetMaster()->GetPrefs()->GetMailServerIsIMAP4();
|
|
XP_Bool deleteToTrash = !imapFolder || imapFolder->DeleteIsMoveToTrash();
|
|
if (deleteToTrash || !serverIsImap)
|
|
{
|
|
// set value to trash folder
|
|
MSG_FolderInfoMail *mailTrash = GetTrashFolder();
|
|
if (mailTrash)
|
|
value = (void *) mailTrash->GetPathname();
|
|
|
|
msgHdr->OrFlags(kIsRead); // mark read in trash.
|
|
}
|
|
else // (!deleteToTrash && serverIsImap)
|
|
{
|
|
msgHdr->OrFlags(kIsRead | kIMAPdeleted);
|
|
IDArray keysToFlag;
|
|
|
|
keysToFlag.Add(msgHdr->GetMessageKey());
|
|
if (imapFolder)
|
|
imapFolder->StoreImapFlags(m_pane, kImapMsgSeenFlag | kImapMsgDeletedFlag, TRUE, keysToFlag, ((ParseIMAPMailboxState *) this)->GetFilterUrlQueue());
|
|
}
|
|
}
|
|
case acMoveToFolder:
|
|
// if moving to a different file, do it.
|
|
if (value && XP_FILENAMECMP(m_mailDB->GetFolderName(), (char *) value))
|
|
{
|
|
if (msgHdr->GetFlags() & kMDNNeeded &&
|
|
!isRead)
|
|
{
|
|
ParseMailMessageState *state =
|
|
GetMsgState();
|
|
struct message_header to;
|
|
struct message_header cc;
|
|
state->GetAggregateHeader (state->m_toList, &to);
|
|
state->GetAggregateHeader (state->m_ccList, &cc);
|
|
msgHdr->SetFlags(msgHdr->GetFlags() & ~kMDNNeeded);
|
|
msgHdr->SetFlags(msgHdr->GetFlags() | kMDNSent);
|
|
|
|
if (actionType == acDelete)
|
|
{
|
|
MSG_ProcessMdnNeededState processMdnNeeded
|
|
(MSG_ProcessMdnNeededState::eDeleted,
|
|
m_pane, m_folder, msgHdr->GetMessageKey(),
|
|
&state->m_return_path, &state->m_mdn_dnt,
|
|
&to, &cc, &state->m_subject,
|
|
&state->m_date, &state->m_mdn_original_recipient,
|
|
&state->m_message_id, state->m_headers,
|
|
(int32) state->m_headers_fp, TRUE);
|
|
}
|
|
char *tmp = (char*) to.value;
|
|
XP_FREEIF(tmp);
|
|
tmp = (char*) cc.value;
|
|
XP_FREEIF(tmp);
|
|
}
|
|
err = MoveIncorporatedMessage(msgHdr, m_mailDB, (char *) value, filter);
|
|
if (err == eSUCCESS)
|
|
msgMoved = TRUE;
|
|
|
|
}
|
|
break;
|
|
case acMarkRead:
|
|
MarkFilteredMessageRead(msgHdr);
|
|
break;
|
|
case acKillThread:
|
|
// for ignore and watch, we will need the db
|
|
// to check for the flags in msgHdr's that
|
|
// get added, because only then will we know
|
|
// the thread they're getting added to.
|
|
msgHdr->OrFlags(kIgnored);
|
|
break;
|
|
case acWatchThread:
|
|
msgHdr->OrFlags(kWatched);
|
|
break;
|
|
case acChangePriority:
|
|
m_mailDB->SetPriority(msgHdr, * ((MSG_PRIORITY *) &value));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (m_filterList->IsLoggingEnabled() && !msgMoved && actionType != acMoveToFolder)
|
|
LogRuleHit(filter, msgHdr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (pMoved)
|
|
*pMoved = msgMoved;
|
|
}
|
|
|
|
// This gets called for every message because libnet calls IncorporateBegin,
|
|
// IncorporateWrite (once or more), and IncorporateComplete for every message.
|
|
void ParseNewMailState::DoneParsingFolder()
|
|
{
|
|
XP_Bool moved = FALSE;
|
|
/* End of file. Flush out any partial line remaining in the buffer. */
|
|
if (m_ibuffer_fp > 0)
|
|
{
|
|
m_parseMsgState->ParseFolderLine(m_ibuffer, m_ibuffer_fp);
|
|
m_ibuffer_fp = 0;
|
|
}
|
|
PublishMsgHeader();
|
|
if (!moved && m_mailDB != NULL) // finished parsing, so flush db folder info
|
|
UpdateDBFolderInfo();
|
|
|
|
if (m_folder != NULL)
|
|
m_folder->SummaryChanged();
|
|
|
|
/* We're done reading the folder - we don't need these things
|
|
any more. */
|
|
FREEIF (m_ibuffer);
|
|
m_ibuffer_size = 0;
|
|
FREEIF (m_obuffer);
|
|
m_obuffer_size = 0;
|
|
}
|
|
|
|
int32 ParseNewMailState::PublishMsgHeader()
|
|
{
|
|
XP_Bool moved = FALSE;
|
|
|
|
m_parseMsgState->FinishHeader();
|
|
|
|
if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
FolderTypeSpecificTweakMsgHeader(m_parseMsgState->m_newMsgHdr);
|
|
if (!m_disableFilters) {
|
|
ApplyFilters(&moved);
|
|
}
|
|
if (!moved)
|
|
{
|
|
if (m_mailDB)
|
|
{
|
|
m_parseMsgState->m_newMsgHdr->OrFlags(kNew);
|
|
m_mailDB->AddHdrToDB (m_parseMsgState->m_newMsgHdr, NULL,
|
|
m_updateAsWeGo);
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
}
|
|
if (m_folder)
|
|
m_folder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW);
|
|
|
|
|
|
} // if it was moved by imap filter, m_parseMsgState->m_newMsgHdr == NULL
|
|
else if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
// make sure global db is set correctly for delete of strings from hash tbl.
|
|
// we do this now by remembering the db in the mail hdr object inside the db
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
}
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ParseNewMailState::SetUsingTempDB(XP_Bool usingTempDB, char *tmpDBName)
|
|
{
|
|
m_usingTempDB = usingTempDB;
|
|
m_tmpdbName = tmpDBName;
|
|
}
|
|
|
|
ParseIMAPMailboxState::ParseIMAPMailboxState(MSG_Master *master, MSG_IMAPHost *host, MSG_FolderInfoMail *folder,
|
|
MSG_UrlQueue *urlQueue, TImapFlagAndUidState *flagStateAdopted)
|
|
: ParseNewMailState(master, folder), fUrlQueue(urlQueue)
|
|
{
|
|
MSG_FolderInfoContainer *imapContainer = m_mailMaster->GetImapMailFolderTreeForHost(host->GetHostName());
|
|
MSG_FolderInfo *filteredFolder = imapContainer->FindMailPathname(folder->GetPathname());
|
|
fParsingInbox = 0 != (filteredFolder->GetFlags() & MSG_FOLDER_FLAG_INBOX);
|
|
fFlagState = flagStateAdopted;
|
|
fB2HaveWarnedUserOfOfflineFiltertarget = FALSE;
|
|
|
|
// we ignore X-mozilla status for imap messages
|
|
GetMsgState()->m_IgnoreXMozillaStatus = TRUE;
|
|
fNextSequenceNum = -1;
|
|
m_host = host;
|
|
m_imapContainer = imapContainer;
|
|
}
|
|
|
|
ParseIMAPMailboxState::~ParseIMAPMailboxState()
|
|
{
|
|
}
|
|
|
|
void ParseIMAPMailboxState::SetPublishUID(int32 uid)
|
|
{
|
|
fNextMessagePublishUID = uid;
|
|
}
|
|
|
|
void ParseIMAPMailboxState::SetPublishByteLength(uint32 byteLength)
|
|
{
|
|
fNextMessageByteLength = byteLength;
|
|
}
|
|
|
|
void ParseIMAPMailboxState::DoneParsingFolder()
|
|
{
|
|
ParseMailboxState::DoneParsingFolder();
|
|
if (m_mailDB)
|
|
{
|
|
// make sure the highwater mark is correct
|
|
if (m_mailDB->m_dbFolderInfo->GetNumVisibleMessages())
|
|
{
|
|
ListContext *listContext;
|
|
DBMessageHdr *currentHdr;
|
|
if ((m_mailDB->ListLast(&listContext, ¤tHdr) == eSUCCESS) &&
|
|
currentHdr)
|
|
{
|
|
m_mailDB->m_dbFolderInfo->m_LastMessageUID = currentHdr->GetMessageKey();
|
|
delete currentHdr;
|
|
m_mailDB->ListDone(listContext);
|
|
}
|
|
else
|
|
m_mailDB->m_dbFolderInfo->m_LastMessageUID = 0;
|
|
}
|
|
else
|
|
m_mailDB->m_dbFolderInfo->m_LastMessageUID = 0;
|
|
|
|
// DMB TODO m_mailDB->m_dbFolderInfo->setDirty();
|
|
|
|
m_mailDB->Close();
|
|
m_mailDB = NULL;
|
|
}
|
|
}
|
|
|
|
int ParseIMAPMailboxState::MarkFilteredMessageRead(MailMessageHdr *msgHdr)
|
|
{
|
|
msgHdr->OrFlags(kIsRead);
|
|
IDArray keysToFlag;
|
|
|
|
keysToFlag.Add(msgHdr->GetMessageKey());
|
|
if (m_folder->GetType() == FOLDER_IMAPMAIL)
|
|
{
|
|
MSG_IMAPFolderInfoMail *imapFolder = m_folder->GetIMAPFolderInfoMail();
|
|
if (imapFolder)
|
|
{
|
|
imapFolder->StoreImapFlags(m_pane, kImapMsgSeenFlag, TRUE, keysToFlag, GetFilterUrlQueue());
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(FALSE); // former location of a cast.
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ParseIMAPMailboxState::MoveIncorporatedMessage(MailMessageHdr *mailHdr,
|
|
MailDB *sourceDB,
|
|
char *destFolder,
|
|
MSG_Filter *filter)
|
|
{
|
|
int err = eUNKNOWN;
|
|
|
|
if (fUrlQueue && fUrlQueue->GetPane())
|
|
{
|
|
// look for matching imap folders, then pop folders
|
|
MSG_FolderInfoContainer *imapContainer = m_imapContainer;
|
|
MSG_FolderInfo *sourceFolder = imapContainer->FindMailPathname(m_mailboxName);
|
|
MSG_FolderInfo *destinationFolder = imapContainer->FindMailPathname(destFolder);
|
|
if (!destinationFolder)
|
|
destinationFolder = m_mailMaster->FindMailFolder(destFolder, FALSE);
|
|
|
|
if (destinationFolder)
|
|
{
|
|
MSG_FolderInfo *inbox=NULL;
|
|
imapContainer->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, &inbox, 1);
|
|
if (inbox)
|
|
{
|
|
MSG_FolderInfoMail *destMailFolder = destinationFolder->GetMailFolderInfo();
|
|
// put the header into the source db, since it needs to be there when we copy it
|
|
// and we need a valid header to pass to StartAsyncCopyMessagesInto
|
|
MessageKey keyToFilter = mailHdr->GetMessageKey();
|
|
|
|
if (sourceDB && destMailFolder)
|
|
{
|
|
XP_Bool imapDeleteIsMoveToTrash = m_host->GetDeleteIsMoveToTrash();
|
|
|
|
IDArray *idsToMoveFromInbox = destMailFolder->GetImapIdsToMoveFromInbox();
|
|
idsToMoveFromInbox->Add(keyToFilter);
|
|
|
|
// this is our last best chance to log this
|
|
if (m_filterList->IsLoggingEnabled())
|
|
LogRuleHit(filter, mailHdr);
|
|
|
|
if (imapDeleteIsMoveToTrash)
|
|
{
|
|
if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
}
|
|
|
|
destinationFolder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW);
|
|
|
|
if (imapDeleteIsMoveToTrash)
|
|
err = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// we have to return an error because we do not actually move the message
|
|
// it is done async and that can fail
|
|
return err;
|
|
}
|
|
|
|
MSG_FolderInfoMail *ParseIMAPMailboxState::GetTrashFolder()
|
|
{
|
|
MSG_IMAPFolderInfoContainer *imapContainerInfo = m_imapContainer->GetIMAPFolderInfoContainer();
|
|
if (!imapContainerInfo)
|
|
return NULL;
|
|
|
|
MSG_FolderInfo *foundTrash = MSG_GetTrashFolderForHost(imapContainerInfo->GetIMAPHost());
|
|
return foundTrash ? foundTrash->GetMailFolderInfo() : (MSG_FolderInfoMail *)NULL;
|
|
}
|
|
|
|
|
|
// only apply filters for new unread messages in the imap inbox
|
|
void ParseIMAPMailboxState::ApplyFilters(XP_Bool *pMoved)
|
|
{
|
|
if (fParsingInbox && !(GetCurrentMsg()->GetFlags() & kIsRead) )
|
|
ParseNewMailState::ApplyFilters(pMoved);
|
|
else
|
|
*pMoved = FALSE;
|
|
|
|
if (!*pMoved && m_parseMsgState->m_newMsgHdr)
|
|
fFetchBodyKeys.Add(m_parseMsgState->m_newMsgHdr->GetMessageKey());
|
|
}
|
|
|
|
|
|
// For IMAP, the message key is the IMAP UID
|
|
// This is where I will add code to fix the message length as well - km
|
|
void ParseIMAPMailboxState::FolderTypeSpecificTweakMsgHeader(MailMessageHdr *tweakMe)
|
|
{
|
|
if (m_mailDB && tweakMe)
|
|
{
|
|
tweakMe->SetMessageKey(fNextMessagePublishUID);
|
|
tweakMe->SetByteLength(fNextMessageByteLength);
|
|
tweakMe->SetMessageSize(fNextMessageByteLength);
|
|
|
|
if (fFlagState)
|
|
{
|
|
XP_Bool foundIt = FALSE;
|
|
imapMessageFlagsType imap_flags = IMAP_GetMessageFlagsFromUID(fNextMessagePublishUID, &foundIt, fFlagState);
|
|
if (foundIt)
|
|
{
|
|
// make a mask and clear these message flags
|
|
uint32 mask = MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_MARKED | MSG_FLAG_IMAP_DELETED;
|
|
tweakMe->SetFlags(tweakMe->GetFlags() & ~mask);
|
|
|
|
// set the new value for these flags
|
|
uint32 newFlags = 0;
|
|
if (imap_flags & kImapMsgSeenFlag)
|
|
newFlags |= MSG_FLAG_READ;
|
|
else // if (imap_flags & kImapMsgRecentFlag)
|
|
newFlags |= MSG_FLAG_NEW;
|
|
|
|
// Okay here is the MDN needed logic (if DNT header seen):
|
|
/* if server support user defined flag:
|
|
MDNSent flag set => clear kMDNNeeded flag
|
|
MDNSent flag not set => do nothing, leave kMDNNeeded on
|
|
else if
|
|
not MSG_FLAG_NEW => clear kMDNNeeded flag
|
|
MSG_FLAG_NEW => do nothing, leave kMDNNeeded on
|
|
*/
|
|
if (imap_flags & kImapMsgSupportUserFlag)
|
|
{
|
|
if (imap_flags & kImapMsgMDNSentFlag)
|
|
{
|
|
newFlags |= kMDNSent;
|
|
if (tweakMe->GetFlags() & kMDNNeeded)
|
|
tweakMe->SetFlags(tweakMe->GetFlags() & ~kMDNNeeded);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(imap_flags & kImapMsgRecentFlag) &&
|
|
tweakMe->GetFlags() & kMDNNeeded)
|
|
tweakMe->SetFlags(tweakMe->GetFlags() & ~kMDNNeeded);
|
|
}
|
|
|
|
if (imap_flags & kImapMsgAnsweredFlag)
|
|
newFlags |= MSG_FLAG_REPLIED;
|
|
if (imap_flags & kImapMsgFlaggedFlag)
|
|
newFlags |= MSG_FLAG_MARKED;
|
|
if (imap_flags & kImapMsgDeletedFlag)
|
|
newFlags |= MSG_FLAG_IMAP_DELETED;
|
|
|
|
if (newFlags)
|
|
tweakMe->SetFlags(tweakMe->GetFlags() | newFlags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ParseIMAPMailboxState::PublishMsgHeader()
|
|
{
|
|
XP_Bool moved = FALSE;
|
|
|
|
m_parseMsgState->FinishHeader();
|
|
|
|
if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
FolderTypeSpecificTweakMsgHeader(m_parseMsgState->m_newMsgHdr);
|
|
if (!m_disableFilters) {
|
|
ApplyFilters(&moved);
|
|
}
|
|
if (!moved)
|
|
{
|
|
XP_Bool thisMessageUnRead = !(m_parseMsgState->m_newMsgHdr->GetFlags() & kIsRead);
|
|
if (m_mailDB)
|
|
{
|
|
if (thisMessageUnRead)
|
|
m_parseMsgState->m_newMsgHdr->OrFlags(kNew);
|
|
m_mailDB->AddHdrToDB (m_parseMsgState->m_newMsgHdr, NULL,
|
|
(fNextSequenceNum == -1) ? m_updateAsWeGo : FALSE);
|
|
// following is for cacheless imap - match sequence number
|
|
// to location to insert in view.
|
|
if (m_msgDBView != NULL && fNextSequenceNum != -1)
|
|
{
|
|
m_msgDBView->InsertHdrAt(m_parseMsgState->m_newMsgHdr, fNextSequenceNum++ - 1);
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("adding hdr to cacheless view at %ld\n", fNextSequenceNum - 2);
|
|
#endif
|
|
}
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
}
|
|
if (m_folder && thisMessageUnRead)
|
|
m_folder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW);
|
|
|
|
|
|
} // if it was moved by imap filter, m_parseMsgState->m_newMsgHdr == NULL
|
|
else if (m_parseMsgState->m_newMsgHdr)
|
|
{
|
|
// make sure global db is set correctly for delete of strings from hash tbl.
|
|
delete m_parseMsgState->m_newMsgHdr;
|
|
}
|
|
m_parseMsgState->m_newMsgHdr = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ParseIMAPMailboxState::SetNextSequenceNum(int32 seqNum)
|
|
{
|
|
fNextSequenceNum = seqNum;
|
|
}
|
|
|
|
ParseOutgoingMessage::ParseOutgoingMessage()
|
|
{
|
|
m_bytes_written = 0;
|
|
m_out_file = 0;
|
|
m_wroteXMozillaStatus = FALSE;
|
|
m_writeToOutFile = TRUE;
|
|
m_lastBodyLineEmpty = FALSE;
|
|
m_outputBuffer = 0;
|
|
m_ouputBufferSize = 0;
|
|
m_outputBufferIndex = 0;
|
|
}
|
|
|
|
ParseOutgoingMessage::~ParseOutgoingMessage()
|
|
{
|
|
FREEIF (m_outputBuffer);
|
|
}
|
|
|
|
void ParseOutgoingMessage::Clear()
|
|
{
|
|
ParseMailMessageState::Clear();
|
|
m_wroteXMozillaStatus = FALSE;
|
|
m_bytes_written = 0;
|
|
}
|
|
|
|
// We've found the start of the next message, so finish this one off.
|
|
void ParseOutgoingMessage::FinishHeader()
|
|
{
|
|
int32 origPosition = m_position, len = 0;
|
|
if (m_out_file && m_writeToOutFile)
|
|
{
|
|
if (origPosition > 0 && !m_lastBodyLineEmpty)
|
|
{
|
|
len = XP_FileWrite (LINEBREAK, LINEBREAK_LEN, m_out_file);
|
|
m_bytes_written += LINEBREAK_LEN;
|
|
m_position += LINEBREAK_LEN;
|
|
}
|
|
}
|
|
ParseMailMessageState::FinishHeader();
|
|
}
|
|
|
|
int ParseOutgoingMessage::StartNewEnvelope(const char *line, uint32 lineLength)
|
|
{
|
|
int status = ParseMailMessageState::StartNewEnvelope(line, lineLength);
|
|
|
|
if ((status >= 0) && m_out_file && m_writeToOutFile)
|
|
{
|
|
status = XP_FileWrite(line, lineLength, m_out_file);
|
|
if (status > 0)
|
|
m_bytes_written += lineLength;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int32 ParseOutgoingMessage::ParseFolderLine(const char *line, uint32 lineLength)
|
|
{
|
|
int32 res = ParseMailMessageState::ParseFolderLine(line, lineLength);
|
|
int len = 0;
|
|
|
|
if (res < 0)
|
|
return res;
|
|
if (m_out_file && m_writeToOutFile)
|
|
{
|
|
if (!XP_STRNCMP(line, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN))
|
|
m_wroteXMozillaStatus = TRUE;
|
|
|
|
m_lastBodyLineEmpty = (m_state == MBOX_PARSE_BODY && (EMPTY_MESSAGE_LINE(line)));
|
|
|
|
// make sure we mangle naked From lines
|
|
if (line[0] == 'F' && !XP_STRNCMP (line, "From ", 5))
|
|
{
|
|
res = XP_FileWrite (">", 1, m_out_file);
|
|
if (res < 1)
|
|
return res;
|
|
m_position += 1;
|
|
}
|
|
if (!m_wroteXMozillaStatus && m_state == MBOX_PARSE_BODY)
|
|
{
|
|
char buf[50];
|
|
uint32 dbFlags = m_newMsgHdr ? m_newMsgHdr->GetFlags() : 0;
|
|
|
|
if (m_newMsgHdr)
|
|
m_newMsgHdr->SetStatusOffset(m_bytes_written);
|
|
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT LINEBREAK, (m_newMsgHdr) ? m_newMsgHdr->GetMozillaStatusFlags() & ~MSG_FLAG_RUNTIME_ONLY : 0);
|
|
len = strlen(buf);
|
|
res = XP_FileWrite(buf, len, m_out_file);
|
|
if (res < len)
|
|
return res;
|
|
m_bytes_written += len;
|
|
m_position += len;
|
|
m_wroteXMozillaStatus = TRUE;
|
|
|
|
MessageDB::ConvertDBFlagsToPublicFlags(&dbFlags);
|
|
dbFlags &= (MSG_FLAG_MDN_REPORT_NEEDED | MSG_FLAG_MDN_REPORT_SENT | MSG_FLAG_TEMPLATE);
|
|
PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT LINEBREAK, dbFlags);
|
|
len = strlen(buf);
|
|
res = XP_FileWrite(buf, len, m_out_file);
|
|
if (res < len)
|
|
return res;
|
|
m_bytes_written += len;
|
|
m_position += len;
|
|
}
|
|
res = XP_FileWrite(line, lineLength, m_out_file);
|
|
if (res == lineLength)
|
|
m_bytes_written += lineLength;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* static */
|
|
int32 ParseOutgoingMessage::LineBufferCallback(char *line, uint32 lineLength,
|
|
void *closure)
|
|
{
|
|
ParseOutgoingMessage *parseState = (ParseOutgoingMessage *) closure;
|
|
|
|
return parseState->ParseFolderLine(line, lineLength);
|
|
}
|
|
|
|
int32 ParseOutgoingMessage::ParseBlock(const char *block, uint32 length)
|
|
{
|
|
m_ouputBufferSize = 10240;
|
|
while (m_outputBuffer == 0)
|
|
{
|
|
|
|
m_outputBuffer = (char *) XP_ALLOC(m_ouputBufferSize);
|
|
if (m_outputBuffer == NULL)
|
|
m_ouputBufferSize /= 2;
|
|
}
|
|
XP_ASSERT(m_outputBuffer != NULL);
|
|
|
|
return msg_LineBuffer (block, length, &m_outputBuffer, &m_ouputBufferSize, &m_outputBufferIndex, FALSE,
|
|
#ifdef XP_OS2
|
|
(int32 (_Optlink*) (char*,uint32,void*))
|
|
#endif
|
|
LineBufferCallback, this);
|
|
}
|
|
|
|
void ParseOutgoingMessage::AdvanceOutPosition(uint32 amountToAdvance)
|
|
{
|
|
m_position += amountToAdvance;
|
|
m_bytes_written += amountToAdvance;
|
|
}
|
|
|
|
void ParseOutgoingMessage::FlushOutputBuffer()
|
|
{
|
|
/* End of file. Flush out any partial line remaining in the buffer. */
|
|
if (m_outputBufferIndex > 0)
|
|
{
|
|
ParseFolderLine(m_outputBuffer, m_outputBufferIndex);
|
|
m_outputBufferIndex = 0;
|
|
}
|
|
}
|
|
|