diff options
Diffstat (limited to 'mailnews/news/src/nsNntpIncomingServer.cpp')
-rw-r--r-- | mailnews/news/src/nsNntpIncomingServer.cpp | 2162 |
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; +} |