Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1

This commit is contained in:
Matt A. Tobin 2019-11-03 00:17:46 -04:00 • committed by Roy Tam
commit e400f4130a
1564 changed files with 510348 additions and 0 deletions

View file

@ -0,0 +1,33 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'nsImapCore.h',
]
SOURCES += [
'nsAutoSyncManager.cpp',
'nsAutoSyncState.cpp',
'nsIMAPBodyShell.cpp',
'nsImapFlagAndUidState.cpp',
'nsIMAPGenericParser.cpp',
'nsIMAPHostSessionList.cpp',
'nsImapIncomingServer.cpp',
'nsImapMailFolder.cpp',
'nsIMAPNamespace.cpp',
'nsImapOfflineSync.cpp',
'nsImapProtocol.cpp',
'nsImapSearchResults.cpp',
'nsImapServerResponseParser.cpp',
'nsImapService.cpp',
'nsImapStringBundle.cpp',
'nsImapUndoTxn.cpp',
'nsImapUrl.cpp',
'nsImapUtils.cpp',
'nsSyncRunnableHelpers.cpp',
]
FINAL_LIBRARY = 'mail'

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,265 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAutoSyncManager_h__
#define nsAutoSyncManager_h__
#include "nsAutoPtr.h"
#include "nsStringGlue.h"
#include "nsCOMArray.h"
#include "nsIObserver.h"
#include "nsIUrlListener.h"
#include "nsITimer.h"
#include "nsTObserverArray.h"
#include "nsIAutoSyncManager.h"
#include "nsIAutoSyncMsgStrategy.h"
#include "nsIAutoSyncFolderStrategy.h"
#include "prtime.h"
class nsImapMailFolder;
class nsIMsgDBHdr;
class nsIIdleService;
class nsIMsgFolder;
/* Auto-Sync
*
* Background:
* it works only with offline imap folders. "autosync_offline_stores" pref
* enables/disables auto-sync mechanism. Note that setting "autosync_offline_stores"
* to false, or setting folder to not-offline doesn't stop synchronization
* process for already queued folders.
*
* Auto-Sync policy:
* o It kicks in during system idle time, and tries to download as much messages
* as possible based on given folder and message prioritization strategies/rules.
* Default folder prioritization strategy dictates to sort the folders based on the
* following order: INBOX > DRAFTS > SUBFOLDERS > TRASH.
* Similarly, default message prioritization strategy dictates to download the most
* recent and smallest message first. Also, by sorting the messages by size in the
* queue, it tries to maximize the number of messages downloaded.
* o It downloads the messages in groups. Default groups size is defined by |kDefaultGroupSize|.
* o It downloads the messages larger than the group size one-by-one.
* o If new messages arrive when not idle, it downloads the messages that do fit into
* |kFirstGroupSizeLimit| size limit immediately, without waiting for idle time, unless there is
* a sibling (a folder owned by the same imap server) in stDownloadInProgress state in the q
* o If new messages arrive when idle, it downloads all the messages without any restriction.
* o If new messages arrive into a folder while auto-sync is downloading other messages of the
* same folder, it simply puts the new messages into the folder's download queue, and
* re-prioritize the messages. That behavior makes sure that the high priority
* (defined by the message strategy) get downloaded first always.
* o If new messages arrive into a folder while auto-sync is downloading messages of a lower
* priority folder, auto-sync switches the folders in the queue and starts downloading the
* messages of the higher priority folder next time it downloads a message group.
* o Currently there is no way to stop/pause/cancel a message download. The smallest
* granularity is the message group size.
* o Auto-Sync manager periodically (kAutoSyncFreq) checks folder for existing messages
* w/o bodies. It persists the last time the folder is checked in the local database of the
* folder. We call this process 'Discovery'. This process is asynchronous and processes
* |kNumberOfHeadersToProcess| number of headers at each cycle. Since it works on local data,
* it doesn't consume lots of system resources, it does its job fast.
* o Discovery is necessary especially when the user makes a transition from not-offline
* to offline mode.
* o Update frequency is defined by nsMsgIncomingServer::BiffMinutes.
*
* Error Handling:
* o if the user moves/deletes/filters all messages of a folder already queued, auto-sync
* deals with that situation by skipping the folder in question, and continuing with the
* next in chain.
* o If the message size is zero, auto-sync ignores the message.
* o If the download of the message group fails for some reason, auto-sync tries to
* download the same group |kGroupRetryCount| times. If it still fails, continues with the
* next group of messages.
*
* Download Model:
* Parallel model should be used with the imap servers that do not have any "max number of sessions
* per IP" limit, and when the bandwidth is significantly large.
*
* How it really works:
* The AutoSyncManager gets an idle notification. First it processes any
* folders in the discovery queue (which means it schedules message download
* for any messages it previously determined it should download). Then it sets
* a timer, and in the timer callback, it processes the update q, by calling
* InitiateAutoSync on the first folder in the update q.
*/
/**
* Default strategy implementation to prioritize messages in the download queue.
*/
class nsDefaultAutoSyncMsgStrategy final : public nsIAutoSyncMsgStrategy
{
static const uint32_t kFirstPassMessageSize = 60U*1024U; // 60K
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTOSYNCMSGSTRATEGY
nsDefaultAutoSyncMsgStrategy();
private:
~nsDefaultAutoSyncMsgStrategy();
};
/**
* Default strategy implementation to prioritize folders in the download queue.
*/
class nsDefaultAutoSyncFolderStrategy final : public nsIAutoSyncFolderStrategy
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTOSYNCFOLDERSTRATEGY
nsDefaultAutoSyncFolderStrategy();
private:
~nsDefaultAutoSyncFolderStrategy();
};
// see the end of the page for auto-sync internals
/**
* Manages background message download operations for offline imap folders.
*/
class nsAutoSyncManager final : public nsIObserver,
public nsIUrlListener,
public nsIAutoSyncManager
{
static const PRTime kAutoSyncFreq = 60UL * (PR_USEC_PER_SEC * 60UL); // 1hr
static const uint32_t kDefaultUpdateInterval = 10UL; // 10min
static const int32_t kTimerIntervalInMs = 400;
static const uint32_t kNumberOfHeadersToProcess = 250U;
// enforced size of the first group that will be downloaded before idle time
static const uint32_t kFirstGroupSizeLimit = 60U*1024U /* 60K */;
static const int32_t kIdleTimeInSec = 10;
static const uint32_t kGroupRetryCount = 3;
enum IdleState { systemIdle, appIdle, notIdle };
enum UpdateState { initiated, completed };
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIURLLISTENER
NS_DECL_NSIAUTOSYNCMANAGER
nsAutoSyncManager();
private:
~nsAutoSyncManager();
void SetIdleState(IdleState st);
IdleState GetIdleState() const;
nsresult StartIdleProcessing();
nsresult AutoUpdateFolders();
void ScheduleFolderForOfflineDownload(nsIAutoSyncState *aAutoSyncStateObj);
nsresult DownloadMessagesForOffline(nsIAutoSyncState *aAutoSyncStateObj, uint32_t aSizeLimit = 0);
nsresult HandleDownloadErrorFor(nsIAutoSyncState *aAutoSyncStateObj, const nsresult error);
// Helper methods for priority Q operations
static
void ChainFoldersInQ(const nsCOMArray<nsIAutoSyncState> &aQueue,
nsCOMArray<nsIAutoSyncState> &aChainedQ);
static
nsIAutoSyncState* SearchQForSibling(const nsCOMArray<nsIAutoSyncState> &aQueue,
nsIAutoSyncState *aAutoSyncStateObj, int32_t aStartIdx, int32_t *aIndex = nullptr);
static
bool DoesQContainAnySiblingOf(const nsCOMArray<nsIAutoSyncState> &aQueue,
nsIAutoSyncState *aAutoSyncStateObj, const int32_t aState,
int32_t *aIndex = nullptr);
static
nsIAutoSyncState* GetNextSibling(const nsCOMArray<nsIAutoSyncState> &aQueue,
nsIAutoSyncState *aAutoSyncStateObj, int32_t *aIndex = nullptr);
static
nsIAutoSyncState* GetHighestPrioSibling(const nsCOMArray<nsIAutoSyncState> &aQueue,
nsIAutoSyncState *aAutoSyncStateObj, int32_t *aIndex = nullptr);
/// timer to process existing keys and updates
void InitTimer();
static void TimerCallback(nsITimer *aTimer, void *aClosure);
void StopTimer();
void StartTimerIfNeeded();
/// pref helpers
uint32_t GetUpdateIntervalFor(nsIAutoSyncState *aAutoSyncStateObj);
protected:
nsCOMPtr<nsIAutoSyncMsgStrategy> mMsgStrategyImpl;
nsCOMPtr<nsIAutoSyncFolderStrategy> mFolderStrategyImpl;
// contains the folders that will be downloaded on background
nsCOMArray<nsIAutoSyncState> mPriorityQ;
// contains the folders that will be examined for existing headers and
// adds the headers we don't have offline into the autosyncState
// object's download queue.
nsCOMArray<nsIAutoSyncState> mDiscoveryQ;
// contains the folders that will be checked for new messages with STATUS,
// and if there are any, we'll call UpdateFolder on them.
nsCOMArray<nsIAutoSyncState> mUpdateQ;
// this is the update state for the current folder.
UpdateState mUpdateState;
// This is set if auto sync has been completely paused.
bool mPaused;
// This is set if we've finished startup and should start
// paying attention to idle notifications.
bool mStartupDone;
private:
uint32_t mGroupSize;
IdleState mIdleState;
int32_t mDownloadModel;
nsCOMPtr<nsIIdleService> mIdleService;
nsCOMPtr<nsITimer> mTimer;
nsTObserverArray<nsCOMPtr<nsIAutoSyncMgrListener> > mListeners;
};
#endif
/*
How queues inter-relate:
nsAutoSyncState has an internal priority queue to store messages waiting to be
downloaded. nsAutoSyncMsgStrategy object determines the order in this queue,
nsAutoSyncManager uses this queue to manage downloads. Two events cause a
change in this queue:
1) nsImapMailFolder::HeaderFetchCompleted: is triggered when TB notices that
there are pending messages on the server -- via IDLE command from the server,
via explicit select from the user, or via automatic Update during idle time. If
it turns out that there are pending messages on the server, it adds them into
nsAutoSyncState's download queue.
2) nsAutoSyncState::ProcessExistingHeaders: is triggered for every imap folder
every hour or so (see kAutoSyncFreq). nsAutoSyncManager uses an internal queue called Discovery
queue to keep track of this task. The purpose of ProcessExistingHeaders()
method is to check existing headers of a given folder in batches and discover
the messages without bodies, in asynchronous fashion. This process is
sequential, one and only one folder at any given time, very similar to
indexing. Again, if it turns out that the folder in hand has messages w/o
bodies, ProcessExistingHeaders adds them into nsAutoSyncState's download queue.
Any change in nsAutoSyncState's download queue, notifies nsAutoSyncManager and
nsAutoSyncManager puts the requesting nsAutoSyncState into its internal
priority queue (called mPriorityQ) -- if the folder is not already there.
nsAutoSyncFolderStrategy object determines the order in this queue. This queue
is processed in two modes: chained and parallel.
i) Chained: One folder per imap server any given time. Folders owned by
different imap servers are simultaneous.
ii) Parallel: All folders at the same time, using all cached-connections -
a.k.a 'Folders gone wild' mode.
The order the folders are added into the mPriorityQ doesn't matter since every
time a batch completed for an imap server, nsAutoSyncManager adjusts the order.
So, lets say that updating a sub-folder starts downloading message immediately,
when an higher priority folder is added into the queue, nsAutoSyncManager
switches to this higher priority folder instead of processing the next group of
messages of the lower priority one. Setting group size too high might delay
this switch at worst.
And finally, Update queue helps nsAutoSyncManager to keep track of folders
waiting to be updated. With the latest change, we update one and only one
folder at any given time. Default frequency of updating is 10 min (kDefaultUpdateInterval).
We add folders into the update queue during idle time, if they are not in mPriorityQ already.
*/

View file

@ -0,0 +1,765 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAutoSyncState.h"
#include "nsImapMailFolder.h"
#include "nsMsgImapCID.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsMsgKeyArray.h"
#include "nsIMsgWindow.h"
#include "nsIMsgMailSession.h"
#include "nsMsgFolderFlags.h"
#include "nsIAutoSyncManager.h"
#include "nsIAutoSyncMsgStrategy.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Logging.h"
using namespace mozilla;
extern PRLogModuleInfo *gAutoSyncLog;
MsgStrategyComparatorAdaptor::MsgStrategyComparatorAdaptor(nsIAutoSyncMsgStrategy* aStrategy,
nsIMsgFolder *aFolder, nsIMsgDatabase *aDatabase) : mStrategy(aStrategy), mFolder(aFolder),
mDatabase(aDatabase)
{
}
/** @return True if the elements are equals; false otherwise. */
bool MsgStrategyComparatorAdaptor::Equals(const nsMsgKey& a, const nsMsgKey& b) const
{
nsCOMPtr<nsIMsgDBHdr> hdrA;
nsCOMPtr<nsIMsgDBHdr> hdrB;
mDatabase->GetMsgHdrForKey(a, getter_AddRefs(hdrA));
mDatabase->GetMsgHdrForKey(b, getter_AddRefs(hdrB));
if (hdrA && hdrB)
{
nsresult rv = NS_OK;
nsAutoSyncStrategyDecisionType decision = nsAutoSyncStrategyDecisions::Same;
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(mFolder);
if (mStrategy)
rv = mStrategy->Sort(folder, hdrA, hdrB, &decision);
if (NS_SUCCEEDED(rv))
return (decision == nsAutoSyncStrategyDecisions::Same);
}
return false;
}
/** @return True if (a < b); false otherwise. */
bool MsgStrategyComparatorAdaptor::LessThan(const nsMsgKey& a, const nsMsgKey& b) const
{
nsCOMPtr<nsIMsgDBHdr> hdrA;
nsCOMPtr<nsIMsgDBHdr> hdrB;
mDatabase->GetMsgHdrForKey(a, getter_AddRefs(hdrA));
mDatabase->GetMsgHdrForKey(b, getter_AddRefs(hdrB));
if (hdrA && hdrB)
{
nsresult rv = NS_OK;
nsAutoSyncStrategyDecisionType decision = nsAutoSyncStrategyDecisions::Same;
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(mFolder);
if (mStrategy)
rv = mStrategy->Sort(folder, hdrA, hdrB, &decision);
if (NS_SUCCEEDED(rv))
return (decision == nsAutoSyncStrategyDecisions::Lower);
}
return false;
}
nsAutoSyncState::nsAutoSyncState(nsImapMailFolder *aOwnerFolder, PRTime aLastSyncTime)
: mSyncState(stCompletedIdle), mOffset(0U), mLastOffset(0U), mLastServerTotal(0),
mLastServerRecent(0), mLastServerUnseen(0), mLastNextUID(0),
mLastSyncTime(aLastSyncTime), mLastUpdateTime(0UL), mProcessPointer(0U),
mIsDownloadQChanged(false), mRetryCounter(0U)
{
mOwnerFolder = do_GetWeakReference(static_cast<nsIMsgImapMailFolder*>(aOwnerFolder));
}
nsAutoSyncState::~nsAutoSyncState()
{
}
// TODO:XXXemre should be implemented when we start
// doing space management
nsresult nsAutoSyncState::ManageStorageSpace()
{
return NS_OK;
}
nsresult nsAutoSyncState::PlaceIntoDownloadQ(const nsTArray<nsMsgKey> &aMsgKeyList)
{
nsresult rv = NS_OK;
if (!aMsgKeyList.IsEmpty())
{
nsCOMPtr <nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
// increase the array size
mDownloadQ.SetCapacity(mDownloadQ.Length() + aMsgKeyList.Length());
// remove excluded messages
int32_t elemCount = aMsgKeyList.Length();
for (int32_t idx = 0; idx < elemCount; idx++)
{
nsCOMPtr<nsIMsgDBHdr> hdr;
bool containsKey;
database->ContainsKey(aMsgKeyList[idx], &containsKey);
if (!containsKey)
continue;
rv = database->GetMsgHdrForKey(aMsgKeyList[idx], getter_AddRefs(hdr));
if(!hdr)
continue; // can't get message header, continue with the next one
bool doesFit = true;
rv = autoSyncMgr->DoesMsgFitDownloadCriteria(hdr, &doesFit);
if (NS_SUCCEEDED(rv) && !mDownloadSet.Contains(aMsgKeyList[idx]) && doesFit)
{
bool excluded = false;
if (msgStrategy)
{
rv = msgStrategy->IsExcluded(folder, hdr, &excluded);
if (NS_SUCCEEDED(rv) && !excluded)
{
mIsDownloadQChanged = true;
mDownloadSet.PutEntry(aMsgKeyList[idx]);
mDownloadQ.AppendElement(aMsgKeyList[idx]);
}
}
}
}//endfor
if (mIsDownloadQChanged)
{
LogOwnerFolderName("Download Q is created for ");
LogQWithSize(mDownloadQ, 0);
rv = autoSyncMgr->OnDownloadQChanged(this);
}
}
return rv;
}
nsresult nsAutoSyncState::SortQueueBasedOnStrategy(nsTArray<nsMsgKey> &aQueue)
{
nsresult rv;
nsCOMPtr <nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
rv = autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
NS_ENSURE_SUCCESS(rv, rv);
MsgStrategyComparatorAdaptor strategyComp(msgStrategy, folder, database);
aQueue.Sort(strategyComp);
return rv;
}
// This method is a hack to prioritize newly inserted messages,
// without changing the size of the queue. It is required since
// we cannot sort ranges in nsTArray.
nsresult nsAutoSyncState::SortSubQueueBasedOnStrategy(nsTArray<nsMsgKey> &aQueue,
uint32_t aStartingOffset)
{
NS_ASSERTION(aStartingOffset < aQueue.Length(), "*** Starting offset is out of range");
// Copy already downloaded messages into a temporary queue,
// we want to exclude them from the sort.
nsTArray<nsMsgKey> tmpQ;
tmpQ.AppendElements(aQueue.Elements(), aStartingOffset);
// Remove already downloaded messages and sort the resulting queue
aQueue.RemoveElementsAt(0, aStartingOffset);
nsresult rv = SortQueueBasedOnStrategy(aQueue);
// copy excluded messages back
aQueue.InsertElementsAt(0, tmpQ);
return rv;
}
NS_IMETHODIMP nsAutoSyncState::GetNextGroupOfMessages(uint32_t aSuggestedGroupSizeLimit,
uint32_t *aActualGroupSize,
nsIMutableArray **aMessagesList)
{
NS_ENSURE_ARG_POINTER(aMessagesList);
NS_ENSURE_ARG_POINTER(aActualGroupSize);
*aActualGroupSize = 0;
nsresult rv;
nsCOMPtr <nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
folder->GetMsgDatabase(getter_AddRefs(database));
nsCOMPtr<nsIMutableArray> group = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (database)
{
if (!mDownloadQ.IsEmpty())
{
// sort the download queue if new items are added since the last time
if (mIsDownloadQChanged)
{
// we want to sort only pending messages. mOffset is
// the position of the first pending message in the download queue
rv = (mOffset > 0)
? SortSubQueueBasedOnStrategy(mDownloadQ, mOffset)
: SortQueueBasedOnStrategy(mDownloadQ);
if (NS_SUCCEEDED(rv))
mIsDownloadQChanged = false;
}
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t msgCount = mDownloadQ.Length();
uint32_t idx = mOffset;
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
for (; idx < msgCount; idx++)
{
bool containsKey = false;
database->ContainsKey(mDownloadQ[idx], &containsKey);
if (!containsKey)
{
mDownloadSet.RemoveEntry(mDownloadQ[idx]);
mDownloadQ.RemoveElementAt(idx--);
msgCount--;
continue;
}
nsCOMPtr<nsIMsgDBHdr> qhdr;
database->GetMsgHdrForKey(mDownloadQ[idx], getter_AddRefs(qhdr));
if(!qhdr)
continue; //maybe deleted, skip it!
// ensure that we don't have this message body offline already,
// possible if the user explicitly selects this message prior
// to auto-sync kicks in
bool hasMessageOffline;
folder->HasMsgOffline(mDownloadQ[idx], &hasMessageOffline);
if (hasMessageOffline)
continue;
// this check point allows msg strategy function
// to do last minute decisions based on the current
// state of TB such as the size of the message store etc.
if (msgStrategy)
{
bool excluded = false;
if (NS_SUCCEEDED(msgStrategy->IsExcluded(folder, qhdr, &excluded)) && excluded)
continue;
}
uint32_t msgSize;
qhdr->GetMessageSize(&msgSize);
// ignore 0 byte messages; the imap parser asserts when we try
// to download them, and there's no point anyway.
if (!msgSize)
continue;
if (!*aActualGroupSize && msgSize >= aSuggestedGroupSizeLimit)
{
*aActualGroupSize = msgSize;
group->AppendElement(qhdr, false);
idx++;
break;
}
else if ((*aActualGroupSize) + msgSize > aSuggestedGroupSizeLimit)
break;
else
{
group->AppendElement(qhdr, false);
*aActualGroupSize += msgSize;
}
}// endfor
mLastOffset = mOffset;
mOffset = idx;
}
LogOwnerFolderName("Next group of messages to be downloaded.");
LogQWithSize(group.get(), 0);
} //endif
// return it to the caller
NS_IF_ADDREF(*aMessagesList = group);
return NS_OK;
}
/**
* Usually called by nsAutoSyncManager when the last sync time is expired.
*/
NS_IMETHODIMP nsAutoSyncState::ProcessExistingHeaders(uint32_t aNumOfHdrsToProcess, uint32_t *aLeftToProcess)
{
NS_ENSURE_ARG_POINTER(aLeftToProcess);
nsresult rv;
nsCOMPtr <nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database)
return NS_ERROR_FAILURE;
// create a queue to process existing headers for the first time
if (mExistingHeadersQ.IsEmpty())
{
RefPtr<nsMsgKeyArray> keys = new nsMsgKeyArray;
rv = database->ListAllKeys(keys);
NS_ENSURE_SUCCESS(rv, rv);
keys->Sort();
mExistingHeadersQ.AppendElements(keys->m_keys);
mProcessPointer = 0;
}
// process the existing headers and find the messages not downloaded yet
uint32_t lastIdx = mProcessPointer;
nsTArray<nsMsgKey> msgKeys;
uint32_t keyCount = mExistingHeadersQ.Length();
for (; mProcessPointer < (lastIdx + aNumOfHdrsToProcess) && mProcessPointer < keyCount; mProcessPointer++)
{
bool hasMessageOffline;
folder->HasMsgOffline(mExistingHeadersQ[mProcessPointer], &hasMessageOffline);
if (!hasMessageOffline)
msgKeys.AppendElement(mExistingHeadersQ[mProcessPointer]);
}
if (!msgKeys.IsEmpty())
{
nsCString folderName;
folder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%d messages will be added into the download q of folder %s\n",
msgKeys.Length(), folderName.get()));
rv = PlaceIntoDownloadQ(msgKeys);
if (NS_FAILED(rv))
mProcessPointer = lastIdx;
}
*aLeftToProcess = keyCount - mProcessPointer;
// cleanup if we are done processing
if (0 == *aLeftToProcess)
{
mLastSyncTime = PR_Now();
mExistingHeadersQ.Clear();
mProcessPointer = 0;
folder->SetMsgDatabase(nullptr);
}
return rv;
}
void nsAutoSyncState::OnNewHeaderFetchCompleted(const nsTArray<nsMsgKey> &aMsgKeyList)
{
SetLastUpdateTime(PR_Now());
if (!aMsgKeyList.IsEmpty())
PlaceIntoDownloadQ(aMsgKeyList);
}
NS_IMETHODIMP nsAutoSyncState::UpdateFolder()
{
nsresult rv;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUrlListener> autoSyncMgrListener = do_QueryInterface(autoSyncMgr, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryReferent(mOwnerFolder, &rv);
SetState(nsAutoSyncState::stUpdateIssued);
return imapFolder->UpdateFolderWithListener(nullptr, autoSyncMgrListener);
}
NS_IMETHODIMP nsAutoSyncState::OnStartRunningUrl(nsIURI* aUrl)
{
nsresult rv = NS_OK;
// if there is a problem to start the download, set rv with the
// corresponding error code. In that case, AutoSyncManager is going to
// set the autosync state to nsAutoSyncState::stReadyToDownload
// to resume downloading another time
// TODO: is there a way to make sure that download started without
// problem through nsIURI interface?
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return autoSyncMgr->OnDownloadStarted(this, rv);
}
NS_IMETHODIMP nsAutoSyncState::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode)
{
nsresult rv;
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUrlListener> autoSyncMgrListener = do_QueryInterface(autoSyncMgr, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (mSyncState == stStatusIssued)
{
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
int32_t serverTotal, serverUnseen, serverRecent, serverNextUID;
imapFolder->GetServerTotal(&serverTotal);
imapFolder->GetServerUnseen(&serverUnseen);
imapFolder->GetServerRecent(&serverRecent);
imapFolder->GetServerNextUID(&serverNextUID);
if (serverNextUID != mLastNextUID || serverTotal != mLastServerTotal ||
serverUnseen != mLastServerUnseen || serverRecent != mLastServerRecent)
{
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("folder %s status changed serverNextUID = %lx lastNextUID = %lx\n", folderName.get(),
serverNextUID, mLastNextUID));
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("serverTotal = %lx lastServerTotal = %lx serverRecent = %lx lastServerRecent = %lx\n",
serverTotal, mLastServerTotal, serverRecent, mLastServerRecent));
SetServerCounts(serverTotal, serverRecent, serverUnseen, serverNextUID);
SetState(nsAutoSyncState::stUpdateIssued);
return imapFolder->UpdateFolderWithListener(nullptr, autoSyncMgrListener);
}
else
{
ownerFolder->SetMsgDatabase(nullptr);
// nothing more to do.
SetState(nsAutoSyncState::stCompletedIdle);
// autoSyncMgr needs this notification, so manufacture it.
return autoSyncMgrListener->OnStopRunningUrl(nullptr, NS_OK);
}
}
//XXXemre how we recover from this error?
rv = ownerFolder->ReleaseSemaphore(ownerFolder);
NS_ASSERTION(NS_SUCCEEDED(rv), "*** Cannot release folder semaphore");
nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
if (mailUrl)
rv = mailUrl->UnRegisterListener(this);
return autoSyncMgr->OnDownloadCompleted(this, aExitCode);
}
NS_IMETHODIMP nsAutoSyncState::GetState(int32_t *aState)
{
NS_ENSURE_ARG_POINTER(aState);
*aState = mSyncState;
return NS_OK;
}
const char *stateStrings[] = {"idle", "status issued", "update needed",
"update issued", "downloading",
"ready to download"};
NS_IMETHODIMP nsAutoSyncState::SetState(int32_t aState)
{
mSyncState = aState;
if (aState == stCompletedIdle)
{
ResetDownloadQ();
//tell folder to let go of its cached msg db pointer
nsresult rv;
nsCOMPtr<nsIMsgMailSession> session =
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && session)
{
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool folderOpen;
uint32_t folderFlags;
ownerFolder->GetFlags(&folderFlags);
session->IsFolderOpenInWindow(ownerFolder, &folderOpen);
if (!folderOpen && ! (folderFlags & nsMsgFolderFlags::Inbox))
ownerFolder->SetMsgDatabase(nullptr);
}
}
nsCString logStr("Sync State set to ");
logStr.Append(stateStrings[aState]);
logStr.Append(" for ");
LogOwnerFolderName(logStr.get());
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::TryCurrentGroupAgain(uint32_t aRetryCount)
{
SetState(stReadyToDownload);
nsresult rv;
if (++mRetryCounter > aRetryCount)
{
ResetRetryCounter();
rv = NS_ERROR_FAILURE;
}
else
rv = Rollback();
return rv;
}
NS_IMETHODIMP nsAutoSyncState::ResetRetryCounter()
{
mRetryCounter = 0;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetPendingMessageCount(int32_t *aMsgCount)
{
NS_ENSURE_ARG_POINTER(aMsgCount);
*aMsgCount = mDownloadQ.Length() - mOffset;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetTotalMessageCount(int32_t *aMsgCount)
{
NS_ENSURE_ARG_POINTER(aMsgCount);
*aMsgCount = mDownloadQ.Length();
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetOwnerFolder(nsIMsgFolder **aFolder)
{
NS_ENSURE_ARG_POINTER(aFolder);
nsresult rv;
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
NS_IF_ADDREF(*aFolder = ownerFolder);
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::Rollback()
{
mOffset = mLastOffset;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::ResetDownloadQ()
{
mOffset = mLastOffset = 0;
mDownloadSet.Clear();
mDownloadQ.Clear();
mDownloadQ.Compact();
return NS_OK;
}
/**
* Tests whether the given folder is owned by the same imap server
* or not.
*/
NS_IMETHODIMP nsAutoSyncState::IsSibling(nsIAutoSyncState *aAnotherStateObj, bool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = false;
nsresult rv;
nsCOMPtr<nsIMsgFolder> folderA, folderB;
rv = GetOwnerFolder(getter_AddRefs(folderA));
NS_ENSURE_SUCCESS(rv,rv);
rv = aAnotherStateObj->GetOwnerFolder(getter_AddRefs(folderB));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr <nsIMsgIncomingServer> serverA, serverB;
rv = folderA->GetServer(getter_AddRefs(serverA));
NS_ENSURE_SUCCESS(rv,rv);
rv = folderB->GetServer(getter_AddRefs(serverB));
NS_ENSURE_SUCCESS(rv,rv);
bool isSibling;
rv = serverA->Equals(serverB, &isSibling);
if (NS_SUCCEEDED(rv))
*aResult = isSibling;
return rv;
}
NS_IMETHODIMP nsAutoSyncState::DownloadMessagesForOffline(nsIArray *aMessagesList)
{
NS_ENSURE_ARG_POINTER(aMessagesList);
uint32_t count;
nsresult rv = aMessagesList->GetLength(&count);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsAutoCString messageIds;
nsTArray<nsMsgKey> msgKeys;
rv = nsImapMailFolder::BuildIdsAndKeyArray(aMessagesList, messageIds, msgKeys);
if (NS_FAILED(rv) || messageIds.IsEmpty())
return rv;
// acquire semaphore for offline store. If it fails, we won't download
nsCOMPtr <nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = folder->AcquireSemaphore(folder);
NS_ENSURE_SUCCESS(rv, rv);
nsCString folderName;
folder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug, ("downloading %s for %s", messageIds.get(),
folderName.get()));
// start downloading
rv = imapService->DownloadMessagesForOffline(messageIds,
folder,
this,
nullptr);
if (NS_SUCCEEDED(rv))
SetState(stDownloadInProgress);
return rv;
}
NS_IMETHODIMP nsAutoSyncState::GetLastSyncTime(PRTime *aLastSyncTime)
{
NS_ENSURE_ARG_POINTER(aLastSyncTime);
*aLastSyncTime = mLastSyncTime;
return NS_OK;
}
void nsAutoSyncState::SetLastSyncTimeInSec(int32_t aLastSyncTime)
{
mLastSyncTime = ((PRTime)aLastSyncTime * PR_USEC_PER_SEC);
}
NS_IMETHODIMP nsAutoSyncState::GetLastUpdateTime(PRTime *aLastUpdateTime)
{
NS_ENSURE_ARG_POINTER(aLastUpdateTime);
*aLastUpdateTime = mLastUpdateTime;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::SetLastUpdateTime(PRTime aLastUpdateTime)
{
mLastUpdateTime = aLastUpdateTime;
return NS_OK;
}
void nsAutoSyncState::SetServerCounts(int32_t total, int32_t recent,
int32_t unseen, int32_t nextUID)
{
mLastServerTotal = total;
mLastServerRecent = recent;
mLastServerUnseen = unseen;
mLastNextUID = nextUID;
}
NS_IMPL_ISUPPORTS(nsAutoSyncState, nsIAutoSyncState, nsIUrlListener)
void nsAutoSyncState::LogQWithSize(nsTArray<nsMsgKey>& q, uint32_t toOffset)
{
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder)
{
nsCOMPtr<nsIMsgDatabase> database;
ownerFolder->GetMsgDatabase(getter_AddRefs(database));
uint32_t x = q.Length();
while (x > toOffset && database)
{
x--;
nsCOMPtr<nsIMsgDBHdr> h;
database->GetMsgHdrForKey(q[x], getter_AddRefs(h));
uint32_t s;
if (h)
{
h->GetMessageSize(&s);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("Elem #%d, size: %u bytes\n", x+1, s));
}
else
MOZ_LOG(gAutoSyncLog, LogLevel::Debug, ("unable to get header for key %ul", q[x]));
}
}
}
void nsAutoSyncState::LogQWithSize(nsIMutableArray *q, uint32_t toOffset)
{
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder)
{
nsCOMPtr<nsIMsgDatabase> database;
ownerFolder->GetMsgDatabase(getter_AddRefs(database));
uint32_t x;
q->GetLength(&x);
while (x > toOffset && database)
{
x--;
nsCOMPtr<nsIMsgDBHdr> h;
q->QueryElementAt(x, NS_GET_IID(nsIMsgDBHdr),
getter_AddRefs(h));
uint32_t s;
if (h)
{
h->GetMessageSize(&s);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("Elem #%d, size: %u bytes\n", x+1, s));
}
else
MOZ_LOG(gAutoSyncLog, LogLevel::Debug, ("null header in q at index %ul", x));
}
}
}
void nsAutoSyncState::LogOwnerFolderName(const char *s)
{
nsCOMPtr <nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder)
{
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("*** %s Folder: %s ***\n", s, folderName.get()));
}
}

View file

