diff --git a/mailnews/base/public/nsIMsgMailNewsUrl.idl b/mailnews/base/public/nsIMsgMailNewsUrl.idl index a2558f2a976..20112927227 100644 --- a/mailnews/base/public/nsIMsgMailNewsUrl.idl +++ b/mailnews/base/public/nsIMsgMailNewsUrl.idl @@ -30,7 +30,7 @@ interface nsIMsgIncomingServer; interface nsIMsgWindow; interface nsILoadGroup; interface nsIMsgSearchSession; -interface nsICachedNetData; +interface nsICacheEntryDescriptor; [scriptable, uuid(6CFFCEB0-CB8C-11d2-8065-006008128C4E)] interface nsIMsgMailNewsUrl : nsIURL { @@ -72,7 +72,7 @@ interface nsIMsgMailNewsUrl : nsIURL { attribute boolean msgIsInLocalCache; attribute boolean suppressErrorMsgs; // used to avoid displaying biff error messages - attribute nsICachedNetData memCacheEntry; + attribute nsICacheEntryDescriptor memCacheEntry; const unsigned long eCopy = 0; const unsigned long eMove = 1; @@ -98,8 +98,8 @@ interface nsIMsgMessageUrl : nsISupports { // used by imap, pop and nntp in order to implement save message to disk attribute nsIFileSpec messageFile; attribute boolean AddDummyEnvelope; - attribute boolean canonicalLineEnding; - attribute string originalSpec; + attribute boolean canonicalLineEnding; + attribute string originalSpec; }; ////////////////////////////////////////////////////////////////////////////////// diff --git a/mailnews/base/util/nsMsgMailNewsUrl.cpp b/mailnews/base/util/nsMsgMailNewsUrl.cpp index a8fc3fd2914..90470a3ff27 100644 --- a/mailnews/base/util/nsMsgMailNewsUrl.cpp +++ b/mailnews/base/util/nsMsgMailNewsUrl.cpp @@ -34,7 +34,6 @@ #include "nsIWebProgressListener.h" #include "nsIInterfaceRequestor.h" #include "nsIIOService.h" -#include "nsINetDataCacheManager.h" #include "nsNetCID.h" static NS_DEFINE_CID(kUrlListenerManagerCID, NS_URLLISTENERMANAGER_CID); @@ -605,36 +604,17 @@ NS_IMETHODIMP nsMsgMailNewsUrl::SetFilePath(const char *i_DirFile) return m_baseURL->SetFilePath(i_DirFile); } -NS_IMETHODIMP nsMsgMailNewsUrl::SetMemCacheEntry(nsICachedNetData *memCacheEntry) +NS_IMETHODIMP nsMsgMailNewsUrl::SetMemCacheEntry(nsICacheEntryDescriptor *memCacheEntry) { m_memCacheEntry = memCacheEntry; return NS_OK; } -NS_IMETHODIMP nsMsgMailNewsUrl:: GetMemCacheEntry(nsICachedNetData **memCacheEntry) +NS_IMETHODIMP nsMsgMailNewsUrl:: GetMemCacheEntry(nsICacheEntryDescriptor **memCacheEntry) { NS_ENSURE_ARG(memCacheEntry); nsresult rv = NS_OK; - if (!m_memCacheEntry) - { - nsCOMPtr cacheManager = do_GetService(NS_NETWORK_CACHE_MANAGER_CONTRACTID, &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....... - 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(m_memCacheEntry)); - } - } if (m_memCacheEntry) { *memCacheEntry = m_memCacheEntry; @@ -645,6 +625,6 @@ NS_IMETHODIMP nsMsgMailNewsUrl:: GetMemCacheEntry(nsICachedNetData **memCacheEnt *memCacheEntry = nsnull; return NS_ERROR_NULL_POINTER; } + return rv; } - diff --git a/mailnews/base/util/nsMsgMailNewsUrl.h b/mailnews/base/util/nsMsgMailNewsUrl.h index 5ce3ba7857a..5470a1db87a 100644 --- a/mailnews/base/util/nsMsgMailNewsUrl.h +++ b/mailnews/base/util/nsMsgMailNewsUrl.h @@ -18,6 +18,7 @@ * Rights Reserved. * * Contributor(s): + * Scott MacGregor */ #ifndef nsMsgMailNewsUrl_h___ @@ -34,7 +35,8 @@ #include "nsIURL.h" #include "nsILoadGroup.h" #include "nsIMsgSearchSession.h" -#include "nsICachedNetData.h" +#include "nsICacheEntryDescriptor.h" + /////////////////////////////////////////////////////////////////////////////////// // Okay, I found that all of the mail and news url interfaces needed to support // several common interfaces (in addition to those provided through nsIURI). @@ -64,7 +66,7 @@ protected: nsCOMPtr m_msgWindow; nsCOMPtr m_loadGroup; nsCOMPtr m_searchSession; - nsCOMPtr m_memCacheEntry; + nsCOMPtr m_memCacheEntry; char *m_errorMessage; PRBool m_runningUrl; PRBool m_updatingFolder; diff --git a/mailnews/imap/public/nsIImapMailFolderSink.idl b/mailnews/imap/public/nsIImapMailFolderSink.idl index 6da10afc27b..3364076e456 100644 --- a/mailnews/imap/public/nsIImapMailFolderSink.idl +++ b/mailnews/imap/public/nsIImapMailFolderSink.idl @@ -27,13 +27,13 @@ #include "nsIMailboxSpec.idl" interface nsIMsgMailNewsUrl; +interface nsIImapMockChannel; typedef long ImapOnlineCopyState; [scriptable, uuid(5f7484b0-68b4-11d3-a53e-0060b0fc04b7)] - - interface ImapOnlineCopyStateType - { +interface ImapOnlineCopyStateType +{ const long kInProgress = 0; const long kSuccessfulCopy = 1; const long kSuccessfulMove = 2; @@ -44,34 +44,32 @@ typedef long ImapOnlineCopyState; const long kInterruptedState = 7; const long kFailedCopy = 8; const long kFailedMove = 9; - }; - +}; [scriptable, uuid(3b2dd7e0-e72c-11d2-ab7b-00805f8ac968)] - interface nsIImapMailFolderSink : nsISupports { - attribute boolean folderNeedsACLListed; - attribute boolean folderNeedsSubscribing; - attribute boolean folderNeedsAdded; + attribute boolean folderNeedsACLListed; + attribute boolean folderNeedsSubscribing; + attribute boolean folderNeedsAdded; attribute boolean folderVerifiedOnline; - string GetOnlineDelimiter(); - // Tell mail master about the newly selected mailbox - void UpdateImapMailboxInfo(in nsIImapProtocol aProtocol, + string GetOnlineDelimiter(); + // Tell mail master about the newly selected mailbox + void UpdateImapMailboxInfo(in nsIImapProtocol aProtocol, + in nsIMailboxSpec aSpec); + void UpdateImapMailboxStatus(in nsIImapProtocol aProtocol, in nsIMailboxSpec aSpec); - void UpdateImapMailboxStatus(in nsIImapProtocol aProtocol, - in nsIMailboxSpec aSpec); - void ChildDiscoverySucceeded(in nsIImapProtocol aProtocol) ; - void PromptUserForSubscribeUpdatePath(in nsIImapProtocol aProtocol, - out boolean aBool) ; - void SetupHeaderParseStream(in nsIImapProtocol aProtocol, in unsigned long size, in string content_type, in nsIMailboxSpec boxSpec); + void ChildDiscoverySucceeded(in nsIImapProtocol aProtocol) ; + void PromptUserForSubscribeUpdatePath(in nsIImapProtocol aProtocol, + out boolean aBool) ; + void SetupHeaderParseStream(in nsIImapProtocol aProtocol, in unsigned long size, in string content_type, in nsIMailboxSpec boxSpec); - void ParseAdoptedHeaderLine(in nsIImapProtocol aProtocol, in string messageLine, in unsigned long msgKey) ; - - void NormalEndHeaderParseStream(in nsIImapProtocol aProtocol); - - void AbortHeaderParseStream(in nsIImapProtocol aProtocol) ; - - void OnlineCopyCompleted(in nsIImapProtocol aProtocol, in ImapOnlineCopyState aCopyState); + void ParseAdoptedHeaderLine(in nsIImapProtocol aProtocol, in string messageLine, in unsigned long msgKey) ; + + void NormalEndHeaderParseStream(in nsIImapProtocol aProtocol); + + void AbortHeaderParseStream(in nsIImapProtocol aProtocol) ; + + void OnlineCopyCompleted(in nsIImapProtocol aProtocol, in ImapOnlineCopyState aCopyState); void StartMessage(in nsIMsgMailNewsUrl aUrl); void EndMessage(in nsIMsgMailNewsUrl aUrl, in nsMsgKey uidOfMessage); @@ -82,4 +80,5 @@ interface nsIImapMailFolderSink : nsISupports { // are only released / destroyed from the UI thread. void PrepareToReleaseUrl(in nsIMsgMailNewsUrl aUrl); void ReleaseUrl(); + void CloseMockChannel(in nsIImapMockChannel aChannel); }; diff --git a/mailnews/imap/public/nsIImapService.idl b/mailnews/imap/public/nsIImapService.idl index 5e11e7cfe55..907989d45e5 100644 --- a/mailnews/imap/public/nsIImapService.idl +++ b/mailnews/imap/public/nsIImapService.idl @@ -40,6 +40,7 @@ interface nsIEventQueue; interface nsIMsgFolder; interface nsIMsgWindow; interface nsIImapIncomingServer; +interface nsICacheSession; [scriptable, uuid(18236127-FA1D-11d3-98BA-001083010E9B)] @@ -224,4 +225,6 @@ interface nsIImapService : nsISupports void playbackAllOfflineOperations(in nsIMsgWindow aMsgWindow, in nsIUrlListener aListener); void downloadAllOffineImapFolders(in nsIMsgWindow aMsgWindow, in nsIUrlListener aListener); + + readonly attribute nsICacheSession cacheSession; }; diff --git a/mailnews/imap/src/nsImapIncomingServer.cpp b/mailnews/imap/src/nsImapIncomingServer.cpp index 8942c0e113e..dbd1c543500 100644 --- a/mailnews/imap/src/nsImapIncomingServer.cpp +++ b/mailnews/imap/src/nsImapIncomingServer.cpp @@ -65,9 +65,7 @@ #include "nsIPrompt.h" #include "nsIWindowWatcher.h" // for the memory cache... -#include "nsINetDataCacheManager.h" -#include "nsINetDataCache.h" -#include "nsICachedNetData.h" +#include "nsICacheEntryDescriptor.h" #include "nsImapUrl.h" #include "nsFileStream.h" @@ -475,10 +473,8 @@ nsImapIncomingServer::LoadNextQueuedUrl(PRBool *aResult) while (cnt > 0 && !urlRun && keepGoing) { - nsCOMPtr - aSupport(getter_AddRefs(m_urlQueue->ElementAt(0))); - nsCOMPtr - aImapUrl(do_QueryInterface(aSupport, &rv)); + nsCOMPtr aSupport(getter_AddRefs(m_urlQueue->ElementAt(0))); + nsCOMPtr aImapUrl(do_QueryInterface(aSupport, &rv)); nsCOMPtr aMailNewsUrl(do_QueryInterface(aSupport, &rv)); if (aImapUrl) @@ -487,9 +483,9 @@ nsImapIncomingServer::LoadNextQueuedUrl(PRBool *aResult) if (NS_SUCCEEDED(aImapUrl->GetMockChannel(getter_AddRefs(mockChannel))) && mockChannel) { - nsCOMPtr request = do_QueryInterface(mockChannel); + nsCOMPtr request = do_QueryInterface(mockChannel); if (!request) - return NS_ERROR_FAILURE; + return NS_ERROR_FAILURE; request->GetStatus(&rv); if (!NS_SUCCEEDED(rv)) { @@ -500,10 +496,10 @@ nsImapIncomingServer::LoadNextQueuedUrl(PRBool *aResult) if (aMailNewsUrl) { - nsCOMPtr cacheEntry; + nsCOMPtr cacheEntry; res = aMailNewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry)); if (NS_SUCCEEDED(res) && cacheEntry) - cacheEntry->Delete(); + cacheEntry->Doom(); } } } @@ -511,14 +507,11 @@ nsImapIncomingServer::LoadNextQueuedUrl(PRBool *aResult) // between the place we set it and here. if (NS_SUCCEEDED(rv)) { - nsISupports *aConsumer = - (nsISupports*)m_urlConsumers.ElementAt(0); - + nsISupports *aConsumer = (nsISupports*)m_urlConsumers.ElementAt(0); NS_IF_ADDREF(aConsumer); nsCOMPtr protocolInstance ; - rv = CreateImapConnection(nsnull, aImapUrl, - getter_AddRefs(protocolInstance)); + rv = CreateImapConnection(nsnull, aImapUrl, getter_AddRefs(protocolInstance)); if (NS_SUCCEEDED(rv) && protocolInstance) { nsCOMPtr url = do_QueryInterface(aImapUrl, &rv); diff --git a/mailnews/imap/src/nsImapMailFolder.cpp b/mailnews/imap/src/nsImapMailFolder.cpp index b91d26b2753..5ae72897699 100644 --- a/mailnews/imap/src/nsImapMailFolder.cpp +++ b/mailnews/imap/src/nsImapMailFolder.cpp @@ -70,6 +70,7 @@ #include "nsImapOfflineSync.h" #include "nsIMsgAccountManager.h" #include "nsQuickSort.h" +#include "nsIImapMockChannel.h" static NS_DEFINE_CID(kMsgAccountManagerCID, NS_MSGACCOUNTMANAGER_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); @@ -3416,6 +3417,13 @@ nsImapMailFolder::ReleaseUrl() return NS_OK; } +NS_IMETHODIMP +nsImapMailFolder::CloseMockChannel(nsIImapMockChannel * aChannel) +{ + aChannel->Close(); + return NS_OK; +} + NS_IMETHODIMP nsImapMailFolder::BeginMessageUpload() { diff --git a/mailnews/imap/src/nsImapProtocol.cpp b/mailnews/imap/src/nsImapProtocol.cpp index 6d8449747f1..74fc9901381 100644 --- a/mailnews/imap/src/nsImapProtocol.cpp +++ b/mailnews/imap/src/nsImapProtocol.cpp @@ -59,7 +59,8 @@ #include "nsIDNSService.h" #include "nsIMsgHdr.h" // for the memory cache... -#include "nsICachedNetData.h" +#include "nsICacheEntryDescriptor.h" +#include "nsICacheSession.h" #include "nsCOMPtr.h" PRLogModuleInfo *IMAP; @@ -90,6 +91,7 @@ static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CI static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); +static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); #define OUTPUT_BUFFER_SIZE (4096*2) // mscott - i should be able to remove this if I can use nsMsgLineBuffer??? @@ -637,7 +639,7 @@ nsresult nsImapProtocol::SetupWithUrl(nsIURI * aURL, nsISupports* aConsumer) nsCOMPtr mailnewsUrl = do_QueryInterface(m_runningUrl); if (mailnewsUrl) { - nsCOMPtr cacheEntry; + nsCOMPtr cacheEntry; mailnewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry)); if (cacheEntry) cacheEntry->SetSecurityInfo(securityInfo); @@ -654,7 +656,10 @@ void nsImapProtocol::ReleaseUrlState() { if (m_mockChannel) { - m_mockChannel->Close(); + if (m_imapMailFolderSink) + m_imapMailFolderSink->CloseMockChannel(m_mockChannel); + else + m_mockChannel->Close(); m_mockChannel = null_nsCOMPtr(); } if (m_runningUrl) @@ -6790,6 +6795,7 @@ NS_INTERFACE_MAP_BEGIN(nsImapMockChannel) NS_INTERFACE_MAP_ENTRY(nsIImapMockChannel) NS_INTERFACE_MAP_ENTRY(nsIChannel) NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsICacheListener) NS_INTERFACE_MAP_END_THREADSAFE @@ -6807,8 +6813,10 @@ nsImapMockChannel::~nsImapMockChannel() NS_IMETHODIMP nsImapMockChannel::Close() { - m_channelListener = null_nsCOMPtr(); - return NS_OK; + m_channelListener = null_nsCOMPtr(); + mCacheRequest = null_nsCOMPtr(); + m_url = null_nsCOMPtr(); + return NS_OK; } NS_IMETHODIMP nsImapMockChannel::GetProgressEventSink(nsIProgressEventSink ** aProgressEventSink) @@ -6885,18 +6893,18 @@ NS_IMETHODIMP nsImapMockChannel::SetURI(nsIURI* aURI) if (!aURI) printf("Clearing URI\n"); #endif - if (m_url) + if (m_url) + { + // if we don't have a progress event sink yet, get it from the url for now... + nsCOMPtr mailnewsUrl = do_QueryInterface(m_url); + if (mailnewsUrl && !mProgressEventSink) { - // if we don't have a progress event sink yet, get it from the url for now... - nsCOMPtr mailnewsUrl = do_QueryInterface(m_url); - if (mailnewsUrl && !mProgressEventSink) - { - nsCOMPtr statusFeedback; - mailnewsUrl->GetStatusFeedback(getter_AddRefs(statusFeedback)); - mProgressEventSink = do_QueryInterface(statusFeedback); - } + nsCOMPtr statusFeedback; + mailnewsUrl->GetStatusFeedback(getter_AddRefs(statusFeedback)); + mProgressEventSink = do_QueryInterface(statusFeedback); } - return NS_OK; + } + return NS_OK; } NS_IMETHODIMP nsImapMockChannel::Open(nsIInputStream **_retval) @@ -6905,70 +6913,100 @@ NS_IMETHODIMP nsImapMockChannel::Open(nsIInputStream **_retval) return NS_ERROR_NOT_IMPLEMENTED; } -NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) +NS_IMETHODIMP +nsImapMockChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status) { - nsCOMPtr cacheEntry; - PRUint32 contentLength = 0; - PRBool partialFlag = PR_FALSE; - - // set the stream listener and then load the url - m_channelContext = ctxt; - m_channelListener = listener; - 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); - rv = mailnewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry)); - if (NS_SUCCEEDED(rv) && cacheEntry) + if (NS_SUCCEEDED(status)) { - PRBool updateInProgress; - cacheEntry->GetPartialFlag(&partialFlag); - cacheEntry->GetUpdateInProgress(&updateInProgress); - cacheEntry->GetStoredContentLength(&contentLength); - // only try to update the cache entry if it isn't being used. - // We always want to try to write to the cache entry if we can - if (!updateInProgress) + nsCOMPtr mailnewsUrl = do_QueryInterface(m_url, &rv); + mailnewsUrl->SetMemCacheEntry(entry); + + // if we have write access then insert a "stream T" into the flow so data + // gets written to both + if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ)) { - // 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)); + entry->MarkValid(); + // use a stream listener Tee to force data into the cache and to our current channel listener... + nsCOMPtr newListener; + nsCOMPtr tee = do_CreateInstance(kStreamListenerTeeCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr transport; + rv = entry->GetTransport(getter_AddRefs(transport)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr out; + rv = transport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(out)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = tee->Init(m_channelListener, out); + m_channelListener = do_QueryInterface(tee); + NS_ENSURE_SUCCESS(rv, rv); } - } - - // 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) - { - PRUint32 annotationLength = 0; - char *annotation = nsnull; - - rv = cacheEntry->GetAnnotation("ContentModified", &annotationLength, &annotation); - if (NS_SUCCEEDED(rv) && annotationLength == nsCRT::strlen("Not Modified") - && annotation && !nsCRT::strncmp(annotation, "Not Modified", annotationLength)) + else { - nsCOMPtr cacheChannel; - rv = cacheEntry->NewChannel(m_loadGroup, getter_AddRefs(cacheChannel)); + rv = ReadFromMemCache(entry); + if (access & nsICache::ACCESS_WRITE) + entry->MarkValid(); + if (NS_SUCCEEDED(rv)) return NS_OK; // kick out if reading from the cache succeeded... + } + } // if we got a valid entry back from the cache... + + // if reading from the cache failed or if we are writing into the cache, default to ReadFromImapConnection. + return ReadFromImapConnection(); +} + +nsresult nsImapMockChannel::OpenCacheEntry() +{ + nsresult rv = NS_OK; + // get the cache session from our imap service... + nsCOMPtr imapService = do_GetService(kCImapService, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr cacheSession; + rv = imapService->GetCacheSession(getter_AddRefs(cacheSession)); + NS_ENSURE_SUCCESS(rv, rv); + + // Open a cache entry with key = url + nsXPIDLCString urlSpec; + m_url->GetSpec(getter_Copies(urlSpec)); + // for now, truncate of the query part so we don't duplicate urls in the cache... + char * anchor = PL_strrchr(urlSpec, '?'); + if (anchor) + *anchor = '\0'; + return cacheSession->AsyncOpenCacheEntry(urlSpec, nsICache::ACCESS_READ_WRITE, this); +} + +nsresult nsImapMockChannel::ReadFromMemCache(nsICacheEntryDescriptor *entry) +{ + NS_ENSURE_ARG(entry); + + PRUint32 annotationLength = 0; + nsXPIDLCString annotation; + nsresult rv = NS_OK; + + rv = entry->GetMetaDataElement("ContentModified", getter_Copies(annotation)); + if (NS_SUCCEEDED(rv) && (annotation.get())) + { + annotationLength = nsCRT::strlen(annotation.get()); + if (annotationLength == nsCRT::strlen("Not Modified") && !nsCRT::strncmp(annotation, "Not Modified", annotationLength)) + { + nsCOMPtr cacheTransport; + rv = entry->GetTransport(getter_AddRefs(cacheTransport)); if (NS_SUCCEEDED(rv)) { // if we are going to read from the cache, then create a mock stream listener class and use it nsImapCacheStreamListener * cacheListener = new nsImapCacheStreamListener(); NS_ADDREF(cacheListener); - SetupPartExtractor(imapUrl, m_channelListener); cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this)); - rv = cacheChannel->AsyncOpen(cacheListener, m_channelContext); + rv = cacheTransport->AsyncRead(cacheListener, m_channelContext, 0, PRUint32(-1), 0, getter_AddRefs(mCacheRequest)); NS_RELEASE(cacheListener); if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return { + nsCOMPtr imapUrl = do_QueryInterface(m_url); + // if the msg is unread, we should mark it read on the server. This lets // the code running this url we're loading from the cache, if it cares. imapUrl->SetMsgLoadingFromCache(PR_TRUE); @@ -6979,15 +7017,58 @@ NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISuppo // be sure to set the cache entry's security info status as our security info status... nsCOMPtr securityInfo; - cacheEntry->GetSecurityInfo(getter_AddRefs(securityInfo)); + entry->GetSecurityInfo(getter_AddRefs(securityInfo)); SetSecurityInfo(securityInfo); - return rv; - } - } - } - } + return NS_OK; + } // if AsyncRead succeeded. + } // if get transport succeeded + } // if contnet is not modified + else + rv = NS_ERROR_FAILURE; // content is modified so return an error so we try to open it the old fashioned way + } // if we got an annotation + + return rv; +} + +// the requested url isn't in any of our caches so create an imap connection +// to process it. +nsresult nsImapMockChannel::ReadFromImapConnection() +{ + nsresult rv = NS_OK; + nsCOMPtr imapUrl = do_QueryInterface(m_url); + nsCOMPtr mailnewsUrl = do_QueryInterface(m_url); + + // okay, add the mock channel to the load group.. + imapUrl->AddChannelToLoadGroup(); + // loading the url consists of asking the server to add the url to it's imap event queue.... + nsCOMPtr server; + rv = mailnewsUrl->GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr imapServer (do_QueryInterface(server, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + // Assume AsyncRead is always called from the UI thread..... + nsCOMPtr queue; + // get the Event Queue for this thread... + nsCOMPtr pEventQService (do_GetService(kEventQueueServiceCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(queue)); + NS_ENSURE_SUCCESS(rv, rv); + rv = imapServer->GetImapConnectionAndLoadUrl(queue, imapUrl, nsnull); + return rv; +} + +// for messages stored in our offline cache, we have special code to handle that... +// If it's in the local cache, we return true and we can abort the download because +// this method does the rest of the work. +PRBool nsImapMockChannel::ReadFromLocalCache() +{ + nsresult rv = NS_OK; + + nsCOMPtr imapUrl = do_QueryInterface(m_url); + nsCOMPtr mailnewsUrl = do_QueryInterface(m_url, &rv); - // check if msg is in local cache. PRBool useLocalCache = PR_FALSE; mailnewsUrl->GetMsgIsInLocalCache(&useLocalCache); if (useLocalCache) @@ -6998,7 +7079,7 @@ NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISuppo rv = imapUrl->GetImapFolder(getter_AddRefs(folder)); if (folder && NS_SUCCEEDED(rv)) { - // we want to create a file channel and read the msg from there. + // we want to create a file channel and read the msg from there. nsCOMPtr fileChannel; nsMsgKey msgKey = atoi(messageIdString); PRUint32 size, offset; @@ -7007,17 +7088,15 @@ NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISuppo // folder sink?) We also need to set the transfer offset to the message offset if (fileChannel && NS_SUCCEEDED(rv)) { - // dougt - This may break the ablity to "cancel" a read from offline mail reading. - // fileChannel->SetLoadGroup(m_loadGroup); + // dougt - This may break the ablity to "cancel" a read from offline mail reading. + // fileChannel->SetLoadGroup(m_loadGroup); // force the url to remove its reference on the mock channel...this is to solve // a nasty reference counting problem... imapUrl->SetMockChannel(nsnull); - // if we are going to read from the cache, then create a mock stream listener class and use it nsImapCacheStreamListener * cacheListener = new nsImapCacheStreamListener(); NS_ADDREF(cacheListener); - SetupPartExtractor(imapUrl, m_channelListener); cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this)); nsCOMPtr request; rv = fileChannel->AsyncRead(cacheListener, m_channelContext, offset, size, 0, getter_AddRefs(request)); @@ -7028,37 +7107,40 @@ NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISuppo // if the msg is unread, we should mark it read on the server. This lets // the code running this url we're loading from the cache, if it cares. imapUrl->SetMsgLoadingFromCache(PR_TRUE); - return rv; + return PR_TRUE; } - } - } - } + } // if we got an offline file transport + } // if we got the folder for this url + } // if use local cache - SetupPartExtractor(imapUrl, m_channelListener); - - // okay, add the mock channel to the load group.. - imapUrl->AddChannelToLoadGroup(); - // loading the url consists of asking the server to add the url to it's imap event queue.... - nsCOMPtr server; - rv = mailnewsUrl->GetServer(getter_AddRefs(server)); - if (NS_FAILED(rv)) return rv; - nsCOMPtr imapServer; - imapServer = do_QueryInterface(server, &rv); - if (NS_FAILED(rv)) return rv; - - // Assume AsyncRead is always called from the UI thread..... - nsCOMPtr queue; - // get the Event Queue for this thread... - NS_WITH_SERVICE(nsIEventQueueService, pEventQService,kEventQueueServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - - rv = pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(queue)); - if (NS_FAILED(rv)) return rv; - rv = imapServer->GetImapConnectionAndLoadUrl(queue, imapUrl, nsnull); - return rv; + return PR_FALSE; } -nsresult nsImapMockChannel::SetupPartExtractor(nsIImapUrl * aUrl, nsIStreamListener * aConsumer) +NS_IMETHODIMP nsImapMockChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) +{ + nsresult rv = NS_OK; + // set the stream listener and then load the url + m_channelContext = ctxt; + m_channelListener = listener; + nsCOMPtr imapUrl (do_QueryInterface(m_url)); + + SetupPartExtractorListener(imapUrl, m_channelListener); + + if (ReadFromLocalCache()) + return NS_OK; + + // okay, it's not in the local cache, now check the memory cache... + + rv = OpenCacheEntry(); + + // if for some reason open cache entry failed then just default to opening an imap connection for the url + if (NS_FAILED(rv)) + return ReadFromImapConnection(); + else + return NS_OK; +} + +nsresult nsImapMockChannel::SetupPartExtractorListener(nsIImapUrl * aUrl, nsIStreamListener * aConsumer) { // if the url we are loading refers to a specific part then we need // libmime to extract that part from the message for us. @@ -7071,7 +7153,7 @@ nsresult nsImapMockChannel::SetupPartExtractor(nsIImapUrl * aUrl, nsIStreamListe { nsCOMPtr newConsumer; converter->AsyncConvertData(NS_LITERAL_STRING("message/rfc822").get(), NS_LITERAL_STRING("*/*").get(), - aConsumer, this, getter_AddRefs(newConsumer)); + aConsumer, NS_STATIC_CAST(nsIChannel *, this), getter_AddRefs(newConsumer)); if (newConsumer) m_channelListener = newConsumer; } diff --git a/mailnews/imap/src/nsImapProtocol.h b/mailnews/imap/src/nsImapProtocol.h index 14814acc969..e093d5c9071 100644 --- a/mailnews/imap/src/nsImapProtocol.h +++ b/mailnews/imap/src/nsImapProtocol.h @@ -63,6 +63,7 @@ #include "nsXPIDLString.h" #include "nsIMsgWindow.h" #include "nsIMsgLogonRedirector.h" +#include "nsICacheListener.h" class nsIMAPMessagePartIDArray; class nsIMsgIncomingServer; @@ -578,7 +579,9 @@ private: // // Threading concern: This class lives entirely in the UI thread. -class nsImapMockChannel : public nsIImapMockChannel +class nsICacheEntryDescriptor; + +class nsImapMockChannel : public nsIImapMockChannel, public nsICacheListener { public: @@ -586,6 +589,7 @@ public: NS_DECL_NSIIMAPMOCKCHANNEL NS_DECL_NSICHANNEL NS_DECL_NSIREQUEST + NS_DECL_NSICACHELISTENER nsImapMockChannel(); virtual ~nsImapMockChannel(); @@ -610,9 +614,17 @@ protected: nsCOMPtr mCallbacks; nsCOMPtr mOwner; nsCOMPtr mSecurityInfo; + nsCOMPtr mCacheRequest; // the request associated with a read from the cache nsCString m_ContentType; - nsresult SetupPartExtractor(nsIImapUrl * aUrl, nsIStreamListener * aConsumer); + // cache related helper methods + nsresult OpenCacheEntry(); // makes a request to the cache service for a cache entry for a url + PRBool 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(nsICacheEntryDescriptor *entry); // attempts to read the url out of our memory cache + + // we end up daisy chaining multiple nsIStreamListeners into the load process. + nsresult SetupPartExtractorListener(nsIImapUrl * aUrl, nsIStreamListener * aConsumer); }; #endif // nsImapProtocol_h___ diff --git a/mailnews/imap/src/nsImapService.cpp b/mailnews/imap/src/nsImapService.cpp index a26f1cb5a91..b27ff9ba791 100644 --- a/mailnews/imap/src/nsImapService.cpp +++ b/mailnews/imap/src/nsImapService.cpp @@ -59,12 +59,16 @@ #include "nsImapOfflineSync.h" #include "nsIMsgHdr.h" #include "nsMsgUtils.h" +#include "nsICacheService.h" +#include "nsNetCID.h" + #define PREF_MAIL_ROOT_IMAP "mail.root.imap" static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kImapUrlCID, NS_IMAPURL_CID); +static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID); static const char *sequenceString = "SEQUENCE"; @@ -410,7 +414,7 @@ NS_IMETHODIMP nsImapService::FetchMimePart(nsIURI *aURI, const char *aMessageURI rv = nsParseImapMessageURI(aMessageURI, folderURI, &key, getter_Copies(mimePart)); if (NS_SUCCEEDED(rv)) { - nsCOMPtr imapMessageSink(do_QueryInterface(folder, &rv)); + nsCOMPtr imapMessageSink(do_QueryInterface(folder, &rv)); if (NS_SUCCEEDED(rv)) { nsCOMPtr imapUrl = do_QueryInterface(aURI); @@ -3321,3 +3325,21 @@ nsImapService::DownloadAllOffineImapFolders(nsIMsgWindow *aMsgWindow, nsIUrlList return NS_ERROR_OUT_OF_MEMORY; } + +NS_IMETHODIMP nsImapService::GetCacheSession(nsICacheSession **result) +{ + nsresult rv = NS_OK; + if (!mCacheSession) + { + nsCOMPtr serv = do_GetService(kCacheServiceCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = serv->CreateSession("IMAP-memory-only", nsICache::STORE_IN_MEMORY, nsICache::STREAM_BASED, getter_AddRefs(mCacheSession)); + NS_ENSURE_SUCCESS(rv, rv); + rv = mCacheSession->SetDoomEntriesIfExpired(PR_FALSE); + } + + *result = mCacheSession; + NS_IF_ADDREF(*result); + return rv; +} diff --git a/mailnews/imap/src/nsImapService.h b/mailnews/imap/src/nsImapService.h index 387a6f790c8..34d9fc6dfc9 100644 --- a/mailnews/imap/src/nsImapService.h +++ b/mailnews/imap/src/nsImapService.h @@ -31,6 +31,8 @@ #include "nsIProtocolHandler.h" #include "nsIMsgProtocolInfo.h" +#include "nsICacheSession.h" + class nsIImapHostSessionList; class nsCString; class nsIImapUrl; @@ -110,6 +112,8 @@ protected: PRBool mPrintingOperation; // Flag for printing operations + // handle to the cache session for imap..... + nsCOMPtr mCacheSession; }; #endif /* nsImapService_h___ */ diff --git a/mailnews/imap/src/nsImapUrl.cpp b/mailnews/imap/src/nsImapUrl.cpp index ebede0a2573..9a2794019f9 100644 --- a/mailnews/imap/src/nsImapUrl.cpp +++ b/mailnews/imap/src/nsImapUrl.cpp @@ -42,7 +42,7 @@ #include "nsXPIDLString.h" #include "nsAutoLock.h" #include "nsIMAPNamespace.h" -#include "nsICachedNetData.h" +#include "nsICacheEntryDescriptor.h" // rdf stuff is needed to get the charset from the imap folder associated with the url. #include "nsIRDFService.h" #include "rdf.h" @@ -1001,7 +1001,7 @@ NS_IMETHODIMP nsImapUrl::SetAllowContentChange(PRBool allowContentChange) NS_IMETHODIMP nsImapUrl::SetContentModified(nsImapContentModifiedType contentModified) { m_contentModified = contentModified; - nsCOMPtr cacheEntry; + nsCOMPtr cacheEntry; nsresult res = GetMemCacheEntry(getter_AddRefs(cacheEntry)); if (NS_SUCCEEDED(res) && cacheEntry) { @@ -1021,7 +1021,7 @@ NS_IMETHODIMP nsImapUrl::SetContentModified(nsImapContentModifiedType contentMod contentModifiedAnnotation = "Force Content Not Modified"; break; } - cacheEntry->SetAnnotation("ContentModified", nsCRT::strlen(contentModifiedAnnotation), contentModifiedAnnotation); + cacheEntry->SetMetaDataElement("ContentModified", contentModifiedAnnotation); } return NS_OK; } diff --git a/mailnews/news/public/nsINntpService.idl b/mailnews/news/public/nsINntpService.idl index 32b20c5040b..1726ef068da 100644 --- a/mailnews/news/public/nsINntpService.idl +++ b/mailnews/news/public/nsINntpService.idl @@ -32,6 +32,7 @@ interface nsISupportsArray; interface nsIFileSpec; interface nsIMsgWindow; interface nsIMsgFolder; +interface nsICacheSession; [scriptable, uuid(4C9F90E0-E19B-11d2-806E-006008128C4E)] interface nsINntpService : nsISupports { @@ -55,5 +56,7 @@ interface nsINntpService : nsISupports { * can handle news_message:// and news:// */ void decomposeNewsURI(in string uri, out nsIMsgFolder folder, out nsMsgKey key); -}; + // handle to the cache session used by news.... + readonly attribute nsICacheSession cacheSession; +}; diff --git a/mailnews/news/src/nsNNTPProtocol.cpp b/mailnews/news/src/nsNNTPProtocol.cpp index 3ce6768816e..2297c0b0199 100644 --- a/mailnews/news/src/nsNNTPProtocol.cpp +++ b/mailnews/news/src/nsNNTPProtocol.cpp @@ -78,7 +78,10 @@ #include "nsIDocShell.h" // for the memory cache... -#include "nsICachedNetData.h" +#include "nsICacheEntryDescriptor.h" +#include "nsICacheSession.h" +#include "nsIStreamListener.h" +#include "nsNetCID.h" #include "nsIPref.h" @@ -160,6 +163,7 @@ static NS_DEFINE_CID(kCMsgMailSessionCID, NS_MSGMAILSESSION_CID); static NS_DEFINE_CID(kCMsgAccountManagerCID, NS_MSGACCOUNTMANAGER_CID); static NS_DEFINE_CID(kPrefServiceCID,NS_PREF_CID); static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); +static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); typedef struct _cancelInfoEntry { char *from; @@ -414,8 +418,9 @@ NS_IMPL_ADDREF_INHERITED(nsNNTPProtocol, nsMsgProtocol) NS_IMPL_RELEASE_INHERITED(nsNNTPProtocol, nsMsgProtocol) NS_INTERFACE_MAP_BEGIN(nsNNTPProtocol) - NS_INTERFACE_MAP_ENTRY(nsINNTPProtocol) + NS_INTERFACE_MAP_ENTRY(nsINNTPProtocol) NS_INTERFACE_MAP_ENTRY(nsITimerCallback) + NS_INTERFACE_MAP_ENTRY(nsICacheListener) NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol) nsNNTPProtocol::nsNNTPProtocol(nsIURI * aURL, nsIMsgWindow *aMsgWindow) @@ -656,7 +661,7 @@ NS_IMETHODIMP nsNNTPProtocol::LoadNewsUrl(nsIURI * aURL, nsISupports * aConsumer nsCOMPtr newsUrl (do_QueryInterface(aURL)); newsUrl->GetNewsAction(&m_newsAction); - SetupPartExtractor(m_channelListener); + SetupPartExtractorListener(m_channelListener); return LoadUrl(aURL, aConsumer); } @@ -748,7 +753,7 @@ nsNntpCacheStreamListener::OnDataAvailable(nsIRequest *request, nsISupports * aC return mListener->OnDataAvailable(ourRequest, aCtxt, aInStream, aSourceOffset, aCount); } -nsresult nsNNTPProtocol::SetupPartExtractor(nsIStreamListener * aConsumer) +nsresult nsNNTPProtocol::SetupPartExtractorListener(nsIStreamListener * aConsumer) { if (m_newsAction == nsINntpUrl::ActionFetchPart) { @@ -769,6 +774,165 @@ nsresult nsNNTPProtocol::SetupPartExtractor(nsIStreamListener * aConsumer) return NS_OK; } +nsresult nsNNTPProtocol::ReadFromMemCache(nsICacheEntryDescriptor *entry) +{ + NS_ENSURE_ARG(entry); + + nsCOMPtr cacheTransport; + nsresult rv = entry->GetTransport(getter_AddRefs(cacheTransport)); + + if (NS_SUCCEEDED(rv)) + { + nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener(); + NS_ADDREF(cacheListener); + + SetLoadGroup(m_loadGroup); + m_typeWanted = ARTICLE_WANTED; + + nsCOMPtr mailnewsUrl = do_QueryInterface(m_runningURL); + cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl); + + nsCOMPtr request; + rv = cacheTransport->AsyncRead(cacheListener, m_channelContext, 0, PRUint32(-1), 0, getter_AddRefs(request)); + NS_RELEASE(cacheListener); + + MarkCurrentMsgRead(); + if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return + { + // we're not calling nsMsgProtocol::AsyncRead(), which calls nsNNTPProtocol::LoadUrl, so we need to take care of some stuff it does. + m_ContentType = ""; + m_channelListener = nsnull; + return rv; + } + } + + return rv; +} + +nsresult nsNNTPProtocol::ReadFromNewsConnection() +{ + return nsMsgProtocol::AsyncOpen(m_channelListener, m_channelContext); +} + +// for messages stored in our offline cache, we have special code to handle that... +// If it's in the local cache, we return true and we can abort the download because +// this method does the rest of the work. +PRBool nsNNTPProtocol::ReadFromLocalCache() +{ + PRBool msgIsInLocalCache = PR_FALSE; + nsresult rv = NS_OK; + nsCOMPtr mailnewsUrl = do_QueryInterface(m_runningURL); + mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache); + + if (msgIsInLocalCache) + { + nsXPIDLCString group; + nsXPIDLCString commandSpecificData; + rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData)); + nsCOMPtr folder = do_QueryInterface(m_newsFolder); + if (folder && NS_SUCCEEDED(rv)) + { + // we want to create a file channel and read the msg from there. + nsCOMPtr fileChannel; + PRUint32 offset=0, size=0; + rv = folder->GetOfflineFileTransport(m_key, &offset, &size, getter_AddRefs(fileChannel)); + + // get the file channel from the folder, somehow (through the message or + // folder sink?) We also need to set the transfer offset to the message offset + if (fileChannel && NS_SUCCEEDED(rv)) + { + // dougt - This may break the ablity to "cancel" a read from offline mail reading. + // fileChannel->SetLoadGroup(m_loadGroup); + + m_typeWanted = ARTICLE_WANTED; + nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener(); + NS_ADDREF(cacheListener); + cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl); + nsCOMPtr request; + rv = fileChannel->AsyncRead(cacheListener, m_channelContext, offset, size, 0, getter_AddRefs(request)); + NS_RELEASE(cacheListener); + MarkCurrentMsgRead(); + + if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return + { + m_ContentType = ""; + m_channelListener = nsnull; + return PR_TRUE; + } + } + } + } + + return PR_FALSE; +} + +NS_IMETHODIMP +nsNNTPProtocol::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status) +{ + nsresult rv = NS_OK; + + if (NS_SUCCEEDED(status)) + { + nsCOMPtr mailnewsUrl = do_QueryInterface(m_runningURL, &rv); + mailnewsUrl->SetMemCacheEntry(entry); + + // if we have write access then insert a "stream T" into the flow so data + // gets written to both + if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ)) + { + entry->MarkValid(); + // use a stream listener Tee to force data into the cache and to our current channel listener... + nsCOMPtr newListener; + nsCOMPtr tee = do_CreateInstance(kStreamListenerTeeCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr transport; + rv = entry->GetTransport(getter_AddRefs(transport)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr out; + rv = transport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(out)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = tee->Init(m_channelListener, out); + m_channelListener = do_QueryInterface(tee); + NS_ENSURE_SUCCESS(rv, rv); + } + else + { + rv = ReadFromMemCache(entry); + if (access & nsICache::ACCESS_WRITE) + entry->MarkValid(); + if (NS_SUCCEEDED(rv)) return NS_OK; // kick out if reading from the cache succeeded... + } + } // if we got a valid entry back from the cache... + + // if reading from the cache failed or if we are writing into the cache, default to ReadFromImapConnection. + return ReadFromNewsConnection(); +} + +nsresult nsNNTPProtocol::OpenCacheEntry() +{ + nsresult rv = NS_OK; + nsCOMPtr mailnewsUrl = do_QueryInterface(m_runningURL, &rv); + // get the cache session from our nntp service... + nsCOMPtr nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr cacheSession; + rv = nntpService->GetCacheSession(getter_AddRefs(cacheSession)); + NS_ENSURE_SUCCESS(rv, rv); + + // Open a cache entry with key = url + nsXPIDLCString urlSpec; + mailnewsUrl->GetSpec(getter_Copies(urlSpec)); + // for now, truncate of the query part so we don't duplicate urls in the cache... + char * anchor = PL_strrchr(urlSpec, '?'); + if (anchor) + *anchor = '\0'; + return cacheSession->AsyncOpenCacheEntry(urlSpec, nsICache::ACCESS_READ_WRITE, this); +} + NS_IMETHODIMP nsNNTPProtocol::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { nsresult rv; @@ -782,110 +946,12 @@ NS_IMETHODIMP nsNNTPProtocol::AsyncOpen(nsIStreamListener *listener, nsISupports // the memory cache or the local msg cache. if (mailnewsUrl && (m_newsAction == nsINntpUrl::ActionFetchArticle || m_newsAction == nsINntpUrl::ActionFetchPart)) { - nsCOMPtr cacheEntry; - PRUint32 contentLength = 0; - PRBool partialFlag = PR_FALSE; - PRBool msgIsInLocalCache; - - SetupPartExtractor(m_channelListener); + SetupPartExtractorListener(m_channelListener); + if (ReadFromLocalCache()) + return NS_OK; - mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache); - if (msgIsInLocalCache) - { - nsXPIDLCString group; - nsXPIDLCString commandSpecificData; - rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData)); - nsCOMPtr folder = do_QueryInterface(m_newsFolder); - if (folder && NS_SUCCEEDED(rv)) - { - // we want to create a file channel and read the msg from there. - nsCOMPtr fileChannel; - PRUint32 offset=0, size=0; - rv = folder->GetOfflineFileTransport(m_key, &offset, &size, getter_AddRefs(fileChannel)); - - // get the file channel from the folder, somehow (through the message or - // folder sink?) We also need to set the transfer offset to the message offset - if (fileChannel && NS_SUCCEEDED(rv)) - { - // dougt - This may break the ablity to "cancel" a read from offline mail reading. - // fileChannel->SetLoadGroup(m_loadGroup); - - m_typeWanted = ARTICLE_WANTED; - nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener(); - NS_ADDREF(cacheListener); - cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl); - nsCOMPtr request; - rv = fileChannel->AsyncRead(cacheListener, m_channelContext, offset, size, 0, getter_AddRefs(request)); - NS_RELEASE(cacheListener); - MarkCurrentMsgRead(); - - if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return - { - m_ContentType = ""; - m_channelListener = nsnull; - return NS_OK; - } - } - } - } - - // look to see if this url should be added to the memory cache.. - PRBool useMemoryCache = PR_FALSE; - mailnewsUrl->GetAddToMemoryCache(&useMemoryCache); - rv = mailnewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry)); - if (NS_SUCCEEDED(rv) && cacheEntry) - { - PRBool updateInProgress; - m_typeWanted = ARTICLE_WANTED; - cacheEntry->GetPartialFlag(&partialFlag); - cacheEntry->GetUpdateInProgress(&updateInProgress); - cacheEntry->GetStoredContentLength(&contentLength); - // only try to update the cache entry if it isn't being used. - // We always want to try to write to the cache entry if we can - if (!updateInProgress) - { - // 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, - // do we have a listener here? - nsIStreamListener *anotherListener = m_channelListener; - rv = cacheEntry->InterceptAsyncRead(anotherListener, 0, getter_AddRefs(m_channelListener)); - nsCOMPtr request; - if (NS_SUCCEEDED(rv)) - return nsMsgProtocol::AsyncOpen(m_channelListener, ctxt); - } - } - } - // 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, getter_AddRefs(cacheChannel)); - if (NS_SUCCEEDED(rv)) - { - nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener(); - NS_ADDREF(cacheListener); - SetLoadGroup(m_loadGroup); - m_typeWanted = ARTICLE_WANTED; - cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl); - nsCOMPtr request; - rv = cacheChannel->AsyncOpen(cacheListener, m_channelContext); - NS_RELEASE(cacheListener); - - MarkCurrentMsgRead(); - if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return - { - // we're not calling nsMsgProtocol::AsyncRead(), which calls nsNNTPProtocol::LoadUrl, so we need to take care of some stuff it does. - m_ContentType = ""; - m_channelListener = nsnull; - return rv; - } - } - } + rv = OpenCacheEntry(); + if (NS_SUCCEEDED(rv)) return NS_OK; // if this didn't return an error then jump out now... } nsCOMPtr parentRequest; diff --git a/mailnews/news/src/nsNNTPProtocol.h b/mailnews/news/src/nsNNTPProtocol.h index a3efb0dbea0..fde53ab0907 100644 --- a/mailnews/news/src/nsNNTPProtocol.h +++ b/mailnews/news/src/nsNNTPProtocol.h @@ -46,6 +46,7 @@ #include "nsIStringBundle.h" #include "nsITimer.h" #include "nsITimerCallback.h" +#include "nsICacheListener.h" // this is only needed as long as our libmime hack is in place #include "prio.h" @@ -145,11 +146,14 @@ NEWS_FREE, NEWS_FINISHED } StatesEnum; -class nsNNTPProtocol : public nsINNTPProtocol, public nsITimerCallback, public nsMsgProtocol +class nsICacheEntryDescriptor; + +class nsNNTPProtocol : public nsINNTPProtocol, public nsITimerCallback, public nsICacheListener, public nsMsgProtocol { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSINNTPPROTOCOL + NS_DECL_NSICACHELISTENER // nsITimerCallback interfaces NS_IMETHOD_(void) Notify(nsITimer *timer); @@ -170,7 +174,6 @@ public: nsresult LoadUrl(nsIURI * aURL, nsISupports * aConsumer); private: - nsresult SetupPartExtractor(nsIStreamListener * aConsumer); // over-rides from nsMsgProtocol virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, PRUint32 sourceOffset, PRUint32 length); @@ -392,21 +395,28 @@ private: void SetProgressBarPercent(PRUint32 aProgress, PRUint32 aProgressMax); nsresult SetProgressStatus(const PRUnichar *aMessage); - nsresult SetCheckingForNewNewsStatus(PRInt32 current, PRInt32 total); - nsresult MarkCurrentMsgRead(); // marks the message corresponding to the currently running url read. + nsresult SetCheckingForNewNewsStatus(PRInt32 current, PRInt32 total); + nsresult MarkCurrentMsgRead(); // marks the message corresponding to the currently running url read. nsresult InitializeNewsFolderFromUri(const char *uri); void TimerCallback(); nsCOMPtr mInputStream; - nsCOMPtr mUpdateTimer; + nsCOMPtr mUpdateTimer; nsresult AlertError(PRInt32 errorCode, const char *text); PRInt32 mBytesReceived; - PRInt32 mBytesReceivedSinceLastStatusUpdate; - PRTime m_startTime; - PRInt32 mNumGroupsListed; - nsMsgKey m_key; + PRInt32 mBytesReceivedSinceLastStatusUpdate; + PRTime m_startTime; + PRInt32 mNumGroupsListed; + nsMsgKey m_key; - nsresult SetCurrentGroup(); /* sets m_currentGroup. should be called after doing a successful GROUP command */ - nsresult CleanupNewsgroupList(); /* cleans up m_newsgroupList, and set it to null */ + nsresult SetCurrentGroup(); /* sets m_currentGroup. should be called after doing a successful GROUP command */ + nsresult CleanupNewsgroupList(); /* cleans up m_newsgroupList, and set it to null */ + + // cache related helper methods + nsresult OpenCacheEntry(); // makes a request to the cache service for a cache entry for a url + PRBool ReadFromLocalCache(); // attempts to read the url out of our local (offline) cache.... + nsresult ReadFromNewsConnection(); // creates a new news connection to read the url + nsresult ReadFromMemCache(nsICacheEntryDescriptor *entry); // attempts to read the url out of our memory cache + nsresult SetupPartExtractorListener(nsIStreamListener * aConsumer); }; NS_BEGIN_EXTERN_C diff --git a/mailnews/news/src/nsNntpService.cpp b/mailnews/news/src/nsNntpService.cpp index 0be2fd6263c..d4ced572b0f 100644 --- a/mailnews/news/src/nsNntpService.cpp +++ b/mailnews/news/src/nsNntpService.cpp @@ -59,6 +59,9 @@ #include "nsIPrompt.h" #include "nsIRDFService.h" #include "nsNewsDownloader.h" +#include "nsICacheService.h" +#include "nsNetCID.h" + #undef GetPort // XXX Windows! #undef SetPort // XXX Windows! @@ -70,6 +73,7 @@ static NS_DEFINE_CID(kCPrefServiceCID, NS_PREF_CID); static NS_DEFINE_CID(kMsgAccountManagerCID, NS_MSGACCOUNTMANAGER_CID); static NS_DEFINE_CID(kMessengerMigratorCID, NS_MESSENGERMIGRATOR_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); +static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID); nsNntpService::nsNntpService() { @@ -361,7 +365,12 @@ NS_IMETHODIMP nsNntpService::OpenAttachment(const char *aContentType, nsCOMPtr url; nsresult rv = NS_OK; - NewURI(aUrl, nsnull, getter_AddRefs(url)); + nsCAutoString newsUrl; + newsUrl = aUrl; + newsUrl += "&type="; + newsUrl += aContentType; + + NewURI(newsUrl, nsnull, getter_AddRefs(url)); if (NS_SUCCEEDED(rv) && url) { @@ -1537,3 +1546,20 @@ nsNntpService::DownloadNewsgroupsForOffline(nsIMsgWindow *aMsgWindow, nsIUrlList return rv; } +NS_IMETHODIMP nsNntpService::GetCacheSession(nsICacheSession **result) +{ + nsresult rv = NS_OK; + if (!mCacheSession) + { + nsCOMPtr serv = do_GetService(kCacheServiceCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = serv->CreateSession("NNTP-memory-only", nsICache::STORE_IN_MEMORY, nsICache::STREAM_BASED, getter_AddRefs(mCacheSession)); + NS_ENSURE_SUCCESS(rv, rv); + rv = mCacheSession->SetDoomEntriesIfExpired(PR_FALSE); + } + + *result = mCacheSession; + NS_IF_ADDREF(*result); + return rv; +} diff --git a/mailnews/news/src/nsNntpService.h b/mailnews/news/src/nsNntpService.h index 12f375769f5..f3a99877a15 100644 --- a/mailnews/news/src/nsNntpService.h +++ b/mailnews/news/src/nsNntpService.h @@ -36,6 +36,7 @@ #include "nsICmdLineHandler.h" #include "nsCOMPtr.h" #include "nsIContentHandler.h" +#include "nsICacheSession.h" class nsIURI; class nsIUrlListener; @@ -46,7 +47,7 @@ class nsNntpService : public nsINntpService, public nsIProtocolHandler, public nsIMsgProtocolInfo, public nsICmdLineHandler, - public nsIContentHandler + public nsIContentHandler { public: @@ -87,6 +88,8 @@ protected: PRBool mPrintingOperation; // Flag for printing operations PRBool mOpenAttachmentOperation; // Flag for opening attachments PRBool mCopyingOperation; + + nsCOMPtr mCacheSession; // the cache session used by news }; #endif /* nsNntpService_h___ */