Bug 1347598 - add nsIURIWithPrincipal as base class to nsIMsgMailNewsUrl. r=rkent a=jorgk
authorJorg K <jorgk@jorgk.com>
Mon, 24 Apr 2017 19:20:59 +0200
changeset 27818 8927d6f79d0a1ffe704673f2a8700b411f71b1c8
parent 27817 75634f13d8d726eeab24822f9cf48b50122fb698
child 27819 56e9ca0a5e80fdb0beb425126e55b9023bf5b28a
push id1925
push usermozilla@jorgk.com
push dateWed, 26 Apr 2017 17:30:24 +0000
treeherdercomm-beta@56e9ca0a5e80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrkent, jorgk
bugs1347598
Bug 1347598 - add nsIURIWithPrincipal as base class to nsIMsgMailNewsUrl. r=rkent a=jorgk
mailnews/base/public/nsIMsgMailNewsUrl.idl
mailnews/base/util/nsMsgMailNewsUrl.cpp
mailnews/base/util/nsMsgMailNewsUrl.h
mailnews/base/util/nsMsgUtils.cpp
mailnews/base/util/nsMsgUtils.h
mailnews/imap/src/nsImapProtocol.cpp
mailnews/imap/src/nsImapUrl.cpp
mailnews/jsaccount/src/JaUrl.cpp
mailnews/local/src/nsMailboxUrl.cpp
mailnews/news/src/nsNntpUrl.cpp
--- a/mailnews/base/public/nsIMsgMailNewsUrl.idl
+++ b/mailnews/base/public/nsIMsgMailNewsUrl.idl
@@ -156,16 +156,19 @@ interface nsIMsgMessageUrl : nsISupports
   // get and set the RDF URI associated with the url. Note, not all urls have
   // had uri's set on them so be prepared to handle cases where this string is empty.
   attribute string uri;
   // used by imap, pop and nntp in order to implement save message to disk
   attribute nsIFile messageFile;
   attribute boolean AddDummyEnvelope;  
   attribute boolean canonicalLineEnding;
   attribute string originalSpec;