@ -0,0 +1,107 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsAutoSyncState_h__
#define nsAutoSyncState_h__
#include "MailNewsTypes.h"
#include "nsIAutoSyncState.h"
#include "nsIAutoSyncManager.h"
#include "nsIUrlListener.h"
#include "nsWeakPtr.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "prlog.h"
#include "nsIWeakReferenceUtils.h"
class nsImapMailFolder;
class nsIAutoSyncMsgStrategy;
class nsIMsgDatabase;
/**
* An adaptor class to make msg strategy nsTArray.Sort()
* compatible.
*/
class MsgStrategyComparatorAdaptor
{
public:
MsgStrategyComparatorAdaptor(nsIAutoSyncMsgStrategy* aStrategy,
nsIMsgFolder *aFolder, nsIMsgDatabase *aDatabase);
/** @return True if the elements are equals; false otherwise. */
bool Equals(const nsMsgKey& a, const nsMsgKey& b) const;
/** @return True if (a < b); false otherwise. */
bool LessThan(const nsMsgKey& a, const nsMsgKey& b) const;
private:
MsgStrategyComparatorAdaptor();
private:
nsIAutoSyncMsgStrategy *mStrategy;
nsIMsgFolder *mFolder;
nsIMsgDatabase *mDatabase;
};
/**
* Facilitates auto-sync capabilities for imap folders.
*/
class nsAutoSyncState final : public nsIAutoSyncState, public nsIUrlListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTOSYNCSTATE
NS_DECL_NSIURLLISTENER
nsAutoSyncState(nsImapMailFolder *aOwnerFolder, PRTime aLastSyncTime = 0UL);
/// Called by owner folder when new headers are fetched from the server
void OnNewHeaderFetchCompleted(const nsTArray<nsMsgKey> &aMsgKeyList);
/// Sets the last sync time in lower precision (seconds)
void SetLastSyncTimeInSec(int32_t aLastSyncTime);
/// Manages storage space for auto-sync operations
nsresult ManageStorageSpace();
void SetServerCounts(int32_t total, int32_t recent, int32_t unseen,
int32_t nextUID);
private:
~nsAutoSyncState();
nsresult PlaceIntoDownloadQ(const nsTArray<nsMsgKey> &aMsgKeyList);
nsresult SortQueueBasedOnStrategy(nsTArray<nsMsgKey> &aQueue);
nsresult SortSubQueueBasedOnStrategy(nsTArray<nsMsgKey> &aQueue,
uint32_t aStartingOffset);
void LogOwnerFolderName(const char *s);
void LogQWithSize(nsTArray<nsMsgKey>& q, uint32_t toOffset = 0);
void LogQWithSize(nsIMutableArray *q, uint32_t toOffset = 0);
private:
int32_t mSyncState;
nsWeakPtr mOwnerFolder;
uint32_t mOffset;
uint32_t mLastOffset;
// used to tell if the Server counts have changed.
int32_t mLastServerTotal;
int32_t mLastServerRecent;
int32_t mLastServerUnseen;
int32_t mLastNextUID;
PRTime mLastSyncTime;
PRTime mLastUpdateTime;
uint32_t mProcessPointer;
bool mIsDownloadQChanged;
uint32_t mRetryCounter;
nsTHashtable<nsUint32HashKey> mDownloadSet;
nsTArray<nsMsgKey> mDownloadQ;
nsTArray<nsMsgKey> mExistingHeadersQ;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,361 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
nsIMAPBodyShell and associated classes
*/
#ifndef IMAPBODY_H
#define IMAPBODY_H
#include "mozilla/Attributes.h"
#include "nsImapCore.h"
#include "nsStringGlue.h"
#include "nsRefPtrHashtable.h"
#include "nsTArray.h"
class nsImapProtocol;
typedef enum _nsIMAPBodypartType {
IMAP_BODY_MESSAGE_RFC822,
IMAP_BODY_MESSAGE_HEADER,
IMAP_BODY_LEAF,
IMAP_BODY_MULTIPART
} nsIMAPBodypartType;
class nsIMAPBodyShell;
class nsIMAPBodypartMessage;
class nsIMAPBodypart
{
public:
// Construction
virtual bool GetIsValid() { return m_isValid; }
virtual void SetIsValid(bool valid);
virtual nsIMAPBodypartType GetType() = 0;
// Generation
// Generates an HTML representation of this part. Returns content length generated, -1 if failed.
virtual int32_t Generate(nsIMAPBodyShell *aShell, bool /*stream*/, bool /* prefetch */) { return -1; }
virtual void AdoptPartDataBuffer(char *buf); // Adopts storage for part data buffer. If NULL, sets isValid to false.
virtual void AdoptHeaderDataBuffer(char *buf); // Adopts storage for header data buffer. If NULL, sets isValid to false.
virtual bool ShouldFetchInline(nsIMAPBodyShell *aShell) { return true; } // returns true if this part should be fetched inline for generation.
virtual bool PreflightCheckAllInline(nsIMAPBodyShell *aShell) { return true; }
virtual bool ShouldExplicitlyFetchInline();
virtual bool ShouldExplicitlyNotFetchInline();
virtual bool IsLastTextPart(const char *partNumberString) {return true;}
protected:
// If stream is false, simply returns the content length that will be generated
// the body of the part itself
virtual int32_t GeneratePart(nsIMAPBodyShell *aShell, bool stream, bool prefetch);
// the MIME headers of the part
virtual int32_t GenerateMIMEHeader(nsIMAPBodyShell *aShell, bool stream, bool prefetch);
// Generates the MIME boundary wrapper for this part.
virtual int32_t GenerateBoundary(nsIMAPBodyShell *aShell, bool stream, bool prefetch, bool lastBoundary);
// lastBoundary indicates whether or not this should be the boundary for the
// final MIME part of the multipart message.
// Generates (possibly empty) filling for a part that won't be filled in inline.
virtual int32_t GenerateEmptyFilling(nsIMAPBodyShell *aShell, bool stream, bool prefetch);
// Part Numbers / Hierarchy
public:
virtual char *GetPartNumberString() { return m_partNumberString; }
virtual nsIMAPBodypart *FindPartWithNumber(const char *partNum); // Returns the part object with the given number
virtual nsIMAPBodypart *GetParentPart() { return m_parentPart; } // Returns the parent of this part.
// We will define a part of type message/rfc822 to be the
// parent of its body and header.
// A multipart is a parent of its child parts.
// All other leafs do not have children.
// Other / Helpers
public:
virtual ~nsIMAPBodypart();
virtual nsIMAPBodypartMessage *GetnsIMAPBodypartMessage() { return NULL; }
const char *GetBodyType() { return m_bodyType; }
const char *GetBodySubType() { return m_bodySubType; }
void SetBoundaryData(char *boundaryData) { m_boundaryData = boundaryData; }
protected:
virtual void QueuePrefetchMIMEHeader(nsIMAPBodyShell *aShell);
//virtual void PrefetchMIMEHeader(); // Initiates a prefetch for the MIME header of this part.
nsIMAPBodypart(char *partNumber, nsIMAPBodypart *parentPart);
protected:
bool m_isValid; // If this part is valid.
char *m_partNumberString; // string representation of this part's full-hierarchy number. Define 0 to be the top-level message
char *m_partData; // data for this part. NULL if not filled in yet.
char *m_headerData; // data for this part's MIME header. NULL if not filled in yet.
char *m_boundaryData; // MIME boundary for this part
int32_t m_partLength;
int32_t m_contentLength; // Total content length which will be Generate()'d. -1 if not filled in yet.
nsIMAPBodypart *m_parentPart; // Parent of this part
// Fields - Filled in from parsed BODYSTRUCTURE response (as well as others)
char *m_contentType; // constructed from m_bodyType and m_bodySubType
char *m_bodyType;
char *m_bodySubType;
char *m_bodyID;
char *m_bodyDescription;
char *m_bodyEncoding;
// we ignore extension data for now
};
// Message headers
// A special type of nsIMAPBodypart
// These may be headers for the top-level message,
// or any body part of type message/rfc822.
class nsIMAPMessageHeaders : public nsIMAPBodypart
{
public:
nsIMAPMessageHeaders(char *partNum, nsIMAPBodypart *parentPart);
virtual nsIMAPBodypartType GetType() override;
// Generates an HTML representation of this part. Returns content length generated, -1 if failed.
virtual int32_t Generate(nsIMAPBodyShell *aShell, bool stream,
bool prefetch) override;
virtual bool ShouldFetchInline(nsIMAPBodyShell *aShell) override;
virtual void QueuePrefetchMessageHeaders(nsIMAPBodyShell *aShell);
};
class nsIMAPBodypartMultipart : public nsIMAPBodypart
{
public:
nsIMAPBodypartMultipart(char *partNum, nsIMAPBodypart *parentPart);
virtual nsIMAPBodypartType GetType() override;
virtual ~nsIMAPBodypartMultipart();
virtual bool ShouldFetchInline(nsIMAPBodyShell *aShell) override;
virtual bool PreflightCheckAllInline(nsIMAPBodyShell *aShell) override;
// Generates an HTML representation of this part. Returns content length generated, -1 if failed.
virtual int32_t Generate(nsIMAPBodyShell *aShell, bool stream,
bool prefetch) override;
// Returns the part object with the given number
virtual nsIMAPBodypart *FindPartWithNumber(const char *partNum
) override;
virtual bool IsLastTextPart(const char *partNumberString) override;
void AppendPart(nsIMAPBodypart *part) { m_partList->AppendElement(part); }
void SetBodySubType(char *bodySubType);
protected:
nsTArray<nsIMAPBodypart*> *m_partList; // An ordered list of top-level body parts for this shell
};
// The name "leaf" is somewhat misleading, since a part of type message/rfc822 is technically
// a leaf, even though it can contain other parts within it.
class nsIMAPBodypartLeaf : public nsIMAPBodypart
{
public:
nsIMAPBodypartLeaf(char *partNum, nsIMAPBodypart *parentPart, char *bodyType,
char *bodySubType, char *bodyID, char *bodyDescription,
char *bodyEncoding, int32_t partLength,
bool preferPlainText);
virtual nsIMAPBodypartType GetType() override;
// Generates an HTML representation of this part. Returns content length generated, -1 if failed.
virtual int32_t Generate(nsIMAPBodyShell *aShell, bool stream, bool prefetch) override;
// returns true if this part should be fetched inline for generation.
virtual bool ShouldFetchInline(nsIMAPBodyShell *aShell) override;
virtual bool PreflightCheckAllInline(nsIMAPBodyShell *aShell) override;
private:
bool mPreferPlainText;
};
class nsIMAPBodypartMessage : public nsIMAPBodypartLeaf
{
public:
nsIMAPBodypartMessage(char *partNum, nsIMAPBodypart *parentPart,
bool topLevelMessage, char *bodyType,
char *bodySubType, char *bodyID,
char *bodyDescription, char *bodyEncoding,
int32_t partLength, bool preferPlainText);
void SetBody(nsIMAPBodypart *body);
virtual nsIMAPBodypartType GetType() override;
virtual ~nsIMAPBodypartMessage();
virtual int32_t Generate(nsIMAPBodyShell *aShell, bool stream,
bool prefetch) override;
virtual bool ShouldFetchInline(nsIMAPBodyShell *aShell) override;
virtual bool PreflightCheckAllInline(nsIMAPBodyShell *aShell) override;
// Returns the part object with the given number
virtual nsIMAPBodypart *FindPartWithNumber(const char *partNum
) override;
void AdoptMessageHeaders(char *headers); // Fills in buffer (and adopts storage) for header object
// partNum specifies the message part number to which the
// headers correspond. NULL indicates the top-level message
virtual nsIMAPBodypartMessage *GetnsIMAPBodypartMessage() override { return this; }
virtual bool GetIsTopLevelMessage() { return m_topLevelMessage; }
protected:
nsIMAPMessageHeaders *m_headers; // Every body shell should have headers
nsIMAPBodypart *m_body;
bool m_topLevelMessage; // Whether or not this is the top-level message
};
class nsIMAPMessagePartIDArray;
// We will refer to a Body "Shell" as a hierarchical object representation of a parsed BODYSTRUCTURE
// response. A shell contains representations of Shell "Parts." A Body Shell can undergo essentially
// two operations: Construction and Generation.
// Shell Construction occurs from a parsed a BODYSTRUCTURE response, split into empty parts.
// Shell Generation generates a "MIME Shell" of the message and streams it to libmime for
// display. The MIME Shell has selected (inline) parts filled in, and leaves all others
// for on-demand retrieval through explicit part fetches.
class nsIMAPBodyShell : public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
nsIMAPBodyShell(nsImapProtocol *protocolConnection,
nsIMAPBodypartMessage *message, uint32_t UID,
const char *folderName);
// To be used after a shell is uncached
void SetConnection(nsImapProtocol *con) { m_protocolConnection = con; }
virtual bool GetIsValid() { return m_isValid; }
virtual void SetIsValid(bool valid);
// Prefetch
// Adds a message body part to the queue to be prefetched
// in a single, pipelined command
void AddPrefetchToQueue(nsIMAPeFetchFields, const char *partNum);
// Runs a single pipelined command which fetches all of the
// elements in the prefetch queue
void FlushPrefetchQueue();
// Fills in buffer (and adopts storage) for header object
// partNum specifies the message part number to which the
// headers correspond. NULL indicates the top-level message
void AdoptMessageHeaders(char *headers, const char *partNum);
// Fills in buffer (and adopts storage) for MIME headers in appropriate object.
// If object can't be found, sets isValid to false.
void AdoptMimeHeader(const char *partNum, char *mimeHeader);
// Generation
// Streams out an HTML representation of this IMAP message, going along and
// fetching parts it thinks it needs, and leaving empty shells for the parts
// it doesn't.
// Returns number of bytes generated, or -1 if invalid.
// If partNum is not NULL, then this works to generates a MIME part that hasn't been downloaded yet
// and leaves out all other parts. By default, to generate a normal message, partNum should be NULL.
virtual int32_t Generate(char *partNum);
// Returns TRUE if the user has the pref "Show Attachments Inline" set.
// Returns FALSE if the setting is "Show Attachments as Links"
virtual bool GetShowAttachmentsInline();
// Returns true if all parts are inline, false otherwise. Does not generate anything.
bool PreflightCheckAllInline();
// Helpers
nsImapProtocol *GetConnection() { return m_protocolConnection; }
bool GetPseudoInterrupted();
bool DeathSignalReceived();
nsCString &GetUID() { return m_UID; }
const char *GetFolderName() { return m_folderName; }
char *GetGeneratingPart() { return m_generatingPart; }
// Returns true if this is in the process of being generated,
// so we don't re-enter
bool IsBeingGenerated() { return m_isBeingGenerated; }
bool IsShellCached() { return m_cached; }
void SetIsCached(bool isCached) { m_cached = isCached; }
bool GetGeneratingWholeMessage() { return m_generatingWholeMessage; }
IMAP_ContentModifiedType GetContentModified() { return m_contentModified; }
void SetContentModified(IMAP_ContentModifiedType modType) { m_contentModified = modType; }
protected:
virtual ~nsIMAPBodyShell();
nsIMAPBodypartMessage *m_message;
nsIMAPMessagePartIDArray *m_prefetchQueue; // array of pipelined part prefetches. Ok, so it's not really a queue.
bool m_isValid;
nsImapProtocol *m_protocolConnection; // Connection, for filling in parts
nsCString m_UID; // UID of this message
char *m_folderName; // folder that contains this message
char *m_generatingPart; // If a specific part is being generated, this is it. Otherwise, NULL.
bool m_isBeingGenerated; // true if this body shell is in the process of being generated
bool m_gotAttachmentPref; // Whether or not m_showAttachmentsInline has been initialized
bool m_showAttachmentsInline; // Whether or not we should display attachment inline
bool m_cached; // Whether or not this shell is cached
bool m_generatingWholeMessage; // whether or not we are generating the whole (non-MPOD) message
// Set to false if we are generating by parts
// under what conditions the content has been modified.
// Either IMAP_CONTENT_MODIFIED_VIEW_INLINE or IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS
IMAP_ContentModifiedType m_contentModified;
};
// This class caches shells, so we don't have to always go and re-fetch them.
// This does not cache any of the filled-in inline parts; those are cached individually
// in the libnet memory cache. (ugh, how will we do that?)
// Since we'll only be retrieving shells for messages over a given size, and since the
// shells themselves won't be very large, this cache will not grow very big (relatively)
// and should handle most common usage scenarios.
// A body cache is associated with a given host, spanning folders.
// It should pay attention to UIDVALIDITY.
class nsIMAPBodyShellCache
{
public:
static nsIMAPBodyShellCache *Create();
virtual ~nsIMAPBodyShellCache();
// Adds shell to cache, possibly ejecting
// another entry based on scheme in EjectEntry().
bool AddShellToCache(nsIMAPBodyShell *shell);
// Looks up a shell in the cache given the message's UID.
nsIMAPBodyShell *FindShellForUID(nsCString &UID, const char *mailboxName,
IMAP_ContentModifiedType modType);
void Clear();
protected:
nsIMAPBodyShellCache();
// Chooses an entry to eject; deletes that entry; and ejects it from the
// cache, clearing up a new space. Returns true if it found an entry
// to eject, false otherwise.
bool EjectEntry();
uint32_t GetSize() { return m_shellList->Length(); }
uint32_t GetMaxSize() { return 20; }
nsTArray<nsIMAPBodyShell*> *m_shellList; // For maintenance
// For quick lookup based on UID
nsRefPtrHashtable <nsCStringHashKey, nsIMAPBodyShell> m_shellHash;
};
// MessagePartID and MessagePartIDArray are used for pipelining prefetches.
class nsIMAPMessagePartID
{
public:
nsIMAPMessagePartID(nsIMAPeFetchFields fields, const char *partNumberString);
nsIMAPeFetchFields GetFields() { return m_fields; }
const char *GetPartNumberString() { return m_partNumberString; }
protected:
const char *m_partNumberString;
nsIMAPeFetchFields m_fields;
};
class nsIMAPMessagePartIDArray : public nsTArray<nsIMAPMessagePartID*> {
public:
nsIMAPMessagePartIDArray();
~nsIMAPMessagePartIDArray();
void RemoveAndFreeAll();
uint32_t GetNumParts() { return Length(); }
nsIMAPMessagePartID *GetPart(uint32_t i)
{
NS_ASSERTION(i < Length(), "invalid message part #");
return ElementAt(i);
}
};
#endif // IMAPBODY_H

View file

@ -0,0 +1,484 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h" // for pre-compiled headers
#include "nsImapCore.h"
#include "nsImapProtocol.h"
#include "nsIMAPGenericParser.h"
#include "nsStringGlue.h"
////////////////// nsIMAPGenericParser /////////////////////////
nsIMAPGenericParser::nsIMAPGenericParser() :
fNextToken(nullptr),
fCurrentLine(nullptr),
fLineOfTokens(nullptr),
fStartOfLineOfTokens(nullptr),
fCurrentTokenPlaceHolder(nullptr),
fAtEndOfLine(false),
fParserState(stateOK)
{
}
nsIMAPGenericParser::~nsIMAPGenericParser()
{
PR_FREEIF( fCurrentLine );
PR_FREEIF( fStartOfLineOfTokens);
}
void nsIMAPGenericParser::HandleMemoryFailure()
{
SetConnected(false);
}
void nsIMAPGenericParser::ResetLexAnalyzer()
{
PR_FREEIF( fCurrentLine );
PR_FREEIF( fStartOfLineOfTokens );
fNextToken = fCurrentLine = fLineOfTokens = fStartOfLineOfTokens = fCurrentTokenPlaceHolder = nullptr;
fAtEndOfLine = false;
}
bool nsIMAPGenericParser::LastCommandSuccessful()
{
return fParserState == stateOK;
}
void nsIMAPGenericParser::SetSyntaxError(bool error, const char *msg)
{
if (error)
fParserState |= stateSyntaxErrorFlag;
else
fParserState &= ~stateSyntaxErrorFlag;
NS_ASSERTION(!error, "syntax error in generic parser");
}
void nsIMAPGenericParser::SetConnected(bool connected)
{
if (connected)
fParserState &= ~stateDisconnectedFlag;
else
fParserState |= stateDisconnectedFlag;
}
void nsIMAPGenericParser::skip_to_CRLF()
{
while (Connected() && !fAtEndOfLine)
AdvanceToNextToken();
}
// fNextToken initially should point to
// a string after the initial open paren ("(")
// After this call, fNextToken points to the
// first character after the matching close
// paren. Only call AdvanceToNextToken() to get the NEXT
// token after the one returned in fNextToken.
void nsIMAPGenericParser::skip_to_close_paren()
{
int numberOfCloseParensNeeded = 1;
while (ContinueParse())
{
// go through fNextToken, account for nested parens
const char *loc;
for (loc = fNextToken; loc && *loc; loc++)
{
if (*loc == '(')
numberOfCloseParensNeeded++;
else if (*loc == ')')
{
numberOfCloseParensNeeded--;
if (numberOfCloseParensNeeded == 0)
{
fNextToken = loc + 1;
if (!fNextToken || !*fNextToken)
AdvanceToNextToken();
return;
}
}
else if (*loc == '{' || *loc == '"') {
// quoted or literal
fNextToken = loc;
char *a = CreateString();
PR_FREEIF(a);
break; // move to next token
}
}
if (ContinueParse())
AdvanceToNextToken();
}
}
void nsIMAPGenericParser::AdvanceToNextToken()
{
if (!fCurrentLine || fAtEndOfLine)
AdvanceToNextLine();
if (Connected())
{
if (!fStartOfLineOfTokens)
{
// this is the first token of the line; setup tokenizer now
fStartOfLineOfTokens = PL_strdup(fCurrentLine);
if (!fStartOfLineOfTokens)
{
HandleMemoryFailure();
return;
}
fLineOfTokens = fStartOfLineOfTokens;
fCurrentTokenPlaceHolder = fStartOfLineOfTokens;
}
fNextToken = NS_strtok(WHITESPACE, &fCurrentTokenPlaceHolder);
if (!fNextToken)
{
fAtEndOfLine = true;
fNextToken = CRLF;
}
}
}
void nsIMAPGenericParser::AdvanceToNextLine()
{
PR_FREEIF( fCurrentLine );
PR_FREEIF( fStartOfLineOfTokens);
bool ok = GetNextLineForParser(&fCurrentLine);
if (!ok)
{
SetConnected(false);
fStartOfLineOfTokens = nullptr;
fLineOfTokens = nullptr;
fCurrentTokenPlaceHolder = nullptr;
fAtEndOfLine = true;
fNextToken = CRLF;
}
else if (!fCurrentLine)
{
HandleMemoryFailure();
}
else
{
fNextToken = nullptr;
// determine if there are any tokens (without calling AdvanceToNextToken);
// otherwise we are already at end of line
NS_ASSERTION(strlen(WHITESPACE) == 3, "assume 3 chars of whitespace");
char *firstToken = fCurrentLine;
while (*firstToken && (*firstToken == WHITESPACE[0] ||
*firstToken == WHITESPACE[1] || *firstToken == WHITESPACE[2]))
firstToken++;
fAtEndOfLine = (*firstToken == '\0');
}
}
// advances |fLineOfTokens| by |bytesToAdvance| bytes
void nsIMAPGenericParser::AdvanceTokenizerStartingPoint(int32_t bytesToAdvance)
{
NS_PRECONDITION(bytesToAdvance>=0, "bytesToAdvance must not be negative");
if (!fStartOfLineOfTokens)
{
AdvanceToNextToken(); // the tokenizer was not yet initialized, do it now
if (!fStartOfLineOfTokens)
return;
}
if(!fStartOfLineOfTokens)
return;
// The last call to AdvanceToNextToken() cleared the token separator to '\0'
// iff |fCurrentTokenPlaceHolder|. We must recover this token separator now.
if (fCurrentTokenPlaceHolder)
{
int endTokenOffset = fCurrentTokenPlaceHolder - fStartOfLineOfTokens - 1;
if (endTokenOffset >= 0)
fStartOfLineOfTokens[endTokenOffset] = fCurrentLine[endTokenOffset];
}
NS_ASSERTION(bytesToAdvance + (fLineOfTokens-fStartOfLineOfTokens) <=
(int32_t)strlen(fCurrentLine), "cannot advance beyond end of fLineOfTokens");
fLineOfTokens += bytesToAdvance;
fCurrentTokenPlaceHolder = fLineOfTokens;
}
// RFC3501: astring = 1*ASTRING-CHAR / string
// string = quoted / literal
// This function leaves us off with fCurrentTokenPlaceHolder immediately after
// the end of the Astring. Call AdvanceToNextToken() to get the token after it.
char *nsIMAPGenericParser::CreateAstring()
{
if (*fNextToken == '{')
return CreateLiteral(); // literal
else if (*fNextToken == '"')
return CreateQuoted(); // quoted
else
return CreateAtom(true); // atom
}
// Create an atom
// This function does not advance the parser.
// Call AdvanceToNextToken() to get the next token after the atom.
// RFC3501: atom = 1*ATOM-CHAR
// ASTRING-CHAR = ATOM-CHAR / resp-specials
// ATOM-CHAR = <any CHAR except atom-specials>
// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards /
// quoted-specials / resp-specials
// list-wildcards = "%" / "*"
// quoted-specials = DQUOTE / "\"
// resp-specials = "]"
// "Characters are 7-bit US-ASCII unless otherwise specified." [RFC3501, 1.2.]
char *nsIMAPGenericParser::CreateAtom(bool isAstring)
{
char *rv = PL_strdup(fNextToken);
if (!rv)
{
HandleMemoryFailure();
return nullptr;
}
// We wish to stop at the following characters (in decimal ascii)
// 1-31 (CTL), 32 (SP), 34 '"', 37 '%', 40-42 "()*", 92 '\\', 123 '{'
// also, ']' is only allowed in astrings
char *last = rv;
char c = *last;
while ((c > 42 || c == 33 || c == 35 || c == 36 || c == 38 || c == 39)
&& c != '\\' && c != '{' && (isAstring || c != ']'))
c = *++last;
if (rv == last) {
SetSyntaxError(true, "no atom characters found");
PL_strfree(rv);
return nullptr;
}
if (*last)
{
// not the whole token was consumed
*last = '\0';
AdvanceTokenizerStartingPoint((fNextToken - fLineOfTokens) + (last-rv));
}
return rv;
}
// CreateNilString return either NULL (for "NIL") or a string
// Call with fNextToken pointing to the thing which we think is the nilstring.
// This function leaves us off with fCurrentTokenPlaceHolder immediately after
// the end of the string.
// Regardless of type, call AdvanceToNextToken() to get the token after it.
// RFC3501: nstring = string / nil
// nil = "NIL"
char *nsIMAPGenericParser::CreateNilString()
{
if (!PL_strncasecmp(fNextToken, "NIL", 3))
{
// check if there is text after "NIL" in fNextToken,
// equivalent handling as in CreateQuoted
if (fNextToken[3])
AdvanceTokenizerStartingPoint((fNextToken - fLineOfTokens) + 3);
return NULL;
}
else
return CreateString();
}
// Create a string, which can either be quoted or literal,
// but not an atom.
// This function leaves us off with fCurrentTokenPlaceHolder immediately after
// the end of the String. Call AdvanceToNextToken() to get the token after it.
char *nsIMAPGenericParser::CreateString()
{
if (*fNextToken == '{')
{
char *rv = CreateLiteral(); // literal
return (rv);
}
else if (*fNextToken == '"')
{
char *rv = CreateQuoted(); // quoted
return (rv);
}
else
{
SetSyntaxError(true, "string does not start with '{' or '\"'");
return NULL;
}
}
// This function sets fCurrentTokenPlaceHolder immediately after the end of the
// closing quote. Call AdvanceToNextToken() to get the token after it.
// QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> /
// "\" quoted_specials
// TEXT_CHAR ::= <any CHAR except CR and LF>
// quoted_specials ::= <"> / "\"
// Note that according to RFC 1064 and RFC 2060, CRs and LFs are not allowed
// inside a quoted string. It is sufficient to read from the current line only.
char *nsIMAPGenericParser::CreateQuoted(bool /*skipToEnd*/)
{
// one char past opening '"'
char *currentChar = fCurrentLine + (fNextToken - fStartOfLineOfTokens) + 1;
int escapeCharsCut = 0;
nsCString returnString(currentChar);
int charIndex;
for (charIndex = 0; returnString.CharAt(charIndex) != '"'; charIndex++)
{
if (!returnString.CharAt(charIndex))
{
SetSyntaxError(true, "no closing '\"' found in quoted");
return nullptr;
}
else if (returnString.CharAt(charIndex) == '\\')
{
// eat the escape character, but keep the escaped character
returnString.Cut(charIndex, 1);
escapeCharsCut++;
}
}
// +2 because of the start and end quotes
AdvanceTokenizerStartingPoint((fNextToken - fLineOfTokens) +
charIndex + escapeCharsCut + 2);
returnString.SetLength(charIndex);
return ToNewCString(returnString);
}
// This function leaves us off with fCurrentTokenPlaceHolder immediately after
// the end of the literal string. Call AdvanceToNextToken() to get the token
// after the literal string.
// RFC3501: literal = "{" number "}" CRLF *CHAR8
// ; Number represents the number of CHAR8s
// CHAR8 = %x01-ff
// ; any OCTET except NUL, %x00
char *nsIMAPGenericParser::CreateLiteral()
{
int32_t numberOfCharsInMessage = atoi(fNextToken + 1);
uint32_t numBytes = numberOfCharsInMessage + 1;
NS_ASSERTION(numBytes, "overflow!");
if (!numBytes)
return nullptr;
char *returnString = (char *)PR_Malloc(numBytes);
if (!returnString)
{
HandleMemoryFailure();
return nullptr;
}
int32_t currentLineLength = 0;
int32_t charsReadSoFar = 0;
int32_t bytesToCopy = 0;
while (charsReadSoFar < numberOfCharsInMessage)
{
AdvanceToNextLine();
if (!ContinueParse())
break;
currentLineLength = strlen(fCurrentLine);
bytesToCopy = (currentLineLength > numberOfCharsInMessage - charsReadSoFar ?
numberOfCharsInMessage - charsReadSoFar : currentLineLength);
NS_ASSERTION(bytesToCopy, "zero-length line?");
memcpy(returnString + charsReadSoFar, fCurrentLine, bytesToCopy);
charsReadSoFar += bytesToCopy;
}
if (ContinueParse())
{
if (currentLineLength == bytesToCopy)
{
// We have consumed the entire line.
// Consider the input "{4}\r\n" "L1\r\n" " A2\r\n" which is read
// line-by-line. Reading an Astring, this should result in "L1\r\n".
// Note that the second line is "L1\r\n", where the "\r\n" is part of
// the literal. Hence, we now read the next line to ensure that the
// next call to AdvanceToNextToken() leads to fNextToken=="A2" in our
// example.
AdvanceToNextLine();
}
else
AdvanceTokenizerStartingPoint(bytesToCopy);
}
returnString[charsReadSoFar] = 0;
return returnString;
}
// Call this to create a buffer containing all characters within
// a given set of parentheses.
// Call this with fNextToken[0]=='(', that is, the open paren
// of the group.
// It will allocate and return all characters up to and including the corresponding
// closing paren, and leave the parser in the right place afterwards.
char *nsIMAPGenericParser::CreateParenGroup()
{
NS_ASSERTION(fNextToken[0] == '(', "we don't have a paren group!");
int numOpenParens = 0;
AdvanceTokenizerStartingPoint(fNextToken - fLineOfTokens);
// Build up a buffer containing the paren group.
nsCString returnString;
char *parenGroupStart = fCurrentTokenPlaceHolder;
NS_ASSERTION(parenGroupStart[0] == '(', "we don't have a paren group (2)!");
while (*fCurrentTokenPlaceHolder)
{
if (*fCurrentTokenPlaceHolder == '{') // literal
{
// Ensure it is a properly formatted literal.
NS_ASSERTION(!strcmp("}\r\n", fCurrentTokenPlaceHolder + strlen(fCurrentTokenPlaceHolder) - 3), "not a literal");
// Append previous characters and the "{xx}\r\n" to buffer.
returnString.Append(parenGroupStart);
// Append literal itself.
AdvanceToNextToken();
if (!ContinueParse())
break;
char *lit = CreateLiteral();
NS_ASSERTION(lit, "syntax error or out of memory");
if (!lit)
break;
returnString.Append(lit);
PR_Free(lit);
if (!ContinueParse())
break;
parenGroupStart = fCurrentTokenPlaceHolder;
}
else if (*fCurrentTokenPlaceHolder == '"') // quoted
{
// Append the _escaped_ version of the quoted string:
// just skip it (because the quoted string must be on the same line).
AdvanceToNextToken();
if (!ContinueParse())
break;
char *q = CreateQuoted();
if (!q)
break;
PR_Free(q);
if (!ContinueParse())
break;
}
else
{
// Append this character to the buffer.
char c = *fCurrentTokenPlaceHolder++;
if (c == '(')
numOpenParens++;
else if (c == ')')
{
numOpenParens--;
if (numOpenParens == 0)
break;
}
}
}
if (numOpenParens != 0 || !ContinueParse())
{
SetSyntaxError(true, "closing ')' not found in paren group");
return nullptr;
}
returnString.Append(parenGroupStart, fCurrentTokenPlaceHolder - parenGroupStart);
AdvanceToNextToken();
return ToNewCString(returnString);
}

View file

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
nsIMAPGenericParser is the base parser class used by the server parser and body shell parser
*/
#ifndef nsIMAPGenericParser_H
#define nsIMAPGenericParser_H
#include "nsImapCore.h"
#define WHITESPACE " \015\012" // token delimiter
class nsIMAPGenericParser
{
public:
nsIMAPGenericParser();
virtual ~nsIMAPGenericParser();
// Add any specific stuff in the derived class
virtual bool LastCommandSuccessful();
bool SyntaxError() { return (fParserState & stateSyntaxErrorFlag) != 0; }
bool ContinueParse() { return fParserState == stateOK; }
bool Connected() { return !(fParserState & stateDisconnectedFlag); }
void SetConnected(bool error);
protected:
// This is a pure virtual member which must be overridden in the derived class
// for each different implementation of a nsIMAPGenericParser.
// For instance, one implementation (the nsIMAPServerState) might get the next line
// from an open socket, whereas another implementation might just get it from a buffer somewhere.
// This fills in nextLine with the buffer, and returns true if everything is OK.
// Returns false if there was some error encountered. In that case, we reset the parser.
virtual bool GetNextLineForParser(char **nextLine) = 0;
virtual void HandleMemoryFailure();
void skip_to_CRLF();
void skip_to_close_paren();
char *CreateString();
char *CreateAstring();
char *CreateNilString();
char *CreateLiteral();
char *CreateAtom(bool isAstring = false);
char *CreateQuoted(bool skipToEnd = true);
char *CreateParenGroup();
virtual void SetSyntaxError(bool error, const char *msg);
void AdvanceToNextToken();
void AdvanceToNextLine();
void AdvanceTokenizerStartingPoint(int32_t bytesToAdvance);
void ResetLexAnalyzer();
protected:
// use with care
const char *fNextToken;
char *fCurrentLine;
char *fLineOfTokens;
char *fStartOfLineOfTokens;
char *fCurrentTokenPlaceHolder;
bool fAtEndOfLine;
private:
enum nsIMAPGenericParserState { stateOK = 0,
stateSyntaxErrorFlag = 0x1,
stateDisconnectedFlag = 0x2 };
uint32_t fParserState;
};
#endif

View file

@ -0,0 +1,701 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h"
#include "nsIMAPHostSessionList.h"
#include "nsIMAPBodyShell.h"
#include "nsIMAPNamespace.h"
#include "nsISupportsUtils.h"
#include "nsIImapIncomingServer.h"
#include "nsCOMPtr.h"
#include "nsIMsgIncomingServer.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsMsgUtils.h"
#include "mozilla/Services.h"
nsIMAPHostInfo::nsIMAPHostInfo(const char *serverKey,
nsIImapIncomingServer *server)
{
fServerKey = serverKey;
NS_ASSERTION(server, "*** Fatal null imap incoming server...\n");
server->GetServerDirectory(fOnlineDir);
fNextHost = NULL;
fCachedPassword = NULL;
fCapabilityFlags = kCapabilityUndefined;
fHierarchyDelimiters = NULL;
#ifdef DEBUG_bienvenu1
fHaveWeEverDiscoveredFolders = true; // try this, see what bad happens - we'll need to
// figure out a way to make new accounts have it be false
#else
fHaveWeEverDiscoveredFolders = false; // try this, see what bad happens
#endif
fCanonicalOnlineSubDir = NULL;
fNamespaceList = nsIMAPNamespaceList::CreatensIMAPNamespaceList();
fUsingSubscription = true;
server->GetUsingSubscription(&fUsingSubscription);
fOnlineTrashFolderExists = false;
fShouldAlwaysListInbox = true;
fShellCache = nsIMAPBodyShellCache::Create();
fPasswordVerifiedOnline = false;
fDeleteIsMoveToTrash = true;
fShowDeletedMessages = false;
fGotNamespaces = false;
fHaveAdminURL = false;
fNamespacesOverridable = true;
server->GetOverrideNamespaces(&fNamespacesOverridable);
fTempNamespaceList = nsIMAPNamespaceList::CreatensIMAPNamespaceList();
}
nsIMAPHostInfo::~nsIMAPHostInfo()
{
PR_Free(fCachedPassword);
PR_Free(fHierarchyDelimiters);
delete fNamespaceList;
delete fTempNamespaceList;
delete fShellCache;
}
NS_IMPL_ISUPPORTS(nsIMAPHostSessionList,
nsIImapHostSessionList,
nsIObserver,
nsISupportsWeakReference)
nsIMAPHostSessionList::nsIMAPHostSessionList()
{
gCachedHostInfoMonitor = PR_NewMonitor(/* "accessing-hostlist-monitor"*/);
fHostInfoList = nullptr;
}
nsIMAPHostSessionList::~nsIMAPHostSessionList()
{
ResetAll();
PR_DestroyMonitor(gCachedHostInfoMonitor);
}
nsresult nsIMAPHostSessionList::Init()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
observerService->AddObserver(this, "profile-before-change", true);
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
return NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
{
if (!strcmp(aTopic, "profile-before-change"))
ResetAll();
else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID))
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
observerService->RemoveObserver(this, "profile-before-change");
}
return NS_OK;
}
nsIMAPHostInfo *nsIMAPHostSessionList::FindHost(const char *serverKey)
{
nsIMAPHostInfo *host;
// ### should also check userName here, if NON NULL
for (host = fHostInfoList; host; host = host->fNextHost)
{
if (host->fServerKey.Equals(serverKey, nsCaseInsensitiveCStringComparator()))
return host;
}
return host;
}
// reset any cached connection info - delete the lot of 'em
NS_IMETHODIMP nsIMAPHostSessionList::ResetAll()
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *nextHost = NULL;
for (nsIMAPHostInfo *host = fHostInfoList; host; host = nextHost)
{
nextHost = host->fNextHost;
delete host;
}
fHostInfoList = NULL;
PR_ExitMonitor(gCachedHostInfoMonitor);
return NS_OK;
}
NS_IMETHODIMP
nsIMAPHostSessionList::AddHostToList(const char *serverKey,
nsIImapIncomingServer *server)
{
nsIMAPHostInfo *newHost=NULL;
PR_EnterMonitor(gCachedHostInfoMonitor);
if (!FindHost(serverKey))
{
// stick it on the front
newHost = new nsIMAPHostInfo(serverKey, server);
if (newHost)
{
newHost->fNextHost = fHostInfoList;
fHostInfoList = newHost;
}
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (newHost == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetPasswordForHost(const char *serverKey, nsString &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
CopyASCIItoUTF16(nsDependentCString(host->fCachedPassword), result);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetPasswordForHost(const char *serverKey, const char *password)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
PR_FREEIF(host->fCachedPassword);
if (password)
host->fCachedPassword = NS_strdup(password);
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetPasswordVerifiedOnline(const char *serverKey)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fPasswordVerifiedOnline = true;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetPasswordVerifiedOnline(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fPasswordVerifiedOnline;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetOnlineDirForHost(const char *serverKey,
nsString &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
CopyASCIItoUTF16(host->fOnlineDir, result);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetOnlineDirForHost(const char *serverKey,
const char *onlineDir)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
if (onlineDir)
host->fOnlineDir = onlineDir;
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetDeleteIsMoveToTrashForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fDeleteIsMoveToTrash;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetShowDeletedMessagesForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fShowDeletedMessages;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetDeleteIsMoveToTrashForHost(const char *serverKey, bool isMoveToTrash)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fDeleteIsMoveToTrash = isMoveToTrash;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetShowDeletedMessagesForHost(const char *serverKey, bool showDeletedMessages)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fShowDeletedMessages = showDeletedMessages;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetGotNamespacesForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fGotNamespaces;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetGotNamespacesForHost(const char *serverKey, bool gotNamespaces)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fGotNamespaces = gotNamespaces;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetHostIsUsingSubscription(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fUsingSubscription;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetHostIsUsingSubscription(const char *serverKey, bool usingSubscription)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fUsingSubscription = usingSubscription;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetHostHasAdminURL(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fHaveAdminURL;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetHostHasAdminURL(const char *serverKey, bool haveAdminURL)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fHaveAdminURL = haveAdminURL;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetHaveWeEverDiscoveredFoldersForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fHaveWeEverDiscoveredFolders;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetHaveWeEverDiscoveredFoldersForHost(const char *serverKey, bool discovered)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fHaveWeEverDiscoveredFolders = discovered;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetOnlineTrashFolderExistsForHost(const char *serverKey, bool exists)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fOnlineTrashFolderExists = exists;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetOnlineTrashFolderExistsForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fOnlineTrashFolderExists;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::AddNewNamespaceForHost(const char *serverKey, nsIMAPNamespace *ns)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fNamespaceList->AddNewNamespace(ns);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetNamespaceFromPrefForHost(const char *serverKey,
const char *namespacePref, EIMAPNamespaceType nstype)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
if (namespacePref)
{
int numNamespaces = host->fNamespaceList->UnserializeNamespaces(namespacePref, nullptr, 0);
char **prefixes = (char**) PR_CALLOC(numNamespaces * sizeof(char*));
if (prefixes)
{
int len = host->fNamespaceList->UnserializeNamespaces(namespacePref, prefixes, numNamespaces);
for (int i = 0; i < len; i++)
{
char *thisns = prefixes[i];
char delimiter = '/'; // a guess
if (PL_strlen(thisns) >= 1)
delimiter = thisns[PL_strlen(thisns)-1];
nsIMAPNamespace *ns = new nsIMAPNamespace(nstype, thisns, delimiter, true);
if (ns)
host->fNamespaceList->AddNewNamespace(ns);
PR_FREEIF(thisns);
}
PR_Free(prefixes);
}
}
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetNamespaceForMailboxForHost(const char *serverKey, const char *mailbox_name, nsIMAPNamespace * &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fNamespaceList->GetNamespaceForMailbox(mailbox_name);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::ClearPrefsNamespacesForHost(const char *serverKey)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fNamespaceList->ClearNamespaces(true, false, true);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::ClearServerAdvertisedNamespacesForHost(const char *serverKey)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fNamespaceList->ClearNamespaces(false, true, true);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetDefaultNamespaceOfTypeForHost(const char *serverKey, EIMAPNamespaceType type, nsIMAPNamespace * &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fNamespaceList->GetDefaultNamespaceOfType(type);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetNamespacesOverridableForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fNamespacesOverridable;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetNamespacesOverridableForHost(const char *serverKey, bool overridable)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fNamespacesOverridable = overridable;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetNumberOfNamespacesForHost(const char *serverKey, uint32_t &result)
{
int32_t intResult = 0;
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
intResult = host->fNamespaceList->GetNumberOfNamespaces();
PR_ExitMonitor(gCachedHostInfoMonitor);
NS_ASSERTION(intResult >= 0, "negative number of namespaces");
result = (uint32_t) intResult;
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetNamespaceNumberForHost(const char *serverKey, int32_t n, nsIMAPNamespace * &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
result = host->fNamespaceList->GetNamespaceNumber(n);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
nsresult nsIMAPHostSessionList::SetNamespacesPrefForHost(nsIImapIncomingServer *aHost,
EIMAPNamespaceType type,
const char *pref)
{
if (type == kPersonalNamespace)
aHost->SetPersonalNamespace(nsDependentCString(pref));
else if (type == kPublicNamespace)
aHost->SetPublicNamespace(nsDependentCString(pref));
else if (type == kOtherUsersNamespace)
aHost->SetOtherUsersNamespace(nsDependentCString(pref));
else
NS_ASSERTION(false, "bogus namespace type");
return NS_OK;
}
// do we need this? What should we do about the master thing?
// Make sure this is running in the Mozilla thread when called
NS_IMETHODIMP nsIMAPHostSessionList::CommitNamespacesForHost(nsIImapIncomingServer *aHost)
{
NS_ENSURE_ARG_POINTER(aHost);
nsCString serverKey;
nsCOMPtr <nsIMsgIncomingServer> incomingServer = do_QueryInterface(aHost);
if (!incomingServer)
return NS_ERROR_NULL_POINTER;
nsresult rv = incomingServer->GetKey(serverKey);
NS_ENSURE_SUCCESS(rv, rv);
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey.get());
if (host)
{
host->fGotNamespaces = true; // so we only issue NAMESPACE once per host per session.
EIMAPNamespaceType type = kPersonalNamespace;
for (int i = 1; i <= 3; i++)
{
switch(i)
{
case 1:
type = kPersonalNamespace;
break;
case 2:
type = kPublicNamespace;
break;
case 3:
type = kOtherUsersNamespace;
break;
default:
type = kPersonalNamespace;
break;
}
int32_t numInNS = host->fNamespaceList->GetNumberOfNamespaces(type);
if (numInNS == 0)
SetNamespacesPrefForHost(aHost, type, "");
else if (numInNS >= 1)
{
char *pref = PR_smprintf("");
for (int count = 1; count <= numInNS; count++)
{
nsIMAPNamespace *ns = host->fNamespaceList->GetNamespaceNumber(count, type);
if (ns)
{
if (count > 1)
{
// append the comma
char *tempPref = PR_smprintf("%s,",pref);
PR_FREEIF(pref);
pref = tempPref;
}
char *tempPref = PR_smprintf("%s\"%s\"",pref,ns->GetPrefix());
PR_FREEIF(pref);
pref = tempPref;
}
}
if (pref)
{
SetNamespacesPrefForHost(aHost, type, pref);
PR_Free(pref);
}
}
}
// clear, but don't delete the entries in, the temp namespace list
host->fTempNamespaceList->ClearNamespaces(true, true, false);
// Now reset all of libmsg's namespace references.
// Did I mention this needs to be running in the mozilla thread?
aHost->ResetNamespaceReferences();
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::FlushUncommittedNamespacesForHost(const char *serverKey, bool &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fTempNamespaceList->ClearNamespaces(true, true, true);
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
// Returns NULL if there is no personal namespace on the given host
NS_IMETHODIMP nsIMAPHostSessionList::GetOnlineInboxPathForHost(const char *serverKey, nsString &result)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
nsIMAPNamespace *ns = NULL;
ns = host->fNamespaceList->GetDefaultNamespaceOfType(kPersonalNamespace);
if (ns)
{
CopyASCIItoUTF16(nsDependentCString(ns->GetPrefix()), result);
result.AppendLiteral("INBOX");
}
}
else
result.Truncate();
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::GetShouldAlwaysListInboxForHost(const char* /*serverKey*/, bool &result)
{
result = true;
/*
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
ret = host->fShouldAlwaysListInbox;
PR_ExitMonitor(gCachedHostInfoMonitor);
*/
return NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetShouldAlwaysListInboxForHost(const char *serverKey, bool shouldList)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
host->fShouldAlwaysListInbox = shouldList;
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP nsIMAPHostSessionList::SetNamespaceHierarchyDelimiterFromMailboxForHost(const char *serverKey, const char *boxName, char delimiter)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
nsIMAPNamespace *ns = host->fNamespaceList->GetNamespaceForMailbox(boxName);
if (ns && !ns->GetIsDelimiterFilledIn())
ns->SetDelimiter(delimiter, true);
}
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host) ? NS_OK : NS_ERROR_ILLEGAL_VALUE ;
}
NS_IMETHODIMP nsIMAPHostSessionList::AddShellToCacheForHost(const char *serverKey, nsIMAPBodyShell *shell)
{
nsresult rv = NS_OK;
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host)
{
if (host->fShellCache)
{
if (!host->fShellCache->AddShellToCache(shell))
rv = NS_ERROR_UNEXPECTED;
}
}
else
rv = NS_ERROR_ILLEGAL_VALUE;
PR_ExitMonitor(gCachedHostInfoMonitor);
return rv;
}
NS_IMETHODIMP nsIMAPHostSessionList::FindShellInCacheForHost(const char *serverKey, const char *mailboxName, const char *UID,
IMAP_ContentModifiedType modType, nsIMAPBodyShell **shell)
{
nsCString uidString(UID);
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host && host->fShellCache)
NS_IF_ADDREF(*shell = host->fShellCache->FindShellForUID(uidString,
mailboxName,
modType));
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}
NS_IMETHODIMP
nsIMAPHostSessionList::ClearShellCacheForHost(const char *serverKey)
{
PR_EnterMonitor(gCachedHostInfoMonitor);
nsIMAPHostInfo *host = FindHost(serverKey);
if (host && host->fShellCache)
host->fShellCache->Clear();
PR_ExitMonitor(gCachedHostInfoMonitor);
return (host == NULL) ? NS_ERROR_ILLEGAL_VALUE : NS_OK;
}

View file

@ -0,0 +1,135 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsIMAPHostSessionList_H_
#define _nsIMAPHostSessionList_H_
#include "mozilla/Attributes.h"
#include "nsImapCore.h"
#include "nsIIMAPHostSessionList.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nspr.h"
class nsIMAPNamespaceList;
class nsIImapIncomingServer;
class nsIMAPHostInfo
{
public:
friend class nsIMAPHostSessionList;
nsIMAPHostInfo(const char *serverKey, nsIImapIncomingServer *server);
~nsIMAPHostInfo();
protected:
nsCString fServerKey;
char *fCachedPassword;
nsCString fOnlineDir;
nsIMAPHostInfo *fNextHost;
eIMAPCapabilityFlags fCapabilityFlags;
char *fHierarchyDelimiters;// string of top-level hierarchy delimiters
bool fHaveWeEverDiscoveredFolders;
char *fCanonicalOnlineSubDir;
nsIMAPNamespaceList *fNamespaceList, *fTempNamespaceList;
bool fNamespacesOverridable;
bool fUsingSubscription;
bool fOnlineTrashFolderExists;
bool fShouldAlwaysListInbox;
bool fHaveAdminURL;
bool fPasswordVerifiedOnline;
bool fDeleteIsMoveToTrash;
bool fShowDeletedMessages;
bool fGotNamespaces;
nsIMAPBodyShellCache *fShellCache;
};
// this is an interface to a linked list of host info's
class nsIMAPHostSessionList : public nsIImapHostSessionList, public nsIObserver, public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
nsIMAPHostSessionList();
nsresult Init();
// Host List
NS_IMETHOD AddHostToList(const char *serverKey,
nsIImapIncomingServer *server) override;
NS_IMETHOD ResetAll() override;
// Capabilities
NS_IMETHOD GetHostHasAdminURL(const char *serverKey, bool &result) override;
NS_IMETHOD SetHostHasAdminURL(const char *serverKey, bool hasAdminUrl) override;
// Subscription
NS_IMETHOD GetHostIsUsingSubscription(const char *serverKey, bool &result) override;
NS_IMETHOD SetHostIsUsingSubscription(const char *serverKey, bool usingSubscription) override;
// Passwords
NS_IMETHOD GetPasswordForHost(const char *serverKey, nsString &result) override;
NS_IMETHOD SetPasswordForHost(const char *serverKey, const char *password) override;
NS_IMETHOD GetPasswordVerifiedOnline(const char *serverKey, bool &result) override;
NS_IMETHOD SetPasswordVerifiedOnline(const char *serverKey) override;
// OnlineDir
NS_IMETHOD GetOnlineDirForHost(const char *serverKey,
nsString &result) override;
NS_IMETHOD SetOnlineDirForHost(const char *serverKey,
const char *onlineDir) override;
// Delete is move to trash folder
NS_IMETHOD GetDeleteIsMoveToTrashForHost(const char *serverKey, bool &result) override;
NS_IMETHOD SetDeleteIsMoveToTrashForHost(const char *serverKey, bool isMoveToTrash) override;
// imap delete model (or not)
NS_IMETHOD GetShowDeletedMessagesForHost(const char *serverKey, bool &result) override;
NS_IMETHOD SetShowDeletedMessagesForHost(const char *serverKey, bool showDeletedMessages) override;
// Get namespaces
NS_IMETHOD GetGotNamespacesForHost(const char *serverKey, bool &result) override;
NS_IMETHOD SetGotNamespacesForHost(const char *serverKey, bool gotNamespaces) override;
// Folders
NS_IMETHOD SetHaveWeEverDiscoveredFoldersForHost(const char *serverKey, bool discovered) override;
NS_IMETHOD GetHaveWeEverDiscoveredFoldersForHost(const char *serverKey, bool &result) override;
// Trash Folder
NS_IMETHOD SetOnlineTrashFolderExistsForHost(const char *serverKey, bool exists) override;
NS_IMETHOD GetOnlineTrashFolderExistsForHost(const char *serverKey, bool &result) override;
// INBOX
NS_IMETHOD GetOnlineInboxPathForHost(const char *serverKey, nsString &result) override;
NS_IMETHOD GetShouldAlwaysListInboxForHost(const char *serverKey, bool &result) override;
NS_IMETHOD SetShouldAlwaysListInboxForHost(const char *serverKey, bool shouldList) override;
// Namespaces
NS_IMETHOD GetNamespaceForMailboxForHost(const char *serverKey, const char *mailbox_name, nsIMAPNamespace *&result) override;
NS_IMETHOD SetNamespaceFromPrefForHost(const char *serverKey, const char *namespacePref, EIMAPNamespaceType type) override;
NS_IMETHOD AddNewNamespaceForHost(const char *serverKey, nsIMAPNamespace *ns) override;
NS_IMETHOD ClearServerAdvertisedNamespacesForHost(const char *serverKey) override;
NS_IMETHOD ClearPrefsNamespacesForHost(const char *serverKey) override;
NS_IMETHOD GetDefaultNamespaceOfTypeForHost(const char *serverKey, EIMAPNamespaceType type, nsIMAPNamespace *&result) override;
NS_IMETHOD SetNamespacesOverridableForHost(const char *serverKey, bool overridable) override;
NS_IMETHOD GetNamespacesOverridableForHost(const char *serverKey,bool &result) override;
NS_IMETHOD GetNumberOfNamespacesForHost(const char *serverKey, uint32_t &result) override;
NS_IMETHOD GetNamespaceNumberForHost(const char *serverKey, int32_t n, nsIMAPNamespace * &result) override;
// ### dmb hoo boy, how are we going to do this?
NS_IMETHOD CommitNamespacesForHost(nsIImapIncomingServer *host) override;
NS_IMETHOD FlushUncommittedNamespacesForHost(const char *serverKey, bool &result) override;
// Hierarchy Delimiters
NS_IMETHOD SetNamespaceHierarchyDelimiterFromMailboxForHost(const char *serverKey, const char *boxName, char delimiter) override;
// Message Body Shells
NS_IMETHOD AddShellToCacheForHost(const char *serverKey, nsIMAPBodyShell *shell) override;
NS_IMETHOD FindShellInCacheForHost(const char *serverKey, const char *mailboxName, const char *UID, IMAP_ContentModifiedType modType, nsIMAPBodyShell **result) override;
NS_IMETHOD ClearShellCacheForHost(const char *serverKey) override;
PRMonitor *gCachedHostInfoMonitor;
nsIMAPHostInfo *fHostInfoList;
protected:
virtual ~nsIMAPHostSessionList();
nsresult SetNamespacesPrefForHost(nsIImapIncomingServer *aHost,
EIMAPNamespaceType type,
const char *pref);
nsIMAPHostInfo *FindHost(const char *serverKey);
};
#endif

View file

@ -0,0 +1,650 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h" // for pre-compiled headers
#include "nsImapCore.h"
#include "nsIMAPNamespace.h"
#include "nsImapProtocol.h"
#include "nsMsgImapCID.h"
#include "nsImapUrl.h"
#include "nsStringGlue.h"
#include "nsServiceManagerUtils.h"
//////////////////// nsIMAPNamespace /////////////////////////////////////////////////////////////
static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);
nsIMAPNamespace::nsIMAPNamespace(EIMAPNamespaceType type, const char *prefix, char delimiter, bool from_prefs)
{
m_namespaceType = type;
m_prefix = PL_strdup(prefix);
m_fromPrefs = from_prefs;
m_delimiter = delimiter;
m_delimiterFilledIn = !m_fromPrefs; // if it's from the prefs, we can't be sure about the delimiter until we list it.
}
nsIMAPNamespace::~nsIMAPNamespace()
{
PR_FREEIF(m_prefix);
}
void nsIMAPNamespace::SetDelimiter(char delimiter, bool delimiterFilledIn)
{
m_delimiter = delimiter;
m_delimiterFilledIn = delimiterFilledIn;
}
// returns -1 if this box is not part of this namespace,
// or the length of the prefix if it is part of this namespace
int nsIMAPNamespace::MailboxMatchesNamespace(const char *boxname)
{
if (!boxname) return -1;
// If the namespace is part of the boxname
if (!m_prefix || !*m_prefix)
return 0;
if (PL_strstr(boxname, m_prefix) == boxname)
return PL_strlen(m_prefix);
// If the boxname is part of the prefix
// (Used for matching Personal mailbox with Personal/ namespace, etc.)
if (PL_strstr(m_prefix, boxname) == m_prefix)
return PL_strlen(boxname);
return -1;
}
nsIMAPNamespaceList *nsIMAPNamespaceList::CreatensIMAPNamespaceList()
{
nsIMAPNamespaceList *rv = new nsIMAPNamespaceList();
return rv;
}
nsIMAPNamespaceList::nsIMAPNamespaceList()
{
}
int nsIMAPNamespaceList::GetNumberOfNamespaces()
{
return m_NamespaceList.Length();
}
nsresult nsIMAPNamespaceList::InitFromString(const char *nameSpaceString, EIMAPNamespaceType nstype)
{
nsresult rv = NS_OK;
if (nameSpaceString)
{
int numNamespaces = UnserializeNamespaces(nameSpaceString, nullptr, 0);
char **prefixes = (char**) PR_CALLOC(numNamespaces * sizeof(char*));
if (prefixes)
{
int len = UnserializeNamespaces(nameSpaceString, prefixes, numNamespaces);
for (int i = 0; i < len; i++)
{
char *thisns = prefixes[i];
char delimiter = '/'; // a guess
if (PL_strlen(thisns) >= 1)
delimiter = thisns[PL_strlen(thisns)-1];
nsIMAPNamespace *ns = new nsIMAPNamespace(nstype, thisns, delimiter, true);
if (ns)
AddNewNamespace(ns);
PR_FREEIF(thisns);
}
PR_Free(prefixes);
}
}
return rv;
}
nsresult nsIMAPNamespaceList::OutputToString(nsCString &string)
{
nsresult rv = NS_OK;
return rv;
}
int nsIMAPNamespaceList::GetNumberOfNamespaces(EIMAPNamespaceType type)
{
int nodeIndex = 0, count = 0;
for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--)
{
nsIMAPNamespace *nspace = m_NamespaceList.ElementAt(nodeIndex);
if (nspace->GetType() == type)
{
count++;
}
}
return count;
}
int nsIMAPNamespaceList::AddNewNamespace(nsIMAPNamespace *ns)
{
// If the namespace is from the NAMESPACE response, then we should see if there
// are any namespaces previously set by the preferences, or the default namespace. If so, remove these.
if (!ns->GetIsNamespaceFromPrefs())
{
int nodeIndex;
// iterate backwards because we delete elements
for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--)
{
nsIMAPNamespace *nspace = m_NamespaceList.ElementAt(nodeIndex);
// if we find existing namespace(s) that matches the
// new one, we'll just remove the old ones and let the
// new one get added when we've finished checking for
// matching namespaces or namespaces that came from prefs.
if (nspace &&
(nspace->GetIsNamespaceFromPrefs() ||
(!PL_strcmp(ns->GetPrefix(), nspace->GetPrefix()) &&
ns->GetType() == nspace->GetType() &&
ns->GetDelimiter() == nspace->GetDelimiter())))
{
m_NamespaceList.RemoveElementAt(nodeIndex);
delete nspace;
}
}
}
// Add the new namespace to the list. This must come after the removing code,
// or else we could never add the initial kDefaultNamespace type to the list.
m_NamespaceList.AppendElement(ns);
return 0;
}
// chrisf - later, fix this to know the real concept of "default" namespace of a given type
nsIMAPNamespace *nsIMAPNamespaceList::GetDefaultNamespaceOfType(EIMAPNamespaceType type)
{
nsIMAPNamespace *rv = 0, *firstOfType = 0;
int nodeIndex, count = m_NamespaceList.Length();
for (nodeIndex= 0; nodeIndex < count && !rv; nodeIndex++)
{
nsIMAPNamespace *ns = m_NamespaceList.ElementAt(nodeIndex);
if (ns->GetType() == type)
{
if (!firstOfType)
firstOfType = ns;
if (!(*(ns->GetPrefix())))
{
// This namespace's prefix is ""
// Therefore it is the default
rv = ns;
}
}
}
if (!rv)
rv = firstOfType;
return rv;
}
nsIMAPNamespaceList::~nsIMAPNamespaceList()
{
ClearNamespaces(true, true, true);
}
// ClearNamespaces removes and deletes the namespaces specified, and if there are no namespaces left,
void nsIMAPNamespaceList::ClearNamespaces(bool deleteFromPrefsNamespaces, bool deleteServerAdvertisedNamespaces, bool reallyDelete)
{
int nodeIndex;
// iterate backwards because we delete elements
for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--)
{
nsIMAPNamespace *ns = m_NamespaceList.ElementAt(nodeIndex);
if (ns->GetIsNamespaceFromPrefs())
{
if (deleteFromPrefsNamespaces)
{
m_NamespaceList.RemoveElementAt(nodeIndex);
if (reallyDelete)
delete ns;
}
}
else if (deleteServerAdvertisedNamespaces)
{
m_NamespaceList.RemoveElementAt(nodeIndex);
if (reallyDelete)
delete ns;
}
}
}
nsIMAPNamespace *nsIMAPNamespaceList::GetNamespaceNumber(int nodeIndex)
{
NS_ASSERTION(nodeIndex >= 0 && nodeIndex < GetNumberOfNamespaces(), "invalid IMAP namespace node index");
if (nodeIndex < 0) nodeIndex = 0;
// XXX really could be just ElementAt; that's why we have the assertion
return m_NamespaceList.SafeElementAt(nodeIndex);
}
nsIMAPNamespace *nsIMAPNamespaceList::GetNamespaceNumber(int nodeIndex, EIMAPNamespaceType type)
{
int nodeCount, count = 0;
for (nodeCount = m_NamespaceList.Length() - 1; nodeCount >= 0; nodeCount--)
{
nsIMAPNamespace *nspace = m_NamespaceList.ElementAt(nodeCount);
if (nspace->GetType() == type)
{
count++;
if (count == nodeIndex)
return nspace;
}
}
return nullptr;
}
nsIMAPNamespace *nsIMAPNamespaceList::GetNamespaceForMailbox(const char *boxname)
{
// We want to find the LONGEST substring that matches the beginning of this mailbox's path.
// This accounts for nested namespaces (i.e. "Public/" and "Public/Users/")
// Also, we want to match the namespace's mailbox to that namespace also:
// The Personal box will match the Personal/ namespace, etc.
// these lists shouldn't be too long (99% chance there won't be more than 3 or 4)
// so just do a linear search
int lengthMatched = -1;
int currentMatchedLength = -1;
nsIMAPNamespace *rv = nullptr;
int nodeIndex = 0;
if (!PL_strcasecmp(boxname, "INBOX"))
return GetDefaultNamespaceOfType(kPersonalNamespace);
for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--)
{
nsIMAPNamespace *nspace = m_NamespaceList.ElementAt(nodeIndex);
currentMatchedLength = nspace->MailboxMatchesNamespace(boxname);
if (currentMatchedLength > lengthMatched)
{
rv = nspace;
lengthMatched = currentMatchedLength;
}
}
return rv;
}
#define SERIALIZER_SEPARATORS ","
/**
* If len is one, copies the first element of prefixes into serializedNamespaces.
* If len > 1, copies len strings from prefixes into serializedNamespaces
* as a comma-separated list of quoted strings.
*/
nsresult nsIMAPNamespaceList::SerializeNamespaces(char **prefixes, int len,
nsCString &serializedNamespaces)
{
if (len <= 0)
return NS_OK;
if (len == 1)
{
serializedNamespaces.Assign(prefixes[0]);
return NS_OK;
}
for (int i = 0; i < len; i++)
{
if (i > 0)
serializedNamespaces.AppendLiteral(",");
serializedNamespaces.AppendLiteral("\"");
serializedNamespaces.Append(prefixes[i]);
serializedNamespaces.AppendLiteral("\"");
}
return NS_OK;
}
/* str is the string which needs to be unserialized.
If prefixes is NULL, simply returns the number of namespaces in str. (len is ignored)
If prefixes is not NULL, it should be an array of length len which is to be filled in
with newly-allocated string. Returns the number of strings filled in.
*/
int nsIMAPNamespaceList::UnserializeNamespaces(const char *str, char **prefixes, int len)
{
if (!str)
return 0;
if (!prefixes)
{
if (str[0] != '"')
return 1;
else
{
int count = 0;
char *ourstr = PL_strdup(str);
char *origOurStr = ourstr;
if (ourstr)
{
char *token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr );
while (token != nullptr)
{
token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr );
count++;
}
PR_Free(origOurStr);
}
return count;
}
}
else
{
if ((str[0] != '"') && (len >= 1))
{
prefixes[0] = PL_strdup(str);
return 1;
}
else
{
int count = 0;
char *ourstr = PL_strdup(str);
char *origOurStr = ourstr;
if (ourstr)
{
char *token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr );
while ((count < len) && (token != nullptr))
{
char *current = PL_strdup(token), *where = current;
if (where[0] == '"')
where++;
if (where[PL_strlen(where)-1] == '"')
where[PL_strlen(where)-1] = 0;
prefixes[count] = PL_strdup(where);
PR_FREEIF(current);
token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr );
count++;
}
PR_Free(origOurStr);
}
return count;
}
}
}
char *nsIMAPNamespaceList::AllocateCanonicalFolderName(const char *onlineFolderName, char delimiter)
{
char *canonicalPath = nullptr;
if (delimiter)
canonicalPath = nsImapUrl::ReplaceCharsInCopiedString(onlineFolderName, delimiter , '/');
else
canonicalPath = PL_strdup(onlineFolderName);
// eat any escape characters for escaped dir separators
if (canonicalPath)
{
char *currentEscapeSequence = strstr(canonicalPath, "\\/");
while (currentEscapeSequence)
{
strcpy(currentEscapeSequence, currentEscapeSequence+1);
currentEscapeSequence = strstr(currentEscapeSequence+1, "\\/");
}
}
return canonicalPath;
}
/*
GetFolderNameWithoutNamespace takes as input a folder name
in canonical form, and the namespace for the given folder. It returns an allocated
string of the folder's path with the namespace string stripped out. For instance,
when passed the folder Folders/a/b where the namespace is "Folders/", it will return
"a/b". Similarly, if the folder name is "#news/comp/mail/imap" in canonical form,
with a real delimiter of "." and a namespace of "#news.", it will return "comp/mail/imap".
The return value is always in canonical form.
*/
char* nsIMAPNamespaceList::GetFolderNameWithoutNamespace(nsIMAPNamespace *namespaceForFolder, const char *canonicalFolderName)
{
NS_ASSERTION(canonicalFolderName, "null folder name");
#ifdef DEBUG
NS_ASSERTION(namespaceForFolder || !PL_strcasecmp(canonicalFolderName, "INBOX"), "need namespace or INBOX");
#endif
char *retFolderName = nullptr;
if (!PL_strcasecmp(canonicalFolderName, "INBOX"))
return PL_strdup(canonicalFolderName);
// convert the canonical path to the online path
char *convertedFolderName = nsIMAPNamespaceList::AllocateServerFolderName(canonicalFolderName, namespaceForFolder->GetDelimiter());
if (convertedFolderName)
{
char *beginFolderPath = nullptr;
if (strlen(convertedFolderName) <= strlen(namespaceForFolder->GetPrefix()))
beginFolderPath = convertedFolderName;
else
beginFolderPath = convertedFolderName + strlen(namespaceForFolder->GetPrefix());
NS_ASSERTION(beginFolderPath, "empty folder path");
retFolderName = nsIMAPNamespaceList::AllocateCanonicalFolderName(beginFolderPath, namespaceForFolder->GetDelimiter());
PR_Free(convertedFolderName);
}
NS_ASSERTION(retFolderName, "returning null folder name");
return retFolderName;
}
nsIMAPNamespace* nsIMAPNamespaceList::GetNamespaceForFolder(const char *hostName,
const char *canonicalFolderName,
char delimiter)
{
if (!hostName || !canonicalFolderName)
return nullptr;
nsIMAPNamespace *resultNamespace = nullptr;
nsresult rv;
char *convertedFolderName = nsIMAPNamespaceList::AllocateServerFolderName(canonicalFolderName, delimiter);
if (convertedFolderName)
{
nsCOMPtr<nsIImapHostSessionList> hostSessionList =
do_GetService(kCImapHostSessionListCID, &rv);
if (NS_FAILED(rv))
return nullptr;
hostSessionList->GetNamespaceForMailboxForHost(hostName, convertedFolderName, resultNamespace);
PR_Free(convertedFolderName);
}
else
{
NS_ASSERTION(false, "couldn't get converted folder name");
}
return resultNamespace;
}
/* static */
char *nsIMAPNamespaceList::AllocateServerFolderName(const char *canonicalFolderName, char delimiter)
{
if (delimiter)
return nsImapUrl::ReplaceCharsInCopiedString(canonicalFolderName, '/', delimiter);
else
return NS_strdup(canonicalFolderName);
}
/*
GetFolderOwnerNameFromPath takes as inputs a folder name
in canonical form, and a namespace for that folder.
The namespace MUST be of type kOtherUsersNamespace, hence the folder MUST be
owned by another user. This function extracts the folder owner's name from the
canonical name of the folder, and returns an allocated copy of that owner's name
*/
/* static */
char *nsIMAPNamespaceList::GetFolderOwnerNameFromPath(nsIMAPNamespace *namespaceForFolder, const char *canonicalFolderName)
{
if (!namespaceForFolder || !canonicalFolderName)
{
NS_ASSERTION(false,"null namespace or canonical folder name");
return nullptr;
}
char *rv = nullptr;
// convert the canonical path to the online path
char *convertedFolderName = AllocateServerFolderName(canonicalFolderName, namespaceForFolder->GetDelimiter());
if (convertedFolderName)
{
#ifdef DEBUG
NS_ASSERTION(strlen(convertedFolderName) > strlen(namespaceForFolder->GetPrefix()), "server folder name invalid");
#endif
if (strlen(convertedFolderName) > strlen(namespaceForFolder->GetPrefix()))
{
char *owner = convertedFolderName + strlen(namespaceForFolder->GetPrefix());
NS_ASSERTION(owner, "couldn't find folder owner");
char *nextDelimiter = strchr(owner, namespaceForFolder->GetDelimiter());
// if !nextDelimiter, then the path is of the form Shared/Users/chrisf (no subfolder)
if (nextDelimiter)
{
*nextDelimiter = 0;
}
rv = PL_strdup(owner);
}
PR_Free(convertedFolderName);
}
else
{
NS_ASSERTION(false, "couldn't allocate server folder name");
}
return rv;
}
/*
GetFolderIsNamespace returns TRUE if the given folder is the folder representing
a namespace.
*/
bool nsIMAPNamespaceList::GetFolderIsNamespace(const char *hostName,
const char *canonicalFolderName,
char delimiter,nsIMAPNamespace *namespaceForFolder)
{
NS_ASSERTION(namespaceForFolder, "null namespace");
bool rv = false;
const char *prefix = namespaceForFolder->GetPrefix();
NS_ASSERTION(prefix, "namespace has no prefix");
if (!prefix || !*prefix) // empty namespace prefix
return false;
char *convertedFolderName = AllocateServerFolderName(canonicalFolderName, delimiter);
if (convertedFolderName)
{
bool lastCharIsDelimiter = (prefix[strlen(prefix) - 1] == delimiter);
if (lastCharIsDelimiter)
{
rv = ((strncmp(convertedFolderName, prefix, strlen(convertedFolderName)) == 0) &&
(strlen(convertedFolderName) == strlen(prefix) - 1));
}
else
{
rv = (strcmp(convertedFolderName, prefix) == 0);
}
PR_Free(convertedFolderName);
}
else
{
NS_ASSERTION(false, "couldn't allocate server folder name");
}
return rv;
}
/*
SuggestHierarchySeparatorForNamespace takes a namespace from libmsg
and a hierarchy delimiter. If the namespace has not been filled in from
online NAMESPACE command yet, it fills in the suggested delimiter to be
used from then on (until it is overridden by an online response).
*/
void nsIMAPNamespaceList::SuggestHierarchySeparatorForNamespace(nsIMAPNamespace *namespaceForFolder, char delimiterFromFolder)
{
NS_ASSERTION(namespaceForFolder, "need namespace");
if (namespaceForFolder && !namespaceForFolder->GetIsDelimiterFilledIn())
namespaceForFolder->SetDelimiter(delimiterFromFolder, false);
}
/*
GenerateFullFolderNameWithDefaultNamespace takes a folder name in canonical form,
converts it to online form, allocates a string to contain the full online server name
including the namespace prefix of the default namespace of the given type, in the form:
PR_smprintf("%s%s", prefix, onlineServerName) if there is a NULL owner
PR_smprintf("%s%s%c%s", prefix, owner, delimiter, onlineServerName) if there is an owner
It then converts this back to canonical form and returns it (allocated) to libmsg.
It returns NULL if there is no namespace of the given type.
If nsUsed is not passed in as NULL, then *nsUsed is filled in and returned; it is the
namespace used for generating the folder name.
*/
char *nsIMAPNamespaceList::GenerateFullFolderNameWithDefaultNamespace(const char *hostName,
const char *canonicalFolderName,
const char *owner,
EIMAPNamespaceType nsType,
nsIMAPNamespace **nsUsed)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIImapHostSessionList> hostSession =
do_GetService(kCImapHostSessionListCID, &rv);
NS_ENSURE_SUCCESS(rv, nullptr);
nsIMAPNamespace *ns;
char *fullFolderName = nullptr;
rv = hostSession->GetDefaultNamespaceOfTypeForHost(hostName, nsType, ns);
NS_ENSURE_SUCCESS(rv, nullptr);
if (ns)
{
if (nsUsed)
*nsUsed = ns;
const char *prefix = ns->GetPrefix();
char *convertedFolderName = AllocateServerFolderName(canonicalFolderName, ns->GetDelimiter());
if (convertedFolderName)
{
char *convertedReturnName = nullptr;
if (owner)
{
convertedReturnName = PR_smprintf("%s%s%c%s", prefix, owner, ns->GetDelimiter(), convertedFolderName);
}
else
{
convertedReturnName = PR_smprintf("%s%s", prefix, convertedFolderName);
}
if (convertedReturnName)
{
fullFolderName = AllocateCanonicalFolderName(convertedReturnName, ns->GetDelimiter());
PR_Free(convertedReturnName);
}
PR_Free(convertedFolderName);
}
else
{
NS_ASSERTION(false, "couldn't allocate server folder name");
}
}
else
{
// Could not find other users namespace on the given host
NS_WARNING("couldn't find namespace for given host");
}
return (fullFolderName);
}

