From df476382d9906e482957fcb5d949e97dd28926c2 Mon Sep 17 00:00:00 2001 From: "mscott%netscape.com" Date: Thu, 13 Jan 2000 04:54:23 +0000 Subject: [PATCH] Landing imap integration with the memory cache!! *Yeah* Bug #23491 --> add memory cache support for imap urls. Bug #23325 --> fix a crasher...only try to find a folder if we have a folder name r=bienvenu --- mailnews/imap/src/nsImapProtocol.cpp | 146 +++++++++++++++++++------ mailnews/imap/src/nsImapProtocol.h | 20 ++-- mailnews/imap/src/nsImapService.cpp | 153 ++++++++++++++------------- 3 files changed, 200 insertions(+), 119 deletions(-) diff --git a/mailnews/imap/src/nsImapProtocol.cpp b/mailnews/imap/src/nsImapProtocol.cpp index 2de5742757dd..8c1c16edb368 100644 --- a/mailnews/imap/src/nsImapProtocol.cpp +++ b/mailnews/imap/src/nsImapProtocol.cpp @@ -49,6 +49,12 @@ #include "nsIPipe.h" #include "nsIMsgFolder.h" #include "nsImapStringBundle.h" + +// for the memory cache... +#include "nsINetDataCacheManager.h" +#include "nsINetDataCache.h" +#include "nsICachedNetData.h" + #include "nsCOMPtr.h" PRLogModuleInfo *IMAP; @@ -1127,8 +1133,6 @@ PRBool nsImapProtocol::ProcessCurrentURL() // a place where we know we're finished with the url. if (m_runningUrl && m_imapServerSink) m_imapServerSink->RemoveChannelFromUrl(mailnewsurl, NS_OK); - - m_runningUrl->RemoveChannel(NS_OK); // release this by hand so that we can load the next queued url without thinking // this connection is busy running a url. @@ -2062,34 +2066,35 @@ void nsImapProtocol::BeginMessageDownLoad( m_imapMailFolderSink->SetupHeaderParseStream(this, total_message_size, content_type, nsnull); } } - // if we have a mock channel, that means we have a channel listener who wants the - // message. So set up a pipe. We'll write the messsage into one end of the pipe - // and they will read it out of the other end. - else if (m_channelListener) - { - // create a pipe to pump the message into...the output will go to whoever - // is consuming the message display - nsresult rv; - rv = NS_NewPipe(getter_AddRefs(m_channelInputStream), getter_AddRefs(m_channelOutputStream)); - NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed!"); - } - // else, if we are saving the message to disk! - else if (m_imapMessageSink /* && m_imapAction == nsIImapUrl::nsImapSaveMessageToDisk */) - { - nsCOMPtr fileSpec; - PRBool addDummyEnvelope = PR_TRUE; - nsCOMPtr msgurl = do_QueryInterface(m_runningUrl); - msgurl->GetMessageFile(getter_AddRefs(fileSpec)); - msgurl->GetAddDummyEnvelope(&addDummyEnvelope); + + // if we have a mock channel, that means we have a channel listener who wants the + // message. So set up a pipe. We'll write the messsage into one end of the pipe + // and they will read it out of the other end. + else if (m_channelListener) + { + // create a pipe to pump the message into...the output will go to whoever + // is consuming the message display + nsresult rv; + rv = NS_NewPipe(getter_AddRefs(m_channelInputStream), getter_AddRefs(m_channelOutputStream)); + NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed!"); + } + // else, if we are saving the message to disk! + else if (m_imapMessageSink /* && m_imapAction == nsIImapUrl::nsImapSaveMessageToDisk */) + { + nsCOMPtr fileSpec; + PRBool addDummyEnvelope = PR_TRUE; + nsCOMPtr msgurl = do_QueryInterface(m_runningUrl); + msgurl->GetMessageFile(getter_AddRefs(fileSpec)); + msgurl->GetAddDummyEnvelope(&addDummyEnvelope); // m_imapMessageSink->SetupMsgWriteStream(fileSpec, addDummyEnvelope); - nsXPIDLCString nativePath; - NS_ASSERTION(fileSpec, "no fileSpec!"); - if (fileSpec) - { - fileSpec->GetNativePath(getter_Copies(nativePath)); - m_imapMessageSink->SetupMsgWriteStream(nativePath, addDummyEnvelope); - } - } + nsXPIDLCString nativePath; + NS_ASSERTION(fileSpec, "no fileSpec!"); + if (fileSpec) + { + fileSpec->GetNativePath(getter_Copies(nativePath)); + m_imapMessageSink->SetupMsgWriteStream(nativePath, addDummyEnvelope); + } + } } else HandleMemoryFailure(); @@ -6254,13 +6259,18 @@ NS_IMPL_ISUPPORTS2(nsImapMockChannel, nsIImapMockChannel, nsIChannel) nsImapMockChannel::nsImapMockChannel() { - NS_INIT_REFCNT(); - m_channelContext = nsnull; + NS_INIT_REFCNT(); + m_channelContext = nsnull; m_cancelled = PR_FALSE; + mOwningRefToUrl = PR_FALSE; } nsImapMockChannel::~nsImapMockChannel() { + // only release the url if we have a owning ref on it... + // this only occurrs when we are loading the url from the cache... + if (mOwningRefToUrl && m_url) + NS_RELEASE(m_url); } @@ -6341,12 +6351,60 @@ NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamObserver *observer, nsISuppo NS_IMETHODIMP nsImapMockChannel::AsyncRead(PRUint32 startPosition, PRInt32 readCount, nsISupports *ctxt, nsIStreamListener *listener) { + nsCOMPtr cacheEntry; + PRUint32 contentLength = 0; + PRBool partialFlag = PR_FALSE; + // set the stream listener and then load the url m_channelContext = ctxt; m_channelListener = listener; - // the following load group code is completely bogus.... nsresult rv = NS_OK; + + nsCOMPtr imapUrl = do_QueryInterface(m_url); + nsCOMPtr mailnewsUrl = do_QueryInterface(m_url, &rv); + if (NS_FAILED(rv)) return rv; + + // look to see if this url should be added to the memory cache.. + PRBool useMemoryCache = PR_FALSE; + mailnewsUrl->GetAddToMemoryCache(&useMemoryCache); + nsCOMPtr cacheManager = do_GetService(NS_NETWORK_CACHE_MANAGER_PROGID, &rv); + if (NS_SUCCEEDED(rv) && cacheManager) + { + // Retrieve an existing cache entry or create a new one if none exists for the + // given URL. + nsXPIDLCString urlCString; + // eventually we are going to want to use the url spec - the query/ref part 'cause that doesn't + // distinguish urls....... + m_url->GetSpec(getter_Copies(urlCString)); + // for now, truncate of the query part so we don't duplicate urls in the cache... + char * anchor = PL_strrchr(urlCString, '?'); + if (anchor) + *anchor = '\0'; + rv = cacheManager->GetCachedNetData(urlCString, 0, 0, nsINetDataCacheManager::BYPASS_PERSISTENT_CACHE, + getter_AddRefs(cacheEntry)); + if (NS_SUCCEEDED(rv) && cacheEntry) + { + PRBool updateInProgress; + cacheEntry->GetPartialFlag(&partialFlag); + cacheEntry->GetUpdateInProgress(&updateInProgress); + cacheEntry->GetStoredContentLength(&contentLength); + // only try to update the cache entry if it isn't being used. + // and we want to write to the memory cache. + if (!updateInProgress && useMemoryCache) + { + // now we need to figure out if the entry is new / or partially unfinished... + // this determines if we are going to USE the cache entry for reading the data + // vs. if we need to write data into the cache entry... + if (!contentLength || partialFlag) + // we're going to fill up this cache entry, + rv = cacheEntry->InterceptAsyncRead(listener, 0, getter_AddRefs(m_channelListener)); + } + } + } + + // regardless as to whether we are inserting into the cache or not, proceed with + // setting up the load group! if (m_loadGroup) { nsCOMPtr factory; @@ -6363,10 +6421,28 @@ NS_IMETHODIMP nsImapMockChannel::AsyncRead(PRUint32 startPosition, PRInt32 readC } } // if aLoadGroup + // now, determine if we should be loading from the cache or if we have + // to really load the msg with a protocol connection... + if (cacheEntry && contentLength > 0 && !partialFlag) + { + nsCOMPtr cacheChannel; + rv = cacheEntry->NewChannel(m_loadGroup, this, getter_AddRefs(cacheChannel)); + if (NS_SUCCEEDED(rv)) + { + // turn around and make our ref on m_url an owning ref...and force the url to remove + // its reference on the mock channel...this is a complicated texas two step to solve + // a nasty reference counting problem... + NS_IF_ADDREF(m_url); + mOwningRefToUrl = PR_TRUE; + imapUrl->SetMockChannel(nsnull); + + rv = cacheChannel->AsyncRead(startPosition, readCount, m_channelContext, m_channelListener); + if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return + return rv; + } + } + // loading the url consists of asking the server to add the url to it's imap event queue.... - nsCOMPtr imapUrl = do_QueryInterface(m_url); - nsCOMPtr mailnewsUrl = do_QueryInterface(m_url, &rv); - if (NS_FAILED(rv)) return rv; nsCOMPtr server; rv = mailnewsUrl->GetServer(getter_AddRefs(server)); if (NS_FAILED(rv)) return rv; diff --git a/mailnews/imap/src/nsImapProtocol.h b/mailnews/imap/src/nsImapProtocol.h index b943705411e9..98d37a8ce10f 100644 --- a/mailnews/imap/src/nsImapProtocol.h +++ b/mailnews/imap/src/nsImapProtocol.h @@ -552,21 +552,25 @@ class nsImapMockChannel : public nsIImapMockChannel public: NS_DECL_ISUPPORTS - NS_DECL_NSIIMAPMOCKCHANNEL - NS_DECL_NSICHANNEL - NS_DECL_NSIREQUEST + NS_DECL_NSIIMAPMOCKCHANNEL + NS_DECL_NSICHANNEL + NS_DECL_NSIREQUEST - nsImapMockChannel(); + nsImapMockChannel(); virtual ~nsImapMockChannel(); - - static nsresult Create (const nsIID& iid, void **result); + static nsresult Create (const nsIID& iid, void **result); protected: // we CANNOT own the uri else we will have a circular ref count // because the imap uri ref counts us....so don't think about - // turning this into a com ptr! - nsCOMPtr m_originalUrl; + // turning this into a com ptr! HOWEVER, because life is complicated, + // there is one scenario where we need to own the url....and that + // is when we are loading the url from the cache....so in that case, + // we'll turn around and make our m_url ptr an owning reference... *sigh* nsIURI * m_url; + PRBool mOwningRefToUrl; + + nsCOMPtr m_originalUrl; nsCOMPtr m_loadGroup; nsCOMPtr m_channelListener; // non owning ref of the context in order to fix a circular ref count diff --git a/mailnews/imap/src/nsImapService.cpp b/mailnews/imap/src/nsImapService.cpp index c2ddea467d1d..30e5e8e90ce2 100644 --- a/mailnews/imap/src/nsImapService.cpp +++ b/mailnews/imap/src/nsImapService.cpp @@ -215,10 +215,10 @@ NS_IMETHODIMP nsImapService::GetUrlForUri(const char *aMessageURI, nsIURI **aURL { nsCOMPtr imapUrl; nsCAutoString urlSpec; - PRUnichar hierarchySeparator = '/'; + PRUnichar hierarchySeparator = '/'; rv = CreateStartOfImapUrl(getter_AddRefs(imapUrl), folder, nsnull, urlSpec, hierarchySeparator); if (NS_FAILED(rv)) return rv; - imapUrl->SetImapMessageSink(imapMessageSink); + imapUrl->SetImapMessageSink(imapMessageSink); nsCOMPtr url = do_QueryInterface(imapUrl); nsXPIDLCString currentSpec; @@ -264,8 +264,10 @@ NS_IMETHODIMP nsImapService::DisplayMessage(const char* aMessageURI, PRUnichar hierarchySeparator = '/'; rv = CreateStartOfImapUrl(getter_AddRefs(imapUrl), folder, aUrlListener, urlSpec, hierarchySeparator); if (NS_FAILED(rv)) return rv; - nsCOMPtr msgurl (do_QueryInterface(imapUrl)); + nsCOMPtr msgurl (do_QueryInterface(imapUrl)); msgurl->SetMsgWindow(aMsgWindow); + // whenever we are displaying a message, we want to add it to the memory cache.. + msgurl->SetAddToMemoryCache(PR_TRUE); imapUrl->AddChannelToLoadGroup(); rv = FetchMessage(imapUrl, nsIImapUrl::nsImapMsgFetch, folder, imapMessageSink, aURL, aDisplayConsumer, msgKey, PR_TRUE); @@ -295,7 +297,7 @@ nsImapService::CopyMessage(const char * aSrcMailboxURI, nsIStreamListener * { nsCOMPtr imapUrl; nsCAutoString urlSpec; - PRUnichar hierarchySeparator = '/'; + PRUnichar hierarchySeparator = '/'; rv = CreateStartOfImapUrl(getter_AddRefs(imapUrl), folder, aUrlListener, urlSpec, hierarchySeparator); // now try to download the message @@ -428,7 +430,7 @@ NS_IMETHODIMP nsImapService::SaveMessageToDisk(const char *aMessageURI, if (NS_FAILED(rv)) return rv; nsCAutoString urlSpec; - PRUnichar hierarchySeparator = '/'; + PRUnichar hierarchySeparator = '/'; rv = CreateStartOfImapUrl(getter_AddRefs(imapUrl), folder, aUrlListener, urlSpec, hierarchySeparator); if (NS_SUCCEEDED(rv)) { @@ -527,10 +529,10 @@ nsImapService::FetchMessage(nsIImapUrl * aImapUrl, if (NS_SUCCEEDED(rv) && aStreamListener) { nsCOMPtr aChannel; - nsCOMPtr aLoadGroup; - nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl, &rv); - if (NS_SUCCEEDED(rv) && mailnewsUrl) - mailnewsUrl->GetLoadGroup(getter_AddRefs(aLoadGroup)); + nsCOMPtr aLoadGroup; + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl) + mailnewsUrl->GetLoadGroup(getter_AddRefs(aLoadGroup)); rv = NewChannel(nsnull, url, aLoadGroup, nsnull, nsIChannel::LOAD_NORMAL, nsnull, 0, 0, getter_AddRefs(aChannel)); @@ -589,28 +591,28 @@ nsImapService::CreateStartOfImapUrl(nsIImapUrl ** imapUrl, imapUrl); if (NS_SUCCEEDED(rv) && imapUrl) { - nsCOMPtr mailnewsUrl = do_QueryInterface(*imapUrl, &rv); - if (NS_SUCCEEDED(rv) && mailnewsUrl && aUrlListener) - mailnewsUrl->RegisterListener(aUrlListener); + nsCOMPtr mailnewsUrl = do_QueryInterface(*imapUrl, &rv); + if (NS_SUCCEEDED(rv) && mailnewsUrl && aUrlListener) + mailnewsUrl->RegisterListener(aUrlListener); - urlSpec = "imap://"; - urlSpec.Append(username); - urlSpec.Append('@'); - urlSpec.Append(hostname); - urlSpec.Append(':'); - urlSpec.Append("143"); // mscott -- i know this is bogus...i'm i a hurry =) + urlSpec = "imap://"; + urlSpec.Append(username); + urlSpec.Append('@'); + urlSpec.Append(hostname); + urlSpec.Append(':'); + urlSpec.Append("143"); // mscott -- i know this is bogus...i'm i a hurry =) - // *** jefft - force to parse the urlSpec in order to search for - // the correct incoming server - // mscott - this cast to a char * is okay...there's a bug in the XPIDL - // compiler that is preventing in string parameters from showing up as - // const char *. hopefully they will fix it soon. - rv = mailnewsUrl->SetSpec((char *) urlSpec.GetBuffer()); + // *** jefft - force to parse the urlSpec in order to search for + // the correct incoming server + // mscott - this cast to a char * is okay...there's a bug in the XPIDL + // compiler that is preventing in string parameters from showing up as + // const char *. hopefully they will fix it soon. + rv = mailnewsUrl->SetSpec((char *) urlSpec.GetBuffer()); - hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; - nsCOMPtr imapFolder = do_QueryInterface(aImapMailFolder); - if (imapFolder) - imapFolder->GetHierarchyDelimiter(&hierarchyDelimiter); + hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; + nsCOMPtr imapFolder = do_QueryInterface(aImapMailFolder); + if (imapFolder) + imapFolder->GetHierarchyDelimiter(&hierarchyDelimiter); } PR_FREEIF(hostname); @@ -2297,58 +2299,57 @@ NS_IMETHODIMP nsImapService::GetDefaultPort(PRInt32 *aDefaultPort) NS_IMETHODIMP nsImapService::NewURI(const char *aSpec, nsIURI *aBaseURI, nsIURI **_retval) { - nsCOMPtr aImapUrl; + nsCOMPtr aImapUrl; nsresult rv = nsComponentManager::CreateInstance(kImapUrlCID, nsnull, NS_GET_IID(nsIImapUrl), getter_AddRefs(aImapUrl)); - if (NS_SUCCEEDED(rv)) - { - // now extract lots of fun information... - nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl); - nsCAutoString unescapedSpec = aSpec; - nsUnescape(unescapedSpec); - mailnewsUrl->SetSpec((char *) unescapedSpec); // set the url spec... - - nsXPIDLCString userName; - nsXPIDLCString hostName; - nsXPIDLCString folderName; - - // extract the user name and host name information... - rv = mailnewsUrl->GetHost(getter_Copies(hostName)); - if (NS_FAILED(rv)) return rv; - rv = mailnewsUrl->GetPreHost(getter_Copies(userName)); - if (NS_FAILED(rv)) return rv; - // if we can't get a folder name out of the url then I think this is an error - aImapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(folderName)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr aServer; - rv = nsGetImapServer(userName, hostName, getter_AddRefs(aServer)); - // if we can't extract the imap server from this url then give up!!! - if (NS_FAILED(rv)) return rv; - - // now try to get the folder in question... - nsCOMPtr aRootFolder; - aServer->GetRootFolder(getter_AddRefs(aRootFolder)); - - if (aRootFolder) - { - nsCOMPtr aFolder; - rv = aRootFolder->FindSubFolder(folderName, getter_AddRefs(aFolder)); - if (NS_SUCCEEDED(rv)) - { - nsCOMPtr msgSink = do_QueryInterface(aFolder); - rv = aImapUrl->SetImapMessageSink(msgSink); - - nsCOMPtr msgFolder = do_QueryInterface(aFolder); - rv = SetImapUrlSink(msgFolder, aImapUrl); - } - - } + if (NS_SUCCEEDED(rv)) + { + // now extract lots of fun information... + nsCOMPtr mailnewsUrl = do_QueryInterface(aImapUrl); + nsCAutoString unescapedSpec = aSpec; + nsUnescape(unescapedSpec); + mailnewsUrl->SetSpec((char *) unescapedSpec); // set the url spec... - // we got an imap url, so be sure to return it... - aImapUrl->QueryInterface(NS_GET_IID(nsIURI), (void **) _retval); + nsXPIDLCString userName; + nsXPIDLCString hostName; + nsXPIDLCString folderName; + + // extract the user name and host name information... + rv = mailnewsUrl->GetHost(getter_Copies(hostName)); + if (NS_FAILED(rv)) return rv; + rv = mailnewsUrl->GetPreHost(getter_Copies(userName)); + if (NS_FAILED(rv)) return rv; + // if we can't get a folder name out of the url then I think this is an error + aImapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(folderName)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr aServer; + rv = nsGetImapServer(userName, hostName, getter_AddRefs(aServer)); + // if we can't extract the imap server from this url then give up!!! + if (NS_FAILED(rv)) return rv; + + // now try to get the folder in question... + nsCOMPtr aRootFolder; + aServer->GetRootFolder(getter_AddRefs(aRootFolder)); + + if (aRootFolder && folderName && (* ((const char *) folderName)) ) + { + nsCOMPtr aFolder; + rv = aRootFolder->FindSubFolder(folderName, getter_AddRefs(aFolder)); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr msgSink = do_QueryInterface(aFolder); + rv = aImapUrl->SetImapMessageSink(msgSink); + + nsCOMPtr msgFolder = do_QueryInterface(aFolder); + rv = SetImapUrlSink(msgFolder, aImapUrl); + } } - return rv; + // we got an imap url, so be sure to return it... + aImapUrl->QueryInterface(NS_GET_IID(nsIURI), (void **) _retval); + } + + return rv; } NS_IMETHODIMP nsImapService::NewChannel(const char *verb,