summaryrefslogtreecommitdiffstats
path: root/mailnews/base/util/nsMsgMailNewsUrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/base/util/nsMsgMailNewsUrl.cpp')
-rw-r--r--mailnews/base/util/nsMsgMailNewsUrl.cpp1060
1 files changed, 1060 insertions, 0 deletions
diff --git a/mailnews/base/util/nsMsgMailNewsUrl.cpp b/mailnews/base/util/nsMsgMailNewsUrl.cpp
new file mode 100644
index 000000000..e9dc52b33
--- /dev/null
+++ b/mailnews/base/util/nsMsgMailNewsUrl.cpp
@@ -0,0 +1,1060 @@
+/* -*- 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 "nsMsgBaseCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsStringGlue.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 <time.h>
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+#include <algorithm>
+#include "nsProxyRelease.h"
+#include "mozilla/BasePrincipal.h"
+
+nsMsgMailNewsUrl::nsMsgMailNewsUrl()
+{
+ // nsIURI specific state
+ m_errorMessage = nullptr;
+ m_runningUrl = false;
+ m_updatingFolder = false;
+ m_msgIsInLocalCache = false;
+ m_suppressErrorMsgs = false;
+ m_isPrincipalURL = false;
+ mMaxProgress = -1;
+ m_baseURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
+}
+
+#define NOTIFY_URL_LISTENERS(propertyfunc_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<nsCOMPtr<nsIUrlListener> >::ForwardIterator iter(mUrlListeners); \
+ while (iter.HasMore()) { \
+ nsCOMPtr<nsIUrlListener> listener = iter.GetNext(); \
+ listener->propertyfunc_ params_; \
+ } \
+ PR_END_MACRO
+
+nsMsgMailNewsUrl::~nsMsgMailNewsUrl()
+{
+ PR_FREEIF(m_errorMessage);
+
+ // 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(m_baseURL.forget());
+ NS_ReleaseOnMainThread(mMimeHeaders.forget());
+ NS_ReleaseOnMainThread(m_searchSession.forget());
+ NS_ReleaseOnMainThread(mMsgHeaderSink.forget());
+
+ nsTObserverArray<nsCOMPtr<nsIUrlListener>>::ForwardIterator iter(mUrlListeners);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIUrlListener> listener = iter.GetNext();
+ if (listener)
+ NS_ReleaseOnMainThread(listener.forget());
+ }
+}
+
+NS_IMPL_ADDREF(nsMsgMailNewsUrl)
+NS_IMPL_RELEASE(nsMsgMailNewsUrl)
+
+// We want part URLs to QI to nsIURIWithPrincipal so we can give
+// them a "normalised" origin. URLs that already have a "normalised"
+// origin should not QI to nsIURIWithPrincipal.
+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(nsIURIWithQuery)
+ NS_INTERFACE_MAP_ENTRY(nsIURI)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIURIWithPrincipal, !m_isPrincipalURL)
+NS_INTERFACE_MAP_END
+
+// Support for nsIURIWithPrincipal.
+NS_IMETHODIMP nsMsgMailNewsUrl::GetPrincipal(nsIPrincipal **aPrincipal)
+{
+ MOZ_ASSERT(!m_isPrincipalURL,
+ "nsMsgMailNewsUrl::GetPrincipal() can only be called for non-principal URLs");
+
+ if (!m_principal) {
+ nsCOMPtr <nsIMsgMessageUrl> msgUrl;
+ QueryInterface(NS_GET_IID(nsIMsgMessageUrl), getter_AddRefs(msgUrl));
+
+ nsAutoCString spec;
+ if (!msgUrl || NS_FAILED(msgUrl->GetPrincipalSpec(spec))) {
+ MOZ_ASSERT(false, "Can't get principal spec");
+ // just use the normal spec.
+ GetSpec(spec);
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mozilla::PrincipalOriginAttributes attrs;
+ m_principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+ }
+
+ NS_IF_ADDREF(*aPrincipal = m_principal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetPrincipalUri(nsIURI **aPrincipalURI)
+{
+ NS_ENSURE_ARG_POINTER(aPrincipalURI);
+ if (!m_principal) {
+ nsCOMPtr<nsIPrincipal> p;
+ GetPrincipal(getter_AddRefs(p));
+ }
+ if (!m_principal)
+ return NS_ERROR_NULL_POINTER;
+ return m_principal->GetURI(aPrincipalURI);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// 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 <nsIMsgStatusFeedback> 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;
+ nsAutoCString scheme;
+
+ nsCOMPtr<nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = m_baseURL->GetSpec(urlstr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = url->SetSpec(urlstr);
+ if (NS_FAILED(rv)) return rv;
+ rv = GetScheme(scheme);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (scheme.EqualsLiteral("pop"))
+ scheme.Assign("pop3");
+ // we use "nntp" in the server list so translate it here.
+ if (scheme.EqualsLiteral("news"))
+ scheme.Assign("nntp");
+ url->SetScheme(scheme);
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = accountManager->FindServerByURI(url, false,
+ 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.
+ url->SetUserPass(EmptyCString());
+ rv = accountManager->FindServerByURI(url, false,
+ aIncomingServer);
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetMsgWindow(nsIMsgWindow **aMsgWindow)
+{
+ NS_ENSURE_ARG_POINTER(aMsgWindow);
+ *aMsgWindow = nullptr;
+
+ nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
+ msgWindow.swap(*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<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
+ if (msgWindow)
+ msgWindow->GetStatusFeedback(aMsgFeedback);
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgStatusFeedback> statusFeedback(do_QueryReferent(m_statusFeedbackWeak));
+ statusFeedback.swap(*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<nsILoadGroup> loadGroup (do_QueryReferent(m_loadGroupWeak));
+ if (!loadGroup)
+ {
+ nsCOMPtr<nsIMsgWindow> 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<nsIDocShell> docShell;
+ msgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ loadGroup = do_GetInterface(docShell);
+ m_loadGroupWeak = do_GetWeakReference(loadGroup);
+ }
+ }
+ loadGroup.swap(*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::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 = do_QueryInterface(aSearchSession);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetSearchSession(nsIMsgSearchSession **aSearchSession)
+{
+ NS_ENSURE_ARG(aSearchSession);
+ *aSearchSession = m_searchSession;
+ NS_IF_ADDREF(*aSearchSession);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// End nsIMsgMailNewsUrl specific support
+////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////
+// Begin nsIURI support
+////////////////////////////////////////////////////////////////////////////////////
+
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetSpec(nsACString &aSpec)
+{
+ return m_baseURL->GetSpec(aSpec);
+}
+
+#define FILENAME_PART_LEN 10
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetSpec(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 = m_baseURL->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Check whether the URL is in normalised form.
+ nsCOMPtr <nsIMsgMessageUrl> msgUrl;
+ QueryInterface(NS_GET_IID(nsIMsgMessageUrl), getter_AddRefs(msgUrl));
+
+ nsAutoCString principalSpec;
+ if (!msgUrl || NS_FAILED(msgUrl->GetPrincipalSpec(principalSpec))) {
+ // If we can't get the principal spec, never QI this to nsIURIWithPrincipal.
+ m_isPrincipalURL = true;
+ } else {
+ m_isPrincipalURL = spec.Equals(principalSpec);
+ }
+ 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);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetScheme(const nsACString &aScheme)
+{
+ return m_baseURL->SetScheme(aScheme);
+}
+
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetUserPass(nsACString &aUserPass)
+{
+ return m_baseURL->GetUserPass(aUserPass);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetUserPass(const nsACString &aUserPass)
+{
+ return m_baseURL->SetUserPass(aUserPass);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetUsername(nsACString &aUsername)
+{
+ /* note: this will return an escaped string */
+ return m_baseURL->GetUsername(aUsername);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetUsername(const nsACString &aUsername)
+{
+ return m_baseURL->SetUsername(aUsername);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetPassword(nsACString &aPassword)
+{
+ return m_baseURL->GetPassword(aPassword);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetPassword(const nsACString &aPassword)
+{
+ return m_baseURL->SetPassword(aPassword);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetHostPort(nsACString &aHostPort)
+{
+ return m_baseURL->GetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetHostPort(const nsACString &aHostPort)
+{
+ return m_baseURL->SetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetHostAndPort(const nsACString &aHostPort)
+{
+ return m_baseURL->SetHostAndPort(aHostPort);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetHost(nsACString &aHost)
+{
+ return m_baseURL->GetHost(aHost);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetHost(const nsACString &aHost)
+{
+ return m_baseURL->SetHost(aHost);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetPort(int32_t *aPort)
+{
+ return m_baseURL->GetPort(aPort);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetPort(int32_t aPort)
+{
+ return m_baseURL->SetPort(aPort);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetPath(nsACString &aPath)
+{
+ return m_baseURL->GetPath(aPath);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetPath(const nsACString &aPath)
+{
+ return m_baseURL->SetPath(aPath);
+}
+
+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::GetOriginCharset(nsACString &aOriginCharset)
+{
+ return m_baseURL->GetOriginCharset(aOriginCharset);
+}
+
+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::GetHasRef(bool *result)
+{
+ return m_baseURL->GetHasRef(result);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SchemeIs(const char *aScheme, bool *_retval)
+{
+ return m_baseURL->SchemeIs(aScheme, _retval);
+}
+
+NS_IMETHODIMP
+nsMsgMailNewsUrl::CloneInternal(uint32_t aRefHandlingMode,
+ const nsACString& newRef, nsIURI** _retval)
+{
+ nsresult rv;
+ nsAutoCString urlSpec;
+ nsCOMPtr<nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+ rv = GetSpec(urlSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ioService->NewURI(urlSpec, nullptr, nullptr, _retval);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // add the msg window to the cloned url
+ nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
+ if (msgWindow)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgMailNewsUrl = do_QueryInterface(*_retval, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ msgMailNewsUrl->SetMsgWindow(msgWindow);
+ }
+
+ if (aRefHandlingMode == nsIMsgMailNewsUrl::REPLACE_REF) {
+ rv = (*_retval)->SetRef(newRef);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (aRefHandlingMode == nsIMsgMailNewsUrl::IGNORE_REF) {
+ rv = (*_retval)->SetRef(EmptyCString());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::Clone(nsIURI **_retval)
+{
+ return CloneInternal(nsIMsgMailNewsUrl::HONOR_REF, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsMsgMailNewsUrl::CloneIgnoringRef(nsIURI** _retval)
+{
+ return CloneInternal(nsIMsgMailNewsUrl::IGNORE_REF, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsMsgMailNewsUrl::CloneWithNewRef(const nsACString& newRef, nsIURI** _retval)
+{
+ return CloneInternal(nsIMsgMailNewsUrl::REPLACE_REF, newRef, _retval);
+}
+
+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() && relativePath.First() == '#') // an anchor
+ return m_baseURL->Resolve(relativePath, result);
+ else
+ {
+ // if relativePath is a complete url with it's own scheme then allow it...
+ nsCOMPtr<nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ 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::SetDirectory(const nsACString &aDirectory)
+{
+
+ return m_baseURL->SetDirectory(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::SetFileBaseName(const nsACString &aFileBaseName)
+{
+ return m_baseURL->SetFileBaseName(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);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetFileExtension(const nsACString &aFileExtension)
+{
+ return m_baseURL->SetFileExtension(aFileExtension);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetFileName(const nsACString &aFileName)
+{
+ mAttachmentFileName = aFileName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetQuery(nsACString &aQuery)
+{
+ return m_baseURL->GetQuery(aQuery);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetQuery(const nsACString &aQuery)
+{
+ return m_baseURL->SetQuery(aQuery);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetRef(nsACString &aRef)
+{
+ return m_baseURL->GetRef(aRef);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetRef(const nsACString &aRef)
+{
+ return m_baseURL->SetRef(aRef);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetFilePath(nsACString &o_DirFile)
+{
+ return m_baseURL->GetFilePath(o_DirFile);
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetFilePath(const nsACString &i_DirFile)
+{
+ return m_baseURL->SetFilePath(i_DirFile);
+}
+
+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)
+ {
+ *memCacheEntry = m_memCacheEntry;
+ NS_ADDREF(*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,
+ nsIDocShellLoadInfo* loadInfo,
+ uint32_t aLoadFlags)
+{
+ NS_ENSURE_ARG_POINTER(docShell);
+ return docShell->LoadURI(this, loadInfo, aLoadFlags, 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<nsIOutputStream> m_outputStream;
+ nsCOMPtr<nsIFile> 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, nsISupports *ctxt)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSaveAsListener::OnStopRequest(nsIRequest *request, nsISupports * aCtxt, nsresult aStatus)
+{
+ if (m_outputStream)
+ {
+ m_outputStream->Flush();
+ m_outputStream->Close();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSaveAsListener::OnDataAvailable(nsIRequest* request,
+ nsISupports* aSupport,
+ 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;
+ nsCOMPtr <nsIMsgMessageUrl> msgUrl = do_QueryInterface(aSupport);
+ 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_strchr(start, '\r');
+ if (!end)
+ end = PL_strchr(start, '\n');
+ else if (*(end+1) == '\n' && linebreak_len == 0)
+ linebreak_len = 2;
+
+ if (linebreak_len == 0) // not initialize yet
+ linebreak_len = 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_strchr(start, '\r');
+ if (!end)
+ end = PL_strchr(start, '\n');
+ 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::SetFolder(nsIMsgFolder * /* aFolder */)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetFolder(nsIMsgFolder ** /* aFolder */)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetMsgHeaderSink(nsIMsgHeaderSink * *aMsgHdrSink)
+{
+ NS_ENSURE_ARG_POINTER(aMsgHdrSink);
+ NS_IF_ADDREF(*aMsgHdrSink = mMsgHeaderSink);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::SetMsgHeaderSink(nsIMsgHeaderSink * aMsgHdrSink)
+{
+ mMsgHeaderSink = aMsgHdrSink;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMailNewsUrl::GetIsMessageUri(bool *aIsMessageUri)
+{
+ NS_ENSURE_ARG(aIsMessageUri);
+ nsAutoCString scheme;
+ m_baseURL->GetScheme(scheme);
+ *aIsMessageUri = StringEndsWith(scheme, NS_LITERAL_CSTRING("-message"));
+ return NS_OK;
+}