diff options
Diffstat (limited to 'mailnews/news/src/nsNntpUrl.cpp')
-rw-r--r-- | mailnews/news/src/nsNntpUrl.cpp | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/mailnews/news/src/nsNntpUrl.cpp b/mailnews/news/src/nsNntpUrl.cpp new file mode 100644 index 000000000..5cdc3ba8d --- /dev/null +++ b/mailnews/news/src/nsNntpUrl.cpp @@ -0,0 +1,578 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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" // precompiled header... + +#include "nsIURL.h" +#include "nsNntpUrl.h" + +#include "nsStringGlue.h" +#include "nsNewsUtils.h" +#include "nsMsgUtils.h" + +#include "nntpCore.h" + +#include "nsCOMPtr.h" +#include "nsIMsgDatabase.h" +#include "nsMsgDBCID.h" +#include "nsMsgNewsCID.h" +#include "nsIMsgFolder.h" +#include "nsIMsgNewsFolder.h" +#include "nsINntpService.h" +#include "nsIMsgMessageService.h" +#include "nsIMsgAccountManager.h" +#include "nsServiceManagerUtils.h" + + +nsNntpUrl::nsNntpUrl() +{ + m_newsgroupPost = nullptr; + m_newsAction = nsINntpUrl::ActionUnknown; + m_addDummyEnvelope = false; + m_canonicalLineEnding = false; + m_filePath = nullptr; + m_getOldMessages = false; + m_key = nsMsgKey_None; +} + +nsNntpUrl::~nsNntpUrl() +{ +} + +NS_IMPL_ADDREF_INHERITED(nsNntpUrl, nsMsgMailNewsUrl) +NS_IMPL_RELEASE_INHERITED(nsNntpUrl, nsMsgMailNewsUrl) + +NS_INTERFACE_MAP_BEGIN(nsNntpUrl) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINntpUrl) + NS_INTERFACE_MAP_ENTRY(nsINntpUrl) + NS_INTERFACE_MAP_ENTRY(nsIMsgMessageUrl) + NS_INTERFACE_MAP_ENTRY(nsIMsgI18NUrl) +NS_INTERFACE_MAP_END_INHERITING(nsMsgMailNewsUrl) + +//////////////////////////////////////////////////////////////////////////////// +// Begin nsINntpUrl specific support +//////////////////////////////////////////////////////////////////////////////// + +/* News URI parsing explanation: + * We support 3 different news URI schemes, essentially boiling down to 8 + * different formats: + * news://host/group + * news://host/message + * news://host/ + * news:group + * news:message + * nntp://host/group + * nntp://host/group/key + * news-message://host/group#key + * + * In addition, we use queries on the news URIs with authorities for internal + * NNTP processing. The most important one is ?group=group&key=key, for cache + * canonicalization. + */ + +NS_IMETHODIMP nsNntpUrl::SetSpec(const nsACString &aSpec) +{ + // For [s]news: URIs, we need to munge the spec if it is no authority, because + // the URI parser guesses the wrong thing otherwise + nsCString parseSpec(aSpec); + int32_t colon = parseSpec.Find(":"); + + // Our smallest scheme is 4 characters long, so colon must be at least 4 + if (colon < 4 || colon + 1 == (int32_t) parseSpec.Length()) + return NS_ERROR_MALFORMED_URI; + + if (Substring(parseSpec, colon - 4, 4).EqualsLiteral("news") && + parseSpec[colon + 1] != '/') + { + // To make this parse properly, we add in three slashes, which convinces the + // parser that the authority component is empty. + parseSpec = Substring(aSpec, 0, colon + 1); + parseSpec.AppendLiteral("///"); + parseSpec += Substring(aSpec, colon + 1); + } + + nsresult rv = nsMsgMailNewsUrl::SetSpec(parseSpec); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoCString scheme; + rv = GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + if (scheme.EqualsLiteral("news") || scheme.EqualsLiteral("snews")) + rv = ParseNewsURL(); + else if (scheme.EqualsLiteral("nntp") || scheme.EqualsLiteral("nntps")) + rv = ParseNntpURL(); + else if (scheme.EqualsLiteral("news-message")) + { + nsAutoCString spec; + rv = GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + rv = nsParseNewsMessageURI(spec.get(), m_group, &m_key); + NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI); + } + else + return NS_ERROR_MALFORMED_URI; + NS_ENSURE_SUCCESS(rv, rv); + + rv = DetermineNewsAction(); + NS_ENSURE_SUCCESS(rv,rv); + return rv; +} + +nsresult nsNntpUrl::ParseNewsURL() +{ + // The path here is the group/msgid portion + nsAutoCString path; + nsresult rv = GetFilePath(path); + NS_ENSURE_SUCCESS(rv, rv); + + // Drop the potential beginning from the path + if (path.Length() && path[0] == '/') + path = Substring(path, 1); + + // The presence of an `@' is a sign we have a msgid + if (path.Find("@") != -1 || path.Find("%40") != -1) + { + MsgUnescapeString(path, 0, m_messageID); + + // Set group, key for ?group=foo&key=123 uris + nsAutoCString spec; + rv = GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + int32_t groupPos = spec.Find(kNewsURIGroupQuery); // find ?group= + int32_t keyPos = spec.Find(kNewsURIKeyQuery); // find &key= + if (groupPos != kNotFound && keyPos != kNotFound) + { + // get group name and message key + m_group = Substring(spec, groupPos + kNewsURIGroupQueryLen, + keyPos - groupPos - kNewsURIGroupQueryLen); + nsCString keyStr(Substring(spec, keyPos + kNewsURIKeyQueryLen)); + m_key = keyStr.ToInteger(&rv, 10); + NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI); + } + } + else + MsgUnescapeString(path, 0, m_group); + + return NS_OK; +} + +nsresult nsNntpUrl::ParseNntpURL() +{ + nsAutoCString path; + nsresult rv = GetFilePath(path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.Length() > 0 && path[0] == '/') + path = Substring(path, 1); + + if (path.IsEmpty()) + return NS_ERROR_MALFORMED_URI; + + int32_t slash = path.FindChar('/'); + if (slash == -1) + { + m_group = path; + m_key = nsMsgKey_None; + } + else + { + m_group = Substring(path, 0, slash); + nsAutoCString keyStr; + keyStr = Substring(path, slash + 1); + m_key = keyStr.ToInteger(&rv, 10); + NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI); + + // Keys must be at least one + if (m_key == 0) + return NS_ERROR_MALFORMED_URI; + } + + return NS_OK; +} + +nsresult nsNntpUrl::DetermineNewsAction() +{ + nsAutoCString path; + nsresult rv = nsMsgMailNewsUrl::GetPath(path); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoCString query; + rv = GetQuery(query); + NS_ENSURE_SUCCESS(rv, rv); + + if (query.EqualsLiteral("cancel")) + { + m_newsAction = nsINntpUrl::ActionCancelArticle; + return NS_OK; + } + if (query.EqualsLiteral("list-ids")) + { + m_newsAction = nsINntpUrl::ActionListIds; + return NS_OK; + } + if (query.EqualsLiteral("newgroups")) + { + m_newsAction = nsINntpUrl::ActionListNewGroups; + return NS_OK; + } + if (StringBeginsWith(query, NS_LITERAL_CSTRING("search"))) + { + m_newsAction = nsINntpUrl::ActionSearch; + return NS_OK; + } + if (StringBeginsWith(query, NS_LITERAL_CSTRING("part=")) || + query.Find("&part=") > 0) + { + // news://news.mozilla.org:119/3B98D201.3020100%40cs.com?part=1 + // news://news.mozilla.org:119/b58dme%24aia2%40ripley.netscape.com?header=print&part=1.2&type=image/jpeg&filename=Pole.jpg + m_newsAction = nsINntpUrl::ActionFetchPart; + return NS_OK; + } + + if (!m_messageID.IsEmpty() || m_key != nsMsgKey_None) + { + m_newsAction = nsINntpUrl::ActionFetchArticle; + return NS_OK; + } + + if (m_group.Find("*") >= 0) + { + // If the group is a wildmat, list groups instead of grabbing a group. + m_newsAction = nsINntpUrl::ActionListGroups; + return NS_OK; + } + if (!m_group.IsEmpty()) + { + m_newsAction = nsINntpUrl::ActionGetNewNews; + return NS_OK; + } + + // At this point, we have a URI that contains neither a query, a group, nor a + // message ID. Ergo, we don't know what it is. + m_newsAction = nsINntpUrl::ActionUnknown; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::SetGetOldMessages(bool aGetOldMessages) +{ + m_getOldMessages = aGetOldMessages; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetGetOldMessages(bool * aGetOldMessages) +{ + NS_ENSURE_ARG(aGetOldMessages); + *aGetOldMessages = m_getOldMessages; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetNewsAction(nsNewsAction *aNewsAction) +{ + if (aNewsAction) + *aNewsAction = m_newsAction; + return NS_OK; +} + + +NS_IMETHODIMP nsNntpUrl::SetNewsAction(nsNewsAction aNewsAction) +{ + m_newsAction = aNewsAction; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetGroup(nsACString &group) +{ + group = m_group; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetMessageID(nsACString &messageID) +{ + messageID = m_messageID; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetKey(nsMsgKey *key) +{ + NS_ENSURE_ARG_POINTER(key); + *key = m_key; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetPrincipalSpec(nsACString& aPrincipalSpec) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsNntpUrl::SetUri(const char * aURI) +{ + mURI = aURI; + return NS_OK; +} + +// from nsIMsgMessageUrl +NS_IMETHODIMP nsNntpUrl::GetUri(char ** aURI) +{ + nsresult rv = NS_OK; + + // if we have been given a uri to associate with this url, then use it + // otherwise try to reconstruct a URI on the fly.... + if (mURI.IsEmpty()) { + nsAutoCString spec; + rv = GetSpec(spec); + NS_ENSURE_SUCCESS(rv,rv); + mURI = spec; + } + + *aURI = ToNewCString(mURI); + if (!*aURI) return NS_ERROR_OUT_OF_MEMORY; + return rv; +} + + +NS_IMPL_GETSET(nsNntpUrl, AddDummyEnvelope, bool, m_addDummyEnvelope) +NS_IMPL_GETSET(nsNntpUrl, CanonicalLineEnding, bool, m_canonicalLineEnding) + +NS_IMETHODIMP nsNntpUrl::SetMessageFile(nsIFile * aFile) +{ + m_messageFile = aFile; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetMessageFile(nsIFile ** aFile) +{ + if (aFile) + NS_IF_ADDREF(*aFile = m_messageFile); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// End nsINntpUrl specific support +//////////////////////////////////////////////////////////////////////////////// + +nsresult nsNntpUrl::SetMessageToPost(nsINNTPNewsgroupPost *post) +{ + m_newsgroupPost = post; + if (post) + SetNewsAction(nsINntpUrl::ActionPostArticle); + return NS_OK; +} + +nsresult nsNntpUrl::GetMessageToPost(nsINNTPNewsgroupPost **aPost) +{ + NS_ENSURE_ARG_POINTER(aPost); + NS_IF_ADDREF(*aPost = m_newsgroupPost); + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::SetMessageHeader(nsIMsgDBHdr *aMsgHdr) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsNntpUrl::GetMessageHeader(nsIMsgDBHdr ** aMsgHdr) +{ + nsresult rv; + + nsCOMPtr <nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr <nsIMsgMessageService> msgService = do_QueryInterface(nntpService, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoCString spec(mOriginalSpec); + if (spec.IsEmpty()) { + // Handle the case where necko directly runs an internal news:// URL, + // one that looks like news://host/message-id?group=mozilla.announce&key=15 + // Other sorts of URLs -- e.g. news://host/message-id -- will not succeed. + rv = GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + } + + return msgService->MessageURIToMsgHdr(spec.get(), aMsgHdr); +} + +NS_IMETHODIMP nsNntpUrl::IsUrlType(uint32_t type, bool *isType) +{ + NS_ENSURE_ARG(isType); + + switch(type) + { + case nsIMsgMailNewsUrl::eDisplay: + *isType = (m_newsAction == nsINntpUrl::ActionFetchArticle); + break; + default: + *isType = false; + }; + + return NS_OK; + +} + +NS_IMETHODIMP +nsNntpUrl::GetOriginalSpec(char **aSpec) +{ + NS_ENSURE_ARG_POINTER(aSpec); + *aSpec = ToNewCString(mOriginalSpec); + if (!*aSpec) return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; +} + +NS_IMETHODIMP +nsNntpUrl::SetOriginalSpec(const char *aSpec) +{ + mOriginalSpec = aSpec; + return NS_OK; +} + +NS_IMETHODIMP +nsNntpUrl::GetServer(nsIMsgIncomingServer **aServer) +{ + NS_ENSURE_ARG_POINTER(aServer); + + nsresult rv; + nsAutoCString scheme, user, host; + + GetScheme(scheme); + GetUsername(user); + GetHost(host); + + // No authority -> no server + if (host.IsEmpty()) + { + *aServer = nullptr; + return NS_OK; + } + + // Looking up the server... + // news-message is used purely internally, so it can never refer to the real + // attribute. nntp is never used internally, so it probably refers to the real + // one. news is used both internally and externally, so it could refer to + // either one. We'll assume it's an internal one first, though. + bool isNews = scheme.EqualsLiteral("news") || scheme.EqualsLiteral("snews"); + bool isNntp = scheme.EqualsLiteral("nntp") || scheme.EqualsLiteral("nntps"); + + bool tryReal = isNntp; + + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Ignoring return results: it is perfectly acceptable for the server to not + // exist, but FindServer (and not FindRealServer) throws NS_ERROR_UNEXPECTED + // in this case. + *aServer = nullptr; + if (tryReal) + accountManager->FindRealServer(user, host, NS_LITERAL_CSTRING("nntp"), 0, + aServer); + else + accountManager->FindServer(user, host, NS_LITERAL_CSTRING("nntp"), aServer); + if (!*aServer && (isNews || isNntp)) + { + // Didn't find it, try the other option + if (tryReal) + accountManager->FindServer(user, host, NS_LITERAL_CSTRING("nntp"), + aServer); + else + accountManager->FindRealServer(user, host, NS_LITERAL_CSTRING("nntp"), 0, + aServer); + } + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::GetFolder(nsIMsgFolder **msgFolder) +{ + NS_ENSURE_ARG_POINTER(msgFolder); + + nsresult rv; + + nsCOMPtr<nsIMsgIncomingServer> server; + rv = GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + + // Need a server and a group to get the folder + if (!server || m_group.IsEmpty()) + { + *msgFolder = nullptr; + return NS_OK; + } + + // Find the group on the server + nsCOMPtr<nsINntpIncomingServer> nntpServer = do_QueryInterface(server, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasGroup = false; + rv = nntpServer->ContainsNewsgroup(m_group, &hasGroup); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasGroup) + { + *msgFolder = nullptr; + return NS_OK; + } + + nsCOMPtr<nsIMsgNewsFolder> newsFolder; + rv = nntpServer->FindGroup(m_group, getter_AddRefs(newsFolder)); + NS_ENSURE_SUCCESS(rv, rv); + + return newsFolder->QueryInterface(NS_GET_IID(nsIMsgFolder), (void**)msgFolder); +} + +NS_IMETHODIMP +nsNntpUrl::GetFolderCharset(char **aCharacterSet) +{ + nsCOMPtr<nsIMsgFolder> folder; + nsresult rv = GetFolder(getter_AddRefs(folder)); + // don't assert here. this can happen if there is no message folder + // like when we display a news://host/message-id url + if (NS_FAILED(rv) || !folder) + return rv; + nsCString tmpStr; + rv = folder->GetCharset(tmpStr); + *aCharacterSet = ToNewCString(tmpStr); + return rv; +} + +NS_IMETHODIMP nsNntpUrl::GetFolderCharsetOverride(bool * aCharacterSetOverride) +{ + nsCOMPtr<nsIMsgFolder> folder; + nsresult rv = GetFolder(getter_AddRefs(folder)); + NS_ENSURE_SUCCESS(rv,rv); + NS_ENSURE_TRUE(folder, NS_ERROR_FAILURE); + rv = folder->GetCharsetOverride(aCharacterSetOverride); + NS_ENSURE_SUCCESS(rv,rv); + return rv; +} + +NS_IMETHODIMP nsNntpUrl::GetCharsetOverRide(char ** aCharacterSet) +{ + if (!mCharsetOverride.IsEmpty()) + *aCharacterSet = ToNewCString(mCharsetOverride); + else + *aCharacterSet = nullptr; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::SetCharsetOverRide(const char * aCharacterSet) +{ + mCharsetOverride = aCharacterSet; + return NS_OK; +} + +NS_IMETHODIMP nsNntpUrl::CloneInternal(uint32_t aRefHandlingMode, + const nsACString& newRef, + nsIURI **_retval) +{ + nsresult rv; + rv = nsMsgMailNewsUrl::CloneInternal(aRefHandlingMode, newRef, _retval); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgMessageUrl> newsurl = do_QueryInterface(*_retval, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return newsurl->SetUri(mURI.get()); +} + |