View file

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsIMAPNamespace_H_
#define _nsIMAPNamespace_H_
#include "nsTArray.h"
class nsIMAPNamespace
{
public:
nsIMAPNamespace(EIMAPNamespaceType type, const char *prefix, char delimiter, bool from_prefs);
~nsIMAPNamespace();
EIMAPNamespaceType GetType() { return m_namespaceType; }
const char * GetPrefix() { return m_prefix; }
char GetDelimiter() { return m_delimiter; }
void SetDelimiter(char delimiter, bool delimiterFilledIn);
bool GetIsDelimiterFilledIn() { return m_delimiterFilledIn; }
bool GetIsNamespaceFromPrefs() { return m_fromPrefs; }
// returns -1 if this box is not part of this namespace,
// or the length of the prefix if it is part of this namespace
int MailboxMatchesNamespace(const char *boxname);
protected:
EIMAPNamespaceType m_namespaceType;
char *m_prefix;
char m_delimiter;
bool m_fromPrefs;
bool m_delimiterFilledIn;
};
// represents an array of namespaces for a given host
class nsIMAPNamespaceList
{
public:
~nsIMAPNamespaceList();
static nsIMAPNamespaceList *CreatensIMAPNamespaceList();
nsresult InitFromString(const char *nameSpaceString, EIMAPNamespaceType nstype);
nsresult OutputToString(nsCString &OutputString);
int UnserializeNamespaces(const char *str, char **prefixes, int len);
nsresult SerializeNamespaces(char **prefixes, int len, nsCString &serializedNamespace);
void ClearNamespaces(bool deleteFromPrefsNamespaces, bool deleteServerAdvertisedNamespaces, bool reallyDelete);
int GetNumberOfNamespaces();
int GetNumberOfNamespaces(EIMAPNamespaceType);
nsIMAPNamespace *GetNamespaceNumber(int nodeIndex);
nsIMAPNamespace *GetNamespaceNumber(int nodeIndex, EIMAPNamespaceType);
nsIMAPNamespace *GetDefaultNamespaceOfType(EIMAPNamespaceType type);
int AddNewNamespace(nsIMAPNamespace *ns);
nsIMAPNamespace *GetNamespaceForMailbox(const char *boxname);
static nsIMAPNamespace* GetNamespaceForFolder(const char *hostName,
const char *canonicalFolderName,
char delimiter);
static bool GetFolderIsNamespace(const char *hostName,
const char *canonicalFolderName,
char delimiter,nsIMAPNamespace *namespaceForFolder);
static char* GetFolderNameWithoutNamespace(nsIMAPNamespace *namespaceForFolder, const char *canonicalFolderName);
static char *AllocateServerFolderName(const char *canonicalFolderName, char delimiter);
static char *GetFolderOwnerNameFromPath(nsIMAPNamespace *namespaceForFolder, const char *canonicalFolderName);
static char *AllocateCanonicalFolderName(const char *onlineFolderName, char delimiter);
static void SuggestHierarchySeparatorForNamespace(nsIMAPNamespace *namespaceForFolder, char delimiterFromFolder);
static char *GenerateFullFolderNameWithDefaultNamespace(const char *hostName,
const char *canonicalFolderName,
const char *owner,
EIMAPNamespaceType nsType,
nsIMAPNamespace **nsUsed);
protected:
nsIMAPNamespaceList(); // use CreatensIMAPNamespaceList to create one
nsTArray<nsIMAPNamespace*> m_NamespaceList;
};
#endif

View file

@ -0,0 +1,188 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsImapCore_H_
#define _nsImapCore_H_
#include "MailNewsTypes.h"
#include "nsStringGlue.h"
#include "nsIMailboxSpec.h"
#include "nsIImapFlagAndUidState.h"
class nsIMAPNamespace;
class nsImapProtocol;
class nsImapFlagAndUidState;
/* imap message flags */
typedef uint16_t imapMessageFlagsType;
/* used for communication between imap thread and event sinks */
#define kNoFlags 0x00 /* RFC flags */
#define kMarked 0x01
#define kUnmarked 0x02
#define kNoinferiors 0x04
#define kNoselect 0x08
#define kImapTrash 0x10 /* Navigator flag */
#define kJustExpunged 0x20 /* This update is a post expunge url update. */
#define kPersonalMailbox 0x40 /* this mailbox is in the personal namespace */
#define kPublicMailbox 0x80 /* this mailbox is in the public namespace */
#define kOtherUsersMailbox 0x100 /* this mailbox is in the other users' namespace */
#define kNameSpace 0x200 /* this mailbox IS a namespace */
#define kNewlyCreatedFolder 0x400 /* this folder was just created */
#define kImapDrafts 0x800 /* XLIST says this is the drafts folder */
#define kImapSpam 0x1000 /* XLIST says this is the spam folder */
#define kImapSent 0x2000 /* XLIST says this is the sent folder */
#define kImapInbox 0x4000 /* XLIST says this is the INBOX folder */
#define kImapAllMail 0x8000 /* XLIST says this is AllMail (GMail) */
#define kImapXListTrash 0x10000 /* XLIST says this is the trash */
#define kNonExistent 0x20000 /* RFC 5258, LIST-EXTENDED */
#define kSubscribed 0x40000 /* RFC 5258, LIST-EXTENDED */
#define kRemote 0x80000 /* RFC 5258, LIST-EXTENDED */
#define kHasChildren 0x100000 /* RFC 5258, LIST-EXTENDED */
#define kHasNoChildren 0x200000 /* RFC 5258, LIST-EXTENDED */
#define kImapArchive 0x400000 /* RFC 5258, LIST-EXTENDED */
/* flags for individual messages */
/* currently the ui only offers \Seen and \Flagged */
#define kNoImapMsgFlag 0x0000
#define kImapMsgSeenFlag 0x0001
#define kImapMsgAnsweredFlag 0x0002
#define kImapMsgFlaggedFlag 0x0004
#define kImapMsgDeletedFlag 0x0008
#define kImapMsgDraftFlag 0x0010
#define kImapMsgRecentFlag 0x0020
#define kImapMsgForwardedFlag 0x0040 /* Not always supported, check mailbox folder */
#define kImapMsgMDNSentFlag 0x0080 /* Not always supported. check mailbox folder */
#define kImapMsgCustomKeywordFlag 0x0100 /* this msg has a custom keyword */
#define kImapMsgLabelFlags 0x0E00 /* supports 5 labels only supported if the folder supports keywords */
#define kImapMsgSupportMDNSentFlag 0x2000
#define kImapMsgSupportForwardedFlag 0x4000
/**
* We use a separate xlist trash flag so we can prefer the GMail trash
* over an existing Trash folder we may have created.
*/
#define kImapMsgSupportUserFlag 0x8000
/* This seems to be the most cost effective way of
* piggying back the server support user flag info.
*/
/* if a url creator does not know the hierarchyDelimiter, use this */
#define kOnlineHierarchySeparatorUnknown '^'
#define kOnlineHierarchySeparatorNil '|'
#define IMAP_URL_TOKEN_SEPARATOR ">"
#define kUidUnknown -1
// Special initial value meaning ACLs need to be loaded from DB.
#define kAclInvalid ((uint32_t) -1)
// this has to do with Mime Parts on Demand. It used to live in net.h
// I'm not sure where this will live, but here is OK temporarily
typedef enum {
IMAP_CONTENT_NOT_MODIFIED = 0,
IMAP_CONTENT_MODIFIED_VIEW_INLINE,
IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS,
IMAP_CONTENT_FORCE_CONTENT_NOT_MODIFIED
} IMAP_ContentModifiedType;
// I think this should really go in an imap.h equivalent file
typedef enum {
kPersonalNamespace = 0,
kOtherUsersNamespace,
kPublicNamespace,
kDefaultNamespace,
kUnknownNamespace
} EIMAPNamespaceType;
/**
* IMAP server feature, mostly CAPABILITY responses
*
* one of the cap flags below
*/
typedef uint64_t eIMAPCapabilityFlag;
/**
* IMAP server features, mostly CAPABILITY responses
*
* any set of the cap flags below, i.e.
* i.e. 0, 1 or more |eIMAPCapabilityFlag|.
*/
typedef uint64_t eIMAPCapabilityFlags;
const eIMAPCapabilityFlag kCapabilityUndefined = 0x00000000;
const eIMAPCapabilityFlag kCapabilityDefined = 0x00000001;
const eIMAPCapabilityFlag kHasAuthLoginCapability = 0x00000002; /* AUTH LOGIN (not the same as kHasAuthOldLoginCapability) */
const eIMAPCapabilityFlag kHasAuthOldLoginCapability = 0x00000004; /* original IMAP login method */
const eIMAPCapabilityFlag kHasXSenderCapability = 0x00000008;
const eIMAPCapabilityFlag kIMAP4Capability = 0x00000010; /* RFC1734 */
const eIMAPCapabilityFlag kIMAP4rev1Capability = 0x00000020; /* RFC2060 */
const eIMAPCapabilityFlag kIMAP4other = 0x00000040; /* future rev?? */
const eIMAPCapabilityFlag kNoHierarchyRename = 0x00000080; /* no hierarchy rename */
const eIMAPCapabilityFlag kACLCapability = 0x00000100; /* ACL extension */
const eIMAPCapabilityFlag kNamespaceCapability = 0x00000200; /* IMAP4 Namespace Extension */
const eIMAPCapabilityFlag kHasIDCapability = 0x00000400; /* client user agent id extension */
const eIMAPCapabilityFlag kXServerInfoCapability = 0x00000800; /* XSERVERINFO extension for admin urls */
const eIMAPCapabilityFlag kHasAuthPlainCapability = 0x00001000; /* new form of auth plain base64 login */
const eIMAPCapabilityFlag kUidplusCapability = 0x00002000; /* RFC 2359 UIDPLUS extension */
const eIMAPCapabilityFlag kLiteralPlusCapability = 0x00004000; /* RFC 2088 LITERAL+ extension */
const eIMAPCapabilityFlag kAOLImapCapability = 0x00008000; /* aol imap extensions */
const eIMAPCapabilityFlag kHasLanguageCapability = 0x00010000; /* language extensions */
const eIMAPCapabilityFlag kHasCRAMCapability = 0x00020000; /* CRAM auth extension */
const eIMAPCapabilityFlag kQuotaCapability = 0x00040000; /* RFC 2087 quota extension */
const eIMAPCapabilityFlag kHasIdleCapability = 0x00080000; /* RFC 2177 idle extension */
const eIMAPCapabilityFlag kHasAuthNTLMCapability = 0x00100000; /* AUTH NTLM extension */
const eIMAPCapabilityFlag kHasAuthMSNCapability = 0x00200000; /* AUTH MSN extension */
const eIMAPCapabilityFlag kHasStartTLSCapability =0x00400000; /* STARTTLS support */
const eIMAPCapabilityFlag kHasAuthNoneCapability = 0x00800000; /* needs no login */
const eIMAPCapabilityFlag kHasAuthGssApiCapability = 0x01000000; /* GSSAPI AUTH */
const eIMAPCapabilityFlag kHasCondStoreCapability = 0x02000000; /* RFC 3551 CondStore extension */
const eIMAPCapabilityFlag kHasEnableCapability = 0x04000000; /* RFC 5161 ENABLE extension */
const eIMAPCapabilityFlag kHasXListCapability = 0x08000000; /* XLIST extension */
const eIMAPCapabilityFlag kHasCompressDeflateCapability = 0x10000000; /* RFC 4978 COMPRESS extension */
const eIMAPCapabilityFlag kHasAuthExternalCapability = 0x20000000; /* RFC 2222 SASL AUTH EXTERNAL */
const eIMAPCapabilityFlag kHasMoveCapability = 0x40000000; /* Proposed MOVE RFC */
const eIMAPCapabilityFlag kHasHighestModSeqCapability = 0x80000000; /* Subset of RFC 3551 */
// above are 32bit; below start the uint64_t bits 33-64
const eIMAPCapabilityFlag kHasListExtendedCapability = 0x100000000LL; /* RFC 5258 */
const eIMAPCapabilityFlag kHasSpecialUseCapability = 0x200000000LL; /* RFC 6154: Sent, Draft etc. folders */
const eIMAPCapabilityFlag kGmailImapCapability = 0x400000000LL; /* X-GM-EXT-1 capability extension for gmail */
const eIMAPCapabilityFlag kHasXOAuth2Capability = 0x800000000LL; /* AUTH XOAUTH2 extension */
// this used to be part of the connection object class - maybe we should move it into
// something similar
typedef enum {
kEveryThingRFC822,
kEveryThingRFC822Peek,
kHeadersRFC822andUid,
kUid,
kFlags,
kRFC822Size,
kRFC822HeadersOnly,
kMIMEPart,
kMIMEHeader,
kBodyStart
} nsIMAPeFetchFields;
typedef struct _utf_name_struct {
bool toUtf7Imap;
unsigned char *sourceString;
unsigned char *convertedString;
} utf_name_struct;
typedef struct _ProgressInfo {
char16_t *message;
int32_t currentProgress;
int32_t maxProgress;
} ProgressInfo;
typedef enum {
eContinue,
eContinueNew,
eListMyChildren,
eNewServerDirectory,
eCancelled
} EMailboxDiscoverStatus;
#endif

View file

