1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsNntpIncomingServer.h"
7 #include "nsIPrefBranch.h"
8 #include "nsIPrefService.h"
9 #include "nsNewsFolder.h"
10 #include "nsIMsgFolder.h"
11 #include "nsIFile.h"
12 #include "nsCOMPtr.h"
13 #include "nsINntpService.h"
14 #include "nsINNTPProtocol.h"
15 #include "nsMsgNewsCID.h"
16 #include "nsNNTPProtocol.h"
17 #include "nsMailDirServiceDefs.h"
18 #include "nsMsgUtils.h"
19 #include "nsIPrompt.h"
20 #include "nsIStringBundle.h"
21 #include "nntpCore.h"
22 #include "nsIWindowWatcher.h"
23 #include "nsTreeColumns.h"
24 #include "nsMsgFolderFlags.h"
25 #include "nsMsgI18N.h"
26 #include "nsUnicharUtils.h"
27 #include "nsILineInputStream.h"
28 #include "nsNetUtil.h"
29 #include "nsISimpleEnumerator.h"
30 #include "nsMsgUtils.h"
31 #include "mozilla/Services.h"
32 #include "mozilla/dom/Element.h"
33 #include "mozilla/ErrorResult.h"
34 #include "mozilla/dom/XULTreeElement.h"
35 #include "mozilla/dom/DataTransfer.h"
36 #include "mozilla/LoadInfo.h"
37 #include "mozilla/Utf8.h"
38
39 #define INVALID_VERSION 0
40 #define VALID_VERSION 2
41 #define NEW_NEWS_DIR_NAME "News"
42 #define PREF_MAIL_NEWSRC_ROOT "mail.newsrc_root"
43 #define PREF_MAIL_NEWSRC_ROOT_REL "mail.newsrc_root-rel"
44 #define HOSTINFO_FILE_NAME "hostinfo.dat"
45
46 #define NEWS_DELIMITER '.'
47
48 // this platform specific junk is so the newsrc filenames we create
49 // will resemble the migrated newsrc filenames.
50 #if defined(XP_UNIX)
51 # define NEWSRC_FILE_PREFIX "newsrc-"
52 # define NEWSRC_FILE_SUFFIX ""
53 #else
54 # define NEWSRC_FILE_PREFIX ""
55 # define NEWSRC_FILE_SUFFIX ".rc"
56 #endif /* XP_UNIX */
57
58 // ###tw This really ought to be the most
59 // efficient file reading size for the current
60 // operating system.
61 #define HOSTINFO_FILE_BUFFER_SIZE 1024
62
63 #include "nsMsgUtils.h"
64
65 /**
66 * A comparator class to do cases insensitive comparisons for nsTArray.Sort()
67 */
68 class nsCStringLowerCaseComparator {
69 public:
Equals(const nsCString & a,const nsCString & b) const70 bool Equals(const nsCString& a, const nsCString& b) const {
71 return a.Equals(b, nsCaseInsensitiveCStringComparator);
72 }
73
LessThan(const nsCString & a,const nsCString & b) const74 bool LessThan(const nsCString& a, const nsCString& b) const {
75 return Compare(a, b, nsCaseInsensitiveCStringComparator) < 0;
76 }
77 };
78
79 static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID);
80
NS_IMPL_ADDREF_INHERITED(nsNntpIncomingServer,nsMsgIncomingServer)81 NS_IMPL_ADDREF_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
82 NS_IMPL_RELEASE_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
83
84 NS_INTERFACE_MAP_BEGIN(nsNntpIncomingServer)
85 NS_INTERFACE_MAP_ENTRY(nsINntpIncomingServer)
86 NS_INTERFACE_MAP_ENTRY(nsIUrlListener)
87 NS_INTERFACE_MAP_ENTRY(nsISubscribableServer)
88 NS_INTERFACE_MAP_ENTRY(nsITreeView)
89 NS_INTERFACE_MAP_END_INHERITING(nsMsgIncomingServer)
90
91 nsNntpIncomingServer::nsNntpIncomingServer() {
92 mNewsrcHasChanged = false;
93
94 mGetOnlyNew = true;
95
96 mHostInfoLoaded = false;
97 mHostInfoHasChanged = false;
98 mVersion = INVALID_VERSION;
99
100 mLastGroupDate = 0;
101 mUniqueId = 0;
102 mHasSeenBeginGroups = false;
103 mPostingAllowed = false;
104 mLastUpdatedTime = 0;
105
106 // we have server wide and per group filters
107 m_canHaveFilters = true;
108
109 SetupNewsrcSaveTimer();
110 }
111
~nsNntpIncomingServer()112 nsNntpIncomingServer::~nsNntpIncomingServer() {
113 mozilla::DebugOnly<nsresult> rv;
114
115 if (mNewsrcSaveTimer) {
116 mNewsrcSaveTimer->Cancel();
117 mNewsrcSaveTimer = nullptr;
118 }
119 rv = ClearInner();
120 NS_ASSERTION(NS_SUCCEEDED(rv), "ClearInner failed");
121
122 rv = CloseCachedConnections();
123 NS_ASSERTION(NS_SUCCEEDED(rv), "CloseCachedConnections failed");
124 }
125
126 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, NotifyOn, "notify.on")
127 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, MarkOldRead, "mark_old_read")
128 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, Abbreviate, "abbreviate")
129 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, PushAuth, "always_authenticate")
130 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, SingleSignon, "singleSignon")
131 NS_IMPL_SERVERPREF_INT(nsNntpIncomingServer, MaxArticles, "max_articles")
132
CreateRootFolderFromUri(const nsACString & serverUri,nsIMsgFolder ** rootFolder)133 nsresult nsNntpIncomingServer::CreateRootFolderFromUri(
134 const nsACString& serverUri, nsIMsgFolder** rootFolder) {
135 nsMsgNewsFolder* newRootFolder = new nsMsgNewsFolder;
136 NS_ADDREF(*rootFolder = newRootFolder);
137 newRootFolder->Init(serverUri);
138 return NS_OK;
139 }
140
141 NS_IMETHODIMP
GetNewsrcFilePath(nsIFile ** aNewsrcFilePath)142 nsNntpIncomingServer::GetNewsrcFilePath(nsIFile** aNewsrcFilePath) {
143 nsresult rv;
144 if (mNewsrcFilePath) {
145 NS_IF_ADDREF(*aNewsrcFilePath = mNewsrcFilePath);
146 return NS_OK;
147 }
148
149 rv = GetFileValue("newsrc.file-rel", "newsrc.file", aNewsrcFilePath);
150 if (NS_SUCCEEDED(rv) && *aNewsrcFilePath) {
151 mNewsrcFilePath = *aNewsrcFilePath;
152 return rv;
153 }
154
155 rv = GetNewsrcRootPath(getter_AddRefs(mNewsrcFilePath));
156 if (NS_FAILED(rv)) return rv;
157
158 nsCString hostname;
159 rv = GetHostName(hostname);
160 NS_ENSURE_SUCCESS(rv, rv);
161
162 nsAutoCString newsrcFileName(NEWSRC_FILE_PREFIX);
163 newsrcFileName.Append(hostname);
164 newsrcFileName.Append(NEWSRC_FILE_SUFFIX);
165 rv = mNewsrcFilePath->AppendNative(newsrcFileName);
166 rv = mNewsrcFilePath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
167 NS_ENSURE_SUCCESS(rv, rv);
168
169 rv = SetNewsrcFilePath(mNewsrcFilePath);
170 NS_ENSURE_SUCCESS(rv, rv);
171
172 NS_ADDREF(*aNewsrcFilePath = mNewsrcFilePath);
173 return NS_OK;
174 }
175
176 NS_IMETHODIMP
SetNewsrcFilePath(nsIFile * aFile)177 nsNntpIncomingServer::SetNewsrcFilePath(nsIFile* aFile) {
178 NS_ENSURE_ARG_POINTER(aFile);
179
180 bool exists;
181 nsresult rv = aFile->Exists(&exists);
182 if (!exists) {
183 rv = aFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0664);
184 if (NS_FAILED(rv)) return rv;
185 }
186 return SetFileValue("newsrc.file-rel", "newsrc.file", aFile);
187 }
188
189 NS_IMETHODIMP
GetLocalStoreType(nsACString & type)190 nsNntpIncomingServer::GetLocalStoreType(nsACString& type) {
191 type.AssignLiteral("news");
192 return NS_OK;
193 }
194
195 NS_IMETHODIMP
GetLocalDatabaseType(nsACString & type)196 nsNntpIncomingServer::GetLocalDatabaseType(nsACString& type) {
197 type.AssignLiteral("news");
198 return NS_OK;
199 }
200
201 NS_IMETHODIMP
SetNewsrcRootPath(nsIFile * aNewsrcRootPath)202 nsNntpIncomingServer::SetNewsrcRootPath(nsIFile* aNewsrcRootPath) {
203 NS_ENSURE_ARG(aNewsrcRootPath);
204 return NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT,
205 aNewsrcRootPath);
206 }
207
208 NS_IMETHODIMP
GetNewsrcRootPath(nsIFile ** aNewsrcRootPath)209 nsNntpIncomingServer::GetNewsrcRootPath(nsIFile** aNewsrcRootPath) {
210 NS_ENSURE_ARG_POINTER(aNewsrcRootPath);
211 *aNewsrcRootPath = nullptr;
212
213 bool havePref;
214 nsresult rv =
215 NS_GetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT,
216 NS_APP_NEWS_50_DIR, havePref, aNewsrcRootPath);
217
218 NS_ENSURE_SUCCESS(rv, rv);
219
220 bool exists;
221 rv = (*aNewsrcRootPath)->Exists(&exists);
222 if (NS_SUCCEEDED(rv) && !exists)
223 rv = (*aNewsrcRootPath)->Create(nsIFile::DIRECTORY_TYPE, 0775);
224 NS_ENSURE_SUCCESS(rv, rv);
225
226 if (!havePref || !exists) {
227 rv = NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT,
228 *aNewsrcRootPath);
229 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
230 }
231 return rv;
232 }
233
OnNewsrcSaveTimer(nsITimer * timer,void * voidIncomingServer)234 /* static */ void nsNntpIncomingServer::OnNewsrcSaveTimer(
235 nsITimer* timer, void* voidIncomingServer) {
236 nsNntpIncomingServer* incomingServer =
237 (nsNntpIncomingServer*)voidIncomingServer;
238 incomingServer->WriteNewsrcFile();
239 }
240
SetupNewsrcSaveTimer()241 nsresult nsNntpIncomingServer::SetupNewsrcSaveTimer() {
242 int64_t ms(300000); // hard code, 5 minutes.
243 // Convert biffDelay into milliseconds
244 uint32_t timeInMSUint32 = (uint32_t)ms;
245 // Can't currently reset a timer when it's in the process of
246 // calling Notify. So, just release the timer here and create a new one.
247 if (mNewsrcSaveTimer) mNewsrcSaveTimer->Cancel();
248 mNewsrcSaveTimer = do_CreateInstance("@mozilla.org/timer;1");
249 mNewsrcSaveTimer->InitWithNamedFuncCallback(
250 OnNewsrcSaveTimer, (void*)this, timeInMSUint32,
251 nsITimer::TYPE_REPEATING_SLACK,
252 "nsNntpIncomingServer::OnNewsrcSaveTimer");
253 return NS_OK;
254 }
255
256 NS_IMETHODIMP
SetCharset(const nsACString & aCharset)257 nsNntpIncomingServer::SetCharset(const nsACString& aCharset) {
258 return SetCharValue("charset", aCharset);
259 }
260
261 NS_IMETHODIMP
GetCharset(nsACString & aCharset)262 nsNntpIncomingServer::GetCharset(nsACString& aCharset) {
263 // first we get the per-server settings mail.server.<serverkey>.charset
264 nsresult rv = GetCharValue("charset", aCharset);
265 NS_ENSURE_SUCCESS(rv, rv);
266
267 // if the per-server setting is empty, default to UTF-8 and set it as
268 // per-server preference.
269 if (aCharset.IsEmpty()) {
270 aCharset.AssignLiteral("UTF-8");
271 SetCharset(aCharset);
272 }
273 return NS_OK;
274 }
275
276 NS_IMETHODIMP
WriteNewsrcFile()277 nsNntpIncomingServer::WriteNewsrcFile() {
278 nsresult rv;
279
280 bool newsrcHasChanged;
281 rv = GetNewsrcHasChanged(&newsrcHasChanged);
282 if (NS_FAILED(rv)) return rv;
283
284 #ifdef DEBUG_NEWS
285 nsCString hostname;
286 rv = GetHostName(hostname);
287 if (NS_FAILED(rv)) return rv;
288 #endif /* DEBUG_NEWS */
289
290 if (newsrcHasChanged) {
291 #ifdef DEBUG_NEWS
292 printf("write newsrc file for %s\n", hostname.get());
293 #endif
294 nsCOMPtr<nsIFile> newsrcFile;
295 rv = GetNewsrcFilePath(getter_AddRefs(newsrcFile));
296 if (NS_FAILED(rv)) return rv;
297
298 nsCOMPtr<nsIOutputStream> newsrcStream;
299 nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(newsrcStream),
300 newsrcFile, -1, 00600);
301 if (NS_FAILED(rv)) return rv;
302
303 nsCOMPtr<nsIMsgFolder> rootFolder;
304 rv = GetRootFolder(getter_AddRefs(rootFolder));
305 if (NS_FAILED(rv)) return rv;
306
307 nsCOMPtr<nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
308 if (NS_FAILED(rv)) return rv;
309
310 uint32_t bytesWritten;
311 nsCString optionLines;
312 rv = newsFolder->GetOptionLines(optionLines);
313 if (NS_SUCCEEDED(rv) && !optionLines.IsEmpty()) {
314 newsrcStream->Write(optionLines.get(), optionLines.Length(),
315 &bytesWritten);
316 #ifdef DEBUG_NEWS
317 printf("option lines:\n%s", optionLines.get());
318 #endif /* DEBUG_NEWS */
319 }
320 #ifdef DEBUG_NEWS
321 else {
322 printf("no option lines to write out\n");
323 }
324 #endif /* DEBUG_NEWS */
325
326 nsCString unsubscribedLines;
327 rv = newsFolder->GetUnsubscribedNewsgroupLines(unsubscribedLines);
328 if (NS_SUCCEEDED(rv) && !unsubscribedLines.IsEmpty()) {
329 newsrcStream->Write(unsubscribedLines.get(), unsubscribedLines.Length(),
330 &bytesWritten);
331 #ifdef DEBUG_NEWS
332 printf("unsubscribedLines:\n%s", unsubscribedLines.get());
333 #endif /* DEBUG_NEWS */
334 }
335 #ifdef DEBUG_NEWS
336 else {
337 printf("no unsubscribed lines to write out\n");
338 }
339 #endif /* DEBUG_NEWS */
340
341 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
342 rv = rootFolder->GetSubFolders(subFolders);
343 NS_ENSURE_SUCCESS(rv, rv);
344 for (nsIMsgFolder* child : subFolders) {
345 newsFolder = do_QueryInterface(child, &rv);
346 if (NS_SUCCEEDED(rv) && newsFolder) {
347 nsCString newsrcLine;
348 rv = newsFolder->GetNewsrcLine(newsrcLine);
349 if (NS_SUCCEEDED(rv) && !newsrcLine.IsEmpty()) {
350 // write the line to the newsrc file
351 newsrcStream->Write(newsrcLine.get(), newsrcLine.Length(),
352 &bytesWritten);
353 }
354 }
355 }
356
357 newsrcStream->Close();
358
359 rv = SetNewsrcHasChanged(false);
360 if (NS_FAILED(rv)) return rv;
361 }
362 #ifdef DEBUG_NEWS
363 else {
364 printf("no need to write newsrc file for %s, it was not dirty\n",
365 (hostname.get()));
366 }
367 #endif /* DEBUG_NEWS */
368
369 return NS_OK;
370 }
371
372 NS_IMETHODIMP
SetNewsrcHasChanged(bool aNewsrcHasChanged)373 nsNntpIncomingServer::SetNewsrcHasChanged(bool aNewsrcHasChanged) {
374 mNewsrcHasChanged = aNewsrcHasChanged;
375 return NS_OK;
376 }
377
378 NS_IMETHODIMP
GetNewsrcHasChanged(bool * aNewsrcHasChanged)379 nsNntpIncomingServer::GetNewsrcHasChanged(bool* aNewsrcHasChanged) {
380 if (!aNewsrcHasChanged) return NS_ERROR_NULL_POINTER;
381
382 *aNewsrcHasChanged = mNewsrcHasChanged;
383 return NS_OK;
384 }
385
386 NS_IMETHODIMP
CloseCachedConnections()387 nsNntpIncomingServer::CloseCachedConnections() {
388 nsresult rv;
389 nsCOMPtr<nsINNTPProtocol> connection;
390
391 // iterate through the connection cache and close the connections.
392 int32_t cnt = mConnectionCache.Count();
393
394 for (int32_t i = 0; i < cnt; ++i) {
395 connection = mConnectionCache[0];
396 if (connection) {
397 rv = connection->CloseConnection();
398 // We need to do this instead of RemoveObjectAt(0) because the
399 // above call will likely cause the object to be removed from the
400 // array anyway
401 mConnectionCache.RemoveObject(connection);
402 }
403 }
404
405 rv = WriteNewsrcFile();
406 if (NS_FAILED(rv)) return rv;
407
408 if (!mGetOnlyNew && !mHostInfoLoaded) {
409 rv = WriteHostInfoFile();
410 NS_ENSURE_SUCCESS(rv, rv);
411 }
412
413 return NS_OK;
414 }
415
416 NS_IMETHODIMP
GetMaximumConnectionsNumber(int32_t * aMaxConnections)417 nsNntpIncomingServer::GetMaximumConnectionsNumber(int32_t* aMaxConnections) {
418 NS_ENSURE_ARG_POINTER(aMaxConnections);
419
420 nsresult rv = GetIntValue("max_cached_connections", aMaxConnections);
421 // Get our maximum connection count. We need at least 1. If the value is 0,
422 // we use the default. If it's negative, we treat that as 1.
423 if (NS_SUCCEEDED(rv) && *aMaxConnections > 0) return NS_OK;
424
425 *aMaxConnections = (NS_FAILED(rv) || (*aMaxConnections == 0)) ? 2 : 1;
426 (void)SetMaximumConnectionsNumber(*aMaxConnections);
427
428 return NS_OK;
429 }
430
431 NS_IMETHODIMP
SetMaximumConnectionsNumber(int32_t aMaxConnections)432 nsNntpIncomingServer::SetMaximumConnectionsNumber(int32_t aMaxConnections) {
433 return SetIntValue("max_cached_connections", aMaxConnections);
434 }
435
ConnectionTimeOut(nsINNTPProtocol * aConnection)436 bool nsNntpIncomingServer::ConnectionTimeOut(nsINNTPProtocol* aConnection) {
437 bool retVal = false;
438 if (!aConnection) return retVal;
439
440 PRTime lastActiveTimeStamp;
441 if (NS_FAILED(aConnection->GetLastActiveTimeStamp(&lastActiveTimeStamp)))
442 return retVal;
443
444 if (PR_Now() - lastActiveTimeStamp >= PRTime(170) * PR_USEC_PER_SEC) {
445 #ifdef DEBUG_seth
446 printf(
447 "XXX connection timed out, close it, and remove it from the connection "
448 "cache\n");
449 #endif
450 aConnection->CloseConnection();
451 mConnectionCache.RemoveObject(aConnection);
452 retVal = true;
453 }
454 return retVal;
455 }
456
CreateProtocolInstance(nsINNTPProtocol ** aNntpConnection,nsIURI * url,nsIMsgWindow * aMsgWindow)457 nsresult nsNntpIncomingServer::CreateProtocolInstance(
458 nsINNTPProtocol** aNntpConnection, nsIURI* url, nsIMsgWindow* aMsgWindow) {
459 // create a new connection and add it to the connection cache
460 // we may need to flag the protocol connection as busy so we don't get
461 // a race
462 // condition where someone else goes through this code
463 nsNNTPProtocol* protocolInstance = new nsNNTPProtocol(this, url, aMsgWindow);
464 if (!protocolInstance) return NS_ERROR_OUT_OF_MEMORY;
465
466 nsresult rv = protocolInstance->QueryInterface(NS_GET_IID(nsINNTPProtocol),
467 (void**)aNntpConnection);
468 // take the protocol instance and add it to the connectionCache
469 if (NS_SUCCEEDED(rv) && *aNntpConnection)
470 mConnectionCache.AppendObject(*aNntpConnection);
471 return rv;
472 }
473
474 /**
475 * Find an available nsNNTPConnection. Can create new connections as long as
476 * we stay under the maximum connection limit.
477 * If none are available, returns NS_OK and nullptr.
478 */
GetNntpConnection(nsIURI * aUri,nsIMsgWindow * aMsgWindow,nsINNTPProtocol ** aNntpConnection)479 nsresult nsNntpIncomingServer::GetNntpConnection(
480 nsIURI* aUri, nsIMsgWindow* aMsgWindow, nsINNTPProtocol** aNntpConnection) {
481 int32_t maxConnections;
482 (void)GetMaximumConnectionsNumber(&maxConnections);
483
484 // Find a non-busy connection
485 nsCOMPtr<nsINNTPProtocol> connection;
486 int32_t cnt = mConnectionCache.Count();
487 for (int32_t i = 0; i < cnt; i++) {
488 connection = mConnectionCache[i];
489 if (connection) {
490 bool isBusy;
491 connection->GetIsBusy(&isBusy);
492 if (!isBusy) break;
493 connection = nullptr;
494 }
495 }
496
497 if (ConnectionTimeOut(connection)) {
498 connection = nullptr;
499 // We have one less connection, since we closed this one.
500 --cnt;
501 }
502
503 if (connection) {
504 // We've got a connection, ready to go.
505 connection.forget(aNntpConnection);
506 } else if (cnt < maxConnections) {
507 // We have room for another connection. Create this connection and return
508 // it to the caller.
509 nsresult rv = CreateProtocolInstance(aNntpConnection, aUri, aMsgWindow);
510 NS_ENSURE_SUCCESS(rv, rv);
511 } else {
512 // We maxed out our connection count. The caller must therefore enqueue the
513 // call.
514 *aNntpConnection = nullptr;
515 return NS_OK;
516 }
517
518 // Initialize the URI here and now.
519 return (*aNntpConnection)->Initialize(aUri, aMsgWindow);
520 }
521
522 /**
523 * Returns an nsIChannel to run the given URI.
524 * The returned channel might be either an nsNNTPProtocol or nsNntpMockChannel,
525 * representing a real connection or a queued command, respectively.
526 */
527 NS_IMETHODIMP
GetNntpChannel(nsIURI * aURI,nsIMsgWindow * aMsgWindow,nsIChannel ** aChannel)528 nsNntpIncomingServer::GetNntpChannel(nsIURI* aURI, nsIMsgWindow* aMsgWindow,
529 nsIChannel** aChannel) {
530 NS_ENSURE_ARG_POINTER(aChannel);
531
532 nsCOMPtr<nsINNTPProtocol> protocol;
533 nsresult rv = GetNntpConnection(aURI, aMsgWindow, getter_AddRefs(protocol));
534 NS_ENSURE_SUCCESS(rv, rv);
535
536 if (protocol) return CallQueryInterface(protocol, aChannel);
537
538 // No protocol? We need our mock channel.
539 nsNntpMockChannel* channel = new nsNntpMockChannel(aURI, aMsgWindow);
540 NS_ADDREF(*aChannel = channel);
541
542 m_queuedChannels.AppendElement(channel);
543 return NS_OK;
544 }
545
546 /**
547 * Submits a news URI to run. If no connections are free, the command will
548 * be queued to run when one becomes available.
549 */
550 NS_IMETHODIMP
LoadNewsUrl(nsIURI * aURI,nsIMsgWindow * aMsgWindow,nsISupports * aConsumer)551 nsNntpIncomingServer::LoadNewsUrl(nsIURI* aURI, nsIMsgWindow* aMsgWindow,
552 nsISupports* aConsumer) {
553 nsCOMPtr<nsINNTPProtocol> protocol;
554 nsresult rv = GetNntpConnection(aURI, aMsgWindow, getter_AddRefs(protocol));
555 NS_ENSURE_SUCCESS(rv, rv);
556
557 nsCOMPtr<nsILoadInfo> loadInfo = new mozilla::net::LoadInfo(
558 nsContentUtils::GetSystemPrincipal(), nullptr, nullptr,
559 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
560 nsIContentPolicy::TYPE_OTHER);
561
562 if (protocol) {
563 nsCOMPtr<nsIChannel> chan = do_QueryInterface(protocol, &rv);
564 NS_ENSURE_SUCCESS(rv, rv);
565 rv = chan->SetLoadInfo(loadInfo);
566 NS_ENSURE_SUCCESS(rv, rv);
567 return protocol->LoadNewsUrl(aURI, aConsumer);
568 }
569
570 // No protocol? We need our mock channel.
571 nsNntpMockChannel* channel =
572 new nsNntpMockChannel(aURI, aMsgWindow, aConsumer);
573 if (!channel) return NS_ERROR_OUT_OF_MEMORY;
574
575 rv = channel->SetLoadInfo(loadInfo);
576 NS_ENSURE_SUCCESS(rv, rv);
577 m_queuedChannels.AppendElement(channel);
578 return NS_OK;
579 }
580
581 /**
582 * Called when an nsNNTPProtocol finishes running a command and is ready to be
583 * assigned a new one.
584 */
585 NS_IMETHODIMP
PrepareForNextUrl(nsNNTPProtocol * aConnection)586 nsNntpIncomingServer::PrepareForNextUrl(nsNNTPProtocol* aConnection) {
587 NS_ENSURE_ARG(aConnection);
588
589 // Start the connection on the next URL in the queue. If it can't get a URL to
590 // work, drop that URL (the channel will handle failure notification) and move
591 // on.
592 while (m_queuedChannels.Length() > 0) {
593 RefPtr<nsNntpMockChannel> channel = m_queuedChannels[0];
594 m_queuedChannels.RemoveElementAt(0);
595 nsresult rv = channel->AttachNNTPConnection(*aConnection);
596 // If this succeeded, the connection is now running the URL.
597 if (NS_SUCCEEDED(rv)) return NS_OK;
598 }
599
600 // No queued uris.
601 return NS_OK;
602 }
603
604 /* void RemoveConnection (in nsINNTPProtocol aNntpConnection); */
RemoveConnection(nsINNTPProtocol * aNntpConnection)605 NS_IMETHODIMP nsNntpIncomingServer::RemoveConnection(
606 nsINNTPProtocol* aNntpConnection) {
607 if (aNntpConnection) mConnectionCache.RemoveObject(aNntpConnection);
608
609 return NS_OK;
610 }
611
612 NS_IMETHODIMP
PerformExpand(nsIMsgWindow * aMsgWindow)613 nsNntpIncomingServer::PerformExpand(nsIMsgWindow* aMsgWindow) {
614 // Get news.update_unread_on_expand pref
615 nsresult rv;
616 bool updateUnreadOnExpand = true;
617 nsCOMPtr<nsIPrefBranch> prefBranch =
618 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
619 if (NS_SUCCEEDED(rv))
620 prefBranch->GetBoolPref("news.update_unread_on_expand",
621 &updateUnreadOnExpand);
622
623 // Only if news.update_unread_on_expand is true do we update the unread counts
624 if (updateUnreadOnExpand) return DownloadMail(aMsgWindow);
625 return NS_OK;
626 }
627
DownloadMail(nsIMsgWindow * aMsgWindow)628 nsresult nsNntpIncomingServer::DownloadMail(nsIMsgWindow* aMsgWindow) {
629 nsCOMPtr<nsIMsgFolder> rootFolder;
630 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
631 NS_ENSURE_SUCCESS(rv, rv);
632
633 nsTArray<RefPtr<nsIMsgFolder>> groups;
634 rv = rootFolder->GetSubFolders(groups);
635 NS_ENSURE_SUCCESS(rv, rv);
636 for (nsIMsgFolder* group : groups) {
637 rv = group->GetNewMessages(aMsgWindow, nullptr);
638 NS_ENSURE_SUCCESS(rv, rv);
639 }
640 return rv;
641 }
642
643 NS_IMETHODIMP
DisplaySubscribedGroup(nsIMsgNewsFolder * aMsgFolder,int32_t aFirstMessage,int32_t aLastMessage,int32_t aTotalMessages)644 nsNntpIncomingServer::DisplaySubscribedGroup(nsIMsgNewsFolder* aMsgFolder,
645 int32_t aFirstMessage,
646 int32_t aLastMessage,
647 int32_t aTotalMessages) {
648 nsresult rv;
649
650 if (!aMsgFolder) return NS_ERROR_NULL_POINTER;
651 #ifdef DEBUG_NEWS
652 printf("DisplaySubscribedGroup(...,%ld,%ld,%ld)\n", aFirstMessage,
653 aLastMessage, aTotalMessages);
654 #endif
655 rv = aMsgFolder->UpdateSummaryFromNNTPInfo(aFirstMessage, aLastMessage,
656 aTotalMessages);
657 return rv;
658 }
659
660 NS_IMETHODIMP
PerformBiff(nsIMsgWindow * aMsgWindow)661 nsNntpIncomingServer::PerformBiff(nsIMsgWindow* aMsgWindow) {
662 // Biff will force a download of the messages. If the user doesn't want this
663 // (e.g., there is a lot of high-traffic newsgroups), the better option is to
664 // just ignore biff.
665 return PerformExpand(aMsgWindow);
666 }
667
GetServerRequiresPasswordForBiff(bool * aServerRequiresPasswordForBiff)668 NS_IMETHODIMP nsNntpIncomingServer::GetServerRequiresPasswordForBiff(
669 bool* aServerRequiresPasswordForBiff) {
670 NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
671 *aServerRequiresPasswordForBiff =
672 false; // for news, biff is getting the unread counts
673 return NS_OK;
674 }
675
676 NS_IMETHODIMP
OnStartRunningUrl(nsIURI * url)677 nsNntpIncomingServer::OnStartRunningUrl(nsIURI* url) { return NS_OK; }
678
679 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * url,nsresult exitCode)680 nsNntpIncomingServer::OnStopRunningUrl(nsIURI* url, nsresult exitCode) {
681 nsresult rv;
682 rv = UpdateSubscribed();
683 if (NS_FAILED(rv)) return rv;
684
685 rv = StopPopulating(mMsgWindow);
686 if (NS_FAILED(rv)) return rv;
687
688 return NS_OK;
689 }
690
691 NS_IMETHODIMP
ContainsNewsgroup(const nsACString & aName,bool * containsGroup)692 nsNntpIncomingServer::ContainsNewsgroup(const nsACString& aName,
693 bool* containsGroup) {
694 NS_ENSURE_ARG_POINTER(containsGroup);
695 NS_ENSURE_FALSE(aName.IsEmpty(), NS_ERROR_FAILURE);
696
697 if (mSubscribedNewsgroups.Length() == 0) {
698 // If this is empty, we may need to discover folders
699 nsCOMPtr<nsIMsgFolder> rootFolder;
700 GetRootFolder(getter_AddRefs(rootFolder));
701 if (rootFolder) {
702 nsTArray<RefPtr<nsIMsgFolder>> dummy;
703 rootFolder->GetSubFolders(dummy);
704 }
705 }
706 nsAutoCString unescapedName;
707 MsgUnescapeString(aName, 0, unescapedName);
708 *containsGroup = mSubscribedNewsgroups.Contains(aName);
709 return NS_OK;
710 }
711
712 NS_IMETHODIMP
SubscribeToNewsgroup(const nsACString & aName)713 nsNntpIncomingServer::SubscribeToNewsgroup(const nsACString& aName) {
714 NS_ASSERTION(!aName.IsEmpty(), "no name");
715 NS_ENSURE_FALSE(aName.IsEmpty(), NS_ERROR_FAILURE);
716
717 // If we already have this newsgroup, do nothing and report success.
718 bool containsGroup = false;
719 nsresult rv = ContainsNewsgroup(aName, &containsGroup);
720 NS_ENSURE_SUCCESS(rv, rv);
721 if (containsGroup) return NS_OK;
722
723 nsCOMPtr<nsIMsgFolder> msgfolder;
724 rv = GetRootMsgFolder(getter_AddRefs(msgfolder));
725 NS_ENSURE_SUCCESS(rv, rv);
726 NS_ENSURE_TRUE(msgfolder, NS_ERROR_FAILURE);
727
728 return msgfolder->CreateSubfolder(NS_ConvertUTF8toUTF16(aName), nullptr);
729 }
730
writeGroupToHostInfoFile(nsCString & aElement,void * aData)731 bool writeGroupToHostInfoFile(nsCString& aElement, void* aData) {
732 nsIOutputStream* stream;
733 stream = (nsIOutputStream*)aData;
734 NS_ASSERTION(stream, "no stream");
735 if (!stream) {
736 // stop, something is bad.
737 return false;
738 }
739 return true;
740 }
741
WriteLine(nsIOutputStream * stream,nsCString & str)742 void nsNntpIncomingServer::WriteLine(nsIOutputStream* stream, nsCString& str) {
743 uint32_t bytesWritten;
744 str.Append(MSG_LINEBREAK);
745 stream->Write(str.get(), str.Length(), &bytesWritten);
746 }
WriteHostInfoFile()747 nsresult nsNntpIncomingServer::WriteHostInfoFile() {
748 if (!mHostInfoHasChanged) return NS_OK;
749
750 mLastUpdatedTime = uint32_t(PR_Now() / PR_USEC_PER_SEC);
751
752 nsCString hostname;
753 nsresult rv = GetHostName(hostname);
754 NS_ENSURE_SUCCESS(rv, rv);
755
756 if (!mHostInfoFile) return NS_ERROR_UNEXPECTED;
757 nsCOMPtr<nsIOutputStream> hostInfoStream;
758 rv = MsgNewBufferedFileOutputStream(getter_AddRefs(hostInfoStream),
759 mHostInfoFile, -1, 00600);
760 NS_ENSURE_SUCCESS(rv, rv);
761
762 // XXX TODO: missing some formatting, see the 4.x code
763 nsAutoCString header("# News host information file.");
764 WriteLine(hostInfoStream, header);
765 header.AssignLiteral("# This is a generated file! Do not edit.");
766 WriteLine(hostInfoStream, header);
767 header.Truncate();
768 WriteLine(hostInfoStream, header);
769 nsAutoCString version("version=");
770 version.AppendInt(VALID_VERSION);
771 WriteLine(hostInfoStream, version);
772 nsAutoCString newsrcname("newsrcname=");
773 newsrcname.Append(hostname);
774 WriteLine(hostInfoStream, hostname);
775 nsAutoCString dateStr("lastgroupdate=");
776 dateStr.AppendInt(mLastUpdatedTime);
777 WriteLine(hostInfoStream, dateStr);
778 dateStr = "uniqueid=";
779 dateStr.AppendInt(mUniqueId);
780 WriteLine(hostInfoStream, dateStr);
781 header.Assign(MSG_LINEBREAK "begingroups");
782 WriteLine(hostInfoStream, header);
783
784 // XXX TODO: sort groups first?
785 uint32_t length = mGroupsOnServer.Length();
786 for (uint32_t i = 0; i < length; ++i) {
787 uint32_t bytesWritten;
788 hostInfoStream->Write(mGroupsOnServer[i].get(), mGroupsOnServer[i].Length(),
789 &bytesWritten);
790 hostInfoStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
791 }
792
793 hostInfoStream->Close();
794 mHostInfoHasChanged = false;
795 return NS_OK;
796 }
797
LoadHostInfoFile()798 nsresult nsNntpIncomingServer::LoadHostInfoFile() {
799 nsresult rv;
800 // we haven't loaded it yet
801 mHostInfoLoaded = false;
802
803 rv = GetLocalPath(getter_AddRefs(mHostInfoFile));
804 if (NS_FAILED(rv)) return rv;
805 if (!mHostInfoFile) return NS_ERROR_FAILURE;
806
807 rv = mHostInfoFile->AppendNative(nsLiteralCString(HOSTINFO_FILE_NAME));
808 if (NS_FAILED(rv)) return rv;
809
810 bool exists;
811 rv = mHostInfoFile->Exists(&exists);
812 if (NS_FAILED(rv)) return rv;
813
814 // it is ok if the hostinfo.dat file does not exist.
815 if (!exists) return NS_OK;
816
817 nsCOMPtr<nsIInputStream> fileStream;
818 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), mHostInfoFile);
819 NS_ENSURE_SUCCESS(rv, rv);
820
821 nsCOMPtr<nsILineInputStream> lineInputStream(
822 do_QueryInterface(fileStream, &rv));
823 NS_ENSURE_SUCCESS(rv, rv);
824
825 bool more = true;
826 nsCString line;
827
828 while (more && NS_SUCCEEDED(rv)) {
829 rv = lineInputStream->ReadLine(line, &more);
830 if (line.IsEmpty()) continue;
831 HandleLine(line.get(), line.Length());
832 }
833 mHasSeenBeginGroups = false;
834 fileStream->Close();
835
836 return UpdateSubscribed();
837 }
838
839 NS_IMETHODIMP
StartPopulatingWithUri(nsIMsgWindow * aMsgWindow,bool aForceToServer,const nsACString & uri)840 nsNntpIncomingServer::StartPopulatingWithUri(nsIMsgWindow* aMsgWindow,
841 bool aForceToServer,
842 const nsACString& uri) {
843 nsresult rv = EnsureInner();
844 NS_ENSURE_SUCCESS(rv, rv);
845 rv = mInner->StartPopulatingWithUri(aMsgWindow, aForceToServer, uri);
846 NS_ENSURE_SUCCESS(rv, rv);
847
848 rv = StopPopulating(mMsgWindow);
849 if (NS_FAILED(rv)) return rv;
850
851 return NS_OK;
852 }
853
854 NS_IMETHODIMP
SubscribeCleanup()855 nsNntpIncomingServer::SubscribeCleanup() {
856 nsresult rv = NS_OK;
857 rv = ClearInner();
858 NS_ENSURE_SUCCESS(rv, rv);
859 return NS_OK;
860 }
861
862 NS_IMETHODIMP
StartPopulating(nsIMsgWindow * aMsgWindow,bool aForceToServer,bool aGetOnlyNew)863 nsNntpIncomingServer::StartPopulating(nsIMsgWindow* aMsgWindow,
864 bool aForceToServer, bool aGetOnlyNew) {
865 mMsgWindow = aMsgWindow;
866
867 nsresult rv = EnsureInner();
868 NS_ENSURE_SUCCESS(rv, rv);
869
870 rv = mInner->StartPopulating(aMsgWindow, aForceToServer, aGetOnlyNew);
871 NS_ENSURE_SUCCESS(rv, rv);
872
873 rv = SetDelimiter(NEWS_DELIMITER);
874 if (NS_FAILED(rv)) return rv;
875
876 rv = SetShowFullName(true);
877 if (NS_FAILED(rv)) return rv;
878
879 nsCOMPtr<nsINntpService> nntpService =
880 do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
881 NS_ENSURE_SUCCESS(rv, rv);
882
883 mHostInfoLoaded = false;
884 mVersion = INVALID_VERSION;
885 mGroupsOnServer.Clear();
886 mGetOnlyNew = aGetOnlyNew;
887
888 if (!aForceToServer) {
889 rv = LoadHostInfoFile();
890 if (NS_FAILED(rv)) return rv;
891 }
892
893 // mHostInfoLoaded can be false if we failed to load anything
894 if (aForceToServer || !mHostInfoLoaded || (mVersion != VALID_VERSION)) {
895 // set these to true, so when we are done and we call WriteHostInfoFile()
896 // we'll write out to hostinfo.dat
897 mHostInfoHasChanged = true;
898 mVersion = VALID_VERSION;
899
900 mGroupsOnServer.Clear();
901 rv = nntpService->GetListOfGroupsOnServer(this, aMsgWindow, aGetOnlyNew);
902 if (NS_FAILED(rv)) return rv;
903 } else {
904 rv = StopPopulating(aMsgWindow);
905 if (NS_FAILED(rv)) return rv;
906 }
907
908 return NS_OK;
909 }
910
911 /**
912 * This method is the entry point for |nsNNTPProtocol| class. |aName| is now
913 * encoded in the serverside character encoding, but we need to handle
914 * newsgroup names in UTF-8 internally, So we convert |aName| to
915 * UTF-8 here for later use.
916 **/
917 NS_IMETHODIMP
AddNewsgroupToList(const char * aName)918 nsNntpIncomingServer::AddNewsgroupToList(const char* aName) {
919 nsresult rv;
920
921 nsAutoString newsgroupName;
922 nsAutoCString dataCharset;
923 rv = GetCharset(dataCharset);
924 NS_ENSURE_SUCCESS(rv, rv);
925
926 rv = nsMsgI18NConvertToUnicode(dataCharset, nsDependentCString(aName),
927 newsgroupName);
928 #ifdef DEBUG_jungshik
929 NS_ASSERTION(NS_SUCCEEDED(rv), "newsgroup name conversion failed");
930 #endif
931 if (NS_FAILED(rv)) {
932 CopyASCIItoUTF16(nsDependentCString(aName), newsgroupName);
933 }
934
935 rv = AddTo(NS_ConvertUTF16toUTF8(newsgroupName), false, true, true);
936 if (NS_FAILED(rv)) return rv;
937 return NS_OK;
938 }
939
940 NS_IMETHODIMP
SetIncomingServer(nsIMsgIncomingServer * aServer)941 nsNntpIncomingServer::SetIncomingServer(nsIMsgIncomingServer* aServer) {
942 nsresult rv = EnsureInner();
943 NS_ENSURE_SUCCESS(rv, rv);
944 return mInner->SetIncomingServer(aServer);
945 }
946
947 NS_IMETHODIMP
SetShowFullName(bool showFullName)948 nsNntpIncomingServer::SetShowFullName(bool showFullName) {
949 nsresult rv = EnsureInner();
950 NS_ENSURE_SUCCESS(rv, rv);
951 return mInner->SetShowFullName(showFullName);
952 }
953
ClearInner()954 nsresult nsNntpIncomingServer::ClearInner() {
955 nsresult rv = NS_OK;
956
957 if (mInner) {
958 rv = mInner->SetSubscribeListener(nullptr);
959 NS_ENSURE_SUCCESS(rv, rv);
960
961 rv = mInner->SetIncomingServer(nullptr);
962 NS_ENSURE_SUCCESS(rv, rv);
963
964 mInner = nullptr;
965 }
966 return NS_OK;
967 }
968
EnsureInner()969 nsresult nsNntpIncomingServer::EnsureInner() {
970 nsresult rv = NS_OK;
971
972 if (mInner) return NS_OK;
973
974 mInner = do_CreateInstance(kSubscribableServerCID, &rv);
975 NS_ENSURE_SUCCESS(rv, rv);
976 if (!mInner) return NS_ERROR_FAILURE;
977
978 rv = SetIncomingServer(this);
979 NS_ENSURE_SUCCESS(rv, rv);
980
981 return NS_OK;
982 }
983
984 NS_IMETHODIMP
GetDelimiter(char * aDelimiter)985 nsNntpIncomingServer::GetDelimiter(char* aDelimiter) {
986 nsresult rv = EnsureInner();
987 NS_ENSURE_SUCCESS(rv, rv);
988 return mInner->GetDelimiter(aDelimiter);
989 }
990
991 NS_IMETHODIMP
SetDelimiter(char aDelimiter)992 nsNntpIncomingServer::SetDelimiter(char aDelimiter) {
993 nsresult rv = EnsureInner();
994 NS_ENSURE_SUCCESS(rv, rv);
995 return mInner->SetDelimiter(aDelimiter);
996 }
997
998 NS_IMETHODIMP
SetAsSubscribed(const nsACString & path)999 nsNntpIncomingServer::SetAsSubscribed(const nsACString& path) {
1000 mTempSubscribed.AppendElement(path);
1001 if (mGetOnlyNew && (!mGroupsOnServer.Contains(path))) return NS_OK;
1002
1003 nsresult rv = EnsureInner();
1004 NS_ENSURE_SUCCESS(rv, rv);
1005 return mInner->SetAsSubscribed(path);
1006 }
1007
1008 NS_IMETHODIMP
UpdateSubscribed()1009 nsNntpIncomingServer::UpdateSubscribed() {
1010 nsresult rv = EnsureInner();
1011 NS_ENSURE_SUCCESS(rv, rv);
1012 mTempSubscribed.Clear();
1013 uint32_t length = mSubscribedNewsgroups.Length();
1014 for (uint32_t i = 0; i < length; ++i)
1015 SetAsSubscribed(mSubscribedNewsgroups[i]);
1016 return NS_OK;
1017 }
1018
1019 NS_IMETHODIMP
AddTo(const nsACString & aName,bool addAsSubscribed,bool aSubscribable,bool changeIfExists)1020 nsNntpIncomingServer::AddTo(const nsACString& aName, bool addAsSubscribed,
1021 bool aSubscribable, bool changeIfExists) {
1022 NS_ASSERTION(mozilla::IsUtf8(aName), "Non-UTF-8 newsgroup name");
1023 nsresult rv = EnsureInner();
1024 NS_ENSURE_SUCCESS(rv, rv);
1025
1026 rv = AddGroupOnServer(aName);
1027 NS_ENSURE_SUCCESS(rv, rv);
1028
1029 rv = mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
1030 NS_ENSURE_SUCCESS(rv, rv);
1031
1032 return rv;
1033 }
1034
1035 NS_IMETHODIMP
StopPopulating(nsIMsgWindow * aMsgWindow)1036 nsNntpIncomingServer::StopPopulating(nsIMsgWindow* aMsgWindow) {
1037 nsresult rv = NS_OK;
1038
1039 rv = EnsureInner();
1040 NS_ENSURE_SUCCESS(rv, rv);
1041 rv = mInner->StopPopulating(aMsgWindow);
1042 NS_ENSURE_SUCCESS(rv, rv);
1043
1044 if (!mGetOnlyNew && !mHostInfoLoaded) {
1045 rv = WriteHostInfoFile();
1046 NS_ENSURE_SUCCESS(rv, rv);
1047 }
1048
1049 // XXX TODO: when do I set this to null?
1050 // rv = ClearInner();
1051 // NS_ENSURE_SUCCESS(rv,rv);
1052 return NS_OK;
1053 }
1054
1055 NS_IMETHODIMP
SetSubscribeListener(nsISubscribeListener * aListener)1056 nsNntpIncomingServer::SetSubscribeListener(nsISubscribeListener* aListener) {
1057 nsresult rv = EnsureInner();
1058 NS_ENSURE_SUCCESS(rv, rv);
1059 return mInner->SetSubscribeListener(aListener);
1060 }
1061
1062 NS_IMETHODIMP
GetSubscribeListener(nsISubscribeListener ** aListener)1063 nsNntpIncomingServer::GetSubscribeListener(nsISubscribeListener** aListener) {
1064 nsresult rv = EnsureInner();
1065 NS_ENSURE_SUCCESS(rv, rv);
1066 return mInner->GetSubscribeListener(aListener);
1067 }
1068
1069 NS_IMETHODIMP
Subscribe(const char16_t * aUnicharName)1070 nsNntpIncomingServer::Subscribe(const char16_t* aUnicharName) {
1071 return SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(aUnicharName));
1072 }
1073
1074 NS_IMETHODIMP
Unsubscribe(const char16_t * aUnicharName)1075 nsNntpIncomingServer::Unsubscribe(const char16_t* aUnicharName) {
1076 NS_ENSURE_ARG_POINTER(aUnicharName);
1077
1078 nsresult rv;
1079
1080 nsCOMPtr<nsIMsgFolder> serverFolder;
1081 rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
1082 if (NS_FAILED(rv)) return rv;
1083 if (!serverFolder) return NS_ERROR_FAILURE;
1084
1085 nsCOMPtr<nsIMsgFolder> newsgroupFolder;
1086 rv = serverFolder->GetChildNamed(nsDependentString(aUnicharName),
1087 getter_AddRefs(newsgroupFolder));
1088 if (NS_FAILED(rv)) return rv;
1089 if (!newsgroupFolder) return NS_ERROR_FAILURE;
1090
1091 rv = serverFolder->PropagateDelete(newsgroupFolder, true /* delete storage */,
1092 nullptr);
1093 if (NS_FAILED(rv)) return rv;
1094
1095 // since we've unsubscribed to a newsgroup, the newsrc needs to be written out
1096 rv = SetNewsrcHasChanged(true);
1097 if (NS_FAILED(rv)) return rv;
1098
1099 return NS_OK;
1100 }
1101
HandleLine(const char * line,uint32_t line_size)1102 nsresult nsNntpIncomingServer::HandleLine(const char* line,
1103 uint32_t line_size) {
1104 NS_ASSERTION(line, "line is null");
1105 if (!line) return NS_OK;
1106
1107 // skip blank lines and comments
1108 if (line[0] == '#' || line[0] == '\0') return NS_OK;
1109 // XXX TODO: make this truly const, maybe pass in an nsCString &
1110
1111 if (mHasSeenBeginGroups) {
1112 // v1 hostinfo files had additional data fields delimited by commas.
1113 // with v2 hostinfo files, the additional data fields are removed.
1114 char* commaPos = (char*)PL_strchr(line, ',');
1115 if (commaPos) *commaPos = 0;
1116
1117 // newsrc entries are all in UTF-8
1118 #ifdef DEBUG_jungshik
1119 NS_ASSERTION(mozilla::IsUtf8(nsDependentCString(line)),
1120 "newsrc line is not utf-8");
1121 #endif
1122 nsresult rv = AddTo(nsDependentCString(line), false, true, true);
1123 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add line");
1124 if (NS_SUCCEEDED(rv)) {
1125 // since we've seen one group, we can claim we've loaded the
1126 // hostinfo file
1127 mHostInfoLoaded = true;
1128 }
1129 } else {
1130 if (PL_strncmp(line, "begingroups", 11) == 0) {
1131 mHasSeenBeginGroups = true;
1132 }
1133 char* equalPos = (char*)PL_strchr(line, '=');
1134 if (equalPos) {
1135 *equalPos++ = '\0';
1136 if (PL_strcmp(line, "lastgroupdate") == 0) {
1137 mLastUpdatedTime = strtoul(equalPos, nullptr, 10);
1138 } else if (PL_strcmp(line, "uniqueid") == 0) {
1139 mUniqueId = strtol(equalPos, nullptr, 16);
1140 } else if (PL_strcmp(line, "version") == 0) {
1141 mVersion = strtol(equalPos, nullptr, 16);
1142 }
1143 }
1144 }
1145
1146 return NS_OK;
1147 }
1148
AddGroupOnServer(const nsACString & aName)1149 nsresult nsNntpIncomingServer::AddGroupOnServer(const nsACString& aName) {
1150 mGroupsOnServer.AppendElement(aName);
1151 return NS_OK;
1152 }
1153
1154 NS_IMETHODIMP
AddNewsgroup(const nsAString & aName)1155 nsNntpIncomingServer::AddNewsgroup(const nsAString& aName) {
1156 // handle duplicates?
1157 mSubscribedNewsgroups.AppendElement(NS_ConvertUTF16toUTF8(aName));
1158 return NS_OK;
1159 }
1160
1161 NS_IMETHODIMP
RemoveNewsgroup(const nsAString & aName)1162 nsNntpIncomingServer::RemoveNewsgroup(const nsAString& aName) {
1163 // handle duplicates?
1164 mSubscribedNewsgroups.RemoveElement(NS_ConvertUTF16toUTF8(aName));
1165 return NS_OK;
1166 }
1167
1168 NS_IMETHODIMP
SetState(const nsACString & path,bool state,bool * stateChanged)1169 nsNntpIncomingServer::SetState(const nsACString& path, bool state,
1170 bool* stateChanged) {
1171 nsresult rv = EnsureInner();
1172 NS_ENSURE_SUCCESS(rv, rv);
1173
1174 rv = mInner->SetState(path, state, stateChanged);
1175 if (*stateChanged) {
1176 if (state)
1177 mTempSubscribed.AppendElement(path);
1178 else
1179 mTempSubscribed.RemoveElement(path);
1180 }
1181 return rv;
1182 }
1183
1184 NS_IMETHODIMP
HasChildren(const nsACString & path,bool * aHasChildren)1185 nsNntpIncomingServer::HasChildren(const nsACString& path, bool* aHasChildren) {
1186 nsresult rv = EnsureInner();
1187 NS_ENSURE_SUCCESS(rv, rv);
1188 return mInner->HasChildren(path, aHasChildren);
1189 }
1190
1191 NS_IMETHODIMP
IsSubscribed(const nsACString & path,bool * aIsSubscribed)1192 nsNntpIncomingServer::IsSubscribed(const nsACString& path,
1193 bool* aIsSubscribed) {
1194 nsresult rv = EnsureInner();
1195 NS_ENSURE_SUCCESS(rv, rv);
1196 return mInner->IsSubscribed(path, aIsSubscribed);
1197 }
1198
1199 NS_IMETHODIMP
IsSubscribable(const nsACString & path,bool * aIsSubscribable)1200 nsNntpIncomingServer::IsSubscribable(const nsACString& path,
1201 bool* aIsSubscribable) {
1202 nsresult rv = EnsureInner();
1203 NS_ENSURE_SUCCESS(rv, rv);
1204 return mInner->IsSubscribable(path, aIsSubscribable);
1205 }
1206
1207 NS_IMETHODIMP
GetLeafName(const nsACString & path,nsAString & aLeafName)1208 nsNntpIncomingServer::GetLeafName(const nsACString& path,
1209 nsAString& aLeafName) {
1210 nsresult rv = EnsureInner();
1211 NS_ENSURE_SUCCESS(rv, rv);
1212 return mInner->GetLeafName(path, aLeafName);
1213 }
1214
1215 NS_IMETHODIMP
GetFirstChildURI(const nsACString & path,nsACString & aResult)1216 nsNntpIncomingServer::GetFirstChildURI(const nsACString& path,
1217 nsACString& aResult) {
1218 nsresult rv = EnsureInner();
1219 NS_ENSURE_SUCCESS(rv, rv);
1220 return mInner->GetFirstChildURI(path, aResult);
1221 }
1222
1223 NS_IMETHODIMP
GetChildURIs(const nsACString & aPath,nsTArray<nsCString> & aResult)1224 nsNntpIncomingServer::GetChildURIs(const nsACString& aPath,
1225 nsTArray<nsCString>& aResult) {
1226 nsresult rv = EnsureInner();
1227 NS_ENSURE_SUCCESS(rv, rv);
1228 return mInner->GetChildURIs(aPath, aResult);
1229 }
1230
1231 NS_IMETHODIMP
CommitSubscribeChanges()1232 nsNntpIncomingServer::CommitSubscribeChanges() {
1233 // we force the newrc to be dirty, so it will get written out when
1234 // we call WriteNewsrcFile()
1235 nsresult rv = SetNewsrcHasChanged(true);
1236 NS_ENSURE_SUCCESS(rv, rv);
1237 return WriteNewsrcFile();
1238 }
1239
1240 NS_IMETHODIMP
ForgetPassword()1241 nsNntpIncomingServer::ForgetPassword() {
1242 // clear password of root folder (for the news account)
1243 nsCOMPtr<nsIMsgFolder> rootFolder;
1244 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
1245 NS_ENSURE_SUCCESS(rv, rv);
1246 if (!rootFolder) return NS_ERROR_FAILURE;
1247
1248 nsCOMPtr<nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
1249 NS_ENSURE_SUCCESS(rv, rv);
1250 if (!newsFolder) return NS_ERROR_FAILURE;
1251
1252 rv = newsFolder->ForgetAuthenticationCredentials();
1253 NS_ENSURE_SUCCESS(rv, rv);
1254
1255 // clear password of all child folders
1256 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
1257 rv = rootFolder->GetSubFolders(subFolders);
1258 NS_ENSURE_SUCCESS(rv, rv);
1259
1260 nsresult return_rv = NS_OK;
1261 for (nsIMsgFolder* child : subFolders) {
1262 newsFolder = do_QueryInterface(child, &rv);
1263 if (NS_SUCCEEDED(rv) && newsFolder) {
1264 rv = newsFolder->ForgetAuthenticationCredentials();
1265 if (NS_FAILED(rv)) return_rv = rv;
1266 } else {
1267 return_rv = NS_ERROR_FAILURE;
1268 }
1269 }
1270
1271 return return_rv;
1272 }
1273
1274 NS_IMETHODIMP
GetSupportsExtensions(bool * aSupportsExtensions)1275 nsNntpIncomingServer::GetSupportsExtensions(bool* aSupportsExtensions) {
1276 return NS_ERROR_NOT_IMPLEMENTED;
1277 }
1278
1279 NS_IMETHODIMP
SetSupportsExtensions(bool aSupportsExtensions)1280 nsNntpIncomingServer::SetSupportsExtensions(bool aSupportsExtensions) {
1281 return NS_ERROR_NOT_IMPLEMENTED;
1282 }
1283
1284 NS_IMETHODIMP
AddExtension(const char * extension)1285 nsNntpIncomingServer::AddExtension(const char* extension) {
1286 return NS_ERROR_NOT_IMPLEMENTED;
1287 }
1288
1289 NS_IMETHODIMP
QueryExtension(const char * extension,bool * result)1290 nsNntpIncomingServer::QueryExtension(const char* extension, bool* result) {
1291 #ifdef DEBUG_seth
1292 printf("no extension support yet\n");
1293 #endif
1294 *result = false;
1295 return NS_OK;
1296 }
1297
1298 NS_IMETHODIMP
GetPostingAllowed(bool * aPostingAllowed)1299 nsNntpIncomingServer::GetPostingAllowed(bool* aPostingAllowed) {
1300 *aPostingAllowed = mPostingAllowed;
1301 return NS_OK;
1302 }
1303
1304 NS_IMETHODIMP
SetPostingAllowed(bool aPostingAllowed)1305 nsNntpIncomingServer::SetPostingAllowed(bool aPostingAllowed) {
1306 mPostingAllowed = aPostingAllowed;
1307 return NS_OK;
1308 }
1309
1310 NS_IMETHODIMP
GetLastUpdatedTime(uint32_t * aLastUpdatedTime)1311 nsNntpIncomingServer::GetLastUpdatedTime(uint32_t* aLastUpdatedTime) {
1312 *aLastUpdatedTime = mLastUpdatedTime;
1313 return NS_OK;
1314 }
1315
1316 NS_IMETHODIMP
SetLastUpdatedTime(uint32_t aLastUpdatedTime)1317 nsNntpIncomingServer::SetLastUpdatedTime(uint32_t aLastUpdatedTime) {
1318 return NS_ERROR_NOT_IMPLEMENTED;
1319 }
1320
1321 NS_IMETHODIMP
AddPropertyForGet(const char * name,const char * value)1322 nsNntpIncomingServer::AddPropertyForGet(const char* name, const char* value) {
1323 return NS_ERROR_NOT_IMPLEMENTED;
1324 }
1325
1326 NS_IMETHODIMP
QueryPropertyForGet(const char * name,char ** value)1327 nsNntpIncomingServer::QueryPropertyForGet(const char* name, char** value) {
1328 return NS_ERROR_NOT_IMPLEMENTED;
1329 }
1330
1331 NS_IMETHODIMP
AddSearchableGroup(const nsAString & name)1332 nsNntpIncomingServer::AddSearchableGroup(const nsAString& name) {
1333 return NS_ERROR_NOT_IMPLEMENTED;
1334 }
1335
1336 NS_IMETHODIMP
QuerySearchableGroup(const nsAString & name,bool * result)1337 nsNntpIncomingServer::QuerySearchableGroup(const nsAString& name,
1338 bool* result) {
1339 return NS_ERROR_NOT_IMPLEMENTED;
1340 }
1341
1342 NS_IMETHODIMP
AddSearchableHeader(const char * name)1343 nsNntpIncomingServer::AddSearchableHeader(const char* name) {
1344 return NS_ERROR_NOT_IMPLEMENTED;
1345 }
1346
1347 NS_IMETHODIMP
QuerySearchableHeader(const char * name,bool * result)1348 nsNntpIncomingServer::QuerySearchableHeader(const char* name, bool* result) {
1349 return NS_ERROR_NOT_IMPLEMENTED;
1350 }
1351
1352 NS_IMETHODIMP
FindGroup(const nsACString & name,nsIMsgNewsFolder ** result)1353 nsNntpIncomingServer::FindGroup(const nsACString& name,
1354 nsIMsgNewsFolder** result) {
1355 NS_ENSURE_ARG_POINTER(result);
1356
1357 nsresult rv;
1358 nsCOMPtr<nsIMsgFolder> serverFolder;
1359 rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
1360 NS_ENSURE_SUCCESS(rv, rv);
1361
1362 if (!serverFolder) return NS_ERROR_FAILURE;
1363
1364 // Escape the name for using FindSubFolder
1365 nsAutoCString escapedName;
1366 rv = MsgEscapeString(name, nsINetUtil::ESCAPE_URL_PATH, escapedName);
1367 NS_ENSURE_SUCCESS(rv, rv);
1368
1369 nsCOMPtr<nsIMsgFolder> subFolder;
1370 rv = serverFolder->FindSubFolder(escapedName, getter_AddRefs(subFolder));
1371 NS_ENSURE_SUCCESS(rv, rv);
1372 if (!subFolder) return NS_ERROR_FAILURE;
1373
1374 rv = subFolder->QueryInterface(NS_GET_IID(nsIMsgNewsFolder), (void**)result);
1375 NS_ENSURE_SUCCESS(rv, rv);
1376 if (!*result) return NS_ERROR_FAILURE;
1377 return NS_OK;
1378 }
1379
1380 NS_IMETHODIMP
GetFirstGroupNeedingExtraInfo(nsACString & result)1381 nsNntpIncomingServer::GetFirstGroupNeedingExtraInfo(nsACString& result) {
1382 return NS_ERROR_NOT_IMPLEMENTED;
1383 }
1384
1385 NS_IMETHODIMP
SetGroupNeedsExtraInfo(const nsACString & name,bool needsExtraInfo)1386 nsNntpIncomingServer::SetGroupNeedsExtraInfo(const nsACString& name,
1387 bool needsExtraInfo) {
1388 return NS_ERROR_NOT_IMPLEMENTED;
1389 }
1390
1391 NS_IMETHODIMP
GroupNotFound(nsIMsgWindow * aMsgWindow,const nsAString & aName,bool aOpening)1392 nsNntpIncomingServer::GroupNotFound(nsIMsgWindow* aMsgWindow,
1393 const nsAString& aName, bool aOpening) {
1394 nsresult rv;
1395 nsCOMPtr<nsIPrompt> prompt;
1396
1397 if (aMsgWindow) {
1398 rv = aMsgWindow->GetPromptDialog(getter_AddRefs(prompt));
1399 NS_ASSERTION(NS_SUCCEEDED(rv), "no prompt from the msg window");
1400 }
1401
1402 if (!prompt) {
1403 nsCOMPtr<nsIWindowWatcher> wwatch(
1404 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1405 rv = wwatch->GetNewPrompter(nullptr, getter_AddRefs(prompt));
1406 NS_ENSURE_SUCCESS(rv, rv);
1407 }
1408
1409 nsCOMPtr<nsIStringBundleService> bundleService =
1410 mozilla::services::GetStringBundleService();
1411 NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
1412
1413 nsCOMPtr<nsIStringBundle> bundle;
1414 rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
1415 NS_ENSURE_SUCCESS(rv, rv);
1416
1417 nsCString hostname;
1418 rv = GetRealHostName(hostname);
1419 NS_ENSURE_SUCCESS(rv, rv);
1420
1421 nsString groupName(aName);
1422 AutoTArray<nsString, 2> formatStrings = {groupName};
1423 CopyUTF8toUTF16(hostname, *formatStrings.AppendElement());
1424 nsString confirmText;
1425 rv = bundle->FormatStringFromName("autoUnsubscribeText", formatStrings,
1426 confirmText);
1427 NS_ENSURE_SUCCESS(rv, rv);
1428
1429 bool confirmResult = false;
1430 rv = prompt->Confirm(nullptr, confirmText.get(), &confirmResult);
1431 NS_ENSURE_SUCCESS(rv, rv);
1432
1433 if (confirmResult) {
1434 rv = Unsubscribe(groupName.get());
1435 NS_ENSURE_SUCCESS(rv, rv);
1436 }
1437
1438 return rv;
1439 }
1440
1441 NS_IMETHODIMP
SetPrettyNameForGroup(const nsAString & name,const nsAString & prettyName)1442 nsNntpIncomingServer::SetPrettyNameForGroup(const nsAString& name,
1443 const nsAString& prettyName) {
1444 return NS_ERROR_NOT_IMPLEMENTED;
1445 }
1446
1447 NS_IMETHODIMP
GetCanCompactFoldersOnServer(bool * canCompactFoldersOnServer)1448 nsNntpIncomingServer::GetCanCompactFoldersOnServer(
1449 bool* canCompactFoldersOnServer) {
1450 NS_ENSURE_ARG_POINTER(canCompactFoldersOnServer);
1451 // Initialize canCompactFoldersOnServer false, a default value for NNTP
1452 *canCompactFoldersOnServer = false;
1453 return NS_OK;
1454 }
1455
1456 NS_IMETHODIMP
GetCanSearchMessages(bool * canSearchMessages)1457 nsNntpIncomingServer::GetCanSearchMessages(bool* canSearchMessages) {
1458 NS_ENSURE_ARG_POINTER(canSearchMessages);
1459 *canSearchMessages = true;
1460 return NS_OK;
1461 }
1462
1463 NS_IMETHODIMP
GetOfflineSupportLevel(int32_t * aSupportLevel)1464 nsNntpIncomingServer::GetOfflineSupportLevel(int32_t* aSupportLevel) {
1465 NS_ENSURE_ARG_POINTER(aSupportLevel);
1466 nsresult rv;
1467
1468 rv = GetIntValue("offline_support_level", aSupportLevel);
1469 if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv;
1470
1471 // set default value
1472 *aSupportLevel = OFFLINE_SUPPORT_LEVEL_EXTENDED;
1473 return NS_OK;
1474 }
1475
1476 NS_IMETHODIMP
GetDefaultCopiesAndFoldersPrefsToServer(bool * aCopiesAndFoldersOnServer)1477 nsNntpIncomingServer::GetDefaultCopiesAndFoldersPrefsToServer(
1478 bool* aCopiesAndFoldersOnServer) {
1479 NS_ENSURE_ARG_POINTER(aCopiesAndFoldersOnServer);
1480
1481 /**
1482 * When a news account is created, the copies and folder prefs for the
1483 * associated identity don't point to folders on the server.
1484 * This makes sense, since there is no "Drafts" folder on a news server.
1485 * They'll point to the ones on "Local Folders"
1486 */
1487
1488 *aCopiesAndFoldersOnServer = false;
1489 return NS_OK;
1490 }
1491
1492 NS_IMETHODIMP
GetCanCreateFoldersOnServer(bool * aCanCreateFoldersOnServer)1493 nsNntpIncomingServer::GetCanCreateFoldersOnServer(
1494 bool* aCanCreateFoldersOnServer) {
1495 NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
1496
1497 // No folder creation on news servers. Return false.
1498 *aCanCreateFoldersOnServer = false;
1499 return NS_OK;
1500 }
1501
1502 NS_IMETHODIMP
SetSearchValue(const nsAString & aSearchValue)1503 nsNntpIncomingServer::SetSearchValue(const nsAString& aSearchValue) {
1504 nsCString searchValue = NS_ConvertUTF16toUTF8(aSearchValue);
1505 searchValue.CompressWhitespace();
1506
1507 if (mTree) {
1508 mTree->BeginUpdateBatch();
1509 mTree->RowCountChanged(
1510 0, -static_cast<int32_t>(mSubscribeSearchResult.Length()));
1511 }
1512
1513 nsTArray<nsCString> searchStringParts;
1514 if (!searchValue.IsEmpty()) ParseString(searchValue, ' ', searchStringParts);
1515
1516 mSubscribeSearchResult.Clear();
1517 uint32_t length = mGroupsOnServer.Length();
1518 for (uint32_t i = 0; i < length; i++) {
1519 // check that all parts of the search string occur
1520 bool found = true;
1521 for (uint32_t j = 0; j < searchStringParts.Length(); ++j) {
1522 if (mGroupsOnServer[i].Find(searchStringParts[j], true, 0) == kNotFound) {
1523 found = false;
1524 break;
1525 }
1526 }
1527
1528 if (found) mSubscribeSearchResult.AppendElement(mGroupsOnServer[i]);
1529 }
1530
1531 nsCStringLowerCaseComparator comparator;
1532 mSubscribeSearchResult.Sort(comparator);
1533
1534 if (mTree) {
1535 mTree->RowCountChanged(0, mSubscribeSearchResult.Length());
1536 mTree->EndUpdateBatch();
1537 }
1538
1539 return NS_OK;
1540 }
1541
1542 NS_IMETHODIMP
GetSupportsSubscribeSearch(bool * retVal)1543 nsNntpIncomingServer::GetSupportsSubscribeSearch(bool* retVal) {
1544 *retVal = true;
1545 return NS_OK;
1546 }
1547
1548 NS_IMETHODIMP
GetFolderView(nsITreeView ** aView)1549 nsNntpIncomingServer::GetFolderView(nsITreeView** aView) {
1550 nsresult rv = EnsureInner();
1551 NS_ENSURE_SUCCESS(rv, rv);
1552 return mInner->GetFolderView(aView);
1553 }
1554
1555 NS_IMETHODIMP
GetRowCount(int32_t * aRowCount)1556 nsNntpIncomingServer::GetRowCount(int32_t* aRowCount) {
1557 *aRowCount = mSubscribeSearchResult.Length();
1558 return NS_OK;
1559 }
1560
1561 NS_IMETHODIMP
GetSelection(nsITreeSelection ** aSelection)1562 nsNntpIncomingServer::GetSelection(nsITreeSelection** aSelection) {
1563 NS_IF_ADDREF(*aSelection = mTreeSelection);
1564 return NS_OK;
1565 }
1566
1567 NS_IMETHODIMP
SetSelection(nsITreeSelection * aSelection)1568 nsNntpIncomingServer::SetSelection(nsITreeSelection* aSelection) {
1569 mTreeSelection = aSelection;
1570 return NS_OK;
1571 }
1572
1573 NS_IMETHODIMP
GetRowProperties(int32_t index,nsAString & properties)1574 nsNntpIncomingServer::GetRowProperties(int32_t index, nsAString& properties) {
1575 return NS_OK;
1576 }
1577
1578 NS_IMETHODIMP
GetCellProperties(int32_t row,nsTreeColumn * col,nsAString & properties)1579 nsNntpIncomingServer::GetCellProperties(int32_t row, nsTreeColumn* col,
1580 nsAString& properties) {
1581 if (!IsValidRow(row)) return NS_ERROR_UNEXPECTED;
1582
1583 NS_ENSURE_ARG_POINTER(col);
1584
1585 const nsAString& colID = col->GetId();
1586 if (colID.IsEmpty()) return NS_OK;
1587
1588 if (colID.First() == 's') {
1589 // if <name> is in our temporary list of subscribed groups
1590 // add the "subscribed-true" property so the check mark shows up
1591 // in the "subscribedColumn2"
1592 if (mSearchResultSortDescending)
1593 row = mSubscribeSearchResult.Length() - 1 - row;
1594 if (mTempSubscribed.Contains(mSubscribeSearchResult.ElementAt(row))) {
1595 properties.AssignLiteral("subscribed-true");
1596 }
1597 } else if (colID.First() == 'n') {
1598 // add the "serverType-nntp" property to the "nameColumn2"
1599 // so we get the news folder icon in the search view
1600 properties.AssignLiteral("serverType-nntp");
1601 }
1602 return NS_OK;
1603 }
1604
1605 NS_IMETHODIMP
GetColumnProperties(nsTreeColumn * col,nsAString & properties)1606 nsNntpIncomingServer::GetColumnProperties(nsTreeColumn* col,
1607 nsAString& properties) {
1608 return NS_OK;
1609 }
1610
1611 NS_IMETHODIMP
IsContainer(int32_t index,bool * _retval)1612 nsNntpIncomingServer::IsContainer(int32_t index, bool* _retval) {
1613 *_retval = false;
1614 return NS_OK;
1615 }
1616
1617 NS_IMETHODIMP
IsContainerOpen(int32_t index,bool * _retval)1618 nsNntpIncomingServer::IsContainerOpen(int32_t index, bool* _retval) {
1619 return NS_ERROR_NOT_IMPLEMENTED;
1620 }
1621
1622 NS_IMETHODIMP
IsContainerEmpty(int32_t index,bool * _retval)1623 nsNntpIncomingServer::IsContainerEmpty(int32_t index, bool* _retval) {
1624 return NS_ERROR_NOT_IMPLEMENTED;
1625 }
1626
1627 NS_IMETHODIMP
IsSeparator(int32_t index,bool * _retval)1628 nsNntpIncomingServer::IsSeparator(int32_t index, bool* _retval) {
1629 *_retval = false;
1630 return NS_OK;
1631 }
1632
1633 NS_IMETHODIMP
IsSorted(bool * _retval)1634 nsNntpIncomingServer::IsSorted(bool* _retval) {
1635 return NS_ERROR_NOT_IMPLEMENTED;
1636 }
1637
1638 NS_IMETHODIMP
CanDrop(int32_t index,int32_t orientation,mozilla::dom::DataTransfer * dataTransfer,bool * _retval)1639 nsNntpIncomingServer::CanDrop(int32_t index, int32_t orientation,
1640 mozilla::dom::DataTransfer* dataTransfer,
1641 bool* _retval) {
1642 return NS_ERROR_NOT_IMPLEMENTED;
1643 }
1644
1645 NS_IMETHODIMP
Drop(int32_t row,int32_t orientation,mozilla::dom::DataTransfer * dataTransfer)1646 nsNntpIncomingServer::Drop(int32_t row, int32_t orientation,
1647 mozilla::dom::DataTransfer* dataTransfer) {
1648 return NS_ERROR_NOT_IMPLEMENTED;
1649 }
1650
1651 NS_IMETHODIMP
GetParentIndex(int32_t rowIndex,int32_t * _retval)1652 nsNntpIncomingServer::GetParentIndex(int32_t rowIndex, int32_t* _retval) {
1653 return NS_ERROR_NOT_IMPLEMENTED;
1654 }
1655
1656 NS_IMETHODIMP
HasNextSibling(int32_t rowIndex,int32_t afterIndex,bool * _retval)1657 nsNntpIncomingServer::HasNextSibling(int32_t rowIndex, int32_t afterIndex,
1658 bool* _retval) {
1659 return NS_ERROR_NOT_IMPLEMENTED;
1660 }
1661
1662 NS_IMETHODIMP
GetLevel(int32_t index,int32_t * _retval)1663 nsNntpIncomingServer::GetLevel(int32_t index, int32_t* _retval) {
1664 *_retval = 0;
1665 return NS_OK;
1666 }
1667
IsValidRow(int32_t row)1668 bool nsNntpIncomingServer::IsValidRow(int32_t row) {
1669 return ((row >= 0) && (row < (int32_t)mSubscribeSearchResult.Length()));
1670 }
1671
1672 NS_IMETHODIMP
GetImageSrc(int32_t row,nsTreeColumn * col,nsAString & _retval)1673 nsNntpIncomingServer::GetImageSrc(int32_t row, nsTreeColumn* col,
1674 nsAString& _retval) {
1675 return NS_OK;
1676 }
1677
1678 NS_IMETHODIMP
GetCellValue(int32_t row,nsTreeColumn * col,nsAString & _retval)1679 nsNntpIncomingServer::GetCellValue(int32_t row, nsTreeColumn* col,
1680 nsAString& _retval) {
1681 if (!IsValidRow(row)) return NS_ERROR_UNEXPECTED;
1682
1683 NS_ENSURE_ARG_POINTER(col);
1684
1685 const nsAString& colID = col->GetId();
1686 nsresult rv = NS_OK;
1687 if (!colID.IsEmpty() && colID.First() == 'n') {
1688 nsAutoCString str;
1689 if (mSearchResultSortDescending)
1690 row = mSubscribeSearchResult.Length() - 1 - row;
1691 _retval.Assign(
1692 NS_ConvertASCIItoUTF16(mSubscribeSearchResult.ElementAt(row)));
1693 }
1694 return rv;
1695 }
1696
1697 NS_IMETHODIMP
GetCellText(int32_t row,nsTreeColumn * col,nsAString & _retval)1698 nsNntpIncomingServer::GetCellText(int32_t row, nsTreeColumn* col,
1699 nsAString& _retval) {
1700 if (!IsValidRow(row)) return NS_ERROR_UNEXPECTED;
1701
1702 NS_ENSURE_ARG_POINTER(col);
1703
1704 const nsAString& colID = col->GetId();
1705 nsresult rv = NS_OK;
1706 if (!colID.IsEmpty() && colID.First() == 'n') {
1707 nsAutoCString str;
1708 if (mSearchResultSortDescending)
1709 row = mSubscribeSearchResult.Length() - 1 - row;
1710 // some servers have newsgroup names that are non ASCII. we store
1711 // those as escaped. unescape here so the UI is consistent
1712 rv = NS_MsgDecodeUnescapeURLPath(mSubscribeSearchResult.ElementAt(row),
1713 _retval);
1714 }
1715 return rv;
1716 }
1717
1718 NS_IMETHODIMP
SetTree(mozilla::dom::XULTreeElement * tree)1719 nsNntpIncomingServer::SetTree(mozilla::dom::XULTreeElement* tree) {
1720 mTree = tree;
1721 if (!tree) return NS_OK;
1722
1723 RefPtr<nsTreeColumns> cols = tree->GetColumns();
1724 if (!cols) return NS_OK;
1725
1726 RefPtr<nsTreeColumn> col = cols->GetKeyColumn();
1727 if (!col) return NS_OK;
1728
1729 RefPtr<mozilla::dom::Element> element = col->Element();
1730 if (!element) return NS_OK;
1731
1732 nsAutoString dir;
1733 element->GetAttribute(u"sortDirection"_ns, dir);
1734 mSearchResultSortDescending = dir.EqualsLiteral("descending");
1735 return NS_OK;
1736 }
1737
1738 NS_IMETHODIMP
ToggleOpenState(int32_t index)1739 nsNntpIncomingServer::ToggleOpenState(int32_t index) {
1740 return NS_ERROR_NOT_IMPLEMENTED;
1741 }
1742
1743 NS_IMETHODIMP
CycleHeader(nsTreeColumn * col)1744 nsNntpIncomingServer::CycleHeader(nsTreeColumn* col) {
1745 NS_ENSURE_ARG_POINTER(col);
1746
1747 bool cycler = col->Cycler();
1748 if (!cycler) {
1749 constexpr auto dir = u"sortDirection"_ns;
1750 RefPtr<mozilla::dom::Element> element = col->Element();
1751 mSearchResultSortDescending = !mSearchResultSortDescending;
1752 mozilla::IgnoredErrorResult rv2;
1753 element->SetAttribute(
1754 dir, mSearchResultSortDescending ? u"descending"_ns : u"ascending"_ns,
1755 rv2);
1756 mTree->Invalidate();
1757 }
1758 return NS_OK;
1759 }
1760
1761 NS_IMETHODIMP
SelectionChangedXPCOM()1762 nsNntpIncomingServer::SelectionChangedXPCOM() {
1763 return NS_ERROR_NOT_IMPLEMENTED;
1764 }
1765
1766 NS_IMETHODIMP
CycleCell(int32_t row,nsTreeColumn * col)1767 nsNntpIncomingServer::CycleCell(int32_t row, nsTreeColumn* col) {
1768 return NS_OK;
1769 }
1770
1771 NS_IMETHODIMP
IsEditable(int32_t row,nsTreeColumn * col,bool * _retval)1772 nsNntpIncomingServer::IsEditable(int32_t row, nsTreeColumn* col,
1773 bool* _retval) {
1774 *_retval = false;
1775 return NS_OK;
1776 }
1777
1778 NS_IMETHODIMP
SetCellValue(int32_t row,nsTreeColumn * col,const nsAString & value)1779 nsNntpIncomingServer::SetCellValue(int32_t row, nsTreeColumn* col,
1780 const nsAString& value) {
1781 return NS_ERROR_NOT_IMPLEMENTED;
1782 }
1783
1784 NS_IMETHODIMP
SetCellText(int32_t row,nsTreeColumn * col,const nsAString & value)1785 nsNntpIncomingServer::SetCellText(int32_t row, nsTreeColumn* col,
1786 const nsAString& value) {
1787 return NS_ERROR_NOT_IMPLEMENTED;
1788 }
1789
1790 NS_IMETHODIMP
GetCanFileMessagesOnServer(bool * aCanFileMessagesOnServer)1791 nsNntpIncomingServer::GetCanFileMessagesOnServer(
1792 bool* aCanFileMessagesOnServer) {
1793 NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
1794
1795 // No folder creation on news servers. Return false.
1796 *aCanFileMessagesOnServer = false;
1797 return NS_OK;
1798 }
1799
1800 NS_IMETHODIMP
GetFilterScope(nsMsgSearchScopeValue * filterScope)1801 nsNntpIncomingServer::GetFilterScope(nsMsgSearchScopeValue* filterScope) {
1802 NS_ENSURE_ARG_POINTER(filterScope);
1803
1804 *filterScope = nsMsgSearchScope::newsFilter;
1805 return NS_OK;
1806 }
1807
1808 NS_IMETHODIMP
GetSearchScope(nsMsgSearchScopeValue * searchScope)1809 nsNntpIncomingServer::GetSearchScope(nsMsgSearchScopeValue* searchScope) {
1810 NS_ENSURE_ARG_POINTER(searchScope);
1811
1812 if (WeAreOffline()) {
1813 // This value is set to the localNewsBody scope to be compatible with
1814 // the legacy default value.
1815 *searchScope = nsMsgSearchScope::localNewsBody;
1816 } else {
1817 *searchScope = nsMsgSearchScope::news;
1818 }
1819 return NS_OK;
1820 }
1821
1822 NS_IMETHODIMP
GetSocketType(int32_t * aSocketType)1823 nsNntpIncomingServer::GetSocketType(int32_t* aSocketType) {
1824 NS_ENSURE_ARG_POINTER(aSocketType);
1825 if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED;
1826
1827 nsresult rv = mPrefBranch->GetIntPref("socketType", aSocketType);
1828 if (NS_FAILED(rv)) {
1829 if (!mDefPrefBranch) return NS_ERROR_NOT_INITIALIZED;
1830 rv = mDefPrefBranch->GetIntPref("socketType", aSocketType);
1831 if (NS_FAILED(rv)) *aSocketType = nsMsgSocketType::plain;
1832 }
1833
1834 // nsMsgIncomingServer::GetSocketType migrates old isSecure to socketType
1835 // style for mail. Unfortunately, a bug caused news socketType 0 to be stored
1836 // in the prefs even for isSecure true, so the migration wouldn't happen :(
1837
1838 // Now that we know the socket, make sure isSecure true + socketType 0
1839 // doesn't mix. Migrate if that's the case here.
1840 if (*aSocketType == nsMsgSocketType::plain) {
1841 bool isSecure = false;
1842 nsresult rv2 = mPrefBranch->GetBoolPref("isSecure", &isSecure);
1843 if (NS_SUCCEEDED(rv2) && isSecure) {
1844 *aSocketType = nsMsgSocketType::SSL;
1845 // Don't call virtual method in case overrides call GetSocketType.
1846 nsMsgIncomingServer::SetSocketType(*aSocketType);
1847 }
1848 }
1849 return rv;
1850 }
1851
1852 NS_IMETHODIMP
SetSocketType(int32_t aSocketType)1853 nsNntpIncomingServer::SetSocketType(int32_t aSocketType) {
1854 if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED;
1855 nsresult rv = nsMsgIncomingServer::SetSocketType(aSocketType);
1856 if (NS_SUCCEEDED(rv)) {
1857 bool isSecure = false;
1858 if (NS_SUCCEEDED(mPrefBranch->GetBoolPref("isSecure", &isSecure))) {
1859 // Must keep isSecure in sync since we migrate based on it... if it's set.
1860 rv = mPrefBranch->SetBoolPref("isSecure",
1861 aSocketType == nsMsgSocketType::SSL);
1862 NS_ENSURE_SUCCESS(rv, rv);
1863 }
1864 }
1865 return rv;
1866 }
1867
1868 NS_IMETHODIMP
OnUserOrHostNameChanged(const nsACString & oldName,const nsACString & newName,bool hostnameChanged)1869 nsNntpIncomingServer::OnUserOrHostNameChanged(const nsACString& oldName,
1870 const nsACString& newName,
1871 bool hostnameChanged) {
1872 nsresult rv;
1873 // 1. Do common things in the base class.
1874 rv = nsMsgIncomingServer::OnUserOrHostNameChanged(oldName, newName,
1875 hostnameChanged);
1876 NS_ENSURE_SUCCESS(rv, rv);
1877
1878 // 2. Remove file hostinfo.dat so that the new subscribe
1879 // list will be reloaded from the new server.
1880 nsCOMPtr<nsIFile> hostInfoFile;
1881 rv = GetLocalPath(getter_AddRefs(hostInfoFile));
1882 NS_ENSURE_SUCCESS(rv, rv);
1883 rv = hostInfoFile->AppendNative(nsLiteralCString(HOSTINFO_FILE_NAME));
1884 NS_ENSURE_SUCCESS(rv, rv);
1885 hostInfoFile->Remove(false);
1886
1887 // 3.Unsubscribe and then subscribe the existing groups to clean up the
1888 // article numbers
1889 // in the rc file (this is because the old and new servers may maintain
1890 // different numbers for the same articles if both servers handle the same
1891 // groups).
1892 nsCOMPtr<nsIMsgFolder> serverFolder;
1893 rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
1894 NS_ENSURE_SUCCESS(rv, rv);
1895
1896 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
1897 rv = serverFolder->GetSubFolders(subFolders);
1898 NS_ENSURE_SUCCESS(rv, rv);
1899
1900 // Prepare the group list
1901 nsTArray<nsString> groupList(subFolders.Length());
1902 for (nsIMsgFolder* newsgroupFolder : subFolders) {
1903 nsString folderName;
1904 rv = newsgroupFolder->GetName(folderName);
1905 NS_ENSURE_SUCCESS(rv, rv);
1906 groupList.AppendElement(folderName);
1907 }
1908
1909 // If nothing subscribed then we're done.
1910 if (groupList.IsEmpty()) return NS_OK;
1911
1912 // Now unsubscribe & subscribe.
1913 uint32_t i;
1914 uint32_t cnt = groupList.Length();
1915 nsAutoCString cname;
1916 for (i = 0; i < cnt; i++) {
1917 // unsubscribe.
1918 rv = Unsubscribe(groupList[i].get());
1919 NS_ENSURE_SUCCESS(rv, rv);
1920 }
1921
1922 for (i = 0; i < cnt; i++) {
1923 // subscribe.
1924 rv = SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(groupList[i]));
1925 NS_ENSURE_SUCCESS(rv, rv);
1926 }
1927
1928 // Force updating the rc file.
1929 return CommitSubscribeChanges();
1930 }
1931
1932 NS_IMETHODIMP
GetSortOrder(int32_t * aSortOrder)1933 nsNntpIncomingServer::GetSortOrder(int32_t* aSortOrder) {
1934 NS_ENSURE_ARG_POINTER(aSortOrder);
1935 *aSortOrder = 500000000;
1936 return NS_OK;
1937 }
1938