summaryrefslogtreecommitdiffstats
path: root/mailnews/news/src/nsNntpIncomingServer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/news/src/nsNntpIncomingServer.cpp')
-rw-r--r--mailnews/news/src/nsNntpIncomingServer.cpp2162
1 files changed, 2162 insertions, 0 deletions
diff --git a/mailnews/news/src/nsNntpIncomingServer.cpp b/mailnews/news/src/nsNntpIncomingServer.cpp
new file mode 100644
index 000000000..7c3fcad4b
--- /dev/null
+++ b/mailnews/news/src/nsNntpIncomingServer.cpp
@@ -0,0 +1,2162 @@
+/* -*- 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 "nsNntpIncomingServer.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsNewsFolder.h"
+#include "nsIMsgFolder.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+#include "nsINntpService.h"
+#include "nsINNTPProtocol.h"
+#include "nsMsgNewsCID.h"
+#include "nsNNTPProtocol.h"
+#include "nsIDirectoryService.h"
+#include "nsMailDirServiceDefs.h"
+#include "nsMsgUtils.h"
+#include "nsIPrompt.h"
+#include "nsIStringBundle.h"
+#include "nntpCore.h"
+#include "nsIWindowWatcher.h"
+#include "nsITreeColumns.h"
+#include "nsIDOMElement.h"
+#include "nsMsgFolderFlags.h"
+#include "nsMsgI18N.h"
+#include "nsUnicharUtils.h"
+#include "nsILineInputStream.h"
+#include "nsNetUtil.h"
+#include "nsISimpleEnumerator.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+#include "nsITreeBoxObject.h"
+
+#define INVALID_VERSION 0
+#define VALID_VERSION 2
+#define NEW_NEWS_DIR_NAME "News"
+#define PREF_MAIL_NEWSRC_ROOT "mail.newsrc_root"
+#define PREF_MAIL_NEWSRC_ROOT_REL "mail.newsrc_root-rel"
+#define PREF_MAILNEWS_VIEW_DEFAULT_CHARSET "mailnews.view_default_charset"
+#define HOSTINFO_FILE_NAME "hostinfo.dat"
+
+#define NEWS_DELIMITER '.'
+
+// this platform specific junk is so the newsrc filenames we create
+// will resemble the migrated newsrc filenames.
+#if defined(XP_UNIX)
+#define NEWSRC_FILE_PREFIX "newsrc-"
+#define NEWSRC_FILE_SUFFIX ""
+#else
+#define NEWSRC_FILE_PREFIX ""
+#define NEWSRC_FILE_SUFFIX ".rc"
+#endif /* XP_UNIX */
+
+// ###tw This really ought to be the most
+// efficient file reading size for the current
+// operating system.
+#define HOSTINFO_FILE_BUFFER_SIZE 1024
+
+#include "nsMsgUtils.h"
+
+/**
+ * A comparator class to do cases insensitive comparisons for nsTArray.Sort()
+ */
+class nsCStringLowerCaseComparator
+{
+public:
+ bool Equals(const nsCString &a, const nsCString &b) const
+ {
+ return a.Equals(b, nsCaseInsensitiveCStringComparator());
+ }
+
+ bool LessThan(const nsCString &a, const nsCString &b) const
+ {
+ return Compare(a, b, nsCaseInsensitiveCStringComparator()) < 0;
+ }
+};
+
+static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID);
+
+NS_IMPL_ADDREF_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
+NS_IMPL_RELEASE_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
+
+NS_INTERFACE_MAP_BEGIN(nsNntpIncomingServer)
+ NS_INTERFACE_MAP_ENTRY(nsINntpIncomingServer)
+ NS_INTERFACE_MAP_ENTRY(nsIUrlListener)
+ NS_INTERFACE_MAP_ENTRY(nsISubscribableServer)
+ NS_INTERFACE_MAP_ENTRY(nsITreeView)
+NS_INTERFACE_MAP_END_INHERITING(nsMsgIncomingServer)
+
+nsNntpIncomingServer::nsNntpIncomingServer()
+{
+ mNewsrcHasChanged = false;
+
+ mGetOnlyNew = true;
+
+ mHostInfoLoaded = false;
+ mHostInfoHasChanged = false;
+ mVersion = INVALID_VERSION;
+
+ mLastGroupDate = 0;
+ mUniqueId = 0;
+ mHasSeenBeginGroups = false;
+ mPostingAllowed = false;
+ mLastUpdatedTime = 0;
+
+ // these atoms are used for subscribe search
+ mSubscribedAtom = MsgGetAtom("subscribed");
+ mNntpAtom = MsgGetAtom("nntp");
+
+ // we have server wide and per group filters
+ m_canHaveFilters = true;
+
+ SetupNewsrcSaveTimer();
+}
+
+nsNntpIncomingServer::~nsNntpIncomingServer()
+{
+ mozilla::DebugOnly<nsresult> rv;
+
+ if (mNewsrcSaveTimer) {
+ mNewsrcSaveTimer->Cancel();
+ mNewsrcSaveTimer = nullptr;
+ }
+ rv = ClearInner();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "ClearInner failed");
+
+ rv = CloseCachedConnections();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CloseCachedConnections failed");
+}
+
+NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, NotifyOn, "notify.on")
+NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, MarkOldRead, "mark_old_read")
+NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, Abbreviate, "abbreviate")
+NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, PushAuth, "always_authenticate")
+NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, SingleSignon, "singleSignon")
+NS_IMPL_SERVERPREF_INT(nsNntpIncomingServer, MaxArticles, "max_articles")
+
+nsresult
+nsNntpIncomingServer::CreateRootFolderFromUri(const nsCString &serverUri,
+ nsIMsgFolder **rootFolder)
+{
+ nsMsgNewsFolder *newRootFolder = new nsMsgNewsFolder;
+ if (!newRootFolder)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*rootFolder = newRootFolder);
+ newRootFolder->Init(serverUri.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetNewsrcFilePath(nsIFile **aNewsrcFilePath)
+{
+ nsresult rv;
+ if (mNewsrcFilePath)
+ {
+ *aNewsrcFilePath = mNewsrcFilePath;
+ NS_IF_ADDREF(*aNewsrcFilePath);
+ return NS_OK;
+ }
+
+ rv = GetFileValue("newsrc.file-rel", "newsrc.file", aNewsrcFilePath);
+ if (NS_SUCCEEDED(rv) && *aNewsrcFilePath)
+ {
+ mNewsrcFilePath = *aNewsrcFilePath;
+ return rv;
+ }
+
+ rv = GetNewsrcRootPath(getter_AddRefs(mNewsrcFilePath));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCString hostname;
+ rv = GetHostName(hostname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString newsrcFileName(NEWSRC_FILE_PREFIX);
+ newsrcFileName.Append(hostname);
+ newsrcFileName.Append(NEWSRC_FILE_SUFFIX);
+ rv = mNewsrcFilePath->AppendNative(newsrcFileName);
+ rv = mNewsrcFilePath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetNewsrcFilePath(mNewsrcFilePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aNewsrcFilePath = mNewsrcFilePath);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetNewsrcFilePath(nsIFile *aFile)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+
+ bool exists;
+ nsresult rv = aFile->Exists(&exists);
+ if (!exists)
+ {
+ rv = aFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0664);
+ if (NS_FAILED(rv)) return rv;
+ }
+ return SetFileValue("newsrc.file-rel", "newsrc.file", aFile);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetLocalStoreType(nsACString& type)
+{
+ type.AssignLiteral("news");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetLocalDatabaseType(nsACString& type)
+{
+ type.AssignLiteral("news");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetNewsrcRootPath(nsIFile *aNewsrcRootPath)
+{
+ NS_ENSURE_ARG(aNewsrcRootPath);
+ return NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT, aNewsrcRootPath);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetNewsrcRootPath(nsIFile **aNewsrcRootPath)
+{
+ NS_ENSURE_ARG_POINTER(aNewsrcRootPath);
+ *aNewsrcRootPath = nullptr;
+
+ bool havePref;
+ nsresult rv = NS_GetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL,
+ PREF_MAIL_NEWSRC_ROOT,
+ NS_APP_NEWS_50_DIR,
+ havePref,
+ aNewsrcRootPath);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = (*aNewsrcRootPath)->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = (*aNewsrcRootPath)->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!havePref || !exists)
+ {
+ rv = NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT, *aNewsrcRootPath);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
+ }
+ return rv;
+}
+
+/* static */ void nsNntpIncomingServer::OnNewsrcSaveTimer(nsITimer *timer, void *voidIncomingServer)
+{
+ nsNntpIncomingServer *incomingServer = (nsNntpIncomingServer*)voidIncomingServer;
+ incomingServer->WriteNewsrcFile();
+}
+
+nsresult nsNntpIncomingServer::SetupNewsrcSaveTimer()
+{
+ int64_t ms(300000); // hard code, 5 minutes.
+ //Convert biffDelay into milliseconds
+ uint32_t timeInMSUint32 = (uint32_t)ms;
+ //Can't currently reset a timer when it's in the process of
+ //calling Notify. So, just release the timer here and create a new one.
+ if(mNewsrcSaveTimer)
+ mNewsrcSaveTimer->Cancel();
+ mNewsrcSaveTimer = do_CreateInstance("@mozilla.org/timer;1");
+ mNewsrcSaveTimer->InitWithFuncCallback(OnNewsrcSaveTimer, (void*)this, timeInMSUint32,
+ nsITimer::TYPE_REPEATING_SLACK);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetCharset(const nsACString & aCharset)
+{
+ return SetCharValue("charset", aCharset);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCharset(nsACString & aCharset)
+{
+ //first we get the per-server settings mail.server.<serverkey>.charset
+ nsresult rv = GetCharValue("charset", aCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //if the per-server setting is empty,we get the default charset from
+ //mailnews.view_default_charset setting and set it as per-server preference.
+ if (aCharset.IsEmpty()) {
+ nsString defaultCharset;
+ rv = NS_GetLocalizedUnicharPreferenceWithDefault(nullptr,
+ PREF_MAILNEWS_VIEW_DEFAULT_CHARSET,
+ NS_LITERAL_STRING("ISO-8859-1"), defaultCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ LossyCopyUTF16toASCII(defaultCharset, aCharset);
+ SetCharset(aCharset);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::WriteNewsrcFile()
+{
+ nsresult rv;
+
+ bool newsrcHasChanged;
+ rv = GetNewsrcHasChanged(&newsrcHasChanged);
+ if (NS_FAILED(rv)) return rv;
+
+#ifdef DEBUG_NEWS
+ nsCString hostname;
+ rv = GetHostName(hostname);
+ if (NS_FAILED(rv)) return rv;
+#endif /* DEBUG_NEWS */
+
+ if (newsrcHasChanged) {
+#ifdef DEBUG_NEWS
+ printf("write newsrc file for %s\n", hostname.get());
+#endif
+ nsCOMPtr <nsIFile> newsrcFile;
+ rv = GetNewsrcFilePath(getter_AddRefs(newsrcFile));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIOutputStream> newsrcStream;
+ nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(newsrcStream), newsrcFile, -1, 00600);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr <nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ uint32_t bytesWritten;
+ nsCString optionLines;
+ rv = newsFolder->GetOptionLines(optionLines);
+ if (NS_SUCCEEDED(rv) && !optionLines.IsEmpty()) {
+ newsrcStream->Write(optionLines.get(), optionLines.Length(), &bytesWritten);
+#ifdef DEBUG_NEWS
+ printf("option lines:\n%s", optionLines.get());
+#endif /* DEBUG_NEWS */
+ }
+#ifdef DEBUG_NEWS
+ else {
+ printf("no option lines to write out\n");
+ }
+#endif /* DEBUG_NEWS */
+
+ nsCString unsubscribedLines;
+ rv = newsFolder->GetUnsubscribedNewsgroupLines(unsubscribedLines);
+ if (NS_SUCCEEDED(rv) && !unsubscribedLines.IsEmpty()) {
+ newsrcStream->Write(unsubscribedLines.get(), unsubscribedLines.Length(), &bytesWritten);
+#ifdef DEBUG_NEWS
+ printf("unsubscribedLines:\n%s", unsubscribedLines.get());
+#endif /* DEBUG_NEWS */
+ }
+#ifdef DEBUG_NEWS
+ else {
+ printf("no unsubscribed lines to write out\n");
+ }
+#endif /* DEBUG_NEWS */
+
+ rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
+ if (NS_FAILED(rv)) return rv;
+
+ bool moreFolders;
+
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
+ moreFolders) {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child) {
+ newsFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && newsFolder) {
+ nsCString newsrcLine;
+ rv = newsFolder->GetNewsrcLine(newsrcLine);
+ if (NS_SUCCEEDED(rv) && !newsrcLine.IsEmpty()) {
+ // write the line to the newsrc file
+ newsrcStream->Write(newsrcLine.get(), newsrcLine.Length(), &bytesWritten);
+ }
+ }
+ }
+ }
+
+ newsrcStream->Close();
+
+ rv = SetNewsrcHasChanged(false);
+ if (NS_FAILED(rv)) return rv;
+ }
+#ifdef DEBUG_NEWS
+ else {
+ printf("no need to write newsrc file for %s, it was not dirty\n", (hostname.get()));
+ }
+#endif /* DEBUG_NEWS */
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetNewsrcHasChanged(bool aNewsrcHasChanged)
+{
+ mNewsrcHasChanged = aNewsrcHasChanged;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetNewsrcHasChanged(bool *aNewsrcHasChanged)
+{
+ if (!aNewsrcHasChanged) return NS_ERROR_NULL_POINTER;
+
+ *aNewsrcHasChanged = mNewsrcHasChanged;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::CloseCachedConnections()
+{
+ nsresult rv;
+ nsCOMPtr<nsINNTPProtocol> connection;
+
+ // iterate through the connection cache and close the connections.
+ int32_t cnt = mConnectionCache.Count();
+
+ for (int32_t i = 0; i < cnt; ++i)
+ {
+ connection = mConnectionCache[0];
+ if (connection)
+ {
+ rv = connection->CloseConnection();
+ // We need to do this instead of RemoveObjectAt(0) because the
+ // above call will likely cause the object to be removed from the
+ // array anyway
+ mConnectionCache.RemoveObject(connection);
+ }
+ }
+
+ rv = WriteNewsrcFile();
+ if (NS_FAILED(rv)) return rv;
+
+ if (!mGetOnlyNew && !mHostInfoLoaded)
+ {
+ rv = WriteHostInfoFile();
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetMaximumConnectionsNumber(int32_t *aMaxConnections)
+{
+ NS_ENSURE_ARG_POINTER(aMaxConnections);
+
+ nsresult rv = GetIntValue("max_cached_connections", aMaxConnections);
+ // Get our maximum connection count. We need at least 1. If the value is 0,
+ // we use the default. If it's negative, we treat that as 1.
+ if (NS_SUCCEEDED(rv) && *aMaxConnections > 0)
+ return NS_OK;
+
+ *aMaxConnections = (NS_FAILED(rv) || (*aMaxConnections == 0)) ? 2 : 1;
+ (void)SetMaximumConnectionsNumber(*aMaxConnections);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetMaximumConnectionsNumber(int32_t aMaxConnections)
+{
+ return SetIntValue("max_cached_connections", aMaxConnections);
+}
+
+bool
+nsNntpIncomingServer::ConnectionTimeOut(nsINNTPProtocol* aConnection)
+{
+ bool retVal = false;
+ if (!aConnection)
+ return retVal;
+
+ PRTime lastActiveTimeStamp;
+ if (NS_FAILED(aConnection->GetLastActiveTimeStamp(&lastActiveTimeStamp)))
+ return retVal;
+
+ if (PR_Now() - lastActiveTimeStamp >= PRTime(170) * PR_USEC_PER_SEC)
+ {
+#ifdef DEBUG_seth
+ printf("XXX connection timed out, close it, and remove it from the connection cache\n");
+#endif
+ aConnection->CloseConnection();
+ mConnectionCache.RemoveObject(aConnection);
+ retVal = true;
+ }
+ return retVal;
+}
+
+
+nsresult
+nsNntpIncomingServer::CreateProtocolInstance(nsINNTPProtocol ** aNntpConnection, nsIURI *url,
+ nsIMsgWindow *aMsgWindow)
+{
+ // create a new connection and add it to the connection cache
+ // we may need to flag the protocol connection as busy so we don't get
+ // a race
+ // condition where someone else goes through this code
+ nsNNTPProtocol *protocolInstance = new nsNNTPProtocol(this, url, aMsgWindow);
+ if (!protocolInstance)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = protocolInstance->QueryInterface(NS_GET_IID(nsINNTPProtocol), (void **) aNntpConnection);
+ // take the protocol instance and add it to the connectionCache
+ if (NS_SUCCEEDED(rv) && *aNntpConnection)
+ mConnectionCache.AppendObject(*aNntpConnection);
+ return rv;
+}
+
+
+nsresult
+nsNntpIncomingServer::GetNntpConnection(nsIURI * aUri, nsIMsgWindow *aMsgWindow,
+ nsINNTPProtocol ** aNntpConnection)
+{
+ int32_t maxConnections;
+ (void)GetMaximumConnectionsNumber(&maxConnections);
+
+ // Find a non-busy connection
+ nsCOMPtr<nsINNTPProtocol> connection;
+ int32_t cnt = mConnectionCache.Count();
+ for (int32_t i = 0; i < cnt; i++)
+ {
+ connection = mConnectionCache[i];
+ if (connection)
+ {
+ bool isBusy;
+ connection->GetIsBusy(&isBusy);
+ if (!isBusy)
+ break;
+ connection = nullptr;
+ }
+ }
+
+ if (ConnectionTimeOut(connection))
+ {
+ connection = nullptr;
+ // We have one less connection, since we closed this one.
+ --cnt;
+ }
+
+ if (connection)
+ {
+ NS_IF_ADDREF(*aNntpConnection = connection);
+ connection->SetIsCachedConnection(true);
+ }
+ else if (cnt < maxConnections)
+ {
+ // We have room for another connection. Create this connection and return
+ // it to the caller.
+ nsresult rv = CreateProtocolInstance(aNntpConnection, aUri, aMsgWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ // We maxed out our connection count. The caller must therefore enqueue the
+ // call.
+ *aNntpConnection = nullptr;
+ return NS_OK;
+ }
+
+ // Initialize the URI here and now.
+ return (*aNntpConnection)->Initialize(aUri, aMsgWindow);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetNntpChannel(nsIURI *aURI, nsIMsgWindow *aMsgWindow,
+ nsIChannel **aChannel)
+{
+ NS_ENSURE_ARG_POINTER(aChannel);
+
+ nsCOMPtr<nsINNTPProtocol> protocol;
+ nsresult rv = GetNntpConnection(aURI, aMsgWindow, getter_AddRefs(protocol));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (protocol)
+ return CallQueryInterface(protocol, aChannel);
+
+ // No protocol? We need our mock channel.
+ nsNntpMockChannel *channel = new nsNntpMockChannel(aURI, aMsgWindow);
+ if (!channel)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*aChannel = channel);
+
+ m_queuedChannels.AppendElement(channel);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::LoadNewsUrl(nsIURI *aURI, nsIMsgWindow *aMsgWindow,
+ nsISupports *aConsumer)
+{
+ nsCOMPtr<nsINNTPProtocol> protocol;
+ nsresult rv = GetNntpConnection(aURI, aMsgWindow, getter_AddRefs(protocol));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (protocol)
+ return protocol->LoadNewsUrl(aURI, aConsumer);
+
+ // No protocol? We need our mock channel.
+ nsNntpMockChannel *channel = new nsNntpMockChannel(aURI, aMsgWindow,
+ aConsumer);
+ if (!channel)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ m_queuedChannels.AppendElement(channel);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PrepareForNextUrl(nsNNTPProtocol *aConnection)
+{
+ NS_ENSURE_ARG(aConnection);
+
+ // Start the connection on the next URL in the queue. If it can't get a URL to
+ // work, drop that URL (the channel will handle failure notification) and move
+ // on.
+ while (m_queuedChannels.Length() > 0)
+ {
+ RefPtr<nsNntpMockChannel> channel = m_queuedChannels[0];
+ m_queuedChannels.RemoveElementAt(0);
+ nsresult rv = channel->AttachNNTPConnection(*aConnection);
+ // If this succeeded, the connection is now running the URL.
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+ }
+
+ // No queued uris.
+ return NS_OK;
+}
+
+/* void RemoveConnection (in nsINNTPProtocol aNntpConnection); */
+NS_IMETHODIMP nsNntpIncomingServer::RemoveConnection(nsINNTPProtocol *aNntpConnection)
+{
+ if (aNntpConnection)
+ mConnectionCache.RemoveObject(aNntpConnection);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PerformExpand(nsIMsgWindow *aMsgWindow)
+{
+ // Get news.update_unread_on_expand pref
+ nsresult rv;
+ bool updateUnreadOnExpand = true;
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ prefBranch->GetBoolPref("news.update_unread_on_expand", &updateUnreadOnExpand);
+
+ // Only if news.update_unread_on_expand is true do we update the unread counts
+ if (updateUnreadOnExpand)
+ return DownloadMail(aMsgWindow);
+ return NS_OK;
+}
+
+nsresult
+nsNntpIncomingServer::DownloadMail(nsIMsgWindow *aMsgWindow)
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> groups;
+ rv = rootFolder->GetSubFolders(getter_AddRefs(groups));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasNext;
+ while (NS_SUCCEEDED(rv = groups->HasMoreElements(&hasNext)) && hasNext)
+ {
+ nsCOMPtr<nsISupports> nextGroup;
+ rv = groups->GetNext(getter_AddRefs(nextGroup));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> group(do_QueryInterface(nextGroup));
+ rv = group->GetNewMessages(aMsgWindow, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::DisplaySubscribedGroup(nsIMsgNewsFolder *aMsgFolder, int32_t aFirstMessage, int32_t aLastMessage, int32_t aTotalMessages)
+{
+ nsresult rv;
+
+ if (!aMsgFolder) return NS_ERROR_NULL_POINTER;
+#ifdef DEBUG_NEWS
+ printf("DisplaySubscribedGroup(...,%ld,%ld,%ld)\n",aFirstMessage,aLastMessage,aTotalMessages);
+#endif
+ rv = aMsgFolder->UpdateSummaryFromNNTPInfo(aFirstMessage,aLastMessage,aTotalMessages);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow)
+{
+ // Biff will force a download of the messages. If the user doesn't want this
+ // (e.g., there is a lot of high-traffic newsgroups), the better option is to
+ // just ignore biff.
+ return PerformExpand(aMsgWindow);
+}
+
+NS_IMETHODIMP nsNntpIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
+{
+ NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
+ *aServerRequiresPasswordForBiff = false; // for news, biff is getting the unread counts
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::OnStartRunningUrl(nsIURI *url)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::OnStopRunningUrl(nsIURI *url, nsresult exitCode)
+{
+ nsresult rv;
+ rv = UpdateSubscribed();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = StopPopulating(mMsgWindow);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::ContainsNewsgroup(const nsACString &aName,
+ bool *containsGroup)
+{
+ NS_ENSURE_ARG_POINTER(containsGroup);
+ NS_ENSURE_FALSE(aName.IsEmpty(), NS_ERROR_FAILURE);
+
+ if (mSubscribedNewsgroups.Length() == 0)
+ {
+ // If this is empty, we may need to discover folders
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ GetRootFolder(getter_AddRefs(rootFolder));
+ if (rootFolder)
+ {
+ nsCOMPtr<nsISimpleEnumerator> subfolders;
+ rootFolder->GetSubFolders(getter_AddRefs(subfolders));
+ }
+ }
+ nsAutoCString unescapedName;
+ MsgUnescapeString(aName, 0, unescapedName);
+ *containsGroup = mSubscribedNewsgroups.Contains(aName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SubscribeToNewsgroup(const nsACString &aName)
+{
+ NS_ASSERTION(!aName.IsEmpty(), "no name");
+ NS_ENSURE_FALSE(aName.IsEmpty(), NS_ERROR_FAILURE);
+
+ // If we already have this newsgroup, do nothing and report success.
+ bool containsGroup = false;
+ nsresult rv = ContainsNewsgroup(aName, &containsGroup);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (containsGroup)
+ return NS_OK;
+
+ nsCOMPtr<nsIMsgFolder> msgfolder;
+ rv = GetRootMsgFolder(getter_AddRefs(msgfolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(msgfolder, NS_ERROR_FAILURE);
+
+ return msgfolder->CreateSubfolder(NS_ConvertUTF8toUTF16(aName), nullptr);
+}
+
+bool
+writeGroupToHostInfoFile(nsCString &aElement, void *aData)
+{
+ nsIOutputStream *stream;
+ stream = (nsIOutputStream *)aData;
+ NS_ASSERTION(stream, "no stream");
+ if (!stream) {
+ // stop, something is bad.
+ return false;
+ }
+ return true;
+}
+
+void nsNntpIncomingServer::WriteLine(nsIOutputStream *stream, nsCString &str)
+{
+ uint32_t bytesWritten;
+ str.Append(MSG_LINEBREAK);
+ stream->Write(str.get(), str.Length(), &bytesWritten);
+}
+nsresult
+nsNntpIncomingServer::WriteHostInfoFile()
+{
+ if (!mHostInfoHasChanged)
+ return NS_OK;
+
+ mLastUpdatedTime = uint32_t(PR_Now() / PR_USEC_PER_SEC);
+
+ nsCString hostname;
+ nsresult rv = GetHostName(hostname);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!mHostInfoFile)
+ return NS_ERROR_UNEXPECTED;
+ nsCOMPtr<nsIOutputStream> hostInfoStream;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(hostInfoStream), mHostInfoFile, -1, 00600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX TODO: missing some formatting, see the 4.x code
+ nsAutoCString header("# News host information file.");
+ WriteLine(hostInfoStream, header);
+ header.Assign("# This is a generated file! Do not edit.");
+ WriteLine(hostInfoStream, header);
+ header.Truncate();
+ WriteLine(hostInfoStream, header);
+ nsAutoCString version("version=");
+ version.AppendInt(VALID_VERSION);
+ WriteLine(hostInfoStream, version);
+ nsAutoCString newsrcname("newsrcname=");
+ newsrcname.Append(hostname);
+ WriteLine(hostInfoStream, hostname);
+ nsAutoCString dateStr("lastgroupdate=");
+ dateStr.AppendInt(mLastUpdatedTime);
+ WriteLine(hostInfoStream, dateStr);
+ dateStr = "uniqueid=";
+ dateStr.AppendInt(mUniqueId);
+ WriteLine(hostInfoStream, dateStr);
+ header.Assign(MSG_LINEBREAK"begingroups");
+ WriteLine(hostInfoStream, header);
+
+ // XXX TODO: sort groups first?
+ uint32_t length = mGroupsOnServer.Length();
+ for (uint32_t i = 0; i < length; ++i)
+ {
+ uint32_t bytesWritten;
+ hostInfoStream->Write(mGroupsOnServer[i].get(), mGroupsOnServer[i].Length(),
+ &bytesWritten);
+ hostInfoStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
+ }
+
+ hostInfoStream->Close();
+ mHostInfoHasChanged = false;
+ return NS_OK;
+}
+
+nsresult
+nsNntpIncomingServer::LoadHostInfoFile()
+{
+ nsresult rv;
+ // we haven't loaded it yet
+ mHostInfoLoaded = false;
+
+ rv = GetLocalPath(getter_AddRefs(mHostInfoFile));
+ if (NS_FAILED(rv)) return rv;
+ if (!mHostInfoFile) return NS_ERROR_FAILURE;
+
+ rv = mHostInfoFile->AppendNative(NS_LITERAL_CSTRING(HOSTINFO_FILE_NAME));
+ if (NS_FAILED(rv)) return rv;
+
+ bool exists;
+ rv = mHostInfoFile->Exists(&exists);
+ if (NS_FAILED(rv)) return rv;
+
+ // it is ok if the hostinfo.dat file does not exist.
+ if (!exists) return NS_OK;
+
+ nsCOMPtr<nsIInputStream> fileStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), mHostInfoFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool more = true;
+ nsCString line;
+
+ while (more && NS_SUCCEEDED(rv))
+ {
+ rv = lineInputStream->ReadLine(line, &more);
+ if (line.IsEmpty())
+ continue;
+ HandleLine(line.get(), line.Length());
+ }
+ mHasSeenBeginGroups = false;
+ fileStream->Close();
+
+ return UpdateSubscribed();
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::StartPopulatingWithUri(nsIMsgWindow *aMsgWindow, bool aForceToServer, const char *uri)
+{
+#ifdef DEBUG_seth
+ printf("StartPopulatingWithUri(%s)\n",uri);
+#endif
+
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = mInner->StartPopulatingWithUri(aMsgWindow, aForceToServer, uri);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = StopPopulating(mMsgWindow);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SubscribeCleanup()
+{
+ nsresult rv = NS_OK;
+ rv = ClearInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::StartPopulating(nsIMsgWindow *aMsgWindow, bool aForceToServer, bool aGetOnlyNew)
+{
+ mMsgWindow = aMsgWindow;
+
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mInner->StartPopulating(aMsgWindow, aForceToServer, aGetOnlyNew);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = SetDelimiter(NEWS_DELIMITER);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = SetShowFullName(true);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ mHostInfoLoaded = false;
+ mVersion = INVALID_VERSION;
+ mGroupsOnServer.Clear();
+ mGetOnlyNew = aGetOnlyNew;
+
+ if (!aForceToServer) {
+ rv = LoadHostInfoFile();
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // mHostInfoLoaded can be false if we failed to load anything
+ if (aForceToServer || !mHostInfoLoaded || (mVersion != VALID_VERSION)) {
+ // set these to true, so when we are done and we call WriteHostInfoFile()
+ // we'll write out to hostinfo.dat
+ mHostInfoHasChanged = true;
+ mVersion = VALID_VERSION;
+
+ mGroupsOnServer.Clear();
+ rv = nntpService->GetListOfGroupsOnServer(this, aMsgWindow, aGetOnlyNew);
+ if (NS_FAILED(rv)) return rv;
+ }
+ else {
+ rv = StopPopulating(aMsgWindow);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+/**
+ * This method is the entry point for |nsNNTPProtocol| class. |aName| is now
+ * encoded in the serverside character encoding, but we need to handle
+ * newsgroup names in UTF-8 internally, So we convert |aName| to
+ * UTF-8 here for later use.
+ **/
+NS_IMETHODIMP
+nsNntpIncomingServer::AddNewsgroupToList(const char *aName)
+{
+ nsresult rv;
+
+ nsAutoString newsgroupName;
+ nsAutoCString dataCharset;
+ rv = GetCharset(dataCharset);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = nsMsgI18NConvertToUnicode(dataCharset.get(),
+ nsDependentCString(aName),
+ newsgroupName);
+#ifdef DEBUG_jungshik
+ NS_ASSERTION(NS_SUCCEEDED(rv), "newsgroup name conversion failed");
+#endif
+ if (NS_FAILED(rv)) {
+ CopyASCIItoUTF16(nsDependentCString(aName), newsgroupName);
+ }
+
+ rv = AddTo(NS_ConvertUTF16toUTF8(newsgroupName),
+ false, true, true);
+ if (NS_FAILED(rv)) return rv;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetIncomingServer(nsIMsgIncomingServer *aServer)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetIncomingServer(aServer);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetShowFullName(bool showFullName)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetShowFullName(showFullName);
+}
+
+nsresult
+nsNntpIncomingServer::ClearInner()
+{
+ nsresult rv = NS_OK;
+
+ if (mInner) {
+ rv = mInner->SetSubscribeListener(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mInner->SetIncomingServer(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ mInner = nullptr;
+ }
+ return NS_OK;
+}
+
+nsresult
+nsNntpIncomingServer::EnsureInner()
+{
+ nsresult rv = NS_OK;
+
+ if (mInner)
+ return NS_OK;
+
+ mInner = do_CreateInstance(kSubscribableServerCID,&rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!mInner)
+ return NS_ERROR_FAILURE;
+
+ rv = SetIncomingServer(this);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetDelimiter(char *aDelimiter)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetDelimiter(aDelimiter);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetDelimiter(char aDelimiter)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetDelimiter(aDelimiter);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetAsSubscribed(const nsACString &path)
+{
+ mTempSubscribed.AppendElement(path);
+ if (mGetOnlyNew && (!mGroupsOnServer.Contains(path)))
+ return NS_OK;
+
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetAsSubscribed(path);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::UpdateSubscribed()
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ mTempSubscribed.Clear();
+ uint32_t length = mSubscribedNewsgroups.Length();
+ for (uint32_t i = 0; i < length; ++i)
+ SetAsSubscribed(mSubscribedNewsgroups[i]);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddTo(const nsACString &aName, bool addAsSubscribed,
+ bool aSubscribable, bool changeIfExists)
+{
+ NS_ASSERTION(MsgIsUTF8(aName), "Non-UTF-8 newsgroup name");
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddGroupOnServer(aName);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsISubscribeListener> listener;
+ rv = GetSubscribeListener(getter_AddRefs(listener));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!listener)
+ return NS_ERROR_FAILURE;
+
+ rv = listener->OnDonePopulating();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = mInner->StopPopulating(aMsgWindow);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!mGetOnlyNew && !mHostInfoLoaded)
+ {
+ rv = WriteHostInfoFile();
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ // XXX TODO: when do I set this to null?
+ // rv = ClearInner();
+ // NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetSubscribeListener(nsISubscribeListener *aListener)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->SetSubscribeListener(aListener);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSubscribeListener(nsISubscribeListener **aListener)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetSubscribeListener(aListener);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::Subscribe(const char16_t *aUnicharName)
+{
+ return SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(aUnicharName));
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::Unsubscribe(const char16_t *aUnicharName)
+{
+ NS_ENSURE_ARG_POINTER(aUnicharName);
+
+ nsresult rv;
+
+ nsCOMPtr <nsIMsgFolder> serverFolder;
+ rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!serverFolder)
+ return NS_ERROR_FAILURE;
+
+ // to handle non-ASCII newsgroup names, we store them internally as escaped.
+ // so we need to escape and encode the name, in order to find it.
+ nsAutoCString escapedName;
+ rv = NS_MsgEscapeEncodeURLPath(nsDependentString(aUnicharName), escapedName);
+
+ nsCOMPtr <nsIMsgFolder> newsgroupFolder;
+ rv = serverFolder->FindSubFolder(escapedName,
+ getter_AddRefs(newsgroupFolder));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newsgroupFolder)
+ return NS_ERROR_FAILURE;
+
+ rv = serverFolder->PropagateDelete(newsgroupFolder, true /* delete storage */, nullptr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // since we've unsubscribed to a newsgroup, the newsrc needs to be written out
+ rv = SetNewsrcHasChanged(true);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsNntpIncomingServer::HandleLine(const char* line, uint32_t line_size)
+{
+ NS_ASSERTION(line, "line is null");
+ if (!line)
+ return NS_OK;
+
+ // skip blank lines and comments
+ if (line[0] == '#' || line[0] == '\0')
+ return NS_OK;
+ // XXX TODO: make this truly const, maybe pass in an nsCString &
+
+ if (mHasSeenBeginGroups) {
+ // v1 hostinfo files had additional data fields delimited by commas.
+ // with v2 hostinfo files, the additional data fields are removed.
+ char *commaPos = (char *) PL_strchr(line,',');
+ if (commaPos) *commaPos = 0;
+
+ // newsrc entries are all in UTF-8
+#ifdef DEBUG_jungshik
+ NS_ASSERTION(MsgIsUTF8(nsDependentCString(line)), "newsrc line is not utf-8");
+#endif
+ nsresult rv = AddTo(nsDependentCString(line), false, true, true);
+ NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add line");
+ if (NS_SUCCEEDED(rv)) {
+ // since we've seen one group, we can claim we've loaded the
+ // hostinfo file
+ mHostInfoLoaded = true;
+ }
+ }
+ else {
+ if (PL_strncmp(line,"begingroups", 11) == 0) {
+ mHasSeenBeginGroups = true;
+ }
+ char*equalPos = (char *) PL_strchr(line, '=');
+ if (equalPos) {
+ *equalPos++ = '\0';
+ if (PL_strcmp(line, "lastgroupdate") == 0) {
+ mLastUpdatedTime = strtoul(equalPos, nullptr, 10);
+ } else if (PL_strcmp(line, "uniqueid") == 0) {
+ mUniqueId = strtol(equalPos, nullptr, 16);
+ } else if (PL_strcmp(line, "version") == 0) {
+ mVersion = strtol(equalPos, nullptr, 16);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsNntpIncomingServer::AddGroupOnServer(const nsACString &aName)
+{
+ mGroupsOnServer.AppendElement(aName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddNewsgroup(const nsAString &aName)
+{
+ // handle duplicates?
+ mSubscribedNewsgroups.AppendElement(NS_ConvertUTF16toUTF8(aName));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::RemoveNewsgroup(const nsAString &aName)
+{
+ // handle duplicates?
+ mSubscribedNewsgroups.RemoveElement(NS_ConvertUTF16toUTF8(aName));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetState(const nsACString &path, bool state,
+ bool *stateChanged)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mInner->SetState(path, state, stateChanged);
+ if (*stateChanged) {
+ if (state)
+ mTempSubscribed.AppendElement(path);
+ else
+ mTempSubscribed.RemoveElement(path);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::HasChildren(const nsACString &path, bool *aHasChildren)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->HasChildren(path, aHasChildren);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsSubscribed(const nsACString &path,
+ bool *aIsSubscribed)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->IsSubscribed(path, aIsSubscribed);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsSubscribable(const nsACString &path,
+ bool *aIsSubscribable)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->IsSubscribable(path, aIsSubscribable);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetLeafName(const nsACString &path, nsAString &aLeafName)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetLeafName(path, aLeafName);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetFirstChildURI(const nsACString &path, nsACString &aResult)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetFirstChildURI(path, aResult);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetChildren(const nsACString &aPath,
+ nsISimpleEnumerator **aResult)
+{
+ nsresult rv = EnsureInner();
+ NS_ENSURE_SUCCESS(rv,rv);
+ return mInner->GetChildren(aPath, aResult);
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::CommitSubscribeChanges()
+{
+ // we force the newrc to be dirty, so it will get written out when
+ // we call WriteNewsrcFile()
+ nsresult rv = SetNewsrcHasChanged(true);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return WriteNewsrcFile();
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::ForgetPassword()
+{
+ // clear password of root folder (for the news account)
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!rootFolder) return NS_ERROR_FAILURE;
+
+ nsCOMPtr <nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!newsFolder) return NS_ERROR_FAILURE;
+
+ rv = newsFolder->ForgetAuthenticationCredentials();
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // clear password of all child folders
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+
+ rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool moreFolders = false;
+
+ nsresult return_rv = NS_OK;
+
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
+ moreFolders) {
+ nsCOMPtr<nsISupports> child;
+ rv = subFolders->GetNext(getter_AddRefs(child));
+ if (NS_SUCCEEDED(rv) && child) {
+ newsFolder = do_QueryInterface(child, &rv);
+ if (NS_SUCCEEDED(rv) && newsFolder) {
+ rv = newsFolder->ForgetAuthenticationCredentials();
+ if (NS_FAILED(rv))
+ return_rv = rv;
+ }
+ else {
+ return_rv = NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ return return_rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSupportsExtensions(bool *aSupportsExtensions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetSupportsExtensions(bool aSupportsExtensions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddExtension(const char *extension)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::QueryExtension(const char *extension, bool *result)
+{
+#ifdef DEBUG_seth
+ printf("no extension support yet\n");
+#endif
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetPostingAllowed(bool *aPostingAllowed)
+{
+ *aPostingAllowed = mPostingAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetPostingAllowed(bool aPostingAllowed)
+{
+ mPostingAllowed = aPostingAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetLastUpdatedTime(uint32_t *aLastUpdatedTime)
+{
+ *aLastUpdatedTime = mLastUpdatedTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetLastUpdatedTime(uint32_t aLastUpdatedTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddPropertyForGet(const char *name, const char *value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::QueryPropertyForGet(const char *name, char **value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddSearchableGroup(const nsAString &name)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::QuerySearchableGroup(const nsAString &name, bool *result)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::AddSearchableHeader(const char *name)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::QuerySearchableHeader(const char *name, bool *result)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::FindGroup(const nsACString &name, nsIMsgNewsFolder **result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+
+ nsresult rv;
+ nsCOMPtr <nsIMsgFolder> serverFolder;
+ rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!serverFolder) return NS_ERROR_FAILURE;
+
+ // Escape the name for using FindSubFolder
+ nsAutoCString escapedName;
+ rv = MsgEscapeString(name, nsINetUtil::ESCAPE_URL_PATH, escapedName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIMsgFolder> subFolder;
+ rv = serverFolder->FindSubFolder(escapedName, getter_AddRefs(subFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!subFolder) return NS_ERROR_FAILURE;
+
+ rv = subFolder->QueryInterface(NS_GET_IID(nsIMsgNewsFolder), (void**)result);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!*result) return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetFirstGroupNeedingExtraInfo(nsACString &result)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetGroupNeedsExtraInfo(const nsACString &name,
+ bool needsExtraInfo)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GroupNotFound(nsIMsgWindow *aMsgWindow,
+ const nsAString &aName, bool aOpening)
+{
+ nsresult rv;
+ nsCOMPtr <nsIPrompt> prompt;
+
+ if (aMsgWindow) {
+ rv = aMsgWindow->GetPromptDialog(getter_AddRefs(prompt));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "no prompt from the msg window");
+ }
+
+ if (!prompt) {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ rv = wwatch->GetNewPrompter(nullptr, getter_AddRefs(prompt));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ nsCOMPtr <nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr <nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString hostname;
+ rv = GetRealHostName(hostname);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ NS_ConvertUTF8toUTF16 hostStr(hostname);
+
+ nsString groupName(aName);
+ const char16_t *formatStrings[2] = { groupName.get(), hostStr.get() };
+ nsString confirmText;
+ rv = bundle->FormatStringFromName(
+ u"autoUnsubscribeText",
+ formatStrings, 2,
+ getter_Copies(confirmText));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool confirmResult = false;
+ rv = prompt->Confirm(nullptr, confirmText.get(), &confirmResult);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (confirmResult) {
+ rv = Unsubscribe(groupName.get());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetPrettyNameForGroup(const nsAString &name,
+ const nsAString &prettyName)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ NS_ENSURE_ARG_POINTER(canSearchMessages);
+ *canSearchMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetOfflineSupportLevel(int32_t *aSupportLevel)
+{
+ NS_ENSURE_ARG_POINTER(aSupportLevel);
+ nsresult rv;
+
+ rv = GetIntValue("offline_support_level", aSupportLevel);
+ if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv;
+
+ // set default value
+ *aSupportLevel = OFFLINE_SUPPORT_LEVEL_EXTENDED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetDefaultCopiesAndFoldersPrefsToServer(bool *aCopiesAndFoldersOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCopiesAndFoldersOnServer);
+
+ /**
+ * When a news account is created, the copies and folder prefs for the
+ * associated identity don't point to folders on the server.
+ * This makes sense, since there is no "Drafts" folder on a news server.
+ * They'll point to the ones on "Local Folders"
+ */
+
+ *aCopiesAndFoldersOnServer = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
+
+ // No folder creation on news servers. Return false.
+ *aCanCreateFoldersOnServer = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetSearchValue(const nsAString &aSearchValue)
+{
+ nsCString searchValue = NS_ConvertUTF16toUTF8(aSearchValue);
+ MsgCompressWhitespace(searchValue);
+
+ if (mTree) {
+ mTree->BeginUpdateBatch();
+ mTree->RowCountChanged(0, -static_cast<int32_t>(mSubscribeSearchResult.Length()));
+ }
+
+ nsTArray<nsCString> searchStringParts;
+ if (!searchValue.IsEmpty())
+ ParseString(searchValue, ' ', searchStringParts);
+
+ mSubscribeSearchResult.Clear();
+ uint32_t length = mGroupsOnServer.Length();
+ for (uint32_t i = 0; i < length; i++)
+ {
+ // check that all parts of the search string occur
+ bool found = true;
+ for (uint32_t j = 0; j < searchStringParts.Length(); ++j) {
+ if (MsgFind(mGroupsOnServer[i], searchStringParts[j], true, 0) == kNotFound) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found)
+ mSubscribeSearchResult.AppendElement(mGroupsOnServer[i]);
+ }
+
+ nsCStringLowerCaseComparator comparator;
+ mSubscribeSearchResult.Sort(comparator);
+
+ if (mTree)
+ {
+ mTree->RowCountChanged(0, mSubscribeSearchResult.Length());
+ mTree->EndUpdateBatch();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSupportsSubscribeSearch(bool *retVal)
+{
+ *retVal = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetRowCount(int32_t *aRowCount)
+{
+ *aRowCount = mSubscribeSearchResult.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSelection(nsITreeSelection * *aSelection)
+{
+ *aSelection = mTreeSelection;
+ NS_IF_ADDREF(*aSelection);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetSelection(nsITreeSelection * aSelection)
+{
+ mTreeSelection = aSelection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetRowProperties(int32_t index, nsAString& properties)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCellProperties(int32_t row, nsITreeColumn* col, nsAString& properties)
+{
+ if (!IsValidRow(row))
+ return NS_ERROR_UNEXPECTED;
+
+ NS_ENSURE_ARG_POINTER(col);
+
+ const char16_t* colID;
+ col->GetIdConst(&colID);
+ if (colID[0] == 's') {
+ // if <name> is in our temporary list of subscribed groups
+ // add the "subscribed" property so the check mark shows up
+ // in the "subscribedCol"
+ if (mSearchResultSortDescending)
+ row = mSubscribeSearchResult.Length() - 1 - row;
+ if (mTempSubscribed.Contains(mSubscribeSearchResult.ElementAt(row))) {
+ properties.AssignLiteral("subscribed");
+ }
+ }
+ else if (colID[0] == 'n') {
+ // add the "nntp" property to the "nameCol"
+ // so we get the news folder icon in the search view
+ properties.AssignLiteral("nntp");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetColumnProperties(nsITreeColumn* col, nsAString& properties)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsContainer(int32_t index, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsContainerOpen(int32_t index, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsContainerEmpty(int32_t index, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsSeparator(int32_t index, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsSorted(bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::CanDrop(int32_t index,
+ int32_t orientation,
+ nsIDOMDataTransfer *dataTransfer,
+ bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::Drop(int32_t row,
+ int32_t orientation,
+ nsIDOMDataTransfer *dataTransfer)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetParentIndex(int32_t rowIndex, int32_t *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetLevel(int32_t index, int32_t *_retval)
+{
+ *_retval = 0;
+ return NS_OK;
+}
+
+bool
+nsNntpIncomingServer::IsValidRow(int32_t row)
+{
+ return ((row >= 0) && (row < (int32_t)mSubscribeSearchResult.Length()));
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ if (!IsValidRow(row))
+ return NS_ERROR_UNEXPECTED;
+
+ NS_ENSURE_ARG_POINTER(col);
+
+ const char16_t* colID;
+ col->GetIdConst(&colID);
+
+ nsresult rv = NS_OK;
+ if (colID[0] == 'n') {
+ nsAutoCString str;
+ if (mSearchResultSortDescending)
+ row = mSubscribeSearchResult.Length() - 1 - row;
+ // some servers have newsgroup names that are non ASCII. we store
+ // those as escaped. unescape here so the UI is consistent
+ rv = NS_MsgDecodeUnescapeURLPath(mSubscribeSearchResult.ElementAt(row), _retval);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetTree(nsITreeBoxObject *tree)
+{
+ mTree = tree;
+ if (!tree)
+ return NS_OK;
+
+ nsCOMPtr<nsITreeColumns> cols;
+ tree->GetColumns(getter_AddRefs(cols));
+ if (!cols)
+ return NS_OK;
+
+ nsCOMPtr<nsITreeColumn> col;
+ cols->GetKeyColumn(getter_AddRefs(col));
+ if (!col)
+ return NS_OK;
+
+ nsCOMPtr<nsIDOMElement> element;
+ col->GetElement(getter_AddRefs(element));
+ if (!element)
+ return NS_OK;
+
+ nsAutoString dir;
+ element->GetAttribute(NS_LITERAL_STRING("sortDirection"), dir);
+ mSearchResultSortDescending = dir.EqualsLiteral("descending");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::ToggleOpenState(int32_t index)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::CycleHeader(nsITreeColumn* col)
+{
+ NS_ENSURE_ARG_POINTER(col);
+
+ bool cycler;
+ col->GetCycler(&cycler);
+ if (!cycler) {
+ NS_NAMED_LITERAL_STRING(dir, "sortDirection");
+ nsCOMPtr<nsIDOMElement> element;
+ col->GetElement(getter_AddRefs(element));
+ mSearchResultSortDescending = !mSearchResultSortDescending;
+ element->SetAttribute(dir, mSearchResultSortDescending ?
+ NS_LITERAL_STRING("descending") : NS_LITERAL_STRING("ascending"));
+ mTree->Invalidate();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SelectionChanged()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::CycleCell(int32_t row, nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PerformAction(const char16_t *action)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PerformActionOnRow(const char16_t *action, int32_t row)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::PerformActionOnCell(const char16_t *action, int32_t row, nsITreeColumn* col)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
+
+ // No folder creation on news servers. Return false.
+ *aCanFileMessagesOnServer = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetFilterScope(nsMsgSearchScopeValue *filterScope)
+{
+ NS_ENSURE_ARG_POINTER(filterScope);
+
+ *filterScope = nsMsgSearchScope::newsFilter;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSearchScope(nsMsgSearchScopeValue *searchScope)
+{
+ NS_ENSURE_ARG_POINTER(searchScope);
+
+ if (WeAreOffline()) {
+ // This value is set to the localNewsBody scope to be compatible with
+ // the legacy default value.
+ *searchScope = nsMsgSearchScope::localNewsBody;
+ }
+ else {
+ *searchScope = nsMsgSearchScope::news;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSocketType(int32_t *aSocketType)
+{
+ NS_ENSURE_ARG_POINTER(aSocketType);
+ if (!mPrefBranch)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = mPrefBranch->GetIntPref("socketType", aSocketType);
+ if (NS_FAILED(rv))
+ {
+ if (!mDefPrefBranch)
+ return NS_ERROR_NOT_INITIALIZED;
+ rv = mDefPrefBranch->GetIntPref("socketType", aSocketType);
+ if (NS_FAILED(rv))
+ *aSocketType = nsMsgSocketType::plain;
+ }
+
+ // nsMsgIncomingServer::GetSocketType migrates old isSecure to socketType
+ // style for mail. Unfortunately, a bug caused news socketType 0 to be stored
+ // in the prefs even for isSecure true, so the migration wouldn't happen :(
+
+ // Now that we know the socket, make sure isSecure true + socketType 0
+ // doesn't mix. Migrate if that's the case here.
+ if (*aSocketType == nsMsgSocketType::plain)
+ {
+ bool isSecure = false;
+ nsresult rv2 = mPrefBranch->GetBoolPref("isSecure", &isSecure);
+ if (NS_SUCCEEDED(rv2) && isSecure)
+ {
+ *aSocketType = nsMsgSocketType::SSL;
+ // Don't call virtual method in case overrides call GetSocketType.
+ nsMsgIncomingServer::SetSocketType(*aSocketType);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::SetSocketType(int32_t aSocketType)
+{
+ if (!mPrefBranch)
+ return NS_ERROR_NOT_INITIALIZED;
+ nsresult rv = nsMsgIncomingServer::SetSocketType(aSocketType);
+ if (NS_SUCCEEDED(rv))
+ {
+ bool isSecure = false;
+ if (NS_SUCCEEDED(mPrefBranch->GetBoolPref("isSecure", &isSecure)))
+ {
+ // Must keep isSecure in sync since we migrate based on it... if it's set.
+ rv = mPrefBranch->SetBoolPref("isSecure",
+ aSocketType == nsMsgSocketType::SSL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::OnUserOrHostNameChanged(const nsACString& oldName,
+ const nsACString& newName,
+ bool hostnameChanged)
+{
+ nsresult rv;
+ // 1. Do common things in the base class.
+ rv = nsMsgIncomingServer::OnUserOrHostNameChanged(oldName, newName, hostnameChanged);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // 2. Remove file hostinfo.dat so that the new subscribe
+ // list will be reloaded from the new server.
+ nsCOMPtr <nsIFile> hostInfoFile;
+ rv = GetLocalPath(getter_AddRefs(hostInfoFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = hostInfoFile->AppendNative(NS_LITERAL_CSTRING(HOSTINFO_FILE_NAME));
+ NS_ENSURE_SUCCESS(rv, rv);
+ hostInfoFile->Remove(false);
+
+ // 3.Unsubscribe and then subscribe the existing groups to clean up the article numbers
+ // in the rc file (this is because the old and new servers may maintain different
+ // numbers for the same articles if both servers handle the same groups).
+ nsCOMPtr <nsIMsgFolder> serverFolder;
+ rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsISimpleEnumerator> subFolders;
+ rv = serverFolder->GetSubFolders(getter_AddRefs(subFolders));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsTArray<nsString> groupList;
+ nsString folderName;
+
+ // Prepare the group list
+ bool hasMore;
+ while (NS_SUCCEEDED(subFolders->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ subFolders->GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIMsgFolder> newsgroupFolder(do_QueryInterface(item));
+ if (!newsgroupFolder)
+ continue;
+
+ rv = newsgroupFolder->GetName(folderName);
+ NS_ENSURE_SUCCESS(rv,rv);
+ groupList.AppendElement(folderName);
+ }
+
+ // If nothing subscribed then we're done.
+ if (groupList.Length() == 0)
+ return NS_OK;
+
+ // Now unsubscribe & subscribe.
+ uint32_t i;
+ uint32_t cnt = groupList.Length();
+ nsAutoCString cname;
+ for (i = 0; i < cnt; i++)
+ {
+ // unsubscribe.
+ rv = Unsubscribe(groupList[i].get());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ // subscribe.
+ rv = SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(groupList[i]));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ // Force updating the rc file.
+ return CommitSubscribeChanges();
+}
+
+NS_IMETHODIMP
+nsNntpIncomingServer::GetSortOrder(int32_t* aSortOrder)
+{
+ NS_ENSURE_ARG_POINTER(aSortOrder);
+ *aSortOrder = 500000000;
+ return NS_OK;
+}