@ -0,0 +1,321 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h" // for pre-compiled headers
#include "nsImapCore.h"
#include "nsImapFlagAndUidState.h"
#include "nsMsgUtils.h"
#include "prcmon.h"
#include "nspr.h"
NS_IMPL_ISUPPORTS(nsImapFlagAndUidState, nsIImapFlagAndUidState)
using namespace mozilla;
NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfMessages(int32_t *result)
{
if (!result)
return NS_ERROR_NULL_POINTER;
*result = fUids.Length();
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetUidOfMessage(int32_t zeroBasedIndex, uint32_t *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
PR_CEnterMonitor(this);
*aResult = fUids.SafeElementAt(zeroBasedIndex, nsMsgKey_None);
PR_CExitMonitor(this);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetMessageFlags(int32_t zeroBasedIndex, uint16_t *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = fFlags.SafeElementAt(zeroBasedIndex, kNoImapMsgFlag);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::SetMessageFlags(int32_t zeroBasedIndex, unsigned short flags)
{
if (zeroBasedIndex < (int32_t)fUids.Length())
fFlags[zeroBasedIndex] = flags;
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfRecentMessages(int32_t *result)
{
if (!result)
return NS_ERROR_NULL_POINTER;
PR_CEnterMonitor(this);
uint32_t counter = 0;
int32_t numUnseenMessages = 0;
for (counter = 0; counter < fUids.Length(); counter++)
{
if (fFlags[counter] & kImapMsgRecentFlag)
numUnseenMessages++;
}
PR_CExitMonitor(this);
*result = numUnseenMessages;
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetPartialUIDFetch(bool *aPartialUIDFetch)
{
NS_ENSURE_ARG_POINTER(aPartialUIDFetch);
*aPartialUIDFetch = fPartialUIDFetch;
return NS_OK;
}
/* amount to expand for imap entry flags when we need more */
nsImapFlagAndUidState::nsImapFlagAndUidState(int32_t numberOfMessages)
: fUids(numberOfMessages),
fFlags(numberOfMessages),
m_customFlagsHash(10),
m_customAttributesHash(10),
mLock("nsImapFlagAndUidState.mLock")
{
fSupportedUserFlags = 0;
fNumberDeleted = 0;
fPartialUIDFetch = true;
}
nsImapFlagAndUidState::~nsImapFlagAndUidState()
{
}
NS_IMETHODIMP
nsImapFlagAndUidState::OrSupportedUserFlags(uint16_t flags)
{
fSupportedUserFlags |= flags;
return NS_OK;
}
NS_IMETHODIMP
nsImapFlagAndUidState::GetSupportedUserFlags(uint16_t *aFlags)
{
NS_ENSURE_ARG_POINTER(aFlags);
*aFlags = fSupportedUserFlags;
return NS_OK;
}
// we need to reset our flags, (re-read all) but chances are the memory allocation needed will be
// very close to what we were already using
NS_IMETHODIMP nsImapFlagAndUidState::Reset()
{
PR_CEnterMonitor(this);
fNumberDeleted = 0;
m_customFlagsHash.Clear();
fUids.Clear();
fFlags.Clear();
fPartialUIDFetch = true;
PR_CExitMonitor(this);
return NS_OK;
}
// Remove (expunge) a message from our array, since now it is gone for good
NS_IMETHODIMP nsImapFlagAndUidState::ExpungeByIndex(uint32_t msgIndex)
{
// protect ourselves in case the server gave us an index key of -1 or 0
if ((int32_t) msgIndex <= 0)
return NS_ERROR_INVALID_ARG;
if ((uint32_t) fUids.Length() < msgIndex)
return NS_ERROR_INVALID_ARG;
PR_CEnterMonitor(this);
msgIndex--; // msgIndex is 1-relative
if (fFlags[msgIndex] & kImapMsgDeletedFlag) // see if we already had counted this one as deleted
fNumberDeleted--;
fUids.RemoveElementAt(msgIndex);
fFlags.RemoveElementAt(msgIndex);
PR_CExitMonitor(this);
return NS_OK;
}
// adds to sorted list, protects against duplicates and going past array bounds.
NS_IMETHODIMP nsImapFlagAndUidState::AddUidFlagPair(uint32_t uid, imapMessageFlagsType flags, uint32_t zeroBasedIndex)
{
if (uid == nsMsgKey_None) // ignore uid of -1
return NS_OK;
// check for potential overflow in buffer size for uid array
if (zeroBasedIndex > 0x3FFFFFFF)
return NS_ERROR_INVALID_ARG;
PR_CEnterMonitor(this);
// make sure there is room for this pair
if (zeroBasedIndex >= fUids.Length())
{
int32_t sizeToGrowBy = zeroBasedIndex - fUids.Length() + 1;
fUids.InsertElementsAt(fUids.Length(), sizeToGrowBy, 0);
fFlags.InsertElementsAt(fFlags.Length(), sizeToGrowBy, 0);
}
fUids[zeroBasedIndex] = uid;
fFlags[zeroBasedIndex] = flags;
if (flags & kImapMsgDeletedFlag)
fNumberDeleted++;
PR_CExitMonitor(this);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfDeletedMessages(int32_t *numDeletedMessages)
{
NS_ENSURE_ARG_POINTER(numDeletedMessages);
*numDeletedMessages = NumberOfDeletedMessages();
return NS_OK;
}
int32_t nsImapFlagAndUidState::NumberOfDeletedMessages()
{
return fNumberDeleted;
}
// since the uids are sorted, start from the back (rb)
uint32_t nsImapFlagAndUidState::GetHighestNonDeletedUID()
{
uint32_t msgIndex = fUids.Length();
do
{
if (msgIndex <= 0)
return(0);
msgIndex--;
if (fUids[msgIndex] && !(fFlags[msgIndex] & kImapMsgDeletedFlag))
return fUids[msgIndex];
}
while (msgIndex > 0);
return 0;
}
// Has the user read the last message here ? Used when we first open the inbox to see if there
// really is new mail there.
bool nsImapFlagAndUidState::IsLastMessageUnseen()
{
uint32_t msgIndex = fUids.Length();
if (msgIndex <= 0)
return false;
msgIndex--;
// if last message is deleted, it was probably filtered the last time around
if (fUids[msgIndex] && (fFlags[msgIndex] & (kImapMsgSeenFlag | kImapMsgDeletedFlag)))
return false;
return true;
}
// find a message flag given a key with non-recursive binary search, since some folders
// may have thousand of messages, once we find the key set its index, or the index of
// where the key should be inserted
imapMessageFlagsType nsImapFlagAndUidState::GetMessageFlagsFromUID(uint32_t uid, bool *foundIt, int32_t *ndx)
{
PR_CEnterMonitor(this);
*ndx = (int32_t) fUids.IndexOfFirstElementGt(uid) - 1;
*foundIt = *ndx >= 0 && fUids[*ndx] == uid;
imapMessageFlagsType retFlags = (*foundIt) ? fFlags[*ndx] : kNoImapMsgFlag;
PR_CExitMonitor(this);
return retFlags;
}
NS_IMETHODIMP nsImapFlagAndUidState::AddUidCustomFlagPair(uint32_t uid, const char *customFlag)
{
if (!customFlag)
return NS_OK;
MutexAutoLock mon(mLock);
nsCString ourCustomFlags;
nsCString oldValue;
if (m_customFlagsHash.Get(uid, &oldValue))
{
// We'll store multiple keys as space-delimited since space is not
// a valid character in a keyword. First, we need to look for the
// customFlag in the existing flags;
nsDependentCString customFlagString(customFlag);
int32_t existingCustomFlagPos = oldValue.Find(customFlagString);
uint32_t customFlagLen = customFlagString.Length();
while (existingCustomFlagPos != kNotFound)
{
// if existing flags ends with this exact flag, or flag + ' '
// and the flag is at the beginning of the string or there is ' ' + flag
// then we have this flag already;
if (((oldValue.Length() == existingCustomFlagPos + customFlagLen) ||
(oldValue.CharAt(existingCustomFlagPos + customFlagLen) == ' ')) &&
((existingCustomFlagPos == 0) ||
(oldValue.CharAt(existingCustomFlagPos - 1) == ' ')))
return NS_OK;
// else, advance to next flag
existingCustomFlagPos = MsgFind(oldValue, customFlagString, false, existingCustomFlagPos + customFlagLen);
}
ourCustomFlags.Assign(oldValue);
ourCustomFlags.AppendLiteral(" ");
ourCustomFlags.Append(customFlag);
m_customFlagsHash.Remove(uid);
}
else
{
ourCustomFlags.Assign(customFlag);
}
m_customFlagsHash.Put(uid, ourCustomFlags);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetCustomFlags(uint32_t uid, char **customFlags)
{
MutexAutoLock mon(mLock);
nsCString value;
if (m_customFlagsHash.Get(uid, &value))
{
*customFlags = NS_strdup(value.get());
return (*customFlags) ? NS_OK : NS_ERROR_FAILURE;
}
*customFlags = nullptr;
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::ClearCustomFlags(uint32_t uid)
{
MutexAutoLock mon(mLock);
m_customFlagsHash.Remove(uid);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::SetCustomAttribute(uint32_t aUid,
const nsACString &aCustomAttributeName,
const nsACString &aCustomAttributeValue)
{
nsCString key;
key.AppendInt((int64_t)aUid);
key.Append(aCustomAttributeName);
nsCString value;
value.Assign(aCustomAttributeValue);
m_customAttributesHash.Put(key, value);
return NS_OK;
}
NS_IMETHODIMP nsImapFlagAndUidState::GetCustomAttribute(uint32_t aUid,
const nsACString &aCustomAttributeName,
nsACString &aCustomAttributeValue)
{
nsCString key;
key.AppendInt((int64_t)aUid);
key.Append(aCustomAttributeName);
nsCString val;
m_customAttributesHash.Get(key, &val);
aCustomAttributeValue.Assign(val);
return NS_OK;
}

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapFlagAndUidState_h___
#define nsImapFlagAndUidState_h___
#include "MailNewsTypes.h"
#include "nsTArray.h"
#include "nsIImapFlagAndUidState.h"
#include "mozilla/Mutex.h"
const int32_t kImapFlagAndUidStateSize = 100;
#include "nsBaseHashtable.h"
#include "nsDataHashtable.h"
class nsImapFlagAndUidState : public nsIImapFlagAndUidState
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
nsImapFlagAndUidState(int numberOfMessages);
NS_DECL_NSIIMAPFLAGANDUIDSTATE
int32_t NumberOfDeletedMessages();
imapMessageFlagsType GetMessageFlagsFromUID(uint32_t uid, bool *foundIt, int32_t *ndx);
bool IsLastMessageUnseen(void);
bool GetPartialUIDFetch() {return fPartialUIDFetch;}
void SetPartialUIDFetch(bool isPartial) {fPartialUIDFetch = isPartial;}
uint32_t GetHighestNonDeletedUID();
uint16_t GetSupportedUserFlags() { return fSupportedUserFlags; }
private:
virtual ~nsImapFlagAndUidState();
nsTArray<nsMsgKey> fUids;
nsTArray<imapMessageFlagsType> fFlags;
// Hash table, mapping uids to extra flags
nsDataHashtable<nsUint32HashKey, nsCString> m_customFlagsHash;
// Hash table, mapping UID+customAttributeName to customAttributeValue.
nsDataHashtable<nsCStringHashKey, nsCString> m_customAttributesHash;
uint16_t fSupportedUserFlags;
int32_t fNumberDeleted;
bool fPartialUIDFetch;
mozilla::Mutex mLock;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __nsImapIncomingServer_h
#define __nsImapIncomingServer_h
#include "mozilla/Attributes.h"
#include "msgCore.h"
#include "nsIImapIncomingServer.h"
#include "nsMsgIncomingServer.h"
#include "nsIImapServerSink.h"
#include "nsIStringBundle.h"
#include "nsISubscribableServer.h"
#include "nsIUrlListener.h"
#include "nsIMsgImapMailFolder.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "mozilla/Mutex.h"
class nsIRDFService;
/* get some implementation from nsMsgIncomingServer */
class nsImapIncomingServer : public nsMsgIncomingServer,
public nsIImapIncomingServer,
public nsIImapServerSink,
public nsISubscribableServer,
public nsIUrlListener
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsImapIncomingServer();
// overriding nsMsgIncomingServer methods
NS_IMETHOD SetKey(const nsACString& aKey) override; // override nsMsgIncomingServer's implementation...
NS_IMETHOD GetLocalStoreType(nsACString& type) override;
NS_IMETHOD GetLocalDatabaseType(nsACString& type) override;
NS_DECL_NSIIMAPINCOMINGSERVER
NS_DECL_NSIIMAPSERVERSINK
NS_DECL_NSISUBSCRIBABLESERVER
NS_DECL_NSIURLLISTENER
NS_IMETHOD PerformBiff(nsIMsgWindow *aMsgWindow) override;
NS_IMETHOD PerformExpand(nsIMsgWindow *aMsgWindow) override;
NS_IMETHOD CloseCachedConnections() override;
NS_IMETHOD GetConstructedPrettyName(nsAString& retval) override;
NS_IMETHOD GetCanBeDefaultServer(bool *canBeDefaultServer) override;
NS_IMETHOD GetCanCompactFoldersOnServer(bool *canCompactFoldersOnServer
) override;
NS_IMETHOD GetCanUndoDeleteOnServer(bool *canUndoDeleteOnServer) override;
NS_IMETHOD GetCanSearchMessages(bool *canSearchMessages) override;
NS_IMETHOD GetCanEmptyTrashOnExit(bool *canEmptyTrashOnExit) override;
NS_IMETHOD GetOfflineSupportLevel(int32_t *aSupportLevel) override;
NS_IMETHOD GeneratePrettyNameForMigration(nsAString& aPrettyName) override;
NS_IMETHOD GetSupportsDiskSpace(bool *aSupportsDiskSpace) override;
NS_IMETHOD GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer
) override;
NS_IMETHOD GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer
) override;
NS_IMETHOD GetFilterScope(nsMsgSearchScopeValue *filterScope) override;
NS_IMETHOD GetSearchScope(nsMsgSearchScopeValue *searchScope) override;
NS_IMETHOD GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff
) override;
NS_IMETHOD OnUserOrHostNameChanged(const nsACString& oldName,
const nsACString& newName,
bool hostnameChanged) override;
NS_IMETHOD GetNumIdleConnections(int32_t *aNumIdleConnections);
NS_IMETHOD ForgetSessionPassword() override;
NS_IMETHOD GetMsgFolderFromURI(nsIMsgFolder *aFolderResource,
const nsACString& aURI,
nsIMsgFolder **aFolder) override;
NS_IMETHOD SetSocketType(int32_t aSocketType) override;
NS_IMETHOD VerifyLogon(nsIUrlListener *aUrlListener, nsIMsgWindow *aMsgWindow,
nsIURI **aURL) override;
protected:
virtual ~nsImapIncomingServer();
nsresult GetFolder(const nsACString& name, nsIMsgFolder** pFolder);
virtual nsresult CreateRootFolderFromUri(const nsCString &serverUri,
nsIMsgFolder **rootFolder) override;
nsresult ResetFoldersToUnverified(nsIMsgFolder *parentFolder);
void GetUnverifiedSubFolders(nsIMsgFolder *parentFolder,
nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray);
void GetUnverifiedFolders(nsCOMArray<nsIMsgImapMailFolder> &aFolderArray);
nsresult DeleteNonVerifiedFolders(nsIMsgFolder *parentFolder);
bool NoDescendentsAreVerified(nsIMsgFolder *parentFolder);
bool AllDescendentsAreNoSelect(nsIMsgFolder *parentFolder);
nsresult GetStringBundle();
static nsresult AlertUser(const nsAString& aString, nsIMsgMailNewsUrl *aUrl);
private:
nsresult SubscribeToFolder(const char16_t *aName, bool subscribe);
nsresult GetImapConnection(nsIImapUrl* aImapUrl,
nsIImapProtocol** aImapConnection);
nsresult CreateProtocolInstance(nsIImapProtocol ** aImapConnection);
nsresult CreateHostSpecificPrefName(const char *prefPrefix, nsAutoCString &prefName);
nsresult DoomUrlIfChannelHasError(nsIImapUrl *aImapUrl, bool *urlDoomed);
bool ConnectionTimeOut(nsIImapProtocol* aImapConnection);
nsresult GetFormattedStringFromName(const nsAString& aValue, const char* aName, nsAString& aResult);
nsresult GetPrefForServerAttribute(const char *prefSuffix, bool *prefValue);
bool CheckSpecialFolder(nsIRDFService *rdf, nsCString &folderUri,
uint32_t folderFlag, nsCString &existingUri);
nsCOMArray<nsIImapProtocol> m_connectionCache;
nsCOMArray<nsIImapUrl> m_urlQueue;
nsCOMPtr<nsIStringBundle> m_stringBundle;
nsCOMArray<nsIMsgFolder> m_subscribeFolders; // used to keep folder resources around while subscribe UI is up.
nsCOMArray<nsIMsgImapMailFolder> m_foldersToStat; // folders to check for new mail with Status
nsTArray<nsISupports*> m_urlConsumers;
eIMAPCapabilityFlags m_capability;
nsCString m_manageMailAccountUrl;
bool m_userAuthenticated;
bool mDoingSubscribeDialog;
bool mDoingLsub;
bool m_shuttingDown;
mozilla::Mutex mLock;
// subscribe dialog stuff
nsresult AddFolderToSubscribeDialog(const char *parentUri, const char *uri,const char *folderName);
nsCOMPtr <nsISubscribableServer> mInner;
nsresult EnsureInner();
nsresult ClearInner();
// Utility function for checking folder existence
nsresult GetExistingMsgFolder(const nsACString& aURI,
nsACString& folderUriWithNamespace,
bool& namespacePrefixAdded,
bool caseInsensitive,
nsIMsgFolder **aFolder);
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,550 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapMailFolder_h__
#define nsImapMailFolder_h__
#include "mozilla/Attributes.h"
#include "nsImapCore.h"
#include "nsMsgDBFolder.h"
#include "nsIImapMailFolderSink.h"
#include "nsIImapMessageSink.h"
#include "nsICopyMessageListener.h"
#include "nsIImapService.h"
#include "nsIUrlListener.h"
#include "nsAutoPtr.h"
#include "nsIImapIncomingServer.h" // we need this for its IID
#include "nsIMsgParseMailMsgState.h"
#include "nsITransactionManager.h"
#include "nsImapUndoTxn.h"
#include "nsIMsgMessageService.h"
#include "nsIMsgFilterHitNotify.h"
#include "nsIMsgFilterList.h"
#include "prmon.h"
#include "nsIMsgImapMailFolder.h"
#include "nsIMsgLocalMailFolder.h"
#include "nsIMsgThread.h"
#include "nsIImapMailFolderSink.h"
#include "nsIImapServerSink.h"
#include "nsIMsgFilterPlugin.h"
#include "nsIThread.h"
#include "nsDataHashtable.h"
#include "nsIMutableArray.h"
#include "nsITimer.h"
#include "nsCOMArray.h"
#include "nsAutoSyncState.h"
#include "nsIRequestObserver.h"
class nsImapMoveCoalescer;
class nsIMsgIdentity;
class nsIMsgOfflineImapOperation;
#define COPY_BUFFER_SIZE 16384
#define NS_IMAPMAILCOPYSTATE_IID \
{ 0xb64534f0, 0x3d53, 0x11d3, \
{ 0xac, 0x2a, 0x00, 0x80, 0x5f, 0x8a, 0xc9, 0x68 } }
class nsImapMailCopyState: public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMAPMAILCOPYSTATE_IID)
NS_DECL_THREADSAFE_ISUPPORTS
nsImapMailCopyState();
nsCOMPtr<nsISupports> m_srcSupport; // source file spec or folder
nsCOMPtr<nsIArray> m_messages; // array of source messages
RefPtr<nsImapMoveCopyMsgTxn> m_undoMsgTxn; // undo object with this copy operation
nsCOMPtr<nsIMsgDBHdr> m_message; // current message to be copied
nsCOMPtr<nsIMsgCopyServiceListener> m_listener; // listener of this copy
// operation
nsCOMPtr<nsIFile> m_tmpFile; // temp file spec for copy operation
nsCOMPtr<nsIMsgWindow> m_msgWindow; // msg window for copy operation
nsCOMPtr<nsIMsgMessageService> m_msgService; // source folder message service; can
// be Nntp, Mailbox, or Imap
bool m_isMove; // is a move
bool m_selectedState; // needs to be in selected state; append msg
bool m_isCrossServerOp; // are we copying between imap servers?
uint32_t m_curIndex; // message index to the message array which we are
// copying
uint32_t m_totalCount;// total count of messages we have to do
uint32_t m_unreadCount; // num unread messages we're moving
bool m_streamCopy;
char *m_dataBuffer; // temporary buffer for this copy operation
nsCOMPtr<nsIOutputStream> m_msgFileStream; // temporary file (processed mail)
uint32_t m_dataBufferSize;
uint32_t m_leftOver;
bool m_allowUndo;
bool m_eatLF;
uint32_t m_newMsgFlags; // only used if there's no m_message
nsCString m_newMsgKeywords; // ditto
// If the server supports UIDPLUS, this is the UID for the append,
// if we're doing an append.
nsMsgKey m_appendUID;
private:
virtual ~nsImapMailCopyState();
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsImapMailCopyState, NS_IMAPMAILCOPYSTATE_IID)
// ACLs for this folder.
// Generally, we will try to always query this class when performing
// an operation on the folder.
// If the server doesn't support ACLs, none of this data will be filled in.
// Therefore, we can assume that if we look up ourselves and don't find
// any info (and also look up "anyone") then we have full rights, that is, ACLs don't exist.
class nsImapMailFolder;
#define IMAP_ACL_READ_FLAG 0x0000001 /* SELECT, CHECK, FETCH, PARTIAL, SEARCH, COPY from folder */
#define IMAP_ACL_STORE_SEEN_FLAG 0x0000002 /* STORE SEEN flag */
#define IMAP_ACL_WRITE_FLAG 0x0000004 /* STORE flags other than SEEN and DELETED */
#define IMAP_ACL_INSERT_FLAG 0x0000008 /* APPEND, COPY into folder */
#define IMAP_ACL_POST_FLAG 0x0000010 /* Can I send mail to the submission address for folder? */
#define IMAP_ACL_CREATE_SUBFOLDER_FLAG 0x0000020 /* Can I CREATE a subfolder of this folder? */
#define IMAP_ACL_DELETE_FLAG 0x0000040 /* STORE DELETED flag */
#define IMAP_ACL_ADMINISTER_FLAG 0x0000080 /* perform SETACL */
#define IMAP_ACL_RETRIEVED_FLAG 0x0000100 /* ACL info for this folder has been initialized */
#define IMAP_ACL_EXPUNGE_FLAG 0x0000200 // can EXPUNGE or do implicit EXPUNGE on CLOSE
#define IMAP_ACL_DELETE_FOLDER 0x0000400 // can DELETE/RENAME folder
class nsMsgIMAPFolderACL
{
public:
nsMsgIMAPFolderACL(nsImapMailFolder *folder);
~nsMsgIMAPFolderACL();
bool SetFolderRightsForUser(const nsACString& userName, const nsACString& rights);
public:
// generic for any user, although we might not use them in
// DO NOT use these for looking up information about the currently authenticated user.
// (There are some different checks and defaults we do).
// Instead, use the functions below, GetICan....()
bool GetCanUserLookupFolder(const nsACString& userName); // Is folder visible to LIST/LSUB?
bool GetCanUserReadFolder(const nsACString& userName); // SELECT, CHECK, FETCH, PARTIAL, SEARCH, COPY from folder?
bool GetCanUserStoreSeenInFolder(const nsACString& userName); // STORE SEEN flag?
bool GetCanUserWriteFolder(const nsACString& userName); // STORE flags other than SEEN and DELETED?
bool GetCanUserInsertInFolder(const nsACString& userName); // APPEND, COPY into folder?
bool GetCanUserPostToFolder(const nsACString& userName); // Can I send mail to the submission address for folder?
bool GetCanUserCreateSubfolder(const nsACString& userName); // Can I CREATE a subfolder of this folder?
bool GetCanUserDeleteInFolder(const nsACString& userName); // STORE DELETED flag, perform EXPUNGE?
bool GetCanUserAdministerFolder(const nsACString& userName); // perform SETACL?
// Functions to find out rights for the currently authenticated user.
bool GetCanILookupFolder(); // Is folder visible to LIST/LSUB?
bool GetCanIReadFolder(); // SELECT, CHECK, FETCH, PARTIAL, SEARCH, COPY from folder?
bool GetCanIStoreSeenInFolder(); // STORE SEEN flag?
bool GetCanIWriteFolder(); // STORE flags other than SEEN and DELETED?
bool GetCanIInsertInFolder(); // APPEND, COPY into folder?
bool GetCanIPostToFolder(); // Can I send mail to the submission address for folder?
bool GetCanICreateSubfolder(); // Can I CREATE a subfolder of this folder?
bool GetCanIDeleteInFolder(); // STORE DELETED flag?
bool GetCanIAdministerFolder(); // perform SETACL?
bool GetCanIExpungeFolder(); // perform EXPUNGE?
bool GetDoIHaveFullRightsForFolder(); // Returns TRUE if I have full rights on this folder (all of the above return TRUE)
bool GetIsFolderShared(); // We use this to see if the ACLs think a folder is shared or not.
// We will define "Shared" in 5.0 to mean:
// At least one user other than the currently authenticated user has at least one
// explicitly-listed ACL right on that folder.
// Returns a newly allocated string describing these rights
nsresult CreateACLRightsString(nsAString& rightsString);
nsresult GetRightsStringForUser(const nsACString& userName, nsCString &rights);
nsresult GetOtherUsers(nsIUTF8StringEnumerator** aResult);
protected:
bool GetFlagSetInRightsForUser(const nsACString& userName, char flag, bool defaultIfNotFound);
void BuildInitialACLFromCache();
void UpdateACLCache();
protected:
nsDataHashtable <nsCStringHashKey, nsCString> m_rightsHash; // Hash table, mapping username strings to rights strings.
nsImapMailFolder *m_folder;
int32_t m_aclCount;
};
/**
* Encapsulates parameters required to playback offline ops
* on given folder.
*/
struct nsPlaybackRequest
{
explicit nsPlaybackRequest(nsImapMailFolder *srcFolder, nsIMsgWindow *msgWindow)
: SrcFolder(srcFolder), MsgWindow(msgWindow)
{
}
nsImapMailFolder *SrcFolder;
nsCOMPtr<nsIMsgWindow> MsgWindow;
};
class nsImapMailFolder : public nsMsgDBFolder,
public nsIMsgImapMailFolder,
public nsIImapMailFolderSink,
public nsIImapMessageSink,
public nsICopyMessageListener,
public nsIMsgFilterHitNotify
{
static const uint32_t PLAYBACK_TIMER_INTERVAL_IN_MS = 500;
public:
nsImapMailFolder();
NS_DECL_ISUPPORTS_INHERITED
// nsIMsgFolder methods:
NS_IMETHOD GetSubFolders(nsISimpleEnumerator **aResult) override;
NS_IMETHOD GetMessages(nsISimpleEnumerator* *result) override;
NS_IMETHOD UpdateFolder(nsIMsgWindow *aWindow) override;
NS_IMETHOD CreateSubfolder(const nsAString& folderName,nsIMsgWindow *msgWindow ) override;
NS_IMETHOD AddSubfolder(const nsAString& aName, nsIMsgFolder** aChild) override;
NS_IMETHODIMP CreateStorageIfMissing(nsIUrlListener* urlListener) override;
NS_IMETHOD Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) override;
NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow,
bool aCompactOfflineAlso) override;
NS_IMETHOD EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener) override;
NS_IMETHOD CopyDataToOutputStreamForAppend(nsIInputStream *aIStream,
int32_t aLength, nsIOutputStream *outputStream) override;
NS_IMETHOD CopyDataDone() override;
NS_IMETHOD Delete () override;
NS_IMETHOD Rename (const nsAString& newName, nsIMsgWindow *msgWindow) override;
NS_IMETHOD RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder) override;
NS_IMETHOD GetNoSelect(bool *aResult) override;
NS_IMETHOD GetPrettyName(nsAString& prettyName) override; // Override of the base, for top-level mail folder
NS_IMETHOD GetFolderURL(nsACString& url) override;
NS_IMETHOD UpdateSummaryTotals(bool force) override;
NS_IMETHOD GetDeletable (bool *deletable) override;
NS_IMETHOD GetSizeOnDisk(int64_t *size) override;
NS_IMETHOD GetCanCreateSubfolders(bool *aResult) override;
NS_IMETHOD GetCanSubscribe(bool *aResult) override;
NS_IMETHOD ApplyRetentionSettings() override;
NS_IMETHOD AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag) override;
NS_IMETHOD MarkMessagesRead(nsIArray *messages, bool markRead) override;
NS_IMETHOD MarkAllMessagesRead(nsIMsgWindow *aMsgWindow) override;
NS_IMETHOD MarkMessagesFlagged(nsIArray *messages, bool markFlagged) override;
NS_IMETHOD MarkThreadRead(nsIMsgThread *thread) override;
NS_IMETHOD SetLabelForMessages(nsIArray *aMessages, nsMsgLabelValue aLabel) override;
NS_IMETHOD SetJunkScoreForMessages(nsIArray *aMessages, const nsACString& aJunkScore) override;
NS_IMETHOD DeleteSubFolders(nsIArray *folders, nsIMsgWindow *msgWindow) override;
NS_IMETHOD ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element) override;
NS_IMETHOD WriteToFolderCacheElem(nsIMsgFolderCacheElement *element) override;
NS_IMETHOD GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo,
nsIMsgDatabase **db) override;
NS_IMETHOD DeleteMessages(nsIArray *messages,
nsIMsgWindow *msgWindow, bool
deleteStorage, bool isMove,
nsIMsgCopyServiceListener* listener, bool allowUndo) override;
NS_IMETHOD CopyMessages(nsIMsgFolder *srcFolder,
nsIArray* messages,
bool isMove, nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener, bool isFolder,
bool allowUndo) override;
NS_IMETHOD CopyFolder(nsIMsgFolder *srcFolder, bool isMove, nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener) override;
NS_IMETHOD CopyFileMessage(nsIFile* file,
nsIMsgDBHdr* msgToReplace,
bool isDraftOrTemplate,
uint32_t aNewMsgFlags,
const nsACString &aNewMsgKeywords,
nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener) override;
NS_IMETHOD GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener) override;
NS_IMETHOD GetFilePath(nsIFile** aPathName) override;
NS_IMETHOD SetFilePath(nsIFile * aPath) override;
NS_IMETHOD Shutdown(bool shutdownChildren) override;
NS_IMETHOD DownloadMessagesForOffline(nsIArray *messages, nsIMsgWindow *msgWindow) override;
NS_IMETHOD DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow) override;
NS_IMETHOD GetCanFileMessages(bool *aCanFileMessages) override;
NS_IMETHOD GetCanDeleteMessages(bool *aCanDeleteMessages) override;
NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, uint32_t aNumKeys,
bool aLocalOnly, nsIUrlListener *aUrlListener,
bool *aAsyncResults) override;
NS_IMETHOD AddKeywordsToMessages(nsIArray *aMessages, const nsACString& aKeywords) override;
NS_IMETHOD RemoveKeywordsFromMessages(nsIArray *aMessages, const nsACString& aKeywords) override;
NS_IMETHOD NotifyCompactCompleted() override;
// overrides nsMsgDBFolder::HasMsgOffline()
NS_IMETHOD HasMsgOffline(nsMsgKey msgKey, bool *_retval) override;
// overrides nsMsgDBFolder::GetOfflineFileStream()
NS_IMETHOD GetOfflineFileStream(nsMsgKey msgKey, int64_t *offset, uint32_t *size, nsIInputStream **aFileStream) override;
NS_DECL_NSIMSGIMAPMAILFOLDER
NS_DECL_NSIIMAPMAILFOLDERSINK
NS_DECL_NSIIMAPMESSAGESINK
NS_DECL_NSICOPYMESSAGELISTENER
// nsIUrlListener methods
NS_IMETHOD OnStartRunningUrl(nsIURI * aUrl) override;
NS_IMETHOD OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode) override;
NS_DECL_NSIMSGFILTERHITNOTIFY
NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
NS_IMETHOD IsCommandEnabled(const nsACString& command, bool *result) override;
NS_IMETHOD SetFilterList(nsIMsgFilterList *aMsgFilterList) override;
NS_IMETHOD GetCustomIdentity(nsIMsgIdentity **aIdentity) override;
/**
* This method is used to locate a folder where a msg could be present, not just
* the folder where the message first arrives, this method searches for the existence
* of msg in all the folders/labels that we retrieve from X-GM-LABELS also.
* overrides nsMsgDBFolder::GetOfflineMsgFolder()
* @param msgKey key of the msg for which we are trying to get the folder;
* @param aMsgFolder required folder;
*/
NS_IMETHOD GetOfflineMsgFolder(nsMsgKey msgKey, nsIMsgFolder **aMsgFolder) override;
NS_IMETHOD GetIncomingServerType(nsACString& serverType) override;
nsresult AddSubfolderWithPath(nsAString& name, nsIFile *dbPath, nsIMsgFolder **child, bool brandNew = false);
nsresult MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
nsIMsgDatabase *sourceDB,
const nsACString& destFolder,
nsIMsgFilter *filter,
nsIMsgWindow *msgWindow);
// send notification to copy service listener.
nsresult OnCopyCompleted(nsISupports *srcSupport, nsresult exitCode);
static nsresult AllocateUidStringFromKeys(nsMsgKey *keys, uint32_t numKeys, nsCString &msgIds);
static nsresult BuildIdsAndKeyArray(nsIArray* messages, nsCString& msgIds, nsTArray<nsMsgKey>& keyArray);
// these might end up as an nsIImapMailFolder attribute.
nsresult SetSupportedUserFlags(uint32_t userFlags);
nsresult GetSupportedUserFlags(uint32_t *userFlags);
// Find the start of a range of msgKeys that can hold srcCount headers.
nsresult FindOpenRange(nsMsgKey &fakeBase, uint32_t srcCount);
protected:
virtual ~nsImapMailFolder();
// Helper methods
virtual nsresult CreateChildFromURI(const nsCString &uri, nsIMsgFolder **folder) override;
void FindKeysToAdd(const nsTArray<nsMsgKey> &existingKeys, nsTArray<nsMsgKey>
&keysToFetch, uint32_t &numNewUnread, nsIImapFlagAndUidState *flagState);
void FindKeysToDelete(const nsTArray<nsMsgKey> &existingKeys, nsTArray<nsMsgKey>
&keysToFetch, nsIImapFlagAndUidState *flagState, uint32_t boxFlags);
void PrepareToAddHeadersToMailDB(nsIImapProtocol* aProtocol);
void TweakHeaderFlags(nsIImapProtocol* aProtocol, nsIMsgDBHdr *tweakMe);
nsresult SyncFlags(nsIImapFlagAndUidState *flagState);
nsresult HandleCustomFlags(nsMsgKey uidOfMessage, nsIMsgDBHdr *dbHdr,
uint16_t userFlags, nsCString& keywords);
nsresult NotifyMessageFlagsFromHdr(nsIMsgDBHdr *dbHdr, nsMsgKey msgKey,
uint32_t flags);
nsresult SetupHeaderParseStream(uint32_t size, const nsACString& content_type, nsIMailboxSpec *boxSpec);
nsresult ParseAdoptedHeaderLine(const char *messageLine, nsMsgKey msgKey);
nsresult NormalEndHeaderParseStream(nsIImapProtocol *aProtocol, nsIImapUrl *imapUrl);
void EndOfflineDownload();
/**
* At the end of a file-to-folder copy operation, copy the file to the
* offline store and/or add to the message database, (if needed).
*
* @param srcFile file containing the message key
* @param msgKey key to use for the new messages
*/
nsresult CopyFileToOfflineStore(nsIFile *srcFile, nsMsgKey msgKey);
nsresult MarkMessagesImapDeleted(nsTArray<nsMsgKey> *keyArray, bool deleted, nsIMsgDatabase *db);
// Notifies imap autosync that it should update this folder when it
// gets a chance.
void NotifyHasPendingMsgs();
void UpdatePendingCounts();
void SetIMAPDeletedFlag(nsIMsgDatabase *mailDB, const nsTArray<nsMsgKey> &msgids, bool markDeleted);
virtual bool ShowDeletedMessages();
virtual bool DeleteIsMoveToTrash();
nsresult GetFolder(const nsACString& name, nsIMsgFolder **pFolder);
nsresult GetTrashFolder(nsIMsgFolder **pTrashFolder);
bool TrashOrDescendentOfTrash(nsIMsgFolder* folder);
static bool ShouldCheckAllFolders(nsIImapIncomingServer *imapServer);
nsresult GetServerKey(nsACString& serverKey);
nsresult DisplayStatusMsg(nsIImapUrl *aImapUrl, const nsAString& msg);
//nsresult RenameLocal(const char *newName);
nsresult AddDirectorySeparator(nsIFile *path);
nsresult CreateSubFolders(nsIFile *path);
nsresult GetDatabase() override;
nsresult GetFolderOwnerUserName(nsACString& userName);
nsIMAPNamespace *GetNamespaceForFolder();
void SetNamespaceForFolder(nsIMAPNamespace *ns);
nsMsgIMAPFolderACL * GetFolderACL();
nsresult CreateACLRightsStringForFolder(nsAString& rightsString);
nsresult GetBodysToDownload(nsTArray<nsMsgKey> *keysOfMessagesToDownload);
// Uber message copy service
nsresult CopyMessagesWithStream(nsIMsgFolder* srcFolder,
nsIArray* messages,
bool isMove,
bool isCrossServerOp,
nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener, bool allowUndo);
nsresult CopyStreamMessage(nsIMsgDBHdr* message, nsIMsgFolder* dstFolder,
nsIMsgWindow *msgWindow, bool isMove);
nsresult InitCopyState(nsISupports* srcSupport,
nsIArray* messages,
bool isMove,
bool selectedState,
bool acrossServers,
uint32_t newMsgFlags,
const nsACString &newMsgKeywords,
nsIMsgCopyServiceListener* listener,
nsIMsgWindow *msgWindow,
bool allowUndo);
nsresult GetMoveCoalescer();
nsresult PlaybackCoalescedOperations();
virtual nsresult CreateBaseMessageURI(const nsACString& aURI) override;
// offline-ish methods
nsresult GetClearedOriginalOp(nsIMsgOfflineImapOperation *op, nsIMsgOfflineImapOperation **originalOp, nsIMsgDatabase **originalDB);
nsresult GetOriginalOp(nsIMsgOfflineImapOperation *op, nsIMsgOfflineImapOperation **originalOp, nsIMsgDatabase **originalDB);
nsresult CopyMessagesOffline(nsIMsgFolder* srcFolder,
nsIArray* messages,
bool isMove,
nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener);
void SetPendingAttributes(nsIArray* messages, bool aIsMove);
nsresult CopyOfflineMsgBody(nsIMsgFolder *srcFolder, nsIMsgDBHdr *destHdr,
nsIMsgDBHdr *origHdr, nsIInputStream *inputStream,
nsIOutputStream *outputStream);
void GetTrashFolderName(nsAString &aFolderName);
bool ShowPreviewText();
// Pseudo-Offline operation playback timer
static void PlaybackTimerCallback(nsITimer *aTimer, void *aClosure);
nsresult CreatePlaybackTimer();
// Allocate and initialize associated auto-sync state object.
void InitAutoSyncState();
bool m_initialized;
bool m_haveDiscoveredAllFolders;
nsCOMPtr<nsIMsgParseMailMsgState> m_msgParser;
nsCOMPtr<nsIMsgFilterList> m_filterList;
nsCOMPtr<nsIMsgFilterPlugin> m_filterPlugin; // XXX should be a list
// used with filter plugins to know when we've finished classifying and can playback moves
bool m_msgMovedByFilter;
nsImapMoveCoalescer *m_moveCoalescer; // strictly owned by the nsImapMailFolder
nsCOMPtr<nsIMutableArray> m_junkMessagesToMarkAsRead;
/// list of keys to be moved to the junk folder
nsTArray<nsMsgKey> mSpamKeysToMove;
/// the junk destination folder
nsCOMPtr<nsIMsgFolder> mSpamFolder;
nsMsgKey m_curMsgUid;
uint32_t m_uidValidity;
// These three vars are used to store counts from STATUS or SELECT command
// They include deleted messages, so they can differ from the generic
// folder total and unread counts.
int32_t m_numServerRecentMessages;
int32_t m_numServerUnseenMessages;
int32_t m_numServerTotalMessages;
// if server supports UIDNEXT, we store it here.
int32_t m_nextUID;
int32_t m_nextMessageByteLength;
nsCOMPtr<nsIUrlListener> m_urlListener;
bool m_urlRunning;
// undo move/copy transaction support
RefPtr<nsMsgTxn> m_pendingUndoTxn;
RefPtr<nsImapMailCopyState> m_copyState;
char m_hierarchyDelimiter;
int32_t m_boxFlags;
nsCString m_onlineFolderName;
nsCString m_ownerUserName; // username of the "other user," as in
// "Other Users' Mailboxes"
nsCString m_adminUrl; // url to run to set admin privileges for this folder
nsIMAPNamespace *m_namespace;
bool m_verifiedAsOnlineFolder;
bool m_explicitlyVerify; // whether or not we need to explicitly verify this through LIST
bool m_folderIsNamespace;
bool m_folderNeedsSubscribing;
bool m_folderNeedsAdded;
bool m_folderNeedsACLListed;
bool m_performingBiff;
bool m_folderQuotaCommandIssued;
bool m_folderQuotaDataIsValid;
bool m_updatingFolder;
// These two vars are used to keep track of compaction state so we can know
// when to send a done notification.
bool m_compactingOfflineStore;
bool m_expunging;
bool m_applyIncomingFilters; // apply filters to this folder, even if not the inbox
nsMsgIMAPFolderACL *m_folderACL;
uint32_t m_aclFlags;
uint32_t m_supportedUserFlags;
// determines if we are on GMail server
bool m_isGmailServer;
// offline imap support
bool m_downloadingFolderForOfflineUse;
bool m_filterListRequiresBody;
// auto-sync (automatic message download) support
RefPtr<nsAutoSyncState> m_autoSyncStateObj;
// Quota support
nsCString m_folderQuotaRoot;
uint32_t m_folderQuotaUsedKB;
uint32_t m_folderQuotaMaxKB;
// Pseudo-Offline Playback support
nsPlaybackRequest *m_pendingPlaybackReq;
nsCOMPtr<nsITimer> m_playbackTimer;
nsTArray<RefPtr<nsImapMoveCopyMsgTxn> > m_pendingOfflineMoves;
// hash table of mapping between messageids and message keys
// for pseudo hdrs.
nsDataHashtable<nsCStringHashKey, nsMsgKey> m_pseudoHdrs;
nsTArray<nsMsgKey> m_keysToFetch;
uint32_t m_totalKeysToFetch;
/**
* delete if appropriate local storage for messages in this folder
*
* @parm aMessages array (of nsIMsgDBHdr) of messages to delete
* (or an array of message keys)
* @parm aSrcFolder the folder containing the messages (optional)
*/
void DeleteStoreMessages(nsIArray* aMessages);
void DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages);
static void DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages, nsIMsgFolder* aFolder);
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsImapOfflineSync_H_
#define _nsImapOfflineSync_H_
#include "mozilla/Attributes.h"
#include "nsIMsgDatabase.h"
#include "nsIUrlListener.h"
#include "nsIMsgOfflineImapOperation.h"
#include "nsIMsgWindow.h"
#include "nsIMsgFolder.h"
#include "nsCOMArray.h"
#include "nsIDBChangeListener.h"
class nsImapOfflineSync : public nsIUrlListener,
public nsIMsgCopyServiceListener,
public nsIDBChangeListener {
public: // set to one folder to playback one folder only
nsImapOfflineSync(nsIMsgWindow *window, nsIUrlListener *listener,
nsIMsgFolder *singleFolderOnly = nullptr,
bool isPseudoOffline = false);
NS_DECL_ISUPPORTS
NS_DECL_NSIURLLISTENER
NS_DECL_NSIMSGCOPYSERVICELISTENER
NS_DECL_NSIDBCHANGELISTENER
virtual nsresult ProcessNextOperation(); // this kicks off playback
int32_t GetCurrentUIDValidity();
void SetCurrentUIDValidity(int32_t uidvalidity) { mCurrentUIDValidity = uidvalidity; }
void SetPseudoOffline(bool pseudoOffline) {m_pseudoOffline = pseudoOffline;}
bool ProcessingStaleFolderUpdate() { return m_singleFolderToUpdate != nullptr; }
bool CreateOfflineFolder(nsIMsgFolder *folder);
void SetWindow(nsIMsgWindow *window);
protected:
virtual ~nsImapOfflineSync();
bool CreateOfflineFolders();
bool DestFolderOnSameServer(nsIMsgFolder *destFolder);
bool AdvanceToNextServer();
bool AdvanceToNextFolder();
void AdvanceToFirstIMAPFolder();
void DeleteAllOfflineOpsForCurrentDB();
void ClearCurrentOps();
// Clears m_currentDB, and unregister listener.
void ClearDB();
void ProcessFlagOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessKeywordOperation(nsIMsgOfflineImapOperation *op);
void ProcessMoveOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessEmptyTrash();
void ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp,
nsOfflineImapOperationType opType);
nsCOMPtr <nsIMsgFolder> m_currentFolder;
nsCOMPtr <nsIMsgFolder> m_singleFolderToUpdate;
nsCOMPtr <nsIMsgWindow> m_window;
nsCOMPtr <nsIArray> m_allServers;
nsCOMPtr <nsIArray> m_allFolders;
nsCOMPtr <nsIMsgIncomingServer> m_currentServer;
nsCOMPtr <nsISimpleEnumerator> m_serverEnumerator;
nsCOMPtr <nsIFile> m_curTempFile;
nsTArray<nsMsgKey> m_CurrentKeys;
nsCOMArray<nsIMsgOfflineImapOperation> m_currentOpsToClear;
uint32_t m_KeyIndex;
nsCOMPtr <nsIMsgDatabase> m_currentDB;
nsCOMPtr <nsIUrlListener> m_listener;
int32_t mCurrentUIDValidity;
int32_t mCurrentPlaybackOpType; // kFlagsChanged -> kMsgCopy -> kMsgMoved
bool m_mailboxupdatesStarted;
bool m_mailboxupdatesFinished;
bool m_pseudoOffline; // for queueing online events in offline db
bool m_createdOfflineFolders;
};
class nsImapOfflineDownloader : public nsImapOfflineSync
{
public:
nsImapOfflineDownloader(nsIMsgWindow *window, nsIUrlListener *listener);
virtual ~nsImapOfflineDownloader();
virtual nsresult ProcessNextOperation() override; // this kicks off download
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,764 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapProtocol_h___
#define nsImapProtocol_h___
#include "mozilla/Attributes.h"
#include "nsIImapProtocol.h"
#include "nsIImapUrl.h"
#include "nsMsgProtocol.h"
#include "nsIStreamListener.h"
#include "nsIAsyncOutputStream.h"
#include "nsIAsyncInputStream.h"
#include "nsImapCore.h"
#include "nsStringGlue.h"
#include "nsIProgressEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsISocketTransport.h"
#include "nsIInputStreamPump.h"
// imap event sinks
#include "nsIImapMailFolderSink.h"
#include "nsIImapServerSink.h"
#include "nsIImapMessageSink.h"
// UI Thread proxy helper
#include "nsIImapProtocolSink.h"
#include "nsImapServerResponseParser.h"
#include "nsImapFlagAndUidState.h"
#include "nsIMAPNamespace.h"
#include "nsTArray.h"
#include "nsWeakPtr.h"
#include "nsMsgLineBuffer.h" // we need this to use the nsMsgLineStreamBuffer helper class...
#include "nsIInputStream.h"
#include "nsIMsgIncomingServer.h"
#include "nsCOMArray.h"
#include "nsIThread.h"
#include "nsIRunnable.h"
#include "nsIImapMockChannel.h"
#include "nsILoadGroup.h"
#include "nsCOMPtr.h"
#include "nsIImapIncomingServer.h"
#include "nsIMsgWindow.h"
#include "nsIImapHeaderXferInfo.h"
#include "nsMsgLineBuffer.h"
#include "nsIAsyncInputStream.h"
#include "nsITimer.h"
#include "nsAutoPtr.h"
#include "nsIMsgFolder.h"
#include "nsIMsgAsyncPrompter.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsSyncRunnableHelpers.h"
#include "nsICacheEntryOpenCallback.h"
class nsIMAPMessagePartIDArray;
class nsIMsgIncomingServer;
class nsIPrefBranch;
class nsIMAPMailboxInfo;
#define kDownLoadCacheSize 16000 // was 1536 - try making it bigger
typedef struct _msg_line_info {
const char *adoptedMessageLine;
uint32_t uidOfMessage;
} msg_line_info;
class nsMsgImapLineDownloadCache : public nsIImapHeaderInfo, public nsByteArray
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPHEADERINFO
nsMsgImapLineDownloadCache();
uint32_t CurrentUID();
uint32_t SpaceAvailable();
bool CacheEmpty();
msg_line_info *GetCurrentLineInfo();
private:
virtual ~nsMsgImapLineDownloadCache();
msg_line_info *fLineInfo;
int32_t m_msgSize;
};
#define kNumHdrsToXfer 10
class nsMsgImapHdrXferInfo : public nsIImapHeaderXferInfo
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPHEADERXFERINFO
nsMsgImapHdrXferInfo();
void ResetAll(); // reset HeaderInfos for re-use
void ReleaseAll(); // release HeaderInfos (frees up memory)
// this will return null if we're full, in which case the client code
// should transfer the headers and retry.
nsIImapHeaderInfo *StartNewHdr();
// call when we've finished adding lines to current hdr
void FinishCurrentHdr();
private:
virtual ~nsMsgImapHdrXferInfo();
nsCOMArray<nsIImapHeaderInfo> m_hdrInfos;
int32_t m_nextFreeHdrInfo;
};
// State Flags (Note, I use the word state in terms of storing
// state information about the connection (authentication, have we sent
// commands, etc. I do not intend it to refer to protocol state)
// Use these flags in conjunction with SetFlag/TestFlag/ClearFlag instead
// of creating PRBools for everything....
#define IMAP_RECEIVED_GREETING 0x00000001 /* should we pause for the next read */
#define IMAP_CONNECTION_IS_OPEN 0x00000004 /* is the connection currently open? */
#define IMAP_WAITING_FOR_DATA 0x00000008
#define IMAP_CLEAN_UP_URL_STATE 0x00000010 // processing clean up url state
#define IMAP_ISSUED_LANGUAGE_REQUEST 0x00000020 // make sure we only issue the language request once per connection...
#define IMAP_ISSUED_COMPRESS_REQUEST 0x00000040 // make sure we only request compression once
class nsImapProtocol : public nsIImapProtocol,
public nsIRunnable,
public nsIInputStreamCallback,
public nsSupportsWeakReference,
public nsMsgProtocol,
public nsIImapProtocolSink,
public nsIMsgAsyncPromptListener
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAMCALLBACK
nsImapProtocol();
virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
uint64_t sourceOffset, uint32_t length) override;
// nsIRunnable method
NS_IMETHOD Run() override;
//////////////////////////////////////////////////////////////////////////////////
// we support the nsIImapProtocol interface
//////////////////////////////////////////////////////////////////////////////////
NS_DECL_NSIIMAPPROTOCOL
//////////////////////////////////////////////////////////////////////////////////
// we support the nsIImapProtocolSink interface
//////////////////////////////////////////////////////////////////////////////////
NS_DECL_NSIIMAPPROTOCOLSINK
NS_DECL_NSIMSGASYNCPROMPTLISTENER
// message id string utilities.
uint32_t CountMessagesInIdString(const char *idString);
static bool HandlingMultipleMessages(const nsCString &messageIdString);
// escape slashes and double quotes in username/passwords for insecure login.
static void EscapeUserNamePasswordString(const char *strToEscape, nsCString *resultStr);
// used to start fetching a message.
void GetShouldDownloadAllHeaders(bool *aResult);
void GetArbitraryHeadersToDownload(nsCString &aResult);
virtual void AdjustChunkSize();
virtual void FetchMessage(const nsCString &messageIds,
nsIMAPeFetchFields whatToFetch,
const char *fetchModifier = nullptr,
uint32_t startByte = 0, uint32_t numBytes = 0,
char *part = 0);
void FetchTryChunking(const nsCString &messageIds,
nsIMAPeFetchFields whatToFetch,
bool idIsUid,
char *part,
uint32_t downloadSize,
bool tryChunking);
virtual void PipelinedFetchMessageParts(nsCString &uid, nsIMAPMessagePartIDArray *parts);
void FallbackToFetchWholeMsg(const nsCString &messageId, uint32_t messageSize);
// used when streaming a message fetch
virtual nsresult BeginMessageDownLoad(uint32_t totalSize, // for user, headers and body
const char *contentType); // some downloads are header only
virtual void HandleMessageDownLoadLine(const char *line, bool isPartialLine, char *lineCopy=nullptr);
virtual void NormalMessageEndDownload();
virtual void AbortMessageDownLoad();
virtual void PostLineDownLoadEvent(const char *line, uint32_t uid);
void FlushDownloadCache();
virtual void SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status);
virtual EMailboxDiscoverStatus GetMailboxDiscoveryStatus();
virtual void ProcessMailboxUpdate(bool handlePossibleUndo);
// Send log output...
void Log(const char *logSubName, const char *extraInfo, const char *logData);
static void LogImapUrl(const char *logMsg, nsIImapUrl *imapUrl);
// Comment from 4.5: We really need to break out the thread synchronizer from the
// connection class...Not sure what this means
bool GetPseudoInterrupted();
void PseudoInterrupt(bool the_interrupt);
uint32_t GetMessageSize(const char * messageId, bool idsAreUids);
bool GetSubscribingNow();
bool DeathSignalReceived();
void ResetProgressInfo();
void SetActive(bool active);
bool GetActive();
bool GetShowAttachmentsInline();
// Sets whether or not the content referenced by the current ActiveEntry has been modified.
// Used for MIME parts on demand.
void SetContentModified(IMAP_ContentModifiedType modified);
bool GetShouldFetchAllParts();
bool GetIgnoreExpunges() {return m_ignoreExpunges;}
// Generic accessors required by the imap parser
char * CreateNewLineFromSocket();
nsresult GetConnectionStatus();
void SetConnectionStatus(nsresult status);
// Cleanup the connection and shutdown the thread.
void TellThreadToDie();
const nsCString& GetImapHostName(); // return the host name from the url for the
// current connection
const nsCString& GetImapUserName(); // return the user name from the identity
const char* GetImapServerKey(); // return the user name from the incoming server;
// state set by the imap parser...
void NotifyMessageFlags(imapMessageFlagsType flags, const nsACString &keywords,
nsMsgKey key, uint64_t highestModSeq);
void NotifySearchHit(const char * hitLine);
// Event handlers for the imap parser.
void DiscoverMailboxSpec(nsImapMailboxSpec * adoptedBoxSpec);
void AlertUserEventUsingName(const char* aMessageId);
void AlertUserEvent(const char * message);
void AlertUserEventFromServer(const char * aServerEvent);
void ProgressEventFunctionUsingName(const char* aMsgId);
void ProgressEventFunctionUsingNameWithString(const char* aMsgName, const char *
aExtraInfo);
void PercentProgressUpdateEvent(char16_t *message, int64_t currentProgress, int64_t maxProgress);
void ShowProgress();
// utility function calls made by the server
void Copy(const char * messageList, const char *destinationMailbox,
bool idsAreUid);
void Search(const char * searchCriteria, bool useUID,
bool notifyHit = true);
// imap commands issued by the parser
void Store(const nsCString &aMessageList, const char * aMessageData, bool
aIdsAreUid);
void ProcessStoreFlags(const nsCString &messageIds,
bool idsAreUids,
imapMessageFlagsType flags,
bool addFlags);
void IssueUserDefinedMsgCommand(const char *command, const char * messageList);
void FetchMsgAttribute(const nsCString &messageIds, const nsCString &attribute);
void Expunge();
void UidExpunge(const nsCString &messageSet);
void Close(bool shuttingDown = false, bool waitForResponse = true);
void Check();
void SelectMailbox(const char *mailboxName);
// more imap commands
void Logout(bool shuttingDown = false, bool waitForResponse = true);
void Noop();
void XServerInfo();
void Netscape();
void XMailboxInfo(const char *mailboxName);
void XAOL_Option(const char *option);
void MailboxData();
void GetMyRightsForFolder(const char *mailboxName);
void Bodystructure(const nsCString &messageId, bool idIsUid);
void PipelinedFetchMessageParts(const char *uid, nsIMAPMessagePartIDArray *parts);
// this function does not ref count!!! be careful!!!
nsIImapUrl *GetCurrentUrl() {return m_runningUrl;}
// acl and namespace stuff
// notifies libmsg that we have a new personal/default namespace that we're using
void CommitNamespacesForHostEvent();
// notifies libmsg that we have new capability data for the current host
void CommitCapability();
// Adds a set of rights for a given user on a given mailbox on the current host.
// if userName is NULL, it means "me," or MYRIGHTS.
// rights is a single string of rights, as specified by RFC2086, the IMAP ACL extension.
void AddFolderRightsForUser(const char *mailboxName, const char *userName, const char *rights);
// Clears all rights for the current folder, for all users.
void ClearAllFolderRights();
void RefreshFolderACLView(const char *mailboxName, nsIMAPNamespace *nsForMailbox);
nsresult SetFolderAdminUrl(const char *mailboxName);
void HandleMemoryFailure();
void HandleCurrentUrlError();
// UIDPLUS extension
void SetCopyResponseUid(const char* msgIdString);
// Quota support
void UpdateFolderQuotaData(nsCString& aQuotaRoot, uint32_t aUsed, uint32_t aMax);
bool GetPreferPlainText() { return m_preferPlainText; }
int32_t GetCurFetchSize() { return m_curFetchSize; }
const nsString &GetEmptyMimePartString() { return m_emptyMimePartString; }
private:
virtual ~nsImapProtocol();
// the following flag is used to determine when a url is currently being run. It is cleared when we
// finish processng a url and it is set whenever we call Load on a url
bool m_urlInProgress;
nsCOMPtr<nsIImapUrl> m_runningUrl; // the nsIImapURL that is currently running
nsImapAction m_imapAction; // current imap action associated with this connnection...
nsCString m_hostName;
nsCString m_userName;
nsCString m_serverKey;
nsCString m_realHostName;
char *m_dataOutputBuf;
nsMsgLineStreamBuffer * m_inputStreamBuffer;
uint32_t m_allocatedSize; // allocated size
uint32_t m_totalDataSize; // total data size
uint32_t m_curReadIndex; // current read index
nsCString m_trashFolderName;
// Ouput stream for writing commands to the socket
nsCOMPtr<nsISocketTransport> m_transport;
nsCOMPtr<nsIAsyncInputStream> m_channelInputStream;
nsCOMPtr<nsIAsyncOutputStream> m_channelOutputStream;
nsCOMPtr<nsIImapMockChannel> m_mockChannel; // this is the channel we should forward to people
uint32_t m_bytesToChannel;
bool m_fetchingWholeMessage;
//nsCOMPtr<nsIRequest> mAsyncReadRequest; // we're going to cancel this when we're done with the conn.
// ******* Thread support *******
nsCOMPtr<nsIThread> m_iThread;
PRThread *m_thread;
mozilla::ReentrantMonitor m_dataAvailableMonitor; // used to notify the arrival of data from the server
mozilla::ReentrantMonitor m_urlReadyToRunMonitor; // used to notify the arrival of a new url to be processed
mozilla::ReentrantMonitor m_pseudoInterruptMonitor;
mozilla::ReentrantMonitor m_dataMemberMonitor;
mozilla::ReentrantMonitor m_threadDeathMonitor;
mozilla::ReentrantMonitor m_waitForBodyIdsMonitor;
mozilla::ReentrantMonitor m_fetchBodyListMonitor;
mozilla::ReentrantMonitor m_passwordReadyMonitor;
mozilla::Mutex mLock;
// If we get an async password prompt, this is where the UI thread
// stores the password, before notifying the imap thread of the password
// via the m_passwordReadyMonitor.
nsCString m_password;
// Set to the result of nsImapServer::PromptPassword
nsresult m_passwordStatus;
bool m_imapThreadIsRunning;
void ImapThreadMainLoop(void);
nsresult m_connectionStatus;
nsCString m_connectionType;
bool m_nextUrlReadyToRun;
nsWeakPtr m_server;
RefPtr<ImapMailFolderSinkProxy> m_imapMailFolderSink;
RefPtr<ImapMessageSinkProxy> m_imapMessageSink;
RefPtr<ImapServerSinkProxy> m_imapServerSink;
RefPtr<ImapProtocolSinkProxy> m_imapProtocolSink;
// helper function to setup imap sink interface proxies
nsresult SetupSinkProxy();
// End thread support stuff
bool GetDeleteIsMoveToTrash();
bool GetShowDeletedMessages();
nsCString m_currentCommand;
nsImapServerResponseParser m_parser;
nsImapServerResponseParser& GetServerStateParser() { return m_parser; }
void HandleIdleResponses();
virtual bool ProcessCurrentURL();
void EstablishServerConnection();
virtual void ParseIMAPandCheckForNewMail(const char* commandString =
nullptr, bool ignoreBadNOResponses = false);
// biff
void PeriodicBiff();
void SendSetBiffIndicatorEvent(nsMsgBiffState newState);
bool CheckNewMail();
// folder opening and listing header functions
void FolderHeaderDump(uint32_t *msgUids, uint32_t msgCount);
void FolderMsgDump(uint32_t *msgUids, uint32_t msgCount, nsIMAPeFetchFields fields);
void FolderMsgDumpLoop(uint32_t *msgUids, uint32_t msgCount, nsIMAPeFetchFields fields);
void WaitForPotentialListOfBodysToFetch(uint32_t **msgIdList, uint32_t &msgCount);
void HeaderFetchCompleted();
void UploadMessageFromFile(nsIFile* file, const char* mailboxName, PRTime date,
imapMessageFlagsType flags, nsCString &keywords);
// mailbox name utilities.
void CreateEscapedMailboxName(const char *rawName, nsCString &escapedName);
void SetupMessageFlagsString(nsCString & flagString,
imapMessageFlagsType flags,
uint16_t userFlags);
// body fetching listing data
bool m_fetchBodyListIsNew;
uint32_t m_fetchBodyCount;
uint32_t *m_fetchBodyIdList;
// initialization function given a new url and transport layer
nsresult SetupWithUrl(nsIURI * aURL, nsISupports* aConsumer);
void ReleaseUrlState(bool rerunningUrl); // release any state that is stored on a per action basis.
/**
* Last ditch effort to run the url without using an imap connection.
* If it turns out that we don't need to run the url at all (e.g., we're
* trying to download a single message for offline use and it has already
* been downloaded, this function will send the appropriate notifications.
*
* @returns true if the url has been run locally, or doesn't need to be run.
*/
bool TryToRunUrlLocally(nsIURI *aURL, nsISupports *aConsumer);
////////////////////////////////////////////////////////////////////////////////////////
// Communication methods --> Reading and writing protocol
////////////////////////////////////////////////////////////////////////////////////////
// SendData not only writes the NULL terminated data in dataBuffer to our output stream
// but it also informs the consumer that the data has been written to the stream.
// aSuppressLogging --> set to true if you wish to suppress logging for this particular command.
// this is useful for making sure we don't log authenication information like the user's password (which was
// encoded anyway), but still we shouldn't add that information to the log.
nsresult SendData(const char * dataBuffer, bool aSuppressLogging = false) override;
// state ported over from 4.5
bool m_pseudoInterrupted;
bool m_active;
bool m_folderNeedsSubscribing;
bool m_folderNeedsACLRefreshed;
bool m_threadShouldDie;
// use to prevent re-entering TellThreadToDie.
bool m_inThreadShouldDie;
// if the UI thread has signalled the IMAP thread to die, and the
// connection has timed out, this will be set to FALSE.
bool m_safeToCloseConnection;
nsImapFlagAndUidState *m_flagState;
nsMsgBiffState m_currentBiffState;
// manage the IMAP server command tags
// 11 = enough memory for the decimal representation of MAX_UINT + trailing nul
char m_currentServerCommandTag[11];
uint32_t m_currentServerCommandTagNumber;
void IncrementCommandTagNumber();
const char *GetServerCommandTag();
void StartTLS();
// login related methods.
nsresult GetPassword(nsCString &password, bool aNewPasswordRequested);
void InitPrefAuthMethods(int32_t authMethodPrefValue,
nsIMsgIncomingServer *aServer);
nsresult ChooseAuthMethod();
void MarkAuthMethodAsFailed(eIMAPCapabilityFlags failedAuthMethod);
void ResetAuthMethods();
// All of these methods actually issue protocol
void Capability(); // query host for capabilities.
void ID(); // send RFC 2971 app info to server
void EnableCondStore();
void StartCompressDeflate();
nsresult BeginCompressing();
void Language(); // set the language on the server if it supports it
void Namespace();
void InsecureLogin(const char *userName, const nsCString &password);
nsresult AuthLogin(const char *userName, const nsCString &password, eIMAPCapabilityFlag flag);
void ProcessAuthenticatedStateURL();
void ProcessAfterAuthenticated();
void ProcessSelectedStateURL();
bool TryToLogon();
// Process Authenticated State Url used to be one giant if statement. I've broken out a set of actions
// based on the imap action passed into the url. The following functions are imap protocol handlers for
// each action. They are called by ProcessAuthenticatedStateUrl.
void OnLSubFolders();
void OnAppendMsgFromFile();
char *GetFolderPathString(); // OK to call from UI thread
char * OnCreateServerSourceFolderPathString();
char * OnCreateServerDestinationFolderPathString();
nsresult CreateServerSourceFolderPathString(char **result);
void OnCreateFolder(const char * aSourceMailbox);
void OnEnsureExistsFolder(const char * aSourceMailbox);
void OnSubscribe(const char * aSourceMailbox);
void OnUnsubscribe(const char * aSourceMailbox);
void RefreshACLForFolderIfNecessary(const char * mailboxName);
void RefreshACLForFolder(const char * aSourceMailbox);
void GetACLForFolder(const char *aMailboxName);
void OnRefreshAllACLs();
void OnListFolder(const char * aSourceMailbox, bool aBool);
void OnStatusForFolder(const char * sourceMailbox);
void OnDeleteFolder(const char * aSourceMailbox);
void OnRenameFolder(const char * aSourceMailbox);
void OnMoveFolderHierarchy(const char * aSourceMailbox);
void DeleteFolderAndMsgs(const char * aSourceMailbox);
void RemoveMsgsAndExpunge();
void FindMailboxesIfNecessary();
void CreateMailbox(const char *mailboxName);
void DeleteMailbox(const char *mailboxName);
void RenameMailbox(const char *existingName, const char *newName);
void RemoveHierarchyDelimiter(nsCString &mailboxName);
bool CreateMailboxRespectingSubscriptions(const char *mailboxName);
bool DeleteMailboxRespectingSubscriptions(const char *mailboxName);
bool RenameMailboxRespectingSubscriptions(const char *existingName,
const char *newName,
bool reallyRename);
// notify the fe that a folder was deleted
void FolderDeleted(const char *mailboxName);
// notify the fe that a folder creation failed
void FolderNotCreated(const char *mailboxName);
// notify the fe that a folder was deleted
void FolderRenamed(const char *oldName,
const char *newName);
bool FolderIsSelected(const char *mailboxName);
bool MailboxIsNoSelectMailbox(const char *mailboxName);
nsCString CreatePossibleTrashName(const char *prefix);
bool FolderNeedsACLInitialized(const char *folderName);
void DiscoverMailboxList();
void DiscoverAllAndSubscribedBoxes();
void MailboxDiscoveryFinished();
void NthLevelChildList(const char *onlineMailboxPrefix, int32_t depth);
// LIST SUBSCRIBED command (from RFC 5258) crashes some servers. so we need to
// identify those servers
bool GetListSubscribedIsBrokenOnServer();
bool IsExtraSelectNeeded();
void Lsub(const char *mailboxPattern, bool addDirectoryIfNecessary);
void List(const char *mailboxPattern, bool addDirectoryIfNecessary,
bool useXLIST = false);
void Subscribe(const char *mailboxName);
void Unsubscribe(const char *mailboxName);
void Idle();
void EndIdle(bool waitForResponse = true);
// Some imap servers include the mailboxName following the dir-separator in the list of
// subfolders of the mailboxName. In fact, they are the same. So we should decide if
// we should delete such subfolder and provide feedback if the delete operation succeed.
bool DeleteSubFolders(const char* aMailboxName, bool & aDeleteSelf);
bool RenameHierarchyByHand(const char *oldParentMailboxName,
const char *newParentMailboxName);
bool RetryUrl();
nsresult GlobalInitialization(nsIPrefBranch *aPrefBranch);
nsresult Configure(int32_t TooFastTime, int32_t IdealTime,
int32_t ChunkAddSize, int32_t ChunkSize, int32_t ChunkThreshold,
bool FetchByChunks);
nsresult GetMsgWindow(nsIMsgWindow ** aMsgWindow);
// End Process AuthenticatedState Url helper methods
virtual char const *GetType() override {return "imap";}
// Quota support
void GetQuotaDataIfSupported(const char *aBoxName);
// CondStore support - true if server supports it, and the user hasn't disabled it.
bool UseCondStore();
// false if pref "mail.server.serverxxx.use_condstore" is false;
bool m_useCondStore;
// COMPRESS=DEFLATE support - true if server supports it, and the user hasn't disabled it.
bool UseCompressDeflate();
// false if pref "mail.server.serverxxx.use_compress_deflate" is false;
bool m_useCompressDeflate;
// these come from the nsIDBFolderInfo in the msgDatabase and
// are initialized in nsImapProtocol::SetupWithUrl.
uint64_t mFolderLastModSeq;
int32_t mFolderTotalMsgCount;
uint32_t mFolderHighestUID;
uint32_t mFolderNumDeleted;
bool m_isGmailServer;
nsTArray<nsCString> mCustomDBHeaders;
nsTArray<nsCString> mCustomHeaders;
bool m_trackingTime;
PRTime m_startTime;
PRTime m_endTime;
PRTime m_lastActiveTime;
int32_t m_tooFastTime;
int32_t m_idealTime;
int32_t m_chunkAddSize;
int32_t m_chunkStartSize;
bool m_fetchByChunks;
bool m_sendID;
int32_t m_curFetchSize;
bool m_ignoreExpunges;
eIMAPCapabilityFlags m_prefAuthMethods; // set of capability flags (in nsImapCore.h) for auth methods
eIMAPCapabilityFlags m_failedAuthMethods; // ditto
eIMAPCapabilityFlag m_currentAuthMethod; // exactly one capability flag, or 0
int32_t m_socketType;
int32_t m_chunkSize;
int32_t m_chunkThreshold;
RefPtr<nsMsgImapLineDownloadCache> m_downloadLineCache;
RefPtr<nsMsgImapHdrXferInfo> m_hdrDownloadCache;
nsCOMPtr <nsIImapHeaderInfo> m_curHdrInfo;
// mapping between mailboxes and the corresponding folder flags
nsDataHashtable<nsCStringHashKey, int32_t> m_standardListMailboxes;
// mapping between special xlist mailboxes and the corresponding folder flags
nsDataHashtable<nsCStringHashKey, int32_t> m_specialXListMailboxes;
nsIImapHostSessionList * m_hostSessionList;
bool m_fromHeaderSeen;
// these settings allow clients to override various pieces of the connection info from the url
bool m_overRideUrlConnectionInfo;
nsCString m_logonHost;
nsCString m_logonCookie;
int16_t m_logonPort;
nsString mAcceptLanguages;
// progress stuff
void SetProgressString(const char* stringName);
nsString m_progressString;
nsCString m_progressStringName;
int32_t m_progressIndex;
int32_t m_progressCount;
nsCString m_lastProgressStringName;
int32_t m_lastPercent;
int64_t m_lastProgressTime;
bool m_notifySearchHit;
bool m_checkForNewMailDownloadsHeaders;
bool m_needNoop;
bool m_idle;
bool m_useIdle;
int32_t m_noopCount;
bool m_autoSubscribe, m_autoUnsubscribe, m_autoSubscribeOnOpen;
bool m_closeNeededBeforeSelect;
bool m_retryUrlOnError;
bool m_preferPlainText;
nsCString m_forceSelectValue;
bool m_forceSelect;
int32_t m_uidValidity; // stored uid validity for the selected folder.
enum EMailboxHierarchyNameState {
kNoOperationInProgress,
kDiscoverBaseFolderInProgress,
kDiscoverTrashFolderInProgress,
kDeleteSubFoldersInProgress,
kListingForInfoOnly,
kListingForInfoAndDiscovery,
kDiscoveringNamespacesOnly,
kXListing,
kListingForFolderFlags,
kListingForCreate
};
EMailboxHierarchyNameState m_hierarchyNameState;
EMailboxDiscoverStatus m_discoveryStatus;
nsTArray<nsIMAPMailboxInfo*> m_listedMailboxList;
nsTArray<char*> * m_deletableChildren;
uint32_t m_flagChangeCount;
PRTime m_lastCheckTime;
bool CheckNeeded();
nsString m_emptyMimePartString;
RefPtr<mozilla::mailnews::OAuth2ThreadHelper> mOAuth2Support;
};
// This small class is a "mock" channel because it is a mockery of the imap channel's implementation...
// it's a light weight channel that we can return to necko when they ask for a channel on a url before
// we actually have an imap protocol instance around which can run the url. Please see my comments in
// nsIImapMockChannel.idl for more details..
//
// Threading concern: This class lives entirely in the UI thread.
class nsICacheEntry;
class nsImapMockChannel : public nsIImapMockChannel
, public nsICacheEntryOpenCallback
, public nsITransportEventSink
, public nsSupportsWeakReference
{
public:
friend class nsImapProtocol;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPMOCKCHANNEL
NS_DECL_NSICHANNEL
NS_DECL_NSIREQUEST
NS_DECL_NSICACHEENTRYOPENCALLBACK
NS_DECL_NSITRANSPORTEVENTSINK
nsImapMockChannel();
static nsresult Create (const nsIID& iid, void **result);
nsresult RunOnStopRequestFailure();
protected:
virtual ~nsImapMockChannel();
nsCOMPtr <nsIURI> m_url;
nsCOMPtr<nsIURI> m_originalUrl;
nsCOMPtr<nsILoadGroup> m_loadGroup;
nsCOMPtr<nsILoadInfo> m_loadInfo;
nsCOMPtr<nsIStreamListener> m_channelListener;
nsISupports * m_channelContext;
nsresult m_cancelStatus;
nsLoadFlags mLoadFlags;
nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIRequest> mCacheRequest; // the request associated with a read from the cache
nsCString mContentType;
nsCString mCharset;
nsWeakPtr mProtocol;
bool mChannelClosed;
bool mReadingFromCache;
bool mTryingToReadPart;
int64_t mContentLength;
// cache related helper methods
nsresult OpenCacheEntry(); // makes a request to the cache service for a cache entry for a url
bool ReadFromLocalCache(); // attempts to read the url out of our local (offline) cache....
nsresult ReadFromImapConnection(); // creates a new imap connection to read the url
nsresult ReadFromMemCache(nsICacheEntry *entry); // attempts to read the url out of our memory cache
nsresult NotifyStartEndReadFromCache(bool start);
// we end up daisy chaining multiple nsIStreamListeners into the load process.
nsresult SetupPartExtractorListener(nsIImapUrl * aUrl, nsIStreamListener * aConsumer);
};
// This class contains the name of a mailbox and whether or not
// its children have been listed.
class nsIMAPMailboxInfo
{
public:
nsIMAPMailboxInfo(const nsACString &aName, char aDelimiter);
virtual ~nsIMAPMailboxInfo();
void SetChildrenListed(bool childrenListed);
bool GetChildrenListed();
const nsACString& GetMailboxName();
char GetDelimiter();
protected:
nsCString mMailboxName;
bool mChildrenListed;
char mDelimiter;
};
#endif // nsImapProtocol_h___

View file

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h" // for pre-compiled headers
#include "nsImapCore.h"
#include "nsImapSearchResults.h"
#include "prmem.h"
#include "nsCRT.h"
nsImapSearchResultSequence::nsImapSearchResultSequence()
{
}
nsImapSearchResultSequence *nsImapSearchResultSequence::CreateSearchResultSequence()
{
return new nsImapSearchResultSequence;
}
void nsImapSearchResultSequence::Clear(void)
{
int32_t i = Length();
while (0 <= --i)
{
char* string = ElementAt(i);
PR_Free(string);
}
nsTArray<char*>::Clear();
}
nsImapSearchResultSequence::~nsImapSearchResultSequence()
{
Clear();
}
void nsImapSearchResultSequence::ResetSequence()
{
Clear();
}
void nsImapSearchResultSequence::AddSearchResultLine(const char *searchLine)
{
// The first add becomes node 2. Fix this.
char *copiedSequence = PL_strdup(searchLine + 9); // 9 == "* SEARCH "
if (copiedSequence) // if we can't allocate this then the search won't hit
AppendElement(copiedSequence);
}
nsImapSearchResultIterator::nsImapSearchResultIterator(nsImapSearchResultSequence &sequence) :
fSequence(sequence)
{
ResetIterator();
}
nsImapSearchResultIterator::~nsImapSearchResultIterator()
{
}
void nsImapSearchResultIterator::ResetIterator()
{
fSequenceIndex = 0;
fCurrentLine = (char *) fSequence.SafeElementAt(fSequenceIndex);
fPositionInCurrentLine = fCurrentLine;
}
int32_t nsImapSearchResultIterator::GetNextMessageNumber()
{
int32_t returnValue = 0;
if (fPositionInCurrentLine)
{
returnValue = atoi(fPositionInCurrentLine);
// eat the current number
while (isdigit(*++fPositionInCurrentLine))
;
if (*fPositionInCurrentLine == 0xD) // found CR, no more digits on line
{
fCurrentLine = (char *) fSequence.SafeElementAt(++fSequenceIndex);
fPositionInCurrentLine = fCurrentLine;
}
else // eat the space
fPositionInCurrentLine++;
}
return returnValue;
}

