From dbfcdfd5ee12c1d4980291b7400e2f0a9a8d7e2b Mon Sep 17 00:00:00 2001 From: "bienvenu%nventure.com" Date: Tue, 8 Nov 2005 02:01:08 +0000 Subject: [PATCH] add method for getting preview text, part of 314124, sr=mscott git-svn-id: svn://10.0.0.236/trunk@184283 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/mailnews/base/public/nsIMsgFolder.idl | 24 ++- mozilla/mailnews/base/util/Makefile.in | 4 + mozilla/mailnews/base/util/nsMsgDBFolder.cpp | 152 +++++++++++++++++- mozilla/mailnews/base/util/nsMsgDBFolder.h | 1 + .../mailnews/imap/src/nsImapMailFolder.cpp | 87 +++++++++- mozilla/mailnews/imap/src/nsImapMailFolder.h | 4 + .../mailnews/local/src/nsLocalMailFolder.cpp | 47 +++++- .../mailnews/local/src/nsLocalMailFolder.h | 3 + 8 files changed, 315 insertions(+), 7 deletions(-) diff --git a/mozilla/mailnews/base/public/nsIMsgFolder.idl b/mozilla/mailnews/base/public/nsIMsgFolder.idl index 950c5b655ab..c4f5968f6ab 100644 --- a/mozilla/mailnews/base/public/nsIMsgFolder.idl +++ b/mozilla/mailnews/base/public/nsIMsgFolder.idl @@ -48,7 +48,6 @@ #include "nsISimpleEnumerator.idl" %{ C++ #include "nsIMsgDatabase.h" -#include "nsMsgKeyArray.h" %} [ptr] native octet_ptr(PRUint8); @@ -58,7 +57,6 @@ interface nsIMsgDBHdr; interface nsIMsgWindow; interface nsIMsgDatabase; interface nsIDBFolderInfo; -interface nsMsgKeyArray; interface nsIMsgFilterList; interface nsIMsgFolderCacheElement; @@ -72,7 +70,7 @@ typedef long nsMsgBiffState; // enumerated type for determining if a message has been replied to, forwarded, etc. typedef long nsMsgDispositionState; -[scriptable, uuid(853d026c-6eda-4d92-a915-8c05953284a2)] +[scriptable, uuid(28424d1c-db6f-4ac5-bc5d-418dd336120b)] interface nsIMsgFolder : nsICollection { const nsMsgBiffState nsMsgBiffState_NewMail = 0; // User has new mail waiting. @@ -479,4 +477,24 @@ const nsMsgBiffState nsMsgBiffState_Unknown = 2; // We dunno whether there is ne void copyDataDone(); void setJunkScoreForMessages(in nsISupportsArray aMessages, in string aJunkScore); void applyRetentionSettings(); + + /** + * Get the beginning of the message bodies for the passed in keys and store + * them in the msg hdr property "preview". This is intended for + * new mail alerts, title tips on folders with new messages, and perhaps + * titletips/message preview in the thread pane. + * + * @param aKeysToFetch keys of msgs to fetch + * @param aNumKeys number of keys to fetch + * @param aLocalOnly whether to fetch msgs from server (imap msgs might + * be in memory cache from junk filter) + * @param aUrlListener url listener to notify if we run url to fetch msgs + * + * @result aAsyncResults if true, we ran a url to fetch one or more of msg bodies + * + */ + void fetchMsgPreviewText([array, size_is (aNumKeys)] in nsMsgKey aKeysToFetch, + in unsigned long aNumKeys, in boolean aLocalOnly, + in nsIUrlListener aUrlListener, out boolean aAsyncResults); + }; diff --git a/mozilla/mailnews/base/util/Makefile.in b/mozilla/mailnews/base/util/Makefile.in index 1065a4332e9..09185d1e009 100644 --- a/mozilla/mailnews/base/util/Makefile.in +++ b/mozilla/mailnews/base/util/Makefile.in @@ -86,6 +86,10 @@ REQUIRES = xpcom \ nkcache \ mimetype \ windowwatcher \ + embed_base \ + htmlparser \ + content \ + layout \ $(NULL) CPPSRCS = \ diff --git a/mozilla/mailnews/base/util/nsMsgDBFolder.cpp b/mozilla/mailnews/base/util/nsMsgDBFolder.cpp index 11c2d7dab78..82298d418a0 100644 --- a/mozilla/mailnews/base/util/nsMsgDBFolder.cpp +++ b/mozilla/mailnews/base/util/nsMsgDBFolder.cpp @@ -77,7 +77,14 @@ #include "nsCPasswordManager.h" #include "nsMsgDBCID.h" #include "nsInt64.h" - +#include "nsReadLine.h" +#include "nsParserCIID.h" +#include "nsIParser.h" +#include "nsIHTMLContentSink.h" +#include "nsIContentSerializer.h" +#include "nsLayoutCID.h" +#include "nsIHTMLToTextSink.h" +#include "nsIDocumentEncoder.h" #include #define oneHour 3600000000U @@ -85,7 +92,6 @@ static PRTime gtimeOfLastPurgeCheck; //variable to know when to check for purge_threshhold - #define PREF_MAIL_PROMPT_PURGE_THRESHOLD "mail.prompt_purge_threshhold" #define PREF_MAIL_PURGE_THRESHOLD "mail.purge_threshhold" #define PREF_MAIL_WARN_FILTER_CHANGED "mail.warn_filter_changed" @@ -93,6 +99,8 @@ static PRTime gtimeOfLastPurgeCheck; //variable to know when to check for pur static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID); static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID); +static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); +static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID); nsIAtom* nsMsgDBFolder::mFolderLoadedAtom=nsnull; nsIAtom* nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom=nsnull; @@ -5068,3 +5076,143 @@ NS_IMETHODIMP nsMsgDBFolder::SetInVFEditSearchScope (PRBool aInVFEditSearchScope NotifyBoolPropertyChanged(kInVFEditSearchScopeAtom, oldInVFEditSearchScope, mInVFEditSearchScope); return NS_OK; } + +NS_IMETHODIMP nsMsgDBFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, + PRBool aLocalOnly, nsIUrlListener *aUrlListener, + PRBool *aAsyncResults) +{ + NS_ENSURE_ARG_POINTER(aKeysToFetch); + NS_ENSURE_ARG_POINTER(aAsyncResults); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult nsMsgDBFolder::GetMsgPreviewTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream) +{ + /* + 1. non mime message - the message body starts after the blank line following the headers. + 2. mime message, multipart/alternative - we could simply scan for the boundary line, + advance past its headers, and treat the next few lines as the text. + 3. mime message, text/plain - body follows headers + 4. multipart/mixed - scan past boundary, treat next part as body. + + TODO need to worry about quoted printable and other encodings, + so look for content transfer encoding. + */ + + PRUint32 len; + msgHdr->GetMessageSize(&len); + nsLineBuffer *lineBuffer; + + nsresult rv = NS_InitLineBuffer(&lineBuffer); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString boundary, msgBody; + nsCAutoString curLine; + // might want to use a state var instead of bools. + PRBool inMsgBody = PR_FALSE, msgBodyIsHtml = PR_FALSE, lookingForBoundary = PR_FALSE; + PRBool haveBoundary = PR_FALSE; + while (len > 0) + { + // might be on same line as content-type, so look before + // we read the next line. + if (lookingForBoundary) + { + PRInt32 boundaryIndex = curLine.Find("boundary=\""); + if (boundaryIndex != kNotFound) + { + boundaryIndex += 10; + PRInt32 endBoundaryIndex = curLine.RFindChar('"'); + if (endBoundaryIndex != kNotFound) + { + // prepend "--" to boundary, and then boundary delimiter, minus the trailing " + boundary.Assign("--"); + boundary.Append(Substring(curLine, boundaryIndex, endBoundaryIndex - boundaryIndex)); + haveBoundary = PR_TRUE; + lookingForBoundary = PR_FALSE; + } + } + } + PRBool more; + rv = NS_ReadLine(stream, lineBuffer, curLine, &more); + if (NS_SUCCEEDED(rv)) + { + len -= MSG_LINEBREAK_LEN; + len -= curLine.Length(); + if (inMsgBody) + { + if (!boundary.IsEmpty() && boundary.Equals(curLine)) + break; + msgBody.Append(curLine); + // how much html should we parse for text? 2K? 4K? + if (msgBody.Length() > 2048 || (!msgBodyIsHtml && msgBody.Length() > 255)) + break; + continue; + } + if (haveBoundary) + { + // this line is the boundary; continue and fall into code that looks + // for msg body after headers + if (curLine.Equals(boundary)) + haveBoundary = PR_FALSE; + continue; + } + if (curLine.IsEmpty()) + { + inMsgBody = PR_TRUE; + continue; + } + if (StringBeginsWith(curLine, NS_LITERAL_CSTRING("Content-Type:"))) + { + if (FindInReadable(NS_LITERAL_CSTRING("text/html"), curLine)) + { + msgBodyIsHtml = PR_TRUE; +// bodyFollowsHeaders = PR_TRUE; + } + else if (FindInReadable(NS_LITERAL_CSTRING("text/plain"), curLine)) + /* bodyFollowsHeaders = PR_TRUE */; + else if (FindInReadable(NS_LITERAL_CSTRING("multipart/mixed"), curLine) + || FindInReadable(NS_LITERAL_CSTRING("multipart/alternative"), curLine)) + { + lookingForBoundary = PR_TRUE; + } + } + } + } + // now we've got a msg body. If it's html, convert it to plain text. + // Then, set the previewProperty on the msg hdr to the plain text. + if (msgBodyIsHtml) + { + nsAutoString bodyText; + nsresult rv = NS_OK; + // Create a parser + nsCOMPtr parser = do_CreateInstance(kParserCID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Create the appropriate output sink + nsCOMPtr sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID,&rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr textSink(do_QueryInterface(sink)); + NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE); + PRUint32 flags = nsIDocumentEncoder::OutputLFLineBreak + | nsIDocumentEncoder::OutputNoScriptContent + | nsIDocumentEncoder::OutputNoFramesContent + | nsIDocumentEncoder::OutputBodyOnly; + + textSink->Initialize(&bodyText, flags, 80); + + parser->SetContentSink(sink); + nsCOMPtr dtd = do_CreateInstance(kNavDTDCID,&rv); + NS_ENSURE_SUCCESS(rv, rv); + + parser->RegisterDTD(dtd); + nsAutoString msgBodyStr; + // need to do an appropriate conversion here. + msgBodyStr.AssignWithConversion(msgBody); + rv = parser->Parse(msgBodyStr, 0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE); + CopyUTF16toUTF8(bodyText, msgBody); + } + msgHdr->SetStringProperty("preview", msgBody.get()); + return rv; +} + diff --git a/mozilla/mailnews/base/util/nsMsgDBFolder.h b/mozilla/mailnews/base/util/nsMsgDBFolder.h index bfe8f84d4d1..bd1dfd5fd9a 100644 --- a/mozilla/mailnews/base/util/nsMsgDBFolder.h +++ b/mozilla/mailnews/base/util/nsMsgDBFolder.h @@ -104,6 +104,7 @@ public: NS_IMETHOD MatchName(nsString *name, PRBool *matches); nsresult CreateDirectoryForFolder(nsFileSpec &path); + nsresult GetMsgPreviewTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream); protected: // this is a little helper function that is not part of the public interface. diff --git a/mozilla/mailnews/imap/src/nsImapMailFolder.cpp b/mozilla/mailnews/imap/src/nsImapMailFolder.cpp index f01bdd06346..29e5c4dfb35 100644 --- a/mozilla/mailnews/imap/src/nsImapMailFolder.cpp +++ b/mozilla/mailnews/imap/src/nsImapMailFolder.cpp @@ -113,6 +113,7 @@ #include "nsEmbedCID.h" #include "nsIMsgComposeService.h" #include "nsMsgCompCID.h" +#include "nsICacheEntryDescriptor.h" static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID); @@ -602,7 +603,7 @@ nsresult nsImapMailFolder::CreateSubFolders(nsFileSpec &path) // automatically computed from the URI, which is in utf7 form. if (!currentFolderNameStr.IsEmpty()) child->SetPrettyName(currentFolderNameStr.get()); - + child->SetMsgDatabase(nsnull); } } return rv; @@ -996,6 +997,8 @@ NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(const char *folderName unusedDB->SetSummaryValid(PR_TRUE); unusedDB->Commit(nsMsgDBCommitType::kLargeCommit); unusedDB->Close(PR_TRUE); + // don't want to hold onto this newly created db. + child->SetMsgDatabase(nsnull); } } if (!suppressNotification) @@ -2624,6 +2627,7 @@ NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxInfo( if ((imapUIDValidity != folderValidity) /* && // if UIDVALIDITY Changed !NET_IsOffline() */) { + NS_ASSERTION(PR_FALSE, "uid validity seems to have changed, blowing away db"); nsCOMPtr pathSpec; rv = GetPath(getter_AddRefs(pathSpec)); if (NS_FAILED(rv)) return rv; @@ -8306,3 +8310,84 @@ void nsImapMailFolder::GetTrashFolderName(nsAString &aFolderName) } } } +NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, + PRBool aLocalOnly, nsIUrlListener *aUrlListener, + PRBool *aAsyncResults) +{ + NS_ENSURE_ARG_POINTER(aKeysToFetch); + NS_ENSURE_ARG_POINTER(aAsyncResults); + + *aAsyncResults = PR_FALSE; + nsresult rv = NS_OK; + + for (PRUint32 i = 0; i < aNumKeys; i++) + { + nsCOMPtr msgHdr; + nsXPIDLCString prevBody; + rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr)); + NS_ENSURE_SUCCESS(rv, rv); + // ignore messages that already have a preview body. + msgHdr->GetStringProperty("preview", getter_Copies(prevBody)); + if (!prevBody.IsEmpty()) + continue; + + /* check if message is in memory cache or offline store. */ + nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr msgService = do_QueryInterface(imapService); + nsCOMPtr url; + nsCOMPtr inputStream; + + nsXPIDLCString messageUri; + rv = GetUriForMsg(msgHdr, getter_Copies(messageUri)); + NS_ENSURE_SUCCESS(rv,rv); + rv = msgService->GetUrlForUri(messageUri, getter_AddRefs(url), nsnull); + NS_ENSURE_SUCCESS(rv,rv); + nsCAutoString urlSpec; + url->GetAsciiSpec(urlSpec); + + nsCOMPtr cacheSession; + rv = imapService->GetCacheSession(getter_AddRefs(cacheSession)); + NS_ENSURE_SUCCESS(rv, rv); + PRInt32 uidValidity; + GetUidValidity(&uidValidity); + // stick the uid validity in front of the url, so that if the uid validity + // changes, we won't re-use the wrong cache entries. + nsCAutoString cacheKey; + cacheKey.AppendInt(uidValidity, 16); + cacheKey.Append(urlSpec); + nsCOMPtr cacheEntry; + + // if mem cache entry is broken or empty, go to next message. + rv = cacheSession->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ, PR_TRUE, getter_AddRefs(cacheEntry)); + if (cacheEntry) + { + rv = cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream)); + if (NS_SUCCEEDED(rv)) + { + PRUint32 bytesAvailable = 0; + rv = inputStream->Available(&bytesAvailable); + if (!bytesAvailable) + continue; + rv = GetMsgPreviewTextFromStream(msgHdr, inputStream); + } + } + else // lets look in the offline store + { + PRUint32 msgFlags; + msgHdr->GetFlags(&msgFlags); + if (msgFlags & MSG_FLAG_OFFLINE) + { + nsMsgKey msgKey; + msgHdr->GetMessageKey(&msgKey); + nsMsgKey messageOffset; + PRUint32 messageSize; + GetOfflineFileStream(msgKey, &messageOffset, &messageSize, getter_AddRefs(inputStream)); + if (inputStream) + rv = GetMsgPreviewTextFromStream(msgHdr, inputStream); + } + } + } + return rv; +} + diff --git a/mozilla/mailnews/imap/src/nsImapMailFolder.h b/mozilla/mailnews/imap/src/nsImapMailFolder.h index ae1647d567a..05169357c93 100644 --- a/mozilla/mailnews/imap/src/nsImapMailFolder.h +++ b/mozilla/mailnews/imap/src/nsImapMailFolder.h @@ -284,6 +284,10 @@ public: NS_IMETHOD DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow); NS_IMETHOD GetCanFileMessages(PRBool *aCanFileMessages); NS_IMETHOD GetCanDeleteMessages(PRBool *aCanDeleteMessages); + NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, + PRBool aLocalOnly, nsIUrlListener *aUrlListener, + PRBool *aAsyncResults); + // nsIMsgImapMailFolder methods NS_DECL_NSIMSGIMAPMAILFOLDER diff --git a/mozilla/mailnews/local/src/nsLocalMailFolder.cpp b/mozilla/mailnews/local/src/nsLocalMailFolder.cpp index 94911cec2f9..e1571e66276 100644 --- a/mozilla/mailnews/local/src/nsLocalMailFolder.cpp +++ b/mozilla/mailnews/local/src/nsLocalMailFolder.cpp @@ -109,6 +109,7 @@ #include "nsIFileStreams.h" #include "nsAutoPtr.h" #include "nsIRssIncomingServer.h" +#include "nsNetUtil.h" static NS_DEFINE_CID(kMailboxServiceCID, NS_MAILBOXSERVICE_CID); @@ -3712,6 +3713,7 @@ nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState *aState, size = aState->m_header.Length(); if (!size) break; + // this isn't quite right - need to account for line endings len -= size; // account key header will always be before X_UIDL header if (!accountKey) @@ -3722,7 +3724,8 @@ nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState *aState, accountKey += strlen(HEADER_X_MOZILLA_ACCOUNT_KEY) + 2; aState->m_accountKey = accountKey; } - } else + } + else { aState->m_uidl = strstr(aState->m_header.get(), X_UIDL); if (aState->m_uidl) @@ -3813,3 +3816,45 @@ nsMsgLocalMailFolder::WarnIfLocalFileTooBig(nsIMsgWindow *aWindow, PRBool *aTooB return NS_OK; } +NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, + PRBool aLocalOnly, nsIUrlListener *aUrlListener, + PRBool *aAsyncResults) +{ + NS_ENSURE_ARG_POINTER(aKeysToFetch); + NS_ENSURE_ARG_POINTER(aAsyncResults); + + *aAsyncResults = PR_FALSE; + nsXPIDLCString nativePath; + mPath->GetNativePath(getter_Copies(nativePath)); + + nsCOMPtr localStore; + nsCOMPtr inputStream; + + nsresult rv = NS_NewNativeLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore)); + NS_ENSURE_SUCCESS(rv, rv); + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), localStore); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < aNumKeys; i++) + { + nsCOMPtr msgHdr; + nsXPIDLCString prevBody; + rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr)); + NS_ENSURE_SUCCESS(rv, rv); + // ignore messages that already have a preview body. + msgHdr->GetStringProperty("preview", getter_Copies(prevBody)); + if (!prevBody.IsEmpty()) + continue; + PRUint32 messageOffset; + + msgHdr->GetMessageOffset(&messageOffset); + nsCOMPtr seekableStream = do_QueryInterface(inputStream); + if (seekableStream) + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, messageOffset); + NS_ENSURE_SUCCESS(rv,rv); + rv = GetMsgPreviewTextFromStream(msgHdr, inputStream); + + } + return rv; +} + diff --git a/mozilla/mailnews/local/src/nsLocalMailFolder.h b/mozilla/mailnews/local/src/nsLocalMailFolder.h index e98da0f37f3..4af9b5f1af0 100644 --- a/mozilla/mailnews/local/src/nsLocalMailFolder.h +++ b/mozilla/mailnews/local/src/nsLocalMailFolder.h @@ -196,6 +196,9 @@ public: // Used when headers_only is TRUE NS_IMETHOD DownloadMessagesForOffline(nsISupportsArray *aMessages, nsIMsgWindow *aWindow); + NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, + PRBool aLocalOnly, nsIUrlListener *aUrlListener, + PRBool *aAsyncResults); protected: