Back to index

lightning-sunbird  0.9+nobinonly
nsNntpIncomingServer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Seth Spitzer <sspitzer@netscape.com>
00024  *   David Bienvenu <bienvenu@nventure.com>
00025  *   Henrik Gemal <mozilla@gemal.dk>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsNntpIncomingServer.h"
00042 #include "nsXPIDLString.h"
00043 #include "nsIPrefBranch.h"
00044 #include "nsIPrefService.h"
00045 #include "nsIMsgNewsFolder.h"
00046 #include "nsIMsgFolder.h"
00047 #include "nsIFileSpec.h"
00048 #include "nsCOMPtr.h"
00049 #include "nsINntpService.h"
00050 #include "nsINNTPProtocol.h"
00051 #include "nsMsgNewsCID.h"
00052 #include "nsNNTPProtocol.h"
00053 #include "nsIDirectoryService.h"
00054 #include "nsAppDirectoryServiceDefs.h"
00055 #include "nsInt64.h"
00056 #include "nsMsgUtils.h"
00057 #include "nsIPrompt.h"
00058 #include "nsIStringBundle.h"
00059 #include "nntpCore.h"
00060 #include "nsIWindowWatcher.h"
00061 #include "nsITreeColumns.h"
00062 #include "nsIDOMElement.h"
00063 #include "nsMsgFolderFlags.h"
00064 #include "nsMsgI18N.h"
00065 #include "nsUnicharUtils.h"
00066 #include "nsEscape.h"
00067 #include "nsISupportsObsolete.h"
00068 
00069 #define INVALID_VERSION         0
00070 #define VALID_VERSION           1
00071 #define NEW_NEWS_DIR_NAME       "News"
00072 #define PREF_MAIL_NEWSRC_ROOT   "mail.newsrc_root"
00073 #define PREF_MAIL_NEWSRC_ROOT_REL "mail.newsrc_root-rel"
00074 #define PREF_MAILNEWS_VIEW_DEFAULT_CHARSET "mailnews.view_default_charset"
00075 #define HOSTINFO_FILE_NAME      "hostinfo.dat"
00076 
00077 #define NEWS_DELIMITER          '.'
00078 
00079 // this platform specific junk is so the newsrc filenames we create 
00080 // will resemble the migrated newsrc filenames.
00081 #if defined(XP_UNIX) || defined(XP_BEOS)
00082 #define NEWSRC_FILE_PREFIX "newsrc-"
00083 #define NEWSRC_FILE_SUFFIX ""
00084 #else
00085 #define NEWSRC_FILE_PREFIX ""
00086 #define NEWSRC_FILE_SUFFIX ".rc"
00087 #endif /* XP_UNIX || XP_BEOS */
00088 
00089 // ###tw  This really ought to be the most
00090 // efficient file reading size for the current
00091 // operating system.
00092 #define HOSTINFO_FILE_BUFFER_SIZE 1024
00093 
00094 static NS_DEFINE_CID(kSubscribableServerCID, NS_SUBSCRIBABLESERVER_CID);
00095 
00096 NS_IMPL_ADDREF_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
00097 NS_IMPL_RELEASE_INHERITED(nsNntpIncomingServer, nsMsgIncomingServer)
00098 
00099 NS_INTERFACE_MAP_BEGIN(nsNntpIncomingServer)
00100     NS_INTERFACE_MAP_ENTRY(nsINntpIncomingServer)
00101     NS_INTERFACE_MAP_ENTRY(nsIUrlListener)
00102     NS_INTERFACE_MAP_ENTRY(nsISubscribableServer)
00103     NS_INTERFACE_MAP_ENTRY(nsITreeView)
00104 NS_INTERFACE_MAP_END_INHERITING(nsMsgIncomingServer)
00105 
00106 nsNntpIncomingServer::nsNntpIncomingServer() : nsMsgLineBuffer(nsnull, PR_FALSE)
00107 {    
00108   mNewsrcHasChanged = PR_FALSE;
00109   mGroupsEnumerator = nsnull;
00110   NS_NewISupportsArray(getter_AddRefs(m_connectionCache));
00111 
00112   mHostInfoLoaded = PR_FALSE;
00113   mHostInfoHasChanged = PR_FALSE;
00114   mVersion = INVALID_VERSION;
00115 
00116   mHostInfoStream = nsnull;
00117 
00118   mLastGroupDate = 0;
00119   mUniqueId = 0;
00120   mHasSeenBeginGroups = PR_FALSE;
00121   mPostingAllowed = PR_FALSE;
00122   m_userAuthenticated = PR_FALSE;
00123   mLastUpdatedTime = 0;
00124 
00125   // these atoms are used for subscribe search
00126   mSubscribedAtom = do_GetAtom("subscribed");
00127   mNntpAtom = do_GetAtom("nntp");
00128 
00129   // we have server wide and per group filters
00130   m_canHaveFilters = PR_TRUE;
00131 
00132   SetupNewsrcSaveTimer();
00133 }
00134 
00135 nsNntpIncomingServer::~nsNntpIncomingServer()
00136 {
00137     nsresult rv;
00138 
00139     if (mGroupsEnumerator) {
00140         delete mGroupsEnumerator;
00141         mGroupsEnumerator = nsnull;
00142     }
00143 
00144     if (mNewsrcSaveTimer) {
00145         mNewsrcSaveTimer->Cancel();
00146         mNewsrcSaveTimer = nsnull;
00147     }
00148 
00149     if (mHostInfoStream) {
00150         mHostInfoStream->close();
00151         delete mHostInfoStream;
00152         mHostInfoStream = nsnull;
00153     }
00154 
00155     rv = ClearInner();
00156     NS_ASSERTION(NS_SUCCEEDED(rv), "ClearInner failed");
00157 
00158     rv = CloseCachedConnections();
00159     NS_ASSERTION(NS_SUCCEEDED(rv), "CloseCachedConnections failed");
00160 }
00161 
00162 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, NotifyOn, "notify.on")
00163 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, MarkOldRead, "mark_old_read")
00164 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, Abbreviate, "abbreviate")
00165 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, PushAuth, "always_authenticate")
00166 NS_IMPL_SERVERPREF_BOOL(nsNntpIncomingServer, SingleSignon, "singleSignon")
00167 NS_IMPL_SERVERPREF_INT(nsNntpIncomingServer, MaxArticles, "max_articles")
00168 
00169 NS_IMETHODIMP
00170 nsNntpIncomingServer::GetNewsrcFilePath(nsIFileSpec **aNewsrcFilePath)
00171 {
00172   nsresult rv;
00173   if (mNewsrcFilePath)
00174   {
00175     *aNewsrcFilePath = mNewsrcFilePath;
00176     NS_IF_ADDREF(*aNewsrcFilePath);
00177     return NS_OK;
00178   }
00179 
00180   rv = GetFileValue("newsrc.file", aNewsrcFilePath);
00181   if (NS_SUCCEEDED(rv) && *aNewsrcFilePath) 
00182   {
00183     mNewsrcFilePath = *aNewsrcFilePath;
00184     return rv;
00185   }
00186 
00187   rv = GetNewsrcRootPath(getter_AddRefs(mNewsrcFilePath));
00188   if (NS_FAILED(rv)) return rv;
00189 
00190   nsXPIDLCString hostname;
00191   rv = GetHostName(getter_Copies(hostname));
00192   if (NS_FAILED(rv)) return rv;
00193 
00194   // set the leaf name to "dummy", and then call MakeUnique with a suggested leaf name
00195   rv = mNewsrcFilePath->AppendRelativeUnixPath("dummy");
00196   if (NS_FAILED(rv)) return rv;
00197   nsCAutoString newsrcFileName(NEWSRC_FILE_PREFIX);
00198   newsrcFileName.Append(hostname);
00199   newsrcFileName.Append(NEWSRC_FILE_SUFFIX);
00200   rv = mNewsrcFilePath->MakeUniqueWithSuggestedName(newsrcFileName.get());
00201   if (NS_FAILED(rv)) return rv;
00202 
00203   rv = SetNewsrcFilePath(mNewsrcFilePath);
00204   if (NS_FAILED(rv)) return rv;
00205 
00206   *aNewsrcFilePath = mNewsrcFilePath;
00207   NS_ADDREF(*aNewsrcFilePath);
00208 
00209   return NS_OK;
00210 }     
00211 
00212 NS_IMETHODIMP
00213 nsNntpIncomingServer::SetNewsrcFilePath(nsIFileSpec *spec)
00214 {
00215     nsresult rv;
00216     if (!spec) {
00217       return NS_ERROR_FAILURE;
00218     }
00219 
00220     PRBool exists;
00221 
00222     rv = spec->Exists(&exists);
00223     if (!exists) {
00224       rv = spec->Touch();
00225       if (NS_FAILED(rv)) return rv;
00226     }
00227     return SetFileValue("newsrc.file", spec);
00228 }          
00229 
00230 NS_IMETHODIMP
00231 nsNntpIncomingServer::GetLocalStoreType(char **type)
00232 {
00233     NS_ENSURE_ARG_POINTER(type);
00234     *type = nsCRT::strdup("news");
00235     return NS_OK;
00236 }
00237 
00238 NS_IMETHODIMP
00239 nsNntpIncomingServer::SetNewsrcRootPath(nsIFileSpec *aNewsrcRootPath)
00240 {
00241     NS_ENSURE_ARG(aNewsrcRootPath);
00242     nsFileSpec spec;
00243 
00244     nsresult rv = aNewsrcRootPath->GetFileSpec(&spec);
00245     NS_ENSURE_SUCCESS(rv, rv);
00246     nsCOMPtr<nsILocalFile> localFile;
00247     NS_FileSpecToIFile(&spec, getter_AddRefs(localFile));
00248     if (!localFile) return NS_ERROR_FAILURE;
00249     
00250     return NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT, localFile);
00251 }
00252 
00253 NS_IMETHODIMP
00254 nsNntpIncomingServer::GetNewsrcRootPath(nsIFileSpec **aNewsrcRootPath)
00255 {
00256     NS_ENSURE_ARG_POINTER(aNewsrcRootPath);
00257     *aNewsrcRootPath = nsnull;
00258     
00259     PRBool havePref;
00260     nsCOMPtr<nsILocalFile> localFile;    
00261     nsresult rv = NS_GetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL,
00262                               PREF_MAIL_NEWSRC_ROOT,
00263                               NS_APP_NEWS_50_DIR,
00264                               havePref,
00265                               getter_AddRefs(localFile));
00266 
00267     NS_ENSURE_SUCCESS(rv, rv);
00268 
00269     PRBool exists;
00270     rv = localFile->Exists(&exists);
00271     if (NS_SUCCEEDED(rv) && !exists)
00272         rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775);
00273     NS_ENSURE_SUCCESS(rv, rv);
00274     
00275     // Make the resulting nsIFileSpec
00276     // TODO: Convert arg to nsILocalFile and avoid this
00277     nsCOMPtr<nsIFileSpec> outSpec;
00278     rv = NS_NewFileSpecFromIFile(localFile, getter_AddRefs(outSpec));
00279     NS_ENSURE_SUCCESS(rv, rv);
00280     
00281     if (!havePref || !exists) 
00282     {
00283         rv = NS_SetPersistentFile(PREF_MAIL_NEWSRC_ROOT_REL, PREF_MAIL_NEWSRC_ROOT, localFile);
00284         NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
00285     }
00286         
00287     NS_IF_ADDREF(*aNewsrcRootPath = outSpec);
00288     return rv;
00289 }
00290 
00291 /* static */ void nsNntpIncomingServer::OnNewsrcSaveTimer(nsITimer *timer, void *voidIncomingServer)
00292 {
00293        nsNntpIncomingServer *incomingServer = (nsNntpIncomingServer*)voidIncomingServer;
00294        incomingServer->WriteNewsrcFile();        
00295 }
00296 
00297 
00298 nsresult nsNntpIncomingServer::SetupNewsrcSaveTimer()
00299 {
00300        nsInt64 ms(300000);   // hard code, 5 minutes.
00301        //Convert biffDelay into milliseconds
00302        PRUint32 timeInMSUint32 = (PRUint32)ms;
00303        //Can't currently reset a timer when it's in the process of
00304        //calling Notify. So, just release the timer here and create a new one.
00305        if(mNewsrcSaveTimer)
00306        {
00307               mNewsrcSaveTimer->Cancel();
00308        }
00309     mNewsrcSaveTimer = do_CreateInstance("@mozilla.org/timer;1");
00310        mNewsrcSaveTimer->InitWithFuncCallback(OnNewsrcSaveTimer, (void*)this, timeInMSUint32, 
00311                                            nsITimer::TYPE_REPEATING_SLACK);
00312 
00313     return NS_OK;
00314 }
00315 
00316 NS_IMETHODIMP
00317 nsNntpIncomingServer::SetCharset(const nsACString & aCharset)
00318 {
00319        nsresult rv;
00320        rv = SetCharValue("charset", PromiseFlatCString(aCharset).get());
00321        return rv;
00322 }
00323 
00324 NS_IMETHODIMP
00325 nsNntpIncomingServer::GetCharset(nsACString & aCharset)
00326 {
00327     nsresult rv; 
00328     nsXPIDLCString serverCharset;
00329     //first we get the per-server settings mail.server.<serverkey>.charset
00330     rv = GetCharValue("charset",getter_Copies(serverCharset));
00331 
00332     //if the per-server setting is empty,we get the default charset from 
00333     //mailnews.view_default_charset setting and set it as per-server preference.
00334     if(serverCharset.IsEmpty()){
00335         nsXPIDLString defaultCharset;
00336         rv = NS_GetLocalizedUnicharPreferenceWithDefault(nsnull,
00337              PREF_MAILNEWS_VIEW_DEFAULT_CHARSET,
00338              NS_LITERAL_STRING("ISO-8859-1"), defaultCharset);
00339         LossyCopyUTF16toASCII(defaultCharset,serverCharset);
00340         SetCharset(serverCharset);
00341     }
00342 #ifdef DEBUG_holywen
00343         printf("default charset for the server is %s\n", 
00344                (const char *)serverCharset);
00345 #endif
00346     aCharset = serverCharset;
00347     return NS_OK;
00348 }
00349 
00350 NS_IMETHODIMP
00351 nsNntpIncomingServer::WriteNewsrcFile()
00352 {
00353     nsresult rv;
00354 
00355     PRBool newsrcHasChanged;
00356     rv = GetNewsrcHasChanged(&newsrcHasChanged);
00357     if (NS_FAILED(rv)) return rv;
00358 
00359 #ifdef DEBUG_NEWS
00360        nsXPIDLCString hostname;
00361        rv = GetHostName(getter_Copies(hostname));
00362        if (NS_FAILED(rv)) return rv;
00363 #endif /* DEBUG_NEWS */
00364 
00365     if (newsrcHasChanged) {        
00366 #ifdef DEBUG_NEWS
00367         printf("write newsrc file for %s\n", (const char *)hostname);
00368 #endif
00369         nsCOMPtr <nsIFileSpec> newsrcFile;
00370         rv = GetNewsrcFilePath(getter_AddRefs(newsrcFile));
00371            if (NS_FAILED(rv)) return rv;
00372 
00373         nsFileSpec newsrcFileSpec;
00374         rv = newsrcFile->GetFileSpec(&newsrcFileSpec);
00375         if (NS_FAILED(rv)) return rv;
00376 
00377         nsIOFileStream newsrcStream(newsrcFileSpec, (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE));
00378 
00379         nsCOMPtr<nsIEnumerator> subFolders;
00380         nsCOMPtr<nsIMsgFolder> rootFolder;
00381         rv = GetRootFolder(getter_AddRefs(rootFolder));
00382         if (NS_FAILED(rv)) return rv;
00383 
00384         nsCOMPtr <nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
00385         if (NS_FAILED(rv)) return rv;
00386 
00387         nsXPIDLCString optionLines;
00388         rv = newsFolder->GetOptionLines(getter_Copies(optionLines));
00389         if (NS_SUCCEEDED(rv) && ((const char *)optionLines)) {
00390                newsrcStream << (const char *)optionLines;
00391 #ifdef DEBUG_NEWS
00392                printf("option lines:\n%s",(const char *)optionLines);
00393 #endif /* DEBUG_NEWS */
00394         }
00395 #ifdef DEBUG_NEWS
00396         else {
00397             printf("no option lines to write out\n");
00398         }
00399 #endif /* DEBUG_NEWS */
00400 
00401         nsXPIDLCString unsubscribedLines;
00402         rv = newsFolder->GetUnsubscribedNewsgroupLines(getter_Copies(unsubscribedLines));
00403         if (NS_SUCCEEDED(rv) && ((const char *)unsubscribedLines)) {
00404                newsrcStream << (const char *)unsubscribedLines;
00405 #ifdef DEBUG_NEWS
00406                printf("unsubscribedLines:\n%s",(const char *)unsubscribedLines);
00407 #endif /* DEBUG_NEWS */
00408         }
00409 #ifdef DEBUG_NEWS
00410         else {
00411             printf("no unsubscribed lines to write out\n");
00412         } 
00413 #endif /* DEBUG_NEWS */
00414 
00415         rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
00416         if (NS_FAILED(rv)) return rv;
00417 
00418         nsAdapterEnumerator *simpleEnumerator = new nsAdapterEnumerator(subFolders);
00419         if (simpleEnumerator == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00420 
00421         PRBool moreFolders;
00422         
00423         while (NS_SUCCEEDED(simpleEnumerator->HasMoreElements(&moreFolders)) && moreFolders) {
00424             nsCOMPtr<nsISupports> child;
00425             rv = simpleEnumerator->GetNext(getter_AddRefs(child));
00426             if (NS_SUCCEEDED(rv) && child) {
00427                 newsFolder = do_QueryInterface(child, &rv);
00428                 if (NS_SUCCEEDED(rv) && newsFolder) {
00429                     nsXPIDLCString newsrcLine;
00430                     rv = newsFolder->GetNewsrcLine(getter_Copies(newsrcLine));
00431                     if (NS_SUCCEEDED(rv) && ((const char *)newsrcLine)) {
00432                         // write the line to the newsrc file
00433                         newsrcStream << (const char *)newsrcLine;
00434                     }
00435                 }
00436             }
00437         }
00438         delete simpleEnumerator;
00439 
00440         newsrcStream.close();
00441         
00442         rv = SetNewsrcHasChanged(PR_FALSE);
00443               if (NS_FAILED(rv)) return rv;
00444     }
00445 #ifdef DEBUG_NEWS
00446     else {
00447         printf("no need to write newsrc file for %s, it was not dirty\n", (const char *)hostname);
00448     }
00449 #endif /* DEBUG_NEWS */
00450 
00451     return NS_OK;
00452 }
00453 
00454 NS_IMETHODIMP
00455 nsNntpIncomingServer::SetNewsrcHasChanged(PRBool aNewsrcHasChanged)
00456 {
00457     mNewsrcHasChanged = aNewsrcHasChanged;
00458     return NS_OK;
00459 }
00460 
00461 NS_IMETHODIMP
00462 nsNntpIncomingServer::GetNewsrcHasChanged(PRBool *aNewsrcHasChanged)
00463 {
00464     if (!aNewsrcHasChanged) return NS_ERROR_NULL_POINTER;
00465 
00466     *aNewsrcHasChanged = mNewsrcHasChanged;
00467     return NS_OK;
00468 }
00469 
00470 NS_IMPL_GETSET(nsNntpIncomingServer, UserAuthenticated, PRBool, m_userAuthenticated)
00471 
00472 NS_IMETHODIMP
00473 nsNntpIncomingServer::CloseCachedConnections()
00474 {
00475   nsresult rv;
00476   PRUint32 cnt;
00477   nsCOMPtr<nsINNTPProtocol> connection;
00478 
00479   // iterate through the connection cache and close the connections.
00480   if (m_connectionCache)
00481   {
00482     rv = m_connectionCache->Count(&cnt);
00483     if (NS_FAILED(rv)) return rv;
00484     for (PRUint32 i = 0; i < cnt; i++) 
00485          {
00486       connection = do_QueryElementAt(m_connectionCache, 0);
00487                 if (connection)
00488       {
00489          rv = connection->CloseConnection();
00490         RemoveConnection(connection);
00491       }
00492          }
00493   }
00494   rv = WriteNewsrcFile();
00495   if (NS_FAILED(rv)) return rv;
00496 
00497   rv = WriteHostInfoFile(); 
00498   if (NS_FAILED(rv)) return rv;
00499        
00500   return NS_OK;
00501 }
00502 
00503 NS_IMPL_SERVERPREF_INT(nsNntpIncomingServer, MaximumConnectionsNumber,
00504                        "max_cached_connections")
00505 
00506 PRBool
00507 nsNntpIncomingServer::ConnectionTimeOut(nsINNTPProtocol* aConnection)
00508 {
00509     PRBool retVal = PR_FALSE;
00510     if (!aConnection) return retVal;
00511     nsresult rv;
00512 
00513     PRTime cacheTimeoutLimits;
00514 
00515     LL_I2L(cacheTimeoutLimits, 170 * 1000000); // 170 seconds in microseconds
00516     PRTime lastActiveTimeStamp;
00517     rv = aConnection->GetLastActiveTimeStamp(&lastActiveTimeStamp);
00518 
00519     PRTime elapsedTime;
00520     LL_SUB(elapsedTime, PR_Now(), lastActiveTimeStamp);
00521     PRTime t;
00522     LL_SUB(t, elapsedTime, cacheTimeoutLimits);
00523     if (LL_GE_ZERO(t))
00524     {
00525 #ifdef DEBUG_seth
00526       printf("XXX connection timed out, close it, and remove it from the connection cache\n");
00527 #endif
00528       aConnection->CloseConnection();
00529             m_connectionCache->RemoveElement(aConnection);
00530             retVal = PR_TRUE;
00531     }
00532     return retVal;
00533 }
00534 
00535 
00536 nsresult
00537 nsNntpIncomingServer::CreateProtocolInstance(nsINNTPProtocol ** aNntpConnection, nsIURI *url,
00538                                              nsIMsgWindow *aMsgWindow)
00539 {
00540   // create a new connection and add it to the connection cache
00541   // we may need to flag the protocol connection as busy so we don't get
00542   // a race 
00543   // condition where someone else goes through this code 
00544   nsNNTPProtocol * protocolInstance = new nsNNTPProtocol(url, aMsgWindow);
00545   if (!protocolInstance)
00546     return NS_ERROR_OUT_OF_MEMORY;
00547 
00548   nsresult rv = protocolInstance->QueryInterface(NS_GET_IID(nsINNTPProtocol), (void **) aNntpConnection);
00549   // take the protocol instance and add it to the connectionCache
00550   if (NS_SUCCEEDED(rv) && *aNntpConnection)
00551     m_connectionCache->AppendElement(*aNntpConnection);
00552   return rv;
00553 }
00554 
00555 /* By default, allow the user to open at most this many connections to one news host */
00556 #define kMaxConnectionsPerHost 2
00557 
00558 NS_IMETHODIMP
00559 nsNntpIncomingServer::GetNntpConnection(nsIURI * aUri, nsIMsgWindow *aMsgWindow,
00560                                            nsINNTPProtocol ** aNntpConnection)
00561 {
00562   nsresult rv = NS_OK;
00563   nsCOMPtr<nsINNTPProtocol> connection;
00564   nsCOMPtr<nsINNTPProtocol> freeConnection;
00565   PRBool isBusy = PR_TRUE;
00566 
00567 
00568   PRInt32 maxConnections = kMaxConnectionsPerHost; 
00569   rv = GetMaximumConnectionsNumber(&maxConnections);
00570   if (NS_FAILED(rv) || maxConnections == 0)
00571   {
00572     maxConnections = kMaxConnectionsPerHost;
00573     rv = SetMaximumConnectionsNumber(maxConnections);
00574   }
00575   else if (maxConnections < 1)
00576   {   // forced to use at least 1
00577     maxConnections = 1;
00578     rv = SetMaximumConnectionsNumber(maxConnections);
00579   }
00580 
00581   *aNntpConnection = nsnull;
00582   // iterate through the connection cache for a connection that can handle this url.
00583   PRUint32 cnt;
00584 
00585   rv = m_connectionCache->Count(&cnt);
00586   if (NS_FAILED(rv)) return rv;
00587 #ifdef DEBUG_seth
00588   printf("XXX there are %d nntp connections in the conn cache.\n", (int)cnt);
00589 #endif
00590   for (PRUint32 i = 0; i < cnt && isBusy; i++) 
00591   {
00592     connection = do_QueryElementAt(m_connectionCache, i);
00593     if (connection)
00594         rv = connection->GetIsBusy(&isBusy);
00595     if (NS_FAILED(rv)) 
00596     {
00597         connection = nsnull;
00598         continue;
00599     }
00600     if (!freeConnection && !isBusy && connection)
00601     {
00602        freeConnection = connection;
00603     }
00604   }
00605     
00606   if (ConnectionTimeOut(freeConnection))
00607       freeConnection = nsnull;
00608 
00609   // if we got here and we have a connection, then we should return it!
00610   if (!isBusy && freeConnection)
00611   {
00612     *aNntpConnection = freeConnection;
00613     freeConnection->SetIsCachedConnection(PR_TRUE);
00614     NS_IF_ADDREF(*aNntpConnection);
00615   }
00616   else // have no queueing mechanism - just create the protocol instance.
00617   {
00618     rv = CreateProtocolInstance(aNntpConnection, aUri, aMsgWindow);
00619   }
00620   return rv;
00621 }
00622 
00623 
00624 /* void RemoveConnection (in nsINNTPProtocol aNntpConnection); */
00625 NS_IMETHODIMP nsNntpIncomingServer::RemoveConnection(nsINNTPProtocol *aNntpConnection)
00626 {
00627     if (aNntpConnection)
00628         m_connectionCache->RemoveElement(aNntpConnection);
00629 
00630     return NS_OK;
00631 }
00632 
00633 
00634 NS_IMETHODIMP 
00635 nsNntpIncomingServer::PerformExpand(nsIMsgWindow *aMsgWindow)
00636 {
00637   // Get news.update_unread_on_expand pref
00638   nsresult rv; 
00639   PRBool updateUnreadOnExpand = PR_TRUE;
00640   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00641   if NS_SUCCEEDED(rv)
00642     prefBranch->GetBoolPref("news.update_unread_on_expand", &updateUnreadOnExpand);
00643   
00644   // Only if news.update_unread_on_expand is true do we update the unread counts
00645   if (updateUnreadOnExpand) 
00646   {
00647     // a user might have a new server without any groups.
00648     // if so, bail out.  no need to establish a connection to the server
00649     PRInt32 numGroups = 0;
00650     rv = GetNumGroupsNeedingCounts(&numGroups);
00651     NS_ENSURE_SUCCESS(rv,rv);
00652     
00653     if (!numGroups)
00654       return NS_OK;
00655     
00656     nsCOMPtr<nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
00657     NS_ENSURE_SUCCESS(rv,rv);
00658   
00659     rv = nntpService->UpdateCounts(this, aMsgWindow);
00660     NS_ENSURE_SUCCESS(rv,rv);
00661   }
00662   return NS_OK;
00663 }
00664 
00665 NS_IMETHODIMP 
00666 nsNntpIncomingServer::GetNumGroupsNeedingCounts(PRInt32 *aNumGroupsNeedingCounts)
00667 {
00668     nsCOMPtr<nsIEnumerator> subFolders;
00669     nsCOMPtr<nsIMsgFolder> rootFolder;
00670  
00671     nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
00672     if (NS_FAILED(rv)) return rv;
00673 
00674     PRBool hasSubFolders = PR_FALSE;
00675     rv = rootFolder->GetHasSubFolders(&hasSubFolders);
00676     if (NS_FAILED(rv)) return rv;
00677 
00678     if (!hasSubFolders) {
00679         *aNumGroupsNeedingCounts = 0;
00680         return NS_OK;
00681     }
00682 
00683     rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
00684     if (NS_FAILED(rv)) return rv;
00685 
00686     if (mGroupsEnumerator) {
00687         delete mGroupsEnumerator;
00688         mGroupsEnumerator = nsnull;
00689     }
00690     mGroupsEnumerator = new nsAdapterEnumerator(subFolders);
00691     if (mGroupsEnumerator == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00692 
00693        PRUint32 count = 0;
00694        rv = rootFolder->Count(&count);
00695     if (NS_FAILED(rv)) return rv;
00696               
00697        *aNumGroupsNeedingCounts = (PRInt32) count;
00698        return NS_OK;
00699 }
00700 
00701 NS_IMETHODIMP
00702 nsNntpIncomingServer::GetFirstGroupNeedingCounts(nsISupports **aFirstGroupNeedingCounts)
00703 {
00704   nsresult rv;
00705   
00706   if (!aFirstGroupNeedingCounts) return NS_ERROR_NULL_POINTER;
00707   
00708   PRBool moreFolders;
00709   if (!mGroupsEnumerator) return NS_ERROR_FAILURE;
00710   
00711   rv = mGroupsEnumerator->HasMoreElements(&moreFolders);
00712   if (NS_FAILED(rv)) return rv;
00713   
00714   if (!moreFolders) 
00715   {
00716     *aFirstGroupNeedingCounts = nsnull;
00717     delete mGroupsEnumerator;
00718     mGroupsEnumerator = nsnull;
00719     return NS_OK; // this is not an error - it just means we reached the end of the groups.
00720   }
00721   
00722   do 
00723   {
00724     rv = mGroupsEnumerator->GetNext(aFirstGroupNeedingCounts);
00725     if (NS_FAILED(rv)) return rv;
00726     if (!*aFirstGroupNeedingCounts) return NS_ERROR_FAILURE;
00727     nsCOMPtr <nsIMsgFolder> folder;
00728     (*aFirstGroupNeedingCounts)->QueryInterface(NS_GET_IID(nsIMsgFolder), getter_AddRefs(folder));
00729     PRUint32 folderFlags;
00730     folder->GetFlags(&folderFlags);
00731     if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL)
00732       continue;
00733     else
00734       break;
00735   }
00736   while (PR_TRUE);
00737   return NS_OK;
00738 }
00739 
00740 NS_IMETHODIMP
00741 nsNntpIncomingServer::DisplaySubscribedGroup(nsIMsgNewsFolder *aMsgFolder, PRInt32 aFirstMessage, PRInt32 aLastMessage, PRInt32 aTotalMessages)
00742 {
00743        nsresult rv;
00744 
00745        if (!aMsgFolder) return NS_ERROR_NULL_POINTER;
00746 #ifdef DEBUG_NEWS
00747        printf("DisplaySubscribedGroup(...,%ld,%ld,%ld)\n",aFirstMessage,aLastMessage,aTotalMessages);
00748 #endif
00749        rv = aMsgFolder->UpdateSummaryFromNNTPInfo(aFirstMessage,aLastMessage,aTotalMessages);
00750        return rv;
00751 }
00752 
00753 NS_IMETHODIMP
00754 nsNntpIncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow)
00755 {
00756 #ifdef DEBUG_NEWS
00757        printf("PerformBiff for nntp\n");
00758 #endif
00759        return PerformExpand(nsnull);
00760 }
00761 
00762 NS_IMETHODIMP nsNntpIncomingServer::GetServerRequiresPasswordForBiff(PRBool *aServerRequiresPasswordForBiff)
00763 {
00764   NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
00765        *aServerRequiresPasswordForBiff = PR_FALSE;  // for news, biff is getting the unread counts
00766        return NS_OK;
00767 }
00768 
00769 NS_IMETHODIMP
00770 nsNntpIncomingServer::OnStartRunningUrl(nsIURI *url)
00771 {
00772        return NS_OK;
00773 }
00774 
00775 NS_IMETHODIMP
00776 nsNntpIncomingServer::OnStopRunningUrl(nsIURI *url, nsresult exitCode)
00777 {
00778        nsresult rv;
00779        rv = UpdateSubscribed();
00780        if (NS_FAILED(rv)) return rv;
00781 
00782        rv = StopPopulating(mMsgWindow);
00783        if (NS_FAILED(rv)) return rv;
00784 
00785        return NS_OK;
00786 }
00787 
00788 
00789 PRBool
00790 checkIfSubscribedFunction(nsCString &aElement, void *aData)
00791 {
00792     if (aElement.Equals(*NS_STATIC_CAST(nsACString *, aData))) {
00793         return PR_FALSE;
00794     }
00795     else {
00796         return PR_TRUE;
00797     }
00798 }
00799 
00800 
00801 NS_IMETHODIMP
00802 nsNntpIncomingServer::ContainsNewsgroup(const nsACString &name,
00803                                         PRBool *containsGroup)
00804 {
00805     if (name.IsEmpty()) return NS_ERROR_FAILURE;
00806     nsCAutoString unescapedName;
00807     NS_UnescapeURL(PromiseFlatCString(name), 
00808                    esc_FileBaseName|esc_Forced|esc_AlwaysCopy, unescapedName);
00809 
00810     *containsGroup = !(mSubscribedNewsgroups.EnumerateForwards(
00811                        nsCStringArrayEnumFunc(checkIfSubscribedFunction),
00812                        (void *) &unescapedName));
00813     return NS_OK;
00814 }
00815 
00816 NS_IMETHODIMP
00817 nsNntpIncomingServer::SubscribeToNewsgroup(const nsACString &aName)
00818 {
00819     NS_ASSERTION(!aName.IsEmpty(), "no name");
00820     if (aName.IsEmpty()) return NS_ERROR_FAILURE;
00821 
00822     nsCOMPtr<nsIMsgFolder> msgfolder;
00823     nsresult rv = GetRootMsgFolder(getter_AddRefs(msgfolder));
00824     if (NS_FAILED(rv)) return rv;
00825     if (!msgfolder) return NS_ERROR_FAILURE;
00826 
00827     rv = msgfolder->CreateSubfolder(NS_ConvertUTF8toUTF16(aName).get(), nsnull);
00828     if (NS_FAILED(rv)) return rv;
00829 
00830     return NS_OK;
00831 }
00832 
00833 PRBool
00834 writeGroupToHostInfoFile(nsCString &aElement, void *aData)
00835 {
00836     nsIOFileStream *stream;
00837     stream = (nsIOFileStream *)aData;
00838     NS_ASSERTION(stream, "no stream");
00839     if (!stream) {
00840         // stop, something is bad.
00841         return PR_FALSE;
00842     }
00843 
00844     // XXX todo ",,1,0,0" is a temporary hack, fix it
00845     *stream << aElement.get() << ",,1,0,0" << MSG_LINEBREAK;
00846     return PR_TRUE;
00847 }
00848 
00849 nsresult
00850 nsNntpIncomingServer::WriteHostInfoFile()
00851 {
00852     nsresult rv = NS_OK;
00853 
00854     if (!mHostInfoHasChanged) {
00855         return NS_OK;
00856     }
00857 
00858     PRInt32 firstnewdate;
00859 
00860     LL_L2I(firstnewdate, mFirstNewDate);
00861 
00862     nsXPIDLCString hostname;
00863     rv = GetHostName(getter_Copies(hostname));
00864     NS_ENSURE_SUCCESS(rv,rv);
00865     
00866     nsFileSpec hostinfoFileSpec;
00867 
00868     if (!mHostInfoFile) 
00869         return NS_ERROR_UNEXPECTED;
00870 
00871     rv = mHostInfoFile->GetFileSpec(&hostinfoFileSpec);
00872     NS_ENSURE_SUCCESS(rv,rv);
00873 
00874     if (mHostInfoStream) {
00875         mHostInfoStream->close();
00876         delete mHostInfoStream;
00877     }
00878 
00879     mHostInfoStream = new nsIOFileStream(hostinfoFileSpec, (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE));
00880     if (!mHostInfoStream)
00881         return NS_ERROR_OUT_OF_MEMORY;
00882 
00883     // todo, missing some formatting, see the 4.x code
00884     *mHostInfoStream
00885          << "# News host information file." << MSG_LINEBREAK
00886          << "# This is a generated file!  Do not edit." << MSG_LINEBREAK
00887          << "" << MSG_LINEBREAK
00888          << "version=" << VALID_VERSION << MSG_LINEBREAK
00889          << "newsrcname=" << (const char*)hostname << MSG_LINEBREAK
00890          << "lastgroupdate=" << mLastGroupDate << MSG_LINEBREAK
00891          << "firstnewdate=" << firstnewdate << MSG_LINEBREAK
00892          << "uniqueid=" << mUniqueId << MSG_LINEBREAK
00893          << "" << MSG_LINEBREAK
00894          << "begingroups" << MSG_LINEBREAK;
00895 
00896     // XXX todo, sort groups first?
00897 
00898     mGroupsOnServer.EnumerateForwards((nsCStringArrayEnumFunc)writeGroupToHostInfoFile, (void *)mHostInfoStream);
00899 
00900     mHostInfoStream->close();
00901     delete mHostInfoStream;
00902     mHostInfoStream = nsnull;
00903 
00904     mHostInfoHasChanged = PR_FALSE;
00905     return NS_OK;
00906 }
00907 
00908 nsresult
00909 nsNntpIncomingServer::LoadHostInfoFile()
00910 {
00911        nsresult rv;
00912        
00913     // we haven't loaded it yet
00914     mHostInfoLoaded = PR_FALSE;
00915 
00916        rv = GetLocalPath(getter_AddRefs(mHostInfoFile));
00917        if (NS_FAILED(rv)) return rv;
00918        if (!mHostInfoFile) return NS_ERROR_FAILURE;
00919 
00920        rv = mHostInfoFile->AppendRelativeUnixPath(HOSTINFO_FILE_NAME);
00921        if (NS_FAILED(rv)) return rv;
00922 
00923        PRBool exists;
00924        rv = mHostInfoFile->Exists(&exists);
00925        if (NS_FAILED(rv)) return rv;
00926 
00927        // it is ok if the hostinfo.dat file does not exist.
00928        if (!exists) return NS_OK;
00929 
00930     char *buffer = nsnull;
00931     rv = mHostInfoFile->OpenStreamForReading();
00932     NS_ENSURE_SUCCESS(rv,rv);
00933 
00934     PRInt32 numread = 0;
00935 
00936     if (NS_FAILED(mHostInfoInputStream.GrowBuffer(HOSTINFO_FILE_BUFFER_SIZE))) {
00937        return NS_ERROR_FAILURE;
00938     }
00939        
00940        mHasSeenBeginGroups = PR_FALSE;
00941 
00942     while (1) {
00943         buffer = mHostInfoInputStream.GetBuffer();
00944         rv = mHostInfoFile->Read(&buffer, HOSTINFO_FILE_BUFFER_SIZE, &numread);
00945         NS_ENSURE_SUCCESS(rv,rv);
00946         if (numread == 0) {
00947               break;
00948        }
00949        else {
00950               rv = BufferInput(mHostInfoInputStream.GetBuffer(), numread);
00951               if (NS_FAILED(rv)) {
00952                      break;
00953               }
00954        }
00955        }
00956 
00957     mHostInfoFile->CloseStream();
00958      
00959        rv = UpdateSubscribed();
00960        if (NS_FAILED(rv)) return rv;
00961 
00962        return NS_OK;
00963 }
00964 
00965 NS_IMETHODIMP
00966 nsNntpIncomingServer::StartPopulatingWithUri(nsIMsgWindow *aMsgWindow, PRBool aForceToServer, const char *uri)
00967 {
00968        nsresult rv = NS_OK;
00969 
00970 #ifdef DEBUG_seth
00971        printf("StartPopulatingWithUri(%s)\n",uri);
00972 #endif
00973 
00974     rv = EnsureInner();
00975     NS_ENSURE_SUCCESS(rv,rv);
00976     rv = mInner->StartPopulatingWithUri(aMsgWindow, aForceToServer, uri);
00977     NS_ENSURE_SUCCESS(rv,rv);
00978 
00979        rv = StopPopulating(mMsgWindow);
00980        if (NS_FAILED(rv)) return rv;
00981 
00982        return NS_OK;
00983 }
00984 
00985 NS_IMETHODIMP
00986 nsNntpIncomingServer::SubscribeCleanup()
00987 {
00988        nsresult rv = NS_OK;
00989     rv = ClearInner();
00990     NS_ENSURE_SUCCESS(rv,rv);
00991        return NS_OK;
00992 }
00993 
00994 NS_IMETHODIMP
00995 nsNntpIncomingServer::StartPopulating(nsIMsgWindow *aMsgWindow, PRBool aForceToServer)
00996 {
00997   nsresult rv;
00998 
00999   mMsgWindow = aMsgWindow;
01000 
01001   rv = EnsureInner();
01002   NS_ENSURE_SUCCESS(rv,rv);
01003 
01004   rv = mInner->StartPopulating(aMsgWindow, aForceToServer);
01005   NS_ENSURE_SUCCESS(rv,rv);
01006 
01007   rv = SetDelimiter(NEWS_DELIMITER);
01008   if (NS_FAILED(rv)) return rv;
01009     
01010   rv = SetShowFullName(PR_TRUE);
01011   if (NS_FAILED(rv)) return rv;
01012 
01013   nsCOMPtr<nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
01014   NS_ENSURE_SUCCESS(rv,rv);
01015 
01016   mHostInfoLoaded = PR_FALSE;
01017   mVersion = INVALID_VERSION;
01018   mGroupsOnServer.Clear();
01019 
01020   if (!aForceToServer) {
01021        rv = LoadHostInfoFile();    
01022        if (NS_FAILED(rv)) return rv;
01023   }
01024 
01025   // mHostInfoLoaded can be false if we failed to load anything
01026   if (!mHostInfoLoaded || (mVersion != VALID_VERSION)) {
01027     // set these to true, so when we are done and we call WriteHostInfoFile() 
01028     // we'll write out to hostinfo.dat
01029        mHostInfoHasChanged = PR_TRUE;
01030        mVersion = VALID_VERSION;
01031 
01032        mGroupsOnServer.Clear();
01033 
01034        rv = nntpService->GetListOfGroupsOnServer(this, aMsgWindow);
01035        if (NS_FAILED(rv)) return rv;
01036   }
01037   else {
01038        rv = StopPopulating(aMsgWindow);
01039        if (NS_FAILED(rv)) return rv;
01040   }
01041 
01042   return NS_OK;
01043 }
01044 
01051 NS_IMETHODIMP
01052 nsNntpIncomingServer::AddNewsgroupToList(const char *aName)
01053 {
01054     nsresult rv;
01055 
01056     nsAutoString newsgroupName;
01057     nsCAutoString dataCharset;
01058     rv = GetCharset(dataCharset);
01059     NS_ENSURE_SUCCESS(rv,rv);
01060 
01061     rv = nsMsgI18NConvertToUnicode(dataCharset.get(), 
01062                                    nsDependentCString(aName),
01063                                    newsgroupName);
01064 #ifdef DEBUG_jungshik
01065     NS_ASSERTION(NS_SUCCEEDED(rv), "newsgroup name conversion failed");
01066 #endif
01067     if (NS_FAILED(rv)) {
01068         CopyASCIItoUTF16(aName, newsgroupName);
01069     }
01070 
01071     rv = AddTo(NS_ConvertUTF16toUTF8(newsgroupName),
01072                PR_FALSE, PR_TRUE, PR_TRUE);
01073     if (NS_FAILED(rv)) return rv;
01074     return NS_OK;
01075 }
01076 
01077 NS_IMETHODIMP
01078 nsNntpIncomingServer::SetIncomingServer(nsIMsgIncomingServer *aServer)
01079 {
01080     nsresult rv = EnsureInner();
01081     NS_ENSURE_SUCCESS(rv,rv);
01082        return mInner->SetIncomingServer(aServer);
01083 }
01084 
01085 NS_IMETHODIMP
01086 nsNntpIncomingServer::SetShowFullName(PRBool showFullName)
01087 {
01088     nsresult rv = EnsureInner();
01089     NS_ENSURE_SUCCESS(rv,rv);
01090        return mInner->SetShowFullName(showFullName);
01091 }
01092 
01093 nsresult
01094 nsNntpIncomingServer::ClearInner()
01095 {
01096     nsresult rv = NS_OK;
01097 
01098     if (mInner) {
01099         rv = mInner->SetSubscribeListener(nsnull);
01100         NS_ENSURE_SUCCESS(rv,rv);
01101 
01102         rv = mInner->SetIncomingServer(nsnull);
01103         NS_ENSURE_SUCCESS(rv,rv);
01104 
01105         mInner = nsnull;
01106     }
01107     return NS_OK;
01108 }
01109 
01110 nsresult
01111 nsNntpIncomingServer::EnsureInner()
01112 {
01113     nsresult rv = NS_OK;
01114 
01115     if (mInner) return NS_OK;
01116 
01117     mInner = do_CreateInstance(kSubscribableServerCID,&rv);
01118     NS_ENSURE_SUCCESS(rv,rv);
01119     if (!mInner) return NS_ERROR_FAILURE;
01120 
01121     rv = SetIncomingServer(this);
01122     NS_ENSURE_SUCCESS(rv,rv);
01123 
01124     return NS_OK;
01125 }
01126 
01127 NS_IMETHODIMP
01128 nsNntpIncomingServer::GetDelimiter(char *aDelimiter)
01129 {
01130     nsresult rv = EnsureInner();
01131     NS_ENSURE_SUCCESS(rv,rv);
01132     return mInner->GetDelimiter(aDelimiter);
01133 }
01134 
01135 NS_IMETHODIMP
01136 nsNntpIncomingServer::SetDelimiter(char aDelimiter)
01137 {
01138     nsresult rv = EnsureInner();
01139     NS_ENSURE_SUCCESS(rv,rv);
01140     return mInner->SetDelimiter(aDelimiter);
01141 }
01142 
01143 NS_IMETHODIMP
01144 nsNntpIncomingServer::SetAsSubscribed(const nsACString &path)
01145 {
01146     mTempSubscribed.AppendCString(path);
01147 
01148     nsresult rv = EnsureInner();
01149     NS_ENSURE_SUCCESS(rv,rv);
01150     return mInner->SetAsSubscribed(path);
01151 }
01152 
01153 PRBool
01154 setAsSubscribedFunction(nsCString &aElement, void *aData)
01155 {
01156     nsresult rv = NS_OK;
01157     nsNntpIncomingServer *server;
01158     server = (nsNntpIncomingServer *)aData;
01159     NS_ASSERTION(server, "no server");
01160     if (!server) {
01161         return PR_FALSE;
01162     }
01163  
01164     rv = server->SetAsSubscribed(aElement);
01165     NS_ASSERTION(NS_SUCCEEDED(rv),"SetAsSubscribed failed");
01166     return PR_TRUE;
01167 }
01168 
01169 NS_IMETHODIMP
01170 nsNntpIncomingServer::UpdateSubscribed()
01171 {
01172     nsresult rv = EnsureInner();
01173     NS_ENSURE_SUCCESS(rv,rv);
01174        mTempSubscribed.Clear();
01175        mSubscribedNewsgroups.EnumerateForwards((nsCStringArrayEnumFunc)setAsSubscribedFunction, (void *)this);
01176        return NS_OK;
01177 }
01178 
01179 NS_IMETHODIMP
01180 nsNntpIncomingServer::AddTo(const nsACString &aName, PRBool addAsSubscribed,
01181                             PRBool aSubscribable, PRBool changeIfExists)
01182 {
01183     NS_ASSERTION(IsUTF8(aName), "Non-UTF-8 newsgroup name");
01184     nsresult rv = EnsureInner();
01185     NS_ENSURE_SUCCESS(rv,rv);
01186 
01187     rv = AddGroupOnServer(aName);
01188     NS_ENSURE_SUCCESS(rv,rv);
01189  
01190     rv = mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
01191     NS_ENSURE_SUCCESS(rv,rv);
01192 
01193     return rv;
01194 }
01195 
01196 NS_IMETHODIMP
01197 nsNntpIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
01198 {
01199        nsresult rv = NS_OK;
01200 
01201     nsCOMPtr<nsISubscribeListener> listener;
01202        rv = GetSubscribeListener(getter_AddRefs(listener));
01203     NS_ENSURE_SUCCESS(rv,rv);
01204        if (!listener) return NS_ERROR_FAILURE;
01205 
01206        rv = listener->OnDonePopulating();
01207     NS_ENSURE_SUCCESS(rv,rv);
01208 
01209     rv = EnsureInner();
01210     NS_ENSURE_SUCCESS(rv,rv);
01211        rv = mInner->StopPopulating(aMsgWindow);
01212     NS_ENSURE_SUCCESS(rv,rv);
01213 
01214     rv = WriteHostInfoFile();
01215     if (NS_FAILED(rv)) return rv;
01216 
01217        //xxx todo when do I set this to null?
01218        //rv = ClearInner();
01219     //NS_ENSURE_SUCCESS(rv,rv);
01220        return NS_OK;
01221 }
01222 
01223 NS_IMETHODIMP
01224 nsNntpIncomingServer::SetSubscribeListener(nsISubscribeListener *aListener)
01225 {      
01226     nsresult rv = EnsureInner();
01227     NS_ENSURE_SUCCESS(rv,rv);
01228        return mInner->SetSubscribeListener(aListener);
01229 }
01230 
01231 NS_IMETHODIMP
01232 nsNntpIncomingServer::GetSubscribeListener(nsISubscribeListener **aListener)
01233 {
01234     nsresult rv = EnsureInner();
01235     NS_ENSURE_SUCCESS(rv,rv);
01236     return mInner->GetSubscribeListener(aListener);
01237 }
01238 
01239 NS_IMETHODIMP
01240 nsNntpIncomingServer::Subscribe(const PRUnichar *aUnicharName)
01241 {
01242   return SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(aUnicharName));
01243 }
01244 
01245 NS_IMETHODIMP
01246 nsNntpIncomingServer::Unsubscribe(const PRUnichar *aUnicharName)
01247 {
01248   nsresult rv;
01249 
01250   nsCOMPtr <nsIMsgFolder> serverFolder;
01251   rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
01252   if (NS_FAILED(rv)) 
01253     return rv;
01254 
01255   if (!serverFolder) 
01256     return NS_ERROR_FAILURE;
01257  
01258   // to handle non-ASCII newsgroup names, we store them internally as escaped.
01259   // so we need to escape and encode the name, in order to find it.
01260   nsCAutoString escapedName;
01261   rv = NS_MsgEscapeEncodeURLPath(nsDependentString(aUnicharName), escapedName);
01262 
01263   nsCOMPtr <nsIMsgFolder> newsgroupFolder;
01264   rv = serverFolder->FindSubFolder(escapedName,
01265                                    getter_AddRefs(newsgroupFolder));
01266 
01267   if (NS_FAILED(rv)) 
01268     return rv;
01269 
01270   if (!newsgroupFolder) 
01271     return NS_ERROR_FAILURE;
01272 
01273   rv = serverFolder->PropagateDelete(newsgroupFolder, PR_TRUE /* delete storage */, nsnull);
01274   if (NS_FAILED(rv)) 
01275     return rv;
01276 
01277   // since we've unsubscribed to a newsgroup, the newsrc needs to be written out
01278   rv = SetNewsrcHasChanged(PR_TRUE);
01279   if (NS_FAILED(rv)) 
01280     return rv;
01281 
01282   return NS_OK;
01283 }
01284 
01285 PRInt32
01286 nsNntpIncomingServer::HandleLine(char* line, PRUint32 line_size)
01287 {
01288   NS_ASSERTION(line, "line is null");
01289   if (!line) return 0;
01290 
01291   // skip blank lines and comments
01292   if (line[0] == '#' || line[0] == '\0') return 0;
01293        
01294   line[line_size] = 0;
01295 
01296   if (mHasSeenBeginGroups) {
01297     char *commaPos = PL_strchr(line,',');
01298     if (commaPos) *commaPos = 0;
01299 
01300         // newsrc entries are all in UTF-8
01301 #ifdef DEBUG_jungshik
01302     NS_ASSERTION(IsUTF8(nsDependentCString(line)), "newsrc line is not utf-8");
01303 #endif
01304     nsresult rv = AddTo(nsDependentCString(line), PR_FALSE, PR_TRUE, PR_TRUE);
01305     NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add line");
01306     if (NS_SUCCEEDED(rv)) {
01307       // since we've seen one group, we can claim we've loaded the
01308       // hostinfo file
01309       mHostInfoLoaded = PR_TRUE;
01310     }
01311   }
01312   else {
01313               if (nsCRT::strncmp(line,"begingroups", 11) == 0) {
01314                      mHasSeenBeginGroups = PR_TRUE;
01315               }
01316               char*equalPos = PL_strchr(line, '=');     
01317               if (equalPos) {
01318                      *equalPos++ = '\0';
01319                      if (PL_strcmp(line, "lastgroupdate") == 0) {
01320                             mLastGroupDate = strtol(equalPos, nsnull, 16);
01321                      } else if (PL_strcmp(line, "firstnewdate") == 0) {
01322                             PRInt32 firstnewdate = strtol(equalPos, nsnull, 16);
01323                             LL_I2L(mFirstNewDate, firstnewdate);
01324                      } else if (PL_strcmp(line, "uniqueid") == 0) {
01325                             mUniqueId = strtol(equalPos, nsnull, 16);
01326                      } else if (PL_strcmp(line, "version") == 0) {
01327                             mVersion = strtol(equalPos, nsnull, 16);
01328                      }
01329               }      
01330        }
01331 
01332        return 0;
01333 }
01334 
01335 nsresult
01336 nsNntpIncomingServer::AddGroupOnServer(const nsACString &aName)
01337 {
01338        mGroupsOnServer.AppendCString(aName); 
01339        return NS_OK;
01340 }
01341 
01342 NS_IMETHODIMP
01343 nsNntpIncomingServer::AddNewsgroup(const nsAString &aName)
01344 {
01345     // handle duplicates?
01346     mSubscribedNewsgroups.AppendCString(NS_ConvertUTF16toUTF8(aName));
01347     return NS_OK;
01348 }
01349 
01350 NS_IMETHODIMP
01351 nsNntpIncomingServer::RemoveNewsgroup(const nsAString &aName)
01352 {
01353     // handle duplicates?
01354     mSubscribedNewsgroups.RemoveCString(NS_ConvertUTF16toUTF8(aName));
01355     return NS_OK;
01356 }
01357 
01358 NS_IMETHODIMP
01359 nsNntpIncomingServer::SetState(const nsACString &path, PRBool state,
01360                                PRBool *stateChanged)
01361 {
01362     nsresult rv = EnsureInner();
01363     NS_ENSURE_SUCCESS(rv,rv);
01364 
01365     rv = mInner->SetState(path, state, stateChanged);
01366     if (*stateChanged) {
01367       if (state)
01368         mTempSubscribed.AppendCString(path);
01369       else
01370         mTempSubscribed.RemoveCString(path);
01371     }
01372     return rv;
01373 }
01374 
01375 NS_IMETHODIMP
01376 nsNntpIncomingServer::HasChildren(const nsACString &path, PRBool *aHasChildren)
01377 {
01378     nsresult rv = EnsureInner();
01379     NS_ENSURE_SUCCESS(rv,rv);
01380     return mInner->HasChildren(path, aHasChildren);
01381 }
01382 
01383 NS_IMETHODIMP
01384 nsNntpIncomingServer::IsSubscribed(const nsACString &path,
01385                                    PRBool *aIsSubscribed)
01386 {
01387     nsresult rv = EnsureInner();
01388     NS_ENSURE_SUCCESS(rv,rv);
01389     return mInner->IsSubscribed(path, aIsSubscribed);
01390 }
01391 
01392 NS_IMETHODIMP
01393 nsNntpIncomingServer::IsSubscribable(const nsACString &path,
01394                                      PRBool *aIsSubscribable)
01395 {
01396     nsresult rv = EnsureInner();
01397     NS_ENSURE_SUCCESS(rv,rv);
01398     return mInner->IsSubscribable(path, aIsSubscribable);
01399 }
01400 
01401 NS_IMETHODIMP
01402 nsNntpIncomingServer::GetLeafName(const nsACString &path, nsAString &aLeafName)
01403 {
01404     nsresult rv = EnsureInner();
01405     NS_ENSURE_SUCCESS(rv,rv);
01406     return mInner->GetLeafName(path, aLeafName);
01407 }
01408 
01409 NS_IMETHODIMP
01410 nsNntpIncomingServer::GetFirstChildURI(const nsACString &path, nsACString &aResult)
01411 {
01412     nsresult rv = EnsureInner();
01413     NS_ENSURE_SUCCESS(rv,rv);
01414     return mInner->GetFirstChildURI(path, aResult);
01415 }
01416 
01417 NS_IMETHODIMP
01418 nsNntpIncomingServer::GetChildren(const nsACString &path,
01419                                   nsISupportsArray *array)
01420 {
01421     nsresult rv = EnsureInner();
01422     NS_ENSURE_SUCCESS(rv,rv);
01423     return mInner->GetChildren(path, array);
01424 }
01425 
01426 NS_IMETHODIMP
01427 nsNntpIncomingServer::CommitSubscribeChanges()
01428 {
01429     nsresult rv;
01430 
01431     // we force the newrc to be dirty, so it will get written out when
01432     // we call WriteNewsrcFile()
01433     rv = SetNewsrcHasChanged(PR_TRUE);
01434     NS_ENSURE_SUCCESS(rv,rv);
01435     return WriteNewsrcFile();
01436 }
01437 
01438 NS_IMETHODIMP
01439 nsNntpIncomingServer::ForgetPassword()
01440 {
01441     nsresult rv;
01442 
01443     // clear password of root folder (for the news account)
01444     nsCOMPtr<nsIMsgFolder> rootFolder;
01445     rv = GetRootFolder(getter_AddRefs(rootFolder));
01446     NS_ENSURE_SUCCESS(rv,rv);
01447     if (!rootFolder) return NS_ERROR_FAILURE;
01448 
01449     nsCOMPtr <nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
01450     NS_ENSURE_SUCCESS(rv,rv);
01451     if (!newsFolder) return NS_ERROR_FAILURE;
01452 
01453     rv = newsFolder->ForgetGroupUsername();
01454     NS_ENSURE_SUCCESS(rv,rv);
01455     rv = newsFolder->ForgetGroupPassword();
01456     NS_ENSURE_SUCCESS(rv,rv);
01457 
01458     // clear password of all child folders
01459     nsCOMPtr<nsIEnumerator> subFolders;
01460 
01461     rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
01462     NS_ENSURE_SUCCESS(rv,rv);
01463 
01464     nsAdapterEnumerator *simpleEnumerator = new nsAdapterEnumerator(subFolders);
01465     if (!simpleEnumerator) return NS_ERROR_OUT_OF_MEMORY;
01466 
01467     PRBool moreFolders = PR_FALSE;
01468         
01469     nsresult return_rv = NS_OK;
01470 
01471     while (NS_SUCCEEDED(simpleEnumerator->HasMoreElements(&moreFolders)) && moreFolders) {
01472         nsCOMPtr<nsISupports> child;
01473         rv = simpleEnumerator->GetNext(getter_AddRefs(child));
01474         if (NS_SUCCEEDED(rv) && child) {
01475             newsFolder = do_QueryInterface(child, &rv);
01476             if (NS_SUCCEEDED(rv) && newsFolder) {
01477                 rv = newsFolder->ForgetGroupUsername();
01478                 if (NS_FAILED(rv)) return_rv = rv;
01479                 rv = newsFolder->ForgetGroupPassword();
01480                 if (NS_FAILED(rv)) return_rv = rv;
01481             }
01482             else {
01483                 return_rv = NS_ERROR_FAILURE;
01484             }
01485         }
01486     }
01487     delete simpleEnumerator;
01488 
01489     return return_rv;
01490 }
01491 
01492 NS_IMETHODIMP
01493 nsNntpIncomingServer::GetSupportsExtensions(PRBool *aSupportsExtensions)
01494 {
01495   NS_ASSERTION(0,"not implemented");
01496   return NS_ERROR_NOT_IMPLEMENTED;
01497 }
01498 
01499 NS_IMETHODIMP
01500 nsNntpIncomingServer::SetSupportsExtensions(PRBool aSupportsExtensions)
01501 {
01502   NS_ASSERTION(0,"not implemented");
01503   return NS_ERROR_NOT_IMPLEMENTED;
01504 }
01505 
01506 NS_IMETHODIMP
01507 nsNntpIncomingServer::AddExtension(const char *extension)
01508 {
01509   NS_ASSERTION(0,"not implemented");
01510   return NS_ERROR_NOT_IMPLEMENTED;
01511 }
01512    
01513 NS_IMETHODIMP
01514 nsNntpIncomingServer::QueryExtension(const char *extension, PRBool *result)
01515 {
01516 #ifdef DEBUG_seth
01517   printf("no extension support yet\n");
01518 #endif
01519   *result = PR_FALSE;
01520   return NS_OK;
01521 }
01522 
01523 NS_IMETHODIMP
01524 nsNntpIncomingServer::GetPostingAllowed(PRBool *aPostingAllowed)
01525 {
01526   *aPostingAllowed = mPostingAllowed;
01527   return NS_OK;
01528 }
01529 
01530 NS_IMETHODIMP
01531 nsNntpIncomingServer::SetPostingAllowed(PRBool aPostingAllowed)
01532 {
01533   mPostingAllowed = aPostingAllowed;
01534   return NS_OK;
01535 }
01536 
01537 NS_IMETHODIMP
01538 nsNntpIncomingServer::GetLastUpdatedTime(PRUint32 *aLastUpdatedTime)
01539 {
01540   *aLastUpdatedTime = mLastUpdatedTime;
01541   return NS_OK;
01542 }
01543 
01544 NS_IMETHODIMP
01545 nsNntpIncomingServer::SetLastUpdatedTime(PRUint32 aLastUpdatedTime)
01546 {
01547   NS_ASSERTION(0,"not implemented");
01548   return NS_ERROR_NOT_IMPLEMENTED;
01549 }
01550 
01551 NS_IMETHODIMP
01552 nsNntpIncomingServer::AddPropertyForGet(const char *name, const char *value)
01553 {
01554   NS_ASSERTION(0,"not implemented");
01555   return NS_ERROR_NOT_IMPLEMENTED;
01556 }
01557 
01558 NS_IMETHODIMP
01559 nsNntpIncomingServer::QueryPropertyForGet(const char *name, char **value)
01560 {
01561   NS_ASSERTION(0,"not implemented");
01562   return NS_ERROR_NOT_IMPLEMENTED;
01563 }
01564   
01565 NS_IMETHODIMP
01566 nsNntpIncomingServer::AddSearchableGroup(const nsAString &name)
01567 {
01568   NS_ASSERTION(0,"not implemented");
01569   return NS_ERROR_NOT_IMPLEMENTED;
01570 }
01571 
01572 NS_IMETHODIMP
01573 nsNntpIncomingServer::QuerySearchableGroup(const nsAString &name, PRBool *result)
01574 {
01575   NS_ASSERTION(0,"not implemented");
01576   return NS_ERROR_NOT_IMPLEMENTED;
01577 }
01578 
01579 NS_IMETHODIMP
01580 nsNntpIncomingServer::AddSearchableHeader(const char *name)
01581 {
01582   NS_ASSERTION(0,"not implemented");
01583   return NS_ERROR_NOT_IMPLEMENTED;
01584 }
01585 
01586 NS_IMETHODIMP
01587 nsNntpIncomingServer::QuerySearchableHeader(const char *name, PRBool *result)
01588 {
01589   NS_ASSERTION(0,"not implemented");
01590   return NS_ERROR_NOT_IMPLEMENTED;
01591 }
01592   
01593 NS_IMETHODIMP
01594 nsNntpIncomingServer::FindGroup(const nsACString &name, nsIMsgNewsFolder **result)
01595 {
01596   NS_ENSURE_ARG_POINTER(result);
01597 
01598   nsresult rv;
01599   nsCOMPtr <nsIMsgFolder> serverFolder;
01600   rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
01601   NS_ENSURE_SUCCESS(rv,rv);
01602 
01603   if (!serverFolder) return NS_ERROR_FAILURE;
01604 
01605   nsCOMPtr <nsIMsgFolder> subFolder;
01606   rv = serverFolder->FindSubFolder(name, getter_AddRefs(subFolder));
01607   NS_ENSURE_SUCCESS(rv,rv);
01608   if (!subFolder) return NS_ERROR_FAILURE;
01609 
01610   rv = subFolder->QueryInterface(NS_GET_IID(nsIMsgNewsFolder), (void**)result);
01611   NS_ENSURE_SUCCESS(rv,rv);
01612   if (!*result) return NS_ERROR_FAILURE;
01613   return NS_OK;
01614 }
01615 
01616 NS_IMETHODIMP
01617 nsNntpIncomingServer::GetFirstGroupNeedingExtraInfo(nsACString &result)
01618 {
01619   NS_ASSERTION(0,"not implemented");
01620   return NS_ERROR_NOT_IMPLEMENTED;
01621 }
01622 
01623 NS_IMETHODIMP
01624 nsNntpIncomingServer::SetGroupNeedsExtraInfo(const nsACString &name,
01625                                              PRBool needsExtraInfo)
01626 {
01627   NS_ASSERTION(0,"not implemented");
01628   return NS_ERROR_NOT_IMPLEMENTED;
01629 }
01630 
01631 
01632 NS_IMETHODIMP
01633 nsNntpIncomingServer::GroupNotFound(nsIMsgWindow *aMsgWindow,
01634                                     const nsAString &aName, PRBool aOpening)
01635 {
01636   nsresult rv;
01637   nsCOMPtr <nsIPrompt> prompt;
01638 
01639   if (aMsgWindow) {
01640     rv = aMsgWindow->GetPromptDialog(getter_AddRefs(prompt));
01641     NS_ASSERTION(NS_SUCCEEDED(rv), "no prompt from the msg window");
01642   }
01643 
01644   if (!prompt) {
01645     nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01646     rv = wwatch->GetNewPrompter(nsnull, getter_AddRefs(prompt));
01647     NS_ENSURE_SUCCESS(rv,rv);
01648   }
01649 
01650   nsCOMPtr <nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID,&rv);
01651   NS_ENSURE_SUCCESS(rv,rv);
01652   
01653   nsCOMPtr <nsIStringBundle> bundle;
01654   rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
01655   NS_ENSURE_SUCCESS(rv,rv);
01656 
01657   nsXPIDLCString hostname;
01658   rv = GetHostName(getter_Copies(hostname));
01659   NS_ENSURE_SUCCESS(rv,rv);
01660 
01661   NS_ConvertUTF8toUTF16 hostStr(hostname); 
01662 
01663   nsAFlatString groupName = PromiseFlatString(aName);
01664   const PRUnichar *formatStrings[2] = { groupName.get(), hostStr.get() };
01665   nsXPIDLString confirmText;
01666   rv = bundle->FormatStringFromName(
01667                     NS_LITERAL_STRING("autoUnsubscribeText").get(),
01668                     formatStrings, 2,
01669                     getter_Copies(confirmText));
01670   NS_ENSURE_SUCCESS(rv,rv);
01671 
01672   PRBool confirmResult = PR_FALSE;
01673   rv = prompt->Confirm(nsnull, confirmText, &confirmResult);
01674   NS_ENSURE_SUCCESS(rv,rv);
01675 
01676   if (confirmResult) {
01677     rv = Unsubscribe(groupName.get());
01678     NS_ENSURE_SUCCESS(rv,rv);
01679   }
01680   
01681   return rv;
01682 }
01683 
01684 NS_IMETHODIMP
01685 nsNntpIncomingServer::SetPrettyNameForGroup(const nsAString &name,
01686                                             const nsAString &prettyName)
01687 {
01688   NS_ASSERTION(0,"not implemented");
01689   return NS_ERROR_NOT_IMPLEMENTED;
01690 }
01691 
01692 NS_IMETHODIMP
01693 nsNntpIncomingServer::GetCanSearchMessages(PRBool *canSearchMessages)
01694 {
01695     NS_ENSURE_ARG_POINTER(canSearchMessages);
01696     *canSearchMessages = PR_TRUE;
01697     return NS_OK;
01698 }
01699 
01700 NS_IMETHODIMP
01701 nsNntpIncomingServer::GetOfflineSupportLevel(PRInt32 *aSupportLevel)
01702 {
01703     NS_ENSURE_ARG_POINTER(aSupportLevel);
01704     nsresult rv;
01705     
01706     rv = GetIntValue("offline_support_level", aSupportLevel);
01707     if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv;
01708     
01709     // set default value
01710     *aSupportLevel = OFFLINE_SUPPORT_LEVEL_EXTENDED;
01711     return NS_OK;
01712 }
01713 
01714 NS_IMETHODIMP
01715 nsNntpIncomingServer::GetDefaultCopiesAndFoldersPrefsToServer(PRBool *aCopiesAndFoldersOnServer)
01716 {
01717     NS_ENSURE_ARG_POINTER(aCopiesAndFoldersOnServer);
01718 
01726     *aCopiesAndFoldersOnServer = PR_FALSE;
01727     return NS_OK;
01728 }
01729 
01730 NS_IMETHODIMP
01731 nsNntpIncomingServer::GetCanCreateFoldersOnServer(PRBool *aCanCreateFoldersOnServer)
01732 {
01733     NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
01734 
01735     // No folder creation on news servers. Return false.
01736     *aCanCreateFoldersOnServer = PR_FALSE;
01737     return NS_OK;
01738 }
01739 
01740 PRBool
01741 buildSubscribeSearchResult(nsCString &aElement, void *aData)
01742 {
01743     nsresult rv = NS_OK;
01744     nsNntpIncomingServer *server;
01745     server = (nsNntpIncomingServer *)aData;
01746     NS_ASSERTION(server, "no server");
01747     if (!server) {
01748         return PR_FALSE;
01749     }
01750  
01751     rv = server->AppendIfSearchMatch(aElement);
01752     NS_ASSERTION(NS_SUCCEEDED(rv),"AddSubscribeSearchResult failed");
01753     return PR_TRUE;
01754 }
01755 
01756 nsresult
01757 nsNntpIncomingServer::AppendIfSearchMatch(nsCString& newsgroupName)
01758 {
01759     NS_ConvertUTF8toUTF16 groupName(newsgroupName);
01760     nsAString::const_iterator start, end;
01761     groupName.BeginReading(start);
01762     groupName.EndReading(end);
01763     if (FindInReadable(mSearchValue, start, end, 
01764                        nsCaseInsensitiveStringComparator())) 
01765         mSubscribeSearchResult.AppendCString(newsgroupName);
01766     return NS_OK;
01767 }
01768 
01769 NS_IMETHODIMP
01770 nsNntpIncomingServer::SetSearchValue(const nsAString &searchValue)
01771 {
01772     mSearchValue = searchValue;
01773 
01774     if (mTree) {
01775         mTree->BeginUpdateBatch();
01776         mTree->RowCountChanged(0, -mSubscribeSearchResult.Count());
01777     }
01778 
01779     mSubscribeSearchResult.Clear();
01780     mGroupsOnServer.
01781         EnumerateForwards(nsCStringArrayEnumFunc(buildSubscribeSearchResult),
01782                           (void *)this);
01783     mSubscribeSearchResult.SortIgnoreCase();
01784 
01785     if (mTree) {
01786         mTree->RowCountChanged(0, mSubscribeSearchResult.Count());
01787         mTree->EndUpdateBatch();
01788     }
01789     return NS_OK;
01790 }
01791 
01792 NS_IMETHODIMP
01793 nsNntpIncomingServer::GetSupportsSubscribeSearch(PRBool *retVal)
01794 {
01795     *retVal = PR_TRUE;
01796     return NS_OK;
01797 }
01798 
01799 NS_IMETHODIMP 
01800 nsNntpIncomingServer::GetRowCount(PRInt32 *aRowCount)
01801 {
01802     *aRowCount = mSubscribeSearchResult.Count();
01803     return NS_OK;
01804 }
01805 
01806 NS_IMETHODIMP 
01807 nsNntpIncomingServer::GetSelection(nsITreeSelection * *aSelection)
01808 {
01809   *aSelection = mTreeSelection;
01810   NS_IF_ADDREF(*aSelection);
01811   return NS_OK;
01812 }
01813 
01814 NS_IMETHODIMP 
01815 nsNntpIncomingServer::SetSelection(nsITreeSelection * aSelection)
01816 {
01817   mTreeSelection = aSelection;
01818   return NS_OK;
01819 }
01820 
01821 NS_IMETHODIMP 
01822 nsNntpIncomingServer::GetRowProperties(PRInt32 index, nsISupportsArray *properties)
01823 {
01824     return NS_OK;
01825 }
01826 
01827 NS_IMETHODIMP 
01828 nsNntpIncomingServer::GetCellProperties(PRInt32 row, nsITreeColumn* col, nsISupportsArray *properties)
01829 {
01830     if (!IsValidRow(row))
01831       return NS_ERROR_UNEXPECTED;
01832 
01833     const PRUnichar* colID;
01834     col->GetIdConst(&colID);
01835     if (colID[0] == 's') { 
01836         // if <name> is in our temporary list of subscribed groups
01837         // add the "subscribed" property so the check mark shows up
01838         // in the "subscribedCol"
01839         nsCString name;
01840         if (mSearchResultSortDescending)
01841           row = mSubscribeSearchResult.Count() + ~row;
01842         mSubscribeSearchResult.CStringAt(row, name);
01843         if (mTempSubscribed.IndexOf(name) != -1) {
01844           properties->AppendElement(mSubscribedAtom); 
01845         }
01846     }
01847     else if (colID[0] == 'n') {
01848       // add the "nntp" property to the "nameCol" 
01849       // so we get the news folder icon in the search view
01850       properties->AppendElement(mNntpAtom); 
01851     }
01852     return NS_OK;
01853 }
01854 
01855 NS_IMETHODIMP 
01856 nsNntpIncomingServer::GetColumnProperties(nsITreeColumn* col, nsISupportsArray *properties)
01857 {
01858     return NS_OK;
01859 }
01860 
01861 NS_IMETHODIMP 
01862 nsNntpIncomingServer::IsContainer(PRInt32 index, PRBool *_retval)
01863 {
01864     *_retval = PR_FALSE;
01865     return NS_OK;
01866 }
01867 
01868 NS_IMETHODIMP 
01869 nsNntpIncomingServer::IsContainerOpen(PRInt32 index, PRBool *_retval)
01870 {
01871     return NS_ERROR_NOT_IMPLEMENTED;
01872 }
01873 
01874 NS_IMETHODIMP 
01875 nsNntpIncomingServer::IsContainerEmpty(PRInt32 index, PRBool *_retval)
01876 {
01877     return NS_ERROR_NOT_IMPLEMENTED;
01878 }
01879 
01880 NS_IMETHODIMP 
01881 nsNntpIncomingServer::IsSeparator(PRInt32 index, PRBool *_retval)
01882 {
01883     *_retval = PR_FALSE;
01884     return NS_OK;
01885 }
01886 
01887 NS_IMETHODIMP 
01888 nsNntpIncomingServer::IsSorted(PRBool *_retval)
01889 {
01890     return NS_ERROR_NOT_IMPLEMENTED;
01891 }
01892 
01893 NS_IMETHODIMP 
01894 nsNntpIncomingServer::CanDrop(PRInt32 index, PRInt32 orientation, PRBool *_retval)
01895 {
01896     return NS_ERROR_NOT_IMPLEMENTED;
01897 }
01898 
01899 NS_IMETHODIMP 
01900 nsNntpIncomingServer::Drop(PRInt32 row, PRInt32 orientation)
01901 {
01902     return NS_ERROR_NOT_IMPLEMENTED;
01903 }
01904 
01905 NS_IMETHODIMP 
01906 nsNntpIncomingServer::GetParentIndex(PRInt32 rowIndex, PRInt32 *_retval)
01907 {
01908     return NS_ERROR_NOT_IMPLEMENTED;
01909 }
01910 
01911 NS_IMETHODIMP 
01912 nsNntpIncomingServer::HasNextSibling(PRInt32 rowIndex, PRInt32 afterIndex, PRBool *_retval)
01913 {
01914     return NS_ERROR_NOT_IMPLEMENTED;
01915 }
01916 
01917 NS_IMETHODIMP 
01918 nsNntpIncomingServer::GetLevel(PRInt32 index, PRInt32 *_retval)
01919 {
01920     *_retval = 0;
01921     return NS_OK;
01922 }
01923 
01924 nsresult 
01925 nsNntpIncomingServer::IsValidRow(PRInt32 row)
01926 {
01927     return ((row >= 0) && (row < mSubscribeSearchResult.Count()));
01928 }
01929 
01930 NS_IMETHODIMP 
01931 nsNntpIncomingServer::GetImageSrc(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
01932 {
01933   return NS_OK;
01934 }
01935 
01936 NS_IMETHODIMP 
01937 nsNntpIncomingServer::GetProgressMode(PRInt32 row, nsITreeColumn* col, PRInt32* _retval)
01938 {
01939   return NS_OK;
01940 }
01941 
01942 NS_IMETHODIMP 
01943 nsNntpIncomingServer::GetCellValue(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
01944 {
01945   return NS_OK;
01946 }
01947 
01948 NS_IMETHODIMP 
01949 nsNntpIncomingServer::GetCellText(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
01950 {
01951     if (!IsValidRow(row))
01952       return NS_ERROR_UNEXPECTED;
01953 
01954     const PRUnichar* colID;
01955     col->GetIdConst(&colID);
01956 
01957     nsresult rv = NS_OK;
01958     if (colID[0] == 'n') {
01959       nsCAutoString str;
01960       if (mSearchResultSortDescending)
01961         row = mSubscribeSearchResult.Count() + ~row;
01962       mSubscribeSearchResult.CStringAt(row, str);
01963       // some servers have newsgroup names that are non ASCII.  we store 
01964       // those as escaped. unescape here so the UI is consistent
01965       rv = NS_MsgDecodeUnescapeURLPath(str, _retval);
01966     }
01967     return rv;
01968 }
01969 
01970 NS_IMETHODIMP 
01971 nsNntpIncomingServer::SetTree(nsITreeBoxObject *tree)
01972 {
01973   mTree = tree;
01974   if (!tree)
01975       return NS_OK;
01976 
01977   nsCOMPtr<nsITreeColumns> cols;
01978   tree->GetColumns(getter_AddRefs(cols));
01979   if (!cols)
01980       return NS_OK;
01981 
01982   nsCOMPtr<nsITreeColumn> col;
01983   cols->GetKeyColumn(getter_AddRefs(col));
01984   if (!col)
01985       return NS_OK;
01986 
01987   nsCOMPtr<nsIDOMElement> element;
01988   col->GetElement(getter_AddRefs(element));
01989   if (!element)
01990       return NS_OK;
01991 
01992   nsAutoString dir;
01993   element->GetAttribute(NS_LITERAL_STRING("sortDirection"), dir);
01994   mSearchResultSortDescending = dir.EqualsLiteral("descending");
01995   return NS_OK;
01996 }
01997 
01998 NS_IMETHODIMP 
01999 nsNntpIncomingServer::ToggleOpenState(PRInt32 index)
02000 {
02001     return NS_ERROR_NOT_IMPLEMENTED;
02002 }
02003 
02004 NS_IMETHODIMP 
02005 nsNntpIncomingServer::CycleHeader(nsITreeColumn* col)
02006 {
02007     PRBool cycler;
02008     col->GetCycler(&cycler);
02009     if (!cycler) {
02010         NS_NAMED_LITERAL_STRING(dir, "sortDirection");
02011         nsCOMPtr<nsIDOMElement> element;
02012         col->GetElement(getter_AddRefs(element));
02013         mSearchResultSortDescending = !mSearchResultSortDescending;
02014         element->SetAttribute(dir, mSearchResultSortDescending ?
02015             NS_LITERAL_STRING("descending") : NS_LITERAL_STRING("ascending"));
02016         mTree->Invalidate();
02017     }
02018     return NS_OK;
02019 }
02020 
02021 NS_IMETHODIMP 
02022 nsNntpIncomingServer::SelectionChanged()
02023 {
02024     return NS_ERROR_NOT_IMPLEMENTED;
02025 }
02026 
02027 NS_IMETHODIMP 
02028 nsNntpIncomingServer::CycleCell(PRInt32 row, nsITreeColumn* col)
02029 {
02030     return NS_OK;
02031 }
02032 
02033 NS_IMETHODIMP 
02034 nsNntpIncomingServer::IsEditable(PRInt32 row, nsITreeColumn* col, PRBool *_retval)
02035 {
02036     *_retval = PR_FALSE;
02037     return NS_OK;
02038 }
02039 
02040 NS_IMETHODIMP 
02041 nsNntpIncomingServer::SetCellValue(PRInt32 row, nsITreeColumn* col, const nsAString& value)
02042 {
02043     return NS_ERROR_NOT_IMPLEMENTED;
02044 }
02045 
02046 NS_IMETHODIMP 
02047 nsNntpIncomingServer::SetCellText(PRInt32 row, nsITreeColumn* col, const nsAString& value)
02048 {
02049     return NS_ERROR_NOT_IMPLEMENTED;
02050 }
02051 
02052 NS_IMETHODIMP 
02053 nsNntpIncomingServer::PerformAction(const PRUnichar *action)
02054 {
02055     return NS_ERROR_NOT_IMPLEMENTED;
02056 }
02057 
02058 NS_IMETHODIMP 
02059 nsNntpIncomingServer::PerformActionOnRow(const PRUnichar *action, PRInt32 row)
02060 {
02061     return NS_ERROR_NOT_IMPLEMENTED;
02062 }
02063 
02064 NS_IMETHODIMP 
02065 nsNntpIncomingServer::PerformActionOnCell(const PRUnichar *action, PRInt32 row, nsITreeColumn* col)
02066 {
02067     return NS_ERROR_NOT_IMPLEMENTED;
02068 }
02069 
02070 NS_IMETHODIMP
02071 nsNntpIncomingServer::GetCanFileMessagesOnServer(PRBool *aCanFileMessagesOnServer)
02072 {
02073     NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
02074 
02075     // No folder creation on news servers. Return false.
02076     *aCanFileMessagesOnServer = PR_FALSE;
02077     return NS_OK;
02078 }
02079 
02080 NS_IMETHODIMP
02081 nsNntpIncomingServer::GetFilterScope(nsMsgSearchScopeValue *filterScope)
02082 {
02083    NS_ENSURE_ARG_POINTER(filterScope);
02084 
02085    *filterScope = nsMsgSearchScope::newsFilter;
02086    return NS_OK;
02087 }
02088 
02089 NS_IMETHODIMP
02090 nsNntpIncomingServer::GetSearchScope(nsMsgSearchScopeValue *searchScope)
02091 {
02092    NS_ENSURE_ARG_POINTER(searchScope);
02093 
02094    if (WeAreOffline()) {
02095      *searchScope = nsMsgSearchScope::localNews;
02096    }
02097    else {
02098      *searchScope = nsMsgSearchScope::news;
02099    }
02100    return NS_OK;
02101 }
02102 
02103 NS_IMETHODIMP
02104 nsNntpIncomingServer::OnUserOrHostNameChanged(const char *oldName, const char *newName)
02105 {
02106   nsresult rv;
02107   // 1. Do common things in the base class.
02108   rv = nsMsgIncomingServer::OnUserOrHostNameChanged(oldName, newName);
02109   NS_ENSURE_SUCCESS(rv,rv);
02110 
02111   // 2. Remove file hostinfo.dat so that the new subscribe 
02112   //    list will be reloaded from the new server.
02113   nsCOMPtr <nsIFileSpec> hostInfoFile;
02114   rv = GetLocalPath(getter_AddRefs(hostInfoFile));
02115   NS_ENSURE_SUCCESS(rv, rv);
02116   rv = hostInfoFile->AppendRelativeUnixPath(HOSTINFO_FILE_NAME);
02117   NS_ENSURE_SUCCESS(rv, rv);
02118   hostInfoFile->Delete(PR_FALSE);
02119 
02120   // 3.Unsubscribe and then subscribe the existing groups to clean up the article numbers
02121   //   in the rc file (this is because the old and new servers may maintain different 
02122   //   numbers for the same articles if both servers handle the same groups).
02123   nsCOMPtr<nsIEnumerator> subFolders;
02124 
02125   nsCOMPtr <nsIMsgFolder> serverFolder;
02126   rv = GetRootMsgFolder(getter_AddRefs(serverFolder));
02127   NS_ENSURE_SUCCESS(rv,rv);
02128 
02129   rv = serverFolder->GetSubFolders(getter_AddRefs(subFolders));
02130   NS_ENSURE_SUCCESS(rv,rv);
02131 
02132   nsStringArray groupList;
02133   nsXPIDLString folderName;
02134   nsCOMPtr<nsISupports> aItem;
02135   nsCOMPtr <nsIMsgFolder> newsgroupFolder;
02136 
02137   // Prepare the group list
02138   while (subFolders->IsDone() != NS_OK)
02139   {
02140     rv = subFolders->CurrentItem(getter_AddRefs(aItem));
02141     NS_ENSURE_SUCCESS(rv,rv);
02142     newsgroupFolder = do_QueryInterface(aItem, &rv);
02143     NS_ENSURE_SUCCESS(rv,rv);
02144     rv = newsgroupFolder->GetName(getter_Copies(folderName));
02145     NS_ENSURE_SUCCESS(rv,rv);
02146     groupList.AppendString(folderName);
02147     if (! NS_SUCCEEDED(subFolders->Next()))
02148       break;
02149   }
02150 
02151   // If nothing subscribed then we're done.
02152   if (groupList.Count() == 0)
02153     return NS_OK;
02154 
02155   // Now unsubscribe & subscribe.
02156   int i, cnt=groupList.Count();
02157   nsAutoString groupStr;
02158   nsCAutoString cname;
02159   for (i=0; i<cnt; i++)
02160   {
02161     // unsubscribe.
02162     groupList.StringAt(i, groupStr);
02163     rv = Unsubscribe(groupStr.get());
02164     NS_ENSURE_SUCCESS(rv,rv);
02165   }
02166 
02167   for (i=0; i<cnt; i++)
02168   {
02169     // subscribe.
02170     groupList.StringAt(i, groupStr);
02171     rv = SubscribeToNewsgroup(NS_ConvertUTF16toUTF8(groupStr));
02172     NS_ENSURE_SUCCESS(rv,rv);
02173   }
02174 
02175   groupList.Clear();
02176   
02177   // Force updating the rc file.
02178   rv = CommitSubscribeChanges();
02179   return rv;
02180 }