View file

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapSearchResults_h___
#define nsImapSearchResults_h___
#include "nsTArray.h"
class nsImapSearchResultSequence : public nsTArray<char*>
{
public:
virtual ~nsImapSearchResultSequence();
static nsImapSearchResultSequence *CreateSearchResultSequence();
virtual void AddSearchResultLine(const char *searchLine);
virtual void ResetSequence();
void Clear();
friend class nsImapSearchResultIterator;
private:
nsImapSearchResultSequence();
};
class nsImapSearchResultIterator {
public:
nsImapSearchResultIterator(nsImapSearchResultSequence &sequence);
virtual ~nsImapSearchResultIterator();
void ResetIterator();
int32_t GetNextMessageNumber(); // returns 0 at end of list
private:
nsImapSearchResultSequence &fSequence;
int32_t fSequenceIndex;
char *fCurrentLine;
char *fPositionInCurrentLine;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsIMAPServerResponseParser_H_
#define _nsIMAPServerResponseParser_H_
#include "mozilla/Attributes.h"
#include "nsIMAPHostSessionList.h"
#include "nsImapSearchResults.h"
#include "nsStringGlue.h"
#include "MailNewsTypes.h"
#include "nsTArray.h"
#include "nsImapUtils.h"
#include "nsAutoPtr.h"
class nsIMAPNamespace;
class nsIMAPNamespaceList;
class nsIMAPBodyShell;
class nsIMAPBodypart;
class nsImapSearchResultIterator;
class nsImapFlagAndUidState;
class nsCString;
#include "nsIMAPGenericParser.h"
class nsImapServerResponseParser : public nsIMAPGenericParser
{
public:
nsImapServerResponseParser(nsImapProtocol &imapConnection);
virtual ~nsImapServerResponseParser();
// Overridden from the base parser class
virtual bool LastCommandSuccessful() override;
virtual void HandleMemoryFailure() override;
// aignoreBadAndNOResponses --> don't throw a error dialog if this command results in a NO or Bad response
// from the server..in other words the command is "exploratory" and we don't really care if it succeeds or fails.
// This value is typically FALSE for almost all cases.
virtual void ParseIMAPServerResponse(const char *aCurrentCommand,
bool aIgnoreBadAndNOResponses,
char *aGreetingWithCapability = NULL);
virtual void InitializeState();
bool CommandFailed();
void SetCommandFailed(bool failed);
enum eIMAPstate {
kNonAuthenticated,
kAuthenticated,
kFolderSelected
} ;
virtual eIMAPstate GetIMAPstate();
virtual bool WaitingForMoreClientInput() { return fWaitingForMoreClientInput; }
const char *GetSelectedMailboxName(); // can be NULL
// if we get a PREAUTH greeting from the server, initialize the parser to begin in
// the kAuthenticated state
void PreauthSetAuthenticatedState();
// these functions represent the state of the currently selected
// folder
bool CurrentFolderReadOnly();
int32_t NumberOfMessages();
int32_t NumberOfRecentMessages();
int32_t NumberOfUnseenMessages();
int32_t FolderUID();
uint32_t CurrentResponseUID();
uint32_t HighestRecordedUID();
void SetCurrentResponseUID(uint32_t uid);
bool IsNumericString(const char *string);
uint32_t SizeOfMostRecentMessage();
void SetTotalDownloadSize(int32_t newSize) { fTotalDownloadSize = newSize; }
nsImapSearchResultIterator *CreateSearchResultIterator();
void ResetSearchResultSequence() {fSearchResults->ResetSequence();}
// create a struct mailbox_spec from our info, used in
// libmsg c interface
nsImapMailboxSpec *CreateCurrentMailboxSpec(const char *mailboxName = nullptr);
// Resets the flags state.
void ResetFlagInfo();
// set this to false if you don't want to alert the user to server
// error messages
void SetReportingErrors(bool reportThem) { fReportingErrors=reportThem;}
bool GetReportingErrors() { return fReportingErrors; }
eIMAPCapabilityFlags GetCapabilityFlag() { return fCapabilityFlag; }
void SetCapabilityFlag(eIMAPCapabilityFlags capability) {fCapabilityFlag = capability;}
bool ServerHasIMAP4Rev1Capability() { return ((fCapabilityFlag & kIMAP4rev1Capability) != 0); }
bool ServerHasACLCapability() { return ((fCapabilityFlag & kACLCapability) != 0); }
bool ServerHasNamespaceCapability() { return ((fCapabilityFlag & kNamespaceCapability) != 0); }
bool ServerIsNetscape3xServer() { return fServerIsNetscape3xServer; }
bool ServerHasServerInfo() {return ((fCapabilityFlag & kXServerInfoCapability) != 0); }
bool ServerIsAOLServer() {return ((fCapabilityFlag & kAOLImapCapability) != 0); }
void SetFetchingFlags(bool aFetchFlags) { fFetchingAllFlags = aFetchFlags;}
void ResetCapabilityFlag() ;
nsCString& GetMailAccountUrl() { return fMailAccountUrl; }
const char *GetXSenderInfo() { return fXSenderInfo; }
void FreeXSenderInfo() { PR_FREEIF(fXSenderInfo); }
nsCString& GetManageListsUrl() { return fManageListsUrl; }
nsCString& GetManageFiltersUrl() {return fManageFiltersUrl;}
const char *GetManageFolderUrl() {return fFolderAdminUrl;}
nsCString &GetServerID() {return fServerIdResponse;}
// Call this when adding a pipelined command to the session
void IncrementNumberOfTaggedResponsesExpected(const char *newExpectedTag);
// Interrupt a Fetch, without really Interrupting (through netlib)
bool GetLastFetchChunkReceived();
void ClearLastFetchChunkReceived();
virtual uint16_t SupportsUserFlags() { return fSupportsUserDefinedFlags; }
virtual uint16_t SettablePermanentFlags() { return fSettablePermanentFlags;}
void SetFlagState(nsIImapFlagAndUidState *state);
bool GetDownloadingHeaders();
bool GetFillingInShell();
void UseCachedShell(nsIMAPBodyShell *cachedShell);
void SetHostSessionList(nsIImapHostSessionList *aHostSession);
char *fAuthChallenge; // the challenge returned by the server in
//response to authenticate using CRAM-MD5 or NTLM
bool fCondStoreEnabled;
bool fUseModSeq; // can use mod seq for currently selected folder
uint64_t fHighestModSeq;
protected:
virtual void flags();
virtual void envelope_data();
virtual void xaolenvelope_data();
virtual void parse_address(nsAutoCString &addressLine);
virtual void internal_date();
virtual nsresult BeginMessageDownload(const char *content_type);
virtual void response_data();
virtual void resp_text();
virtual void resp_cond_state(bool isTagged);
virtual void text_mime2();
virtual void text();
virtual void parse_folder_flags();
virtual void enable_data();
virtual void language_data();
virtual void authChallengeResponse_data();
virtual void resp_text_code();
virtual void response_done();
virtual void response_tagged();
virtual void response_fatal();
virtual void resp_cond_bye();
virtual void id_data();
virtual void mailbox_data();
virtual void numeric_mailbox_data();
virtual void capability_data();
virtual void xserverinfo_data();
virtual void xmailboxinfo_data();
virtual void namespace_data();
virtual void myrights_data(bool unsolicited);
virtual void acl_data();
virtual void bodystructure_data();
nsIMAPBodypart *bodystructure_part(char *partNum, nsIMAPBodypart *parentPart);
nsIMAPBodypart *bodystructure_leaf(char *partNum, nsIMAPBodypart *parentPart);
nsIMAPBodypart *bodystructure_multipart(char *partNum, nsIMAPBodypart *parentPart);
virtual void mime_data();
virtual void mime_part_data();
virtual void mime_header_data();
virtual void quota_data();
virtual void msg_fetch();
virtual void msg_obsolete();
virtual void msg_fetch_headers(const char *partNum);
virtual void msg_fetch_content(bool chunk, int32_t origin, const char *content_type);
virtual bool msg_fetch_quoted();
virtual bool msg_fetch_literal(bool chunk, int32_t origin);
virtual void mailbox_list(bool discoveredFromLsub);
virtual void mailbox(nsImapMailboxSpec *boxSpec);
virtual void ProcessOkCommand(const char *commandToken);
virtual void ProcessBadCommand(const char *commandToken);
virtual void PreProcessCommandToken(const char *commandToken,
const char *currentCommand);
virtual void PostProcessEndOfLine();
// Overridden from the nsIMAPGenericParser, to retrieve the next line
// from the open socket.
virtual bool GetNextLineForParser(char **nextLine) override;
// overriden to do logging
virtual void SetSyntaxError(bool error, const char *msg = nullptr) override;
private:
bool fCurrentCommandFailed;
bool fReportingErrors;
bool fCurrentFolderReadOnly;
bool fCurrentLineContainedFlagInfo;
bool fFetchingAllFlags;
bool fWaitingForMoreClientInput;
// Is the server a Netscape 3.x Messaging Server?
bool fServerIsNetscape3xServer;
bool fDownloadingHeaders;
bool fCurrentCommandIsSingleMessageFetch;
bool fGotPermanentFlags;
imapMessageFlagsType fSavedFlagInfo;
nsTArray<nsCString> fCustomFlags;
uint16_t fSupportsUserDefinedFlags;
uint16_t fSettablePermanentFlags;
int32_t fFolderUIDValidity;
int32_t fNumberOfUnseenMessages;
int32_t fNumberOfExistingMessages;
int32_t fNumberOfRecentMessages;
uint32_t fCurrentResponseUID;
uint32_t fHighestRecordedUID;
// used to handle server that sends msg size after headers
uint32_t fReceivedHeaderOrSizeForUID;
int32_t fSizeOfMostRecentMessage;
int32_t fTotalDownloadSize;
int32_t fStatusUnseenMessages;
int32_t fStatusRecentMessages;
uint32_t fStatusNextUID;
uint32_t fStatusExistingMessages;
int fNumberOfTaggedResponsesExpected;
char *fCurrentCommandTag;
nsCString fZeroLengthMessageUidString;
char *fSelectedMailboxName;
nsImapSearchResultSequence *fSearchResults;
nsCOMPtr <nsIImapFlagAndUidState> fFlagState; // NOT owned by us, it's a copy, do not destroy
eIMAPstate fIMAPstate;
eIMAPCapabilityFlags fCapabilityFlag;
nsCString fMailAccountUrl;
char *fNetscapeServerVersionString;
char *fXSenderInfo; /* changed per message download */
char *fLastAlert; /* used to avoid displaying the same alert over and over */
char *fMsgID; /* MessageID for Gmail only (X-GM-MSGID) */
char *fThreadID; /* ThreadID for Gmail only (X-GM-THRID) */
char *fLabels; /* Labels for Gmail only (X-GM-LABELS) [will include parens, removed while passing to hashTable ]*/
nsCString fManageListsUrl;
nsCString fManageFiltersUrl;
char *fFolderAdminUrl;
nsCString fServerIdResponse; // RFC
int32_t fFetchResponseIndex;
// used for aborting a fetch stream when we're pseudo-Interrupted
int32_t numberOfCharsInThisChunk;
int32_t charsReadSoFar;
bool fLastChunk;
// points to the current body shell, if any
RefPtr<nsIMAPBodyShell> m_shell;
// The connection object
nsImapProtocol &fServerConnection;
nsIImapHostSessionList *fHostSessionList;
nsTArray<nsMsgKey> fCopyResponseKeyArray;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapService_h___
#define nsImapService_h___
#include "nsIImapService.h"
#include "nsIMsgMessageService.h"
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsIProtocolHandler.h"
#include "nsIMsgProtocolInfo.h"
#include "nsIContentHandler.h"
#include "nsICacheStorage.h"
class nsIImapHostSessionList;
class nsCString;
class nsIImapUrl;
class nsIMsgFolder;
class nsIMsgStatusFeedback;
class nsIMsgIncomingServer;
class nsImapService : public nsIImapService,
public nsIMsgMessageService,
public nsIMsgMessageFetchPartService,
public nsIProtocolHandler,
public nsIMsgProtocolInfo,
public nsIContentHandler
{
public:
nsImapService();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMSGPROTOCOLINFO
NS_DECL_NSIIMAPSERVICE
NS_DECL_NSIMSGMESSAGESERVICE
NS_DECL_NSIPROTOCOLHANDLER
NS_DECL_NSIMSGMESSAGEFETCHPARTSERVICE
NS_DECL_NSICONTENTHANDLER
protected:
virtual ~nsImapService();
char GetHierarchyDelimiter(nsIMsgFolder *aMsgFolder);
nsresult GetFolderName(nsIMsgFolder *aImapFolder, nsACString &aFolderName);
// This is called by both FetchMessage and StreamMessage
nsresult GetMessageFromUrl(nsIImapUrl *aImapUrl,
nsImapAction aImapAction,
nsIMsgFolder *aImapMailFolder,
nsIImapMessageSink *aImapMessage,
nsIMsgWindow *aMsgWindow,
nsISupports *aDisplayConsumer,
bool aConvertDataToText,
nsIURI **aURL);
nsresult CreateStartOfImapUrl(const nsACString &aImapURI, // a RDF URI for the current message/folder, can be empty
nsIImapUrl **imapUrl,
nsIMsgFolder *aImapFolder,
nsIUrlListener *aUrlListener,
nsACString &urlSpec,
char &hierarchyDelimiter);
nsresult GetImapConnectionAndLoadUrl(nsIImapUrl *aImapUrl,
nsISupports *aConsumer,
nsIURI **aURL);
nsresult SetImapUrlSink(nsIMsgFolder *aMsgFolder, nsIImapUrl *aImapUrl);
nsresult FetchMimePart(nsIImapUrl *aImapUrl,
nsImapAction aImapAction,
nsIMsgFolder *aImapMailFolder,
nsIImapMessageSink *aImapMessage,
nsIURI **aURL,
nsISupports *aDisplayConsumer,
const nsACString &messageIdentifierList,
const nsACString &mimePart);
nsresult FolderCommand(nsIMsgFolder *imapMailFolder,
nsIUrlListener *urlListener,
const char *aCommand,
nsImapAction imapAction,
nsIMsgWindow *msgWindow,
nsIURI **url);
nsresult ChangeFolderSubscription(nsIMsgFolder *folder,
const nsAString &folderName,
const char *aCommand,
nsIUrlListener *urlListener,
nsIURI **url);
nsresult DiddleFlags(nsIMsgFolder *aImapMailFolder,
nsIUrlListener *aUrlListener,
nsIURI **aURL,
const nsACString &messageIdentifierList,
const char *howToDiddle,
imapMessageFlagsType flags,
bool messageIdsAreUID);
nsresult OfflineAppendFromFile(nsIFile *aFile,
nsIURI *aUrl,
nsIMsgFolder *aDstFolder,
const nsACString &messageId, // to be replaced
bool inSelectedState, // needs to be in
nsIUrlListener *aListener,
nsIURI **aURL,
nsISupports *aCopyState);
nsresult GetServerFromUrl(nsIImapUrl *aImapUrl, nsIMsgIncomingServer **aServer);
// just a little helper method...maybe it should be a macro? which helps break down a imap message uri
// into the folder and message key equivalents
nsresult DecomposeImapURI(const nsACString &aMessageURI, nsIMsgFolder **aFolder, nsACString &msgKey);
nsresult DecomposeImapURI(const nsACString &aMessageURI, nsIMsgFolder **aFolder, nsMsgKey *msgKey);
nsCOMPtr<nsICacheStorage> mCacheStorage;
bool mPrintingOperation; // Flag for printing operations
};
#endif /* nsImapService_h___ */

View file

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "prprf.h"
#include "prmem.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "nsIStringBundle.h"
#include "nsImapStringBundle.h"
#include "nsIServiceManager.h"
#include "nsIURI.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Services.h"
#define IMAP_MSGS_URL "chrome://messenger/locale/imapMsgs.properties"
extern "C"
nsresult
IMAPGetStringByName(const char* stringName, char16_t **aString)
{
nsCOMPtr <nsIStringBundle> sBundle;
nsresult rv = IMAPGetStringBundle(getter_AddRefs(sBundle));
if (NS_SUCCEEDED(rv) && sBundle)
rv = sBundle->GetStringFromName(NS_ConvertASCIItoUTF16(stringName).get(),
aString);
return rv;
}
nsresult
IMAPGetStringBundle(nsIStringBundle **aBundle)
{
nsresult rv=NS_OK;
nsCOMPtr<nsIStringBundleService> stringService =
mozilla::services::GetStringBundleService();
if (!stringService) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIStringBundle> stringBundle;
rv = stringService->CreateBundle(IMAP_MSGS_URL, getter_AddRefs(stringBundle));
*aBundle = stringBundle;
NS_IF_ADDREF(*aBundle);
return rv;
}

View file

@ -0,0 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsImapStringBundle_H__
#define _nsImapStringBundle_H__
#include "nsIStringBundle.h"
PR_BEGIN_EXTERN_C
nsresult IMAPGetStringByName(const char* stringName, char16_t **aString);
nsresult IMAPGetStringBundle(nsIStringBundle **aBundle);
PR_END_EXTERN_C
#endif /* _nsImapStringBundle_H__ */

View file

@ -0,0 +1,751 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h" // for precompiled headers
#include "nsMsgImapCID.h"
#include "nsIMsgHdr.h"
#include "nsImapUndoTxn.h"
#include "nsIIMAPHostSessionList.h"
#include "nsIMsgIncomingServer.h"
#include "nsImapMailFolder.h"
#include "nsIDBFolderInfo.h"
#include "nsIMsgDatabase.h"
#include "nsMsgUtils.h"
#include "nsThreadUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
nsImapMoveCopyMsgTxn::nsImapMoveCopyMsgTxn() :
m_idsAreUids(false), m_isMove(false), m_srcIsPop3(false)
{
}
nsresult
nsImapMoveCopyMsgTxn::Init(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray,
const char* srcMsgIdString, nsIMsgFolder* dstFolder,
bool idsAreUids, bool isMove)
{
m_srcMsgIdString = srcMsgIdString;
m_idsAreUids = idsAreUids;
m_isMove = isMove;
m_srcFolder = do_GetWeakReference(srcFolder);
m_dstFolder = do_GetWeakReference(dstFolder);
m_srcKeyArray = *srcKeyArray;
m_dupKeyArray = *srcKeyArray;
nsCString uri;
nsresult rv = srcFolder->GetURI(uri);
nsCString protocolType(uri);
protocolType.SetLength(protocolType.FindChar(':'));
nsCOMPtr<nsIMsgDatabase> srcDB;
rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
NS_ENSURE_SUCCESS(rv, rv);
uint32_t i, count = m_srcKeyArray.Length();
nsCOMPtr<nsIMsgDBHdr> srcHdr;
nsCOMPtr<nsIMsgDBHdr> copySrcHdr;
nsCString messageId;
for (i = 0; i < count; i++)
{
rv = srcDB->GetMsgHdrForKey(m_srcKeyArray[i],
getter_AddRefs(srcHdr));
if (NS_SUCCEEDED(rv))
{
// ** jt -- only do this for mailbox protocol
if (MsgLowerCaseEqualsLiteral(protocolType, "mailbox"))
{
m_srcIsPop3 = true;
uint32_t msgSize;
rv = srcHdr->GetMessageSize(&msgSize);
if (NS_SUCCEEDED(rv))
m_srcSizeArray.AppendElement(msgSize);
if (isMove)
{
rv = srcDB->CopyHdrFromExistingHdr(nsMsgKey_None, srcHdr, false,
getter_AddRefs(copySrcHdr));
nsMsgKey pseudoKey = nsMsgKey_None;
if (NS_SUCCEEDED(rv))
{
copySrcHdr->GetMessageKey(&pseudoKey);
m_srcHdrs.AppendObject(copySrcHdr);
}
m_dupKeyArray[i] = pseudoKey;
}
}
srcHdr->GetMessageId(getter_Copies(messageId));
m_srcMessageIds.AppendElement(messageId);
}
}
return nsMsgTxn::Init();
}
nsImapMoveCopyMsgTxn::~nsImapMoveCopyMsgTxn()
{
}
NS_IMPL_ISUPPORTS_INHERITED(nsImapMoveCopyMsgTxn, nsMsgTxn, nsIUrlListener)
NS_IMETHODIMP
nsImapMoveCopyMsgTxn::UndoTransaction(void)
{
nsresult rv;
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool finishInOnStopRunningUrl = false;
if (m_isMove || !m_dstFolder)
{
if (m_srcIsPop3)
{
rv = UndoMailboxDelete();
NS_ENSURE_SUCCESS(rv, rv);
}
else
{
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder)
return rv;
nsCOMPtr<nsIUrlListener> srcListener = do_QueryInterface(srcFolder, &rv);
if (NS_FAILED(rv))
return rv;
m_onStopListener = do_GetWeakReference(srcListener);
// ** make sure we are in the selected state; use lite select
// folder so we won't hit performance hard
rv = imapService->LiteSelectFolder(srcFolder, srcListener, nullptr, nullptr);
if (NS_FAILED(rv))
return rv;
bool deletedMsgs = true; //default is true unless imapDelete model
nsMsgImapDeleteModel deleteModel;
rv = GetImapDeleteModel(srcFolder, &deleteModel);
// protect against a bogus undo txn without any source keys
// see bug #179856 for details
NS_ASSERTION(!m_srcKeyArray.IsEmpty(), "no source keys");
if (m_srcKeyArray.IsEmpty())
return NS_ERROR_UNEXPECTED;
if (!m_srcMsgIdString.IsEmpty())
{
if (NS_SUCCEEDED(rv) && deleteModel == nsMsgImapDeleteModels::IMAPDelete)
CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deletedMsgs);
if (deletedMsgs)
rv = imapService->SubtractMessageFlags(srcFolder,
this, nullptr,
m_srcMsgIdString,
kImapMsgDeletedFlag,
m_idsAreUids);
else
rv = imapService->AddMessageFlags(srcFolder,
srcListener, nullptr,
m_srcMsgIdString,
kImapMsgDeletedFlag,
m_idsAreUids);
if (NS_FAILED(rv))
return rv;
finishInOnStopRunningUrl = true;
if (deleteModel != nsMsgImapDeleteModels::IMAPDelete)
rv = imapService->GetHeaders(srcFolder, srcListener, nullptr,
m_srcMsgIdString, true);
}
}
}
if (!finishInOnStopRunningUrl && !m_dstMsgIdString.IsEmpty())
{
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
if (NS_FAILED(rv) || !dstFolder)
return rv;
nsCOMPtr<nsIUrlListener> dstListener;
dstListener = do_QueryInterface(dstFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// ** make sure we are in the selected state; use lite select folder
// so we won't potentially download a bunch of headers.
rv = imapService->LiteSelectFolder(dstFolder,
dstListener, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
rv = imapService->AddMessageFlags(dstFolder, dstListener,
nullptr, m_dstMsgIdString,
kImapMsgDeletedFlag, m_idsAreUids);
}
return rv;
}
NS_IMETHODIMP
nsImapMoveCopyMsgTxn::RedoTransaction(void)
{
nsresult rv;
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (m_isMove || !m_dstFolder)
{
if (m_srcIsPop3)
{
rv = RedoMailboxDelete();
if (NS_FAILED(rv)) return rv;
}
else if (!m_srcMsgIdString.IsEmpty())
{
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder)
return rv;
nsCOMPtr<nsIUrlListener> srcListener = do_QueryInterface(srcFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool deletedMsgs = false; //default will be false unless imapDeleteModel;
nsMsgImapDeleteModel deleteModel;
rv = GetImapDeleteModel(srcFolder, &deleteModel);
// protect against a bogus undo txn without any source keys
// see bug #179856 for details
NS_ASSERTION(!m_srcKeyArray.IsEmpty(), "no source keys");
if (m_srcKeyArray.IsEmpty())
return NS_ERROR_UNEXPECTED;
if (NS_SUCCEEDED(rv) && deleteModel == nsMsgImapDeleteModels::IMAPDelete)
rv = CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deletedMsgs);
// Make sure we are in the selected state; use lite select
// folder so performance won't suffer.
rv = imapService->LiteSelectFolder(srcFolder, srcListener, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
if (deletedMsgs)
{
rv = imapService->SubtractMessageFlags(srcFolder,
srcListener, nullptr,
m_srcMsgIdString,
kImapMsgDeletedFlag,
m_idsAreUids);
}
else
{
rv = imapService->AddMessageFlags(srcFolder,
srcListener, nullptr, m_srcMsgIdString,
kImapMsgDeletedFlag, m_idsAreUids);
}
}
}
if (!m_dstMsgIdString.IsEmpty())
{
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
if (NS_FAILED(rv) || !dstFolder) return rv;
nsCOMPtr<nsIUrlListener> dstListener;
dstListener = do_QueryInterface(dstFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// ** make sure we are in the selected state; use lite select
// folder so we won't hit performance hard
rv = imapService->LiteSelectFolder(dstFolder, dstListener, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
rv = imapService->SubtractMessageFlags(dstFolder,
dstListener, nullptr,
m_dstMsgIdString,
kImapMsgDeletedFlag,
m_idsAreUids);
NS_ENSURE_SUCCESS(rv, rv);
nsMsgImapDeleteModel deleteModel;
rv = GetImapDeleteModel(dstFolder, &deleteModel);
if (NS_FAILED(rv) || deleteModel == nsMsgImapDeleteModels::MoveToTrash)
{
rv = imapService->GetHeaders(dstFolder, dstListener,
nullptr, m_dstMsgIdString, true);
}
}
return rv;
}
nsresult
nsImapMoveCopyMsgTxn::SetCopyResponseUid(const char* aMsgIdString)
{
if (!aMsgIdString) return NS_ERROR_NULL_POINTER;
m_dstMsgIdString = aMsgIdString;
if (m_dstMsgIdString.Last() == ']')
{
int32_t len = m_dstMsgIdString.Length();
m_dstMsgIdString.SetLength(len - 1);
}
return NS_OK;
}
nsresult
nsImapMoveCopyMsgTxn::GetSrcKeyArray(nsTArray<nsMsgKey>& srcKeyArray)
{
srcKeyArray = m_srcKeyArray;
return NS_OK;
}
nsresult
nsImapMoveCopyMsgTxn::AddDstKey(nsMsgKey aKey)
{
if (!m_dstMsgIdString.IsEmpty())
m_dstMsgIdString.Append(",");
m_dstMsgIdString.AppendInt((int32_t) aKey);
return NS_OK;
}
nsresult
nsImapMoveCopyMsgTxn::UndoMailboxDelete()
{
nsresult rv = NS_ERROR_FAILURE;
// ** jt -- only do this for mailbox protocol
if (m_srcIsPop3)
{
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder) return rv;
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
if (NS_FAILED(rv) || !dstFolder) return rv;
nsCOMPtr<nsIMsgDatabase> srcDB;
nsCOMPtr<nsIMsgDatabase> dstDB;
rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
if (NS_FAILED(rv)) return rv;
rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB));
if (NS_FAILED(rv)) return rv;
uint32_t count = m_srcKeyArray.Length();
uint32_t i;
nsCOMPtr<nsIMsgDBHdr> oldHdr;
nsCOMPtr<nsIMsgDBHdr> newHdr;
for (i = 0; i < count; i++)
{
oldHdr = m_srcHdrs[i];
NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n");
rv = srcDB->CopyHdrFromExistingHdr(m_srcKeyArray[i],
oldHdr,true,
getter_AddRefs(newHdr));
NS_ASSERTION(newHdr, "fatal ... cannot create new header\n");
if (NS_SUCCEEDED(rv) && newHdr)
{
if (i < m_srcSizeArray.Length())
newHdr->SetMessageSize(m_srcSizeArray[i]);
srcDB->UndoDelete(newHdr);
}
}
srcDB->SetSummaryValid(true);
return NS_OK; // always return NS_OK
}
else
{
rv = NS_ERROR_FAILURE;
}
return rv;
}
nsresult
nsImapMoveCopyMsgTxn::RedoMailboxDelete()
{
nsresult rv = NS_ERROR_FAILURE;
if (m_srcIsPop3)
{
nsCOMPtr<nsIMsgDatabase> srcDB;
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder) return rv;
rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
if (NS_SUCCEEDED(rv))
{
srcDB->DeleteMessages(m_srcKeyArray.Length(), m_srcKeyArray.Elements(), nullptr);
srcDB->SetSummaryValid(true);
}
return NS_OK; // always return NS_OK
}
else
{
rv = NS_ERROR_FAILURE;
}
return rv;
}
nsresult nsImapMoveCopyMsgTxn::GetImapDeleteModel(nsIMsgFolder *aFolder, nsMsgImapDeleteModel *aDeleteModel)
{
nsresult rv;
nsCOMPtr<nsIMsgIncomingServer> server;
if (!aFolder)
return NS_ERROR_NULL_POINTER;
rv = aFolder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server, &rv);
if (NS_SUCCEEDED(rv) && imapServer)
rv = imapServer->GetDeleteModel(aDeleteModel);
return rv;
}
NS_IMETHODIMP nsImapMoveCopyMsgTxn::OnStartRunningUrl(nsIURI *aUrl)
{
return NS_OK;
}
NS_IMETHODIMP nsImapMoveCopyMsgTxn::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
{
nsCOMPtr<nsIUrlListener> urlListener = do_QueryReferent(m_onStopListener);
if (urlListener)
urlListener->OnStopRunningUrl(aUrl, aExitCode);
nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl);
if (imapUrl)
{
nsresult rv;
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsImapAction imapAction;
imapUrl->GetImapAction(&imapAction);
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (imapAction == nsIImapUrl::nsImapSubtractMsgFlags)
{
int32_t extraStatus;
imapUrl->GetExtraStatus(&extraStatus);
if (extraStatus != nsIImapUrl::ImapStatusNone)
{
// If subtracting the deleted flag didn't work, try
// moving the message back from the target folder to the src folder
if (!m_dstMsgIdString.IsEmpty())
imapService->OnlineMessageCopy(dstFolder,
m_dstMsgIdString,
srcFolder,
true,
true,
nullptr, /* listener */
nullptr,
nullptr,
nullptr);
else
{
// server doesn't support COPYUID, so we're going to update the dest
// folder, and when that's done, use the db to find the messages
// to move back, looking them up by message-id.
nsCOMPtr<nsIMsgImapMailFolder> imapDest = do_QueryInterface(dstFolder);
if (imapDest)
imapDest->UpdateFolderWithListener(nullptr, this);
}
}
else if (!m_dstMsgIdString.IsEmpty())
{
nsCOMPtr<nsIUrlListener> dstListener;
dstListener = do_QueryInterface(dstFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// ** make sure we are in the selected state; use lite select folder
// so we won't potentially download a bunch of headers.
rv = imapService->LiteSelectFolder(dstFolder, dstListener, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
rv = imapService->AddMessageFlags(dstFolder, dstListener,
nullptr, m_dstMsgIdString,
kImapMsgDeletedFlag, m_idsAreUids);
}
}
else if (imapAction == nsIImapUrl::nsImapSelectFolder)
{
// Now we should have the headers from the dest folder.
// Look them up and move them back to the source folder.
uint32_t count = m_srcMessageIds.Length();
uint32_t i;
nsCString messageId;
nsTArray<nsMsgKey> dstKeys;
nsCOMPtr<nsIMsgDatabase> destDB;
nsCOMPtr<nsIMsgDBHdr> dstHdr;
rv = dstFolder->GetMsgDatabase(getter_AddRefs(destDB));
NS_ENSURE_SUCCESS(rv, rv);
for (i = 0; i < count; i++)
{
rv = destDB->GetMsgHdrForMessageID(m_srcMessageIds[i].get(), getter_AddRefs(dstHdr));
if (NS_SUCCEEDED(rv) && dstHdr)
{
nsMsgKey dstKey;
dstHdr->GetMessageKey(&dstKey);
dstKeys.AppendElement(dstKey);
}
}
if (dstKeys.Length())
{
nsAutoCString uids;
nsImapMailFolder::AllocateUidStringFromKeys(dstKeys.Elements(), dstKeys.Length(), uids);
rv = imapService->OnlineMessageCopy(dstFolder, uids, srcFolder,
true, true, nullptr,
nullptr, nullptr, nullptr);
}
}
}
return NS_OK;
}
nsImapOfflineTxn::nsImapOfflineTxn(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray,
const char *srcMsgIdString, nsIMsgFolder* dstFolder,
bool isMove, nsOfflineImapOperationType opType,
nsCOMArray<nsIMsgDBHdr> &srcHdrs)
{
Init(srcFolder, srcKeyArray, srcMsgIdString, dstFolder, true,
isMove);
m_opType = opType;
m_flags = 0;
m_addFlags = false;
if (opType == nsIMsgOfflineImapOperation::kDeletedMsg)
{
nsCOMPtr<nsIMsgDatabase> srcDB;
nsCOMPtr<nsIDBFolderInfo> folderInfo;
nsresult rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB));
if (NS_SUCCEEDED(rv) && srcDB)
{
nsMsgKey pseudoKey;
nsCOMPtr <nsIMsgDBHdr> copySrcHdr;
// Imap protocols have conflated key/UUID so we cannot use
// auto key with them.
nsCString protocolType;
srcFolder->GetURI(protocolType);
protocolType.SetLength(protocolType.FindChar(':'));
for (int32_t i = 0; i < srcHdrs.Count(); i++)
{
if (protocolType.EqualsLiteral("imap"))
{
srcDB->GetNextPseudoMsgKey(&pseudoKey);
pseudoKey--;
}
else
{
pseudoKey = nsMsgKey_None;
}
rv = srcDB->CopyHdrFromExistingHdr(pseudoKey, srcHdrs[i], false, getter_AddRefs(copySrcHdr));
if (NS_SUCCEEDED(rv))
{
copySrcHdr->GetMessageKey(&pseudoKey);
m_srcHdrs.AppendObject(copySrcHdr);
}
m_dupKeyArray[i] = pseudoKey;
}
}
}
else
m_srcHdrs.AppendObjects(srcHdrs);
}
nsImapOfflineTxn::~nsImapOfflineTxn()
{
}
// Open the database and find the key for the offline operation that we want to
// undo, then remove it from the database, we also hold on to this
// data for a redo operation.
NS_IMETHODIMP nsImapOfflineTxn::UndoTransaction(void)
{
nsresult rv;
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder)
return rv;
nsCOMPtr <nsIMsgOfflineImapOperation> op;
nsCOMPtr <nsIDBFolderInfo> folderInfo;
nsCOMPtr <nsIMsgDatabase> srcDB;
nsCOMPtr <nsIMsgDatabase> destDB;
rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB));
NS_ENSURE_SUCCESS(rv, rv);
switch (m_opType)
{
case nsIMsgOfflineImapOperation::kMsgMoved:
case nsIMsgOfflineImapOperation::kMsgCopy:
case nsIMsgOfflineImapOperation::kAddedHeader:
case nsIMsgOfflineImapOperation::kFlagsChanged:
case nsIMsgOfflineImapOperation::kDeletedMsg:
{
if (m_srcHdrs.IsEmpty())
{
NS_ASSERTION(false, "No msg header to apply undo.");
break;
}
nsCOMPtr<nsIMsgDBHdr> firstHdr = m_srcHdrs[0];
nsMsgKey hdrKey;
firstHdr->GetMessageKey(&hdrKey);
rv = srcDB->GetOfflineOpForKey(hdrKey, false, getter_AddRefs(op));
bool offlineOpPlayedBack = true;
if (NS_SUCCEEDED(rv) && op)
{
op->GetPlayingBack(&offlineOpPlayedBack);
srcDB->RemoveOfflineOp(op);
op = nullptr;
}
if (!WeAreOffline() && offlineOpPlayedBack)
{
// couldn't find offline op - it must have been played back already
// so we should undo the transaction online.
return nsImapMoveCopyMsgTxn::UndoTransaction();
}
if (!firstHdr)
break;
nsMsgKey msgKey;
if (m_opType == nsIMsgOfflineImapOperation::kAddedHeader)
{
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
m_srcHdrs[i]->GetMessageKey(&msgKey);
nsCOMPtr<nsIMsgDBHdr> mailHdr;
rv = srcDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr));
if (mailHdr)
srcDB->DeleteHeader(mailHdr, nullptr, false, false);
}
srcDB->Commit(true);
}
else if (m_opType == nsIMsgOfflineImapOperation::kDeletedMsg)
{
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsCOMPtr<nsIMsgDBHdr> undeletedHdr = m_srcHdrs[i];
m_srcHdrs[i]->GetMessageKey(&msgKey);
if (undeletedHdr)
{
nsCOMPtr<nsIMsgDBHdr> newHdr;
srcDB->CopyHdrFromExistingHdr (msgKey, undeletedHdr, true, getter_AddRefs(newHdr));
}
}
srcDB->Close(true);
srcFolder->SummaryChanged();
}
break;
}
case nsIMsgOfflineImapOperation::kMsgMarkedDeleted:
{
nsMsgKey msgKey;
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
m_srcHdrs[i]->GetMessageKey(&msgKey);
srcDB->MarkImapDeleted(msgKey, false, nullptr);
}
}
break;
default:
break;
}
srcDB->Close(true);
srcFolder->SummaryChanged();
return NS_OK;
}
NS_IMETHODIMP nsImapOfflineTxn::RedoTransaction(void)
{
nsresult rv;
nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
if (NS_FAILED(rv) || !srcFolder)
return rv;
nsCOMPtr <nsIMsgOfflineImapOperation> op;
nsCOMPtr <nsIDBFolderInfo> folderInfo;
nsCOMPtr <nsIMsgDatabase> srcDB;
nsCOMPtr <nsIMsgDatabase> destDB;
rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB));
NS_ENSURE_SUCCESS(rv, rv);
switch (m_opType)
{
case nsIMsgOfflineImapOperation::kMsgMoved:
case nsIMsgOfflineImapOperation::kMsgCopy:
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsMsgKey hdrKey;
m_srcHdrs[i]->GetMessageKey(&hdrKey);
rv = srcDB->GetOfflineOpForKey(hdrKey, false, getter_AddRefs(op));
if (NS_SUCCEEDED(rv) && op)
{
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
if (dstFolder)
{
nsCString folderURI;
dstFolder->GetURI(folderURI);
if (m_opType == nsIMsgOfflineImapOperation::kMsgMoved)
op->SetDestinationFolderURI(folderURI.get()); // offline move
if (m_opType == nsIMsgOfflineImapOperation::kMsgCopy)
{
op->SetOperation(nsIMsgOfflineImapOperation::kMsgMoved);
op->AddMessageCopyOperation(folderURI.get()); // offline copy
}
dstFolder->SummaryChanged();
}
}
else if (!WeAreOffline())
{
// couldn't find offline op - it must have been played back already
// so we should redo the transaction online.
return nsImapMoveCopyMsgTxn::RedoTransaction();
}
}
break;
case nsIMsgOfflineImapOperation::kAddedHeader:
{
nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(destDB));
NS_ENSURE_SUCCESS(rv, rv);
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsCOMPtr<nsIMsgDBHdr> restoreHdr;
nsMsgKey msgKey;
m_srcHdrs[i]->GetMessageKey(&msgKey);
destDB->CopyHdrFromExistingHdr (msgKey, m_srcHdrs[i], true, getter_AddRefs(restoreHdr));
rv = destDB->GetOfflineOpForKey(msgKey, true, getter_AddRefs(op));
if (NS_SUCCEEDED(rv) && op)
{
nsCString folderURI;
srcFolder->GetURI(folderURI);
op->SetSourceFolderURI(folderURI.get());
}
}
dstFolder->SummaryChanged();
destDB->Close(true);
}
break;
case nsIMsgOfflineImapOperation::kDeletedMsg:
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsMsgKey msgKey;
m_srcHdrs[i]->GetMessageKey(&msgKey);
srcDB->DeleteMessage(msgKey, nullptr, true);
}
break;
case nsIMsgOfflineImapOperation::kMsgMarkedDeleted:
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsMsgKey msgKey;
m_srcHdrs[i]->GetMessageKey(&msgKey);
srcDB->MarkImapDeleted(msgKey, true, nullptr);
}
break;
case nsIMsgOfflineImapOperation::kFlagsChanged:
for (int32_t i = 0; i < m_srcHdrs.Count(); i++)
{
nsMsgKey msgKey;
m_srcHdrs[i]->GetMessageKey(&msgKey);
rv = srcDB->GetOfflineOpForKey(msgKey, true, getter_AddRefs(op));
if (NS_SUCCEEDED(rv) && op)
{
imapMessageFlagsType newMsgFlags;
op->GetNewFlags(&newMsgFlags);
if (m_addFlags)
op->SetFlagOperation(newMsgFlags | m_flags);
else
op->SetFlagOperation(newMsgFlags & ~m_flags);
}
}
break;
default:
break;
}
srcDB->Close(true);
srcDB = nullptr;
srcFolder->SummaryChanged();
return NS_OK;
}

