Back to index

lightning-sunbird  0.9+nobinonly
nsNNTPProtocol.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  *   Scott MacGregor <mscott@netscape.com>
00024  *   Seth Spitzer <sspitzer@netscape.com>
00025  *   Alec Flett <alecf@netscape.com>
00026  *   David Bienvenu <bienvenu@nventure.com>
00027  *   Jeff Tsai <jefft@netscape.com>
00028  *   Pierre Phaneuf <pp@ludusdesign.com>
00029  *   Håkan Waara <hwaara@chello.se>
00030  *
00031  * Alternatively, the contents of this file may be used under the terms of
00032  * either of the GNU General Public License Version 2 or later (the "GPL"),
00033  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00034  * in which case the provisions of the GPL or the LGPL are applicable instead
00035  * of those above. If you wish to allow use of your version of this file only
00036  * under the terms of either the GPL or the LGPL, and not to allow others to
00037  * use your version of this file under the terms of the MPL, indicate your
00038  * decision by deleting the provisions above and replace them with the notice
00039  * and other provisions required by the GPL or the LGPL. If you do not delete
00040  * the provisions above, a recipient may use your version of this file under
00041  * the terms of any one of the MPL, the GPL or the LGPL.
00042  *
00043  * ***** END LICENSE BLOCK ***** */
00044 
00045 #ifdef MOZ_LOGGING
00046 #define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */
00047 #endif
00048 
00049 #include "msgCore.h"    // precompiled header...
00050 #include "MailNewsTypes.h"
00051 #include "nntpCore.h"
00052 #include "nsNetUtil.h"
00053 
00054 #include "nsIMsgHdr.h"
00055 #include "nsNNTPProtocol.h"
00056 #include "nsINNTPArticleList.h"
00057 #include "nsIOutputStream.h"
00058 #include "nsFileStream.h"
00059 #include "nsIMemory.h"
00060 #include "nsIPipe.h"
00061 #include "nsCOMPtr.h"
00062 #include "nsReadableUtils.h"
00063 #include "nsMsgI18N.h"
00064 
00065 #include "nsMsgBaseCID.h"
00066 #include "nsMsgNewsCID.h"
00067 
00068 #include "nsINntpUrl.h"
00069 
00070 #include "nsCRT.h"
00071 
00072 #include "prtime.h"
00073 #include "prlog.h"
00074 #include "prerror.h"
00075 #include "nsEscape.h"
00076 #include "nsString.h"
00077 
00078 #include "prprf.h"
00079 
00080 /* include event sink interfaces for news */
00081 
00082 #include "nsIMsgHeaderParser.h" 
00083 #include "nsIMsgSearchSession.h"
00084 #include "nsIMsgSearchAdapter.h"
00085 #include "nsIMsgStatusFeedback.h"
00086 
00087 #include "nsMsgKeySet.h"
00088 
00089 #include "nsNewsUtils.h"
00090 #include "nsMsgUtils.h"
00091 
00092 #include "nsIMsgMailSession.h"
00093 #include "nsIMsgIdentity.h"
00094 #include "nsIMsgAccountManager.h"
00095 
00096 #include "nsIPrompt.h"
00097 #include "nsIMsgStatusFeedback.h" 
00098 
00099 #include "nsIMsgFolder.h"
00100 #include "nsIMsgNewsFolder.h"
00101 #include "nsIDocShell.h"
00102 
00103 // for the memory cache...
00104 #include "nsICacheEntryDescriptor.h"
00105 #include "nsICacheSession.h"
00106 #include "nsIStreamListener.h"
00107 #include "nsNetCID.h"
00108 
00109 #include "nsIPrefBranch.h"
00110 #include "nsIPrefService.h"
00111 
00112 #include "nsIMsgWindow.h"
00113 #include "nsIWindowWatcher.h"
00114 
00115 #include "nsINntpService.h"
00116 #include "nntpCore.h"
00117 #include "nsIStreamConverterService.h"
00118 #include "nsIStreamListenerTee.h"
00119 #include "nsISocketTransport.h"
00120 
00121 #include <time.h>
00122 
00123 #undef GetPort  // XXX Windows!
00124 #undef SetPort  // XXX Windows!
00125 
00126 #define PREF_NEWS_CANCEL_CONFIRM   "news.cancel.confirm"
00127 #define PREF_NEWS_CANCEL_ALERT_ON_SUCCESS "news.cancel.alert_on_success"
00128 #define READ_NEWS_LIST_COUNT_MAX 500 /* number of groups to process at a time when reading the list from the server */
00129 #define READ_NEWS_LIST_TIMEOUT 50  /* uSec to wait until doing more */
00130 #define RATE_STR_BUF_LEN 32
00131 #define UPDATE_THRESHHOLD 25600 /* only update every 25 KB */
00132 
00133 // NNTP extensions are supported yet
00134 // until the extension code is ported, 
00135 // we'll skip right to the first nntp command 
00136 // after doing "mode reader"
00137 // and "pushed" authentication (if necessary),
00138 //#define HAVE_NNTP_EXTENSIONS
00139 
00140 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
00141 
00142 typedef struct _cancelInfoEntry {
00143     char *from;
00144     char *old_from;
00145 } cancelInfoEntry;
00146 
00147 // quiet compiler warnings by defining these function prototypes
00148 char *MSG_UnEscapeSearchUrl (const char *commandSpecificData);
00149 
00150 /* Logging stuff */
00151 
00152 PRLogModuleInfo* NNTP = NULL;
00153 #define out     PR_LOG_ALWAYS
00154 
00155 #define NNTP_LOG_READ(buf) \
00156 if (NNTP==NULL) \
00157     NNTP = PR_NewLogModule("NNTP"); \
00158 PR_LOG(NNTP, out, ("(%p) Receiving: %s", this, buf)) ;
00159 
00160 #define NNTP_LOG_WRITE(buf) \
00161 if (NNTP==NULL) \
00162     NNTP = PR_NewLogModule("NNTP"); \
00163 PR_LOG(NNTP, out, ("(%p) Sending: %s", this, buf)) ;
00164 
00165 #define NNTP_LOG_NOTE(buf) \
00166 if (NNTP==NULL) \
00167     NNTP = PR_NewLogModule("NNTP"); \
00168 PR_LOG(NNTP, out, ("(%p) %s",this, buf)) ;
00169 
00170 const char *const stateLabels[] = {
00171 "NNTP_RESPONSE",
00172 #ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
00173 "NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE",
00174 "NNTP_CONNECTIONS_ARE_AVAILABLE",
00175 #endif
00176 "NNTP_CONNECT",
00177 "NNTP_CONNECT_WAIT",
00178 "NNTP_LOGIN_RESPONSE",
00179 "NNTP_SEND_MODE_READER",
00180 "NNTP_SEND_MODE_READER_RESPONSE",
00181 "SEND_LIST_EXTENSIONS",
00182 "SEND_LIST_EXTENSIONS_RESPONSE",
00183 "SEND_LIST_SEARCHES",
00184 "SEND_LIST_SEARCHES_RESPONSE",
00185 "NNTP_LIST_SEARCH_HEADERS",
00186 "NNTP_LIST_SEARCH_HEADERS_RESPONSE",
00187 "NNTP_GET_PROPERTIES",
00188 "NNTP_GET_PROPERTIES_RESPONSE",
00189 "SEND_LIST_SUBSCRIPTIONS",
00190 "SEND_LIST_SUBSCRIPTIONS_RESPONSE",
00191 "SEND_FIRST_NNTP_COMMAND",
00192 "SEND_FIRST_NNTP_COMMAND_RESPONSE",
00193 "SETUP_NEWS_STREAM",
00194 "NNTP_BEGIN_AUTHORIZE",
00195 "NNTP_AUTHORIZE_RESPONSE",
00196 "NNTP_PASSWORD_RESPONSE",
00197 "NNTP_READ_LIST_BEGIN",
00198 "NNTP_READ_LIST",
00199 "DISPLAY_NEWSGROUPS",
00200 "NNTP_NEWGROUPS_BEGIN",
00201 "NNTP_NEWGROUPS",
00202 "NNTP_BEGIN_ARTICLE",
00203 "NNTP_READ_ARTICLE",
00204 "NNTP_XOVER_BEGIN",
00205 "NNTP_FIGURE_NEXT_CHUNK",
00206 "NNTP_XOVER_SEND",
00207 "NNTP_XOVER_RESPONSE",
00208 "NNTP_XOVER",
00209 "NEWS_PROCESS_XOVER",
00210 "NNTP_READ_GROUP",
00211 "NNTP_READ_GROUP_RESPONSE",
00212 "NNTP_READ_GROUP_BODY",
00213 "NNTP_SEND_GROUP_FOR_ARTICLE",
00214 "NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE",
00215 "NNTP_PROFILE_ADD",
00216 "NNTP_PROFILE_ADD_RESPONSE",
00217 "NNTP_PROFILE_DELETE",
00218 "NNTP_PROFILE_DELETE_RESPONSE",
00219 "NNTP_SEND_ARTICLE_NUMBER",
00220 "NEWS_PROCESS_BODIES",
00221 "NNTP_PRINT_ARTICLE_HEADERS",
00222 "NNTP_SEND_POST_DATA",
00223 "NNTP_SEND_POST_DATA_RESPONSE",
00224 "NNTP_CHECK_FOR_MESSAGE",
00225 "NEWS_NEWS_RC_POST",
00226 "NEWS_DISPLAY_NEWS_RC",
00227 "NEWS_DISPLAY_NEWS_RC_RESPONSE",
00228 "NEWS_START_CANCEL",
00229 "NEWS_DO_CANCEL",
00230 "NNTP_XPAT_SEND",
00231 "NNTP_XPAT_RESPONSE",
00232 "NNTP_SEARCH",
00233 "NNTP_SEARCH_RESPONSE",
00234 "NNTP_SEARCH_RESULTS",
00235 "NNTP_LIST_PRETTY_NAMES",
00236 "NNTP_LIST_PRETTY_NAMES_RESPONSE",
00237 "NNTP_LIST_XACTIVE_RESPONSE",
00238 "NNTP_LIST_XACTIVE",
00239 "NNTP_LIST_GROUP",
00240 "NNTP_LIST_GROUP_RESPONSE",
00241 "NEWS_DONE",
00242 "NEWS_POST_DONE",
00243 "NEWS_ERROR",
00244 "NNTP_ERROR",
00245 "NEWS_FREE",
00246 "NEWS_FINISHED"
00247 };
00248 
00249 
00250 /* end logging */
00251 
00252 /* Forward declarations */
00253 
00254 #define LIST_WANTED     0
00255 #define ARTICLE_WANTED  1
00256 #define CANCEL_WANTED   2
00257 #define GROUP_WANTED    3
00258 #define NEWS_POST       4
00259 #define READ_NEWS_RC    5
00260 #define NEW_GROUPS      6
00261 #define SEARCH_WANTED   7
00262 #define PRETTY_NAMES_WANTED 8
00263 #define PROFILE_WANTED      9
00264 #define IDS_WANTED          10
00265 
00266 /* the output_buffer_size must be larger than the largest possible line
00267  * 2000 seems good for news
00268  *
00269  * jwz: I increased this to 4k since it must be big enough to hold the
00270  * entire button-bar HTML, and with the new "mailto" format, that can
00271  * contain arbitrarily long header fields like "references".
00272  *
00273  * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
00274  */
00275 #define OUTPUT_BUFFER_SIZE (4096*2)
00276 
00277 /* the amount of time to subtract from the machine time
00278  * for the newgroup command sent to the nntp server
00279  */
00280 #define NEWGROUPS_TIME_OFFSET 60L*60L*12L   /* 12 hours */
00281 
00283 // TEMPORARY HARD CODED FUNCTIONS 
00285 #ifdef XP_WIN
00286 static char *XP_AppCodeName = "Mozilla";
00287 #else
00288 static const char *XP_AppCodeName = "Mozilla";
00289 #endif
00290 #define NET_IS_SPACE(x) ((x)==' ' || (x)=='\t')
00291 
00292 // turn "\xx" (with xx being hex numbers) in string into chars
00293 char *MSG_UnEscapeSearchUrl (const char *commandSpecificData)
00294 {
00295   nsCAutoString result(commandSpecificData);
00296   PRInt32 slashpos = 0;
00297   while (slashpos = result.FindChar('\\', slashpos),
00298          slashpos != kNotFound)
00299   {
00300     nsCAutoString hex;
00301     hex.Assign(Substring(result, slashpos + 1, 2));
00302     PRInt32 err, ch;
00303     ch = hex.ToInteger(&err, 16);
00304     result.Replace(slashpos, 3, err == NS_OK && ch != 0 ? (char) ch : 'X');
00305     slashpos++;
00306   }
00307   return ToNewCString(result);
00308 }
00309 
00311 // END OF TEMPORARY HARD CODED FUNCTIONS 
00313 
00314 NS_IMPL_ADDREF_INHERITED(nsNNTPProtocol, nsMsgProtocol)
00315 NS_IMPL_RELEASE_INHERITED(nsNNTPProtocol, nsMsgProtocol)
00316 
00317 NS_INTERFACE_MAP_BEGIN(nsNNTPProtocol)
00318   NS_INTERFACE_MAP_ENTRY(nsINNTPProtocol)
00319        NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
00320   NS_INTERFACE_MAP_ENTRY(nsICacheListener)
00321 NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
00322 
00323 nsNNTPProtocol::nsNNTPProtocol(nsIURI * aURL, nsIMsgWindow *aMsgWindow)
00324     : nsMsgProtocol(aURL)
00325 {
00326        if (!NNTP)
00327               NNTP = PR_NewLogModule("NNTP");
00328 
00329     m_ProxyServer = nsnull;
00330     m_lineStreamBuffer = nsnull;
00331     m_responseText = nsnull;
00332     m_dataBuf = nsnull;
00333     m_path = nsnull;
00334     
00335        m_cancelFromHdr = nsnull;
00336        m_cancelNewsgroups = nsnull;
00337        m_cancelDistribution = nsnull;
00338        m_cancelID = nsnull;
00339 
00340        m_messageID = nsnull;
00341     m_key = nsMsgKey_None;
00342 
00343     m_commandSpecificData = nsnull;
00344     m_searchData = nsnull;
00345 
00346        mBytesReceived = 0;
00347     mBytesReceivedSinceLastStatusUpdate = 0;
00348     m_startTime = PR_Now();
00349 
00350     if (aMsgWindow) {
00351         m_msgWindow = aMsgWindow;
00352     }
00353 
00354        m_runningURL = nsnull;
00355   SetIsBusy(PR_FALSE);
00356   m_fromCache = PR_FALSE;
00357     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) creating",this));
00358     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) initializing, so unset m_currentGroup",this));
00359        m_currentGroup.Truncate();
00360   LL_I2L(m_lastActiveTimeStamp, 0);
00361 }
00362 
00363 nsNNTPProtocol::~nsNNTPProtocol()
00364 {
00365   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) destroying",this));
00366   if (m_nntpServer) {
00367     m_nntpServer->WriteNewsrcFile();
00368     m_nntpServer->RemoveConnection(this);
00369   }
00370   if (m_lineStreamBuffer) {
00371      delete m_lineStreamBuffer;
00372   }
00373   if (mUpdateTimer) {
00374     mUpdateTimer->Cancel();
00375     mUpdateTimer = nsnull;
00376   }
00377   Cleanup();
00378 }
00379 
00380 void nsNNTPProtocol::Cleanup()  //free char* member variables
00381 {
00382   PR_FREEIF(m_responseText);
00383   PR_FREEIF(m_dataBuf);
00384   PR_FREEIF(m_path);
00385   PR_FREEIF(m_cancelFromHdr);
00386   PR_FREEIF(m_cancelNewsgroups);
00387   PR_FREEIF(m_cancelDistribution);
00388   PR_FREEIF(m_cancelID);
00389   PR_FREEIF(m_messageID);
00390   PR_FREEIF(m_commandSpecificData);
00391 }
00392 
00393 NS_IMETHODIMP nsNNTPProtocol::Initialize(nsIURI * aURL, nsIMsgWindow *aMsgWindow)
00394 {
00395     nsresult rv = NS_OK;
00396     PRBool isSecure = PR_FALSE;
00397 
00398     if (aMsgWindow) {
00399         m_msgWindow = aMsgWindow;
00400     }
00401     nsMsgProtocol::InitFromURI(aURL);
00402 
00403     nsCAutoString userPass;
00404     rv = m_url->GetUserPass(userPass);
00405     NS_ENSURE_SUCCESS(rv,rv);
00406 
00407     nsCAutoString hostName;
00408     rv = m_url->GetAsciiHost(hostName);
00409     NS_ENSURE_SUCCESS(rv,rv);
00410 
00411     nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00412     NS_ENSURE_SUCCESS(rv,rv);
00413 
00414     char *unescapedUserPass = ToNewCString(userPass);
00415     if (!unescapedUserPass)
00416       return NS_ERROR_OUT_OF_MEMORY;
00417     nsUnescape(unescapedUserPass);
00418 
00419     // find the server
00420     nsCOMPtr<nsIMsgIncomingServer> server;
00421     rv = accountManager->FindServer(unescapedUserPass, hostName.get(), "nntp",
00422                                     getter_AddRefs(server));
00423     PR_FREEIF(unescapedUserPass);
00424     NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
00425     if (!server) return NS_MSG_INVALID_OR_MISSING_SERVER;
00426     
00427     m_nntpServer = do_QueryInterface(server, &rv);
00428     NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
00429     if (!m_nntpServer) return NS_MSG_INVALID_OR_MISSING_SERVER;
00430 
00431     rv = m_nntpServer->GetMaxArticles(&m_maxArticles);
00432     NS_ENSURE_SUCCESS(rv,rv);
00433 
00434     rv = server->GetIsSecure(&isSecure);
00435     NS_ENSURE_SUCCESS(rv,rv);
00436 
00437     PRInt32 port = 0;
00438     rv = m_url->GetPort(&port);
00439     if (NS_FAILED(rv) || (port<=0)) {
00440               rv = server->GetPort(&port);
00441         if (NS_FAILED(rv)) return rv;
00442 
00443               if (port<=0) {
00444                      if (isSecure) {
00445               port = SECURE_NEWS_PORT;
00446               }
00447               else {
00448               port = NEWS_PORT;
00449               }
00450               }
00451 
00452         rv = m_url->SetPort(port);
00453         if (NS_FAILED(rv)) return rv;
00454     }
00455 
00456        NS_PRECONDITION(m_url , "invalid URL passed into NNTP Protocol");
00457 
00458        m_runningURL = do_QueryInterface(m_url);
00459   SetIsBusy(PR_TRUE);
00460 
00461        if (NS_SUCCEEDED(rv) && m_runningURL)
00462        {
00463        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
00464     if (mailnewsUrl)
00465     {
00466       mailnewsUrl->SetMsgWindow(aMsgWindow);
00467 
00468       m_runningURL->GetNewsAction(&m_newsAction);
00469       if (m_newsAction == nsINntpUrl::ActionFetchArticle || m_newsAction == nsINntpUrl::ActionFetchPart
00470         || m_newsAction == nsINntpUrl::ActionSaveMessageToDisk) {
00471         PRBool msgIsInLocalCache = PR_FALSE;
00472         mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache);
00473         if (msgIsInLocalCache)
00474           return NS_OK; // probably don't need to do anything else - definitely don't want
00475                         // to open the socket.
00476       }
00477     }
00478        }
00479   else {
00480     return rv;
00481   }
00482        
00483   if (!m_socketIsOpen)
00484   {
00485 
00486     // When we are making a secure connection, we need to make sure that we
00487     // pass an interface requestor down to the socket transport so that PSM can
00488     // retrieve a nsIPrompt instance if needed.
00489     nsCOMPtr<nsIInterfaceRequestor> ir;
00490     if (isSecure && aMsgWindow) {
00491       nsCOMPtr<nsIDocShell> docShell;
00492       aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
00493       ir = do_QueryInterface(docShell);
00494     }
00495 
00496     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) opening connection to %s on port %d",this, hostName.get(), port));
00497     // call base class to set up the transport
00498 
00499     PRInt32 port = 0;
00500     nsXPIDLCString hostName;
00501     m_url->GetPort(&port);
00502     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer);
00503     if (server)
00504       server->GetRealHostName(getter_Copies(hostName));
00505 
00506     nsCOMPtr<nsIProxyInfo> proxyInfo;
00507     rv = NS_ExamineForProxy("nntp", hostName.get(), port, getter_AddRefs(proxyInfo));
00508     if (NS_FAILED(rv)) proxyInfo = nsnull;
00509 
00510     rv = OpenNetworkSocketWithInfo(hostName.get(), port, isSecure ? "ssl" : nsnull, proxyInfo, ir);
00511 
00512        NS_ENSURE_SUCCESS(rv,rv);
00513        m_nextState = NNTP_LOGIN_RESPONSE;
00514   }
00515   else {
00516     m_nextState = SEND_FIRST_NNTP_COMMAND;
00517   }
00518        m_dataBuf = (char *) PR_Malloc(sizeof(char) * OUTPUT_BUFFER_SIZE);
00519        m_dataBufSize = OUTPUT_BUFFER_SIZE;
00520 
00521   if (!m_lineStreamBuffer)
00522          m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE /* create new lines */);
00523 
00524        m_typeWanted = 0;
00525        m_responseCode = 0;
00526        m_previousResponseCode = 0;
00527        m_responseText = nsnull;
00528 
00529        m_path = nsnull;
00530 
00531        m_firstArticle = 0;
00532        m_lastArticle = 0;
00533        m_firstPossibleArticle = 0;
00534        m_lastPossibleArticle = 0;
00535        m_numArticlesLoaded = 0;
00536        m_numArticlesWanted = 0;
00537 
00538        m_newsRCListIndex = 0;
00539     m_RCIndexToResumeAfterAuthRequest  = 0;
00540        m_newsRCListCount = 0;
00541        
00542        PR_FREEIF(m_messageID);
00543        m_messageID = nsnull;
00544 
00545     m_key = nsMsgKey_None;
00546 
00547        m_articleNumber = 0;
00548        m_originalContentLength = 0;
00549        m_cancelID = nsnull;
00550        m_cancelFromHdr = nsnull;
00551        m_cancelNewsgroups = nsnull;
00552        m_cancelDistribution = nsnull;
00553        return NS_OK;
00554 }
00555 
00556 NS_IMETHODIMP nsNNTPProtocol::GetIsBusy(PRBool *aIsBusy)
00557 {
00558   NS_ENSURE_ARG_POINTER(aIsBusy);
00559   *aIsBusy = m_connectionBusy;
00560   return NS_OK;
00561 }
00562 
00563 NS_IMETHODIMP nsNNTPProtocol::SetIsBusy(PRBool aIsBusy)
00564 {
00565   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) setting busy to %d",this, aIsBusy));
00566   m_connectionBusy = aIsBusy;
00567   return NS_OK;
00568 }
00569 
00570 NS_IMETHODIMP nsNNTPProtocol::GetIsCachedConnection(PRBool *aIsCachedConnection)
00571 {
00572   NS_ENSURE_ARG_POINTER(aIsCachedConnection);
00573   *aIsCachedConnection = m_fromCache;
00574   return NS_OK;
00575 }
00576 
00577 NS_IMETHODIMP nsNNTPProtocol::SetIsCachedConnection(PRBool aIsCachedConnection)
00578 {
00579   m_fromCache = aIsCachedConnection;
00580   return NS_OK;
00581 }
00582 
00583 /* void GetLastActiveTimeStamp (out PRTime aTimeStamp); */
00584 NS_IMETHODIMP nsNNTPProtocol::GetLastActiveTimeStamp(PRTime *aTimeStamp)
00585 {
00586   NS_ENSURE_ARG_POINTER(aTimeStamp);
00587   *aTimeStamp = m_lastActiveTimeStamp;
00588   return NS_OK;
00589 }
00590 
00591 NS_IMETHODIMP nsNNTPProtocol::LoadNewsUrl(nsIURI * aURL, nsISupports * aConsumer)
00592 {
00593   // clear the previous channel listener and use the new one....
00594   // don't reuse an existing channel listener
00595   m_channelListener = nsnull;
00596   m_channelListener = do_QueryInterface(aConsumer);
00597   nsCOMPtr<nsINntpUrl> newsUrl (do_QueryInterface(aURL));
00598   newsUrl->GetNewsAction(&m_newsAction);
00599 
00600   SetupPartExtractorListener(m_channelListener);
00601   return LoadUrl(aURL, aConsumer);
00602 }
00603 
00604 
00605 // WARNING: the cache stream listener is intended to be accessed from the UI thread!
00606 // it will NOT create another proxy for the stream listener that gets passed in...
00607 class nsNntpCacheStreamListener : public nsIStreamListener
00608 {
00609 public:
00610   NS_DECL_ISUPPORTS
00611   NS_DECL_NSIREQUESTOBSERVER
00612   NS_DECL_NSISTREAMLISTENER
00613 
00614   nsNntpCacheStreamListener ();
00615   virtual ~nsNntpCacheStreamListener();
00616 
00617   nsresult Init(nsIStreamListener * aStreamListener, nsIChannel* channel, nsIMsgMailNewsUrl *aRunningUrl);
00618 protected:
00619     nsCOMPtr<nsIChannel> mChannelToUse;
00620   nsCOMPtr<nsIStreamListener> mListener;
00621   nsCOMPtr<nsIMsgMailNewsUrl> mRunningUrl;
00622 };
00623 
00624 NS_IMPL_ADDREF(nsNntpCacheStreamListener)
00625 NS_IMPL_RELEASE(nsNntpCacheStreamListener)
00626 
00627 NS_INTERFACE_MAP_BEGIN(nsNntpCacheStreamListener)
00628    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
00629    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00630    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00631 NS_INTERFACE_MAP_END
00632 
00633 nsNntpCacheStreamListener::nsNntpCacheStreamListener()
00634 {
00635 }
00636 
00637 nsNntpCacheStreamListener::~nsNntpCacheStreamListener()
00638 {}
00639 
00640 nsresult nsNntpCacheStreamListener::Init(nsIStreamListener * aStreamListener, nsIChannel* channel,
00641                                          nsIMsgMailNewsUrl *aRunningUrl)
00642 {
00643   NS_ENSURE_ARG(aStreamListener);
00644   NS_ENSURE_ARG(channel);
00645   
00646   mChannelToUse = channel;
00647 
00648   mListener = aStreamListener;
00649   mRunningUrl = aRunningUrl;
00650   return NS_OK;
00651 }
00652 
00653 NS_IMETHODIMP
00654 nsNntpCacheStreamListener::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
00655 {
00656   nsCOMPtr <nsILoadGroup> loadGroup;
00657   nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
00658 
00659   mChannelToUse->GetLoadGroup(getter_AddRefs(loadGroup));
00660   if (loadGroup)
00661     loadGroup->AddRequest(ourRequest, nsnull /* context isupports */);
00662   return mListener->OnStartRequest(ourRequest, aCtxt);
00663 }
00664 
00665 NS_IMETHODIMP
00666 nsNntpCacheStreamListener::OnStopRequest(nsIRequest *request, nsISupports * aCtxt, nsresult aStatus)
00667 {
00668   nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
00669   nsresult rv = mListener->OnStopRequest(ourRequest, aCtxt, aStatus);
00670   nsCOMPtr <nsILoadGroup> loadGroup;
00671   mChannelToUse->GetLoadGroup(getter_AddRefs(loadGroup));
00672   if (loadGroup)
00673       loadGroup->RemoveRequest(ourRequest, nsnull, aStatus);
00674 
00675   // clear out mem cache entry so we're not holding onto it.
00676   if (mRunningUrl)
00677     mRunningUrl->SetMemCacheEntry(nsnull);
00678 
00679   mListener = nsnull;
00680   nsCOMPtr <nsINNTPProtocol> nntpProtocol = do_QueryInterface(mChannelToUse);
00681   if (nntpProtocol) {
00682     rv = nntpProtocol->SetIsBusy(PR_FALSE);
00683     NS_ENSURE_SUCCESS(rv,rv);
00684   }
00685   mChannelToUse = nsnull;
00686   return rv;
00687 }
00688 
00689 NS_IMETHODIMP
00690 nsNntpCacheStreamListener::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt, nsIInputStream * aInStream, PRUint32 aSourceOffset, PRUint32 aCount)
00691 {
00692     nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
00693     return mListener->OnDataAvailable(ourRequest, aCtxt, aInStream, aSourceOffset, aCount);
00694 }
00695 
00696 NS_IMETHODIMP nsNNTPProtocol::GetOriginalURI(nsIURI* *aURI)
00697 {
00698     // News does not seem to have the notion of an original URI (See Bug #193317)
00699     // *aURI = m_originalUrl ? m_originalUrl : m_url;
00700     *aURI = m_url;
00701     NS_IF_ADDREF(*aURI);
00702     return NS_OK; 
00703 }
00704  
00705 NS_IMETHODIMP nsNNTPProtocol::SetOriginalURI(nsIURI* aURI)
00706 {
00707     // News does not seem to have the notion of an original URI (See Bug #193317)
00708     return NS_OK;       // ignore
00709 }
00710  
00711 nsresult nsNNTPProtocol::SetupPartExtractorListener(nsIStreamListener * aConsumer)
00712 {
00713   PRBool convertData;
00714   nsresult rv = NS_OK;
00715 
00716   if (m_newsAction == nsINntpUrl::ActionFetchArticle)
00717   {
00718     nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_runningURL, &rv);
00719     NS_ENSURE_SUCCESS(rv,rv);
00720 
00721     nsCAutoString queryStr;
00722     rv = msgUrl->GetQuery(queryStr);
00723     NS_ENSURE_SUCCESS(rv,rv);
00724 
00725     // check if this is a filter plugin requesting the message.
00726     // in that case, set up a text converter
00727     convertData = (queryStr.Find("header=filter") != kNotFound 
00728       || queryStr.Find("header=attach") != kNotFound);
00729   }
00730   else
00731   {
00732     convertData = (m_newsAction == nsINntpUrl::ActionFetchPart);
00733   }
00734   if (convertData)
00735   {
00736     nsCOMPtr<nsIStreamConverterService> converter = do_GetService("@mozilla.org/streamConverters;1");
00737     if (converter && aConsumer)
00738     {
00739       nsCOMPtr<nsIStreamListener> newConsumer;
00740       nsCOMPtr<nsIChannel> channel;
00741       QueryInterface(NS_GET_IID(nsIChannel), getter_AddRefs(channel));
00742       converter->AsyncConvertData("message/rfc822", "*/*",
00743            aConsumer, channel, getter_AddRefs(newConsumer));
00744       if (newConsumer)
00745         m_channelListener = newConsumer;
00746     }
00747   }
00748 
00749   return rv;
00750 }
00751 
00752 nsresult nsNNTPProtocol::ReadFromMemCache(nsICacheEntryDescriptor *entry)
00753 {
00754   NS_ENSURE_ARG(entry);
00755 
00756   nsCOMPtr<nsIInputStream> cacheStream;
00757   nsresult rv = entry->OpenInputStream(0, getter_AddRefs(cacheStream));
00758      
00759   if (NS_SUCCEEDED(rv))
00760   {
00761     nsCOMPtr<nsIInputStreamPump> pump;
00762     rv = NS_NewInputStreamPump(getter_AddRefs(pump), cacheStream);
00763     if (NS_FAILED(rv)) return rv;
00764 
00765     nsXPIDLCString group;
00766     nsXPIDLCString commandSpecificData;
00767     // do this to get m_key set, so that marking the message read will work.
00768     PR_FREEIF(m_messageID);
00769     rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
00770 
00771     nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener();
00772     if (!cacheListener)
00773        return NS_ERROR_OUT_OF_MEMORY;
00774 
00775     NS_ADDREF(cacheListener);
00776 
00777     SetLoadGroup(m_loadGroup);
00778     m_typeWanted = ARTICLE_WANTED;
00779 
00780     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
00781     cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl);
00782     
00783     m_ContentType = ""; // reset the content type for the upcoming read....
00784 
00785     rv = pump->AsyncRead(cacheListener, m_channelContext);
00786     NS_RELEASE(cacheListener);
00787  
00788     if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
00789     {
00790       // we're not calling nsMsgProtocol::AsyncRead(), which calls nsNNTPProtocol::LoadUrl, so we need to take care of some stuff it does.
00791       m_channelListener = nsnull;
00792       return rv;
00793     }
00794   }
00795 
00796   return rv;
00797 }
00798 
00799 nsresult nsNNTPProtocol::ReadFromNewsConnection()
00800 {
00801   return nsMsgProtocol::AsyncOpen(m_channelListener, m_channelContext);
00802 }
00803 
00804 // for messages stored in our offline cache, we have special code to handle that...
00805 // If it's in the local cache, we return true and we can abort the download because
00806 // this method does the rest of the work.
00807 PRBool nsNNTPProtocol::ReadFromLocalCache()
00808 {
00809   PRBool msgIsInLocalCache = PR_FALSE;
00810   nsresult rv = NS_OK;
00811   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
00812   mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache);
00813 
00814   if (msgIsInLocalCache)
00815   {
00816     nsXPIDLCString group;
00817     nsXPIDLCString commandSpecificData;
00818     PR_FREEIF(m_messageID);
00819     rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
00820     nsCOMPtr <nsIMsgFolder> folder = do_QueryInterface(m_newsFolder);
00821     if (folder && NS_SUCCEEDED(rv))
00822     {
00823     // we want to create a file channel and read the msg from there.
00824       nsCOMPtr<nsIInputStream> fileStream;
00825       PRUint32 offset=0, size=0;
00826       rv = folder->GetOfflineFileStream(m_key, &offset, &size, getter_AddRefs(fileStream));
00827 
00828       // get the file stream from the folder, somehow (through the message or
00829       // folder sink?) We also need to set the transfer offset to the message offset
00830       if (fileStream && NS_SUCCEEDED(rv))
00831       {
00832         // dougt - This may break the ablity to "cancel" a read from offline mail reading.
00833         // fileChannel->SetLoadGroup(m_loadGroup);
00834 
00835         m_typeWanted = ARTICLE_WANTED;
00836 
00837         nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener();
00838         if (!cacheListener)
00839           return PR_FALSE;
00840 
00841         NS_ADDREF(cacheListener);
00842         cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl);
00843 
00844         // create a stream pump that will async read the specified amount of data.
00845         // XXX make offset and size 64-bit ints
00846         nsCOMPtr<nsIInputStreamPump> pump;
00847         rv = NS_NewInputStreamPump(getter_AddRefs(pump),
00848                                    fileStream, nsInt64(offset), nsInt64(size));
00849         if (NS_SUCCEEDED(rv))
00850           rv = pump->AsyncRead(cacheListener, m_channelContext);
00851 
00852         NS_RELEASE(cacheListener);
00853 
00854         if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
00855         {
00856           m_ContentType.Truncate();
00857           m_channelListener = nsnull;
00858           return PR_TRUE;
00859         }
00860       }
00861     }
00862   }
00863 
00864   return PR_FALSE;
00865 }
00866 
00867 NS_IMETHODIMP
00868 nsNNTPProtocol::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status)
00869 {
00870   nsresult rv = NS_OK;
00871 
00872   if (NS_SUCCEEDED(status)) 
00873   {
00874     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
00875     mailnewsUrl->SetMemCacheEntry(entry);
00876 
00877     // if we have write access then insert a "stream T" into the flow so data 
00878     // gets written to both 
00879     if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ))
00880     {
00881       // use a stream listener Tee to force data into the cache and to our current channel listener...
00882       nsCOMPtr<nsIStreamListener> newListener;
00883       nsCOMPtr<nsIStreamListenerTee> tee = do_CreateInstance(kStreamListenerTeeCID, &rv);
00884       NS_ENSURE_SUCCESS(rv, rv);
00885 
00886       nsCOMPtr<nsIOutputStream> out;
00887       rv = entry->OpenOutputStream(0, getter_AddRefs(out));
00888       NS_ENSURE_SUCCESS(rv, rv);
00889 
00890       rv = tee->Init(m_channelListener, out);
00891       m_channelListener = do_QueryInterface(tee);
00892       NS_ENSURE_SUCCESS(rv, rv);
00893     }
00894     else
00895     {
00896       rv = ReadFromMemCache(entry);
00897       if (access & nsICache::ACCESS_WRITE)
00898         entry->MarkValid();
00899       if (NS_SUCCEEDED(rv)) return NS_OK; // kick out if reading from the cache succeeded...
00900     }
00901   } // if we got a valid entry back from the cache...
00902 
00903   // if reading from the cache failed or if we are writing into the cache, default to ReadFromImapConnection.
00904   return ReadFromNewsConnection();
00905 }
00906 
00907 nsresult nsNNTPProtocol::OpenCacheEntry()
00908 {
00909   nsresult rv = NS_OK;
00910   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
00911   // get the cache session from our nntp service...
00912   nsCOMPtr <nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
00913   NS_ENSURE_SUCCESS(rv, rv);
00914 
00915   nsCOMPtr<nsICacheSession> cacheSession;
00916   rv = nntpService->GetCacheSession(getter_AddRefs(cacheSession));
00917   NS_ENSURE_SUCCESS(rv, rv);
00918 
00919   // Open a cache entry with key = url
00920   nsCAutoString urlSpec;
00921   mailnewsUrl->GetAsciiSpec(urlSpec);
00922   // for now, truncate of the query part so we don't duplicate urls in the cache...
00923   char * anchor = (char *)strrchr(urlSpec.BeginWriting(), '?');
00924   if (anchor)
00925     *anchor = '\0';
00926   return cacheSession->AsyncOpenCacheEntry(urlSpec, nsICache::ACCESS_READ_WRITE, this);
00927 }
00928 
00929 NS_IMETHODIMP nsNNTPProtocol::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
00930 {
00931   nsresult rv;
00932   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
00933   NS_ENSURE_SUCCESS(rv,rv);
00934 
00935   PRInt32 port;
00936   rv = mailnewsUrl->GetPort(&port);
00937   if (NS_FAILED(rv))
00938       return rv;
00939  
00940   rv = NS_CheckPortSafety(port, "news");
00941   if (NS_FAILED(rv))
00942       return rv;
00943 
00944   m_channelContext = ctxt;
00945   m_channelListener = listener;
00946   m_runningURL->GetNewsAction(&m_newsAction);
00947   // first, check if this is a message load that should come from either
00948   // the memory cache or the local msg cache.
00949   if (mailnewsUrl && (m_newsAction == nsINntpUrl::ActionFetchArticle || m_newsAction == nsINntpUrl::ActionFetchPart
00950     || m_newsAction == nsINntpUrl::ActionSaveMessageToDisk))
00951   {
00952 
00953     SetupPartExtractorListener(m_channelListener);
00954     if (ReadFromLocalCache())
00955      return NS_OK;
00956 
00957     rv = OpenCacheEntry();
00958     if (NS_SUCCEEDED(rv)) return NS_OK; // if this didn't return an error then jump out now...
00959 
00960   }
00961   nsCOMPtr<nsIRequest> parentRequest;
00962   return nsMsgProtocol::AsyncOpen(listener, ctxt);
00963 }
00964 
00965 nsresult nsNNTPProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
00966 {
00967   NS_ENSURE_ARG_POINTER(aURL);
00968 
00969   nsXPIDLCString group;
00970   nsXPIDLCString commandSpecificData;
00971   PRBool cancel = PR_FALSE;
00972   m_ContentType.Truncate();
00973   nsresult rv = NS_OK;
00974 
00975   m_runningURL = do_QueryInterface(aURL, &rv);
00976   if (NS_FAILED(rv)) return rv;
00977   m_runningURL->GetNewsAction(&m_newsAction);
00978 
00979   SetIsBusy(PR_TRUE);
00980 
00981   PR_FREEIF(m_messageID);
00982   m_messageID = nsnull;
00983 
00984   rv = ParseURL(aURL, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
00985   NS_ASSERTION(NS_SUCCEEDED(rv),"failed to parse news url");
00986   //if (NS_FAILED(rv)) return rv;
00987   // XXX group returned from ParseURL is assumed to be in UTF-8
00988   NS_ASSERTION(IsUTF8(group), "newsgroup name is not in UTF-8"); 
00989 
00990   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) m_messageID = %s",this, m_messageID?m_messageID:"(null)"));
00991   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) group = %s",this,group.get()));
00992   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) commandSpecificData = %s",this,commandSpecificData.get()?commandSpecificData.get():"(null)"));
00993   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) m_key = %d",this,m_key));
00994  
00995   // for now, only support "news://host/message-id?cancel", and not "news://host/group#key?cancel"
00996   if (m_messageID && !PL_strcmp(commandSpecificData.get(), "?cancel")) {
00997      // XXX todo, don't allow manual cancel urls.  only allow internal ones
00998      cancel = PR_TRUE;
00999   }
01000 
01001   NS_MsgSACopy(&m_path, m_messageID);
01002 
01003   /* We are posting a user-written message
01004      if and only if this message has a message to post
01005      Cancel messages are created later with a ?cancel URL
01006   */
01007   nsCOMPtr <nsINNTPNewsgroupPost> message;
01008   rv = m_runningURL->GetMessageToPost(getter_AddRefs(message));
01009   if (NS_SUCCEEDED(rv) && message)
01010   {
01011     m_typeWanted = NEWS_POST;
01012     NS_MsgSACopy(&m_path, "");
01013   }
01014   else 
01015     if (m_messageID || (m_key != nsMsgKey_None))
01016     {
01017     /* 
01018     news-message://HOST/GROUP#key
01019     news://HOST/MESSAGE_ID
01020     
01021       not sure about these:
01022       
01023         news:MESSAGE_ID
01024         news:/GROUP/MESSAGE_ID (useless)
01025         news://HOST/GROUP/MESSAGE_ID (useless)
01026       */
01027       if (cancel)
01028         m_typeWanted = CANCEL_WANTED;
01029       else
01030         m_typeWanted = ARTICLE_WANTED;
01031     }
01032     else if (!commandSpecificData.IsEmpty())
01033     {
01034       if (PL_strstr (commandSpecificData.get(), "?newgroups"))
01035       /* news://HOST/?newsgroups
01036       news:/GROUP/?newsgroups (useless)
01037       news:?newsgroups (default host)
01038             */
01039              m_typeWanted = NEW_GROUPS;
01040       else
01041       {
01042         if (PL_strstr(commandSpecificData.get(), "?list-pretty"))
01043         {
01044           m_typeWanted = PRETTY_NAMES_WANTED;
01045           m_commandSpecificData = ToNewCString(commandSpecificData);
01046         }
01047         else if (PL_strstr(commandSpecificData.get(), "?profile"))
01048         {
01049           m_typeWanted = PROFILE_WANTED;
01050           m_commandSpecificData = ToNewCString(commandSpecificData);
01051         }
01052         else if (PL_strstr(commandSpecificData.get(), "?list-ids"))
01053         {
01054           m_typeWanted = IDS_WANTED;
01055           m_commandSpecificData = ToNewCString(commandSpecificData);
01056           
01057           rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
01058           if (!m_newsFolder) goto FAIL;
01059         }
01060         else
01061         {
01062           m_typeWanted = SEARCH_WANTED;
01063           m_commandSpecificData = ToNewCString(commandSpecificData);
01064           nsUnescape(m_commandSpecificData);
01065           m_searchData = m_commandSpecificData;
01066           
01067           
01068           rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
01069           if (!m_newsFolder) goto FAIL;
01070         }
01071       }
01072     }
01073     else if (!group.IsEmpty())
01074     {
01075     /* news:GROUP
01076     news:/GROUP
01077     news://HOST/GROUP
01078       */
01079       if (PL_strchr(group.get(),'*')) {
01080         // getting all the newsgroups on the server, for subscribe dialog
01081         m_typeWanted = LIST_WANTED;
01082       }
01083       else 
01084       {
01085         if (m_nntpServer)
01086         {
01087           PRBool containsGroup = PR_TRUE;
01088           rv = m_nntpServer->ContainsNewsgroup(group, &containsGroup);
01089           if (NS_FAILED(rv)) goto FAIL;
01090           
01091           if (!containsGroup) 
01092           {
01093             // We have the name of a newsgroup which we're not subscribed to,
01094             // the next step is to ask the user whether we should subscribe to it.
01095             
01096             nsCOMPtr<nsIPrompt> dialog;
01097             
01098             if (m_msgWindow)
01099               m_msgWindow->GetPromptDialog(getter_AddRefs(dialog));
01100             
01101             if (!dialog)
01102             {
01103               nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01104               wwatch->GetNewPrompter(nsnull, getter_AddRefs(dialog));
01105             }
01106             
01107             nsXPIDLString statusString, confirmText;
01108             nsCOMPtr<nsIStringBundle> bundle;
01109             nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
01110             
01111             // to handle non-ASCII newsgroup names, we store them internally
01112             // as escaped. decode and unescape the newsgroup name so we'll
01113             // display a proper name.
01114 
01115             nsAutoString unescapedName;
01116             rv = NS_MsgDecodeUnescapeURLPath(group, unescapedName);
01117             NS_ENSURE_SUCCESS(rv,rv);
01118             
01119             bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
01120             const PRUnichar *formatStrings[1] = { unescapedName.get() };
01121             
01122             rv = bundle->FormatStringFromName(NS_LITERAL_STRING("autoSubscribeText").get(),
01123               formatStrings, 1,
01124               getter_Copies(confirmText));
01125             NS_ENSURE_SUCCESS(rv,rv);
01126             
01127             PRBool confirmResult = PR_FALSE;
01128             rv = dialog->Confirm(nsnull, confirmText, &confirmResult);
01129             NS_ENSURE_SUCCESS(rv, rv);
01130             
01131             if (confirmResult)
01132             {
01133               rv = m_nntpServer->SubscribeToNewsgroup(group);
01134               containsGroup = PR_TRUE;
01135             }
01136             else {
01137               // XXX FIX ME
01138               // the way news is current written, we've already opened the socket
01139               // and initialized the connection.
01140               //
01141               // until that is fixed, when the user cancels an autosubscribe, we've got to close it and clean up after ourselves
01142               //
01143               // see bug http://bugzilla.mozilla.org/show_bug.cgi?id=108293
01144               // another problem, autosubscribe urls are ending up as cache entries
01145               // because the default action on nntp urls is ActionFetchArticle
01146               //
01147               // see bug http://bugzilla.mozilla.org/show_bug.cgi?id=108294
01148               if (m_runningURL)
01149                 FinishMemCacheEntry(PR_FALSE); // cleanup mem cache entry
01150               
01151               return CloseConnection();
01152             }
01153           }
01154           
01155           // If we have a group (since before, or just subscribed), set the m_newsFolder.
01156           if (containsGroup)
01157           {
01158             rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
01159             if (!m_newsFolder) goto FAIL;
01160           }
01161         }
01162         m_typeWanted = GROUP_WANTED;
01163       }
01164     }
01165     else
01166       // news: or news://HOST
01167       m_typeWanted = READ_NEWS_RC;
01168     
01169     // if this connection comes from the cache, we need to initialize the
01170     // load group here, by generating the start request notification. nsMsgProtocol::OnStartRequest
01171     // ignores the first parameter (which is supposed to be the channel) so we'll pass in null.
01172     if (m_fromCache)
01173       nsMsgProtocol::OnStartRequest(nsnull, aURL);
01174     
01175       /* At this point, we're all done parsing the URL, and know exactly
01176       what we want to do with it.
01177     */
01178 #ifdef UNREADY_CODE
01179 #ifndef NO_ARTICLE_CACHEING
01180     /* Turn off caching on all news entities, except articles. */
01181     /* It's very important that this be turned off for CANCEL_WANTED;
01182     for the others, I don't know what cacheing would cause, but
01183     it could only do harm, not good. */
01184     if(m_typeWanted != ARTICLE_WANTED)
01185 #endif
01186       ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
01187 #endif
01188     
01189     
01190 FAIL:
01191     if (NS_FAILED(rv))
01192     {
01193       AlertError(rv, nsnull);
01194       return rv;
01195     }
01196     else 
01197     {
01198       if (!m_socketIsOpen)
01199       {
01200         m_nextStateAfterResponse = m_nextState;
01201         m_nextState = NNTP_RESPONSE; 
01202       }
01203       rv = nsMsgProtocol::LoadUrl(aURL, aConsumer);
01204     }
01205     
01206     return rv;
01207 
01208 }
01209 
01210 void nsNNTPProtocol::FinishMemCacheEntry(PRBool valid)
01211 {
01212   nsCOMPtr <nsICacheEntryDescriptor> memCacheEntry;
01213   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
01214   if (mailnewsurl)
01215     mailnewsurl->GetMemCacheEntry(getter_AddRefs(memCacheEntry));
01216   if (memCacheEntry)
01217   {
01218     if (valid)
01219       memCacheEntry->MarkValid();
01220     else
01221       memCacheEntry->Doom();
01222   }
01223 }
01224 
01225 // stop binding is a "notification" informing us that the stream associated with aURL is going away. 
01226 NS_IMETHODIMP nsNNTPProtocol::OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus)
01227 {
01228     // either remove mem cache entry, or mark it valid if url successful and 
01229     // command succeeded
01230     FinishMemCacheEntry(NS_SUCCEEDED(aStatus) 
01231       && MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK);
01232 
01233     nsMsgProtocol::OnStopRequest(request, aContext, aStatus);
01234 
01235     // nsMsgProtocol::OnStopRequest() has called m_channelListener. There is
01236     // no need to be called again in CloseSocket(). Let's clear it here.
01237     if (m_channelListener) {
01238         m_channelListener = nsnull;
01239     }
01240 
01241        // okay, we've been told that the send is done and the connection is going away. So 
01242        // we need to release all of our state
01243        return CloseSocket();
01244 }
01245 
01246 NS_IMETHODIMP nsNNTPProtocol::Cancel(nsresult status)  // handle stop button
01247 {
01248        m_nextState = NNTP_ERROR;
01249        return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
01250 }
01251 
01252 /* 
01253    FIX THIS COMMENT, this is how 4.x worked.  6.x is different.
01254 
01255    The main news load init routine, and URL parser.
01256    The syntax of news URLs is:
01257 
01258         To list all hosts:
01259           news:
01260 
01261         To list all groups on a host, or to post a new message:
01262           news://HOST
01263 
01264         To list some articles in a group:
01265           news:GROUP
01266           news:/GROUP
01267           news://HOST/GROUP
01268 
01269         To list a particular range of articles in a group:
01270           news:GROUP/N1-N2
01271           news:/GROUP/N1-N2
01272           news://HOST/GROUP/N1-N2
01273 
01274         To retrieve an article:
01275           news:MESSAGE-ID                (message-id must contain "@")
01276 
01277     To cancel an article:
01278           news:MESSAGE-ID?cancel
01279 
01280         (Note that message IDs may contain / before the @:)
01281 
01282           news:SOME/ID@HOST?headers=all
01283           news:/SOME/ID@HOST?headers=all
01284           news:/SOME?ID@HOST?headers=all
01285         are the same as
01286           news://HOST/SOME/ID@HOST?headers=all
01287           news://HOST//SOME/ID@HOST?headers=all
01288           news://HOST//SOME?ID@HOST?headers=all
01289         bug: if the ID is <//xxx@host> we will parse this correctly:
01290           news://non-default-host///xxx@host
01291         but will mis-parse it if it's on the default host:
01292           news://xxx@host
01293         whoever had the idea to leave the <> off the IDs should be flogged.
01294               So, we'll make sure we quote / in message IDs as %2F.
01295  */
01296 nsresult 
01297 nsNNTPProtocol::ParseURL(nsIURI * aURL, char ** aGroup, char ** aMessageID,
01298                                                           char ** aCommandSpecificData)
01299 {
01300     NS_ENSURE_ARG_POINTER(aURL);
01301     NS_ENSURE_ARG_POINTER(aGroup);
01302     NS_ENSURE_ARG_POINTER(aMessageID);
01303     NS_ENSURE_ARG_POINTER(aCommandSpecificData);
01304 
01305        PRInt32 status = 0;
01306        char *group = 0;
01307     char *message_id = 0;
01308     char *command_specific_data = 0;
01309        char *s = 0;
01310 
01311     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ParseURL",this));
01312 
01313     nsresult rv;
01314     nsCOMPtr <nsIMsgFolder> folder;
01315     nsCOMPtr <nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
01316     NS_ENSURE_SUCCESS(rv,rv);
01317 
01318     nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningURL, &rv);
01319     NS_ENSURE_SUCCESS(rv,rv);
01320 
01321     nsXPIDLCString spec;
01322     rv = msgUrl->GetOriginalSpec(getter_Copies(spec));
01323     NS_ENSURE_SUCCESS(rv,rv);
01324 
01325     // if the original spec is non empty, use it to determine m_newsFolder and m_key
01326     if (spec.get() && spec.get()[0]) {
01327         PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) original message spec = %s",this,spec.get()));
01328 
01329         rv = nntpService->DecomposeNewsURI(spec.get(), getter_AddRefs(folder), &m_key);
01330         NS_ENSURE_SUCCESS(rv,rv);
01331 
01332         // since we are reading a message in this folder, we can set m_newsFolder
01333         m_newsFolder = do_QueryInterface(folder, &rv);
01334         NS_ENSURE_SUCCESS(rv,rv);
01335 
01336         // if we are cancelling, we aren't done.  we still need to parse out the messageID from the url
01337         // later, we'll use m_newsFolder and m_key to delete the message from the DB, if the cancel is successful.
01338         if (m_newsAction != nsINntpUrl::ActionCancelArticle) {
01339             return NS_OK;
01340         }
01341     }
01342     else {
01343         // clear this, we'll set it later.
01344         m_newsFolder = nsnull;
01345         m_currentGroup.Truncate();
01346     }
01347 
01348        // get the file path part and store it as the group...
01349        nsCAutoString fullPath;
01350        rv = aURL->GetPath(fullPath);
01351     NS_ENSURE_SUCCESS(rv,rv);
01352 
01353     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) fullPath = %s",this, fullPath.get()));
01354 
01355        if (fullPath.First() == '/')
01356               group = nsCRT::strdup(fullPath.get()+1); 
01357        else
01358               group = nsCRT::strdup(fullPath.get());
01359 
01360        // more to do here, but for now, this works.
01361        // only escape if we are doing a search
01362        if (m_newsAction == nsINntpUrl::ActionSearch) { 
01363               nsUnescape(group);
01364        }
01365        else if (strchr(group, '@') || strstr(group,"%40")) {
01366       message_id = nsUnescape(group);
01367          group = 0;
01368        }
01369     else if (!*group) {
01370          nsCRT::free(group);
01371          group = 0;
01372        }
01373 
01374     /* At this point, the search data is attached to `message_id' (if there
01375         is one) or `group' (if there is one) or `host_and_port' (otherwise.)
01376         Separate the search data from what it is clinging to, being careful
01377         to interpret the "?" only if it comes after the "@" in an ID, since
01378         the syntax of message IDs is tricky.  (There may be a ? in the
01379         random-characters part of the ID (before @), but not in the host part
01380         (after @).)
01381    */
01382    if (message_id || group) {
01383          char *start;
01384          if (message_id) {
01385                 start = PL_strchr(message_id,'@');
01386          }
01387          else {
01388                 start = group; /*  ? group : hostAndPort; */ // mscott -- fix me...necko sloppiness on my part
01389          }
01390 
01391          /* Take off the "?" or "#" search data */
01392          for (s = start; *s; s++)
01393               if (*s == '?' || *s == '#')
01394                 break;
01395          
01396          if (*s) {
01397                 command_specific_data = nsCRT::strdup (s);
01398                 *s = 0;
01399                 if (!command_specific_data) {
01400                        status = MK_OUT_OF_MEMORY;
01401                        goto FAIL;
01402           }
01403       }
01404 
01405          /* Discard any now-empty strings. */
01406          if (message_id && !*message_id) {
01407                 PR_Free (message_id);
01408                 message_id = 0;
01409       }
01410          else if (group && !*group) {
01411                 PR_Free(group);
01412                 group = 0;
01413       }
01414        }
01415 
01416   FAIL:
01417   NS_ASSERTION (!message_id || message_id != group, "something not null");
01418   if (status >= 0) {  
01419       *aGroup = group;
01420       *aMessageID = message_id;
01421       *aCommandSpecificData = command_specific_data;
01422   }
01423   else {
01424          PR_FREEIF(group);
01425          PR_FREEIF(message_id);
01426          PR_FREEIF(command_specific_data);
01427   }
01428 
01429   // if we are cancelling, we've got our message id, our m_key and our m_newsFolder.
01430   // bail out now to prevent messing those up.
01431   if (m_newsAction == nsINntpUrl::ActionCancelArticle) {
01432     if (status < 0)
01433       return NS_ERROR_FAILURE;
01434     else
01435       return NS_OK;
01436   }
01437 
01438   nsCAutoString serverURI;
01439 
01440   if (*aMessageID) {
01441     // if this is a message id, use the pre path (the server) for the folder uri.
01442     rv = aURL->GetPrePath(serverURI);
01443     NS_ENSURE_SUCCESS(rv,rv);
01444   }
01445   else if (*aGroup) {
01446     if (PL_strchr(*aGroup,'*')) {
01447       rv = aURL->GetPrePath(serverURI);
01448       NS_ENSURE_SUCCESS(rv,rv);
01449     }
01450     else {
01451       // let if fall through.  we'll set m_newsFolder later.
01452     }
01453   }
01454 
01455   if (!serverURI.IsEmpty()) {
01456     // if we get here, we, we are either doing:
01457     // news://host/message-id or news://host/*
01458     // (but not news://host/message-id?cancel)
01459     // for authentication, we set m_newsFolder to be the server's folder.
01460     // while we are here, we set m_nntpServer.
01461     rv = nntpService->DecomposeNewsURI(serverURI.get(), getter_AddRefs(folder), &m_key);
01462     NS_ENSURE_SUCCESS(rv,rv);
01463     
01464     // since we are reading a message in this folder, we can set m_newsFolder
01465     m_newsFolder = do_QueryInterface(folder, &rv);
01466     NS_ENSURE_SUCCESS(rv,rv);
01467     
01468     rv = m_newsFolder->GetNntpServer(getter_AddRefs(m_nntpServer));
01469     NS_ENSURE_SUCCESS(rv,rv);
01470 
01471     m_currentGroup.Truncate();
01472   }
01473 
01474   // mscott - this function might need to be re-written to use nsresults
01475   // so we don't lose the nature of the error in this return code I'm adding.
01476   if (status < 0)
01477          return NS_ERROR_FAILURE;
01478   else
01479          return NS_OK;
01480 }
01481 /*
01482  * Writes the data contained in dataBuffer into the current output stream. It also informs
01483  * the transport layer that this data is now available for transmission.
01484  * Returns a positive number for success, 0 for failure (not all the bytes were written to the
01485  * stream, etc). We need to make another pass through this file to install an error system (mscott)
01486  */
01487 
01488 PRInt32 nsNNTPProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
01489 {
01490     if (!aSuppressLogging) {
01491         NNTP_LOG_WRITE(dataBuffer);
01492     }
01493     else {
01494         PR_LOG(NNTP, out, ("(%p) Logging suppressed for this command (it probably contained authentication information)", this));
01495     }
01496 
01497        return nsMsgProtocol::SendData(aURL, dataBuffer); // base class actually transmits the data
01498 }
01499 
01500 /* gets the response code from the nntp server and the
01501  * response line
01502  *
01503  * returns the TCP return code from the read
01504  */
01505 PRInt32 nsNNTPProtocol::NewsResponse(nsIInputStream * inputStream, PRUint32 length)
01506 {
01507        PRUint32 status = 0;
01508 
01509        NS_PRECONDITION(nsnull != inputStream, "invalid input stream");
01510        
01511        PRBool pauseForMoreData = PR_FALSE;
01512        char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01513 
01514        NNTP_LOG_READ(line);
01515 
01516     if(pauseForMoreData)
01517        {
01518               SetFlag(NNTP_PAUSE_FOR_READ);
01519               return 0;
01520        }
01521 
01522        if(!line)
01523               return NS_ERROR_FAILURE;
01524 
01525     ClearFlag(NNTP_PAUSE_FOR_READ);  /* don't pause if we got a line */
01526 
01527     /* almost correct */
01528     if(status > 1)
01529        {
01530               mBytesReceived += status;
01531         mBytesReceivedSinceLastStatusUpdate += status;
01532        }
01533 
01534     m_previousResponseCode = m_responseCode;
01535 
01536     PR_sscanf(line, "%d", &m_responseCode);
01537 
01538     if (m_responseCode && PL_strlen(line) > 3)
01539         NS_MsgSACopy(&m_responseText, line + 4);
01540     else
01541         NS_MsgSACopy(&m_responseText, line);
01542 
01543               /* login failed */
01544   if (m_responseCode == MK_NNTP_RESPONSE_AUTHINFO_DENIED)
01545     HandleAuthenticationFailure();
01546 
01547 
01548        /* authentication required can come at any time
01549         */
01550        if (MK_NNTP_RESPONSE_AUTHINFO_REQUIRE == m_responseCode ||
01551         MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_REQUIRE == m_responseCode) 
01552        {
01553               m_nextState = NNTP_BEGIN_AUTHORIZE;
01554         GotAuthorizationRequest();
01555        }
01556        else if (MK_NNTP_RESPONSE_PERMISSION_DENIED == m_responseCode)
01557        {
01558               PR_FREEIF(line);
01559               return (0);
01560        }
01561        else {
01562        m_nextState = m_nextStateAfterResponse;
01563     }
01564 
01565        PR_FREEIF(line);
01566     return(0);  /* everything ok */
01567 }
01568 
01569 /* interpret the server response after the connect
01570  *
01571  * returns negative if the server responds unexpectedly
01572  */
01573  
01574 PRInt32 nsNNTPProtocol::LoginResponse()
01575 {
01576        PRBool postingAllowed = m_responseCode == MK_NNTP_RESPONSE_POSTING_ALLOWED;
01577 
01578     if(MK_NNTP_RESPONSE_TYPE(m_responseCode)!=MK_NNTP_RESPONSE_TYPE_OK)
01579        {
01580               AlertError(MK_NNTP_ERROR_MESSAGE, m_responseText);
01581 
01582        m_nextState = NNTP_ERROR;
01583         return MK_BAD_NNTP_CONNECTION;
01584        }
01585 
01586        m_nntpServer->SetPostingAllowed(postingAllowed);
01587     m_nextState = NNTP_SEND_MODE_READER;
01588     return(0);  /* good */
01589 }
01590 
01591 PRInt32 nsNNTPProtocol::SendModeReader()
01592 {  
01593        nsresult rv = NS_OK;
01594        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL,&rv);
01595     NS_ENSURE_SUCCESS(rv,rv);
01596 
01597        rv = SendData(mailnewsurl, NNTP_CMD_MODE_READER); 
01598     m_nextState = NNTP_RESPONSE;
01599     m_nextStateAfterResponse = NNTP_SEND_MODE_READER_RESPONSE;
01600     SetFlag(NNTP_PAUSE_FOR_READ); 
01601 
01602     NS_ENSURE_SUCCESS(rv,rv);
01603     return rv;
01604 }
01605 
01606 PRInt32 nsNNTPProtocol::SendModeReaderResponse()
01607 {
01608   SetFlag(NNTP_READER_PERFORMED);
01609   
01610   /* ignore the response code and continue
01611         */
01612   PRBool pushAuth = PR_FALSE;
01613   nsresult rv = NS_OK;
01614 
01615   NS_ASSERTION(m_nntpServer, "no server, see bug #107797");
01616   if (m_nntpServer) {
01617     rv = m_nntpServer->GetPushAuth(&pushAuth);
01618   }
01619   if (NS_SUCCEEDED(rv) && pushAuth) {
01620               /* if the news host is set up to require volunteered (pushed) authentication,
01621     * do that before we do anything else
01622     */
01623     m_nextState = NNTP_BEGIN_AUTHORIZE;
01624   }
01625   else {
01626 #ifdef HAVE_NNTP_EXTENSIONS
01627     m_nextState = SEND_LIST_EXTENSIONS;
01628 #else
01629               m_nextState = SEND_FIRST_NNTP_COMMAND;
01630 #endif  /* HAVE_NNTP_EXTENSIONS */
01631   }
01632 
01633   return(0);
01634 }
01635 
01636 PRInt32 nsNNTPProtocol::SendListExtensions()
01637 {
01638        PRInt32 status = 0;
01639        nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01640        if (url)
01641               status = SendData(url, NNTP_CMD_LIST_EXTENSIONS);
01642 
01643        m_nextState = NNTP_RESPONSE;
01644        m_nextStateAfterResponse = SEND_LIST_EXTENSIONS_RESPONSE;
01645        ClearFlag(NNTP_PAUSE_FOR_READ);
01646        return status;
01647 }
01648 
01649 PRInt32 nsNNTPProtocol::SendListExtensionsResponse(nsIInputStream * inputStream, PRUint32 length)
01650 {
01651        PRUint32 status = 0; 
01652 
01653        if (MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK)
01654        {
01655               PRBool pauseForMoreData = PR_FALSE;
01656               char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01657 
01658               if(pauseForMoreData)
01659               {
01660                      SetFlag(NNTP_PAUSE_FOR_READ);
01661                      return 0;
01662               }
01663               if (!line)
01664                      return status;  /* no line yet */
01665 
01666         if ('.' != line[0]) {
01667             m_nntpServer->AddExtension(line);
01668         }
01669               else
01670               {
01671                      /* tell libmsg that it's ok to ask this news host for extensions */          
01672                      m_nntpServer->SetSupportsExtensions(PR_TRUE);
01673                      /* all extensions received */
01674                      m_nextState = SEND_LIST_SEARCHES;
01675                      ClearFlag(NNTP_PAUSE_FOR_READ);
01676               }
01677        }
01678        else
01679        {
01680               /* LIST EXTENSIONS not recognized 
01681                * tell libmsg not to ask for any more extensions and move on to
01682                * the real NNTP command we were trying to do. */
01683                
01684                m_nntpServer->SetSupportsExtensions(PR_FALSE);
01685                m_nextState = SEND_FIRST_NNTP_COMMAND;
01686        }
01687 
01688        return status;
01689 }
01690 
01691 PRInt32 nsNNTPProtocol::SendListSearches()
01692 {  
01693     nsresult rv;
01694     PRBool searchable=PR_FALSE;
01695        PRInt32 status = 0;
01696     
01697     rv = m_nntpServer->QueryExtension("SEARCH",&searchable);
01698     if (NS_SUCCEEDED(rv) && searchable)
01699        {
01700               nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
01701               if (mailnewsurl)
01702                      status = SendData(mailnewsurl, NNTP_CMD_LIST_SEARCHES);
01703 
01704               m_nextState = NNTP_RESPONSE;
01705               m_nextStateAfterResponse = SEND_LIST_SEARCHES_RESPONSE;
01706               SetFlag(NNTP_PAUSE_FOR_READ);
01707        }
01708        else
01709        {
01710               /* since SEARCH isn't supported, move on to GET */
01711               m_nextState = NNTP_GET_PROPERTIES;
01712               ClearFlag(NNTP_PAUSE_FOR_READ);
01713        }
01714 
01715        return status;
01716 }
01717 
01718 PRInt32 nsNNTPProtocol::SendListSearchesResponse(nsIInputStream * inputStream, PRUint32 length)
01719 {
01720        PRUint32 status = 0;
01721 
01722        NS_PRECONDITION(inputStream, "invalid input stream");
01723        
01724        PRBool pauseForMoreData = PR_FALSE;
01725        char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01726 
01727        NNTP_LOG_READ(line);
01728 
01729        if(pauseForMoreData)
01730        {
01731               SetFlag(NNTP_PAUSE_FOR_READ);
01732               return 0;
01733        }
01734        if (!line)
01735               return status;  /* no line yet */
01736 
01737        if ('.' != line[0])
01738        {
01739         nsCAutoString charset;
01740         nsAutoString lineUtf16;
01741         if (NS_FAILED(m_nntpServer->GetCharset(charset)) ||
01742             NS_FAILED(nsMsgI18NConvertToUnicode(charset.get(),
01743                                                 nsDependentCString(line),
01744                                                 lineUtf16, PR_TRUE)))
01745             CopyUTF8toUTF16(nsDependentCString(line), lineUtf16); 
01746 
01747               m_nntpServer->AddSearchableGroup(lineUtf16);
01748        }
01749        else
01750        {
01751               /* all searchable groups received */
01752               /* LIST SRCHFIELDS is legal if the server supports the SEARCH extension, which */
01753               /* we already know it does */
01754               m_nextState = NNTP_LIST_SEARCH_HEADERS;
01755               ClearFlag(NNTP_PAUSE_FOR_READ); 
01756        }
01757 
01758        PR_FREEIF(line);
01759        return status;
01760 }
01761 
01762 PRInt32 nsNNTPProtocol::SendListSearchHeaders()
01763 {
01764        PRInt32 status = 0;
01765        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
01766        if (mailnewsurl)
01767               status = SendData(mailnewsurl, NNTP_CMD_LIST_SEARCH_FIELDS);
01768 
01769        m_nextState = NNTP_RESPONSE;
01770        m_nextStateAfterResponse = NNTP_LIST_SEARCH_HEADERS_RESPONSE;
01771        SetFlag(NNTP_PAUSE_FOR_READ);
01772 
01773        return status;
01774 }
01775 
01776 PRInt32 nsNNTPProtocol::SendListSearchHeadersResponse(nsIInputStream * inputStream, PRUint32 length)
01777 {
01778        PRUint32 status = 0; 
01779 
01780        PRBool pauseForMoreData = PR_FALSE;
01781        char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01782 
01783        if(pauseForMoreData)
01784        {
01785               SetFlag(NNTP_PAUSE_FOR_READ);
01786               return 0;
01787        }
01788        if (!line)
01789               return status;  /* no line yet */
01790 
01791        if ('.' != line[0])
01792         m_nntpServer->AddSearchableHeader(line);
01793        else
01794        {
01795               m_nextState = NNTP_GET_PROPERTIES;
01796               ClearFlag(NNTP_PAUSE_FOR_READ);
01797        }
01798 
01799        PR_FREEIF(line);
01800        return status;
01801 }
01802 
01803 PRInt32 nsNNTPProtocol::GetProperties()
01804 {
01805     nsresult rv;
01806     PRBool setget=PR_FALSE;
01807        PRInt32 status = 0; 
01808     
01809     rv = m_nntpServer->QueryExtension("SETGET",&setget);
01810     if (NS_SUCCEEDED(rv) && setget)
01811        {
01812               nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
01813               if (mailnewsurl)
01814                      status = SendData(mailnewsurl, NNTP_CMD_GET_PROPERTIES);
01815               m_nextState = NNTP_RESPONSE;
01816               m_nextStateAfterResponse = NNTP_GET_PROPERTIES_RESPONSE;
01817               SetFlag(NNTP_PAUSE_FOR_READ);
01818        }
01819        else
01820        {
01821               /* since GET isn't supported, move on LIST SUBSCRIPTIONS */
01822               m_nextState = SEND_LIST_SUBSCRIPTIONS;
01823               ClearFlag(NNTP_PAUSE_FOR_READ);
01824        }
01825        return status;
01826 }
01827 
01828 PRInt32 nsNNTPProtocol::GetPropertiesResponse(nsIInputStream * inputStream, PRUint32 length)
01829 {
01830        PRUint32 status = 0;
01831 
01832        PRBool pauseForMoreData = PR_FALSE;
01833        char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01834 
01835        if(pauseForMoreData)
01836        {
01837               SetFlag(NNTP_PAUSE_FOR_READ);
01838               return 0;
01839        }
01840        if (!line)
01841               return status;  /* no line yet */
01842 
01843        if ('.' != line[0])
01844        {
01845               char *propertyName = nsCRT::strdup(line);
01846               if (propertyName)
01847               {
01848                      char *space = PL_strchr(propertyName, ' ');
01849                      if (space)
01850                      {
01851                             char *propertyValue = space + 1;
01852                             *space = '\0';
01853                 m_nntpServer->AddPropertyForGet(propertyName, propertyValue);
01854                      }
01855                      PR_Free(propertyName);
01856               }
01857        }
01858        else
01859        {
01860               /* all GET properties received, move on to LIST SUBSCRIPTIONS */
01861               m_nextState = SEND_LIST_SUBSCRIPTIONS;
01862               ClearFlag(NNTP_PAUSE_FOR_READ);
01863        }
01864 
01865        PR_FREEIF(line);
01866        return status;
01867 }
01868 
01869 PRInt32 nsNNTPProtocol::SendListSubscriptions()
01870 {
01871    PRInt32 status = 0; 
01872 #if 0
01873     nsresult rv;
01874     PRBool searchable=PR_FALSE;
01875     rv = m_nntpServer->QueryExtension("LISTSUBSCR",&listsubscr);
01876     if (NS_SUCCEEDED(rv) && listsubscr)
01877 #else
01878        if (0)
01879 #endif
01880        {
01881               nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
01882               if (mailnewsurl)
01883                      status = SendData(mailnewsurl, NNTP_CMD_LIST_SUBSCRIPTIONS);
01884               m_nextState = NNTP_RESPONSE;
01885               m_nextStateAfterResponse = SEND_LIST_SUBSCRIPTIONS_RESPONSE;
01886               SetFlag(NNTP_PAUSE_FOR_READ);
01887        }
01888        else
01889        {
01890               /* since LIST SUBSCRIPTIONS isn't supported, move on to real work */
01891               m_nextState = SEND_FIRST_NNTP_COMMAND;
01892               ClearFlag(NNTP_PAUSE_FOR_READ);
01893        }
01894 
01895        return status;
01896 }
01897 
01898 PRInt32 nsNNTPProtocol::SendListSubscriptionsResponse(nsIInputStream * inputStream, PRUint32 length)
01899 {
01900        PRUint32 status = 0;
01901 
01902        PRBool pauseForMoreData = PR_FALSE;
01903        char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
01904 
01905        if(pauseForMoreData)
01906        {
01907               SetFlag(NNTP_PAUSE_FOR_READ);
01908               return 0;
01909        }
01910        if (!line)
01911               return status;  /* no line yet */
01912 
01913        if ('.' != line[0])
01914        {
01915         NS_ASSERTION(0,"fix me");
01916 #if 0
01917               char *url = PR_smprintf ("%s//%s/%s", NEWS_SCHEME, m_hostName, line);
01918               if (url)
01919                      MSG_AddSubscribedNewsgroup (cd->pane, url);
01920 #endif
01921        }
01922        else
01923        {
01924               /* all default subscriptions received */
01925               m_nextState = SEND_FIRST_NNTP_COMMAND;
01926               ClearFlag(NNTP_PAUSE_FOR_READ);
01927        }
01928 
01929        PR_FREEIF(line);
01930        return status;
01931 }
01932 
01933 /* figure out what the first command is and send it
01934  *
01935  * returns the status from the NETWrite */
01936 
01937 PRInt32 nsNNTPProtocol::SendFirstNNTPCommand(nsIURI * url)
01938 {
01939     char *command=0;
01940     PRInt32 status = 0;
01941 
01942     if (m_typeWanted == ARTICLE_WANTED) {
01943         if (m_key != nsMsgKey_None) {
01944             nsresult rv;
01945             nsXPIDLCString newsgroupName;
01946             if (m_newsFolder) {
01947                 rv = m_newsFolder->GetRawName(newsgroupName);
01948                 NS_ENSURE_SUCCESS(rv,rv);
01949             }
01950 
01951             PR_LOG(NNTP,PR_LOG_ALWAYS,
01952                    ("(%p) current group = %s, desired group = %s", this,
01953                    m_currentGroup.get(), newsgroupName.get()));
01954             // if the current group is the desired group, we can just issue the ARTICLE command
01955             // if not, we have to do a GROUP first
01956             if (!PL_strcmp(m_currentGroup.get(), newsgroupName.get()))
01957                 m_nextState = NNTP_SEND_ARTICLE_NUMBER;
01958             else
01959                 m_nextState = NNTP_SEND_GROUP_FOR_ARTICLE;
01960 
01961             ClearFlag(NNTP_PAUSE_FOR_READ);
01962             return 0;
01963         }
01964     }
01965 
01966     if(m_typeWanted == NEWS_POST)
01967     {  /* posting to the news group */
01968         NS_MsgSACopy(&command, "POST");
01969     }
01970     else if(m_typeWanted == READ_NEWS_RC)
01971     {
01972               /* extract post method from the url when we have it... */
01973 #ifdef HAVE_NEWS_URL
01974               if(ce->URL_s->method == URL_POST_METHOD ||
01975                                                         PL_strchr(ce->URL_s->address, '?'))
01976               m_nextState = NEWS_NEWS_RC_POST;
01977               else
01978 #endif
01979               m_nextState = NEWS_DISPLAY_NEWS_RC;
01980               return(0);
01981     } 
01982        else if(m_typeWanted == NEW_GROUPS)
01983        {
01984         PRUint32 last_update;
01985         nsresult rv;
01986               
01987               if (!m_nntpServer) {
01988                      NNTP_LOG_NOTE("m_nntpServer is null, panic!");
01989                      return -1;
01990               }
01991         rv = m_nntpServer->GetLastUpdatedTime(&last_update);
01992               char small_buf[64];
01993         PRExplodedTime  expandedTime;
01994 
01995               if(!last_update)
01996               {
01997                      AlertError(MK_NNTP_NEWSGROUP_SCAN_ERROR, nsnull);
01998                      m_nextState = NEWS_ERROR;
01999                      return(MK_INTERRUPTED);
02000               }
02001        
02002               /* subtract some hours just to be sure */
02003               last_update -= NEWGROUPS_TIME_OFFSET;
02004        
02005         {
02006            PRInt64  secToUSec, timeInSec, timeInUSec;
02007            LL_I2L(timeInSec, last_update);
02008            LL_I2L(secToUSec, PR_USEC_PER_SEC);
02009            LL_MUL(timeInUSec, timeInSec, secToUSec);
02010            PR_ExplodeTime(timeInUSec, PR_LocalTimeParameters, &expandedTime);
02011         }
02012               PR_FormatTimeUSEnglish(small_buf, sizeof(small_buf), 
02013                                "NEWGROUPS %y%m%d %H%M%S", &expandedTime);
02014               
02015         NS_MsgSACopy(&command, small_buf);
02016 
02017        }
02018     else if(m_typeWanted == LIST_WANTED)
02019     {
02020            nsresult rv;
02021 
02022               ClearFlag(NNTP_USE_FANCY_NEWSGROUP);
02023         PRUint32 last_update;
02024        
02025         NS_ASSERTION(m_nntpServer, "no m_nntpServer");
02026               if (!m_nntpServer) {
02027           NNTP_LOG_NOTE("m_nntpServer is null, panic!");
02028           return -1;
02029               }
02030 
02031               rv = m_nntpServer->GetLastUpdatedTime(&last_update); 
02032         if (NS_SUCCEEDED(rv) && last_update!=0)
02033               {
02034                      m_nextState = DISPLAY_NEWSGROUPS;
02035               return(0);
02036            }
02037               else
02038               {
02039                      PRBool xactive=PR_FALSE;
02040                      rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
02041                      if (NS_SUCCEEDED(rv) && xactive)
02042                      {
02043                             NS_MsgSACopy(&command, "LIST XACTIVE");
02044                             SetFlag(NNTP_USE_FANCY_NEWSGROUP);
02045                      }
02046                      else
02047                      {
02048                             NS_MsgSACopy(&command, "LIST");
02049                      }
02050               }
02051        }
02052        else if(m_typeWanted == GROUP_WANTED) 
02053     {
02054         nsresult rv = NS_ERROR_NULL_POINTER;
02055         
02056         NS_ASSERTION(m_newsFolder, "m_newsFolder is null, panic!");
02057         if (!m_newsFolder) return -1;
02058 
02059         nsXPIDLCString group_name;
02060         rv = m_newsFolder->GetRawName(group_name);
02061         NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get newsgroup name");
02062         if (NS_FAILED(rv)) return -1;
02063               
02064         m_firstArticle = 0;
02065         m_lastArticle = 0;
02066         
02067         NS_MsgSACopy(&command, "GROUP ");
02068         NS_MsgSACat(&command, (const char *)group_name);
02069       }
02070        else if (m_typeWanted == SEARCH_WANTED)
02071        {
02072               nsresult rv;
02073               PRBool searchable=PR_FALSE;
02074               if (!m_nntpServer) {
02075                      NNTP_LOG_NOTE("m_nntpServer is null, panic!");
02076                      return -1;
02077               }
02078               rv = m_nntpServer->QueryExtension("SEARCH", &searchable);
02079               if (NS_SUCCEEDED(rv) && searchable)
02080               {
02081                      /* use the SEARCH extension */
02082                      char *slash = PL_strchr (m_commandSpecificData, '/');
02083                      if (slash)
02084                      {
02085                             char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
02086                             if (allocatedCommand)
02087                             {
02088                                    NS_MsgSACopy (&command, allocatedCommand);
02089                                    PR_Free(allocatedCommand);
02090                             }
02091                      }
02092                      m_nextState = NNTP_RESPONSE;
02093                      m_nextStateAfterResponse = NNTP_SEARCH_RESPONSE;
02094               }
02095               else
02096               {
02097             PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) doing GROUP for XPAT", this));
02098             nsXPIDLCString group_name;
02099             
02100             /* for XPAT, we have to GROUP into the group before searching */
02101             if (!m_newsFolder) {
02102                 NNTP_LOG_NOTE("m_newsFolder is null, panic!");
02103                 return -1;
02104             }
02105             rv = m_newsFolder->GetRawName(group_name);
02106             if (NS_FAILED(rv)) return -1;
02107 
02108             NS_MsgSACopy(&command, "GROUP ");
02109             NS_MsgSACat (&command, group_name);
02110 
02111             // force a GROUP next time
02112             m_currentGroup.Truncate();
02113             m_nextState = NNTP_RESPONSE;
02114             m_nextStateAfterResponse = NNTP_XPAT_SEND;
02115               }
02116        }
02117        else if (m_typeWanted == PRETTY_NAMES_WANTED)
02118        {
02119               nsresult rv;
02120               PRBool listpretty=PR_FALSE;
02121               rv = m_nntpServer->QueryExtension("LISTPRETTY",&listpretty);
02122               if (NS_SUCCEEDED(rv) && listpretty)
02123               {
02124                      m_nextState = NNTP_LIST_PRETTY_NAMES;
02125                      return 0;
02126               }
02127               else
02128               {
02129                      NS_ASSERTION(PR_FALSE, "unexpected");
02130                      m_nextState = NNTP_ERROR;
02131               }
02132        }
02133        else if (m_typeWanted == PROFILE_WANTED)
02134        {
02135               char *slash = PL_strchr (m_commandSpecificData, '/');
02136               if (slash)
02137               {
02138                      char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
02139                      if (allocatedCommand)
02140                      {
02141                             NS_MsgSACopy(&command, allocatedCommand);
02142                             PR_Free(allocatedCommand);
02143                      }
02144               }
02145               m_nextState = NNTP_RESPONSE;
02146                      m_nextStateAfterResponse = NNTP_PROFILE_DELETE_RESPONSE;
02147        }
02148        else if (m_typeWanted == IDS_WANTED)
02149        {
02150               m_nextState = NNTP_LIST_GROUP;
02151               return 0;
02152        }
02153     else  /* article or cancel */
02154        {
02155         NS_ASSERTION(m_path, "no m_path, see bugs #57659 and #72317");
02156         if (!m_path) return -1;
02157 
02158               if (m_typeWanted == CANCEL_WANTED) {
02159                      NS_MsgSACopy(&command, "HEAD ");
02160         }
02161               else {
02162             NS_ASSERTION(m_typeWanted == ARTICLE_WANTED, "not cancel, and not article");
02163                      NS_MsgSACopy(&command, "ARTICLE ");
02164         }
02165 
02166               if (*m_path != '<')
02167                      NS_MsgSACat(&command,"<");
02168 
02169               NS_MsgSACat(&command, m_path);
02170 
02171               if (PL_strchr(command+8, '>')==0) 
02172                      NS_MsgSACat(&command,">");
02173        }
02174 
02175     NS_MsgSACat(&command, CRLF);
02176        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02177        if (mailnewsurl)
02178               status = SendData(mailnewsurl, command);
02179     PR_Free(command);
02180 
02181        m_nextState = NNTP_RESPONSE;
02182        if (m_typeWanted != SEARCH_WANTED && m_typeWanted != PROFILE_WANTED)
02183               m_nextStateAfterResponse = SEND_FIRST_NNTP_COMMAND_RESPONSE;
02184        SetFlag(NNTP_PAUSE_FOR_READ);
02185     return(status);
02186 } /* sent first command */
02187 
02188 
02189 /* interprets the server response from the first command sent
02190  *
02191  * returns negative if the server responds unexpectedly 
02192  */
02193 
02194 PRInt32 nsNNTPProtocol::SendFirstNNTPCommandResponse()
02195 {
02196   PRInt32 status = 0;
02197   PRInt32 major_opcode = MK_NNTP_RESPONSE_TYPE(m_responseCode);
02198   
02199   if((major_opcode == MK_NNTP_RESPONSE_TYPE_CONT &&
02200     m_typeWanted == NEWS_POST)
02201     || (major_opcode == MK_NNTP_RESPONSE_TYPE_OK &&
02202     m_typeWanted != NEWS_POST) )
02203   {
02204     
02205     m_nextState = SETUP_NEWS_STREAM;
02206     SetFlag(NNTP_SOME_PROTOCOL_SUCCEEDED);
02207     return(0);  /* good */
02208   }
02209   else
02210   {
02211     nsresult rv = NS_OK;
02212     
02213     nsXPIDLString group_name;
02214     NS_ASSERTION(m_newsFolder, "no newsFolder");
02215     if (m_newsFolder) {
02216       rv = m_newsFolder->GetUnicodeName(group_name);
02217     }
02218     
02219     if (m_responseCode == MK_NNTP_RESPONSE_GROUP_NO_GROUP &&
02220       m_typeWanted == GROUP_WANTED) {
02221       PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) group (%s) not found, so unset"
02222                                  " m_currentGroup", this,
02223                                  NS_ConvertUTF16toUTF8(group_name).get()));
02224       m_currentGroup.Truncate();
02225       
02226       m_nntpServer->GroupNotFound(m_msgWindow, group_name, PR_TRUE /* opening */);
02227     }
02228     
02229     /* if the server returned a 400 error then it is an expected
02230     * error.  the NEWS_ERROR state will not sever the connection
02231     */
02232     if(major_opcode == MK_NNTP_RESPONSE_TYPE_CANNOT)
02233       m_nextState = NEWS_ERROR;
02234     else
02235       m_nextState = NNTP_ERROR;
02236     // if we have no channel listener, then we're likely downloading
02237     // the message for offline use (or at least not displaying it)
02238     PRBool savingArticleOffline = (m_channelListener == nsnull);
02239     
02240     if (m_runningURL)
02241       FinishMemCacheEntry(PR_FALSE);  // cleanup mem cache entry
02242     
02243     if (NS_SUCCEEDED(rv) && group_name && !savingArticleOffline) {
02244       nsXPIDLString titleStr;
02245       rv = GetNewsStringByName("htmlNewsErrorTitle", getter_Copies(titleStr));
02246       NS_ENSURE_SUCCESS(rv,rv);
02247       
02248       nsXPIDLString newsErrorStr;
02249       rv = GetNewsStringByName("htmlNewsError", getter_Copies(newsErrorStr));
02250       NS_ENSURE_SUCCESS(rv,rv);
02251       nsAutoString errorHtml;
02252       errorHtml.Append(newsErrorStr);
02253       
02254       errorHtml.Append(NS_LITERAL_STRING("<b>").get());
02255       errorHtml.AppendWithConversion(m_responseText);
02256       errorHtml.Append(NS_LITERAL_STRING("</b><p>").get());
02257       
02258       rv = GetNewsStringByName("articleExpired", getter_Copies(newsErrorStr));
02259       NS_ENSURE_SUCCESS(rv,rv);
02260       errorHtml.Append(newsErrorStr);
02261       
02262       char outputBuffer[OUTPUT_BUFFER_SIZE];
02263       
02264       if ((m_key != nsMsgKey_None) && m_newsFolder) {
02265         nsXPIDLCString messageID;
02266         rv = m_newsFolder->GetMessageIdForKey(m_key, getter_Copies(messageID));
02267         if (NS_SUCCEEDED(rv)) {
02268           PR_snprintf(outputBuffer,OUTPUT_BUFFER_SIZE,"<P>&lt;%.512s&gt; (%lu)", (const char *)messageID, m_key);
02269           errorHtml.AppendWithConversion(outputBuffer);
02270         }
02271       }
02272       
02273       if (m_newsFolder) {
02274         nsCOMPtr <nsIMsgFolder> folder = do_QueryInterface(m_newsFolder, &rv);
02275         if (NS_SUCCEEDED(rv) && folder) {
02276           nsXPIDLCString folderURI;
02277           rv = folder->GetURI(getter_Copies(folderURI));
02278           if (NS_SUCCEEDED(rv)) {
02279             PR_snprintf(outputBuffer,OUTPUT_BUFFER_SIZE,"<P> <A HREF=\"%s?list-ids\">", (const char *)folderURI);
02280           }
02281         }
02282       }
02283       
02284       errorHtml.AppendWithConversion(outputBuffer);
02285       
02286       rv = GetNewsStringByName("removeExpiredArtLinkText", getter_Copies(newsErrorStr));
02287       NS_ENSURE_SUCCESS(rv,rv);
02288       errorHtml.Append(newsErrorStr);
02289       errorHtml.Append(NS_LITERAL_STRING("</A> </P>").get());
02290       
02291       if (!m_msgWindow) {
02292         nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02293         if (mailnewsurl) {
02294           rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
02295           NS_ENSURE_SUCCESS(rv,rv);
02296         }
02297       }
02298       if (!m_msgWindow) return NS_ERROR_FAILURE;
02299       
02300       // note, this will cause us to close the connection.
02301       // this will call nsDocShell::LoadURI(), which will
02302       // call nsDocShell::Stop(STOP_NETWORK), which will eventually
02303       // call nsNNTPProtocol::Cancel(), which will close the socket.
02304       // we need to fix this, since the connection is still valid.
02305       rv = m_msgWindow->DisplayHTMLInMessagePane((const PRUnichar *)titleStr, errorHtml.get(), PR_TRUE);
02306       NS_ENSURE_SUCCESS(rv,rv);
02307     }
02308     // let's take the opportunity of removing the hdr from the db so we don't try to download
02309     // it again.
02310     else if (savingArticleOffline)
02311     {
02312       if ((m_key != nsMsgKey_None) && (m_newsFolder)) {
02313          rv = m_newsFolder->RemoveMessage(m_key);
02314       }
02315     }
02316 
02317     return MK_NNTP_SERVER_ERROR;
02318   }
02319   
02320   /* start the graph progress indicator
02321   */
02322   NNTP_LOG_NOTE("start the graph progress indicator");
02323   SetFlag(NNTP_DESTROY_PROGRESS_GRAPH);
02324   return(status);
02325 }
02326 
02327 PRInt32 nsNNTPProtocol::SendGroupForArticle()
02328 {
02329   nsresult rv;
02330   PRInt32 status = 0; 
02331 
02332   nsXPIDLCString groupname;
02333   rv = m_newsFolder->GetRawName(groupname);
02334   NS_ASSERTION(NS_SUCCEEDED(rv) && groupname.get() && groupname.get()[0], "no group name");
02335 
02336   char outputBuffer[OUTPUT_BUFFER_SIZE];
02337   
02338   PR_snprintf(outputBuffer, 
02339                      OUTPUT_BUFFER_SIZE, 
02340                      "GROUP %.512s" CRLF, 
02341                      (const char *)groupname);
02342 
02343   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02344   if (mailnewsurl)
02345        status = SendData(mailnewsurl, outputBuffer);
02346 
02347   m_nextState = NNTP_RESPONSE;
02348   m_nextStateAfterResponse = NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE;
02349   SetFlag(NNTP_PAUSE_FOR_READ);
02350 
02351   return(status);
02352 }
02353 
02354 nsresult 
02355 nsNNTPProtocol::SetCurrentGroup()
02356 {
02357   nsresult rv;
02358   nsXPIDLCString groupname;
02359   NS_ASSERTION(m_newsFolder, "no news folder");
02360   if (!m_newsFolder) {
02361     m_currentGroup.Truncate();
02362     return NS_ERROR_UNEXPECTED;
02363   }
02364 
02365   rv = m_newsFolder->GetRawName(groupname);
02366   NS_ASSERTION(NS_SUCCEEDED(rv) && groupname.get()[0], "no group name");
02367   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) SetCurrentGroup to %s",this,(const char *)groupname));
02368   m_currentGroup = groupname;
02369   return NS_OK;
02370 }
02371 
02372 PRInt32 nsNNTPProtocol::SendGroupForArticleResponse()
02373 {
02374   /* ignore the response code and continue
02375    */
02376   m_nextState = NNTP_SEND_ARTICLE_NUMBER;
02377 
02378   SetCurrentGroup();
02379 
02380   return(0);
02381 }
02382 
02383 
02384 PRInt32 nsNNTPProtocol::SendArticleNumber()
02385 {
02386        char outputBuffer[OUTPUT_BUFFER_SIZE];
02387        PRInt32 status = 0; 
02388        PR_snprintf(outputBuffer, OUTPUT_BUFFER_SIZE, "ARTICLE %lu" CRLF, m_key);
02389 
02390        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02391        if (mailnewsurl)
02392               status = SendData(mailnewsurl, outputBuffer);
02393 
02394     m_nextState = NNTP_RESPONSE;
02395     m_nextStateAfterResponse = SEND_FIRST_NNTP_COMMAND_RESPONSE;
02396     SetFlag(NNTP_PAUSE_FOR_READ);
02397 
02398     return(status);
02399 }
02400 
02401 PRInt32 nsNNTPProtocol::BeginArticle()
02402 {
02403   if (m_typeWanted != ARTICLE_WANTED &&
02404          m_typeWanted != CANCEL_WANTED)
02405        return 0;
02406 
02407   /*  Set up the HTML stream
02408    */ 
02409 
02410 #ifdef NO_ARTICLE_CACHEING
02411   ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
02412 #endif
02413 
02414   // if we have a channel listener,
02415   // create a pipe to pump the message into...the output will go to whoever
02416   // is consuming the message display
02417   //
02418   // the pipe must have an unlimited length since we are going to be filling
02419   // it on the main thread while reading it from the main thread.  iow, the
02420   // write must not block!! (see bug 190988)
02421   //
02422   if (m_channelListener) {
02423       nsresult rv;
02424       rv = NS_NewPipe(getter_AddRefs(mDisplayInputStream),
02425                       getter_AddRefs(mDisplayOutputStream),
02426                       4096, PRUint32(-1));
02427       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create pipe");
02428       // TODO: return on failure?
02429   }
02430 
02431   m_nextState = NNTP_READ_ARTICLE;
02432 
02433   return 0;
02434 }
02435 
02436 PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 length)
02437 {
02438   PRUint32 line_length = 0;
02439   
02440   PRBool pauseForMoreData = PR_FALSE;
02441   if (m_channelListener)
02442   {
02443     nsresult rv = NS_OK;
02444     char *line = m_lineStreamBuffer->ReadNextLine(inputStream, line_length, pauseForMoreData, &rv, PR_TRUE);
02445     if (pauseForMoreData)
02446     {
02447       PRUint32 inlength = 0;
02448       mDisplayInputStream->Available(&inlength);
02449       if (inlength > 0) // broadcast our batched up ODA changes
02450         m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
02451       SetFlag(NNTP_PAUSE_FOR_READ);
02452       PR_Free(line);
02453       return line_length;
02454     }
02455 
02456     if (m_newsFolder)
02457       m_newsFolder->NotifyDownloadedLine(line, m_key);
02458 
02459     // line only contains a single dot -> message end
02460     if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
02461     {
02462       m_nextState = NEWS_DONE;
02463       
02464       ClearFlag(NNTP_PAUSE_FOR_READ);
02465       
02466       PRUint32 inlength = 0;
02467       mDisplayInputStream->Available(&inlength);
02468       if (inlength > 0) // broadcast our batched up ODA changes
02469         m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
02470       PR_Free(line);
02471       return line_length;
02472     }
02473     else // we aren't finished with the message yet
02474     {
02475       PRUint32 count = 0;
02476 
02477       // skip over the quoted '.'
02478       if (line_length > 1 && line[0] == '.' && line[1] == '.')
02479         mDisplayOutputStream->Write(line+1, line_length-1, &count);
02480       else 
02481         mDisplayOutputStream->Write(line, line_length, &count);
02482     }
02483 
02484     PR_Free(line);
02485   }
02486 
02487   return 0;   
02488 }
02489 
02490 PRInt32 nsNNTPProtocol::ReadArticle(nsIInputStream * inputStream, PRUint32 length)
02491 {
02492   PRUint32 status = 0;
02493   char *outputBuffer;
02494   
02495   PRBool pauseForMoreData = PR_FALSE;
02496   
02497   // if we have a channel listener, spool directly to it....
02498   // otherwise we must be doing something like save to disk or cancel
02499   // in which case we are doing the work.
02500   if (m_channelListener)
02501     return DisplayArticle(inputStream, length);
02502   
02503   
02504   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData, nsnull, PR_TRUE);
02505   if (m_newsFolder && line)
02506     m_newsFolder->NotifyDownloadedLine(line, m_key);
02507   
02508   if(pauseForMoreData)
02509   {
02510     SetFlag(NNTP_PAUSE_FOR_READ);
02511     return 0;
02512   }
02513   if(status > 1)
02514   {           
02515 #ifdef UNREADY_CODE
02516     ce->bytes_received += status;
02517     FE_GraphProgress(ce->window_id, ce->URL_s,
02518       ce->bytes_received, status,
02519       ce->URL_s->content_length);
02520 #else
02521     mBytesReceived += status;
02522     mBytesReceivedSinceLastStatusUpdate += status;
02523 #endif
02524   }
02525   
02526   if(!line)
02527     return(status);  /* no line yet or error */
02528   
02529   nsCOMPtr<nsISupports> ctxt = do_QueryInterface(m_runningURL);
02530   
02531   if (m_typeWanted == CANCEL_WANTED && m_responseCode != MK_NNTP_RESPONSE_ARTICLE_HEAD)
02532   {
02533     /* HEAD command failed. */
02534     PR_FREEIF(line);
02535     return MK_NNTP_CANCEL_ERROR;
02536   }
02537   
02538   if (line[0] == '.' && line[MSG_LINEBREAK_LEN + 1] == 0)
02539   {
02540     if (m_typeWanted == CANCEL_WANTED)
02541       m_nextState = NEWS_START_CANCEL;
02542     else
02543       m_nextState = NEWS_DONE;
02544     
02545     ClearFlag(NNTP_PAUSE_FOR_READ);
02546   }
02547   else
02548   {
02549     if (line[0] == '.')
02550       outputBuffer = line + 1;
02551     else
02552       outputBuffer = line;
02553     
02554       /* Don't send content-type to mime parser if we're doing a cancel
02555       because it confuses mime parser into not parsing.
02556                 */
02557     if (m_typeWanted != CANCEL_WANTED || nsCRT::strncmp(outputBuffer, "Content-Type:", 13))
02558     {
02559       // if we are attempting to cancel, we want to snarf the headers and save the aside, which is what
02560       // ParseHeaderForCancel() does.
02561       if (m_typeWanted == CANCEL_WANTED) {
02562         ParseHeaderForCancel(outputBuffer);
02563       }
02564       
02565     }
02566   }
02567   
02568   PR_Free(line);
02569   
02570   return 0;
02571 }
02572 
02573 void nsNNTPProtocol::ParseHeaderForCancel(char *buf)
02574 {
02575     nsCAutoString header(buf);
02576     PRInt32 colon = header.FindChar(':');
02577     if (!colon)
02578               return;
02579 
02580     nsCAutoString value;
02581     header.Right(value, header.Length() - colon -1);
02582     value.StripWhitespace();
02583     
02584     switch (header.First()) {
02585     case 'F': case 'f':
02586         if (header.Find("From",PR_TRUE) == 0) {
02587             PR_FREEIF(m_cancelFromHdr);
02588                      m_cancelFromHdr = ToNewCString(value);
02589         }
02590         break;
02591     case 'M': case 'm':
02592         if (header.Find("Message-ID",PR_TRUE) == 0) {
02593             PR_FREEIF(m_cancelID);
02594                      m_cancelID = ToNewCString(value);
02595         }
02596         break;
02597     case 'N': case 'n':
02598         if (header.Find("Newsgroups",PR_TRUE) == 0) {
02599             PR_FREEIF(m_cancelNewsgroups);
02600                      m_cancelNewsgroups = ToNewCString(value);
02601         }
02602         break;
02603      case 'D': case 'd':
02604         if (header.Find("Distributions",PR_TRUE) == 0) {
02605             PR_FREEIF(m_cancelDistribution);
02606                      m_cancelDistribution = ToNewCString(value);
02607         }       
02608         break;
02609     }
02610 
02611   return;
02612 }
02613 
02614 PRInt32 nsNNTPProtocol::BeginAuthorization()
02615 {
02616   char * command = 0;
02617   nsXPIDLCString username;
02618   nsresult rv = NS_OK;
02619   PRInt32 status = 0;
02620   nsXPIDLCString cachedUsername;
02621   
02622   if (!m_newsFolder && m_nntpServer) {
02623     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer);
02624     if (m_nntpServer) {
02625       nsCOMPtr<nsIMsgFolder> rootFolder;
02626       rv = server->GetRootFolder(getter_AddRefs(rootFolder));
02627       if (NS_SUCCEEDED(rv) && rootFolder) {
02628         m_newsFolder = do_QueryInterface(rootFolder);
02629       }
02630     }
02631   }
02632   
02633   NS_ASSERTION(m_newsFolder, "no m_newsFolder");
02634   if (m_newsFolder) 
02635     rv = m_newsFolder->GetGroupUsername(getter_Copies(cachedUsername));
02636   
02637   if (NS_FAILED(rv) || !cachedUsername) {
02638     rv = NS_OK;
02639     NNTP_LOG_NOTE("ask for the news username");
02640     
02641     nsXPIDLString usernamePromptText;
02642     GetNewsStringByName("enterUsername", getter_Copies(usernamePromptText));
02643     if (m_newsFolder) {
02644       if (!m_msgWindow) {
02645         nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02646         if (mailnewsurl) {
02647           rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
02648         }
02649       }
02650       
02651       rv = m_newsFolder->GetGroupUsernameWithUI(usernamePromptText, nsnull, m_msgWindow, getter_Copies(username));
02652     }
02653     else {
02654 #ifdef DEBUG_sspitzer
02655       printf("we don't know the folder\n");
02656       printf("this can happen if someone gives us just an article url\n");
02657 #endif
02658       return(MK_NNTP_AUTH_FAILED);
02659     }
02660     
02661     if (NS_FAILED(rv)) {
02662       AlertError(MK_NNTP_AUTH_FAILED, "Aborted by user");
02663       return(MK_NNTP_AUTH_FAILED);
02664     } 
02665   } // !username
02666   
02667   if (NS_FAILED(rv) || (!username && !cachedUsername)) {
02668                 return(MK_NNTP_AUTH_FAILED);
02669   }
02670   
02671   NS_MsgSACopy(&command, "AUTHINFO user ");
02672   if (cachedUsername) {
02673     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, (const char *)cachedUsername));
02674     NS_MsgSACat(&command, (const char *)cachedUsername);
02675   }
02676   else {
02677     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, (const char *)username));
02678     NS_MsgSACat(&command, (const char *)username);
02679   }
02680   NS_MsgSACat(&command, CRLF);
02681   
02682   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02683   if (mailnewsurl)
02684     status = SendData(mailnewsurl, command);
02685   
02686   PR_Free(command);
02687   
02688   m_nextState = NNTP_RESPONSE;
02689   m_nextStateAfterResponse = NNTP_AUTHORIZE_RESPONSE;
02690   
02691   SetFlag(NNTP_PAUSE_FOR_READ);
02692   
02693   return status;
02694 }
02695 
02696 PRInt32 nsNNTPProtocol::AuthorizationResponse()
02697 {
02698   nsresult rv = NS_OK;
02699   PRInt32 status = 0;
02700   
02701   
02702   if (MK_NNTP_RESPONSE_AUTHINFO_OK == m_responseCode ||
02703     MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == m_responseCode) 
02704   {
02705     /* successful login */
02706 #ifdef HAVE_NNTP_EXTENSIONS
02707     PRBool pushAuth;
02708     /* If we're here because the host demanded authentication before we
02709     * even sent a single command, then jump back to the beginning of everything
02710     */
02711     rv = m_nntpServer->GetPushAuth(&pushAuth);
02712     
02713     if (!TestFlag(NNTP_READER_PERFORMED))
02714       m_nextState = NNTP_SEND_MODE_READER;
02715       /* If we're here because the host needs pushed authentication, then we 
02716       * should jump back to SEND_LIST_EXTENSIONS
02717     */
02718     else if (NS_SUCCEEDED(rv) && pushAuth)
02719       m_nextState = SEND_LIST_EXTENSIONS;
02720     else
02721       /* Normal authentication */
02722       m_nextState = SEND_FIRST_NNTP_COMMAND;
02723 #else
02724     if (!TestFlag(NNTP_READER_PERFORMED))
02725       m_nextState = NNTP_SEND_MODE_READER;
02726     else
02727       m_nextState = SEND_FIRST_NNTP_COMMAND;
02728 #endif /* HAVE_NNTP_EXTENSIONS */
02729     
02730     return(0); 
02731   }
02732   else if (MK_NNTP_RESPONSE_AUTHINFO_CONT == m_responseCode)
02733   {
02734     /* password required */ 
02735     char * command = 0;
02736     nsXPIDLCString password;
02737     nsXPIDLCString cachedPassword;
02738     
02739     NS_ASSERTION(m_newsFolder, "no newsFolder");
02740     if (m_newsFolder) {
02741       rv = m_newsFolder->GetGroupPassword(getter_Copies(cachedPassword));
02742     }
02743     if (NS_FAILED(rv) || !cachedPassword) {
02744       rv = NS_OK;
02745       NNTP_LOG_NOTE("ask for the news password");
02746       
02747       nsXPIDLString passwordPromptText;
02748       GetNewsStringByName("enterPassword", getter_Copies(passwordPromptText));
02749       nsXPIDLString passwordPromptTitleText;
02750       GetNewsStringByName("enterPasswordTitle", getter_Copies(passwordPromptTitleText));
02751       
02752       NS_ASSERTION(m_newsFolder, "no newsFolder");
02753       if (m_newsFolder) {
02754         if (!m_msgWindow) {
02755           nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02756           if (mailnewsurl) {
02757             rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
02758           }
02759         }
02760         
02761         rv = m_newsFolder->GetGroupPasswordWithUI(passwordPromptText, passwordPromptTitleText, m_msgWindow, getter_Copies(password));
02762       }
02763       else {
02764         NNTP_LOG_NOTE("we don't know the folder");
02765         NNTP_LOG_NOTE("this can happen if someone gives us just an article url");
02766         return(MK_NNTP_AUTH_FAILED);
02767       }
02768       
02769       if (NS_FAILED(rv)) {
02770         AlertError(MK_NNTP_AUTH_FAILED,"Aborted by user");
02771         return(MK_NNTP_AUTH_FAILED);
02772       }
02773     }
02774                 
02775     if(NS_FAILED(rv) || (!password && !cachedPassword)) {
02776       return(MK_NNTP_AUTH_FAILED);
02777     }
02778     
02779     NS_MsgSACopy(&command, "AUTHINFO pass ");
02780     if (cachedPassword) {
02781       PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use cached password", this));
02782       NS_MsgSACat(&command, (const char *)cachedPassword);
02783     }
02784     else {
02785       // *don't log the password!* PR_LOG(NNTP,PR_LOG_ALWAYS,("use %s as the password",(const char *)password)); 
02786       NS_MsgSACat(&command, (const char *)password);
02787     }
02788     NS_MsgSACat(&command, CRLF);
02789     
02790     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
02791     if (mailnewsurl)
02792       status = SendData(mailnewsurl, command, PR_TRUE);
02793     
02794     PR_FREEIF(command);
02795     
02796     m_nextState = NNTP_RESPONSE;
02797     m_nextStateAfterResponse = NNTP_PASSWORD_RESPONSE;
02798     SetFlag(NNTP_PAUSE_FOR_READ);
02799     
02800     return status;
02801   }
02802   else
02803   {
02804     /* login failed */
02805     HandleAuthenticationFailure();
02806     return(MK_NNTP_AUTH_FAILED);
02807   }
02808               
02809   NS_ASSERTION(0,"should never get here");
02810   return(-1);
02811   
02812 }
02813 
02814 PRInt32 nsNNTPProtocol::PasswordResponse()
02815 {
02816   nsresult rv = NS_OK;
02817   
02818   if (MK_NNTP_RESPONSE_AUTHINFO_OK == m_responseCode ||
02819     MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == m_responseCode) 
02820   {
02821     /* successful login */
02822 #ifdef HAVE_NNTP_EXTENSIONS
02823     PRBool pushAuth;
02824     /* If we're here because the host demanded authentication before we
02825     * even sent a single command, then jump back to the beginning of everything
02826     */
02827     rv = m_nntpServer->GetPushAuth(&pushAuth);
02828     
02829     if (!TestFlag(NNTP_READER_PERFORMED))
02830       m_nextState = NNTP_SEND_MODE_READER;
02831       /* If we're here because the host needs pushed authentication, then we 
02832       * should jump back to SEND_LIST_EXTENSIONS
02833     */
02834     else if (NS_SUCCEEDED(rv) && pushAuth)
02835       m_nextState = SEND_LIST_EXTENSIONS;
02836     else
02837       /* Normal authentication */
02838       m_nextState = SEND_FIRST_NNTP_COMMAND;
02839 #else
02840     if (!TestFlag(NNTP_READER_PERFORMED))
02841       m_nextState = NNTP_SEND_MODE_READER;
02842     else
02843       m_nextState = SEND_FIRST_NNTP_COMMAND;
02844 #endif /* HAVE_NNTP_EXTENSIONS */
02845     m_nntpServer->SetUserAuthenticated(PR_TRUE);
02846     return(0);
02847   }
02848   else
02849   {
02850     HandleAuthenticationFailure();
02851     return(MK_NNTP_AUTH_FAILED);
02852   }
02853               
02854   NS_ASSERTION(0,"should never get here");
02855   return(-1);
02856 }
02857 
02858 PRInt32 nsNNTPProtocol::DisplayNewsgroups()
02859 {
02860   m_nextState = NEWS_DONE;
02861   ClearFlag(NNTP_PAUSE_FOR_READ);
02862   
02863   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) DisplayNewsgroups()",this));
02864   
02865   return(MK_DATA_LOADED);  /* all finished */
02866 }
02867 
02868 PRInt32 nsNNTPProtocol::BeginNewsgroups()
02869 {
02870        PRInt32 status = 0; 
02871        m_nextState = NNTP_NEWGROUPS;
02872        mBytesReceived = 0;
02873     mBytesReceivedSinceLastStatusUpdate = 0;
02874     m_startTime = PR_Now();
02875        return(status);
02876 }
02877 
02878 PRInt32 nsNNTPProtocol::ProcessNewsgroups(nsIInputStream * inputStream, PRUint32 length)
02879 {
02880   char *line, *lineToFree, *s, *s1=NULL, *s2=NULL, *flag=NULL;
02881   PRInt32 oldest, youngest;
02882   PRUint32 status = 0;
02883   nsresult rv = NS_OK;
02884   
02885   PRBool pauseForMoreData = PR_FALSE;
02886   line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
02887   
02888   if(pauseForMoreData)
02889   {
02890     SetFlag(NNTP_PAUSE_FOR_READ);
02891     return 0;
02892   }
02893   
02894   if(!line)
02895     return(status);  /* no line yet */
02896   
02897                      /* End of list? 
02898         */
02899   if (line[0]=='.' && line[1]=='\0')
02900   {
02901     ClearFlag(NNTP_PAUSE_FOR_READ);
02902     PRBool xactive=PR_FALSE;
02903     rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
02904     if (NS_SUCCEEDED(rv) && xactive)
02905     {
02906       nsCAutoString groupName;
02907       rv = m_nntpServer->GetFirstGroupNeedingExtraInfo(groupName);
02908       if (NS_SUCCEEDED(rv)) {
02909         rv = m_nntpServer->FindGroup(groupName, getter_AddRefs(m_newsFolder));
02910         NS_ASSERTION(NS_SUCCEEDED(rv), "FindGroup failed");
02911         m_nextState = NNTP_LIST_XACTIVE;
02912         PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) listing xactive for %s", this, 
02913                                    groupName.get()));
02914         PR_Free(lineToFree);
02915         return 0;
02916       }
02917     }
02918     m_nextState = NEWS_DONE;
02919     
02920 #ifdef UNREADY_CODE
02921     if(ce->bytes_received == 0)
02922     {
02923       /* #### no new groups */
02924     }
02925 #endif
02926     
02927     PR_Free(lineToFree);
02928     if(status > 0)
02929       return MK_DATA_LOADED;
02930     else
02931       return status;
02932   }
02933   else if (line [0] == '.' && line [1] == '.')
02934     /* The NNTP server quotes all lines beginning with "." by doubling it. */
02935     line++;
02936   
02937     /* almost correct
02938   */
02939   if(status > 1)
02940   {
02941 #ifdef UNREADY_CODE
02942     ce->bytes_received += status;
02943     FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->content_length);
02944 #else
02945     mBytesReceived += status;
02946     mBytesReceivedSinceLastStatusUpdate += status;
02947 #endif
02948   }
02949   
02950   /* format is "rec.arts.movies.past-films 7302 7119 y"
02951         */
02952   s = PL_strchr (line, ' ');
02953   if (s)
02954   {
02955     *s = 0;
02956     s1 = s+1;
02957     s = PL_strchr (s1, ' ');
02958     if (s)
02959     {
02960       *s = 0;
02961       s2 = s+1;
02962       s = PL_strchr (s2, ' ');
02963       if (s)
02964       {
02965         *s = 0;
02966         flag = s+1;
02967       }
02968     }
02969   }
02970   youngest = s2 ? atol(s1) : 0;
02971   oldest   = s1 ? atol(s2) : 0;
02972   
02973 #ifdef UNREADY_CODE
02974   ce->bytes_received++;  /* small numbers of groups never seem to trigger this */
02975 #else
02976   mBytesReceived += status;
02977   mBytesReceivedSinceLastStatusUpdate += status;
02978 #endif
02979   
02980   NS_ASSERTION(m_nntpServer, "no nntp incoming server");
02981   if (m_nntpServer) {
02982     rv = m_nntpServer->AddNewsgroupToList(line);
02983     NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
02984   }
02985   
02986   PRBool xactive=PR_FALSE;
02987   rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
02988   if (NS_SUCCEEDED(rv) && xactive)
02989   {
02990     nsCAutoString charset;
02991     nsAutoString lineUtf16;
02992     if (NS_SUCCEEDED(m_nntpServer->GetCharset(charset)) &&
02993         NS_SUCCEEDED(nsMsgI18NConvertToUnicode(charset.get(),
02994                                                nsDependentCString(line),
02995                                                lineUtf16, PR_TRUE)))
02996       m_nntpServer->SetGroupNeedsExtraInfo(NS_ConvertUTF16toUTF8(lineUtf16),
02997                                            PR_TRUE);
02998     else
02999       m_nntpServer->SetGroupNeedsExtraInfo(nsDependentCString(line), PR_TRUE);
03000   }
03001   
03002   PR_Free(lineToFree);
03003   return(status);
03004 }
03005 
03006 /* Ahhh, this like print's out the headers and stuff
03007  *
03008  * always returns 0
03009  */
03010         
03011 PRInt32 nsNNTPProtocol::BeginReadNewsList()
03012 {
03013        m_readNewsListCount = 0;
03014     mNumGroupsListed = 0;
03015     m_nextState = NNTP_READ_LIST;
03016 
03017     mBytesReceived = 0;
03018     mBytesReceivedSinceLastStatusUpdate = 0;
03019     m_startTime = PR_Now();
03020 
03021        PRInt32 status = 0;
03022 
03023     return(status);
03024 }
03025 
03026 #define RATE_CONSTANT 976.5625      /* PR_USEC_PER_SEC / 1024 bytes */
03027 
03028 static void ComputeRate(PRInt32 bytes, PRTime startTime, float *rate)
03029 {
03030   // rate = (bytes / USECS since start) * RATE_CONSTANT
03031 
03032   // compute usecs since we started.
03033   PRTime timeSinceStart;
03034   PRTime now = PR_Now();
03035   LL_SUB(timeSinceStart, now, startTime);
03036 
03037   // convert PRTime to PRInt32
03038   PRInt32 delta;
03039   LL_L2I(delta, timeSinceStart);
03040 
03041   // compute rate
03042   if (delta > 0) {
03043     *rate = (float) ((bytes * RATE_CONSTANT) / delta);
03044   }
03045   else {
03046     *rate = 0.0;
03047   }
03048 }
03049 
03050 /* display a list of all or part of the newsgroups list
03051  * from the news server
03052  */
03053 PRInt32 nsNNTPProtocol::ReadNewsList(nsIInputStream * inputStream, PRUint32 length)
03054 {
03055   nsresult rv = NS_OK;
03056   PRInt32 i=0;
03057   PRUint32 status = 1;
03058   
03059   PRBool pauseForMoreData = PR_FALSE;
03060   char *line, *lineToFree;
03061   line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
03062   
03063   if (pauseForMoreData)
03064   {
03065     SetFlag(NNTP_PAUSE_FOR_READ);
03066     PR_Free(lineToFree);
03067     return 0;
03068   }
03069   
03070   if (!line) 
03071     return(status);  /* no line yet */
03072   
03073   /* End of list? */
03074   if (line[0]=='.' && line[1]=='\0')
03075   {
03076     PRBool listpnames=PR_FALSE;
03077     NS_ASSERTION(m_nntpServer, "no nntp incoming server");
03078     if (m_nntpServer) {
03079       rv = m_nntpServer->QueryExtension("LISTPNAMES",&listpnames);
03080     }
03081     if (NS_SUCCEEDED(rv) && listpnames)
03082       m_nextState = NNTP_LIST_PRETTY_NAMES;
03083     else
03084       m_nextState = DISPLAY_NEWSGROUPS;
03085     ClearFlag(NNTP_PAUSE_FOR_READ);
03086     PR_Free(lineToFree);
03087     return 0;  
03088   }
03089   else if (line[0] == '.')
03090   {
03091     if ((line[1] == ' ') || (line[1] == '.' && line [2] == '.' && line[3] == ' '))
03092     {
03093       // some servers send "... 0000000001 0000000002 y"
03094       // and some servers send ". 0000000001 0000000002 y"
03095       // just skip that those lines
03096       // see bug #69231 and #123560
03097       PR_Free(lineToFree);
03098       return status;
03099     }
03100     // The NNTP server quotes all lines beginning with "." by doubling it, so unquote
03101     line++;
03102   }
03103   
03104   /* almost correct
03105   */
03106   if(status > 1)
03107   {
03108     mBytesReceived += status;
03109     mBytesReceivedSinceLastStatusUpdate += status;
03110     
03111     if ((mBytesReceivedSinceLastStatusUpdate > UPDATE_THRESHHOLD) && m_msgWindow) {
03112       mBytesReceivedSinceLastStatusUpdate = 0;
03113       
03114       nsCOMPtr <nsIMsgStatusFeedback> msgStatusFeedback;
03115       
03116       rv = m_msgWindow->GetStatusFeedback(getter_AddRefs(msgStatusFeedback));
03117       NS_ENSURE_SUCCESS(rv, rv);
03118       
03119       nsXPIDLString statusString;
03120       
03121       nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
03122       NS_ENSURE_SUCCESS(rv, rv);
03123       
03124       nsCOMPtr<nsIStringBundle> bundle;
03125       rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
03126       NS_ENSURE_SUCCESS(rv, rv);
03127       
03128       nsAutoString bytesStr; 
03129       bytesStr.AppendInt(mBytesReceived / 1024);
03130       
03131       // compute the rate, and then convert it have one 
03132       // decimal precision.
03133       float rate = 0.0;
03134       ComputeRate(mBytesReceived, m_startTime, &rate);
03135       char rate_buf[RATE_STR_BUF_LEN];
03136       PR_snprintf(rate_buf,RATE_STR_BUF_LEN,"%.1f", rate);
03137       
03138       nsAutoString rateStr;
03139       rateStr.AppendWithConversion(rate_buf);
03140       
03141       nsAutoString numGroupsStr;
03142       numGroupsStr.AppendInt(mNumGroupsListed);
03143       
03144       const PRUnichar *formatStrings[3] = { numGroupsStr.get(), bytesStr.get(), rateStr.get() };
03145       rv = bundle->FormatStringFromName(NS_LITERAL_STRING("bytesReceived").get(),
03146         formatStrings, 3,
03147         getter_Copies(statusString));
03148       
03149       rv = msgStatusFeedback->ShowStatusString(statusString);
03150       if (NS_FAILED(rv)) {
03151         PR_Free(lineToFree);
03152         return rv;
03153       }
03154     }
03155   }
03156   
03157   /* find whitespace separator if it exits */
03158   for(i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
03159     ;  /* null body */
03160   
03161   char *description;
03162   if(line[i] == '\0')
03163     description = &line[i];
03164   else
03165     description = &line[i+1];
03166   
03167   line[i] = 0; /* terminate group name */
03168   
03169   /* store all the group names */
03170   NS_ASSERTION(m_nntpServer, "no nntp incoming server");
03171   if (m_nntpServer) {
03172     m_readNewsListCount++;
03173     mNumGroupsListed++;
03174     rv = m_nntpServer->AddNewsgroupToList(line);
03175 //    NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
03176     // since it's not fatal, don't let this error stop the LIST command.
03177     rv = NS_OK;
03178   }
03179   else {
03180     rv = NS_ERROR_FAILURE;
03181   }
03182   
03183   if (m_readNewsListCount == READ_NEWS_LIST_COUNT_MAX) {
03184     m_readNewsListCount = 0;
03185     if (mUpdateTimer) {
03186       mUpdateTimer->Cancel();
03187       mUpdateTimer = nsnull;
03188     }
03189     mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
03190     NS_ASSERTION(NS_SUCCEEDED(rv),"failed to create timer");
03191     if (NS_FAILED(rv)) {
03192       PR_Free(lineToFree);
03193       return -1;
03194     }
03195     
03196     mInputStream = inputStream;
03197     
03198     const PRUint32 kUpdateTimerDelay = READ_NEWS_LIST_TIMEOUT;
03199     rv = mUpdateTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback*,this), kUpdateTimerDelay,
03200       nsITimer::TYPE_ONE_SHOT);
03201     NS_ASSERTION(NS_SUCCEEDED(rv),"failed to init timer");
03202     if (NS_FAILED(rv)) {
03203       PR_Free(lineToFree);
03204       return -1;
03205     }
03206     
03207     m_nextState = NEWS_FINISHED;
03208 
03209     // suspend necko request until timeout
03210     // might not have a request if someone called CloseSocket()
03211     // see bug #195440
03212     if (m_request)
03213       m_request->Suspend();
03214   }
03215   
03216   PR_Free(lineToFree);
03217   if (NS_FAILED(rv))
03218     return -1;
03219   return(status);
03220 }
03221 
03222 NS_IMETHODIMP
03223 nsNNTPProtocol::Notify(nsITimer *timer)
03224 {
03225   NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
03226   mUpdateTimer = nsnull;    // release my hold  
03227   TimerCallback();
03228   return NS_OK;
03229 }
03230 
03231 void nsNNTPProtocol::TimerCallback()
03232 {
03233   PR_LOG(NNTP,PR_LOG_ALWAYS,("nsNNTPProtocol::TimerCallback\n"));
03234   m_nextState = NNTP_READ_LIST;
03235   
03236   // process whatever is already in the buffer at least once.
03237   //
03238   // NOTE: while downloading, it would almost be enough to just
03239   // resume necko since it will call us again with data.  however,
03240   // if we are at the end of the data stream then we must call
03241   // ProcessProtocolState since necko will not call us again.
03242   //
03243   // NOTE: this function may Suspend necko.  Suspend is a reference
03244   // counted (i.e., two suspends requires two resumes before the
03245   // request will actually be resumed).
03246   //
03247   ProcessProtocolState(nsnull, mInputStream, 0,0); 
03248   
03249   // resume necko request
03250   // might not have a request if someone called CloseSocket() 
03251   // see bug #195440
03252   if (m_request)
03253     m_request->Resume();
03254   
03255   return;
03256 }
03257 
03258 void nsNNTPProtocol::HandleAuthenticationFailure()
03259 {
03260   PRBool userHasAuthenticatedInThisSession;
03261   m_nntpServer->GetUserAuthenticated(&userHasAuthenticatedInThisSession);
03262 
03263   /* login failed */
03264   AlertError(MK_NNTP_AUTH_FAILED, m_responseText);
03265   
03266   NS_ASSERTION(m_newsFolder, "no newsFolder");
03267   if (m_newsFolder) 
03268   {
03269     if (!userHasAuthenticatedInThisSession)
03270     {
03271       (void) m_newsFolder->ForgetGroupUsername();
03272       (void) m_newsFolder->ForgetGroupPassword();       
03273     }
03274     // we'll allow one failure before clearing out password,
03275     // but we need to handle the case where the password has
03276     // changed while the app is running, and 
03277     // reprompt the user for the (potentially) new password.
03278     // So clear the userAuthenticated flag.
03279     m_nntpServer->SetUserAuthenticated(PR_FALSE);
03280   }
03281 }
03282 
03283 /* start the xover command
03284  */
03285 
03286 PRInt32 nsNNTPProtocol::BeginReadXover()
03287 {
03288   PRInt32 count;     /* Response fields */
03289   nsresult rv = NS_OK;
03290   
03291   rv = SetCurrentGroup();
03292   if (NS_FAILED(rv)) return -1;
03293   
03294   /* Make sure we never close and automatically reopen the connection at this
03295   point; we'll confuse libmsg too much... */
03296   
03297   SetFlag(NNTP_SOME_PROTOCOL_SUCCEEDED); 
03298   
03299   /* We have just issued a GROUP command and read the response.
03300   Now parse that response to help decide which articles to request
03301   xover data for.
03302         */
03303   PR_sscanf(m_responseText,
03304     "%d %d %d", 
03305     &count, 
03306     &m_firstPossibleArticle, 
03307     &m_lastPossibleArticle);
03308   
03309   m_newsgroupList = do_CreateInstance(NS_NNTPNEWSGROUPLIST_CONTRACTID, &rv);
03310   if (NS_FAILED(rv)) return -1;
03311   
03312   rv = m_newsgroupList->Initialize(m_runningURL, m_newsFolder);
03313   if (NS_FAILED(rv)) return -1;
03314   
03315   rv = m_newsFolder->UpdateSummaryFromNNTPInfo(m_firstPossibleArticle, m_lastPossibleArticle, count);
03316   if (NS_FAILED(rv)) return -1;
03317   
03318   m_numArticlesLoaded = 0;
03319   
03320   // if the user sets max_articles to a bogus value, get them everything
03321   m_numArticlesWanted = m_maxArticles > 0 ? m_maxArticles : 1L << 30; 
03322   
03323   m_nextState = NNTP_FIGURE_NEXT_CHUNK;
03324   ClearFlag(NNTP_PAUSE_FOR_READ);
03325   return 0;
03326 }
03327 
03328 PRInt32 nsNNTPProtocol::FigureNextChunk()
03329 {
03330     nsresult rv = NS_OK;
03331        PRInt32 status = 0;
03332 
03333        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
03334        if (m_firstArticle > 0) 
03335        {
03336       PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) add to known articles:  %d - %d", this, m_firstArticle, m_lastArticle));
03337 
03338       if (NS_SUCCEEDED(rv) && m_newsgroupList) {
03339           rv = m_newsgroupList->AddToKnownArticles(m_firstArticle,
03340                                                  m_lastArticle);
03341       }
03342 
03343          if (NS_FAILED(rv)) return status;
03344        }
03345                                                                        
03346        if (m_numArticlesLoaded >= m_numArticlesWanted) 
03347        {
03348          m_nextState = NEWS_PROCESS_XOVER;
03349          ClearFlag(NNTP_PAUSE_FOR_READ);
03350          return 0;
03351        }
03352 
03353     NS_ASSERTION(m_newsgroupList, "no newsgroupList");
03354     if (!m_newsgroupList) return -1;
03355         
03356     PRBool getOldMessages = PR_FALSE;
03357     if (m_runningURL) {
03358       rv = m_runningURL->GetGetOldMessages(&getOldMessages);
03359       if (NS_FAILED(rv)) return status;
03360     }
03361 
03362     rv = m_newsgroupList->SetGetOldMessages(getOldMessages);
03363     if (NS_FAILED(rv)) return status;
03364     
03365     rv = m_newsgroupList->GetRangeOfArtsToDownload(m_msgWindow,
03366       m_firstPossibleArticle,
03367       m_lastPossibleArticle,
03368       m_numArticlesWanted -
03369       m_numArticlesLoaded,
03370       &(m_firstArticle),
03371       &(m_lastArticle),
03372       &status);
03373 
03374        if (NS_FAILED(rv)) return status;
03375 
03376        if (m_firstArticle <= 0 || m_firstArticle > m_lastArticle) 
03377        {
03378          /* Nothing more to get. */
03379          m_nextState = NEWS_PROCESS_XOVER;
03380          ClearFlag(NNTP_PAUSE_FOR_READ);
03381          return 0;
03382        }
03383 
03384        PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) Chunk will be (%d-%d)", this, m_firstArticle, m_lastArticle));
03385     
03386        m_articleNumber = m_firstArticle;
03387 
03388     /* was MSG_InitXOVER() */
03389     if (m_newsgroupList) {
03390         rv = m_newsgroupList->InitXOVER(m_firstArticle, m_lastArticle);
03391        }
03392 
03393     /* convert nsresult->status */
03394     status = NS_FAILED(rv);
03395 
03396        if (status < 0) 
03397          return status;
03398 
03399        ClearFlag(NNTP_PAUSE_FOR_READ);
03400        if (TestFlag(NNTP_NO_XOVER_SUPPORT)) 
03401               m_nextState = NNTP_READ_GROUP;
03402        else 
03403               m_nextState = NNTP_XOVER_SEND;
03404 
03405        return 0;
03406 }
03407 
03408 PRInt32 nsNNTPProtocol::XoverSend()
03409 {
03410        char outputBuffer[OUTPUT_BUFFER_SIZE];
03411        PRInt32 status = 0;
03412 
03413     PR_snprintf(outputBuffer, 
03414                             OUTPUT_BUFFER_SIZE,
03415                             "XOVER %d-%d" CRLF, 
03416                             m_firstArticle, 
03417                             m_lastArticle);
03418 
03419        NNTP_LOG_WRITE(outputBuffer);
03420 
03421     m_nextState = NNTP_RESPONSE;
03422     m_nextStateAfterResponse = NNTP_XOVER_RESPONSE;
03423     SetFlag(NNTP_PAUSE_FOR_READ);
03424 
03425        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
03426        if (mailnewsurl)
03427               status = SendData(mailnewsurl, outputBuffer); 
03428        return status;
03429 }
03430 
03431 /* see if the xover response is going to return us data
03432  * if the proper code isn't returned then assume xover
03433  * isn't supported and use
03434  * normal read_group
03435  */
03436 
03437 PRInt32 nsNNTPProtocol::ReadXoverResponse()
03438 {
03439 #ifdef TEST_NO_XOVER_SUPPORT
03440        m_responseCode = MK_NNTP_RESPONSE_CHECK_ERROR; /* pretend XOVER generated an error */
03441 #endif
03442 
03443     if(m_responseCode != MK_NNTP_RESPONSE_XOVER_OK)
03444     {
03445         /* If we didn't get back "224 data follows" from the XOVER request,
03446                  then that must mean that this server doesn't support XOVER.  Or
03447                  maybe the server's XOVER support is busted or something.  So,
03448                  in that case, fall back to the very slow HEAD method.
03449 
03450                  But, while debugging here at HQ, getting into this state means
03451                  something went very wrong, since our servers do XOVER.  Thus
03452                  the assert.
03453          */
03454               /*NS_ASSERTION (0,"something went very wrong");*/
03455               m_nextState = NNTP_READ_GROUP;
03456               SetFlag(NNTP_NO_XOVER_SUPPORT);
03457     }
03458     else
03459     {
03460         m_nextState = NNTP_XOVER;
03461     }
03462 
03463     return(0);  /* continue */
03464 }
03465 
03466 /* process the xover list as it comes from the server
03467  * and load it into the sort list.  
03468  */
03469 
03470 PRInt32 nsNNTPProtocol::ReadXover(nsIInputStream * inputStream, PRUint32 length)
03471 {
03472   char *line, *lineToFree;
03473   nsresult rv;
03474   PRUint32 status = 1;
03475   
03476   PRBool pauseForMoreData = PR_FALSE;
03477   line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
03478   
03479   if(pauseForMoreData)
03480   {
03481     SetFlag(NNTP_PAUSE_FOR_READ);
03482     return 0;
03483   }
03484   
03485   if(!line)
03486     return(status);  /* no line yet or TCP error */
03487   
03488   if(line[0] == '.' && line[1] == '\0')
03489   {
03490     m_nextState = NNTP_FIGURE_NEXT_CHUNK;
03491     ClearFlag(NNTP_PAUSE_FOR_READ);
03492     PR_Free(lineToFree);
03493     return(0);
03494   }
03495   else if (line [0] == '.' && line [1] == '.')
03496     /* The NNTP server quotes all lines beginning with "." by doubling it. */
03497     line++;
03498   
03499     /* almost correct
03500   */
03501   if(status > 1)
03502   {
03503     mBytesReceived += status;
03504     mBytesReceivedSinceLastStatusUpdate += status;
03505   }
03506   
03507   rv = m_newsgroupList->ProcessXOVERLINE(line, &status);
03508   NS_ASSERTION(NS_SUCCEEDED(rv), "failed to process the XOVERLINE");
03509   
03510   m_numArticlesLoaded++;
03511   PR_Free(lineToFree);
03512   return NS_SUCCEEDED(rv) ? status : -1; /* keep going if no error */
03513 }
03514 
03515 /* Finished processing all the XOVER data.
03516 */
03517 
03518 PRInt32 nsNNTPProtocol::ProcessXover()
03519 {
03520   nsresult rv;
03521   
03522   /* xover_parse_state stored in MSG_Pane cd->pane */
03523   NS_ASSERTION(m_newsgroupList, "no newsgroupList");
03524   if (!m_newsgroupList) return -1;
03525   
03526   PRInt32 status = 0;
03527   rv = m_newsgroupList->FinishXOVERLINE(0,&status);
03528   m_newsgroupList = nsnull;
03529   if (NS_SUCCEEDED(rv) && status < 0) return status;
03530   
03531   m_nextState = NEWS_DONE;
03532   
03533   return(MK_DATA_LOADED);
03534 }
03535 
03536 PRInt32 nsNNTPProtocol::ReadNewsgroup()
03537 {
03538   if(m_articleNumber > m_lastArticle)
03539   {  /* end of groups */
03540     
03541     m_nextState = NNTP_FIGURE_NEXT_CHUNK;
03542     ClearFlag(NNTP_PAUSE_FOR_READ);
03543     return(0);
03544   }
03545   else
03546   {
03547     char outputBuffer[OUTPUT_BUFFER_SIZE];
03548     PR_snprintf(outputBuffer, 
03549       OUTPUT_BUFFER_SIZE,  
03550       "HEAD %ld" CRLF, 
03551       m_articleNumber++);
03552     m_nextState = NNTP_RESPONSE;
03553     m_nextStateAfterResponse = NNTP_READ_GROUP_RESPONSE;
03554     
03555     SetFlag(NNTP_PAUSE_FOR_READ);
03556     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
03557     if (mailnewsurl)
03558       return SendData(mailnewsurl, outputBuffer);
03559     else
03560       return 0;
03561   }
03562 }
03563 
03564 /* See if the "HEAD" command was successful
03565 */
03566 
03567 PRInt32 nsNNTPProtocol::ReadNewsgroupResponse()
03568 {
03569   nsresult rv;
03570   
03571   if (m_responseCode == MK_NNTP_RESPONSE_ARTICLE_HEAD)
03572   {     /* Head follows - parse it:*/
03573     m_nextState = NNTP_READ_GROUP_BODY;
03574     
03575     if(m_messageID)
03576       *m_messageID = '\0';
03577     
03578     m_key = nsMsgKey_None;
03579     
03580     /* Give the message number to the header parser. */
03581     rv = m_newsgroupList->ProcessNonXOVER(m_responseText);
03582     /* convert nsresult->status */
03583     return NS_FAILED(rv);
03584   }
03585   else
03586   {
03587     NNTP_LOG_NOTE(("Bad group header found!"));
03588     m_nextState = NNTP_READ_GROUP;
03589     return(0);
03590   }
03591 }
03592 
03593 /* read the body of the "HEAD" command
03594 */
03595 PRInt32 nsNNTPProtocol::ReadNewsgroupBody(nsIInputStream * inputStream, PRUint32 length)
03596 {
03597   char *line, *lineToFree;
03598   nsresult rv;
03599   PRUint32 status = 1;
03600   
03601   PRBool pauseForMoreData = PR_FALSE;
03602   line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
03603   
03604   if(pauseForMoreData)
03605   {
03606     SetFlag(NNTP_PAUSE_FOR_READ);
03607     return 0;
03608   }
03609   
03610   /* if TCP error of if there is not a full line yet return
03611   */
03612   if(!line)
03613     return status;
03614   
03615   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) read_group_body: got line: %s|",this,line));
03616   
03617   /* End of body? */
03618   if (line[0]=='.' && line[1]=='\0')
03619   {
03620     m_nextState = NNTP_READ_GROUP;
03621     ClearFlag(NNTP_PAUSE_FOR_READ);
03622   }
03623   else if (line [0] == '.' && line [1] == '.')
03624     /* The NNTP server quotes all lines beginning with "." by doubling it. */
03625     line++;
03626   
03627   rv = m_newsgroupList->ProcessNonXOVER(line);
03628   /* convert nsresult->status */
03629   PR_Free(lineToFree);
03630   return NS_FAILED(rv);
03631 }
03632 
03633 
03634 nsresult nsNNTPProtocol::GetNewsStringByID(PRInt32 stringID, PRUnichar **aString)
03635 {
03636   nsresult rv;
03637   nsAutoString resultString(NS_LITERAL_STRING("???"));
03638   
03639   if (!m_stringBundle)
03640   {
03641     char*       propertyURL = NEWS_MSGS_URL;
03642     
03643     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
03644     NS_ENSURE_SUCCESS(rv, rv);
03645     
03646     rv = bundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
03647     NS_ENSURE_SUCCESS(rv, rv);
03648   }
03649   
03650   if (m_stringBundle) {
03651     PRUnichar *ptrv = nsnull;
03652     rv = m_stringBundle->GetStringFromID(stringID, &ptrv);
03653     
03654     if (NS_FAILED(rv)) {
03655       resultString.AssignLiteral("[StringID");
03656       resultString.AppendInt(stringID);
03657       resultString.AppendLiteral("?]");
03658       *aString = ToNewUnicode(resultString);
03659     }
03660     else {
03661       *aString = ptrv;
03662     }
03663   }
03664   else {
03665     rv = NS_OK;
03666     *aString = ToNewUnicode(resultString);
03667   }
03668   return rv;
03669 }
03670 
03671 nsresult nsNNTPProtocol::GetNewsStringByName(const char *aName, PRUnichar **aString)
03672 {
03673   nsresult rv;
03674   nsAutoString resultString(NS_LITERAL_STRING("???"));
03675   if (!m_stringBundle)
03676   {
03677     char*       propertyURL = NEWS_MSGS_URL;
03678     
03679     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
03680     NS_ENSURE_SUCCESS(rv, rv);
03681     
03682     rv = bundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
03683   }
03684   
03685   if (m_stringBundle)
03686   {
03687     nsAutoString unicodeName; unicodeName.AssignWithConversion(aName);
03688     
03689     PRUnichar *ptrv = nsnull;
03690     rv = m_stringBundle->GetStringFromName(unicodeName.get(), &ptrv);
03691     
03692     if (NS_FAILED(rv)) 
03693     {
03694       resultString.AssignLiteral("[StringName");
03695       resultString.AppendWithConversion(aName);
03696       resultString.AppendLiteral("?]");
03697       *aString = ToNewUnicode(resultString);
03698     }
03699     else
03700     {
03701       *aString = ptrv;
03702     }
03703   }
03704   else
03705   {
03706     rv = NS_OK;
03707     *aString = ToNewUnicode(resultString);
03708   }
03709   return rv;
03710 }
03711 
03712 // sspitzer:  PostMessageInFile is derived from nsSmtpProtocol::SendMessageInFile()
03713 PRInt32 nsNNTPProtocol::PostMessageInFile(nsIFileSpec *aPostMessageFile)
03714 {
03715     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
03716     if (url && aPostMessageFile)
03717         nsMsgProtocol::PostMessage(url, aPostMessageFile);
03718 
03719     SetFlag(NNTP_PAUSE_FOR_READ);
03720     
03721     // for now, we are always done at this point..we aren't making multiple
03722     // calls to post data...
03723 
03724     // always issue a '.' and CRLF when we are done...
03725     PL_strcpy(m_dataBuf, "." CRLF);
03726     if (url)
03727       SendData(url, m_dataBuf);
03728 #ifdef UNREADY_CODE
03729     NET_Progress(CE_WINDOW_ID,
03730                  XP_GetString(XP_MESSAGE_SENT_WAITING_MAIL_REPLY));
03731 #endif /* UNREADY_CODE */
03732     m_nextState = NNTP_RESPONSE;
03733     m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
03734     return(0);
03735 }
03736     
03737 PRInt32 nsNNTPProtocol::PostData()
03738 {
03739     /* returns 0 on done and negative on error
03740      * positive if it needs to continue.
03741      */
03742     NNTP_LOG_NOTE("nsNNTPProtocol::PostData()");
03743     nsresult rv = NS_OK;
03744     
03745     nsCOMPtr <nsINNTPNewsgroupPost> message;
03746     rv = m_runningURL->GetMessageToPost(getter_AddRefs(message));
03747     if (NS_SUCCEEDED(rv)) 
03748     {
03749         nsCOMPtr<nsIFileSpec> filePath;
03750         rv = message->GetPostMessageFile(getter_AddRefs(filePath));
03751         if (NS_SUCCEEDED(rv)) 
03752             PostMessageInFile(filePath);
03753      }
03754 
03755     return 0;
03756 }
03757 
03758 
03759 /* interpret the response code from the server
03760  * after the post is done
03761  */   
03762 PRInt32 nsNNTPProtocol::PostDataResponse()
03763 {
03764        if (m_responseCode != MK_NNTP_RESPONSE_POST_OK) 
03765        {
03766          AlertError(MK_NNTP_ERROR_MESSAGE,m_responseText);
03767          m_nextState = NEWS_ERROR;
03768          return(MK_NNTP_ERROR_MESSAGE);
03769        }
03770     m_nextState = NEWS_POST_DONE;
03771        ClearFlag(NNTP_PAUSE_FOR_READ);
03772     return(MK_DATA_LOADED);
03773 }
03774 
03775 PRInt32 nsNNTPProtocol::CheckForArticle()
03776 {
03777   m_nextState = NEWS_ERROR;
03778   if (m_responseCode >= 220 && m_responseCode <= 223) {
03779        /* Yes, this article is already there, we're all done. */
03780        return MK_DATA_LOADED;
03781   } 
03782   else 
03783   {
03784        /* The article isn't there, so the failure we had earlier wasn't due to
03785           a duplicate message-id.  Return the error from that previous
03786           posting attempt (which is already in ce->URL_s->error_msg). */
03787        return MK_NNTP_ERROR_MESSAGE;
03788   }
03789 }
03790 
03791 #define NEWS_GROUP_DISPLAY_FREQ 1
03792 
03793 nsresult
03794 nsNNTPProtocol::SetCheckingForNewNewsStatus(PRInt32 current, PRInt32 total)
03795 {
03796     nsresult rv;
03797     nsXPIDLString statusString;
03798 
03799     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
03800     NS_ENSURE_SUCCESS(rv, rv);
03801 
03802     nsCOMPtr<nsIStringBundle> bundle;
03803     rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
03804     NS_ENSURE_SUCCESS(rv, rv);
03805 
03806     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer, &rv);
03807     NS_ENSURE_SUCCESS(rv, rv);
03808  
03809     nsXPIDLCString hostName;
03810     rv = server->GetHostName(getter_Copies(hostName));
03811     NS_ENSURE_SUCCESS(rv, rv);
03812 
03813     nsAutoString thisGroupStr; 
03814     thisGroupStr.AppendInt(current);
03815 
03816     nsAutoString totalGroupStr; 
03817     totalGroupStr.AppendInt(total);
03818 
03819     nsAutoString hostNameStr;
03820     hostNameStr.AssignWithConversion(hostName);
03821     
03822     const PRUnichar *formatStrings[] = { thisGroupStr.get(), totalGroupStr.get(), hostNameStr.get() };
03823 
03824     rv = bundle->FormatStringFromName(NS_LITERAL_STRING("checkingForNewNews").get(),
03825                                                   formatStrings, 3,
03826                                                   getter_Copies(statusString));
03827     NS_ENSURE_SUCCESS(rv, rv);
03828 
03829     rv = SetProgressStatus(statusString);
03830     NS_ENSURE_SUCCESS(rv, rv);
03831 
03832     SetProgressBarPercent(current, total);
03833     return NS_OK;
03834 }
03835 
03836 PRInt32 nsNNTPProtocol::GetNextGroupNeedingCounts( nsISupports** pNextGroup, PRInt32* returnStatus )
03837 {
03838   
03839   nsresult rv = m_nntpServer->GetFirstGroupNeedingCounts( pNextGroup );
03840   if (NS_FAILED(rv)) {
03841     ClearFlag(NNTP_NEWSRC_PERFORMED);
03842     *returnStatus = -1;
03843     return rv;
03844   }
03845   else if (!*pNextGroup) {
03846     ClearFlag(NNTP_NEWSRC_PERFORMED);
03847     m_nextState = NEWS_DONE;
03848     
03849     if (m_newsRCListCount) {
03850       // clear the status text.
03851       rv = SetProgressStatus(EmptyString().get());
03852       NS_ENSURE_SUCCESS(rv, rv);
03853       
03854       SetProgressBarPercent(0, -1);
03855       m_newsRCListCount = 0;
03856       *returnStatus = 0;
03857     }
03858     else if (m_responseCode == MK_NNTP_RESPONSE_LIST_OK)  {
03859     /*
03860      * 5-9-96 jefft 
03861      * If for some reason the news server returns an empty 
03862      * newsgroups list with a nntp response code MK_NNTP_RESPONSE_LIST_OK -- list of
03863      * newsgroups follows. We set status to MK_EMPTY_NEWS_LIST
03864      * to end the infinite dialog loop.
03865      */
03866       *returnStatus = MK_EMPTY_NEWS_LIST;
03867     }
03868     
03869     if(*returnStatus > -1)
03870       *returnStatus = MK_DATA_LOADED; 
03871     
03872     return NS_ERROR_FAILURE;
03873   }
03874   
03875   return NS_OK;
03876 }
03877 
03878 PRInt32 nsNNTPProtocol::DisplayNewsRC()
03879 {
03880   PRInt32 status = 0;
03881   nsresult rv;
03882   
03883   if(!TestFlag(NNTP_NEWSRC_PERFORMED)) {
03884     SetFlag(NNTP_NEWSRC_PERFORMED);
03885     rv = m_nntpServer->GetNumGroupsNeedingCounts(&m_newsRCListCount);
03886     NS_ENSURE_SUCCESS(rv, rv);
03887   }
03888   
03889   nsCOMPtr <nsISupports> currChild;
03890   
03891   PRInt32 groupsToAdvance = m_RCIndexToResumeAfterAuthRequest + 1;
03892   m_RCIndexToResumeAfterAuthRequest = 0;
03893   // if we entered this method with a m_RCIndexToResumeAfterAuthRequest > 0, then this is a
03894   // re-incarnation: we already did a run before, but it was disrupted by
03895   // an authorization request (see GotAuthorizationRequest)
03896   while ( groupsToAdvance-- )
03897     if ( NS_FAILED( GetNextGroupNeedingCounts( getter_AddRefs( currChild ), &status ) ) )
03898       return status;
03899     
03900     nsCOMPtr<nsIMsgFolder> currFolder = do_QueryInterface(currChild, &rv);
03901     if (NS_FAILED(rv)) return -1;
03902     if (!currFolder) return -1;
03903     
03904     m_newsFolder = do_QueryInterface(currFolder, &rv);
03905     if (NS_FAILED(rv)) return -1;
03906     if (!m_newsFolder) return -1;
03907     
03908     nsXPIDLCString name;
03909     rv = m_newsFolder->GetRawName(name);
03910     if (NS_FAILED(rv)) return -1;
03911     if (!name) return -1;
03912     
03913     /* send group command to server */
03914     char outputBuffer[OUTPUT_BUFFER_SIZE];
03915     
03916     PR_snprintf(outputBuffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF, (const char *)name);
03917     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
03918     if (mailnewsurl) {
03919       status = SendData(mailnewsurl, outputBuffer);
03920     }
03921     
03922     /* only update every NEWS_GROUP_DISPLAY_FREQ groups for speed */
03923     if ((m_newsRCListCount >= NEWS_GROUP_DISPLAY_FREQ) && ((m_newsRCListIndex % NEWS_GROUP_DISPLAY_FREQ) == 0 || ((m_newsRCListIndex+1) == m_newsRCListCount))) {
03924       rv = SetCheckingForNewNewsStatus(m_newsRCListIndex+1, m_newsRCListCount);
03925       if (NS_FAILED(rv)) return -1;
03926     }
03927     
03928     m_newsRCListIndex++;
03929     
03930     SetFlag(NNTP_PAUSE_FOR_READ);
03931     m_nextState = NNTP_RESPONSE;
03932     m_nextStateAfterResponse = NEWS_DISPLAY_NEWS_RC_RESPONSE;
03933     
03934     return status; /* keep going */
03935 }
03936 
03937 /* Parses output of GROUP command */
03938 PRInt32 nsNNTPProtocol::DisplayNewsRCResponse()
03939 {
03940   nsresult rv = NS_OK;
03941   PRInt32 status = 0;
03942   if(m_responseCode == MK_NNTP_RESPONSE_GROUP_SELECTED)
03943   {
03944     char *num_arts = 0, *low = 0, *high = 0, *group = 0;
03945     PRInt32 first_art, last_art;
03946     
03947     /* line looks like:
03948     *     211 91 3693 3789 comp.infosystems
03949     */
03950     
03951     num_arts = m_responseText;
03952     low = PL_strchr(num_arts, ' ');
03953     
03954     if(low)
03955     {
03956       first_art = atol(low);
03957       *low++ = '\0';
03958       high= PL_strchr(low, ' ');
03959     }
03960     if(high)
03961     {
03962       *high++ = '\0';
03963       group = PL_strchr(high, ' ');
03964     }
03965     if(group)
03966     {
03967       *group++ = '\0';
03968       /* the group name may be contaminated by "group selected" at
03969                         the end.  This will be space separated from the group name.
03970                            If a space is found in the group name terminate at that
03971                         point. */
03972       strtok(group, " ");
03973       last_art = atol(high);
03974     }
03975     
03976     // this might save us a GROUP command, if the user reads a message in the 
03977     // last group we update.
03978     m_currentGroup = group;
03979     
03980     // prevent crash when
03981     // if going offline in the middle of
03982     // updating the unread counts on a news server
03983     // (running a "news://host/*" url)
03984     NS_ASSERTION(m_nntpServer,"no server");
03985     if (!m_nntpServer) return -1;
03986     
03987     rv = m_nntpServer->DisplaySubscribedGroup(m_newsFolder,
03988       low ? atol(low) : 0,
03989       high ? atol(high) : 0,
03990       atol(num_arts));
03991     NS_ASSERTION(NS_SUCCEEDED(rv),"DisplaySubscribedGroup() failed");
03992     if (NS_FAILED(rv)) status = -1;
03993     
03994     if (status < 0) return status;
03995   }
03996   else if (m_responseCode == MK_NNTP_RESPONSE_GROUP_NO_GROUP)
03997   {
03998     nsXPIDLString name;
03999     rv = m_newsFolder->GetUnicodeName(name);
04000     
04001     if (NS_SUCCEEDED(rv)) {
04002         m_nntpServer->GroupNotFound(m_msgWindow, name, PR_FALSE);
04003     }
04004     
04005     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) NO_GROUP, so unset m_currentGroup", this));
04006     m_currentGroup.Truncate();
04007   }
04008   /* it turns out subscribe ui depends on getting this displaysubscribedgroup call,
04009   even if there was an error.
04010   */
04011   if(m_responseCode != MK_NNTP_RESPONSE_GROUP_SELECTED)
04012   {
04013               /* only on news server error or when zero articles
04014     */
04015 #ifdef DEBUG_seth
04016     NS_ASSERTION(PR_FALSE,"check this code");
04017 #endif
04018     rv = m_nntpServer->DisplaySubscribedGroup(m_newsFolder, 0, 0, 0);
04019     NS_ASSERTION(NS_SUCCEEDED(rv),"DisplaySubscribedGroup() failed");
04020     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) error, so unset m_currentGroup", this));
04021     m_currentGroup.Truncate();
04022   }
04023   
04024   m_nextState = NEWS_DISPLAY_NEWS_RC;
04025               
04026   return 0;
04027 }
04028 
04029 PRInt32 nsNNTPProtocol::StartCancel()
04030 {
04031   PRInt32 status = 0;
04032   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04033   if (mailnewsurl)
04034     status = SendData(mailnewsurl, NNTP_CMD_POST);
04035 
04036   m_nextState = NNTP_RESPONSE;
04037   m_nextStateAfterResponse = NEWS_DO_CANCEL;
04038   SetFlag(NNTP_PAUSE_FOR_READ);
04039   return (status);
04040 }
04041 
04042 PRBool nsNNTPProtocol::CheckIfAuthor(nsISupports *aElement, void *data)
04043 {
04044     nsresult rv;
04045 
04046     cancelInfoEntry *cancelInfo = (cancelInfoEntry*) data;
04047 
04048     if (cancelInfo->from) {
04049        // already found a match, no need to go any further
04050         // keep going
04051         return PR_TRUE;
04052     }
04053     
04054     nsCOMPtr<nsIMsgIdentity> identity = do_QueryInterface(aElement, &rv);
04055     if (NS_FAILED(rv)) {
04056         // keep going
04057         return PR_TRUE;
04058     }
04059     
04060     if (identity) {
04061         identity->GetEmail(&cancelInfo->from);
04062         PR_LOG(NNTP,PR_LOG_ALWAYS,("from = %s", cancelInfo->from));
04063     }
04064     
04065     nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv);
04066 
04067     if (NS_FAILED(rv)) {
04068         PR_FREEIF(cancelInfo->from);
04069         cancelInfo->from = nsnull;
04070 
04071         // keep going
04072         return PR_TRUE;
04073     }
04074 
04075     nsXPIDLCString us;
04076     nsXPIDLCString them;
04077     nsresult rv1 = parser->ExtractHeaderAddressMailboxes(nsnull, cancelInfo->from, getter_Copies(us));
04078     nsresult rv2 = parser->ExtractHeaderAddressMailboxes(nsnull, cancelInfo->old_from, getter_Copies(them));
04079     
04080     PR_LOG(NNTP,PR_LOG_ALWAYS,("us = %s, them = %s", us.get(), them.get()));
04081 
04082     if ((NS_FAILED(rv1) || NS_FAILED(rv2) || PL_strcasecmp(us, them))) {
04083         //no match.  don't set cancel email
04084         PR_FREEIF(cancelInfo->from);
04085         cancelInfo->from = nsnull;
04086 
04087         // keep going
04088         return PR_TRUE;
04089     }
04090     else {
04091 
04092       // we have a match, stop.
04093         return PR_FALSE;
04094     }          
04095 }
04096 
04097 PRInt32 nsNNTPProtocol::DoCancel()
04098 {
04099     PRInt32 status = 0;
04100     PRBool failure = PR_FALSE;
04101     nsresult rv = NS_OK;
04102     char *id = nsnull;
04103     char *subject = nsnull;
04104     char *newsgroups = nsnull;
04105     char *distribution = nsnull;
04106     char *body = nsnull;
04107     cancelInfoEntry cancelInfo;
04108     PRBool requireConfirmationForCancel = PR_TRUE;
04109     PRBool showAlertAfterCancel = PR_TRUE;
04110 
04111     int L;
04112 
04113   /* #### Should we do a more real check than this?  If the POST command
04114         didn't respond with "MK_NNTP_RESPONSE_POST_SEND_NOW Ok", then it's not ready for us to throw a
04115         message at it...   But the normal posting code doesn't do this check.
04116         Why?
04117    */
04118   NS_ASSERTION (m_responseCode == MK_NNTP_RESPONSE_POST_SEND_NOW, "code != POST_SEND_NOW");
04119 
04120   // These shouldn't be set yet, since the headers haven't been "flushed"
04121   // "Distribution: " doesn't appear to be required, so
04122   // don't assert on m_cancelDistribution
04123   NS_ASSERTION (m_cancelID &&
04124                       m_cancelFromHdr &&
04125                       m_cancelNewsgroups, "null ptr");
04126 
04127   newsgroups = m_cancelNewsgroups;
04128   distribution = m_cancelDistribution;
04129   id = m_cancelID;
04130   cancelInfo.old_from = m_cancelFromHdr;
04131   cancelInfo.from = nsnull;
04132 
04133   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
04134   NS_ENSURE_SUCCESS(rv,rv);
04135 
04136   nsCOMPtr<nsIPrompt> dialog;
04137   if (m_runningURL)
04138   {
04139     nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(m_runningURL));    
04140     rv = GetPromptDialogFromUrl(msgUrl, getter_AddRefs(dialog));
04141     NS_ENSURE_SUCCESS(rv, rv);
04142   }
04143 
04144   NS_ASSERTION (id && newsgroups, "null ptr");
04145   if (!id || !newsgroups) return -1; /* "unknown error"... */
04146 
04147   m_cancelNewsgroups = nsnull;
04148   m_cancelDistribution = nsnull;
04149   m_cancelFromHdr = nsnull;
04150   m_cancelID = nsnull;
04151   
04152   L = PL_strlen (id);
04153   
04154   subject = (char *) PR_Malloc (L + 20);
04155   body = (char *) PR_Malloc (PL_strlen (XP_AppCodeName) + 100);
04156   
04157   nsXPIDLString alertText;
04158   nsXPIDLString confirmText;
04159   
04160   PRInt32 confirmCancelResult = 0;
04161 
04162   // A little early to declare, but the goto causes problems
04163   nsCAutoString otherHeaders;
04164  
04165   /* Make sure that this loser isn't cancelling someone else's posting.
04166      Yes, there are occasionally good reasons to do so.  Those people
04167      capable of making that decision (news admins) have other tools with
04168      which to cancel postings (like telnet.)
04169  
04170      Don't do this if server tells us it will validate user. DMB 3/19/97
04171    */
04172   PRBool cancelchk=PR_FALSE;
04173   rv = m_nntpServer->QueryExtension("CANCELCHK",&cancelchk);
04174   if (NS_SUCCEEDED(rv) && !cancelchk) {
04175          NNTP_LOG_NOTE("CANCELCHK not supported");
04176       
04177       // get the current identity from the news session....
04178       nsCOMPtr<nsIMsgAccountManager> accountManager = 
04179                do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
04180       if (NS_SUCCEEDED(rv) && accountManager) {
04181           nsCOMPtr<nsISupportsArray> identities;
04182           rv = accountManager->GetAllIdentities(getter_AddRefs(identities));
04183           if (NS_FAILED(rv)) return -1;
04184 
04185           // CheckIfAuthor will set cancelInfo.from if a match is found
04186           identities->EnumerateForwards(CheckIfAuthor, (void *)&cancelInfo);
04187       }
04188   
04189       if (!cancelInfo.from) {
04190           GetNewsStringByName("cancelDisallowed", getter_Copies(alertText));
04191           rv = dialog->Alert(nsnull, alertText);
04192                 // XXX:  todo, check rv?
04193           
04194 /* After the cancel is disallowed, Make the status update to be the same as though the 
04195 cancel was allowed, otherwise, the newsgroup is not able to take further requests as
04196 reported here */
04197           status = MK_NNTP_CANCEL_DISALLOWED;
04198           m_nextState = NNTP_RESPONSE; 
04199           m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
04200           SetFlag(NNTP_PAUSE_FOR_READ);
04201           failure = PR_TRUE;
04202           goto FAIL;
04203       }
04204       else {
04205                 PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) CANCELCHK not supported, so post the cancel message as %s", this, cancelInfo.from));
04206       }
04207   }
04208   else {
04209          NNTP_LOG_NOTE("CANCELCHK supported, don't do the us vs. them test");
04210   }
04211 
04212   // QA needs to be able to disable this confirm dialog, for the automated tests.  see bug #31057
04213   rv = prefBranch->GetBoolPref(PREF_NEWS_CANCEL_CONFIRM, &requireConfirmationForCancel);
04214   if (NS_FAILED(rv) || requireConfirmationForCancel) {
04215     /* Last chance to cancel the cancel.*/
04216     GetNewsStringByName("cancelConfirm", getter_Copies(confirmText));
04217     rv = dialog->Confirm(nsnull, confirmText, &confirmCancelResult);
04218     // XXX:  todo, check rv?
04219   }
04220   else 
04221   {
04222     confirmCancelResult = 1;
04223   }
04224 
04225   if (confirmCancelResult != 1) {
04226       // they cancelled the cancel
04227       status = MK_NNTP_NOT_CANCELLED;
04228       failure = PR_TRUE;
04229       goto FAIL;
04230   }  
04231   
04232   if (!subject || !body) 
04233   {
04234     status = MK_OUT_OF_MEMORY;
04235     failure = PR_TRUE;
04236     goto FAIL;
04237   }
04238   
04239   PL_strcpy (subject, "cancel ");
04240   PL_strcat (subject, id);
04241 
04242   otherHeaders.AppendLiteral("Control: cancel ");
04243   otherHeaders += id;
04244   otherHeaders.AppendLiteral(CRLF);
04245   if (distribution) {
04246     otherHeaders.AppendLiteral("Distribution: ");
04247     otherHeaders += distribution;
04248     otherHeaders.AppendLiteral(CRLF);
04249   }
04250 
04251   PL_strcpy (body, "This message was cancelled from within ");
04252   PL_strcat (body, XP_AppCodeName);
04253   PL_strcat (body, "." CRLF);
04254   
04255   
04256   m_cancelStatus = 0;
04257 
04258   {
04259     /* NET_BlockingWrite() should go away soon? I think. */
04260     /* The following are what we really need to cancel a posted message */
04261     char *data;
04262     data = PR_smprintf("From: %s" CRLF
04263                        "Newsgroups: %s" CRLF
04264                        "Subject: %s" CRLF
04265                        "References: %s" CRLF
04266                        "%s" /* otherHeaders, already with CRLF */
04267                        CRLF /* body separator */
04268                        "%s" /* body, already with CRLF */
04269                        "." CRLF, /* trailing message terminator "." */
04270                        cancelInfo.from, newsgroups, subject, id,
04271                        otherHeaders.get(), body);
04272     
04273     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04274     if (mailnewsurl)
04275       status = SendData(mailnewsurl, data);
04276     PR_Free (data);
04277     if (status < 0) {
04278       nsCAutoString errorText;
04279       errorText.AppendInt(status);
04280       AlertError(MK_TCP_WRITE_ERROR, errorText.get());
04281       failure = PR_TRUE;
04282       goto FAIL;
04283     }
04284 
04285     SetFlag(NNTP_PAUSE_FOR_READ);
04286     m_nextState = NNTP_RESPONSE;
04287     m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
04288 
04289     // QA needs to be able to turn this alert off, for the automate tests.  see bug #31057
04290     rv = prefBranch->GetBoolPref(PREF_NEWS_CANCEL_ALERT_ON_SUCCESS, &showAlertAfterCancel);
04291     if (NS_FAILED(rv) || showAlertAfterCancel) {
04292       GetNewsStringByName("messageCancelled", getter_Copies(alertText));
04293       rv = dialog->Alert(nsnull, alertText);
04294       // XXX:  todo, check rv?
04295     }
04296 
04297     if (!m_runningURL) return -1;
04298 
04299     // delete the message from the db here.
04300     NS_ASSERTION(NS_SUCCEEDED(rv) && m_newsFolder && (m_key != nsMsgKey_None), "need more to remove this message from the db");
04301     if ((m_key != nsMsgKey_None) && (m_newsFolder)) 
04302        rv = m_newsFolder->RemoveMessage(m_key);
04303 
04304   }
04305    
04306 FAIL:
04307   NS_ASSERTION(m_newsFolder,"no news folder");
04308   if (m_newsFolder) 
04309     rv = ( failure ) ? m_newsFolder->CancelFailed() 
04310                      : m_newsFolder->CancelComplete();
04311 
04312   PR_Free (id);
04313   PR_Free (cancelInfo.old_from);
04314   PR_Free (cancelInfo.from);
04315   PR_Free (subject);
04316   PR_Free (newsgroups);
04317   PR_Free (distribution);
04318   PR_Free (body);
04319 
04320   return status;
04321 }
04322 
04323 PRInt32 nsNNTPProtocol::XPATSend()
04324 {
04325   int status = 0;
04326   char *thisTerm = NULL;
04327   
04328   if (m_searchData &&
04329     (thisTerm = PL_strchr(m_searchData, '/')) != NULL) {
04330     /* extract the XPAT encoding for one query term */
04331     /* char *next_search = NULL; */
04332     char *command = NULL;
04333     char *unescapedCommand = NULL;
04334     char *endOfTerm = NULL;
04335     NS_MsgSACopy (&command, ++thisTerm);
04336     endOfTerm = PL_strchr(command, '/');
04337     if (endOfTerm)
04338       *endOfTerm = '\0';
04339     NS_MsgSACat(&command, CRLF);
04340     
04341     unescapedCommand = MSG_UnEscapeSearchUrl(command);
04342     
04343     /* send one term off to the server */
04344     NNTP_LOG_WRITE(command);
04345     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04346     if (mailnewsurl)
04347       status = SendData(mailnewsurl, unescapedCommand);
04348     
04349     m_nextState = NNTP_RESPONSE;
04350     m_nextStateAfterResponse = NNTP_XPAT_RESPONSE;
04351     SetFlag(NNTP_PAUSE_FOR_READ);
04352     
04353     PR_Free(command);
04354     PR_Free(unescapedCommand);
04355   }
04356   else
04357   {
04358     m_nextState = NEWS_DONE;
04359     status = MK_DATA_LOADED;
04360   }
04361   return status;
04362 }
04363 
04364 PRInt32 nsNNTPProtocol::XPATResponse(nsIInputStream * inputStream, PRUint32 length)
04365 {
04366   PRUint32 status = 1; 
04367   
04368   if (m_responseCode != MK_NNTP_RESPONSE_XPAT_OK)
04369   {
04370     AlertError(MK_NNTP_ERROR_MESSAGE,m_responseText);
04371     m_nextState = NNTP_ERROR;
04372     ClearFlag(NNTP_PAUSE_FOR_READ);
04373     return MK_NNTP_SERVER_ERROR;
04374   }
04375   
04376   PRBool pauseForMoreData = PR_FALSE;
04377   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
04378   
04379   NNTP_LOG_READ(line);
04380   
04381   if(pauseForMoreData)
04382   {
04383     SetFlag(NNTP_PAUSE_FOR_READ);
04384     return 0;
04385   }
04386   
04387   if (line)
04388   {
04389     if (line[0] != '.')
04390     {
04391       long articleNumber;
04392       PR_sscanf(line, "%ld", &articleNumber);
04393       nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04394       if (mailnewsurl)
04395       {
04396         nsCOMPtr <nsIMsgSearchSession> searchSession;
04397         nsCOMPtr <nsIMsgSearchAdapter> searchAdapter;
04398         mailnewsurl->GetSearchSession(getter_AddRefs(searchSession));
04399         if (searchSession)
04400         {
04401           searchSession->GetRunningAdapter(getter_AddRefs(searchAdapter));
04402           if (searchAdapter)
04403             searchAdapter->AddHit((PRUint32) articleNumber);
04404         }
04405       }
04406     }
04407     else
04408     {
04409       /* set up the next term for next time around */
04410       char *nextTerm = PL_strchr(m_searchData, '/');
04411       
04412       if (nextTerm)
04413         m_searchData = ++nextTerm;
04414       else
04415         m_searchData = nsnull;
04416       
04417       m_nextState = NNTP_XPAT_SEND;
04418       ClearFlag(NNTP_PAUSE_FOR_READ);
04419       PR_FREEIF(line);
04420       return 0;
04421     }
04422   }
04423   PR_FREEIF(line);
04424   return 0;
04425 }
04426 
04427 PRInt32 nsNNTPProtocol::ListPrettyNames()
04428 {
04429   
04430   nsXPIDLCString group_name;
04431   char outputBuffer[OUTPUT_BUFFER_SIZE];
04432   PRInt32 status = 0; 
04433   
04434   nsresult rv = m_newsFolder->GetRawName(group_name);
04435   PR_snprintf(outputBuffer, 
04436     OUTPUT_BUFFER_SIZE, 
04437     "LIST PRETTYNAMES %.512s" CRLF,
04438     NS_SUCCEEDED(rv) ? (const char *) group_name : "");
04439   
04440   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04441   if (mailnewsurl)
04442     status = SendData(mailnewsurl, outputBuffer);
04443   NNTP_LOG_NOTE(outputBuffer);
04444   m_nextState = NNTP_RESPONSE;
04445   m_nextStateAfterResponse = NNTP_LIST_PRETTY_NAMES_RESPONSE;
04446   
04447   return status;
04448 }
04449 
04450 PRInt32 nsNNTPProtocol::ListPrettyNamesResponse(nsIInputStream * inputStream, PRUint32 length)
04451 {
04452   PRUint32 status = 0;
04453   
04454   if (m_responseCode != MK_NNTP_RESPONSE_LIST_OK)
04455   {
04456     m_nextState = DISPLAY_NEWSGROUPS;
04457     /*        m_nextState = NEWS_DONE; */
04458     ClearFlag(NNTP_PAUSE_FOR_READ);
04459     return 0;
04460   }
04461   
04462   PRBool pauseForMoreData = PR_FALSE;
04463   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
04464   
04465   NNTP_LOG_READ(line);
04466   
04467   if(pauseForMoreData)
04468   {
04469     SetFlag(NNTP_PAUSE_FOR_READ);
04470     return 0;
04471   }
04472   
04473   if (line)
04474   {
04475     if (line[0] != '.')
04476     {
04477 #if 0 // SetPrettyName is not yet implemented. No reason to bother
04478       int i;
04479       /* find whitespace separator if it exits */
04480       for (i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
04481         ;  /* null body */
04482       
04483       char *prettyName;
04484       if(line[i] == '\0')
04485         prettyName = &line[i];
04486       else
04487         prettyName = &line[i+1];
04488       
04489       line[i] = 0; /* terminate group name */
04490       if (i > 0) {
04491         nsCAutoString charset;   
04492         nsAutoString lineUtf16, prettyNameUtf16;
04493         if (NS_FAILED(m_nntpServer->GetCharset(charset) ||
04494             NS_FAILED(ConvertToUnicode(charset, line, lineUtf16)) ||
04495             NS_FAILED(ConvertToUnicode(charset, prettyName, prettyNameUtf16)))) {
04496           CopyUTF8toUTF16(line, lineUtf16);
04497           CopyUTF8toUTF16(prettyName, prettyNameUtf16);
04498         }
04499         m_nntpServer->SetPrettyNameForGroup(lineUtf16, prettyNameUtf16);
04500       
04501         PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) adding pretty name %s", this, 
04502                NS_ConvertUTF16toUTF8(prettyNameUtf16).get()));
04503       }
04504 #endif
04505     }
04506     else
04507     {
04508       m_nextState = DISPLAY_NEWSGROUPS;   /* this assumes we were doing a list */
04509       /*                    m_nextState = NEWS_DONE;     */ /* ### dmb - don't really know */
04510       ClearFlag(NNTP_PAUSE_FOR_READ);
04511       PR_FREEIF(line);
04512       return 0;
04513     }
04514   }
04515   PR_FREEIF(line);
04516   return 0;
04517 }
04518 
04519 PRInt32 nsNNTPProtocol::ListXActive()
04520 { 
04521   nsXPIDLCString group_name;
04522   nsresult rv;
04523   rv = m_newsFolder->GetRawName(group_name);
04524   if (NS_FAILED(rv)) return -1;
04525   
04526   PRInt32 status = 0;
04527   char outputBuffer[OUTPUT_BUFFER_SIZE];
04528   
04529   PR_snprintf(outputBuffer, 
04530     OUTPUT_BUFFER_SIZE, 
04531     "LIST XACTIVE %.512s" CRLF,
04532     (const char *) group_name);
04533   
04534   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04535   if (mailnewsurl)
04536     status = SendData(mailnewsurl, outputBuffer);
04537   
04538   m_nextState = NNTP_RESPONSE;
04539   m_nextStateAfterResponse = NNTP_LIST_XACTIVE_RESPONSE;
04540   
04541   return status;
04542 }
04543 
04544 PRInt32 nsNNTPProtocol::ListXActiveResponse(nsIInputStream * inputStream, PRUint32 length)
04545 {
04546   PRUint32 status = 0;
04547   nsresult rv;
04548   
04549   NS_ASSERTION(m_responseCode == MK_NNTP_RESPONSE_LIST_OK, "code != LIST_OK");
04550   if (m_responseCode != MK_NNTP_RESPONSE_LIST_OK)
04551   {
04552     m_nextState = DISPLAY_NEWSGROUPS;
04553     /*        m_nextState = NEWS_DONE; */
04554     ClearFlag(NNTP_PAUSE_FOR_READ);
04555     return MK_DATA_LOADED;
04556   }
04557   
04558   PRBool pauseForMoreData = PR_FALSE;
04559   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
04560   
04561   NNTP_LOG_READ(line);
04562   
04563   if(pauseForMoreData)
04564   {
04565     SetFlag(NNTP_PAUSE_FOR_READ);
04566     return 0;
04567   }
04568   
04569         /* almost correct */
04570   if(status > 1)
04571   {
04572 #ifdef UNREADY_CODE
04573     ce->bytes_received += status;
04574     FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->content_length);
04575 #else
04576     mBytesReceived += status;
04577     mBytesReceivedSinceLastStatusUpdate += status;
04578 #endif
04579   }
04580   
04581   if (line)
04582   {
04583     if (line[0] != '.')
04584     {
04585       char *s = line;
04586       /* format is "rec.arts.movies.past-films 7302 7119 csp"
04587       */
04588       while (*s && !NET_IS_SPACE(*s))
04589         s++;
04590       if (*s)
04591       {
04592         char flags[32];     /* ought to be big enough */
04593         *s = 0;
04594         PR_sscanf(s + 1,
04595           "%d %d %31s", 
04596           &m_firstPossibleArticle, 
04597           &m_lastPossibleArticle,
04598           flags);
04599         
04600         
04601         NS_ASSERTION(m_nntpServer, "no nntp incoming server");
04602         if (m_nntpServer) {
04603           rv = m_nntpServer->AddNewsgroupToList(line);
04604           NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
04605         }
04606         
04607         /* we're either going to list prettynames first, or list
04608         all prettynames every time, so we won't care so much
04609         if it gets interrupted. */
04610         PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) got xactive for %s of %s", this, line, flags));
04611         /*    This isn't required, because the extra info is
04612         initialized to false for new groups. And it's
04613         an expensive call.
04614         */
04615         /* MSG_SetGroupNeedsExtraInfo(cd->host, line, PR_FALSE); */
04616       }
04617     }
04618     else
04619     {
04620       PRBool xactive=PR_FALSE;
04621       rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
04622       if (m_typeWanted == NEW_GROUPS &&
04623         NS_SUCCEEDED(rv) && xactive)
04624       {
04625         nsCOMPtr <nsIMsgNewsFolder> old_newsFolder;
04626         old_newsFolder = m_newsFolder;
04627         nsXPIDLCString groupName;
04628         
04629         rv = m_nntpServer->GetFirstGroupNeedingExtraInfo(groupName);
04630         if (NS_FAILED(rv)) return -1;
04631         rv = m_nntpServer->FindGroup(groupName,
04632                                      getter_AddRefs(m_newsFolder));
04633         if (NS_FAILED(rv)) return -1;
04634         
04635         // see if we got a different group
04636         if (old_newsFolder && m_newsFolder &&
04637           (old_newsFolder.get() != m_newsFolder.get()))
04638           /* make sure we're not stuck on the same group */
04639         {
04640           PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) listing xactive for %s", this, (const char *)groupName));
04641           m_nextState = NNTP_LIST_XACTIVE;
04642           ClearFlag(NNTP_PAUSE_FOR_READ); 
04643           PR_FREEIF(line);
04644           return 0;
04645         }
04646         else
04647         {
04648           m_newsFolder = nsnull;
04649         }
04650       }
04651       PRBool listpname;
04652       rv = m_nntpServer->QueryExtension("LISTPNAME",&listpname);
04653       if (NS_SUCCEEDED(rv) && listpname)
04654         m_nextState = NNTP_LIST_PRETTY_NAMES;
04655       else
04656         m_nextState = DISPLAY_NEWSGROUPS; /* this assumes we were doing a list - who knows? */
04657       /*                    m_nextState = NEWS_DONE;     */ /* ### dmb - don't really know */
04658       ClearFlag(NNTP_PAUSE_FOR_READ);
04659       PR_FREEIF(line);
04660       return 0;
04661     }
04662   }
04663   PR_FREEIF(line);
04664   return 0;
04665 }
04666 
04667 PRInt32 nsNNTPProtocol::SendListGroup()
04668 {
04669   nsresult rv;
04670   char outputBuffer[OUTPUT_BUFFER_SIZE];
04671   PRInt32 status = 0; 
04672   
04673   NS_ASSERTION(m_newsFolder,"no newsFolder");
04674   if (!m_newsFolder) return -1;
04675   nsXPIDLCString newsgroupName;
04676   
04677   rv = m_newsFolder->GetRawName(newsgroupName);
04678   NS_ENSURE_SUCCESS(rv,rv);
04679   
04680   PR_snprintf(outputBuffer, 
04681     OUTPUT_BUFFER_SIZE, 
04682     "listgroup %.512s" CRLF,
04683     newsgroupName.get());
04684   
04685   m_articleList = do_CreateInstance(NS_NNTPARTICLELIST_CONTRACTID, &rv);
04686   NS_ENSURE_SUCCESS(rv,rv);
04687   
04688   rv = m_articleList->Initialize(m_newsFolder);
04689   NS_ENSURE_SUCCESS(rv,rv);
04690   
04691   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04692   if (mailnewsurl)
04693     status = SendData(mailnewsurl, outputBuffer); 
04694   
04695   m_nextState = NNTP_RESPONSE;
04696   m_nextStateAfterResponse = NNTP_LIST_GROUP_RESPONSE;
04697   SetFlag(NNTP_PAUSE_FOR_READ);
04698   
04699   return status;
04700 }
04701 
04702 PRInt32 nsNNTPProtocol::SendListGroupResponse(nsIInputStream * inputStream, PRUint32 length)
04703 {
04704   PRUint32 status = 0;
04705   
04706   NS_ASSERTION(m_responseCode == MK_NNTP_RESPONSE_GROUP_SELECTED, "code != GROUP_SELECTED");
04707   if (m_responseCode != MK_NNTP_RESPONSE_GROUP_SELECTED)
04708   {
04709     m_nextState = NEWS_DONE; 
04710     ClearFlag(NNTP_PAUSE_FOR_READ);
04711     return MK_DATA_LOADED;
04712   }
04713   
04714   PRBool pauseForMoreData = PR_FALSE;
04715   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
04716   
04717   if(pauseForMoreData)
04718   {
04719     SetFlag(NNTP_PAUSE_FOR_READ);
04720     return 0;
04721   }
04722   
04723   if (line)
04724   {
04725     nsresult rv;
04726     if (line[0] != '.')
04727     {
04728       nsMsgKey found_id = nsMsgKey_None;
04729       PR_sscanf(line, "%ld", &found_id);
04730       rv = m_articleList->AddArticleKey(found_id);
04731       NS_ASSERTION(NS_SUCCEEDED(rv), "add article key failed");
04732     }
04733     else
04734     {
04735       rv = m_articleList->FinishAddingArticleKeys();
04736       NS_ASSERTION(NS_SUCCEEDED(rv), "finish adding article key failed");
04737       m_articleList = nsnull;
04738       m_nextState = NEWS_DONE;      /* ### dmb - don't really know */
04739       ClearFlag(NNTP_PAUSE_FOR_READ); 
04740       PR_FREEIF(line);
04741       return 0;
04742     }
04743   }
04744   PR_FREEIF(line);
04745   return 0;
04746 }
04747 
04748 
04749 PRInt32 nsNNTPProtocol::Search()
04750 {
04751   NS_ASSERTION(0,"Search not implemented");
04752   return 0;
04753 }
04754 
04755 PRInt32 nsNNTPProtocol::SearchResponse()
04756 {
04757   if (MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK)
04758     m_nextState = NNTP_SEARCH_RESULTS;
04759   else
04760     m_nextState = NEWS_DONE;
04761   ClearFlag(NNTP_PAUSE_FOR_READ);
04762   return 0;
04763 }
04764 
04765 PRInt32 nsNNTPProtocol::SearchResults(nsIInputStream *inputStream, PRUint32 length)
04766 {
04767   PRUint32 status = 1;
04768   
04769   PRBool pauseForMoreData = PR_FALSE;
04770   char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
04771   
04772   if(pauseForMoreData)
04773   {
04774     SetFlag(NNTP_PAUSE_FOR_READ);
04775     return 0;
04776   }
04777   if (!line)
04778     return status;  /* no line yet */
04779   
04780   if ('.' != line[0])
04781   {
04782 #ifdef UNREADY_CODE
04783     MSG_AddNewsSearchHit (ce->window_id, line);
04784 #endif
04785   }
04786   else
04787   {
04788     /* all overview lines received */
04789     m_nextState = NEWS_DONE;
04790     ClearFlag(NNTP_PAUSE_FOR_READ);
04791   }
04792   PR_FREEIF(line);
04793   return status;
04794 }
04795 
04796 /* Sets state for the transfer. This used to be known as net_setup_news_stream */
04797 PRInt32 nsNNTPProtocol::SetupForTransfer()
04798 {   
04799   if (m_typeWanted == NEWS_POST)
04800   {
04801     m_nextState = NNTP_SEND_POST_DATA;
04802 #ifdef UNREADY_CODE
04803     NET_Progress(ce->window_id, XP_GetString(MK_MSG_DELIV_NEWS));
04804 #endif
04805   }
04806   else if(m_typeWanted == LIST_WANTED)
04807   {
04808     if (TestFlag(NNTP_USE_FANCY_NEWSGROUP))
04809       m_nextState = NNTP_LIST_XACTIVE_RESPONSE;
04810     else
04811       m_nextState = NNTP_READ_LIST_BEGIN;
04812   }
04813   else if(m_typeWanted == GROUP_WANTED)
04814     m_nextState = NNTP_XOVER_BEGIN;
04815   else if(m_typeWanted == NEW_GROUPS)
04816     m_nextState = NNTP_NEWGROUPS_BEGIN;
04817   else if(m_typeWanted == ARTICLE_WANTED ||
04818     m_typeWanted== CANCEL_WANTED)
04819     m_nextState = NNTP_BEGIN_ARTICLE;
04820   else if (m_typeWanted== SEARCH_WANTED)
04821     m_nextState = NNTP_XPAT_SEND;
04822   else if (m_typeWanted == PRETTY_NAMES_WANTED)
04823     m_nextState = NNTP_LIST_PRETTY_NAMES;
04824 #ifdef UNREADY_CODE
04825   else if (m_typeWanted == PROFILE_WANTED)
04826   {
04827     if (PL_strstr(ce->URL_s->address, "PROFILE NEW"))
04828       m_nextState = NNTP_PROFILE_ADD;
04829     else
04830       m_nextState = NNTP_PROFILE_DELETE;
04831   }
04832 #endif
04833   else
04834   {
04835     NS_ASSERTION(0, "unexpected");
04836     return -1;
04837   }
04838   
04839   return(0); /* good */
04840 }
04841 
04843 // The following method is used for processing the news state machine. 
04844 // It returns a negative number (mscott: we'll change this to be an enumerated type which we'll coordinate
04845 // with the netlib folks?) when we are done processing.
04847 nsresult nsNNTPProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, 
04848                                               PRUint32 sourceOffset, PRUint32 length)
04849 {
04850   PRInt32 status = 0; 
04851   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
04852   if (!mailnewsurl)
04853     return NS_OK; // probably no data available - it's OK.
04854   
04855   
04856   ClearFlag(NNTP_PAUSE_FOR_READ); 
04857   
04858   while(!TestFlag(NNTP_PAUSE_FOR_READ))
04859   {
04860     PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) Next state: %s",this, stateLabels[m_nextState]));
04861     // examine our current state and call an appropriate handler for that state.....
04862     switch(m_nextState)
04863     {
04864     case NNTP_RESPONSE:
04865       if (inputStream == nsnull)
04866         SetFlag(NNTP_PAUSE_FOR_READ);
04867       else
04868         status = NewsResponse(inputStream, length);
04869       break;
04870       
04871       // mscott: I've removed the states involving connections on the assumption
04872       // that core netlib will now be managing that information.
04873       
04874     case NNTP_LOGIN_RESPONSE:
04875       if (inputStream == nsnull)
04876         SetFlag(NNTP_PAUSE_FOR_READ);
04877       else
04878         status = LoginResponse();
04879       break;
04880       
04881     case NNTP_SEND_MODE_READER:
04882       status = SendModeReader(); 
04883       break;
04884       
04885     case NNTP_SEND_MODE_READER_RESPONSE:
04886       if (inputStream == nsnull)
04887         SetFlag(NNTP_PAUSE_FOR_READ);
04888       else
04889         status = SendModeReaderResponse(); 
04890       break;
04891       
04892     case SEND_LIST_EXTENSIONS:
04893       status = SendListExtensions(); 
04894       break;
04895     case SEND_LIST_EXTENSIONS_RESPONSE:
04896       if (inputStream == nsnull)
04897         SetFlag(NNTP_PAUSE_FOR_READ);
04898       else
04899         status = SendListExtensionsResponse(inputStream, length);
04900       break;
04901     case SEND_LIST_SEARCHES:
04902       status = SendListSearches(); 
04903       break;
04904     case SEND_LIST_SEARCHES_RESPONSE:
04905       if (inputStream == nsnull) 
04906         SetFlag(NNTP_PAUSE_FOR_READ);
04907       else
04908         status = SendListSearchesResponse(inputStream, length); 
04909       break;
04910     case NNTP_LIST_SEARCH_HEADERS:
04911       status = SendListSearchHeaders();
04912       break;
04913     case NNTP_LIST_SEARCH_HEADERS_RESPONSE:
04914       if (inputStream == nsnull)
04915         SetFlag(NNTP_PAUSE_FOR_READ);
04916       else
04917         status = SendListSearchHeadersResponse(inputStream, length); 
04918       break;
04919     case NNTP_GET_PROPERTIES:
04920       status = GetProperties();
04921       break;
04922     case NNTP_GET_PROPERTIES_RESPONSE:
04923       if (inputStream == nsnull)
04924         SetFlag(NNTP_PAUSE_FOR_READ);
04925       else
04926         status = GetPropertiesResponse(inputStream, length);
04927       break;                       
04928     case SEND_LIST_SUBSCRIPTIONS:
04929       status = SendListSubscriptions();
04930       break;
04931     case SEND_LIST_SUBSCRIPTIONS_RESPONSE:
04932       if (inputStream == nsnull)
04933         SetFlag(NNTP_PAUSE_FOR_READ);
04934       else
04935         status = SendListSubscriptionsResponse(inputStream, length);
04936       break;
04937       
04938     case SEND_FIRST_NNTP_COMMAND:
04939       status = SendFirstNNTPCommand(url);
04940       break;
04941     case SEND_FIRST_NNTP_COMMAND_RESPONSE:
04942       if (inputStream == nsnull)
04943         SetFlag(NNTP_PAUSE_FOR_READ);
04944       else
04945         status = SendFirstNNTPCommandResponse();
04946       break;
04947       
04948     case NNTP_SEND_GROUP_FOR_ARTICLE:
04949       status = SendGroupForArticle();
04950       break;
04951     case NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE:
04952       if (inputStream == nsnull)
04953         SetFlag(NNTP_PAUSE_FOR_READ);
04954       else
04955         status = SendGroupForArticleResponse();
04956       break;
04957     case NNTP_SEND_ARTICLE_NUMBER:
04958       status = SendArticleNumber();
04959       break;
04960       
04961     case SETUP_NEWS_STREAM:
04962       status = SetupForTransfer();
04963       break;
04964       
04965     case NNTP_BEGIN_AUTHORIZE:
04966       status = BeginAuthorization(); 
04967       break;
04968       
04969     case NNTP_AUTHORIZE_RESPONSE:
04970       if (inputStream == nsnull)
04971         SetFlag(NNTP_PAUSE_FOR_READ);
04972       else
04973         status = AuthorizationResponse(); 
04974       break;
04975       
04976     case NNTP_PASSWORD_RESPONSE:
04977       if (inputStream == nsnull)
04978         SetFlag(NNTP_PAUSE_FOR_READ);
04979       else
04980         status = PasswordResponse();
04981       break;
04982       
04983       // read list
04984     case NNTP_READ_LIST_BEGIN:
04985       status = BeginReadNewsList(); 
04986       break;
04987     case NNTP_READ_LIST:
04988       status = ReadNewsList(inputStream, length);
04989       break;
04990       
04991       // news group
04992     case DISPLAY_NEWSGROUPS:
04993       status = DisplayNewsgroups(); 
04994       break;
04995     case NNTP_NEWGROUPS_BEGIN:
04996       status = BeginNewsgroups();
04997       break;
04998     case NNTP_NEWGROUPS:
04999       status = ProcessNewsgroups(inputStream, length);
05000       break;
05001       
05002       // article specific
05003     case NNTP_BEGIN_ARTICLE:
05004       status = BeginArticle(); 
05005       break;
05006       
05007     case NNTP_READ_ARTICLE:
05008       status = ReadArticle(inputStream, length);
05009       break;
05010       
05011     case NNTP_XOVER_BEGIN:
05012       status = BeginReadXover();
05013       break;
05014       
05015     case NNTP_FIGURE_NEXT_CHUNK:
05016       status = FigureNextChunk(); 
05017       break;
05018       
05019     case NNTP_XOVER_SEND:
05020       status = XoverSend();
05021       break;
05022       
05023     case NNTP_XOVER:
05024       status = ReadXover(inputStream, length);
05025       break;
05026       
05027     case NNTP_XOVER_RESPONSE:
05028       if (inputStream == nsnull)
05029         SetFlag(NNTP_PAUSE_FOR_READ);
05030       else
05031         status = ReadXoverResponse();
05032       break;
05033       
05034     case NEWS_PROCESS_XOVER:
05035     case NEWS_PROCESS_BODIES:
05036 #ifdef UNREADY_CODE
05037       NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_SORT_ARTICLES));
05038 #endif
05039       status = ProcessXover();
05040       break;
05041       
05042     case NNTP_READ_GROUP:
05043       status = ReadNewsgroup();
05044       break;
05045       
05046     case NNTP_READ_GROUP_RESPONSE:
05047       if (inputStream == nsnull)
05048         SetFlag(NNTP_PAUSE_FOR_READ);
05049       else
05050         status = ReadNewsgroupResponse();
05051       break;
05052       
05053     case NNTP_READ_GROUP_BODY:
05054       status = ReadNewsgroupResponse();
05055       break;
05056       
05057     case NNTP_SEND_POST_DATA:
05058       status = PostData();
05059       break;
05060     case NNTP_SEND_POST_DATA_RESPONSE:
05061       if (inputStream == nsnull)
05062         SetFlag(NNTP_PAUSE_FOR_READ);
05063       else
05064         status = PostDataResponse();
05065       break;
05066       
05067     case NNTP_CHECK_FOR_MESSAGE:
05068       status = CheckForArticle();
05069       break;
05070       
05071     case NEWS_NEWS_RC_POST:
05072 #ifdef UNREADY_CODE
05073       status = net_NewsRCProcessPost(ce);
05074 #endif
05075       break;
05076       
05077     case NEWS_DISPLAY_NEWS_RC:
05078       status = DisplayNewsRC();
05079       break;
05080     case NEWS_DISPLAY_NEWS_RC_RESPONSE:
05081       if (inputStream == nsnull)
05082         SetFlag(NNTP_PAUSE_FOR_READ);
05083       else
05084         status = DisplayNewsRCResponse();
05085       break;
05086       
05087       // cancel
05088     case NEWS_START_CANCEL:
05089       status = StartCancel();
05090       break;
05091       
05092     case NEWS_DO_CANCEL:
05093       status = DoCancel();
05094       break;
05095       
05096       // XPAT
05097     case NNTP_XPAT_SEND:
05098       status = XPATSend();
05099       break;
05100     case NNTP_XPAT_RESPONSE:
05101       if (inputStream == nsnull)
05102         SetFlag(NNTP_PAUSE_FOR_READ);
05103       else
05104         status = XPATResponse(inputStream, length);
05105       break;
05106       
05107       // search
05108     case NNTP_SEARCH:
05109       status = Search();
05110       break;
05111     case NNTP_SEARCH_RESPONSE:
05112       if (inputStream == nsnull)
05113         SetFlag(NNTP_PAUSE_FOR_READ);
05114       else
05115         status = SearchResponse();
05116       break;
05117     case NNTP_SEARCH_RESULTS:
05118       status = SearchResults(inputStream, length);
05119       break;
05120       
05121       
05122     case NNTP_LIST_PRETTY_NAMES:
05123       status = ListPrettyNames();
05124       break;
05125     case NNTP_LIST_PRETTY_NAMES_RESPONSE:
05126       if (inputStream == nsnull)
05127         SetFlag(NNTP_PAUSE_FOR_READ);
05128       else
05129         status = ListPrettyNamesResponse(inputStream, length);
05130       break;
05131     case NNTP_LIST_XACTIVE:
05132       status = ListXActive();
05133       break;
05134     case NNTP_LIST_XACTIVE_RESPONSE:
05135       if (inputStream == nsnull)
05136         SetFlag(NNTP_PAUSE_FOR_READ);
05137       else
05138         status = ListXActiveResponse(inputStream, length);
05139       break;
05140     case NNTP_LIST_GROUP:
05141       status = SendListGroup();
05142       break;
05143     case NNTP_LIST_GROUP_RESPONSE:
05144       if (inputStream == nsnull)
05145         SetFlag(NNTP_PAUSE_FOR_READ);
05146       else
05147         status = SendListGroupResponse(inputStream, length);
05148       break;
05149     case NEWS_DONE:
05150       m_nextState = NEWS_FREE;
05151       break;
05152     case NEWS_POST_DONE:
05153       NNTP_LOG_NOTE("NEWS_POST_DONE");
05154       mailnewsurl->SetUrlState(PR_FALSE, NS_OK);
05155       m_nextState = NEWS_FREE;
05156       break;
05157     case NEWS_ERROR:
05158       NNTP_LOG_NOTE("NEWS_ERROR"); 
05159       if (m_responseCode == MK_NNTP_RESPONSE_ARTICLE_NOTFOUND || m_responseCode == MK_NNTP_RESPONSE_ARTICLE_NONEXIST)
05160         mailnewsurl->SetUrlState(PR_FALSE, NS_MSG_NEWS_ARTICLE_NOT_FOUND);
05161       else
05162         mailnewsurl->SetUrlState(PR_FALSE, NS_ERROR_FAILURE);
05163       m_nextState = NEWS_FREE;
05164       break;
05165     case NNTP_ERROR:
05166       // XXX do we really want to remove the connection from
05167       // the cache on error?
05168       /* check if this connection came from the cache or if it was
05169       * a new connection.  If it was not new lets start it over
05170       * again.  But only if we didn't have any successful protocol
05171       * dialog at all.
05172       */
05173       FinishMemCacheEntry(PR_FALSE);  // cleanup mem cache entry
05174       if (m_responseCode != MK_NNTP_RESPONSE_ARTICLE_NOTFOUND && m_responseCode != MK_NNTP_RESPONSE_ARTICLE_NONEXIST)
05175         return CloseConnection();
05176     case NEWS_FREE:
05177       m_lastActiveTimeStamp = PR_Now(); // remmeber when we last used this connection.
05178       return CleanupAfterRunningUrl();
05179     case NEWS_FINISHED:
05180       return NS_OK;
05181       break;
05182     default:
05183       /* big error */
05184       return NS_ERROR_FAILURE;
05185       
05186     } // end switch
05187                 
05188     if(status < 0 && m_nextState != NEWS_ERROR &&
05189       m_nextState != NNTP_ERROR && m_nextState != NEWS_FREE)
05190     {
05191       m_nextState = NNTP_ERROR;
05192       ClearFlag(NNTP_PAUSE_FOR_READ);
05193     }
05194                 
05195   } /* end big while */
05196         
05197   return NS_OK; /* keep going */
05198 }
05199 
05200 NS_IMETHODIMP nsNNTPProtocol::CloseConnection()
05201 {
05202   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ClosingConnection",this));
05203   SendData(nsnull, NNTP_CMD_QUIT); // this will cause OnStopRequest get called, which will call CloseSocket()
05204   // break some cycles
05205   CleanupNewsgroupList();
05206 
05207   if (m_nntpServer) {
05208     m_nntpServer->RemoveConnection(this);
05209     m_nntpServer = nsnull;
05210   }
05211   CloseSocket();
05212   m_newsFolder = nsnull;
05213   
05214   if (m_articleList) {
05215     m_articleList->FinishAddingArticleKeys();
05216     m_articleList = nsnull;
05217   }
05218   
05219   m_key = nsMsgKey_None;
05220   return NS_OK;
05221 }
05222 
05223 nsresult nsNNTPProtocol::CleanupNewsgroupList()
05224 {
05225     nsresult rv;
05226     if (!m_newsgroupList) return NS_OK;
05227        PRInt32 status = 0;
05228     rv = m_newsgroupList->FinishXOVERLINE(0,&status);
05229     m_newsgroupList = nsnull;
05230     NS_ASSERTION(NS_SUCCEEDED(rv), "FinishXOVERLINE failed");
05231     return rv;
05232 }
05233 
05234 nsresult nsNNTPProtocol::CleanupAfterRunningUrl()
05235 {
05236   /* do we need to know if we're parsing xover to call finish xover?  */
05237   /* yes, I think we do! Why did I think we should??? */
05238   /* If we've gotten to NEWS_FREE and there is still XOVER
05239   data, there was an error or we were interrupted or
05240   something.  So, tell libmsg there was an abnormal
05241   exit so that it can free its data. */
05242   
05243   nsresult rv = NS_OK;
05244   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) CleanupAfterRunningUrl()", this));
05245   
05246   // send StopRequest notification after we've cleaned up the protocol
05247   // because it can synchronously causes a new url to get run in the
05248   // protocol - truly evil, but we're stuck at the moment.
05249   if (m_channelListener)
05250     rv = m_channelListener->OnStopRequest(this, m_channelContext, NS_OK);
05251   
05252   if (m_loadGroup)
05253     m_loadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull, NS_OK);
05254   CleanupNewsgroupList();
05255   
05256   // clear out mem cache entry so we're not holding onto it.
05257   if (m_runningURL)
05258   {
05259     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
05260     if (mailnewsurl)
05261     {
05262       mailnewsurl->SetUrlState(PR_FALSE, NS_OK);
05263       mailnewsurl->SetMemCacheEntry(nsnull);
05264     }
05265   }
05266   
05267   Cleanup();
05268   
05269   mDisplayInputStream = nsnull;
05270   mDisplayOutputStream = nsnull;
05271   mProgressEventSink = nsnull;
05272   SetOwner(nsnull);
05273   
05274   m_channelContext = nsnull;
05275   m_channelListener = nsnull;
05276   m_loadGroup = nsnull;
05277   mCallbacks = nsnull;
05278 
05279   // disable timeout before caching.
05280   nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport);
05281   if (strans)
05282     strans->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, PR_UINT32_MAX);  
05283 
05284   // don't mark ourselves as not busy until we are done cleaning up the connection. it should be the
05285   // last thing we do.
05286   SetIsBusy(PR_FALSE);
05287   
05288   return NS_OK;
05289 }
05290 
05291 nsresult nsNNTPProtocol::CloseSocket()
05292 {
05293   PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ClosingSocket()",this));
05294   
05295   if (m_nntpServer) {
05296     m_nntpServer->RemoveConnection(this);
05297     m_nntpServer = nsnull;
05298   }
05299   
05300   CleanupAfterRunningUrl(); // is this needed?
05301   return nsMsgProtocol::CloseSocket();
05302 }
05303 
05304 void nsNNTPProtocol::SetProgressBarPercent(PRUint32 aProgress, PRUint32 aProgressMax)
05305 {
05306   // XXX 64-bit
05307   if (mProgressEventSink)
05308     mProgressEventSink->OnProgress(this, m_channelContext, nsUint64(aProgress),
05309                                    nsUint64(aProgressMax));                                       
05310 }
05311 
05312 nsresult
05313 nsNNTPProtocol::SetProgressStatus(const PRUnichar *aMessage)
05314 {
05315   nsresult rv = NS_OK;
05316   if (mProgressEventSink) 
05317     rv = mProgressEventSink->OnStatus(this, m_channelContext, NS_OK, aMessage);
05318   return rv;
05319 }
05320 
05321 NS_IMETHODIMP nsNNTPProtocol::GetContentType(nsACString &aContentType)
05322 {      
05323 
05324   // if we've been set with a content type, then return it....
05325   // this happens when we go through libmime now as it sets our new content type
05326   if (!m_ContentType.IsEmpty())
05327   {
05328     aContentType = m_ContentType;
05329     return NS_OK;
05330   }
05331 
05332   // otherwise do what we did before...  
05333 
05334   if (m_typeWanted == GROUP_WANTED)  
05335     aContentType.AssignLiteral("x-application-newsgroup");
05336   else if (m_typeWanted == IDS_WANTED)
05337     aContentType.AssignLiteral("x-application-newsgroup-listids");
05338   else 
05339     aContentType.AssignLiteral("message/rfc822");
05340   return NS_OK;
05341 }
05342 
05343 nsresult
05344 nsNNTPProtocol::AlertError(PRInt32 errorCode, const char *text)
05345 {
05346   nsresult rv = NS_OK;
05347 
05348   // get the prompt from the running url....
05349   if (m_runningURL) {
05350     nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(m_runningURL));
05351     nsCOMPtr<nsIPrompt> dialog;
05352     rv = GetPromptDialogFromUrl(msgUrl, getter_AddRefs(dialog));
05353     NS_ENSURE_SUCCESS(rv, rv);
05354 
05355     nsAutoString alertText;
05356     nsXPIDLString str;
05357     rv = GetNewsStringByID(MK_NNTP_ERROR_MESSAGE, getter_Copies(str));
05358     NS_ENSURE_SUCCESS(rv,rv);
05359        alertText.Append(str);
05360 
05361        if (text) {
05362            alertText.Append(' ');
05363               alertText.AppendWithConversion(text);
05364     }
05365 
05366        rv = dialog->Alert(nsnull, alertText.get());
05367     NS_ENSURE_SUCCESS(rv, rv);
05368   }
05369   
05370   return rv;
05371 }
05372 
05373 NS_IMETHODIMP nsNNTPProtocol::GetCurrentFolder(nsIMsgFolder **aFolder)
05374 {
05375   nsresult rv = NS_ERROR_NULL_POINTER;
05376   NS_ENSURE_ARG_POINTER(aFolder);
05377   if (m_newsFolder)
05378     rv = m_newsFolder->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) aFolder);
05379   return rv;
05380 }
05381 
05382 void nsNNTPProtocol::GotAuthorizationRequest()
05383 {
05384   if ( m_nextStateAfterResponse == NEWS_DISPLAY_NEWS_RC_RESPONSE )
05385   {
05386     // the authorization request disrupted our NEWSRC reading.
05387     // => restart it, to not lose groups
05388     // (see http://bugzilla.mozilla.org/show_bug.cgi?id=111855)
05389     ClearFlag( NNTP_NEWSRC_PERFORMED );
05390 
05391     // with setting m_newsRCListIndex to 0, we could simply restart
05392     // the whole process, starting over with the very first group.
05393     // However, this may lead to problems in some rare scenarios:
05394     // Consider, for instance, an account which has n newsgroups which
05395     // all require different authentication, and a user which does not
05396     // allow the password manager to remember the user/pwd settings. This
05397     // would lead to constantly annoying this user with the authorization
05398     // request for the first group.
05399     // To prevent this (well, and to save some network traffic :), we go
05400     // back one step only
05401     NS_ASSERTION( m_newsRCListIndex > 0, "next state == NEWS_DISPLAY_NEWS_RC_RESPONSE, but no single group handled, yet?" );
05402     if ( m_newsRCListIndex > 0 )
05403     {   // yes, I'm paranoid :)
05404         m_RCIndexToResumeAfterAuthRequest = --m_newsRCListIndex;
05405     }
05406   }
05407 }