/* -*- 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 "nsMsgMailNewsUrl.h" #include "nsIMsgAccountManager.h" #include "nsString.h" #include "nsILoadGroup.h" #include "nsIDocShell.h" #include "nsIWebProgress.h" #include "nsIWebProgressListener.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIIOService.h" #include "nsNetCID.h" #include "nsIStreamListener.h" #include "nsIOutputStream.h" #include "nsIInputStream.h" #include "nsNetUtil.h" #include "nsIFile.h" #include "prmem.h" #include #include "nsMsgUtils.h" #include "mozilla/Components.h" #include "nsProxyRelease.h" #include "mozilla/Encoding.h" #include "nsDocShellLoadState.h" #include "nsContentUtils.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsIChannel.h" nsMsgMailNewsUrl::nsMsgMailNewsUrl() { // nsIURI specific state m_runningUrl = false; m_updatingFolder = false; m_msgIsInLocalCache = false; m_suppressErrorMsgs = false; m_hasNormalizedOrigin = false; // SetSpecInternal() will set this correctly. mMaxProgress = -1; } #define NOTIFY_URL_LISTENERS(propertyfunc_, params_) \ PR_BEGIN_MACRO \ nsTObserverArray>::ForwardIterator iter( \ mUrlListeners); \ while (iter.HasMore()) { \ nsCOMPtr listener = iter.GetNext(); \ listener->propertyfunc_ params_; \ } \ PR_END_MACRO nsMsgMailNewsUrl::~nsMsgMailNewsUrl() { // In IMAP this URL is created and destroyed on the imap thread, // so we must ensure that releases of XPCOM objects (which might be // implemented by non-threadsafe JS components) are released on the // main thread. NS_ReleaseOnMainThread("nsMsgMailNewsUrl::m_baseURL", m_baseURL.forget()); NS_ReleaseOnMainThread("nsMsgMailNewsUrl::mMimeHeaders", mMimeHeaders.forget()); NS_ReleaseOnMainThread("nsMsgMailNewsUrl::m_searchSession", m_searchSession.forget()); nsTObserverArray>::ForwardIterator iter( mUrlListeners); while (iter.HasMore()) { nsCOMPtr listener = iter.GetNext(); if (listener) NS_ReleaseOnMainThread("nsMsgMailNewsUrl::mUrlListeners", listener.forget()); } } NS_IMPL_ADDREF(nsMsgMailNewsUrl) NS_IMPL_RELEASE(nsMsgMailNewsUrl) // We want part URLs to QI to nsIURIWithSpecialOrigin so we can give // them a "normalized" origin. URLs that already have a "normalized" // origin should not QI to nsIURIWithSpecialOrigin. NS_INTERFACE_MAP_BEGIN(nsMsgMailNewsUrl) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMsgMailNewsUrl) NS_INTERFACE_MAP_ENTRY(nsIMsgMailNewsUrl) NS_INTERFACE_MAP_ENTRY(nsIURL) NS_INTERFACE_MAP_ENTRY(nsIURI) NS_INTERFACE_MAP_ENTRY(nsISerializable) NS_INTERFACE_MAP_ENTRY(nsIClassInfo) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIURIWithSpecialOrigin, m_hasNormalizedOrigin) NS_INTERFACE_MAP_END //-------------------------- // Support for serialization //-------------------------- // nsMsgMailNewsUrl is only partly serialized by serializing the "base URL" // which is an nsStandardURL, or by only serializing the Spec. This may // cause problems in the future. See bug 1512356 and bug 1515337 for details, // follow-up in bug 1512698. NS_IMETHODIMP_(void) nsMsgMailNewsUrl::Serialize(mozilla::ipc::URIParams& aParams) { m_baseURL->Serialize(aParams); } //---------------------------- // Support for nsISerializable //---------------------------- NS_IMETHODIMP nsMsgMailNewsUrl::Read(nsIObjectInputStream* stream) { nsAutoCString urlstr; nsresult rv = NS_ReadOptionalCString(stream, urlstr); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr ioService = mozilla::components::IO::Service(); NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED); nsCOMPtr url; rv = ioService->NewURI(urlstr, nullptr, nullptr, getter_AddRefs(url)); NS_ENSURE_SUCCESS(rv, rv); m_baseURL = do_QueryInterface(url); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::Write(nsIObjectOutputStream* stream) { nsAutoCString urlstr; nsresult rv = m_baseURL->GetSpec(urlstr); NS_ENSURE_SUCCESS(rv, rv); return NS_WriteOptionalStringZ(stream, urlstr.get()); } //------------------------- // Support for nsIClassInfo //------------------------- NS_IMETHODIMP nsMsgMailNewsUrl::GetInterfaces(nsTArray& array) { array.Clear(); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetScriptableHelper( nsIXPCScriptable** _retval) { *_retval = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetClassDescription( nsACString& aClassDescription) { aClassDescription.SetIsVoid(true); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetClassID(nsCID** aClassID) { *aClassID = (nsCID*)moz_xmalloc(sizeof(nsCID)); return GetClassIDNoAlloc(*aClassID); } NS_IMETHODIMP nsMsgMailNewsUrl::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } #define NS_MSGMAILNEWSURL_CID \ { \ 0x3fdae3ab, 0x4ac1, 0x4ad4, { \ 0xb2, 0x8a, 0x28, 0xd0, 0xfa, 0x36, 0x39, 0x29 \ } \ } static NS_DEFINE_CID(kNS_MSGMAILNEWSURL_CID, NS_MSGMAILNEWSURL_CID); NS_IMETHODIMP nsMsgMailNewsUrl::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { *aClassIDNoAlloc = kNS_MSGMAILNEWSURL_CID; return NS_OK; } //------------------------------------ // Support for nsIURIWithSpecialOrigin //------------------------------------ NS_IMETHODIMP nsMsgMailNewsUrl::GetOrigin(nsIURI** aOrigin) { MOZ_ASSERT(m_hasNormalizedOrigin, "nsMsgMailNewsUrl::GetOrigin() can only be called for URLs with " "normalized spec"); if (!m_normalizedOrigin) { nsCOMPtr msgUrl; QueryInterface(NS_GET_IID(nsIMsgMessageUrl), getter_AddRefs(msgUrl)); nsAutoCString spec; if (!msgUrl || NS_FAILED(msgUrl->GetNormalizedSpec(spec))) { MOZ_ASSERT(false, "Can't get normalized spec"); // just use the normal spec. GetSpec(spec); } nsresult rv = NS_NewURI(getter_AddRefs(m_normalizedOrigin), spec); NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aOrigin = m_normalizedOrigin); return NS_OK; } //////////////////////////////////////////////////////////////////////////////////// // Begin nsIMsgMailNewsUrl specific support //////////////////////////////////////////////////////////////////////////////////// nsresult nsMsgMailNewsUrl::GetUrlState(bool* aRunningUrl) { if (aRunningUrl) *aRunningUrl = m_runningUrl; return NS_OK; } nsresult nsMsgMailNewsUrl::SetUrlState(bool aRunningUrl, nsresult aExitCode) { // if we already knew this running state, return, unless the url was aborted if (m_runningUrl == aRunningUrl && aExitCode != NS_MSG_ERROR_URL_ABORTED) { return NS_OK; } m_runningUrl = aRunningUrl; nsCOMPtr statusFeedback; // put this back - we need it for urls that don't run through the doc loader if (NS_SUCCEEDED(GetStatusFeedback(getter_AddRefs(statusFeedback))) && statusFeedback) { if (m_runningUrl) statusFeedback->StartMeteors(); else { statusFeedback->ShowProgress(0); statusFeedback->StopMeteors(); } } if (m_runningUrl) { NOTIFY_URL_LISTENERS(OnStartRunningUrl, (this)); } else { NOTIFY_URL_LISTENERS(OnStopRunningUrl, (this, aExitCode)); mUrlListeners.Clear(); } return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::RegisterListener(nsIUrlListener* aUrlListener) { NS_ENSURE_ARG_POINTER(aUrlListener); mUrlListeners.AppendElement(aUrlListener); return NS_OK; } nsresult nsMsgMailNewsUrl::UnRegisterListener(nsIUrlListener* aUrlListener) { NS_ENSURE_ARG_POINTER(aUrlListener); // Due to the way mailnews is structured, some listeners attempt to remove // themselves twice. This may in fact be an error in the coding, however // if they didn't do it as they do currently, then they could fail to remove // their listeners. mUrlListeners.RemoveElement(aUrlListener); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetServer( nsIMsgIncomingServer** aIncomingServer) { // mscott --> we could cache a copy of the server here....but if we did, we // run the risk of leaking the server if any single url gets leaked....of // course that shouldn't happen...but it could. so i'm going to look it up // every time and we can look at caching it later. nsresult rv; nsAutoCString urlstr; rv = m_baseURL->GetSpec(urlstr); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr url; rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) .SetSpec(urlstr) .Finalize(url); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString scheme; rv = GetScheme(scheme); if (NS_SUCCEEDED(rv)) { if (scheme.EqualsLiteral("pop")) scheme.AssignLiteral("pop3"); // we use "nntp" in the server list so translate it here. if (scheme.EqualsLiteral("news")) scheme.AssignLiteral("nntp"); rv = NS_MutateURI(url).SetScheme(scheme).Finalize(url); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr accountManager = do_GetService("@mozilla.org/messenger/account-manager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr server; rv = accountManager->FindServerByURI(url, aIncomingServer); if (!*aIncomingServer && scheme.EqualsLiteral("imap")) { // look for any imap server with this host name so clicking on // other users folder urls will work. We could override this method // for imap urls, or we could make caching of servers work and // just set the server in the imap code for this case. rv = NS_MutateURI(url).SetUserPass(EmptyCString()).Finalize(url); NS_ENSURE_SUCCESS(rv, rv); rv = accountManager->FindServerByURI(url, aIncomingServer); } } return rv; } NS_IMETHODIMP nsMsgMailNewsUrl::GetMsgWindow(nsIMsgWindow** aMsgWindow) { NS_ENSURE_ARG_POINTER(aMsgWindow); *aMsgWindow = nullptr; nsCOMPtr msgWindow(do_QueryReferent(m_msgWindowWeak)); msgWindow.forget(aMsgWindow); return *aMsgWindow ? NS_OK : NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsMsgMailNewsUrl::SetMsgWindow(nsIMsgWindow* aMsgWindow) { #ifdef DEBUG_David_Bienvenu NS_ASSERTION(aMsgWindow || !m_msgWindowWeak, "someone crunching non-null msg window"); #endif m_msgWindowWeak = do_GetWeakReference(aMsgWindow); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetStatusFeedback( nsIMsgStatusFeedback** aMsgFeedback) { // note: it is okay to return a null status feedback and not return an error // it's possible the url really doesn't have status feedback *aMsgFeedback = nullptr; if (!m_statusFeedbackWeak) { nsCOMPtr msgWindow(do_QueryReferent(m_msgWindowWeak)); if (msgWindow) msgWindow->GetStatusFeedback(aMsgFeedback); } else { nsCOMPtr statusFeedback( do_QueryReferent(m_statusFeedbackWeak)); statusFeedback.forget(aMsgFeedback); } return *aMsgFeedback ? NS_OK : NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsMsgMailNewsUrl::SetStatusFeedback( nsIMsgStatusFeedback* aMsgFeedback) { if (aMsgFeedback) m_statusFeedbackWeak = do_GetWeakReference(aMsgFeedback); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetMaxProgress(int64_t* aMaxProgress) { *aMaxProgress = mMaxProgress; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetMaxProgress(int64_t aMaxProgress) { mMaxProgress = aMaxProgress; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetLoadGroup(nsILoadGroup** aLoadGroup) { *aLoadGroup = nullptr; // note: it is okay to return a null load group and not return an error // it's possible the url really doesn't have load group nsCOMPtr loadGroup(do_QueryReferent(m_loadGroupWeak)); if (!loadGroup) { nsCOMPtr msgWindow(do_QueryReferent(m_msgWindowWeak)); if (msgWindow) { // XXXbz This is really weird... why are we getting some // random loadgroup we're not really a part of? nsCOMPtr docShell; msgWindow->GetRootDocShell(getter_AddRefs(docShell)); loadGroup = do_GetInterface(docShell); m_loadGroupWeak = do_GetWeakReference(loadGroup); } } loadGroup.forget(aLoadGroup); return *aLoadGroup ? NS_OK : NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsMsgMailNewsUrl::GetUpdatingFolder(bool* aResult) { NS_ENSURE_ARG(aResult); *aResult = m_updatingFolder; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetUpdatingFolder(bool updatingFolder) { m_updatingFolder = updatingFolder; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetMsgIsInLocalCache(bool* aMsgIsInLocalCache) { NS_ENSURE_ARG(aMsgIsInLocalCache); *aMsgIsInLocalCache = m_msgIsInLocalCache; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetMsgIsInLocalCache(bool aMsgIsInLocalCache) { m_msgIsInLocalCache = aMsgIsInLocalCache; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetSuppressErrorMsgs(bool* aSuppressErrorMsgs) { NS_ENSURE_ARG(aSuppressErrorMsgs); *aSuppressErrorMsgs = m_suppressErrorMsgs; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetSuppressErrorMsgs(bool aSuppressErrorMsgs) { m_suppressErrorMsgs = aSuppressErrorMsgs; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetErrorCode(nsACString& aErrorCode) { aErrorCode = m_errorCode; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetErrorCode(const nsACString& aErrorCode) { m_errorCode.Assign(aErrorCode); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetErrorMessage(nsAString& aErrorMessage) { aErrorMessage = m_errorMessage; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetErrorMessage( const nsAString& aErrorMessage) { m_errorMessage.Assign(aErrorMessage); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetSeeOtherURI(const nsACString& aSeeOtherURI) { m_seeOtherURI.Assign(aSeeOtherURI); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetSeeOtherURI(nsACString& aSeeOtherURI) { aSeeOtherURI = m_seeOtherURI; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::IsUrlType(uint32_t type, bool* isType) { // base class doesn't know about any specific types NS_ENSURE_ARG(isType); *isType = false; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetSearchSession( nsIMsgSearchSession* aSearchSession) { if (aSearchSession) m_searchSession = aSearchSession; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetSearchSession( nsIMsgSearchSession** aSearchSession) { NS_ENSURE_ARG(aSearchSession); NS_IF_ADDREF(*aSearchSession = m_searchSession); return NS_OK; } //////////////////////////////////////////////////////////////////////////////////// // End nsIMsgMailNewsUrl specific support //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// // Begin nsIURI support //////////////////////////////////////////////////////////////////////////////////// NS_IMETHODIMP nsMsgMailNewsUrl::GetSpec(nsACString& aSpec) { return m_baseURL->GetSpec(aSpec); } nsresult nsMsgMailNewsUrl::CreateURL(const nsACString& aSpec, nsIURL** aURL) { nsCOMPtr url; nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) .SetSpec(aSpec) .Finalize(url); NS_ENSURE_SUCCESS(rv, rv); url.forget(aURL); return NS_OK; } #define FILENAME_PART_LEN 10 nsresult nsMsgMailNewsUrl::SetSpecInternal(const nsACString& aSpec) { nsAutoCString spec(aSpec); // Parse out "filename" attribute if present. char *start, *end; start = PL_strcasestr(spec.BeginWriting(), "?filename="); if (!start) start = PL_strcasestr(spec.BeginWriting(), "&filename="); if (start) { // Make sure we only get our own value. end = PL_strcasestr((char*)(start + FILENAME_PART_LEN), "&"); if (end) { *end = 0; mAttachmentFileName = start + FILENAME_PART_LEN; *end = '&'; } else mAttachmentFileName = start + FILENAME_PART_LEN; } // Now, set the rest. nsresult rv = CreateURL(aSpec, getter_AddRefs(m_baseURL)); NS_ENSURE_SUCCESS(rv, rv); // Check whether the URL is in normalized form. nsCOMPtr msgUrl; QueryInterface(NS_GET_IID(nsIMsgMessageUrl), getter_AddRefs(msgUrl)); nsAutoCString normalizedSpec; if (!msgUrl || NS_FAILED(msgUrl->GetNormalizedSpec(normalizedSpec))) { // If we can't get the normalized spec, never QI this to // nsIURIWithSpecialOrigin. m_hasNormalizedOrigin = false; } else { m_hasNormalizedOrigin = !spec.Equals(normalizedSpec); } return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetPrePath(nsACString& aPrePath) { return m_baseURL->GetPrePath(aPrePath); } NS_IMETHODIMP nsMsgMailNewsUrl::GetScheme(nsACString& aScheme) { return m_baseURL->GetScheme(aScheme); } nsresult nsMsgMailNewsUrl::SetScheme(const nsACString& aScheme) { return NS_MutateURI(m_baseURL).SetScheme(aScheme).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetUserPass(nsACString& aUserPass) { return m_baseURL->GetUserPass(aUserPass); } nsresult nsMsgMailNewsUrl::SetUserPass(const nsACString& aUserPass) { return NS_MutateURI(m_baseURL).SetUserPass(aUserPass).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetUsername(nsACString& aUsername) { /* note: this will return an escaped string */ return m_baseURL->GetUsername(aUsername); } nsresult nsMsgMailNewsUrl::SetUsername(const nsACString& aUsername) { return NS_MutateURI(m_baseURL).SetUsername(aUsername).Finalize(m_baseURL); } nsresult nsMsgMailNewsUrl::SetUsernameInternal(const nsACString& aUsername) { return NS_MutateURI(m_baseURL).SetUsername(aUsername).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetPassword(nsACString& aPassword) { return m_baseURL->GetPassword(aPassword); } nsresult nsMsgMailNewsUrl::SetPassword(const nsACString& aPassword) { return NS_MutateURI(m_baseURL).SetPassword(aPassword).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetHostPort(nsACString& aHostPort) { return m_baseURL->GetHostPort(aHostPort); } nsresult nsMsgMailNewsUrl::SetHostPort(const nsACString& aHostPort) { return NS_MutateURI(m_baseURL).SetHostPort(aHostPort).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetHost(nsACString& aHost) { return m_baseURL->GetHost(aHost); } nsresult nsMsgMailNewsUrl::SetHost(const nsACString& aHost) { return NS_MutateURI(m_baseURL).SetHost(aHost).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetPort(int32_t* aPort) { return m_baseURL->GetPort(aPort); } nsresult nsMsgMailNewsUrl::SetPort(int32_t aPort) { return NS_MutateURI(m_baseURL).SetPort(aPort).Finalize(m_baseURL); } nsresult nsMsgMailNewsUrl::SetPortInternal(int32_t aPort) { return NS_MutateURI(m_baseURL).SetPort(aPort).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetPathQueryRef(nsACString& aPath) { return m_baseURL->GetPathQueryRef(aPath); } nsresult nsMsgMailNewsUrl::SetPathQueryRef(const nsACString& aPath) { return NS_MutateURI(m_baseURL).SetPathQueryRef(aPath).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetAsciiHost(nsACString& aHostA) { return m_baseURL->GetAsciiHost(aHostA); } NS_IMETHODIMP nsMsgMailNewsUrl::GetAsciiHostPort(nsACString& aHostPortA) { return m_baseURL->GetAsciiHostPort(aHostPortA); } NS_IMETHODIMP nsMsgMailNewsUrl::GetAsciiSpec(nsACString& aSpecA) { return m_baseURL->GetAsciiSpec(aSpecA); } NS_IMETHODIMP nsMsgMailNewsUrl::GetBaseURI(nsIURI** aBaseURI) { NS_ENSURE_ARG_POINTER(aBaseURI); return m_baseURL->QueryInterface(NS_GET_IID(nsIURI), (void**)aBaseURI); } NS_IMETHODIMP nsMsgMailNewsUrl::Equals(nsIURI* other, bool* _retval) { // The passed-in URI might be a mail news url. Pass our inner URL to its // Equals method. The other mail news url will then pass its inner URL to // to the Equals method of our inner URL. Other URIs will return false. if (other) return other->Equals(m_baseURL, _retval); return m_baseURL->Equals(other, _retval); } NS_IMETHODIMP nsMsgMailNewsUrl::EqualsExceptRef(nsIURI* other, bool* result) { // The passed-in URI might be a mail news url. Pass our inner URL to its // Equals method. The other mail news url will then pass its inner URL to // to the Equals method of our inner URL. Other URIs will return false. if (other) return other->EqualsExceptRef(m_baseURL, result); return m_baseURL->EqualsExceptRef(other, result); } NS_IMETHODIMP nsMsgMailNewsUrl::GetSpecIgnoringRef(nsACString& result) { return m_baseURL->GetSpecIgnoringRef(result); } NS_IMETHODIMP nsMsgMailNewsUrl::GetDisplaySpec(nsACString& aUnicodeSpec) { return m_baseURL->GetDisplaySpec(aUnicodeSpec); } NS_IMETHODIMP nsMsgMailNewsUrl::GetDisplayHostPort(nsACString& aHostPort) { return m_baseURL->GetDisplayHostPort(aHostPort); } NS_IMETHODIMP nsMsgMailNewsUrl::GetDisplayHost(nsACString& aHost) { return m_baseURL->GetDisplayHost(aHost); } NS_IMETHODIMP nsMsgMailNewsUrl::GetDisplayPrePath(nsACString& aPrePath) { return m_baseURL->GetDisplayPrePath(aPrePath); } NS_IMETHODIMP nsMsgMailNewsUrl::GetHasRef(bool* result) { return m_baseURL->GetHasRef(result); } NS_IMETHODIMP nsMsgMailNewsUrl::SchemeIs(const char* aScheme, bool* _retval) { return m_baseURL->SchemeIs(aScheme, _retval); } nsresult nsMsgMailNewsUrl::Clone(nsIURI** _retval) { nsresult rv; nsAutoCString urlSpec; nsCOMPtr ioService = mozilla::components::IO::Service(); NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED); rv = GetSpec(urlSpec); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr newUri; rv = ioService->NewURI(urlSpec, nullptr, nullptr, getter_AddRefs(newUri)); NS_ENSURE_SUCCESS(rv, rv); // add the msg window to the cloned url nsCOMPtr msgWindow(do_QueryReferent(m_msgWindowWeak)); if (msgWindow) { nsCOMPtr msgMailNewsUrl = do_QueryInterface(newUri, &rv); NS_ENSURE_SUCCESS(rv, rv); msgMailNewsUrl->SetMsgWindow(msgWindow); } newUri.forget(_retval); return rv; } NS_IMETHODIMP nsMsgMailNewsUrl::Resolve(const nsACString& relativePath, nsACString& result) { // only resolve anchor urls....i.e. urls which start with '#' against the // mailnews url... everything else shouldn't be resolved against mailnews // urls. nsresult rv = NS_OK; if (relativePath.IsEmpty()) { // Return base URL. rv = GetSpec(result); } else if (!relativePath.IsEmpty() && relativePath.First() == '#') // an anchor { rv = m_baseURL->Resolve(relativePath, result); } else { // if relativePath is a complete url with it's own scheme then allow it... nsCOMPtr ioService = mozilla::components::IO::Service(); NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED); nsAutoCString scheme; rv = ioService->ExtractScheme(relativePath, scheme); // if we have a fully qualified scheme then pass the relative path back as // the result if (NS_SUCCEEDED(rv) && !scheme.IsEmpty()) { result = relativePath; rv = NS_OK; } else { result.Truncate(); rv = NS_ERROR_FAILURE; } } return rv; } NS_IMETHODIMP nsMsgMailNewsUrl::GetDirectory(nsACString& aDirectory) { return m_baseURL->GetDirectory(aDirectory); } NS_IMETHODIMP nsMsgMailNewsUrl::GetFileName(nsACString& aFileName) { if (!mAttachmentFileName.IsEmpty()) { aFileName = mAttachmentFileName; return NS_OK; } return m_baseURL->GetFileName(aFileName); } NS_IMETHODIMP nsMsgMailNewsUrl::GetFileBaseName(nsACString& aFileBaseName) { return m_baseURL->GetFileBaseName(aFileBaseName); } NS_IMETHODIMP nsMsgMailNewsUrl::GetFileExtension(nsACString& aFileExtension) { if (!mAttachmentFileName.IsEmpty()) { int32_t pos = mAttachmentFileName.RFindChar(char16_t('.')); if (pos > 0) aFileExtension = Substring(mAttachmentFileName, pos + 1 /* skip the '.' */); return NS_OK; } return m_baseURL->GetFileExtension(aFileExtension); } nsresult nsMsgMailNewsUrl::SetFileNameInternal(const nsACString& aFileName) { mAttachmentFileName = aFileName; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetQuery(nsACString& aQuery) { return m_baseURL->GetQuery(aQuery); } nsresult nsMsgMailNewsUrl::SetQuery(const nsACString& aQuery) { return NS_MutateURI(m_baseURL).SetQuery(aQuery).Finalize(m_baseURL); } nsresult nsMsgMailNewsUrl::SetQueryInternal(const nsACString& aQuery) { return NS_MutateURI(m_baseURL).SetQuery(aQuery).Finalize(m_baseURL); } nsresult nsMsgMailNewsUrl::SetQueryWithEncoding( const nsACString& aQuery, const mozilla::Encoding* aEncoding) { return NS_MutateURI(m_baseURL) .SetQueryWithEncoding(aQuery, aEncoding) .Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetRef(nsACString& aRef) { return m_baseURL->GetRef(aRef); } nsresult nsMsgMailNewsUrl::SetRef(const nsACString& aRef) { return NS_MutateURI(m_baseURL).SetRef(aRef).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetFilePath(nsACString& o_DirFile) { return m_baseURL->GetFilePath(o_DirFile); } nsresult nsMsgMailNewsUrl::SetFilePath(const nsACString& i_DirFile) { return NS_MutateURI(m_baseURL).SetFilePath(i_DirFile).Finalize(m_baseURL); } NS_IMETHODIMP nsMsgMailNewsUrl::GetCommonBaseSpec(nsIURI* uri2, nsACString& result) { return m_baseURL->GetCommonBaseSpec(uri2, result); } NS_IMETHODIMP nsMsgMailNewsUrl::GetRelativeSpec(nsIURI* uri2, nsACString& result) { return m_baseURL->GetRelativeSpec(uri2, result); } NS_IMETHODIMP nsMsgMailNewsUrl::SetMemCacheEntry(nsICacheEntry* memCacheEntry) { m_memCacheEntry = memCacheEntry; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetMemCacheEntry( nsICacheEntry** memCacheEntry) { NS_ENSURE_ARG(memCacheEntry); nsresult rv = NS_OK; if (m_memCacheEntry) { NS_ADDREF(*memCacheEntry = m_memCacheEntry); } else { *memCacheEntry = nullptr; return NS_ERROR_NULL_POINTER; } return rv; } NS_IMETHODIMP nsMsgMailNewsUrl::GetMimeHeaders(nsIMimeHeaders** mimeHeaders) { NS_ENSURE_ARG_POINTER(mimeHeaders); NS_IF_ADDREF(*mimeHeaders = mMimeHeaders); return (mMimeHeaders) ? NS_OK : NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsMsgMailNewsUrl::SetMimeHeaders(nsIMimeHeaders* mimeHeaders) { mMimeHeaders = mimeHeaders; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::LoadURI(nsIDocShell* docShell, uint32_t aLoadFlags) { NS_ENSURE_ARG_POINTER(docShell); RefPtr loadState = new nsDocShellLoadState(this); loadState->SetLoadFlags(aLoadFlags); loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags)); loadState->SetFirstParty(false); loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); return docShell->LoadURI(loadState, false); } #define SAVE_BUF_SIZE FILE_IO_BUFFER_SIZE class nsMsgSaveAsListener : public nsIStreamListener { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER nsMsgSaveAsListener(nsIFile* aFile, bool addDummyEnvelope); nsresult SetupMsgWriteStream(nsIFile* aFile, bool addDummyEnvelope); protected: virtual ~nsMsgSaveAsListener(); nsCOMPtr m_outputStream; nsCOMPtr m_outputFile; bool m_addDummyEnvelope; bool m_writtenData; uint32_t m_leftOver; char m_dataBuffer[SAVE_BUF_SIZE + 1]; // temporary buffer for this save operation }; NS_IMPL_ISUPPORTS(nsMsgSaveAsListener, nsIStreamListener, nsIRequestObserver) nsMsgSaveAsListener::nsMsgSaveAsListener(nsIFile* aFile, bool addDummyEnvelope) { m_outputFile = aFile; m_writtenData = false; m_addDummyEnvelope = addDummyEnvelope; m_leftOver = 0; } nsMsgSaveAsListener::~nsMsgSaveAsListener() {} NS_IMETHODIMP nsMsgSaveAsListener::OnStartRequest(nsIRequest* request) { return NS_OK; } NS_IMETHODIMP nsMsgSaveAsListener::OnStopRequest(nsIRequest* request, nsresult aStatus) { if (m_outputStream) { m_outputStream->Flush(); m_outputStream->Close(); } return NS_OK; } NS_IMETHODIMP nsMsgSaveAsListener::OnDataAvailable(nsIRequest* request, nsIInputStream* inStream, uint64_t srcOffset, uint32_t count) { nsresult rv; uint64_t available; rv = inStream->Available(&available); if (!m_writtenData) { m_writtenData = true; rv = SetupMsgWriteStream(m_outputFile, m_addDummyEnvelope); NS_ENSURE_SUCCESS(rv, rv); } bool useCanonicalEnding = false; // We know the request is an nsIChannel we can get a URI from, but this is // probably bad form. See Bug 1528662. nsCOMPtr channel = do_QueryInterface(request, &rv); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "error QI nsIRequest to nsIChannel failed"); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr uri; rv = channel->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr msgUrl = do_QueryInterface(uri); if (msgUrl) msgUrl->GetCanonicalLineEnding(&useCanonicalEnding); const char* lineEnding = (useCanonicalEnding) ? CRLF : MSG_LINEBREAK; uint32_t lineEndingLength = (useCanonicalEnding) ? 2 : MSG_LINEBREAK_LEN; uint32_t readCount, maxReadCount = SAVE_BUF_SIZE - m_leftOver; uint32_t writeCount; char *start, *end, lastCharInPrevBuf = '\0'; uint32_t linebreak_len = 0; while (count > 0) { if (count < maxReadCount) maxReadCount = count; rv = inStream->Read(m_dataBuffer + m_leftOver, maxReadCount, &readCount); if (NS_FAILED(rv)) return rv; m_leftOver += readCount; m_dataBuffer[m_leftOver] = '\0'; start = m_dataBuffer; // make sure we don't insert another LF, accidentally, by ignoring // second half of CRLF spanning blocks. if (lastCharInPrevBuf == '\r' && *start == '\n') start++; end = PL_strpbrk(start, "\r\n"); if (end) linebreak_len = (end[0] == '\r' && end[1] == '\n') ? 2 : 1; count -= readCount; maxReadCount = SAVE_BUF_SIZE - m_leftOver; if (!end && count > maxReadCount) // must be a very very long line; sorry cannot handle it return NS_ERROR_FAILURE; while (start && end) { if (m_outputStream && PL_strncasecmp(start, "X-Mozilla-Status:", 17) && PL_strncasecmp(start, "X-Mozilla-Status2:", 18) && PL_strncmp(start, "From - ", 7)) { rv = m_outputStream->Write(start, end - start, &writeCount); nsresult tmp = m_outputStream->Write(lineEnding, lineEndingLength, &writeCount); if (NS_FAILED(tmp)) { rv = tmp; } } start = end + linebreak_len; if (start >= m_dataBuffer + m_leftOver) { maxReadCount = SAVE_BUF_SIZE; m_leftOver = 0; break; } end = PL_strpbrk(start, "\r\n"); if (end) linebreak_len = (end[0] == '\r' && end[1] == '\n') ? 2 : 1; if (start && !end) { m_leftOver -= (start - m_dataBuffer); memcpy(m_dataBuffer, start, m_leftOver + 1); // including null maxReadCount = SAVE_BUF_SIZE - m_leftOver; } } if (NS_FAILED(rv)) return rv; if (end) lastCharInPrevBuf = *end; } return rv; // rv = m_outputStream->WriteFrom(inStream, std::min(available, count), // &bytesWritten); } nsresult nsMsgSaveAsListener::SetupMsgWriteStream(nsIFile* aFile, bool addDummyEnvelope) { // If the file already exists, delete it, but do this before // getting the outputstream. // Due to bug 328027, the nsSaveMsgListener created in // nsMessenger::SaveAs now opens the stream on the nsIFile // object, thus creating an empty file. Actual save operations for // IMAP and NNTP use this nsMsgSaveAsListener here, though, so we // have to close the stream before deleting the file, else data // would still be written happily into a now non-existing file. // (Windows doesn't care, btw, just unixoids do...) aFile->Remove(false); nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_outputStream), aFile, -1, 0666); NS_ENSURE_SUCCESS(rv, rv); if (m_outputStream && addDummyEnvelope) { nsAutoCString result; uint32_t writeCount; time_t now = time((time_t*)0); char* ct = ctime(&now); // Remove the ending new-line character. ct[24] = '\0'; result = "From - "; result += ct; result += MSG_LINEBREAK; m_outputStream->Write(result.get(), result.Length(), &writeCount); result = "X-Mozilla-Status: 0001"; result += MSG_LINEBREAK; result += "X-Mozilla-Status2: 00000000"; result += MSG_LINEBREAK; m_outputStream->Write(result.get(), result.Length(), &writeCount); } return rv; } NS_IMETHODIMP nsMsgMailNewsUrl::GetSaveAsListener( bool addDummyEnvelope, nsIFile* aFile, nsIStreamListener** aSaveListener) { NS_ENSURE_ARG_POINTER(aSaveListener); nsMsgSaveAsListener* saveAsListener = new nsMsgSaveAsListener(aFile, addDummyEnvelope); return saveAsListener->QueryInterface(NS_GET_IID(nsIStreamListener), (void**)aSaveListener); } NS_IMETHODIMP nsMsgMailNewsUrl::SetFailedSecInfo(nsITransportSecurityInfo* secInfo) { mFailedSecInfo = secInfo; return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::GetFailedSecInfo( nsITransportSecurityInfo** secInfo) { NS_ENSURE_ARG_POINTER(secInfo); NS_IF_ADDREF(*secInfo = mFailedSecInfo); return NS_OK; } NS_IMETHODIMP nsMsgMailNewsUrl::SetFolder(nsIMsgFolder* /* aFolder */) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgMailNewsUrl::GetFolder(nsIMsgFolder** /* aFolder */) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgMailNewsUrl::GetIsMessageUri(bool* aIsMessageUri) { NS_ENSURE_ARG(aIsMessageUri); nsAutoCString scheme; m_baseURL->GetScheme(scheme); *aIsMessageUri = StringEndsWith(scheme, "-message"_ns); return NS_OK; } NS_IMPL_ISUPPORTS(nsMsgMailNewsUrl::Mutator, nsIURISetters, nsIURIMutator) NS_IMETHODIMP nsMsgMailNewsUrl::Mutate(nsIURIMutator** aMutator) { RefPtr mutator = new nsMsgMailNewsUrl::Mutator(); nsresult rv = mutator->InitFromURI(this); if (NS_FAILED(rv)) { return rv; } mutator.forget(aMutator); return NS_OK; }