View file

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapUndoTxn_h__
#define nsImapUndoTxn_h__
#include "mozilla/Attributes.h"
#include "nsIMsgFolder.h"
#include "nsImapCore.h"
#include "nsIImapService.h"
#include "nsIImapIncomingServer.h"
#include "nsIUrlListener.h"
#include "nsMsgTxn.h"
#include "MailNewsTypes.h"
#include "nsTArray.h"
#include "nsIMsgOfflineImapOperation.h"
#include "nsCOMPtr.h"
#include "nsWeakReference.h"
#include "nsCOMArray.h"
class nsImapMoveCopyMsgTxn : public nsMsgTxn, nsIUrlListener
{
public:
nsImapMoveCopyMsgTxn();
nsImapMoveCopyMsgTxn(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray,
const char* srcMsgIdString, nsIMsgFolder* dstFolder,
bool isMove);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIURLLISTENER
NS_IMETHOD UndoTransaction(void) override;
NS_IMETHOD RedoTransaction(void) override;
// helper
nsresult SetCopyResponseUid(const char *msgIdString);
nsresult GetSrcKeyArray(nsTArray<nsMsgKey>& srcKeyArray);
void GetSrcMsgIds(nsCString &srcMsgIds) {srcMsgIds = m_srcMsgIdString;}
nsresult AddDstKey(nsMsgKey aKey);
nsresult UndoMailboxDelete();
nsresult RedoMailboxDelete();
nsresult Init(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray,
const char* srcMsgIdString, nsIMsgFolder* dstFolder,
bool idsAreUids, bool isMove);
protected:
virtual ~nsImapMoveCopyMsgTxn();
nsWeakPtr m_srcFolder;
nsCOMArray<nsIMsgDBHdr> m_srcHdrs;
nsTArray<nsMsgKey> m_dupKeyArray;
nsTArray<nsMsgKey> m_srcKeyArray;
nsTArray<nsCString> m_srcMessageIds;
nsCString m_srcMsgIdString;
nsWeakPtr m_dstFolder;
nsCString m_dstMsgIdString;
bool m_idsAreUids;
bool m_isMove;
bool m_srcIsPop3;
nsTArray<uint32_t> m_srcSizeArray;
// this is used when we chain urls for imap undo, since "this" needs
// to be the listener, but the folder may need to also be notified.
nsWeakPtr m_onStopListener;
nsresult GetImapDeleteModel(nsIMsgFolder* aFolder, nsMsgImapDeleteModel *aDeleteModel);
};
class nsImapOfflineTxn : public nsImapMoveCopyMsgTxn
{
public:
nsImapOfflineTxn(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray,
const char* srcMsgIdString,
nsIMsgFolder* dstFolder,
bool isMove,
nsOfflineImapOperationType opType,
nsCOMArray<nsIMsgDBHdr> &srcHdrs);
NS_IMETHOD UndoTransaction(void) override;
NS_IMETHOD RedoTransaction(void) override;
void SetAddFlags(bool addFlags) {m_addFlags = addFlags;}
void SetFlags(uint32_t flags) {m_flags = flags;}
protected:
virtual ~nsImapOfflineTxn();
nsOfflineImapOperationType m_opType;
// these two are used to undo flag changes, which we don't currently do.
bool m_addFlags;
uint32_t m_flags;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsImapUrl_h___
#define nsImapUrl_h___
#include "mozilla/Attributes.h"
#include "nsIImapUrl.h"
#include "nsIImapMockChannel.h"
#include "nsCOMPtr.h"
#include "nsMsgMailNewsUrl.h"
#include "nsIMsgIncomingServer.h"
#include "nsIImapMailFolderSink.h"
#include "nsIImapServerSink.h"
#include "nsIImapMessageSink.h"
#include "nsWeakPtr.h"
#include "nsIFile.h"
#include "mozilla/Mutex.h"
class nsImapUrl : public nsIImapUrl, public nsMsgMailNewsUrl, public nsIMsgMessageUrl, public nsIMsgI18NUrl
{
public:
NS_DECL_ISUPPORTS_INHERITED
// nsIURI override
NS_IMETHOD SetSpec(const nsACString &aSpec) override;
NS_IMETHOD SetQuery(const nsACString &aQuery) override;
NS_IMETHOD CloneInternal(uint32_t aRefHandlingMode,
const nsACString& newRef, nsIURI **_retval) override;
//////////////////////////////////////////////////////////////////////////////
// we support the nsIImapUrl interface
//////////////////////////////////////////////////////////////////////////////
NS_DECL_NSIIMAPURL
// nsIMsgMailNewsUrl overrides
NS_IMETHOD IsUrlType(uint32_t type, bool *isType) override;
NS_IMETHOD GetFolder(nsIMsgFolder **aFolder) override;
NS_IMETHOD SetFolder(nsIMsgFolder *aFolder) override;
// nsIMsgMessageUrl
NS_DECL_NSIMSGMESSAGEURL
NS_DECL_NSIMSGI18NURL
// nsImapUrl
nsImapUrl();
static nsresult ConvertToCanonicalFormat(const char *folderName, char onlineDelimiter, char **resultingCanonicalPath);
static nsresult EscapeSlashes(const char *sourcePath, char **resultPath);
static nsresult UnescapeSlashes(char *path);
static char * ReplaceCharsInCopiedString(const char *stringToCopy, char oldChar, char newChar);
protected:
virtual ~nsImapUrl();
virtual nsresult ParseUrl();
char *m_listOfMessageIds;
// handle the imap specific parsing
void ParseImapPart(char *imapPartOfUrl);
void ParseFolderPath(char **resultingCanonicalPath);
void ParseSearchCriteriaString();
void ParseUidChoice();
void ParseMsgFlags();
void ParseListOfMessageIds();
void ParseCustomMsgFetchAttribute();
void ParseNumBytes();
nsresult GetMsgFolder(nsIMsgFolder **msgFolder);
char *m_sourceCanonicalFolderPathSubString;
char *m_destinationCanonicalFolderPathSubString;
char *m_tokenPlaceHolder;
char *m_urlidSubString;
char m_onlineSubDirSeparator;
char *m_searchCriteriaString; // should we use m_search, or is this special?
nsCString m_command; // for custom commands
nsCString m_msgFetchAttribute; // for fetching custom msg attributes
nsCString m_customAttributeResult; // for fetching custom msg attributes
nsCString m_customCommandResult; // custom command response
nsCString m_customAddFlags; // these two are for setting and clearing custom flags
nsCString m_customSubtractFlags;
int32_t m_numBytesToFetch; // when doing a msg body preview, how many bytes to read
bool m_validUrl;
bool m_runningUrl;
bool m_idsAreUids;
bool m_mimePartSelectorDetected;
bool m_allowContentChange; // if false, we can't use Mime parts on demand
bool m_fetchPartsOnDemand; // if true, we should fetch leave parts on server.
bool m_msgLoadingFromCache; // if true, we might need to mark read on server
bool m_externalLinkUrl; // if true, we're running this url because the user
// True if the fetch results should be put in the offline store.
bool m_storeResultsOffline;
bool m_storeOfflineOnFallback;
bool m_localFetchOnly;
bool m_rerunningUrl; // first attempt running this failed with connection error; retrying
bool m_moreHeadersToDownload;
nsImapContentModifiedType m_contentModified;
int32_t m_extraStatus;
nsCString m_userName;
nsCString m_serverKey;
// event sinks
imapMessageFlagsType m_flags;
nsImapAction m_imapAction;
nsWeakPtr m_imapFolder;
nsWeakPtr m_imapMailFolderSink;
nsWeakPtr m_imapMessageSink;
nsWeakPtr m_imapServerSink;
// online message copy support; i don't have a better solution yet
nsCOMPtr <nsISupports> m_copyState; // now, refcounted.
nsCOMPtr<nsIFile> m_file;
nsWeakPtr m_channelWeakPtr;
// used by save message to disk
nsCOMPtr<nsIFile> m_messageFile;
bool m_addDummyEnvelope;
bool m_canonicalLineEnding; // CRLF
nsCString mURI; // the RDF URI associated with this url.
nsCString mCharsetOverride; // used by nsIMsgI18NUrl...
mozilla::Mutex mLock;
};
#endif /* nsImapUrl_h___ */

View file

@ -0,0 +1,373 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "msgCore.h"
#include "nsImapUtils.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "prsystem.h"
#include "prprf.h"
#include "nsNetCID.h"
// stuff for temporary root folder hack
#include "nsIMsgAccountManager.h"
#include "nsIMsgIncomingServer.h"
#include "nsIImapIncomingServer.h"
#include "nsMsgBaseCID.h"
#include "nsImapCore.h"
#include "nsMsgUtils.h"
#include "nsImapFlagAndUidState.h"
#include "nsIMAPNamespace.h"
#include "nsIImapFlagAndUidState.h"
nsresult
nsImapURI2FullName(const char* rootURI, const char* hostName, const char* uriStr,
char **name)
{
nsAutoCString uri(uriStr);
nsAutoCString fullName;
if (uri.Find(rootURI) != 0)
return NS_ERROR_FAILURE;
fullName = Substring(uri, strlen(rootURI));
uri = fullName;
int32_t hostStart = uri.Find(hostName);
if (hostStart <= 0)
return NS_ERROR_FAILURE;
fullName = Substring(uri, hostStart);
uri = fullName;
int32_t hostEnd = uri.FindChar('/');
if (hostEnd <= 0)
return NS_ERROR_FAILURE;
fullName = Substring(uri, hostEnd + 1);
if (fullName.IsEmpty())
return NS_ERROR_FAILURE;
*name = ToNewCString(fullName);
return NS_OK;
}
/* parses ImapMessageURI */
nsresult nsParseImapMessageURI(const char* uri, nsCString& folderURI, uint32_t *key, char **part)
{
if(!key)
return NS_ERROR_NULL_POINTER;
nsAutoCString uriStr(uri);
int32_t folderEnd = -1;
// imap-message uri's can have imap:// url strings tacked on the end,
// e.g., when opening/saving attachments. We don't want to look for '#'
// in that part of the uri, if the attachment name contains '#',
// so check for that here.
if (StringBeginsWith(uriStr, NS_LITERAL_CSTRING("imap-message")))
folderEnd = uriStr.Find("imap://");
int32_t keySeparator = MsgRFindChar(uriStr, '#', folderEnd);
if(keySeparator != -1)
{
int32_t keyEndSeparator = MsgFindCharInSet(uriStr, "/?&", keySeparator);
nsAutoString folderPath;
folderURI = StringHead(uriStr, keySeparator);
folderURI.Cut(4, 8); // cut out the _message part of imap-message:
// folder uri's don't have fully escaped usernames.
int32_t atPos = folderURI.FindChar('@');
if (atPos != -1)
{
nsCString unescapedName, escapedName;
int32_t userNamePos = folderURI.Find("//") + 2;
uint32_t origUserNameLen = atPos - userNamePos;
if (NS_SUCCEEDED(MsgUnescapeString(Substring(folderURI, userNamePos,
origUserNameLen),
0, unescapedName)))
{
// Re-escape the username, matching the way we do it in uris, not the
// way necko escapes urls. See nsMsgIncomingServer::GetServerURI.
MsgEscapeString(unescapedName, nsINetUtil::ESCAPE_XALPHAS, escapedName);
folderURI.Replace(userNamePos, origUserNameLen, escapedName);
}
}
nsAutoCString keyStr;
if (keyEndSeparator != -1)
keyStr = Substring(uriStr, keySeparator + 1, keyEndSeparator - (keySeparator + 1));
else
keyStr = Substring(uriStr, keySeparator + 1);
*key = strtoul(keyStr.get(), nullptr, 10);
if (part && keyEndSeparator != -1)
{
int32_t partPos = MsgFind(uriStr, "part=", false, keyEndSeparator);
if (partPos != -1)
{
*part = ToNewCString(Substring(uriStr, keyEndSeparator));
}
}
}
return NS_OK;
}
nsresult nsBuildImapMessageURI(const char *baseURI, uint32_t key, nsCString& uri)
{
uri.Append(baseURI);
uri.Append('#');
uri.AppendInt(key);
return NS_OK;
}
nsresult nsCreateImapBaseMessageURI(const nsACString& baseURI, nsCString &baseMessageURI)
{
nsAutoCString tailURI(baseURI);
// chop off imap:/
if (tailURI.Find(kImapRootURI) == 0)
tailURI.Cut(0, PL_strlen(kImapRootURI));
baseMessageURI = kImapMessageRootURI;
baseMessageURI += tailURI;
return NS_OK;
}
// nsImapMailboxSpec definition
NS_IMPL_ISUPPORTS(nsImapMailboxSpec, nsIMailboxSpec)
nsImapMailboxSpec::nsImapMailboxSpec()
{
mFolder_UIDVALIDITY = 0;
mHighestModSeq = 0;
mNumOfMessages = 0;
mNumOfUnseenMessages = 0;
mNumOfRecentMessages = 0;
mNextUID = 0;
mBoxFlags = 0;
mSupportedUserFlags = 0;
mHierarchySeparator = '\0';
mFolderSelected = false;
mDiscoveredFromLsub = false;
mOnlineVerified = false;
mNamespaceForFolder = nullptr;
}
nsImapMailboxSpec::~nsImapMailboxSpec()
{
}
NS_IMPL_GETSET(nsImapMailboxSpec, Folder_UIDVALIDITY, int32_t, mFolder_UIDVALIDITY)
NS_IMPL_GETSET(nsImapMailboxSpec, HighestModSeq, uint64_t, mHighestModSeq)
NS_IMPL_GETSET(nsImapMailboxSpec, NumMessages, int32_t, mNumOfMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NumUnseenMessages, int32_t, mNumOfUnseenMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NumRecentMessages, int32_t, mNumOfRecentMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NextUID, int32_t, mNextUID)
NS_IMPL_GETSET(nsImapMailboxSpec, HierarchyDelimiter, char, mHierarchySeparator)
NS_IMPL_GETSET(nsImapMailboxSpec, FolderSelected, bool, mFolderSelected)
NS_IMPL_GETSET(nsImapMailboxSpec, DiscoveredFromLsub, bool, mDiscoveredFromLsub)
NS_IMPL_GETSET(nsImapMailboxSpec, OnlineVerified, bool, mOnlineVerified)
NS_IMPL_GETSET(nsImapMailboxSpec, SupportedUserFlags, uint32_t, mSupportedUserFlags)
NS_IMPL_GETSET(nsImapMailboxSpec, Box_flags, uint32_t, mBoxFlags)
NS_IMPL_GETSET(nsImapMailboxSpec, NamespaceForFolder, nsIMAPNamespace *, mNamespaceForFolder)
NS_IMETHODIMP nsImapMailboxSpec::GetAllocatedPathName(nsACString &aAllocatedPathName)
{
aAllocatedPathName = mAllocatedPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetAllocatedPathName(const nsACString &aAllocatedPathName)
{
mAllocatedPathName = aAllocatedPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetUnicharPathName(nsAString &aUnicharPathName)
{
aUnicharPathName = aUnicharPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetUnicharPathName(const nsAString &aUnicharPathName)
{
mUnicharPathName = aUnicharPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetHostName(nsACString &aHostName)
{
aHostName = mHostName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetHostName(const nsACString &aHostName)
{
mHostName = aHostName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetFlagState(nsIImapFlagAndUidState ** aFlagState)
{
NS_ENSURE_ARG_POINTER(aFlagState);
NS_IF_ADDREF(*aFlagState = mFlagState);
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetFlagState(nsIImapFlagAndUidState * aFlagState)
{
NS_ENSURE_ARG_POINTER(aFlagState);
mFlagState = aFlagState;
return NS_OK;
}
nsImapMailboxSpec& nsImapMailboxSpec::operator= (const nsImapMailboxSpec& aCopy)
{
mFolder_UIDVALIDITY = aCopy.mFolder_UIDVALIDITY;
mHighestModSeq = aCopy.mHighestModSeq;
mNumOfMessages = aCopy.mNumOfMessages;
mNumOfUnseenMessages = aCopy.mNumOfUnseenMessages;
mNumOfRecentMessages = aCopy.mNumOfRecentMessages;
mBoxFlags = aCopy.mBoxFlags;
mSupportedUserFlags = aCopy.mSupportedUserFlags;
mAllocatedPathName.Assign(aCopy.mAllocatedPathName);
mUnicharPathName.Assign(aCopy.mUnicharPathName);
mHostName.Assign(aCopy.mHostName);
mFlagState = aCopy.mFlagState;
mNamespaceForFolder = aCopy.mNamespaceForFolder;
mFolderSelected = aCopy.mFolderSelected;
mDiscoveredFromLsub = aCopy.mDiscoveredFromLsub;
mOnlineVerified = aCopy.mOnlineVerified;
return *this;
}
// use the flagState to determine if the gaps in the msgUids correspond to gaps in the mailbox,
// in which case we can still use ranges. If flagState is null, we won't do this.
void AllocateImapUidString(uint32_t *msgUids, uint32_t &msgCount,
nsImapFlagAndUidState *flagState, nsCString &returnString)
{
uint32_t startSequence = (msgCount > 0) ? msgUids[0] : 0xFFFFFFFF;
uint32_t curSequenceEnd = startSequence;
uint32_t total = msgCount;
int32_t curFlagStateIndex = -1;
// a partial fetch flag state doesn't help us, so don't use it.
if (flagState && flagState->GetPartialUIDFetch())
flagState = nullptr;
for (uint32_t keyIndex = 0; keyIndex < total; keyIndex++)
{
uint32_t curKey = msgUids[keyIndex];
uint32_t nextKey = (keyIndex + 1 < total) ? msgUids[keyIndex + 1] : 0xFFFFFFFF;
bool lastKey = (nextKey == 0xFFFFFFFF);
if (lastKey)
curSequenceEnd = curKey;
if (!lastKey)
{
if (nextKey == curSequenceEnd + 1)
{
curSequenceEnd = nextKey;
curFlagStateIndex++;
continue;
}
if (flagState)
{
if (curFlagStateIndex == -1)
{
bool foundIt;
flagState->GetMessageFlagsFromUID(curSequenceEnd, &foundIt, &curFlagStateIndex);
if (!foundIt)
{
NS_WARNING("flag state missing key");
// The start of this sequence is missing from flag state, so move
// on to the next key.
curFlagStateIndex = -1;
curSequenceEnd = startSequence = nextKey;
continue;
}
}
curFlagStateIndex++;
uint32_t nextUidInFlagState;
nsresult rv = flagState->GetUidOfMessage(curFlagStateIndex, &nextUidInFlagState);
if (NS_SUCCEEDED(rv) && nextUidInFlagState == nextKey)
{
curSequenceEnd = nextKey;
continue;
}
}
}
if (curSequenceEnd > startSequence)
{
returnString.AppendInt((int64_t) startSequence);
returnString += ':';
returnString.AppendInt((int64_t) curSequenceEnd);
startSequence = nextKey;
curSequenceEnd = startSequence;
curFlagStateIndex = -1;
}
else
{
startSequence = nextKey;
curSequenceEnd = startSequence;
returnString.AppendInt((int64_t) msgUids[keyIndex]);
curFlagStateIndex = -1;
}
// check if we've generated too long a string - if there's no flag state,
// it means we just need to go ahead and generate a too long string
// because the calling code won't handle breaking up the strings.
if (flagState && returnString.Length() > 950)
{
msgCount = keyIndex;
break;
}
// If we are not the last item then we need to add the comma
// but it's important we do it here, after the length check
if (!lastKey)
returnString += ',';
}
}
void ParseUidString(const char *uidString, nsTArray<nsMsgKey> &keys)
{
// This is in the form <id>,<id>, or <id1>:<id2>
if (!uidString)
return;
char curChar = *uidString;
bool isRange = false;
uint32_t curToken;
uint32_t saveStartToken = 0;
for (const char *curCharPtr = uidString; curChar && *curCharPtr;)
{
const char *currentKeyToken = curCharPtr;
curChar = *curCharPtr;
while (curChar != ':' && curChar != ',' && curChar != '\0')
curChar = *curCharPtr++;
// we don't need to null terminate currentKeyToken because strtoul
// stops at non-numeric chars.
curToken = strtoul(currentKeyToken, nullptr, 10);
if (isRange)
{
while (saveStartToken < curToken)
keys.AppendElement(saveStartToken++);
}
keys.AppendElement(curToken);
isRange = (curChar == ':');
if (isRange)
saveStartToken = curToken + 1;
}
}
void AppendUid(nsCString &msgIds, uint32_t uid)
{
char buf[20];
PR_snprintf(buf, sizeof(buf), "%u", uid);
msgIds.Append(buf);
}

View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NS_IMAPUTILS_H
#define NS_IMAPUTILS_H
#include "nsStringGlue.h"
#include "nsIMsgIncomingServer.h"
#include "MailNewsTypes.h"
#include "nsTArray.h"
#include "nsIMailboxSpec.h"
#include "nsCOMPtr.h"
class nsImapFlagAndUidState;
class nsImapProtocol;
static const char kImapRootURI[] = "imap:/";
static const char kImapMessageRootURI[] = "imap-message:/";
static const char kModSeqPropertyName[] = "highestModSeq";
static const char kHighestRecordedUIDPropertyName[] = "highestRecordedUID";
static const char kDeletedHdrCountPropertyName[] = "numDeletedHeaders";
extern nsresult
nsImapURI2FullName(const char* rootURI, const char* hostname, const char* uriStr,
char **name);
extern nsresult
nsParseImapMessageURI(const char* uri, nsCString& folderURI, uint32_t *key, char **part);
extern nsresult
nsBuildImapMessageURI(const char *baseURI, uint32_t key, nsCString& uri);
extern nsresult
nsCreateImapBaseMessageURI(const nsACString& baseURI, nsCString& baseMessageURI);
void AllocateImapUidString(uint32_t *msgUids, uint32_t &msgCount, nsImapFlagAndUidState *flagState, nsCString &returnString);
void ParseUidString(const char *uidString, nsTArray<nsMsgKey> &keys);
void AppendUid(nsCString &msgIds, uint32_t uid);
class nsImapMailboxSpec : public nsIMailboxSpec
{
public:
nsImapMailboxSpec();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMAILBOXSPEC
nsImapMailboxSpec& operator= (const nsImapMailboxSpec& aCopy);
nsCOMPtr<nsIImapFlagAndUidState> mFlagState;
nsIMAPNamespace *mNamespaceForFolder;
uint32_t mBoxFlags;
uint32_t mSupportedUserFlags;
int32_t mFolder_UIDVALIDITY;
uint64_t mHighestModSeq;
int32_t mNumOfMessages;
int32_t mNumOfUnseenMessages;
int32_t mNumOfRecentMessages;
int32_t mNextUID;
nsCString mAllocatedPathName;
nsCString mHostName;
nsString mUnicharPathName;
char mHierarchySeparator;
bool mFolderSelected;
bool mDiscoveredFromLsub;
bool mOnlineVerified;
nsImapProtocol *mConnection; // do we need this? It seems evil
private:
virtual ~nsImapMailboxSpec();
};
#endif //NS_IMAPUTILS_H

View file

@ -0,0 +1,600 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsSyncRunnableHelpers.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIMsgWindow.h"
#include "nsImapMailFolder.h"
#include "mozilla/Monitor.h"
NS_IMPL_ISUPPORTS(StreamListenerProxy, nsIStreamListener)
NS_IMPL_ISUPPORTS(ImapMailFolderSinkProxy, nsIImapMailFolderSink)
NS_IMPL_ISUPPORTS(ImapServerSinkProxy, nsIImapServerSink)
NS_IMPL_ISUPPORTS(ImapMessageSinkProxy,
nsIImapMessageSink)
NS_IMPL_ISUPPORTS(ImapProtocolSinkProxy,
nsIImapProtocolSink)
namespace {
// Traits class for a reference type, specialized for parameters which are
// already references.
template<typename T>
struct RefType
{
typedef T& type;
};
template<>
struct RefType<nsAString&>
{
typedef nsAString& type;
};
template<>
struct RefType<const nsAString&>
{
typedef const nsAString& type;
};
template<>
struct RefType<nsACString&>
{
typedef nsACString& type;
};
template<>
struct RefType<const nsACString&>
{
typedef const nsACString& type;
};
template<>
struct RefType<const nsIID&>
{
typedef const nsIID& type;
};
class SyncRunnableBase : public mozilla::Runnable
{
public:
nsresult Result() {
return mResult;
}
mozilla::Monitor& Monitor() {
return mMonitor;
}
protected:
SyncRunnableBase()
: mResult(NS_ERROR_UNEXPECTED)
, mMonitor("SyncRunnableBase")
{ }
nsresult mResult;
mozilla::Monitor mMonitor;
};
template<typename Receiver>
class SyncRunnable0 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)();
SyncRunnable0(Receiver* receiver, ReceiverMethod method)
: mReceiver(receiver)
, mMethod(method)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)();
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
};
template<typename Receiver, typename Arg1>
class SyncRunnable1 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)(Arg1);
typedef typename RefType<Arg1>::type Arg1Ref;
SyncRunnable1(Receiver* receiver, ReceiverMethod method,
Arg1Ref arg1)
: mReceiver(receiver)
, mMethod(method)
, mArg1(arg1)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)(mArg1);
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
Arg1Ref mArg1;
};
template<typename Receiver, typename Arg1, typename Arg2>
class SyncRunnable2 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)(Arg1, Arg2);
typedef typename RefType<Arg1>::type Arg1Ref;
typedef typename RefType<Arg2>::type Arg2Ref;
SyncRunnable2(Receiver* receiver, ReceiverMethod method,
Arg1Ref arg1, Arg2Ref arg2)
: mReceiver(receiver)
, mMethod(method)
, mArg1(arg1)
, mArg2(arg2)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)(mArg1, mArg2);
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
Arg1Ref mArg1;
Arg2Ref mArg2;
};
template<typename Receiver, typename Arg1, typename Arg2, typename Arg3>
class SyncRunnable3 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)(Arg1, Arg2, Arg3);
typedef typename RefType<Arg1>::type Arg1Ref;
typedef typename RefType<Arg2>::type Arg2Ref;
typedef typename RefType<Arg3>::type Arg3Ref;
SyncRunnable3(Receiver* receiver, ReceiverMethod method,
Arg1Ref arg1, Arg2Ref arg2, Arg3Ref arg3)
: mReceiver(receiver)
, mMethod(method)
, mArg1(arg1)
, mArg2(arg2)
, mArg3(arg3)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)(mArg1, mArg2, mArg3);
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
Arg1Ref mArg1;
Arg2Ref mArg2;
Arg3Ref mArg3;
};
template<typename Receiver, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
class SyncRunnable4 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)(Arg1, Arg2, Arg3, Arg4);
typedef typename RefType<Arg1>::type Arg1Ref;
typedef typename RefType<Arg2>::type Arg2Ref;
typedef typename RefType<Arg3>::type Arg3Ref;
typedef typename RefType<Arg4>::type Arg4Ref;
SyncRunnable4(Receiver* receiver, ReceiverMethod method,
Arg1Ref arg1, Arg2Ref arg2, Arg3Ref arg3, Arg4Ref arg4)
: mReceiver(receiver)
, mMethod(method)
, mArg1(arg1)
, mArg2(arg2)
, mArg3(arg3)
, mArg4(arg4)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)(mArg1, mArg2, mArg3, mArg4);
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
Arg1Ref mArg1;
Arg2Ref mArg2;
Arg3Ref mArg3;
Arg4Ref mArg4;
};
template<typename Receiver, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
class SyncRunnable5 : public SyncRunnableBase
{
public:
typedef nsresult (NS_STDCALL Receiver::*ReceiverMethod)(Arg1, Arg2, Arg3, Arg4, Arg5);
typedef typename RefType<Arg1>::type Arg1Ref;
typedef typename RefType<Arg2>::type Arg2Ref;
typedef typename RefType<Arg3>::type Arg3Ref;
typedef typename RefType<Arg4>::type Arg4Ref;
typedef typename RefType<Arg5>::type Arg5Ref;
SyncRunnable5(Receiver* receiver, ReceiverMethod method,
Arg1Ref arg1, Arg2Ref arg2, Arg3Ref arg3, Arg4Ref arg4, Arg5Ref arg5)
: mReceiver(receiver)
, mMethod(method)
, mArg1(arg1)
, mArg2(arg2)
, mArg3(arg3)
, mArg4(arg4)
, mArg5(arg5)
{ }
NS_IMETHOD Run() {
mResult = (mReceiver->*mMethod)(mArg1, mArg2, mArg3, mArg4, mArg5);
mozilla::MonitorAutoLock(mMonitor).Notify();
return NS_OK;
}
private:
Receiver* mReceiver;
ReceiverMethod mMethod;
Arg1Ref mArg1;
Arg2Ref mArg2;
Arg3Ref mArg3;
Arg4Ref mArg4;
Arg5Ref mArg5;
};
nsresult
DispatchSyncRunnable(SyncRunnableBase* r)
{
if (NS_IsMainThread()) {
r->Run();
}
else {
mozilla::MonitorAutoLock lock(r->Monitor());
nsresult rv = NS_DispatchToMainThread(r);
if (NS_FAILED(rv))
return rv;
lock.Wait();
}
return r->Result();
}
} // anonymous namespace
#define NS_SYNCRUNNABLEMETHOD0(iface, method) \
NS_IMETHODIMP iface##Proxy::method() { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable0<nsI##iface> \
(mReceiver, &nsI##iface::method); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEMETHOD1(iface, method, \
arg1) \
NS_IMETHODIMP iface##Proxy::method(arg1 a1) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable1<nsI##iface, arg1> \
(mReceiver, &nsI##iface::method, a1); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEMETHOD2(iface, method, \
arg1, arg2) \
NS_IMETHODIMP iface##Proxy::method(arg1 a1, arg2 a2) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable2<nsI##iface, arg1, arg2> \
(mReceiver, &nsI##iface::method, a1, a2); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEMETHOD3(iface, method, \
arg1, arg2, arg3) \
NS_IMETHODIMP iface##Proxy::method(arg1 a1, arg2 a2, arg3 a3) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable3<nsI##iface, arg1, arg2, arg3> \
(mReceiver, &nsI##iface::method, \
a1, a2, a3); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEMETHOD4(iface, method, \
arg1, arg2, arg3, arg4) \
NS_IMETHODIMP iface##Proxy::method(arg1 a1, arg2 a2, arg3 a3, arg4 a4) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable4<nsI##iface, arg1, arg2, arg3, arg4> \
(mReceiver, &nsI##iface::method, \
a1, a2, a3, a4); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEMETHOD5(iface, method, \
arg1, arg2, arg3, arg4, arg5) \
NS_IMETHODIMP iface##Proxy::method(arg1 a1, arg2 a2, arg3 a3, arg4 a4, arg5 a5) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable5<nsI##iface, arg1, arg2, arg3, arg4, arg5> \
(mReceiver, &nsI##iface::method, \
a1, a2, a3, a4, a5); \
return DispatchSyncRunnable(r); \
}
#define NS_SYNCRUNNABLEATTRIBUTE(iface, attribute, \
type) \
NS_IMETHODIMP iface##Proxy::Get##attribute(type *a1) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable1<nsI##iface, type *> \
(mReceiver, &nsI##iface::Get##attribute, a1); \
return DispatchSyncRunnable(r); \
} \
NS_IMETHODIMP iface##Proxy::Set##attribute(type a1) { \
RefPtr<SyncRunnableBase> r = \
new SyncRunnable1<nsI##iface, type> \
(mReceiver, &nsI##iface::Set##attribute, a1); \
return DispatchSyncRunnable(r); \
}
#define NS_NOTIMPLEMENTED \
{ NS_RUNTIMEABORT("Not implemented"); return NS_ERROR_UNEXPECTED; }
NS_SYNCRUNNABLEMETHOD5(StreamListener, OnDataAvailable,
nsIRequest *, nsISupports *, nsIInputStream *, uint64_t, uint32_t)
NS_SYNCRUNNABLEMETHOD2(StreamListener, OnStartRequest,
nsIRequest *, nsISupports *)
NS_SYNCRUNNABLEMETHOD3(StreamListener, OnStopRequest,
nsIRequest *, nsISupports *, nsresult)
NS_SYNCRUNNABLEMETHOD2(ImapProtocolSink, GetUrlWindow, nsIMsgMailNewsUrl *,
nsIMsgWindow **)
NS_SYNCRUNNABLEMETHOD0(ImapProtocolSink, CloseStreams)
NS_SYNCRUNNABLEMETHOD0(ImapProtocolSink, SetupMainThreadProxies)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, FolderNeedsACLListed, bool)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, FolderNeedsSubscribing, bool)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, FolderNeedsAdded, bool)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, AclFlags, uint32_t)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, UidValidity, int32_t)
NS_SYNCRUNNABLEATTRIBUTE(ImapMailFolderSink, FolderQuotaCommandIssued, bool)
NS_SYNCRUNNABLEMETHOD3(ImapMailFolderSink, SetFolderQuotaData, const nsACString &, uint32_t, uint32_t)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, GetShouldDownloadAllHeaders, bool *)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, GetOnlineDelimiter, char *)
NS_SYNCRUNNABLEMETHOD0(ImapMailFolderSink, OnNewIdleMessages)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, UpdateImapMailboxStatus, nsIImapProtocol *, nsIMailboxSpec *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, UpdateImapMailboxInfo, nsIImapProtocol *, nsIMailboxSpec *)
NS_SYNCRUNNABLEMETHOD4(ImapMailFolderSink, GetMsgHdrsToDownload, bool *, int32_t *, uint32_t *, nsMsgKey **)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, ParseMsgHdrs, nsIImapProtocol *, nsIImapHeaderXferInfo *)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, AbortHeaderParseStream, nsIImapProtocol *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, OnlineCopyCompleted, nsIImapProtocol *, ImapOnlineCopyState)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, StartMessage, nsIMsgMailNewsUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, EndMessage, nsIMsgMailNewsUrl *, nsMsgKey)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, NotifySearchHit, nsIMsgMailNewsUrl *, const char *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, CopyNextStreamMessage, bool, nsISupports *)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, CloseMockChannel, nsIImapMockChannel *)
NS_SYNCRUNNABLEMETHOD5(ImapMailFolderSink, SetUrlState, nsIImapProtocol *, nsIMsgMailNewsUrl *,
bool, bool, nsresult)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, ReleaseUrlCacheEntry, nsIMsgMailNewsUrl *)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, HeaderFetchCompleted, nsIImapProtocol *)
NS_SYNCRUNNABLEMETHOD1(ImapMailFolderSink, SetBiffStateAndUpdate, int32_t)
NS_SYNCRUNNABLEMETHOD3(ImapMailFolderSink, ProgressStatusString, nsIImapProtocol*, const char*, const char16_t *)
NS_SYNCRUNNABLEMETHOD4(ImapMailFolderSink, PercentProgress, nsIImapProtocol*, const char16_t *, int64_t, int64_t)
NS_SYNCRUNNABLEMETHOD0(ImapMailFolderSink, ClearFolderRights)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, SetCopyResponseUid, const char *, nsIImapUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, SetAppendMsgUid, nsMsgKey, nsIImapUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapMailFolderSink, GetMessageId, nsIImapUrl *, nsACString &)
NS_SYNCRUNNABLEMETHOD2(ImapMessageSink, SetupMsgWriteStream, nsIFile *, bool)
NS_SYNCRUNNABLEMETHOD3(ImapMessageSink, ParseAdoptedMsgLine, const char *, nsMsgKey, nsIImapUrl *)
NS_SYNCRUNNABLEMETHOD4(ImapMessageSink, NormalEndMsgWriteStream, nsMsgKey, bool, nsIImapUrl *, int32_t)
NS_SYNCRUNNABLEMETHOD0(ImapMessageSink, AbortMsgWriteStream)
NS_SYNCRUNNABLEMETHOD0(ImapMessageSink, BeginMessageUpload)
NS_SYNCRUNNABLEMETHOD4(ImapMessageSink, NotifyMessageFlags, uint32_t, const nsACString &, nsMsgKey, uint64_t)
NS_SYNCRUNNABLEMETHOD3(ImapMessageSink, NotifyMessageDeleted, const char *, bool, const char *)
NS_SYNCRUNNABLEMETHOD2(ImapMessageSink, GetMessageSizeFromDB, const char *, uint32_t *)
NS_SYNCRUNNABLEMETHOD2(ImapMessageSink, SetContentModified, nsIImapUrl *, nsImapContentModifiedType)
NS_SYNCRUNNABLEMETHOD4(ImapMessageSink, GetCurMoveCopyMessageInfo, nsIImapUrl *, PRTime *, nsACString &, uint32_t *)
NS_SYNCRUNNABLEMETHOD4(ImapServerSink, PossibleImapMailbox, const nsACString &, char, int32_t, bool *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FolderNeedsACLInitialized, const nsACString &, bool *)
NS_SYNCRUNNABLEMETHOD3(ImapServerSink, AddFolderRights, const nsACString &, const nsACString &, const nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, RefreshFolderRights, const nsACString &)
NS_SYNCRUNNABLEMETHOD0(ImapServerSink, DiscoveryDone)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, OnlineFolderDelete, const nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, OnlineFolderCreateFailed, const nsACString &)
NS_SYNCRUNNABLEMETHOD3(ImapServerSink, OnlineFolderRename, nsIMsgWindow *, const nsACString &, const nsACString &)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FolderIsNoSelect, const nsACString &, bool *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, SetFolderAdminURL, const nsACString &, const nsACString &)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FolderVerifiedOnline, const nsACString &, bool *)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, SetCapability, eIMAPCapabilityFlags)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, SetServerID, const nsACString &)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, LoadNextQueuedUrl, nsIImapProtocol *, bool *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, PrepareToRetryUrl, nsIImapUrl *, nsIImapMockChannel **)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, SuspendUrl, nsIImapUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, RetryUrl, nsIImapUrl *, nsIImapMockChannel *)
NS_SYNCRUNNABLEMETHOD0(ImapServerSink, AbortQueuedUrls)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, GetImapStringByName, const char*, nsAString &)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, PromptLoginFailed, nsIMsgWindow *, int32_t *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FEAlert, const nsAString &, nsIMsgMailNewsUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FEAlertWithName, const char*, nsIMsgMailNewsUrl *)
NS_SYNCRUNNABLEMETHOD2(ImapServerSink, FEAlertFromServer, const nsACString &, nsIMsgMailNewsUrl *)
NS_SYNCRUNNABLEMETHOD0(ImapServerSink, CommitNamespaces)
NS_SYNCRUNNABLEMETHOD3(ImapServerSink, AsyncGetPassword, nsIImapProtocol *, bool, nsACString &)
NS_SYNCRUNNABLEATTRIBUTE(ImapServerSink, UserAuthenticated, bool)
NS_SYNCRUNNABLEMETHOD3(ImapServerSink, SetMailServerUrls, const nsACString &, const nsACString &, const nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetArbitraryHeaders, nsACString &)
NS_SYNCRUNNABLEMETHOD0(ImapServerSink, ForgetPassword)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetShowAttachmentsInline, bool *)
NS_SYNCRUNNABLEMETHOD3(ImapServerSink, CramMD5Hash, const char *, const char *, char **)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetLoginUsername, nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, UpdateTrySTARTTLSPref, bool)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetOriginalUsername, nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetServerKey, nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetServerPassword, nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, RemoveServerConnection, nsIImapProtocol *)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, GetServerShuttingDown, bool *)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, ResetServerConnection, const nsACString &)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, SetServerDoingLsub, bool)
NS_SYNCRUNNABLEMETHOD1(ImapServerSink, SetServerForceSelect, const nsACString &)
namespace mozilla {
namespace mailnews {
NS_IMPL_ISUPPORTS(OAuth2ThreadHelper, msgIOAuth2ModuleListener)
OAuth2ThreadHelper::OAuth2ThreadHelper(nsIMsgIncomingServer *aServer)
: mMonitor("OAuth thread lock"),
mServer(aServer)
{
}
OAuth2ThreadHelper::~OAuth2ThreadHelper()
{
if (mOAuth2Support)
{
NS_ReleaseOnMainThread(mOAuth2Support.forget());
}
}
bool OAuth2ThreadHelper::SupportsOAuth2()
{
// Acquire a lock early, before reading anything. Guarantees memory visibility
// issues.
MonitorAutoLock lockGuard(mMonitor);
// If we don't have a server, we can't init, and therefore, we don't support
// OAuth2.
if (!mServer)
return false;
// If we have this, then we support OAuth2.
if (mOAuth2Support)
return true;
// Initialize. This needs to be done on-main-thread: if we're off that thread,
// synchronously dispatch to the main thread.
if (NS_IsMainThread())
{
MonitorAutoUnlock lockGuard(mMonitor);
Init();
}
else
{
nsCOMPtr<nsIRunnable> runInit =
NewRunnableMethod(this, &OAuth2ThreadHelper::Init);
NS_DispatchToMainThread(runInit);
mMonitor.Wait();
}
// After synchronously initializing, if we didn't get an object, then we don't
// support XOAuth2.
return mOAuth2Support != nullptr;
}
void OAuth2ThreadHelper::GetXOAuth2String(nsACString &base64Str)
{
MOZ_ASSERT(!NS_IsMainThread(), "This method cannot run on the main thread");
// Acquire a lock early, before reading anything. Guarantees memory visibility
// issues.
MonitorAutoLock lockGuard(mMonitor);
// Umm... what are you trying to do?
if (!mOAuth2Support)
return;
nsCOMPtr<nsIRunnable> runInit =
NewRunnableMethod(this, &OAuth2ThreadHelper::Connect);
NS_DispatchToMainThread(runInit);
mMonitor.Wait();
// Now we either have the string, or we failed (in which case the string is
// empty).
base64Str = mOAuth2String;
}
void OAuth2ThreadHelper::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Can't touch JS off-main-thread");
MonitorAutoLock lockGuard(mMonitor);
// Create the OAuth2 helper module and initialize it. If the preferences are
// not set up on this server, we don't support OAuth2, and we nullify our
// members to indicate this.
mOAuth2Support = do_CreateInstance(MSGIOAUTH2MODULE_CONTRACTID);
if (mOAuth2Support)
{
bool supportsOAuth = false;
mOAuth2Support->InitFromMail(mServer, &supportsOAuth);
if (!supportsOAuth)
mOAuth2Support = nullptr;
}
// There is now no longer any need for the server. Kill it now--this helps
// prevent us from maintaining a refcount cycle.
mServer = nullptr;
// Notify anyone waiting that we're done.
mMonitor.Notify();
}
void OAuth2ThreadHelper::Connect()
{
MOZ_ASSERT(NS_IsMainThread(), "Can't touch JS off-main-thread");
MOZ_ASSERT(mOAuth2Support, "Should not be here if no OAuth2 support");
// OK to delay lock since mOAuth2Support is only written on main thread.
nsresult rv = mOAuth2Support->Connect(true, this);
// If the method failed, we'll never get a callback, so notify the monitor
// immediately so that IMAP can react.
if (NS_FAILED(rv))
{
MonitorAutoLock lockGuard(mMonitor);
mMonitor.Notify();
}
}
nsresult OAuth2ThreadHelper::OnSuccess(const nsACString &aAccessToken)
{
MOZ_ASSERT(NS_IsMainThread(), "Can't touch JS off-main-thread");
MonitorAutoLock lockGuard(mMonitor);
MOZ_ASSERT(mOAuth2Support, "Should not be here if no OAuth2 support");
mOAuth2Support->BuildXOAuth2String(mOAuth2String);
mMonitor.Notify();
return NS_OK;
}
nsresult OAuth2ThreadHelper::OnFailure(nsresult aError)
{
MOZ_ASSERT(NS_IsMainThread(), "Can't touch JS off-main-thread");
MonitorAutoLock lockGuard(mMonitor);
mOAuth2String.Truncate();
mMonitor.Notify();
return NS_OK;
}
} // namespace mailnews
} // namespace mozilla

View file

@ -0,0 +1,146 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsSyncRunnableHelpers_h
#define nsSyncRunnableHelpers_h
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
#include "mozilla/Monitor.h"
#include "msgIOAuth2Module.h"
#include "nsIStreamListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIImapMailFolderSink.h"
#include "nsIImapServerSink.h"
#include "nsIImapProtocolSink.h"
#include "nsIImapMessageSink.h"
// The classes in this file proxy method calls to the main thread
// synchronously. The main thread must not block on this thread, or a
// deadlock condition can occur.
class StreamListenerProxy final : public nsIStreamListener
{
public:
StreamListenerProxy(nsIStreamListener* receiver)
: mReceiver(receiver)
{ }
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
private:
~StreamListenerProxy() {
NS_ReleaseOnMainThread(mReceiver.forget());
}
nsCOMPtr<nsIStreamListener> mReceiver;
};
class ImapMailFolderSinkProxy final : public nsIImapMailFolderSink
{
public:
ImapMailFolderSinkProxy(nsIImapMailFolderSink* receiver)
: mReceiver(receiver)
{
NS_ASSERTION(receiver, "Don't allow receiver is nullptr");
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPMAILFOLDERSINK
private:
~ImapMailFolderSinkProxy() {
NS_ReleaseOnMainThread(mReceiver.forget());
}
nsCOMPtr<nsIImapMailFolderSink> mReceiver;
};
class ImapServerSinkProxy final : public nsIImapServerSink
{
public:
ImapServerSinkProxy(nsIImapServerSink* receiver)
: mReceiver(receiver)
{ }
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPSERVERSINK
private:
~ImapServerSinkProxy() {
NS_ReleaseOnMainThread(mReceiver.forget());
}
nsCOMPtr<nsIImapServerSink> mReceiver;
};
class ImapMessageSinkProxy final : public nsIImapMessageSink
{
public:
ImapMessageSinkProxy(nsIImapMessageSink* receiver)
: mReceiver(receiver)
{ }
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPMESSAGESINK
private:
~ImapMessageSinkProxy() {
NS_ReleaseOnMainThread(mReceiver.forget());
}
nsCOMPtr<nsIImapMessageSink> mReceiver;
};
class ImapProtocolSinkProxy final : public nsIImapProtocolSink
{
public:
ImapProtocolSinkProxy(nsIImapProtocolSink* receiver)
: mReceiver(receiver)
{ }
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIMAPPROTOCOLSINK
private:
~ImapProtocolSinkProxy() {
NS_ReleaseOnMainThread(mReceiver.forget());
}
nsCOMPtr<nsIImapProtocolSink> mReceiver;
};
class msgIOAuth2Module;
class nsIMsgIncomingServer;
class nsIVariant;
class nsIWritableVariant;
namespace mozilla {
namespace mailnews {
class OAuth2ThreadHelper final : public msgIOAuth2ModuleListener
{
public:
OAuth2ThreadHelper(nsIMsgIncomingServer *aServer);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MSGIOAUTH2MODULELISTENER
bool SupportsOAuth2();
void GetXOAuth2String(nsACString &base64Str);
private:
~OAuth2ThreadHelper();
void Init();
void Connect();
Monitor mMonitor;
nsCOMPtr<msgIOAuth2Module> mOAuth2Support;
nsCOMPtr<nsIMsgIncomingServer> mServer;
nsCString mOAuth2String;
};
} // namespace mailnews
} // namespace mozilla
#endif // nsSyncRunnableHelpers_h