+
+  // This is used when creating a principal for the URL.
+  readonly attribute AUTF8String principalSpec;
   
   /**
    *  A message db header for that message.
    *  
    *  @note This attribute is not guaranteed to be set, so callers that
    *  actually require an nsIMsgDBHdr will need to use the uri attribute
    *  on this interface to get the  appropriate nsIMsgMessageService and
    *  then get the header from there.
--- a/mailnews/base/util/nsMsgMailNewsUrl.cpp
+++ b/mailnews/base/util/nsMsgMailNewsUrl.cpp
@@ -22,25 +22,27 @@
 #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()) {                                             \
@@ -65,17 +67,69 @@ nsMsgMailNewsUrl::~nsMsgMailNewsUrl()
   nsTObserverArray<nsCOMPtr<nsIUrlListener>>::ForwardIterator iter(mUrlListeners);
   while (iter.HasMore()) {
     nsCOMPtr<nsIUrlListener> listener = iter.GetNext();
     if (listener)
       NS_ReleaseOnMainThread(listener.forget());
   }
 }
 
-NS_IMPL_ISUPPORTS(nsMsgMailNewsUrl, nsIMsgMailNewsUrl, nsIURL, nsIURI)
+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(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::OriginAttributes 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)
@@ -360,18 +414,33 @@ NS_IMETHODIMP nsMsgMailNewsUrl::SetSpec(
     {
       *end = 0;
       mAttachmentFileName = start+FILENAME_PART_LEN;
       *end = '&';
     }
     else
       mAttachmentFileName = start+FILENAME_PART_LEN;
   }
+
   // Now, set the rest.
-  return m_baseURL->SetSpec(aSpec);
+  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)
--- a/mailnews/base/util/nsMsgMailNewsUrl.h
+++ b/mailnews/base/util/nsMsgMailNewsUrl.h
@@ -12,16 +12,17 @@
 #include "nsTObserverArray.h"
 #include "nsIMsgWindow.h"
 #include "nsIMsgStatusFeedback.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIMimeHeaders.h"
 #include "nsIMsgMailNewsUrl.h"
 #include "nsIURL.h"
+#include "nsIURIWithPrincipal.h"
 #include "nsILoadGroup.h"
 #include "nsIMsgSearchSession.h"
 #include "nsICacheEntry.h"
 #include "nsICacheSession.h"
 #include "nsIMimeMiscStatus.h"
 #include "nsWeakReference.h"
 #include "nsString.h"
 
@@ -31,43 +32,47 @@
 // So I decided to group them all in this implementation so we don't have to
 // duplicate the code.
 //
 //////////////////////////////////////////////////////////////////////////////////
 
 #undef  IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_VISIBILITY_DEFAULT
 
-class NS_MSG_BASE nsMsgMailNewsUrl : public nsIMsgMailNewsUrl
+class NS_MSG_BASE nsMsgMailNewsUrl : public nsIMsgMailNewsUrl,
+                                     public nsIURIWithPrincipal
 {
 public:
     nsMsgMailNewsUrl();
 
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIMSGMAILNEWSURL
     NS_DECL_NSIURI
     NS_DECL_NSIURL
+    NS_DECL_NSIURIWITHPRINCIPAL
 
 protected:
   virtual ~nsMsgMailNewsUrl();
 
   nsCOMPtr<nsIURL> m_baseURL;
+  nsCOMPtr<nsIPrincipal> m_principal;
   nsWeakPtr m_statusFeedbackWeak;
   nsWeakPtr m_msgWindowWeak;
   nsWeakPtr m_loadGroupWeak;
   nsCOMPtr<nsIMimeHeaders> mMimeHeaders;
   nsCOMPtr<nsIMsgSearchSession> m_searchSession;
   nsCOMPtr<nsICacheEntry> m_memCacheEntry;
   nsCOMPtr<nsIMsgHeaderSink> mMsgHeaderSink;
   char *m_errorMessage;
   int64_t mMaxProgress;
   bool m_runningUrl;
   bool m_updatingFolder;
   bool m_msgIsInLocalCache;
   bool m_suppressErrorMsgs;
+  bool m_isPrincipalURL;
 
   // the following field is really a bit of a hack to make
   // open attachments work. The external applications code sometimes tries to figure out the right
   // handler to use by looking at the file extension of the url we are trying to load. Unfortunately,
   // the attachment file name really isn't part of the url string....so we'll store it here...and if
   // the url we are running is an attachment url, we'll set it here. Then when the helper apps code
   // asks us for it, we'll return the right value.
   nsCString mAttachmentFileName;
--- a/mailnews/base/util/nsMsgUtils.cpp
+++ b/mailnews/base/util/nsMsgUtils.cpp
@@ -2100,8 +2100,29 @@ NS_MSG_BASE nsMsgKey msgKeyFromInt(uint3
   return aValue;
 }
 
 NS_MSG_BASE nsMsgKey msgKeyFromInt(uint64_t aValue)
 {
   NS_ASSERTION(aValue <= PR_UINT32_MAX, "Msg key value too big!");
   return aValue;
 }
+
+// Helper function to extract a query qualifier.
+nsAutoCString MsgExtractQueryPart(nsAutoCString spec, const char* queryToExtract)
+{
+  nsAutoCString queryPart;
+  int32_t queryIndex = spec.Find(queryToExtract);
+  if (queryIndex == kNotFound)
+    return queryPart;
+
+  int32_t queryEnd = Substring(spec, queryIndex + 1).FindChar('&');
+  if (queryEnd == kNotFound)
+    queryEnd = Substring(spec, queryIndex + 1).FindChar('?');
+  if (queryEnd == kNotFound) {
+    // Nothing follows, so return from where the query qualifier started.
+    queryPart.Assign(Substring(spec, queryIndex));
+  } else {
+    // Return the substring that represents the query qualifier.
+    queryPart.Assign(Substring(spec, queryIndex, queryEnd + 1));
+  }
+  return queryPart;
+}
--- a/mailnews/base/util/nsMsgUtils.h
+++ b/mailnews/base/util/nsMsgUtils.h
@@ -379,16 +379,21 @@ NS_MSG_BASE bool MsgIsHex(const char *aH
  * (except when storing it into the database). It enables type safety checks and
  * may prevent coding errors.
  */
 NS_MSG_BASE nsMsgKey msgKeyFromInt(uint32_t aValue);
 
 NS_MSG_BASE nsMsgKey msgKeyFromInt(uint64_t aValue);
 
 /**
+ * Helper function to extract query part from URL spec.
+ */
+nsAutoCString MsgExtractQueryPart(nsAutoCString spec, const char* queryToExtract);
+
+/**
  * Helper macro for defining getter/setters. Ported from nsISupportsObsolete.h
  */
 #define NS_IMPL_GETSET(clazz, attr, type, member) \
   NS_IMETHODIMP clazz::Get##attr(type *result) \
   { \
     NS_ENSURE_ARG_POINTER(result); \
     *result = member; \
     return NS_OK; \
--- a/mailnews/imap/src/nsImapProtocol.cpp
+++ b/mailnews/imap/src/nsImapProtocol.cpp
@@ -9223,37 +9223,16 @@ nsImapMockChannel::OnCacheEntryCheck(nsI
   int64_t size = 0;
   nsresult rv = entry->GetDataSize(&size);
   if (rv == NS_ERROR_IN_PROGRESS)
     *aResult = nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED;
 
   return NS_OK;
 }
 
-// Little helper function to extract a query qualifier.
-static nsAutoCString extractQueryPart(nsAutoCString path, const char* queryToExtract)
-{
-  nsAutoCString queryPart;
-  int32_t queryIndex = path.Find(queryToExtract);
-  if (queryIndex == kNotFound)
-    return queryPart;
-
-  int32_t queryEnd = Substring(path, queryIndex + 1).FindChar('&');
-  if (queryEnd == kNotFound)
-    queryEnd = Substring(path, queryIndex + 1).FindChar('?');
-  if (queryEnd == kNotFound) {
-    // Nothing follows, so return from where the query qualifier started.
-    queryPart.Assign(Substring(path, queryIndex));
-  } else {
-    // Return the substring that represents the query qualifier.
-    queryPart.Assign(Substring(path, queryIndex, queryEnd + 1));
-  }
-  return queryPart;
-}
-
 nsresult nsImapMockChannel::OpenCacheEntry()
 {
   nsresult rv;
   // get the cache session from our imap service...
   nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsICacheStorage> cacheStorage;
@@ -9291,25 +9270,25 @@ nsresult nsImapMockChannel::OpenCacheEnt
 
   // First we need to "normalise" the URL by extracting ?part= and &filename.
   // The path should only contain: ?part=x.y&filename=file.ext
   // These are seen in the wild:
   // /;section=2?part=1.2&filename=A01.JPG
   // ?section=2?part=1.2&filename=A01.JPG&type=image/jpeg&filename=A01.JPG
   // ?part=1.2&type=image/jpeg&filename=IMG_C0030.jpg
   // ?header=quotebody&part=1.2&filename=lijbmghmkilicioj.png
-  nsAutoCString partQuery = extractQueryPart(path, "?part=");
+  nsAutoCString partQuery = MsgExtractQueryPart(path, "?part=");
   if (partQuery.IsEmpty()) {
-    partQuery = extractQueryPart(path, "&part=");
+    partQuery = MsgExtractQueryPart(path, "&part=");
     if (!partQuery.IsEmpty()) {
       // ? indicates a part query, so set the first character to that.
       partQuery.SetCharAt('?', 0);
     }
   }
-  nsAutoCString filenameQuery = extractQueryPart(path, "&filename=");
+  nsAutoCString filenameQuery = MsgExtractQueryPart(path, "&filename=");
 
   // Truncate path at either /; or ?
   int32_t ind = path.FindChar('?');
   if (ind != kNotFound)
     path.SetLength(ind);
   ind = path.Find("/;");
   if (ind != kNotFound)
     path.SetLength(ind);
--- a/mailnews/imap/src/nsImapUrl.cpp
+++ b/mailnews/imap/src/nsImapUrl.cpp
@@ -1180,16 +1180,45 @@ NS_IMETHODIMP nsImapUrl::CloneInternal(u
   // also clone the mURI member, because GetUri below won't work if
   // mURI isn't set due to escaping issues.
   nsCOMPtr <nsIMsgMessageUrl> clonedUrl = do_QueryInterface(*_retval);
   if (clonedUrl)
     clonedUrl->SetUri(mURI.get());
   return rv;
 }
 
+NS_IMETHODIMP nsImapUrl::GetPrincipalSpec(nsACString& aPrincipalSpec)
+{
+  // URLs look like this:
+  // imap://user@domain@server:port/fetch>UID>folder>nn
+  // We simply strip any query part beginning with ? & or /;
+  // Normalised spec: imap://user@domain@server:port/fetch>UID>folder>nn
+  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+  QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+  nsAutoCString spec;
+  mailnewsURL->GetSpec(spec);
+
+  // Strip any query part beginning with ? & or /;
+  int32_t ind = spec.Find("/;");
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('?');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('&');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  aPrincipalSpec.Assign(spec);
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsImapUrl::SetUri(const char * aURI)
 {
   mURI= aURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapUrl::GetUri(char** aURI)
 {
--- a/mailnews/jsaccount/src/JaUrl.cpp
+++ b/mailnews/jsaccount/src/JaUrl.cpp
@@ -6,16 +6,17 @@
 
 #include "JaUrl.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIMessenger.h"
 #include "nsIMsgHdr.h"
 #include "nsISupportsUtils.h"
 #include "nsMsgBaseCID.h"
+#include "nsMsgUtils.h"
 
 // This file contains an implementation of mailnews URLs in JsAccount.
 
 namespace mozilla {
 namespace mailnews {
 
 NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppUrl, nsMsgMailNewsUrl,
                             nsIMsgMessageUrl,
@@ -92,16 +93,48 @@ NS_IMETHODIMP JaBaseCppUrl::GetOriginalS
   return NS_OK;
 }
 NS_IMETHODIMP JaBaseCppUrl::SetOriginalSpec(const char *aOriginalSpec)
 {
   mOriginalSpec = aOriginalSpec;
   return NS_OK;
 }
 
+NS_IMETHODIMP JaBaseCppUrl::GetPrincipalSpec(nsACString& aPrincipalSpec)
+{
+  // URLs contain a lot of query parts. We want need a normalised form:
+  // scheme://server/folder?number=123
+  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+  QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+  nsAutoCString spec;
+  mailnewsURL->GetSpec(spec);
+
+  nsAutoCString queryPart = MsgExtractQueryPart(spec, "number=");
+
+  // Strip any query part beginning with ? & or /;
+  int32_t ind = spec.Find("/;");
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('?');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('&');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  if (!queryPart.IsEmpty())
+    spec += NS_LITERAL_CSTRING("?") + queryPart;
+
+  aPrincipalSpec.Assign(spec);
+  return NS_OK;
+}
+
 NS_IMETHODIMP JaBaseCppUrl::GetMessageHeader(nsIMsgDBHdr **aMessageHeader)
 {
   // This routine does a lookup using messenger, assumming that the message URI
   // has been set in mUri.
   NS_ENSURE_TRUE(!mUri.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
   nsresult rv;
   nsCOMPtr<nsIMessenger> messenger(do_CreateInstance(NS_MESSENGER_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/mailnews/local/src/nsMailboxUrl.cpp
+++ b/mailnews/local/src/nsMailboxUrl.cpp
@@ -119,16 +119,50 @@ NS_IMETHODIMP nsMailboxUrl::GetMessageSi
 }
 
 nsresult nsMailboxUrl::SetMessageSize(uint32_t aMessageSize)
 {
   m_messageSize = aMessageSize;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsMailboxUrl::GetPrincipalSpec(nsACString& aPrincipalSpec)
+{
+  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+  QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+  nsAutoCString spec;
+  mailnewsURL->GetSpec(spec);
+
+  // mailbox: URLs contain a lot of query parts. We want need a normalised form:
+  // mailbox://folder?number=nn.
+
+  char* messageKey = extractAttributeValue(spec.get(), "number=");
+
+  // Strip any query part beginning with ? & or /;
+  int32_t ind = spec.Find("/;");
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('?');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  ind = spec.FindChar('&');
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+
+  spec += NS_LITERAL_CSTRING("?number=");
+  spec.Append(messageKey);
+  PR_Free(messageKey);
+
+  aPrincipalSpec.Assign(spec);
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsMailboxUrl::SetUri(const char * aURI)
 {
   mURI= aURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMailboxUrl::CloneInternal(uint32_t aRefHandlingMode,
                                           const nsACString& newRef,
--- a/mailnews/news/src/nsNntpUrl.cpp
+++ b/mailnews/news/src/nsNntpUrl.cpp
@@ -297,16 +297,34 @@ NS_IMETHODIMP nsNntpUrl::GetMessageID(ns
 
 NS_IMETHODIMP nsNntpUrl::GetKey(nsMsgKey *key)
 {
   NS_ENSURE_ARG_POINTER(key);
   *key = m_key;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsNntpUrl::GetPrincipalSpec(nsACString& aPrincipalSpec)
+{
+  // URLs look like this:
+  // news://server:port/folder?group=ggg&key=nnn [ &part=ppp &filename=fff ].
+  // Just strip the part which will also remove the filename.
+  // Normalised spec: news://server:port/folder?group=ggg&key=nnn
+  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+  QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+  nsAutoCString spec;
+  mailnewsURL->GetSpec(spec);
+  int32_t ind = spec.Find("&part");
+  if (ind != kNotFound)
+    spec.SetLength(ind);
+  aPrincipalSpec.Assign(spec);
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsNntpUrl::SetUri(const char * aURI)
 {
   mURI = aURI;
   return NS_OK;
 }
 
 // from nsIMsgMessageUrl
 NS_IMETHODIMP nsNntpUrl::GetUri(char ** aURI)