5408 lines
153 KiB
C++
5408 lines
153 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Scott MacGregor <mscott@netscape.com>
|
|
* Seth Spitzer <sspitzer@netscape.com>
|
|
* Alec Flett <alecf@netscape.com>
|
|
* David Bienvenu <bienvenu@nventure.com>
|
|
* Jeff Tsai <jefft@netscape.com>
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
* Håkan Waara <hwaara@chello.se>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifdef MOZ_LOGGING
|
|
#define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */
|
|
#endif
|
|
|
|
#include "msgCore.h" // precompiled header...
|
|
#include "MailNewsTypes.h"
|
|
#include "nntpCore.h"
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsIMsgHdr.h"
|
|
#include "nsNNTPProtocol.h"
|
|
#include "nsINNTPArticleList.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsFileStream.h"
|
|
#include "nsIMemory.h"
|
|
#include "nsIPipe.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsMsgI18N.h"
|
|
|
|
#include "nsMsgBaseCID.h"
|
|
#include "nsMsgNewsCID.h"
|
|
|
|
#include "nsINntpUrl.h"
|
|
|
|
#include "nsCRT.h"
|
|
|
|
#include "prtime.h"
|
|
#include "prlog.h"
|
|
#include "prerror.h"
|
|
#include "nsEscape.h"
|
|
#include "nsString.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
/* include event sink interfaces for news */
|
|
|
|
#include "nsIMsgHeaderParser.h"
|
|
#include "nsIMsgSearchSession.h"
|
|
#include "nsIMsgSearchAdapter.h"
|
|
#include "nsIMsgStatusFeedback.h"
|
|
|
|
#include "nsMsgKeySet.h"
|
|
|
|
#include "nsNewsUtils.h"
|
|
#include "nsMsgUtils.h"
|
|
|
|
#include "nsIMsgMailSession.h"
|
|
#include "nsIMsgIdentity.h"
|
|
#include "nsIMsgAccountManager.h"
|
|
|
|
#include "nsIPrompt.h"
|
|
#include "nsIMsgStatusFeedback.h"
|
|
|
|
#include "nsIMsgFolder.h"
|
|
#include "nsIMsgNewsFolder.h"
|
|
#include "nsIDocShell.h"
|
|
|
|
// for the memory cache...
|
|
#include "nsICacheEntryDescriptor.h"
|
|
#include "nsICacheSession.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsNetCID.h"
|
|
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIPrefService.h"
|
|
|
|
#include "nsIMsgWindow.h"
|
|
#include "nsIWindowWatcher.h"
|
|
|
|
#include "nsINntpService.h"
|
|
#include "nntpCore.h"
|
|
#include "nsIStreamConverterService.h"
|
|
#include "nsIStreamListenerTee.h"
|
|
#include "nsISocketTransport.h"
|
|
|
|
#include <time.h>
|
|
|
|
#undef GetPort // XXX Windows!
|
|
#undef SetPort // XXX Windows!
|
|
|
|
#define PREF_NEWS_CANCEL_CONFIRM "news.cancel.confirm"
|
|
#define PREF_NEWS_CANCEL_ALERT_ON_SUCCESS "news.cancel.alert_on_success"
|
|
#define READ_NEWS_LIST_COUNT_MAX 500 /* number of groups to process at a time when reading the list from the server */
|
|
#define READ_NEWS_LIST_TIMEOUT 50 /* uSec to wait until doing more */
|
|
#define RATE_STR_BUF_LEN 32
|
|
#define UPDATE_THRESHHOLD 25600 /* only update every 25 KB */
|
|
|
|
// NNTP extensions are supported yet
|
|
// until the extension code is ported,
|
|
// we'll skip right to the first nntp command
|
|
// after doing "mode reader"
|
|
// and "pushed" authentication (if necessary),
|
|
//#define HAVE_NNTP_EXTENSIONS
|
|
|
|
static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
|
|
|
|
typedef struct _cancelInfoEntry {
|
|
char *from;
|
|
char *old_from;
|
|
} cancelInfoEntry;
|
|
|
|
// quiet compiler warnings by defining these function prototypes
|
|
char *MSG_UnEscapeSearchUrl (const char *commandSpecificData);
|
|
|
|
/* Logging stuff */
|
|
|
|
PRLogModuleInfo* NNTP = NULL;
|
|
#define out PR_LOG_ALWAYS
|
|
|
|
#define NNTP_LOG_READ(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, ("(%p) Receiving: %s", this, buf)) ;
|
|
|
|
#define NNTP_LOG_WRITE(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, ("(%p) Sending: %s", this, buf)) ;
|
|
|
|
#define NNTP_LOG_NOTE(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, ("(%p) %s",this, buf)) ;
|
|
|
|
const char *const stateLabels[] = {
|
|
"NNTP_RESPONSE",
|
|
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
|
|
"NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE",
|
|
"NNTP_CONNECTIONS_ARE_AVAILABLE",
|
|
#endif
|
|
"NNTP_CONNECT",
|
|
"NNTP_CONNECT_WAIT",
|
|
"NNTP_LOGIN_RESPONSE",
|
|
"NNTP_SEND_MODE_READER",
|
|
"NNTP_SEND_MODE_READER_RESPONSE",
|
|
"SEND_LIST_EXTENSIONS",
|
|
"SEND_LIST_EXTENSIONS_RESPONSE",
|
|
"SEND_LIST_SEARCHES",
|
|
"SEND_LIST_SEARCHES_RESPONSE",
|
|
"NNTP_LIST_SEARCH_HEADERS",
|
|
"NNTP_LIST_SEARCH_HEADERS_RESPONSE",
|
|
"NNTP_GET_PROPERTIES",
|
|
"NNTP_GET_PROPERTIES_RESPONSE",
|
|
"SEND_LIST_SUBSCRIPTIONS",
|
|
"SEND_LIST_SUBSCRIPTIONS_RESPONSE",
|
|
"SEND_FIRST_NNTP_COMMAND",
|
|
"SEND_FIRST_NNTP_COMMAND_RESPONSE",
|
|
"SETUP_NEWS_STREAM",
|
|
"NNTP_BEGIN_AUTHORIZE",
|
|
"NNTP_AUTHORIZE_RESPONSE",
|
|
"NNTP_PASSWORD_RESPONSE",
|
|
"NNTP_READ_LIST_BEGIN",
|
|
"NNTP_READ_LIST",
|
|
"DISPLAY_NEWSGROUPS",
|
|
"NNTP_NEWGROUPS_BEGIN",
|
|
"NNTP_NEWGROUPS",
|
|
"NNTP_BEGIN_ARTICLE",
|
|
"NNTP_READ_ARTICLE",
|
|
"NNTP_XOVER_BEGIN",
|
|
"NNTP_FIGURE_NEXT_CHUNK",
|
|
"NNTP_XOVER_SEND",
|
|
"NNTP_XOVER_RESPONSE",
|
|
"NNTP_XOVER",
|
|
"NEWS_PROCESS_XOVER",
|
|
"NNTP_READ_GROUP",
|
|
"NNTP_READ_GROUP_RESPONSE",
|
|
"NNTP_READ_GROUP_BODY",
|
|
"NNTP_SEND_GROUP_FOR_ARTICLE",
|
|
"NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE",
|
|
"NNTP_PROFILE_ADD",
|
|
"NNTP_PROFILE_ADD_RESPONSE",
|
|
"NNTP_PROFILE_DELETE",
|
|
"NNTP_PROFILE_DELETE_RESPONSE",
|
|
"NNTP_SEND_ARTICLE_NUMBER",
|
|
"NEWS_PROCESS_BODIES",
|
|
"NNTP_PRINT_ARTICLE_HEADERS",
|
|
"NNTP_SEND_POST_DATA",
|
|
"NNTP_SEND_POST_DATA_RESPONSE",
|
|
"NNTP_CHECK_FOR_MESSAGE",
|
|
"NEWS_NEWS_RC_POST",
|
|
"NEWS_DISPLAY_NEWS_RC",
|
|
"NEWS_DISPLAY_NEWS_RC_RESPONSE",
|
|
"NEWS_START_CANCEL",
|
|
"NEWS_DO_CANCEL",
|
|
"NNTP_XPAT_SEND",
|
|
"NNTP_XPAT_RESPONSE",
|
|
"NNTP_SEARCH",
|
|
"NNTP_SEARCH_RESPONSE",
|
|
"NNTP_SEARCH_RESULTS",
|
|
"NNTP_LIST_PRETTY_NAMES",
|
|
"NNTP_LIST_PRETTY_NAMES_RESPONSE",
|
|
"NNTP_LIST_XACTIVE_RESPONSE",
|
|
"NNTP_LIST_XACTIVE",
|
|
"NNTP_LIST_GROUP",
|
|
"NNTP_LIST_GROUP_RESPONSE",
|
|
"NEWS_DONE",
|
|
"NEWS_POST_DONE",
|
|
"NEWS_ERROR",
|
|
"NNTP_ERROR",
|
|
"NEWS_FREE",
|
|
"NEWS_FINISHED"
|
|
};
|
|
|
|
|
|
/* end logging */
|
|
|
|
/* Forward declarations */
|
|
|
|
#define LIST_WANTED 0
|
|
#define ARTICLE_WANTED 1
|
|
#define CANCEL_WANTED 2
|
|
#define GROUP_WANTED 3
|
|
#define NEWS_POST 4
|
|
#define READ_NEWS_RC 5
|
|
#define NEW_GROUPS 6
|
|
#define SEARCH_WANTED 7
|
|
#define PRETTY_NAMES_WANTED 8
|
|
#define PROFILE_WANTED 9
|
|
#define IDS_WANTED 10
|
|
|
|
/* the output_buffer_size must be larger than the largest possible line
|
|
* 2000 seems good for news
|
|
*
|
|
* jwz: I increased this to 4k since it must be big enough to hold the
|
|
* entire button-bar HTML, and with the new "mailto" format, that can
|
|
* contain arbitrarily long header fields like "references".
|
|
*
|
|
* fortezza: proxy auth is huge, buffer increased to 8k (sigh).
|
|
*/
|
|
#define OUTPUT_BUFFER_SIZE (4096*2)
|
|
|
|
/* the amount of time to subtract from the machine time
|
|
* for the newgroup command sent to the nntp server
|
|
*/
|
|
#define NEWGROUPS_TIME_OFFSET 60L*60L*12L /* 12 hours */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TEMPORARY HARD CODED FUNCTIONS
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
#ifdef XP_WIN
|
|
static char *XP_AppCodeName = "RetroZilla";
|
|
#else
|
|
static const char *XP_AppCodeName = "RetroZilla";
|
|
#endif
|
|
#define NET_IS_SPACE(x) ((x)==' ' || (x)=='\t')
|
|
|
|
// turn "\xx" (with xx being hex numbers) in string into chars
|
|
char *MSG_UnEscapeSearchUrl (const char *commandSpecificData)
|
|
{
|
|
nsCAutoString result(commandSpecificData);
|
|
PRInt32 slashpos = 0;
|
|
while (slashpos = result.FindChar('\\', slashpos),
|
|
slashpos != kNotFound)
|
|
{
|
|
nsCAutoString hex;
|
|
hex.Assign(Substring(result, slashpos + 1, 2));
|
|
PRInt32 err, ch;
|
|
ch = hex.ToInteger(&err, 16);
|
|
result.Replace(slashpos, 3, err == NS_OK && ch != 0 ? (char) ch : 'X');
|
|
slashpos++;
|
|
}
|
|
return ToNewCString(result);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// END OF TEMPORARY HARD CODED FUNCTIONS
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsNNTPProtocol, nsMsgProtocol)
|
|
NS_IMPL_RELEASE_INHERITED(nsNNTPProtocol, nsMsgProtocol)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsNNTPProtocol)
|
|
NS_INTERFACE_MAP_ENTRY(nsINNTPProtocol)
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsICacheListener)
|
|
NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
|
|
|
|
nsNNTPProtocol::nsNNTPProtocol(nsIURI * aURL, nsIMsgWindow *aMsgWindow)
|
|
: nsMsgProtocol(aURL)
|
|
{
|
|
if (!NNTP)
|
|
NNTP = PR_NewLogModule("NNTP");
|
|
|
|
m_ProxyServer = nsnull;
|
|
m_lineStreamBuffer = nsnull;
|
|
m_responseText = nsnull;
|
|
m_dataBuf = nsnull;
|
|
m_path = nsnull;
|
|
|
|
m_cancelFromHdr = nsnull;
|
|
m_cancelNewsgroups = nsnull;
|
|
m_cancelDistribution = nsnull;
|
|
m_cancelID = nsnull;
|
|
|
|
m_messageID = nsnull;
|
|
m_key = nsMsgKey_None;
|
|
|
|
m_commandSpecificData = nsnull;
|
|
m_searchData = nsnull;
|
|
|
|
mBytesReceived = 0;
|
|
mBytesReceivedSinceLastStatusUpdate = 0;
|
|
m_startTime = PR_Now();
|
|
|
|
if (aMsgWindow) {
|
|
m_msgWindow = aMsgWindow;
|
|
}
|
|
|
|
m_runningURL = nsnull;
|
|
SetIsBusy(PR_FALSE);
|
|
m_fromCache = PR_FALSE;
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) creating",this));
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) initializing, so unset m_currentGroup",this));
|
|
m_currentGroup.Truncate();
|
|
LL_I2L(m_lastActiveTimeStamp, 0);
|
|
}
|
|
|
|
nsNNTPProtocol::~nsNNTPProtocol()
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) destroying",this));
|
|
if (m_nntpServer) {
|
|
m_nntpServer->WriteNewsrcFile();
|
|
m_nntpServer->RemoveConnection(this);
|
|
}
|
|
if (m_lineStreamBuffer) {
|
|
delete m_lineStreamBuffer;
|
|
}
|
|
if (mUpdateTimer) {
|
|
mUpdateTimer->Cancel();
|
|
mUpdateTimer = nsnull;
|
|
}
|
|
Cleanup();
|
|
}
|
|
|
|
void nsNNTPProtocol::Cleanup() //free char* member variables
|
|
{
|
|
PR_FREEIF(m_responseText);
|
|
PR_FREEIF(m_dataBuf);
|
|
PR_FREEIF(m_path);
|
|
PR_FREEIF(m_cancelFromHdr);
|
|
PR_FREEIF(m_cancelNewsgroups);
|
|
PR_FREEIF(m_cancelDistribution);
|
|
PR_FREEIF(m_cancelID);
|
|
PR_FREEIF(m_messageID);
|
|
PR_FREEIF(m_commandSpecificData);
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::Initialize(nsIURI * aURL, nsIMsgWindow *aMsgWindow)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool isSecure = PR_FALSE;
|
|
|
|
if (aMsgWindow) {
|
|
m_msgWindow = aMsgWindow;
|
|
}
|
|
nsMsgProtocol::InitFromURI(aURL);
|
|
|
|
nsCAutoString userPass;
|
|
rv = m_url->GetUserPass(userPass);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCAutoString hostName;
|
|
rv = m_url->GetAsciiHost(hostName);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
char *unescapedUserPass = ToNewCString(userPass);
|
|
if (!unescapedUserPass)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
nsUnescape(unescapedUserPass);
|
|
|
|
// find the server
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = accountManager->FindServer(unescapedUserPass, hostName.get(), "nntp",
|
|
getter_AddRefs(server));
|
|
PR_FREEIF(unescapedUserPass);
|
|
NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
|
|
if (!server) return NS_MSG_INVALID_OR_MISSING_SERVER;
|
|
|
|
m_nntpServer = do_QueryInterface(server, &rv);
|
|
NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
|
|
if (!m_nntpServer) return NS_MSG_INVALID_OR_MISSING_SERVER;
|
|
|
|
rv = m_nntpServer->GetMaxArticles(&m_maxArticles);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = server->GetIsSecure(&isSecure);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PRInt32 port = 0;
|
|
rv = m_url->GetPort(&port);
|
|
if (NS_FAILED(rv) || (port<=0)) {
|
|
rv = server->GetPort(&port);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (port<=0) {
|
|
if (isSecure) {
|
|
port = SECURE_NEWS_PORT;
|
|
}
|
|
else {
|
|
port = NEWS_PORT;
|
|
}
|
|
}
|
|
|
|
rv = m_url->SetPort(port);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
NS_PRECONDITION(m_url , "invalid URL passed into NNTP Protocol");
|
|
|
|
m_runningURL = do_QueryInterface(m_url);
|
|
SetIsBusy(PR_TRUE);
|
|
|
|
if (NS_SUCCEEDED(rv) && m_runningURL)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsUrl)
|
|
{
|
|
mailnewsUrl->SetMsgWindow(aMsgWindow);
|
|
|
|
m_runningURL->GetNewsAction(&m_newsAction);
|
|
if (m_newsAction == nsINntpUrl::ActionFetchArticle || m_newsAction == nsINntpUrl::ActionFetchPart
|
|
|| m_newsAction == nsINntpUrl::ActionSaveMessageToDisk) {
|
|
PRBool msgIsInLocalCache = PR_FALSE;
|
|
mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache);
|
|
if (msgIsInLocalCache)
|
|
return NS_OK; // probably don't need to do anything else - definitely don't want
|
|
// to open the socket.
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return rv;
|
|
}
|
|
|
|
if (!m_socketIsOpen)
|
|
{
|
|
|
|
// When we are making a secure connection, we need to make sure that we
|
|
// pass an interface requestor down to the socket transport so that PSM can
|
|
// retrieve a nsIPrompt instance if needed.
|
|
nsCOMPtr<nsIInterfaceRequestor> ir;
|
|
if (isSecure && aMsgWindow) {
|
|
nsCOMPtr<nsIDocShell> docShell;
|
|
aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
|
|
ir = do_QueryInterface(docShell);
|
|
}
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) opening connection to %s on port %d",this, hostName.get(), port));
|
|
// call base class to set up the transport
|
|
|
|
PRInt32 port = 0;
|
|
nsXPIDLCString hostName;
|
|
m_url->GetPort(&port);
|
|
nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer);
|
|
if (server)
|
|
server->GetRealHostName(getter_Copies(hostName));
|
|
|
|
nsCOMPtr<nsIProxyInfo> proxyInfo;
|
|
rv = NS_ExamineForProxy("nntp", hostName.get(), port, getter_AddRefs(proxyInfo));
|
|
if (NS_FAILED(rv)) proxyInfo = nsnull;
|
|
|
|
rv = OpenNetworkSocketWithInfo(hostName.get(), port, isSecure ? "ssl" : nsnull, proxyInfo, ir);
|
|
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
m_nextState = NNTP_LOGIN_RESPONSE;
|
|
}
|
|
else {
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
}
|
|
m_dataBuf = (char *) PR_Malloc(sizeof(char) * OUTPUT_BUFFER_SIZE);
|
|
m_dataBufSize = OUTPUT_BUFFER_SIZE;
|
|
|
|
if (!m_lineStreamBuffer)
|
|
m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE /* create new lines */);
|
|
|
|
m_typeWanted = 0;
|
|
m_responseCode = 0;
|
|
m_previousResponseCode = 0;
|
|
m_responseText = nsnull;
|
|
|
|
m_path = nsnull;
|
|
|
|
m_firstArticle = 0;
|
|
m_lastArticle = 0;
|
|
m_firstPossibleArticle = 0;
|
|
m_lastPossibleArticle = 0;
|
|
m_numArticlesLoaded = 0;
|
|
m_numArticlesWanted = 0;
|
|
|
|
m_newsRCListIndex = 0;
|
|
m_RCIndexToResumeAfterAuthRequest = 0;
|
|
m_newsRCListCount = 0;
|
|
|
|
PR_FREEIF(m_messageID);
|
|
m_messageID = nsnull;
|
|
|
|
m_key = nsMsgKey_None;
|
|
|
|
m_articleNumber = 0;
|
|
m_originalContentLength = 0;
|
|
m_cancelID = nsnull;
|
|
m_cancelFromHdr = nsnull;
|
|
m_cancelNewsgroups = nsnull;
|
|
m_cancelDistribution = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::GetIsBusy(PRBool *aIsBusy)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aIsBusy);
|
|
*aIsBusy = m_connectionBusy;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::SetIsBusy(PRBool aIsBusy)
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) setting busy to %d",this, aIsBusy));
|
|
m_connectionBusy = aIsBusy;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::GetIsCachedConnection(PRBool *aIsCachedConnection)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aIsCachedConnection);
|
|
*aIsCachedConnection = m_fromCache;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::SetIsCachedConnection(PRBool aIsCachedConnection)
|
|
{
|
|
m_fromCache = aIsCachedConnection;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* void GetLastActiveTimeStamp (out PRTime aTimeStamp); */
|
|
NS_IMETHODIMP nsNNTPProtocol::GetLastActiveTimeStamp(PRTime *aTimeStamp)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aTimeStamp);
|
|
*aTimeStamp = m_lastActiveTimeStamp;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::LoadNewsUrl(nsIURI * aURL, nsISupports * aConsumer)
|
|
{
|
|
// clear the previous channel listener and use the new one....
|
|
// don't reuse an existing channel listener
|
|
m_channelListener = nsnull;
|
|
m_channelListener = do_QueryInterface(aConsumer);
|
|
nsCOMPtr<nsINntpUrl> newsUrl (do_QueryInterface(aURL));
|
|
newsUrl->GetNewsAction(&m_newsAction);
|
|
|
|
SetupPartExtractorListener(m_channelListener);
|
|
return LoadUrl(aURL, aConsumer);
|
|
}
|
|
|
|
|
|
// WARNING: the cache stream listener is intended to be accessed from the UI thread!
|
|
// it will NOT create another proxy for the stream listener that gets passed in...
|
|
class nsNntpCacheStreamListener : public nsIStreamListener
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
NS_DECL_NSISTREAMLISTENER
|
|
|
|
nsNntpCacheStreamListener ();
|
|
virtual ~nsNntpCacheStreamListener();
|
|
|
|
nsresult Init(nsIStreamListener * aStreamListener, nsIChannel* channel, nsIMsgMailNewsUrl *aRunningUrl);
|
|
protected:
|
|
nsCOMPtr<nsIChannel> mChannelToUse;
|
|
nsCOMPtr<nsIStreamListener> mListener;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mRunningUrl;
|
|
};
|
|
|
|
NS_IMPL_ADDREF(nsNntpCacheStreamListener)
|
|
NS_IMPL_RELEASE(nsNntpCacheStreamListener)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsNntpCacheStreamListener)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
nsNntpCacheStreamListener::nsNntpCacheStreamListener()
|
|
{
|
|
}
|
|
|
|
nsNntpCacheStreamListener::~nsNntpCacheStreamListener()
|
|
{}
|
|
|
|
nsresult nsNntpCacheStreamListener::Init(nsIStreamListener * aStreamListener, nsIChannel* channel,
|
|
nsIMsgMailNewsUrl *aRunningUrl)
|
|
{
|
|
NS_ENSURE_ARG(aStreamListener);
|
|
NS_ENSURE_ARG(channel);
|
|
|
|
mChannelToUse = channel;
|
|
|
|
mListener = aStreamListener;
|
|
mRunningUrl = aRunningUrl;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNntpCacheStreamListener::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
|
|
{
|
|
nsCOMPtr <nsILoadGroup> loadGroup;
|
|
nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
|
|
|
|
mChannelToUse->GetLoadGroup(getter_AddRefs(loadGroup));
|
|
if (loadGroup)
|
|
loadGroup->AddRequest(ourRequest, nsnull /* context isupports */);
|
|
return mListener->OnStartRequest(ourRequest, aCtxt);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNntpCacheStreamListener::OnStopRequest(nsIRequest *request, nsISupports * aCtxt, nsresult aStatus)
|
|
{
|
|
nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
|
|
nsresult rv = mListener->OnStopRequest(ourRequest, aCtxt, aStatus);
|
|
nsCOMPtr <nsILoadGroup> loadGroup;
|
|
mChannelToUse->GetLoadGroup(getter_AddRefs(loadGroup));
|
|
if (loadGroup)
|
|
loadGroup->RemoveRequest(ourRequest, nsnull, aStatus);
|
|
|
|
// clear out mem cache entry so we're not holding onto it.
|
|
if (mRunningUrl)
|
|
mRunningUrl->SetMemCacheEntry(nsnull);
|
|
|
|
mListener = nsnull;
|
|
nsCOMPtr <nsINNTPProtocol> nntpProtocol = do_QueryInterface(mChannelToUse);
|
|
if (nntpProtocol) {
|
|
rv = nntpProtocol->SetIsBusy(PR_FALSE);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
mChannelToUse = nsnull;
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNntpCacheStreamListener::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt, nsIInputStream * aInStream, PRUint32 aSourceOffset, PRUint32 aCount)
|
|
{
|
|
nsCOMPtr <nsIRequest> ourRequest = do_QueryInterface(mChannelToUse);
|
|
return mListener->OnDataAvailable(ourRequest, aCtxt, aInStream, aSourceOffset, aCount);
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::GetOriginalURI(nsIURI* *aURI)
|
|
{
|
|
// News does not seem to have the notion of an original URI (See Bug #193317)
|
|
// *aURI = m_originalUrl ? m_originalUrl : m_url;
|
|
*aURI = m_url;
|
|
NS_IF_ADDREF(*aURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::SetOriginalURI(nsIURI* aURI)
|
|
{
|
|
// News does not seem to have the notion of an original URI (See Bug #193317)
|
|
return NS_OK; // ignore
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::SetupPartExtractorListener(nsIStreamListener * aConsumer)
|
|
{
|
|
PRBool convertData;
|
|
nsresult rv = NS_OK;
|
|
|
|
if (m_newsAction == nsINntpUrl::ActionFetchArticle)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_runningURL, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCAutoString queryStr;
|
|
rv = msgUrl->GetQuery(queryStr);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// check if this is a filter plugin requesting the message.
|
|
// in that case, set up a text converter
|
|
convertData = (queryStr.Find("header=filter") != kNotFound
|
|
|| queryStr.Find("header=attach") != kNotFound);
|
|
}
|
|
else
|
|
{
|
|
convertData = (m_newsAction == nsINntpUrl::ActionFetchPart);
|
|
}
|
|
if (convertData)
|
|
{
|
|
nsCOMPtr<nsIStreamConverterService> converter = do_GetService("@mozilla.org/streamConverters;1");
|
|
if (converter && aConsumer)
|
|
{
|
|
nsCOMPtr<nsIStreamListener> newConsumer;
|
|
nsCOMPtr<nsIChannel> channel;
|
|
QueryInterface(NS_GET_IID(nsIChannel), getter_AddRefs(channel));
|
|
converter->AsyncConvertData("message/rfc822", "*/*",
|
|
aConsumer, channel, getter_AddRefs(newConsumer));
|
|
if (newConsumer)
|
|
m_channelListener = newConsumer;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::ReadFromMemCache(nsICacheEntryDescriptor *entry)
|
|
{
|
|
NS_ENSURE_ARG(entry);
|
|
|
|
nsCOMPtr<nsIInputStream> cacheStream;
|
|
nsresult rv = entry->OpenInputStream(0, getter_AddRefs(cacheStream));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsIInputStreamPump> pump;
|
|
rv = NS_NewInputStreamPump(getter_AddRefs(pump), cacheStream);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsXPIDLCString group;
|
|
nsXPIDLCString commandSpecificData;
|
|
// do this to get m_key set, so that marking the message read will work.
|
|
PR_FREEIF(m_messageID);
|
|
rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
|
|
|
|
nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener();
|
|
if (!cacheListener)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(cacheListener);
|
|
|
|
SetLoadGroup(m_loadGroup);
|
|
m_typeWanted = ARTICLE_WANTED;
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
|
|
cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl);
|
|
|
|
m_ContentType = ""; // reset the content type for the upcoming read....
|
|
|
|
rv = pump->AsyncRead(cacheListener, m_channelContext);
|
|
NS_RELEASE(cacheListener);
|
|
|
|
if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
|
|
{
|
|
// we're not calling nsMsgProtocol::AsyncRead(), which calls nsNNTPProtocol::LoadUrl, so we need to take care of some stuff it does.
|
|
m_channelListener = nsnull;
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::ReadFromNewsConnection()
|
|
{
|
|
return nsMsgProtocol::AsyncOpen(m_channelListener, m_channelContext);
|
|
}
|
|
|
|
// for messages stored in our offline cache, we have special code to handle that...
|
|
// If it's in the local cache, we return true and we can abort the download because
|
|
// this method does the rest of the work.
|
|
PRBool nsNNTPProtocol::ReadFromLocalCache()
|
|
{
|
|
PRBool msgIsInLocalCache = PR_FALSE;
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
|
|
mailnewsUrl->GetMsgIsInLocalCache(&msgIsInLocalCache);
|
|
|
|
if (msgIsInLocalCache)
|
|
{
|
|
nsXPIDLCString group;
|
|
nsXPIDLCString commandSpecificData;
|
|
PR_FREEIF(m_messageID);
|
|
rv = ParseURL(m_url, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
|
|
nsCOMPtr <nsIMsgFolder> folder = do_QueryInterface(m_newsFolder);
|
|
if (folder && NS_SUCCEEDED(rv))
|
|
{
|
|
// we want to create a file channel and read the msg from there.
|
|
nsCOMPtr<nsIInputStream> fileStream;
|
|
PRUint32 offset=0, size=0;
|
|
rv = folder->GetOfflineFileStream(m_key, &offset, &size, getter_AddRefs(fileStream));
|
|
|
|
// get the file stream from the folder, somehow (through the message or
|
|
// folder sink?) We also need to set the transfer offset to the message offset
|
|
if (fileStream && NS_SUCCEEDED(rv))
|
|
{
|
|
// dougt - This may break the ablity to "cancel" a read from offline mail reading.
|
|
// fileChannel->SetLoadGroup(m_loadGroup);
|
|
|
|
m_typeWanted = ARTICLE_WANTED;
|
|
|
|
nsNntpCacheStreamListener * cacheListener = new nsNntpCacheStreamListener();
|
|
if (!cacheListener)
|
|
return PR_FALSE;
|
|
|
|
NS_ADDREF(cacheListener);
|
|
cacheListener->Init(m_channelListener, NS_STATIC_CAST(nsIChannel *, this), mailnewsUrl);
|
|
|
|
// create a stream pump that will async read the specified amount of data.
|
|
// XXX make offset and size 64-bit ints
|
|
nsCOMPtr<nsIInputStreamPump> pump;
|
|
rv = NS_NewInputStreamPump(getter_AddRefs(pump),
|
|
fileStream, nsInt64(offset), nsInt64(size));
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = pump->AsyncRead(cacheListener, m_channelContext);
|
|
|
|
NS_RELEASE(cacheListener);
|
|
|
|
if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
|
|
{
|
|
m_ContentType.Truncate();
|
|
m_channelListener = nsnull;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNNTPProtocol::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (NS_SUCCEEDED(status))
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
|
|
mailnewsUrl->SetMemCacheEntry(entry);
|
|
|
|
// if we have write access then insert a "stream T" into the flow so data
|
|
// gets written to both
|
|
if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ))
|
|
{
|
|
// use a stream listener Tee to force data into the cache and to our current channel listener...
|
|
nsCOMPtr<nsIStreamListener> newListener;
|
|
nsCOMPtr<nsIStreamListenerTee> tee = do_CreateInstance(kStreamListenerTeeCID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIOutputStream> out;
|
|
rv = entry->OpenOutputStream(0, getter_AddRefs(out));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = tee->Init(m_channelListener, out);
|
|
m_channelListener = do_QueryInterface(tee);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
else
|
|
{
|
|
rv = ReadFromMemCache(entry);
|
|
if (access & nsICache::ACCESS_WRITE)
|
|
entry->MarkValid();
|
|
if (NS_SUCCEEDED(rv)) return NS_OK; // kick out if reading from the cache succeeded...
|
|
}
|
|
} // if we got a valid entry back from the cache...
|
|
|
|
// if reading from the cache failed or if we are writing into the cache, default to ReadFromImapConnection.
|
|
return ReadFromNewsConnection();
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::OpenCacheEntry()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
|
|
// get the cache session from our nntp service...
|
|
nsCOMPtr <nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsICacheSession> cacheSession;
|
|
rv = nntpService->GetCacheSession(getter_AddRefs(cacheSession));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Open a cache entry with key = url
|
|
nsCAutoString urlSpec;
|
|
mailnewsUrl->GetAsciiSpec(urlSpec);
|
|
// for now, truncate of the query part so we don't duplicate urls in the cache...
|
|
char * anchor = (char *)strrchr(urlSpec.BeginWriting(), '?');
|
|
if (anchor)
|
|
*anchor = '\0';
|
|
return cacheSession->AsyncOpenCacheEntry(urlSpec, nsICache::ACCESS_READ_WRITE, this);
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PRInt32 port;
|
|
rv = mailnewsUrl->GetPort(&port);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = NS_CheckPortSafety(port, "news");
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
m_channelContext = ctxt;
|
|
m_channelListener = listener;
|
|
m_runningURL->GetNewsAction(&m_newsAction);
|
|
// first, check if this is a message load that should come from either
|
|
// the memory cache or the local msg cache.
|
|
if (mailnewsUrl && (m_newsAction == nsINntpUrl::ActionFetchArticle || m_newsAction == nsINntpUrl::ActionFetchPart
|
|
|| m_newsAction == nsINntpUrl::ActionSaveMessageToDisk))
|
|
{
|
|
|
|
SetupPartExtractorListener(m_channelListener);
|
|
if (ReadFromLocalCache())
|
|
return NS_OK;
|
|
|
|
rv = OpenCacheEntry();
|
|
if (NS_SUCCEEDED(rv)) return NS_OK; // if this didn't return an error then jump out now...
|
|
|
|
}
|
|
nsCOMPtr<nsIRequest> parentRequest;
|
|
return nsMsgProtocol::AsyncOpen(listener, ctxt);
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aURL);
|
|
|
|
nsXPIDLCString group;
|
|
nsXPIDLCString commandSpecificData;
|
|
PRBool cancel = PR_FALSE;
|
|
m_ContentType.Truncate();
|
|
nsresult rv = NS_OK;
|
|
|
|
m_runningURL = do_QueryInterface(aURL, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
m_runningURL->GetNewsAction(&m_newsAction);
|
|
|
|
SetIsBusy(PR_TRUE);
|
|
|
|
PR_FREEIF(m_messageID);
|
|
m_messageID = nsnull;
|
|
|
|
rv = ParseURL(aURL, getter_Copies(group), &m_messageID, getter_Copies(commandSpecificData));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to parse news url");
|
|
//if (NS_FAILED(rv)) return rv;
|
|
// XXX group returned from ParseURL is assumed to be in UTF-8
|
|
NS_ASSERTION(IsUTF8(group), "newsgroup name is not in UTF-8");
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) m_messageID = %s",this, m_messageID?m_messageID:"(null)"));
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) group = %s",this,group.get()));
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) commandSpecificData = %s",this,commandSpecificData.get()?commandSpecificData.get():"(null)"));
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) m_key = %d",this,m_key));
|
|
|
|
// for now, only support "news://host/message-id?cancel", and not "news://host/group#key?cancel"
|
|
if (m_messageID && !PL_strcmp(commandSpecificData.get(), "?cancel")) {
|
|
// XXX todo, don't allow manual cancel urls. only allow internal ones
|
|
cancel = PR_TRUE;
|
|
}
|
|
|
|
NS_MsgSACopy(&m_path, m_messageID);
|
|
|
|
/* We are posting a user-written message
|
|
if and only if this message has a message to post
|
|
Cancel messages are created later with a ?cancel URL
|
|
*/
|
|
nsCOMPtr <nsINNTPNewsgroupPost> message;
|
|
rv = m_runningURL->GetMessageToPost(getter_AddRefs(message));
|
|
if (NS_SUCCEEDED(rv) && message)
|
|
{
|
|
m_typeWanted = NEWS_POST;
|
|
NS_MsgSACopy(&m_path, "");
|
|
}
|
|
else
|
|
if (m_messageID || (m_key != nsMsgKey_None))
|
|
{
|
|
/*
|
|
news-message://HOST/GROUP#key
|
|
news://HOST/MESSAGE_ID
|
|
|
|
not sure about these:
|
|
|
|
news:MESSAGE_ID
|
|
news:/GROUP/MESSAGE_ID (useless)
|
|
news://HOST/GROUP/MESSAGE_ID (useless)
|
|
*/
|
|
if (cancel)
|
|
m_typeWanted = CANCEL_WANTED;
|
|
else
|
|
m_typeWanted = ARTICLE_WANTED;
|
|
}
|
|
else if (!commandSpecificData.IsEmpty())
|
|
{
|
|
if (PL_strstr (commandSpecificData.get(), "?newgroups"))
|
|
/* news://HOST/?newsgroups
|
|
news:/GROUP/?newsgroups (useless)
|
|
news:?newsgroups (default host)
|
|
*/
|
|
m_typeWanted = NEW_GROUPS;
|
|
else
|
|
{
|
|
if (PL_strstr(commandSpecificData.get(), "?list-pretty"))
|
|
{
|
|
m_typeWanted = PRETTY_NAMES_WANTED;
|
|
m_commandSpecificData = ToNewCString(commandSpecificData);
|
|
}
|
|
else if (PL_strstr(commandSpecificData.get(), "?profile"))
|
|
{
|
|
m_typeWanted = PROFILE_WANTED;
|
|
m_commandSpecificData = ToNewCString(commandSpecificData);
|
|
}
|
|
else if (PL_strstr(commandSpecificData.get(), "?list-ids"))
|
|
{
|
|
m_typeWanted = IDS_WANTED;
|
|
m_commandSpecificData = ToNewCString(commandSpecificData);
|
|
|
|
rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
|
|
if (!m_newsFolder) goto FAIL;
|
|
}
|
|
else
|
|
{
|
|
m_typeWanted = SEARCH_WANTED;
|
|
m_commandSpecificData = ToNewCString(commandSpecificData);
|
|
nsUnescape(m_commandSpecificData);
|
|
m_searchData = m_commandSpecificData;
|
|
|
|
|
|
rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
|
|
if (!m_newsFolder) goto FAIL;
|
|
}
|
|
}
|
|
}
|
|
else if (!group.IsEmpty())
|
|
{
|
|
/* news:GROUP
|
|
news:/GROUP
|
|
news://HOST/GROUP
|
|
*/
|
|
if (PL_strchr(group.get(),'*')) {
|
|
// getting all the newsgroups on the server, for subscribe dialog
|
|
m_typeWanted = LIST_WANTED;
|
|
}
|
|
else
|
|
{
|
|
if (m_nntpServer)
|
|
{
|
|
PRBool containsGroup = PR_TRUE;
|
|
rv = m_nntpServer->ContainsNewsgroup(group, &containsGroup);
|
|
if (NS_FAILED(rv)) goto FAIL;
|
|
|
|
if (!containsGroup)
|
|
{
|
|
// We have the name of a newsgroup which we're not subscribed to,
|
|
// the next step is to ask the user whether we should subscribe to it.
|
|
|
|
nsCOMPtr<nsIPrompt> dialog;
|
|
|
|
if (m_msgWindow)
|
|
m_msgWindow->GetPromptDialog(getter_AddRefs(dialog));
|
|
|
|
if (!dialog)
|
|
{
|
|
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
|
|
wwatch->GetNewPrompter(nsnull, getter_AddRefs(dialog));
|
|
}
|
|
|
|
nsXPIDLString statusString, confirmText;
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
|
|
|
|
// to handle non-ASCII newsgroup names, we store them internally
|
|
// as escaped. decode and unescape the newsgroup name so we'll
|
|
// display a proper name.
|
|
|
|
nsAutoString unescapedName;
|
|
rv = NS_MsgDecodeUnescapeURLPath(group, unescapedName);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
|
|
const PRUnichar *formatStrings[1] = { unescapedName.get() };
|
|
|
|
rv = bundle->FormatStringFromName(NS_LITERAL_STRING("autoSubscribeText").get(),
|
|
formatStrings, 1,
|
|
getter_Copies(confirmText));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PRBool confirmResult = PR_FALSE;
|
|
rv = dialog->Confirm(nsnull, confirmText, &confirmResult);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (confirmResult)
|
|
{
|
|
rv = m_nntpServer->SubscribeToNewsgroup(group);
|
|
containsGroup = PR_TRUE;
|
|
}
|
|
else {
|
|
// XXX FIX ME
|
|
// the way news is current written, we've already opened the socket
|
|
// and initialized the connection.
|
|
//
|
|
// until that is fixed, when the user cancels an autosubscribe, we've got to close it and clean up after ourselves
|
|
//
|
|
// see bug http://bugzilla.mozilla.org/show_bug.cgi?id=108293
|
|
// another problem, autosubscribe urls are ending up as cache entries
|
|
// because the default action on nntp urls is ActionFetchArticle
|
|
//
|
|
// see bug http://bugzilla.mozilla.org/show_bug.cgi?id=108294
|
|
if (m_runningURL)
|
|
FinishMemCacheEntry(PR_FALSE); // cleanup mem cache entry
|
|
|
|
return CloseConnection();
|
|
}
|
|
}
|
|
|
|
// If we have a group (since before, or just subscribed), set the m_newsFolder.
|
|
if (containsGroup)
|
|
{
|
|
rv = m_nntpServer->FindGroup(group, getter_AddRefs(m_newsFolder));
|
|
if (!m_newsFolder) goto FAIL;
|
|
}
|
|
}
|
|
m_typeWanted = GROUP_WANTED;
|
|
}
|
|
}
|
|
else
|
|
// news: or news://HOST
|
|
m_typeWanted = READ_NEWS_RC;
|
|
|
|
// if this connection comes from the cache, we need to initialize the
|
|
// load group here, by generating the start request notification. nsMsgProtocol::OnStartRequest
|
|
// ignores the first parameter (which is supposed to be the channel) so we'll pass in null.
|
|
if (m_fromCache)
|
|
nsMsgProtocol::OnStartRequest(nsnull, aURL);
|
|
|
|
/* At this point, we're all done parsing the URL, and know exactly
|
|
what we want to do with it.
|
|
*/
|
|
#ifdef UNREADY_CODE
|
|
#ifndef NO_ARTICLE_CACHEING
|
|
/* Turn off caching on all news entities, except articles. */
|
|
/* It's very important that this be turned off for CANCEL_WANTED;
|
|
for the others, I don't know what cacheing would cause, but
|
|
it could only do harm, not good. */
|
|
if(m_typeWanted != ARTICLE_WANTED)
|
|
#endif
|
|
ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
|
|
#endif
|
|
|
|
|
|
FAIL:
|
|
if (NS_FAILED(rv))
|
|
{
|
|
AlertError(rv, nsnull);
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
if (!m_socketIsOpen)
|
|
{
|
|
m_nextStateAfterResponse = m_nextState;
|
|
m_nextState = NNTP_RESPONSE;
|
|
}
|
|
rv = nsMsgProtocol::LoadUrl(aURL, aConsumer);
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
void nsNNTPProtocol::FinishMemCacheEntry(PRBool valid)
|
|
{
|
|
nsCOMPtr <nsICacheEntryDescriptor> memCacheEntry;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
mailnewsurl->GetMemCacheEntry(getter_AddRefs(memCacheEntry));
|
|
if (memCacheEntry)
|
|
{
|
|
if (valid)
|
|
memCacheEntry->MarkValid();
|
|
else
|
|
memCacheEntry->Doom();
|
|
}
|
|
}
|
|
|
|
// stop binding is a "notification" informing us that the stream associated with aURL is going away.
|
|
NS_IMETHODIMP nsNNTPProtocol::OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus)
|
|
{
|
|
// either remove mem cache entry, or mark it valid if url successful and
|
|
// command succeeded
|
|
FinishMemCacheEntry(NS_SUCCEEDED(aStatus)
|
|
&& MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK);
|
|
|
|
nsMsgProtocol::OnStopRequest(request, aContext, aStatus);
|
|
|
|
// nsMsgProtocol::OnStopRequest() has called m_channelListener. There is
|
|
// no need to be called again in CloseSocket(). Let's clear it here.
|
|
if (m_channelListener) {
|
|
m_channelListener = nsnull;
|
|
}
|
|
|
|
// okay, we've been told that the send is done and the connection is going away. So
|
|
// we need to release all of our state
|
|
return CloseSocket();
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::Cancel(nsresult status) // handle stop button
|
|
{
|
|
m_nextState = NNTP_ERROR;
|
|
return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
|
|
}
|
|
|
|
/*
|
|
FIX THIS COMMENT, this is how 4.x worked. 6.x is different.
|
|
|
|
The main news load init routine, and URL parser.
|
|
The syntax of news URLs is:
|
|
|
|
To list all hosts:
|
|
news:
|
|
|
|
To list all groups on a host, or to post a new message:
|
|
news://HOST
|
|
|
|
To list some articles in a group:
|
|
news:GROUP
|
|
news:/GROUP
|
|
news://HOST/GROUP
|
|
|
|
To list a particular range of articles in a group:
|
|
news:GROUP/N1-N2
|
|
news:/GROUP/N1-N2
|
|
news://HOST/GROUP/N1-N2
|
|
|
|
To retrieve an article:
|
|
news:MESSAGE-ID (message-id must contain "@")
|
|
|
|
To cancel an article:
|
|
news:MESSAGE-ID?cancel
|
|
|
|
(Note that message IDs may contain / before the @:)
|
|
|
|
news:SOME/ID@HOST?headers=all
|
|
news:/SOME/ID@HOST?headers=all
|
|
news:/SOME?ID@HOST?headers=all
|
|
are the same as
|
|
news://HOST/SOME/ID@HOST?headers=all
|
|
news://HOST//SOME/ID@HOST?headers=all
|
|
news://HOST//SOME?ID@HOST?headers=all
|
|
bug: if the ID is <//xxx@host> we will parse this correctly:
|
|
news://non-default-host///xxx@host
|
|
but will mis-parse it if it's on the default host:
|
|
news://xxx@host
|
|
whoever had the idea to leave the <> off the IDs should be flogged.
|
|
So, we'll make sure we quote / in message IDs as %2F.
|
|
*/
|
|
nsresult
|
|
nsNNTPProtocol::ParseURL(nsIURI * aURL, char ** aGroup, char ** aMessageID,
|
|
char ** aCommandSpecificData)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aURL);
|
|
NS_ENSURE_ARG_POINTER(aGroup);
|
|
NS_ENSURE_ARG_POINTER(aMessageID);
|
|
NS_ENSURE_ARG_POINTER(aCommandSpecificData);
|
|
|
|
PRInt32 status = 0;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
char *s = 0;
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ParseURL",this));
|
|
|
|
nsresult rv;
|
|
nsCOMPtr <nsIMsgFolder> folder;
|
|
nsCOMPtr <nsINntpService> nntpService = do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningURL, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsXPIDLCString spec;
|
|
rv = msgUrl->GetOriginalSpec(getter_Copies(spec));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// if the original spec is non empty, use it to determine m_newsFolder and m_key
|
|
if (spec.get() && spec.get()[0]) {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) original message spec = %s",this,spec.get()));
|
|
|
|
rv = nntpService->DecomposeNewsURI(spec.get(), getter_AddRefs(folder), &m_key);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// since we are reading a message in this folder, we can set m_newsFolder
|
|
m_newsFolder = do_QueryInterface(folder, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// if we are cancelling, we aren't done. we still need to parse out the messageID from the url
|
|
// later, we'll use m_newsFolder and m_key to delete the message from the DB, if the cancel is successful.
|
|
if (m_newsAction != nsINntpUrl::ActionCancelArticle) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
// clear this, we'll set it later.
|
|
m_newsFolder = nsnull;
|
|
m_currentGroup.Truncate();
|
|
}
|
|
|
|
// get the file path part and store it as the group...
|
|
nsCAutoString fullPath;
|
|
rv = aURL->GetPath(fullPath);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) fullPath = %s",this, fullPath.get()));
|
|
|
|
if (fullPath.First() == '/')
|
|
group = nsCRT::strdup(fullPath.get()+1);
|
|
else
|
|
group = nsCRT::strdup(fullPath.get());
|
|
|
|
// more to do here, but for now, this works.
|
|
// only escape if we are doing a search
|
|
if (m_newsAction == nsINntpUrl::ActionSearch) {
|
|
nsUnescape(group);
|
|
}
|
|
else if (strchr(group, '@') || strstr(group,"%40")) {
|
|
message_id = nsUnescape(group);
|
|
group = 0;
|
|
}
|
|
else if (!*group) {
|
|
nsCRT::free(group);
|
|
group = 0;
|
|
}
|
|
|
|
/* At this point, the search data is attached to `message_id' (if there
|
|
is one) or `group' (if there is one) or `host_and_port' (otherwise.)
|
|
Separate the search data from what it is clinging to, being careful
|
|
to interpret the "?" only if it comes after the "@" in an ID, since
|
|
the syntax of message IDs is tricky. (There may be a ? in the
|
|
random-characters part of the ID (before @), but not in the host part
|
|
(after @).)
|
|
*/
|
|
if (message_id || group) {
|
|
char *start;
|
|
if (message_id) {
|
|
start = PL_strchr(message_id,'@');
|
|
}
|
|
else {
|
|
start = group; /* ? group : hostAndPort; */ // mscott -- fix me...necko sloppiness on my part
|
|
}
|
|
|
|
/* Take off the "?" or "#" search data */
|
|
for (s = start; *s; s++)
|
|
if (*s == '?' || *s == '#')
|
|
break;
|
|
|
|
if (*s) {
|
|
command_specific_data = nsCRT::strdup (s);
|
|
*s = 0;
|
|
if (!command_specific_data) {
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
}
|
|
|
|
/* Discard any now-empty strings. */
|
|
if (message_id && !*message_id) {
|
|
PR_Free (message_id);
|
|
message_id = 0;
|
|
}
|
|
else if (group && !*group) {
|
|
PR_Free(group);
|
|
group = 0;
|
|
}
|
|
}
|
|
|
|
FAIL:
|
|
NS_ASSERTION (!message_id || message_id != group, "something not null");
|
|
if (status >= 0) {
|
|
*aGroup = group;
|
|
*aMessageID = message_id;
|
|
*aCommandSpecificData = command_specific_data;
|
|
}
|
|
else {
|
|
PR_FREEIF(group);
|
|
PR_FREEIF(message_id);
|
|
PR_FREEIF(command_specific_data);
|
|
}
|
|
|
|
// if we are cancelling, we've got our message id, our m_key and our m_newsFolder.
|
|
// bail out now to prevent messing those up.
|
|
if (m_newsAction == nsINntpUrl::ActionCancelArticle) {
|
|
if (status < 0)
|
|
return NS_ERROR_FAILURE;
|
|
else
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCAutoString serverURI;
|
|
|
|
if (*aMessageID) {
|
|
// if this is a message id, use the pre path (the server) for the folder uri.
|
|
rv = aURL->GetPrePath(serverURI);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
else if (*aGroup) {
|
|
if (PL_strchr(*aGroup,'*')) {
|
|
rv = aURL->GetPrePath(serverURI);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
else {
|
|
// let if fall through. we'll set m_newsFolder later.
|
|
}
|
|
}
|
|
|
|
if (!serverURI.IsEmpty()) {
|
|
// if we get here, we, we are either doing:
|
|
// news://host/message-id or news://host/*
|
|
// (but not news://host/message-id?cancel)
|
|
// for authentication, we set m_newsFolder to be the server's folder.
|
|
// while we are here, we set m_nntpServer.
|
|
rv = nntpService->DecomposeNewsURI(serverURI.get(), getter_AddRefs(folder), &m_key);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// since we are reading a message in this folder, we can set m_newsFolder
|
|
m_newsFolder = do_QueryInterface(folder, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = m_newsFolder->GetNntpServer(getter_AddRefs(m_nntpServer));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
m_currentGroup.Truncate();
|
|
}
|
|
|
|
// mscott - this function might need to be re-written to use nsresults
|
|
// so we don't lose the nature of the error in this return code I'm adding.
|
|
if (status < 0)
|
|
return NS_ERROR_FAILURE;
|
|
else
|
|
return NS_OK;
|
|
}
|
|
/*
|
|
* Writes the data contained in dataBuffer into the current output stream. It also informs
|
|
* the transport layer that this data is now available for transmission.
|
|
* Returns a positive number for success, 0 for failure (not all the bytes were written to the
|
|
* stream, etc). We need to make another pass through this file to install an error system (mscott)
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
|
|
{
|
|
if (!aSuppressLogging) {
|
|
NNTP_LOG_WRITE(dataBuffer);
|
|
}
|
|
else {
|
|
PR_LOG(NNTP, out, ("(%p) Logging suppressed for this command (it probably contained authentication information)", this));
|
|
}
|
|
|
|
return nsMsgProtocol::SendData(aURL, dataBuffer); // base class actually transmits the data
|
|
}
|
|
|
|
/* gets the response code from the nntp server and the
|
|
* response line
|
|
*
|
|
* returns the TCP return code from the read
|
|
*/
|
|
PRInt32 nsNNTPProtocol::NewsResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
NS_PRECONDITION(nsnull != inputStream, "invalid input stream");
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if(!line)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ); /* don't pause if we got a line */
|
|
|
|
/* almost correct */
|
|
if(status > 1)
|
|
{
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
}
|
|
|
|
m_previousResponseCode = m_responseCode;
|
|
|
|
PR_sscanf(line, "%d", &m_responseCode);
|
|
|
|
if (m_responseCode && PL_strlen(line) > 3)
|
|
NS_MsgSACopy(&m_responseText, line + 4);
|
|
else
|
|
NS_MsgSACopy(&m_responseText, line);
|
|
|
|
/* login failed */
|
|
if (m_responseCode == MK_NNTP_RESPONSE_AUTHINFO_DENIED)
|
|
HandleAuthenticationFailure();
|
|
|
|
|
|
/* authentication required can come at any time
|
|
*/
|
|
if (MK_NNTP_RESPONSE_AUTHINFO_REQUIRE == m_responseCode ||
|
|
MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_REQUIRE == m_responseCode)
|
|
{
|
|
m_nextState = NNTP_BEGIN_AUTHORIZE;
|
|
GotAuthorizationRequest();
|
|
}
|
|
else if (MK_NNTP_RESPONSE_PERMISSION_DENIED == m_responseCode)
|
|
{
|
|
PR_FREEIF(line);
|
|
return (0);
|
|
}
|
|
else {
|
|
m_nextState = m_nextStateAfterResponse;
|
|
}
|
|
|
|
PR_FREEIF(line);
|
|
return(0); /* everything ok */
|
|
}
|
|
|
|
/* interpret the server response after the connect
|
|
*
|
|
* returns negative if the server responds unexpectedly
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::LoginResponse()
|
|
{
|
|
PRBool postingAllowed = m_responseCode == MK_NNTP_RESPONSE_POSTING_ALLOWED;
|
|
|
|
if(MK_NNTP_RESPONSE_TYPE(m_responseCode)!=MK_NNTP_RESPONSE_TYPE_OK)
|
|
{
|
|
AlertError(MK_NNTP_ERROR_MESSAGE, m_responseText);
|
|
|
|
m_nextState = NNTP_ERROR;
|
|
return MK_BAD_NNTP_CONNECTION;
|
|
}
|
|
|
|
m_nntpServer->SetPostingAllowed(postingAllowed);
|
|
m_nextState = NNTP_SEND_MODE_READER;
|
|
return(0); /* good */
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendModeReader()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL,&rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = SendData(mailnewsurl, NNTP_CMD_MODE_READER);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEND_MODE_READER_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
return rv;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendModeReaderResponse()
|
|
{
|
|
SetFlag(NNTP_READER_PERFORMED);
|
|
|
|
/* ignore the response code and continue
|
|
*/
|
|
PRBool pushAuth = PR_FALSE;
|
|
nsresult rv = NS_OK;
|
|
|
|
NS_ASSERTION(m_nntpServer, "no server, see bug #107797");
|
|
if (m_nntpServer) {
|
|
rv = m_nntpServer->GetPushAuth(&pushAuth);
|
|
}
|
|
if (NS_SUCCEEDED(rv) && pushAuth) {
|
|
/* if the news host is set up to require volunteered (pushed) authentication,
|
|
* do that before we do anything else
|
|
*/
|
|
m_nextState = NNTP_BEGIN_AUTHORIZE;
|
|
}
|
|
else {
|
|
#ifdef HAVE_NNTP_EXTENSIONS
|
|
m_nextState = SEND_LIST_EXTENSIONS;
|
|
#else
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
#endif /* HAVE_NNTP_EXTENSIONS */
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListExtensions()
|
|
{
|
|
PRInt32 status = 0;
|
|
nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
|
|
if (url)
|
|
status = SendData(url, NNTP_CMD_LIST_EXTENSIONS);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = SEND_LIST_EXTENSIONS_RESPONSE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListExtensionsResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
if (MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK)
|
|
{
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0]) {
|
|
m_nntpServer->AddExtension(line);
|
|
}
|
|
else
|
|
{
|
|
/* tell libmsg that it's ok to ask this news host for extensions */
|
|
m_nntpServer->SetSupportsExtensions(PR_TRUE);
|
|
/* all extensions received */
|
|
m_nextState = SEND_LIST_SEARCHES;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* LIST EXTENSIONS not recognized
|
|
* tell libmsg not to ask for any more extensions and move on to
|
|
* the real NNTP command we were trying to do. */
|
|
|
|
m_nntpServer->SetSupportsExtensions(PR_FALSE);
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSearches()
|
|
{
|
|
nsresult rv;
|
|
PRBool searchable=PR_FALSE;
|
|
PRInt32 status = 0;
|
|
|
|
rv = m_nntpServer->QueryExtension("SEARCH",&searchable);
|
|
if (NS_SUCCEEDED(rv) && searchable)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, NNTP_CMD_LIST_SEARCHES);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = SEND_LIST_SEARCHES_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
else
|
|
{
|
|
/* since SEARCH isn't supported, move on to GET */
|
|
m_nextState = NNTP_GET_PROPERTIES;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSearchesResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
NS_PRECONDITION(inputStream, "invalid input stream");
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
nsCAutoString charset;
|
|
nsAutoString lineUtf16;
|
|
if (NS_FAILED(m_nntpServer->GetCharset(charset)) ||
|
|
NS_FAILED(nsMsgI18NConvertToUnicode(charset.get(),
|
|
nsDependentCString(line),
|
|
lineUtf16, PR_TRUE)))
|
|
CopyUTF8toUTF16(nsDependentCString(line), lineUtf16);
|
|
|
|
m_nntpServer->AddSearchableGroup(lineUtf16);
|
|
}
|
|
else
|
|
{
|
|
/* all searchable groups received */
|
|
/* LIST SRCHFIELDS is legal if the server supports the SEARCH extension, which */
|
|
/* we already know it does */
|
|
m_nextState = NNTP_LIST_SEARCH_HEADERS;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
PR_FREEIF(line);
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSearchHeaders()
|
|
{
|
|
PRInt32 status = 0;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, NNTP_CMD_LIST_SEARCH_FIELDS);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_LIST_SEARCH_HEADERS_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSearchHeadersResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0])
|
|
m_nntpServer->AddSearchableHeader(line);
|
|
else
|
|
{
|
|
m_nextState = NNTP_GET_PROPERTIES;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
PR_FREEIF(line);
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::GetProperties()
|
|
{
|
|
nsresult rv;
|
|
PRBool setget=PR_FALSE;
|
|
PRInt32 status = 0;
|
|
|
|
rv = m_nntpServer->QueryExtension("SETGET",&setget);
|
|
if (NS_SUCCEEDED(rv) && setget)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, NNTP_CMD_GET_PROPERTIES);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_GET_PROPERTIES_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
else
|
|
{
|
|
/* since GET isn't supported, move on LIST SUBSCRIPTIONS */
|
|
m_nextState = SEND_LIST_SUBSCRIPTIONS;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::GetPropertiesResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
char *propertyName = nsCRT::strdup(line);
|
|
if (propertyName)
|
|
{
|
|
char *space = PL_strchr(propertyName, ' ');
|
|
if (space)
|
|
{
|
|
char *propertyValue = space + 1;
|
|
*space = '\0';
|
|
m_nntpServer->AddPropertyForGet(propertyName, propertyValue);
|
|
}
|
|
PR_Free(propertyName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* all GET properties received, move on to LIST SUBSCRIPTIONS */
|
|
m_nextState = SEND_LIST_SUBSCRIPTIONS;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
PR_FREEIF(line);
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSubscriptions()
|
|
{
|
|
PRInt32 status = 0;
|
|
#if 0
|
|
nsresult rv;
|
|
PRBool searchable=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("LISTSUBSCR",&listsubscr);
|
|
if (NS_SUCCEEDED(rv) && listsubscr)
|
|
#else
|
|
if (0)
|
|
#endif
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, NNTP_CMD_LIST_SUBSCRIPTIONS);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = SEND_LIST_SUBSCRIPTIONS_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
else
|
|
{
|
|
/* since LIST SUBSCRIPTIONS isn't supported, move on to real work */
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListSubscriptionsResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
NS_ASSERTION(0,"fix me");
|
|
#if 0
|
|
char *url = PR_smprintf ("%s//%s/%s", NEWS_SCHEME, m_hostName, line);
|
|
if (url)
|
|
MSG_AddSubscribedNewsgroup (cd->pane, url);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* all default subscriptions received */
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
PR_FREEIF(line);
|
|
return status;
|
|
}
|
|
|
|
/* figure out what the first command is and send it
|
|
*
|
|
* returns the status from the NETWrite */
|
|
|
|
PRInt32 nsNNTPProtocol::SendFirstNNTPCommand(nsIURI * url)
|
|
{
|
|
char *command=0;
|
|
PRInt32 status = 0;
|
|
|
|
if (m_typeWanted == ARTICLE_WANTED) {
|
|
if (m_key != nsMsgKey_None) {
|
|
nsresult rv;
|
|
nsXPIDLCString newsgroupName;
|
|
if (m_newsFolder) {
|
|
rv = m_newsFolder->GetRawName(newsgroupName);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,
|
|
("(%p) current group = %s, desired group = %s", this,
|
|
m_currentGroup.get(), newsgroupName.get()));
|
|
// if the current group is the desired group, we can just issue the ARTICLE command
|
|
// if not, we have to do a GROUP first
|
|
if (!PL_strcmp(m_currentGroup.get(), newsgroupName.get()))
|
|
m_nextState = NNTP_SEND_ARTICLE_NUMBER;
|
|
else
|
|
m_nextState = NNTP_SEND_GROUP_FOR_ARTICLE;
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(m_typeWanted == NEWS_POST)
|
|
{ /* posting to the news group */
|
|
NS_MsgSACopy(&command, "POST");
|
|
}
|
|
else if(m_typeWanted == READ_NEWS_RC)
|
|
{
|
|
/* extract post method from the url when we have it... */
|
|
#ifdef HAVE_NEWS_URL
|
|
if(ce->URL_s->method == URL_POST_METHOD ||
|
|
PL_strchr(ce->URL_s->address, '?'))
|
|
m_nextState = NEWS_NEWS_RC_POST;
|
|
else
|
|
#endif
|
|
m_nextState = NEWS_DISPLAY_NEWS_RC;
|
|
return(0);
|
|
}
|
|
else if(m_typeWanted == NEW_GROUPS)
|
|
{
|
|
PRUint32 last_update;
|
|
nsresult rv;
|
|
|
|
if (!m_nntpServer) {
|
|
NNTP_LOG_NOTE("m_nntpServer is null, panic!");
|
|
return -1;
|
|
}
|
|
rv = m_nntpServer->GetLastUpdatedTime(&last_update);
|
|
char small_buf[64];
|
|
PRExplodedTime expandedTime;
|
|
|
|
if(!last_update)
|
|
{
|
|
AlertError(MK_NNTP_NEWSGROUP_SCAN_ERROR, nsnull);
|
|
m_nextState = NEWS_ERROR;
|
|
return(MK_INTERRUPTED);
|
|
}
|
|
|
|
/* subtract some hours just to be sure */
|
|
last_update -= NEWGROUPS_TIME_OFFSET;
|
|
|
|
{
|
|
PRInt64 secToUSec, timeInSec, timeInUSec;
|
|
LL_I2L(timeInSec, last_update);
|
|
LL_I2L(secToUSec, PR_USEC_PER_SEC);
|
|
LL_MUL(timeInUSec, timeInSec, secToUSec);
|
|
PR_ExplodeTime(timeInUSec, PR_LocalTimeParameters, &expandedTime);
|
|
}
|
|
PR_FormatTimeUSEnglish(small_buf, sizeof(small_buf),
|
|
"NEWGROUPS %y%m%d %H%M%S", &expandedTime);
|
|
|
|
NS_MsgSACopy(&command, small_buf);
|
|
|
|
}
|
|
else if(m_typeWanted == LIST_WANTED)
|
|
{
|
|
nsresult rv;
|
|
|
|
ClearFlag(NNTP_USE_FANCY_NEWSGROUP);
|
|
PRUint32 last_update;
|
|
|
|
NS_ASSERTION(m_nntpServer, "no m_nntpServer");
|
|
if (!m_nntpServer) {
|
|
NNTP_LOG_NOTE("m_nntpServer is null, panic!");
|
|
return -1;
|
|
}
|
|
|
|
rv = m_nntpServer->GetLastUpdatedTime(&last_update);
|
|
if (NS_SUCCEEDED(rv) && last_update!=0)
|
|
{
|
|
m_nextState = DISPLAY_NEWSGROUPS;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
PRBool xactive=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
|
|
if (NS_SUCCEEDED(rv) && xactive)
|
|
{
|
|
NS_MsgSACopy(&command, "LIST XACTIVE");
|
|
SetFlag(NNTP_USE_FANCY_NEWSGROUP);
|
|
}
|
|
else
|
|
{
|
|
NS_MsgSACopy(&command, "LIST");
|
|
}
|
|
}
|
|
}
|
|
else if(m_typeWanted == GROUP_WANTED)
|
|
{
|
|
nsresult rv = NS_ERROR_NULL_POINTER;
|
|
|
|
NS_ASSERTION(m_newsFolder, "m_newsFolder is null, panic!");
|
|
if (!m_newsFolder) return -1;
|
|
|
|
nsXPIDLCString group_name;
|
|
rv = m_newsFolder->GetRawName(group_name);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get newsgroup name");
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
m_firstArticle = 0;
|
|
m_lastArticle = 0;
|
|
|
|
NS_MsgSACopy(&command, "GROUP ");
|
|
NS_MsgSACat(&command, (const char *)group_name);
|
|
}
|
|
else if (m_typeWanted == SEARCH_WANTED)
|
|
{
|
|
nsresult rv;
|
|
PRBool searchable=PR_FALSE;
|
|
if (!m_nntpServer) {
|
|
NNTP_LOG_NOTE("m_nntpServer is null, panic!");
|
|
return -1;
|
|
}
|
|
rv = m_nntpServer->QueryExtension("SEARCH", &searchable);
|
|
if (NS_SUCCEEDED(rv) && searchable)
|
|
{
|
|
/* use the SEARCH extension */
|
|
char *slash = PL_strchr (m_commandSpecificData, '/');
|
|
if (slash)
|
|
{
|
|
char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
|
|
if (allocatedCommand)
|
|
{
|
|
NS_MsgSACopy (&command, allocatedCommand);
|
|
PR_Free(allocatedCommand);
|
|
}
|
|
}
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEARCH_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) doing GROUP for XPAT", this));
|
|
nsXPIDLCString group_name;
|
|
|
|
/* for XPAT, we have to GROUP into the group before searching */
|
|
if (!m_newsFolder) {
|
|
NNTP_LOG_NOTE("m_newsFolder is null, panic!");
|
|
return -1;
|
|
}
|
|
rv = m_newsFolder->GetRawName(group_name);
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
NS_MsgSACopy(&command, "GROUP ");
|
|
NS_MsgSACat (&command, group_name);
|
|
|
|
// force a GROUP next time
|
|
m_currentGroup.Truncate();
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_XPAT_SEND;
|
|
}
|
|
}
|
|
else if (m_typeWanted == PRETTY_NAMES_WANTED)
|
|
{
|
|
nsresult rv;
|
|
PRBool listpretty=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("LISTPRETTY",&listpretty);
|
|
if (NS_SUCCEEDED(rv) && listpretty)
|
|
{
|
|
m_nextState = NNTP_LIST_PRETTY_NAMES;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "unexpected");
|
|
m_nextState = NNTP_ERROR;
|
|
}
|
|
}
|
|
else if (m_typeWanted == PROFILE_WANTED)
|
|
{
|
|
char *slash = PL_strchr (m_commandSpecificData, '/');
|
|
if (slash)
|
|
{
|
|
char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
|
|
if (allocatedCommand)
|
|
{
|
|
NS_MsgSACopy(&command, allocatedCommand);
|
|
PR_Free(allocatedCommand);
|
|
}
|
|
}
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_PROFILE_DELETE_RESPONSE;
|
|
}
|
|
else if (m_typeWanted == IDS_WANTED)
|
|
{
|
|
m_nextState = NNTP_LIST_GROUP;
|
|
return 0;
|
|
}
|
|
else /* article or cancel */
|
|
{
|
|
NS_ASSERTION(m_path, "no m_path, see bugs #57659 and #72317");
|
|
if (!m_path) return -1;
|
|
|
|
if (m_typeWanted == CANCEL_WANTED) {
|
|
NS_MsgSACopy(&command, "HEAD ");
|
|
}
|
|
else {
|
|
NS_ASSERTION(m_typeWanted == ARTICLE_WANTED, "not cancel, and not article");
|
|
NS_MsgSACopy(&command, "ARTICLE ");
|
|
}
|
|
|
|
if (*m_path != '<')
|
|
NS_MsgSACat(&command,"<");
|
|
|
|
NS_MsgSACat(&command, m_path);
|
|
|
|
if (PL_strchr(command+8, '>')==0)
|
|
NS_MsgSACat(&command,">");
|
|
}
|
|
|
|
NS_MsgSACat(&command, CRLF);
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, command);
|
|
PR_Free(command);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
if (m_typeWanted != SEARCH_WANTED && m_typeWanted != PROFILE_WANTED)
|
|
m_nextStateAfterResponse = SEND_FIRST_NNTP_COMMAND_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return(status);
|
|
} /* sent first command */
|
|
|
|
|
|
/* interprets the server response from the first command sent
|
|
*
|
|
* returns negative if the server responds unexpectedly
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::SendFirstNNTPCommandResponse()
|
|
{
|
|
PRInt32 status = 0;
|
|
PRInt32 major_opcode = MK_NNTP_RESPONSE_TYPE(m_responseCode);
|
|
|
|
if((major_opcode == MK_NNTP_RESPONSE_TYPE_CONT &&
|
|
m_typeWanted == NEWS_POST)
|
|
|| (major_opcode == MK_NNTP_RESPONSE_TYPE_OK &&
|
|
m_typeWanted != NEWS_POST) )
|
|
{
|
|
|
|
m_nextState = SETUP_NEWS_STREAM;
|
|
SetFlag(NNTP_SOME_PROTOCOL_SUCCEEDED);
|
|
return(0); /* good */
|
|
}
|
|
else
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
nsXPIDLString group_name;
|
|
NS_ASSERTION(m_newsFolder, "no newsFolder");
|
|
if (m_newsFolder) {
|
|
rv = m_newsFolder->GetUnicodeName(group_name);
|
|
}
|
|
|
|
if (m_responseCode == MK_NNTP_RESPONSE_GROUP_NO_GROUP &&
|
|
m_typeWanted == GROUP_WANTED) {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) group (%s) not found, so unset"
|
|
" m_currentGroup", this,
|
|
NS_ConvertUTF16toUTF8(group_name).get()));
|
|
m_currentGroup.Truncate();
|
|
|
|
m_nntpServer->GroupNotFound(m_msgWindow, group_name, PR_TRUE /* opening */);
|
|
}
|
|
|
|
/* if the server returned a 400 error then it is an expected
|
|
* error. the NEWS_ERROR state will not sever the connection
|
|
*/
|
|
if(major_opcode == MK_NNTP_RESPONSE_TYPE_CANNOT)
|
|
m_nextState = NEWS_ERROR;
|
|
else
|
|
m_nextState = NNTP_ERROR;
|
|
// if we have no channel listener, then we're likely downloading
|
|
// the message for offline use (or at least not displaying it)
|
|
PRBool savingArticleOffline = (m_channelListener == nsnull);
|
|
|
|
if (m_runningURL)
|
|
FinishMemCacheEntry(PR_FALSE); // cleanup mem cache entry
|
|
|
|
if (NS_SUCCEEDED(rv) && group_name && !savingArticleOffline) {
|
|
nsXPIDLString titleStr;
|
|
rv = GetNewsStringByName("htmlNewsErrorTitle", getter_Copies(titleStr));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsXPIDLString newsErrorStr;
|
|
rv = GetNewsStringByName("htmlNewsError", getter_Copies(newsErrorStr));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
nsAutoString errorHtml;
|
|
errorHtml.Append(newsErrorStr);
|
|
|
|
errorHtml.Append(NS_LITERAL_STRING("<b>").get());
|
|
errorHtml.AppendWithConversion(m_responseText);
|
|
errorHtml.Append(NS_LITERAL_STRING("</b><p>").get());
|
|
|
|
rv = GetNewsStringByName("articleExpired", getter_Copies(newsErrorStr));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
errorHtml.Append(newsErrorStr);
|
|
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
if ((m_key != nsMsgKey_None) && m_newsFolder) {
|
|
nsXPIDLCString messageID;
|
|
rv = m_newsFolder->GetMessageIdForKey(m_key, getter_Copies(messageID));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
PR_snprintf(outputBuffer,OUTPUT_BUFFER_SIZE,"<P><%.512s> (%lu)", (const char *)messageID, m_key);
|
|
errorHtml.AppendWithConversion(outputBuffer);
|
|
}
|
|
}
|
|
|
|
if (m_newsFolder) {
|
|
nsCOMPtr <nsIMsgFolder> folder = do_QueryInterface(m_newsFolder, &rv);
|
|
if (NS_SUCCEEDED(rv) && folder) {
|
|
nsXPIDLCString folderURI;
|
|
rv = folder->GetURI(getter_Copies(folderURI));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
PR_snprintf(outputBuffer,OUTPUT_BUFFER_SIZE,"<P> <A HREF=\"%s?list-ids\">", (const char *)folderURI);
|
|
}
|
|
}
|
|
}
|
|
|
|
errorHtml.AppendWithConversion(outputBuffer);
|
|
|
|
rv = GetNewsStringByName("removeExpiredArtLinkText", getter_Copies(newsErrorStr));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
errorHtml.Append(newsErrorStr);
|
|
errorHtml.Append(NS_LITERAL_STRING("</A> </P>").get());
|
|
|
|
if (!m_msgWindow) {
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl) {
|
|
rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
}
|
|
if (!m_msgWindow) return NS_ERROR_FAILURE;
|
|
|
|
// note, this will cause us to close the connection.
|
|
// this will call nsDocShell::LoadURI(), which will
|
|
// call nsDocShell::Stop(STOP_NETWORK), which will eventually
|
|
// call nsNNTPProtocol::Cancel(), which will close the socket.
|
|
// we need to fix this, since the connection is still valid.
|
|
rv = m_msgWindow->DisplayHTMLInMessagePane((const PRUnichar *)titleStr, errorHtml.get(), PR_TRUE);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
// let's take the opportunity of removing the hdr from the db so we don't try to download
|
|
// it again.
|
|
else if (savingArticleOffline)
|
|
{
|
|
if ((m_key != nsMsgKey_None) && (m_newsFolder)) {
|
|
rv = m_newsFolder->RemoveMessage(m_key);
|
|
}
|
|
}
|
|
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
/* start the graph progress indicator
|
|
*/
|
|
NNTP_LOG_NOTE("start the graph progress indicator");
|
|
SetFlag(NNTP_DESTROY_PROGRESS_GRAPH);
|
|
return(status);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendGroupForArticle()
|
|
{
|
|
nsresult rv;
|
|
PRInt32 status = 0;
|
|
|
|
nsXPIDLCString groupname;
|
|
rv = m_newsFolder->GetRawName(groupname);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && groupname.get() && groupname.get()[0], "no group name");
|
|
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"GROUP %.512s" CRLF,
|
|
(const char *)groupname);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return(status);
|
|
}
|
|
|
|
nsresult
|
|
nsNNTPProtocol::SetCurrentGroup()
|
|
{
|
|
nsresult rv;
|
|
nsXPIDLCString groupname;
|
|
NS_ASSERTION(m_newsFolder, "no news folder");
|
|
if (!m_newsFolder) {
|
|
m_currentGroup.Truncate();
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
rv = m_newsFolder->GetRawName(groupname);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && groupname.get()[0], "no group name");
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) SetCurrentGroup to %s",this,(const char *)groupname));
|
|
m_currentGroup = groupname;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendGroupForArticleResponse()
|
|
{
|
|
/* ignore the response code and continue
|
|
*/
|
|
m_nextState = NNTP_SEND_ARTICLE_NUMBER;
|
|
|
|
SetCurrentGroup();
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
PRInt32 nsNNTPProtocol::SendArticleNumber()
|
|
{
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
PRInt32 status = 0;
|
|
PR_snprintf(outputBuffer, OUTPUT_BUFFER_SIZE, "ARTICLE %lu" CRLF, m_key);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = SEND_FIRST_NNTP_COMMAND_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return(status);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::BeginArticle()
|
|
{
|
|
if (m_typeWanted != ARTICLE_WANTED &&
|
|
m_typeWanted != CANCEL_WANTED)
|
|
return 0;
|
|
|
|
/* Set up the HTML stream
|
|
*/
|
|
|
|
#ifdef NO_ARTICLE_CACHEING
|
|
ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
|
|
#endif
|
|
|
|
// if we have a channel listener,
|
|
// create a pipe to pump the message into...the output will go to whoever
|
|
// is consuming the message display
|
|
//
|
|
// the pipe must have an unlimited length since we are going to be filling
|
|
// it on the main thread while reading it from the main thread. iow, the
|
|
// write must not block!! (see bug 190988)
|
|
//
|
|
if (m_channelListener) {
|
|
nsresult rv;
|
|
rv = NS_NewPipe(getter_AddRefs(mDisplayInputStream),
|
|
getter_AddRefs(mDisplayOutputStream),
|
|
4096, PRUint32(-1));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create pipe");
|
|
// TODO: return on failure?
|
|
}
|
|
|
|
m_nextState = NNTP_READ_ARTICLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::DisplayArticle(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 line_length = 0;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
if (m_channelListener)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, line_length, pauseForMoreData, &rv, PR_TRUE);
|
|
if (pauseForMoreData)
|
|
{
|
|
PRUint32 inlength = 0;
|
|
mDisplayInputStream->Available(&inlength);
|
|
if (inlength > 0) // broadcast our batched up ODA changes
|
|
m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_Free(line);
|
|
return line_length;
|
|
}
|
|
|
|
if (m_newsFolder)
|
|
m_newsFolder->NotifyDownloadedLine(line, m_key);
|
|
|
|
// line only contains a single dot -> message end
|
|
if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
|
|
{
|
|
m_nextState = NEWS_DONE;
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
PRUint32 inlength = 0;
|
|
mDisplayInputStream->Available(&inlength);
|
|
if (inlength > 0) // broadcast our batched up ODA changes
|
|
m_channelListener->OnDataAvailable(this, m_channelContext, mDisplayInputStream, 0, inlength);
|
|
PR_Free(line);
|
|
return line_length;
|
|
}
|
|
else // we aren't finished with the message yet
|
|
{
|
|
PRUint32 count = 0;
|
|
|
|
// skip over the quoted '.'
|
|
if (line_length > 1 && line[0] == '.' && line[1] == '.')
|
|
mDisplayOutputStream->Write(line+1, line_length-1, &count);
|
|
else
|
|
mDisplayOutputStream->Write(line, line_length, &count);
|
|
}
|
|
|
|
PR_Free(line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ReadArticle(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
char *outputBuffer;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
|
|
// if we have a channel listener, spool directly to it....
|
|
// otherwise we must be doing something like save to disk or cancel
|
|
// in which case we are doing the work.
|
|
if (m_channelListener)
|
|
return DisplayArticle(inputStream, length);
|
|
|
|
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData, nsnull, PR_TRUE);
|
|
if (m_newsFolder && line)
|
|
m_newsFolder->NotifyDownloadedLine(line, m_key);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if(status > 1)
|
|
{
|
|
#ifdef UNREADY_CODE
|
|
ce->bytes_received += status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s,
|
|
ce->bytes_received, status,
|
|
ce->URL_s->content_length);
|
|
#else
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
#endif
|
|
}
|
|
|
|
if(!line)
|
|
return(status); /* no line yet or error */
|
|
|
|
nsCOMPtr<nsISupports> ctxt = do_QueryInterface(m_runningURL);
|
|
|
|
if (m_typeWanted == CANCEL_WANTED && m_responseCode != MK_NNTP_RESPONSE_ARTICLE_HEAD)
|
|
{
|
|
/* HEAD command failed. */
|
|
PR_FREEIF(line);
|
|
return MK_NNTP_CANCEL_ERROR;
|
|
}
|
|
|
|
if (line[0] == '.' && line[MSG_LINEBREAK_LEN + 1] == 0)
|
|
{
|
|
if (m_typeWanted == CANCEL_WANTED)
|
|
m_nextState = NEWS_START_CANCEL;
|
|
else
|
|
m_nextState = NEWS_DONE;
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
else
|
|
{
|
|
if (line[0] == '.')
|
|
outputBuffer = line + 1;
|
|
else
|
|
outputBuffer = line;
|
|
|
|
/* Don't send content-type to mime parser if we're doing a cancel
|
|
because it confuses mime parser into not parsing.
|
|
*/
|
|
if (m_typeWanted != CANCEL_WANTED || nsCRT::strncmp(outputBuffer, "Content-Type:", 13))
|
|
{
|
|
// if we are attempting to cancel, we want to snarf the headers and save the aside, which is what
|
|
// ParseHeaderForCancel() does.
|
|
if (m_typeWanted == CANCEL_WANTED) {
|
|
ParseHeaderForCancel(outputBuffer);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
PR_Free(line);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nsNNTPProtocol::ParseHeaderForCancel(char *buf)
|
|
{
|
|
nsCAutoString header(buf);
|
|
PRInt32 colon = header.FindChar(':');
|
|
if (!colon)
|
|
return;
|
|
|
|
nsCAutoString value;
|
|
header.Right(value, header.Length() - colon -1);
|
|
value.StripWhitespace();
|
|
|
|
switch (header.First()) {
|
|
case 'F': case 'f':
|
|
if (header.Find("From",PR_TRUE) == 0) {
|
|
PR_FREEIF(m_cancelFromHdr);
|
|
m_cancelFromHdr = ToNewCString(value);
|
|
}
|
|
break;
|
|
case 'M': case 'm':
|
|
if (header.Find("Message-ID",PR_TRUE) == 0) {
|
|
PR_FREEIF(m_cancelID);
|
|
m_cancelID = ToNewCString(value);
|
|
}
|
|
break;
|
|
case 'N': case 'n':
|
|
if (header.Find("Newsgroups",PR_TRUE) == 0) {
|
|
PR_FREEIF(m_cancelNewsgroups);
|
|
m_cancelNewsgroups = ToNewCString(value);
|
|
}
|
|
break;
|
|
case 'D': case 'd':
|
|
if (header.Find("Distributions",PR_TRUE) == 0) {
|
|
PR_FREEIF(m_cancelDistribution);
|
|
m_cancelDistribution = ToNewCString(value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::BeginAuthorization()
|
|
{
|
|
char * command = 0;
|
|
nsXPIDLCString username;
|
|
nsresult rv = NS_OK;
|
|
PRInt32 status = 0;
|
|
nsXPIDLCString cachedUsername;
|
|
|
|
if (!m_newsFolder && m_nntpServer) {
|
|
nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer);
|
|
if (m_nntpServer) {
|
|
nsCOMPtr<nsIMsgFolder> rootFolder;
|
|
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
|
|
if (NS_SUCCEEDED(rv) && rootFolder) {
|
|
m_newsFolder = do_QueryInterface(rootFolder);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(m_newsFolder, "no m_newsFolder");
|
|
if (m_newsFolder)
|
|
rv = m_newsFolder->GetGroupUsername(getter_Copies(cachedUsername));
|
|
|
|
if (NS_FAILED(rv) || !cachedUsername) {
|
|
rv = NS_OK;
|
|
NNTP_LOG_NOTE("ask for the news username");
|
|
|
|
nsXPIDLString usernamePromptText;
|
|
GetNewsStringByName("enterUsername", getter_Copies(usernamePromptText));
|
|
if (m_newsFolder) {
|
|
if (!m_msgWindow) {
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl) {
|
|
rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
|
|
}
|
|
}
|
|
|
|
rv = m_newsFolder->GetGroupUsernameWithUI(usernamePromptText, nsnull, m_msgWindow, getter_Copies(username));
|
|
}
|
|
else {
|
|
#ifdef DEBUG_sspitzer
|
|
printf("we don't know the folder\n");
|
|
printf("this can happen if someone gives us just an article url\n");
|
|
#endif
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
AlertError(MK_NNTP_AUTH_FAILED, "Aborted by user");
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
} // !username
|
|
|
|
if (NS_FAILED(rv) || (!username && !cachedUsername)) {
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
NS_MsgSACopy(&command, "AUTHINFO user ");
|
|
if (cachedUsername) {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, (const char *)cachedUsername));
|
|
NS_MsgSACat(&command, (const char *)cachedUsername);
|
|
}
|
|
else {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, (const char *)username));
|
|
NS_MsgSACat(&command, (const char *)username);
|
|
}
|
|
NS_MsgSACat(&command, CRLF);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, command);
|
|
|
|
PR_Free(command);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_AUTHORIZE_RESPONSE;
|
|
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::AuthorizationResponse()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRInt32 status = 0;
|
|
|
|
|
|
if (MK_NNTP_RESPONSE_AUTHINFO_OK == m_responseCode ||
|
|
MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == m_responseCode)
|
|
{
|
|
/* successful login */
|
|
#ifdef HAVE_NNTP_EXTENSIONS
|
|
PRBool pushAuth;
|
|
/* If we're here because the host demanded authentication before we
|
|
* even sent a single command, then jump back to the beginning of everything
|
|
*/
|
|
rv = m_nntpServer->GetPushAuth(&pushAuth);
|
|
|
|
if (!TestFlag(NNTP_READER_PERFORMED))
|
|
m_nextState = NNTP_SEND_MODE_READER;
|
|
/* If we're here because the host needs pushed authentication, then we
|
|
* should jump back to SEND_LIST_EXTENSIONS
|
|
*/
|
|
else if (NS_SUCCEEDED(rv) && pushAuth)
|
|
m_nextState = SEND_LIST_EXTENSIONS;
|
|
else
|
|
/* Normal authentication */
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
#else
|
|
if (!TestFlag(NNTP_READER_PERFORMED))
|
|
m_nextState = NNTP_SEND_MODE_READER;
|
|
else
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
#endif /* HAVE_NNTP_EXTENSIONS */
|
|
|
|
return(0);
|
|
}
|
|
else if (MK_NNTP_RESPONSE_AUTHINFO_CONT == m_responseCode)
|
|
{
|
|
/* password required */
|
|
char * command = 0;
|
|
nsXPIDLCString password;
|
|
nsXPIDLCString cachedPassword;
|
|
|
|
NS_ASSERTION(m_newsFolder, "no newsFolder");
|
|
if (m_newsFolder) {
|
|
rv = m_newsFolder->GetGroupPassword(getter_Copies(cachedPassword));
|
|
}
|
|
if (NS_FAILED(rv) || !cachedPassword) {
|
|
rv = NS_OK;
|
|
NNTP_LOG_NOTE("ask for the news password");
|
|
|
|
nsXPIDLString passwordPromptText;
|
|
GetNewsStringByName("enterPassword", getter_Copies(passwordPromptText));
|
|
nsXPIDLString passwordPromptTitleText;
|
|
GetNewsStringByName("enterPasswordTitle", getter_Copies(passwordPromptTitleText));
|
|
|
|
NS_ASSERTION(m_newsFolder, "no newsFolder");
|
|
if (m_newsFolder) {
|
|
if (!m_msgWindow) {
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl) {
|
|
rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
|
|
}
|
|
}
|
|
|
|
rv = m_newsFolder->GetGroupPasswordWithUI(passwordPromptText, passwordPromptTitleText, m_msgWindow, getter_Copies(password));
|
|
}
|
|
else {
|
|
NNTP_LOG_NOTE("we don't know the folder");
|
|
NNTP_LOG_NOTE("this can happen if someone gives us just an article url");
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
AlertError(MK_NNTP_AUTH_FAILED,"Aborted by user");
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
}
|
|
|
|
if(NS_FAILED(rv) || (!password && !cachedPassword)) {
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
NS_MsgSACopy(&command, "AUTHINFO pass ");
|
|
if (cachedPassword) {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use cached password", this));
|
|
NS_MsgSACat(&command, (const char *)cachedPassword);
|
|
}
|
|
else {
|
|
// *don't log the password!* PR_LOG(NNTP,PR_LOG_ALWAYS,("use %s as the password",(const char *)password));
|
|
NS_MsgSACat(&command, (const char *)password);
|
|
}
|
|
NS_MsgSACat(&command, CRLF);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, command, PR_TRUE);
|
|
|
|
PR_FREEIF(command);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_PASSWORD_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
/* login failed */
|
|
HandleAuthenticationFailure();
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
NS_ASSERTION(0,"should never get here");
|
|
return(-1);
|
|
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::PasswordResponse()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (MK_NNTP_RESPONSE_AUTHINFO_OK == m_responseCode ||
|
|
MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == m_responseCode)
|
|
{
|
|
/* successful login */
|
|
#ifdef HAVE_NNTP_EXTENSIONS
|
|
PRBool pushAuth;
|
|
/* If we're here because the host demanded authentication before we
|
|
* even sent a single command, then jump back to the beginning of everything
|
|
*/
|
|
rv = m_nntpServer->GetPushAuth(&pushAuth);
|
|
|
|
if (!TestFlag(NNTP_READER_PERFORMED))
|
|
m_nextState = NNTP_SEND_MODE_READER;
|
|
/* If we're here because the host needs pushed authentication, then we
|
|
* should jump back to SEND_LIST_EXTENSIONS
|
|
*/
|
|
else if (NS_SUCCEEDED(rv) && pushAuth)
|
|
m_nextState = SEND_LIST_EXTENSIONS;
|
|
else
|
|
/* Normal authentication */
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
#else
|
|
if (!TestFlag(NNTP_READER_PERFORMED))
|
|
m_nextState = NNTP_SEND_MODE_READER;
|
|
else
|
|
m_nextState = SEND_FIRST_NNTP_COMMAND;
|
|
#endif /* HAVE_NNTP_EXTENSIONS */
|
|
m_nntpServer->SetUserAuthenticated(PR_TRUE);
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
HandleAuthenticationFailure();
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
NS_ASSERTION(0,"should never get here");
|
|
return(-1);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::DisplayNewsgroups()
|
|
{
|
|
m_nextState = NEWS_DONE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) DisplayNewsgroups()",this));
|
|
|
|
return(MK_DATA_LOADED); /* all finished */
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::BeginNewsgroups()
|
|
{
|
|
PRInt32 status = 0;
|
|
m_nextState = NNTP_NEWGROUPS;
|
|
mBytesReceived = 0;
|
|
mBytesReceivedSinceLastStatusUpdate = 0;
|
|
m_startTime = PR_Now();
|
|
return(status);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ProcessNewsgroups(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
char *line, *lineToFree, *s, *s1=NULL, *s2=NULL, *flag=NULL;
|
|
PRInt32 oldest, youngest;
|
|
PRUint32 status = 0;
|
|
nsresult rv = NS_OK;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if(!line)
|
|
return(status); /* no line yet */
|
|
|
|
/* End of list?
|
|
*/
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PRBool xactive=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
|
|
if (NS_SUCCEEDED(rv) && xactive)
|
|
{
|
|
nsCAutoString groupName;
|
|
rv = m_nntpServer->GetFirstGroupNeedingExtraInfo(groupName);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = m_nntpServer->FindGroup(groupName, getter_AddRefs(m_newsFolder));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "FindGroup failed");
|
|
m_nextState = NNTP_LIST_XACTIVE;
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) listing xactive for %s", this,
|
|
groupName.get()));
|
|
PR_Free(lineToFree);
|
|
return 0;
|
|
}
|
|
}
|
|
m_nextState = NEWS_DONE;
|
|
|
|
#ifdef UNREADY_CODE
|
|
if(ce->bytes_received == 0)
|
|
{
|
|
/* #### no new groups */
|
|
}
|
|
#endif
|
|
|
|
PR_Free(lineToFree);
|
|
if(status > 0)
|
|
return MK_DATA_LOADED;
|
|
else
|
|
return status;
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
/* almost correct
|
|
*/
|
|
if(status > 1)
|
|
{
|
|
#ifdef UNREADY_CODE
|
|
ce->bytes_received += status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->content_length);
|
|
#else
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
#endif
|
|
}
|
|
|
|
/* format is "rec.arts.movies.past-films 7302 7119 y"
|
|
*/
|
|
s = PL_strchr (line, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
s1 = s+1;
|
|
s = PL_strchr (s1, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
s2 = s+1;
|
|
s = PL_strchr (s2, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
flag = s+1;
|
|
}
|
|
}
|
|
}
|
|
youngest = s2 ? atol(s1) : 0;
|
|
oldest = s1 ? atol(s2) : 0;
|
|
|
|
#ifdef UNREADY_CODE
|
|
ce->bytes_received++; /* small numbers of groups never seem to trigger this */
|
|
#else
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
#endif
|
|
|
|
NS_ASSERTION(m_nntpServer, "no nntp incoming server");
|
|
if (m_nntpServer) {
|
|
rv = m_nntpServer->AddNewsgroupToList(line);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
|
|
}
|
|
|
|
PRBool xactive=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
|
|
if (NS_SUCCEEDED(rv) && xactive)
|
|
{
|
|
nsCAutoString charset;
|
|
nsAutoString lineUtf16;
|
|
if (NS_SUCCEEDED(m_nntpServer->GetCharset(charset)) &&
|
|
NS_SUCCEEDED(nsMsgI18NConvertToUnicode(charset.get(),
|
|
nsDependentCString(line),
|
|
lineUtf16, PR_TRUE)))
|
|
m_nntpServer->SetGroupNeedsExtraInfo(NS_ConvertUTF16toUTF8(lineUtf16),
|
|
PR_TRUE);
|
|
else
|
|
m_nntpServer->SetGroupNeedsExtraInfo(nsDependentCString(line), PR_TRUE);
|
|
}
|
|
|
|
PR_Free(lineToFree);
|
|
return(status);
|
|
}
|
|
|
|
/* Ahhh, this like print's out the headers and stuff
|
|
*
|
|
* always returns 0
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::BeginReadNewsList()
|
|
{
|
|
m_readNewsListCount = 0;
|
|
mNumGroupsListed = 0;
|
|
m_nextState = NNTP_READ_LIST;
|
|
|
|
mBytesReceived = 0;
|
|
mBytesReceivedSinceLastStatusUpdate = 0;
|
|
m_startTime = PR_Now();
|
|
|
|
PRInt32 status = 0;
|
|
|
|
return(status);
|
|
}
|
|
|
|
#define RATE_CONSTANT 976.5625 /* PR_USEC_PER_SEC / 1024 bytes */
|
|
|
|
static void ComputeRate(PRInt32 bytes, PRTime startTime, float *rate)
|
|
{
|
|
// rate = (bytes / USECS since start) * RATE_CONSTANT
|
|
|
|
// compute usecs since we started.
|
|
PRTime timeSinceStart;
|
|
PRTime now = PR_Now();
|
|
LL_SUB(timeSinceStart, now, startTime);
|
|
|
|
// convert PRTime to PRInt32
|
|
PRInt32 delta;
|
|
LL_L2I(delta, timeSinceStart);
|
|
|
|
// compute rate
|
|
if (delta > 0) {
|
|
*rate = (float) ((bytes * RATE_CONSTANT) / delta);
|
|
}
|
|
else {
|
|
*rate = 0.0;
|
|
}
|
|
}
|
|
|
|
/* display a list of all or part of the newsgroups list
|
|
* from the news server
|
|
*/
|
|
PRInt32 nsNNTPProtocol::ReadNewsList(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRInt32 i=0;
|
|
PRUint32 status = 1;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line, *lineToFree;
|
|
line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if (pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_Free(lineToFree);
|
|
return 0;
|
|
}
|
|
|
|
if (!line)
|
|
return(status); /* no line yet */
|
|
|
|
/* End of list? */
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
PRBool listpnames=PR_FALSE;
|
|
NS_ASSERTION(m_nntpServer, "no nntp incoming server");
|
|
if (m_nntpServer) {
|
|
rv = m_nntpServer->QueryExtension("LISTPNAMES",&listpnames);
|
|
}
|
|
if (NS_SUCCEEDED(rv) && listpnames)
|
|
m_nextState = NNTP_LIST_PRETTY_NAMES;
|
|
else
|
|
m_nextState = DISPLAY_NEWSGROUPS;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_Free(lineToFree);
|
|
return 0;
|
|
}
|
|
else if (line[0] == '.')
|
|
{
|
|
if ((line[1] == ' ') || (line[1] == '.' && line [2] == '.' && line[3] == ' '))
|
|
{
|
|
// some servers send "... 0000000001 0000000002 y"
|
|
// and some servers send ". 0000000001 0000000002 y"
|
|
// just skip that those lines
|
|
// see bug #69231 and #123560
|
|
PR_Free(lineToFree);
|
|
return status;
|
|
}
|
|
// The NNTP server quotes all lines beginning with "." by doubling it, so unquote
|
|
line++;
|
|
}
|
|
|
|
/* almost correct
|
|
*/
|
|
if(status > 1)
|
|
{
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
|
|
if ((mBytesReceivedSinceLastStatusUpdate > UPDATE_THRESHHOLD) && m_msgWindow) {
|
|
mBytesReceivedSinceLastStatusUpdate = 0;
|
|
|
|
nsCOMPtr <nsIMsgStatusFeedback> msgStatusFeedback;
|
|
|
|
rv = m_msgWindow->GetStatusFeedback(getter_AddRefs(msgStatusFeedback));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsXPIDLString statusString;
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString bytesStr;
|
|
bytesStr.AppendInt(mBytesReceived / 1024);
|
|
|
|
// compute the rate, and then convert it have one
|
|
// decimal precision.
|
|
float rate = 0.0;
|
|
ComputeRate(mBytesReceived, m_startTime, &rate);
|
|
char rate_buf[RATE_STR_BUF_LEN];
|
|
PR_snprintf(rate_buf,RATE_STR_BUF_LEN,"%.1f", rate);
|
|
|
|
nsAutoString rateStr;
|
|
rateStr.AppendWithConversion(rate_buf);
|
|
|
|
nsAutoString numGroupsStr;
|
|
numGroupsStr.AppendInt(mNumGroupsListed);
|
|
|
|
const PRUnichar *formatStrings[3] = { numGroupsStr.get(), bytesStr.get(), rateStr.get() };
|
|
rv = bundle->FormatStringFromName(NS_LITERAL_STRING("bytesReceived").get(),
|
|
formatStrings, 3,
|
|
getter_Copies(statusString));
|
|
|
|
rv = msgStatusFeedback->ShowStatusString(statusString);
|
|
if (NS_FAILED(rv)) {
|
|
PR_Free(lineToFree);
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* find whitespace separator if it exits */
|
|
for(i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
|
|
; /* null body */
|
|
|
|
char *description;
|
|
if(line[i] == '\0')
|
|
description = &line[i];
|
|
else
|
|
description = &line[i+1];
|
|
|
|
line[i] = 0; /* terminate group name */
|
|
|
|
/* store all the group names */
|
|
NS_ASSERTION(m_nntpServer, "no nntp incoming server");
|
|
if (m_nntpServer) {
|
|
m_readNewsListCount++;
|
|
mNumGroupsListed++;
|
|
rv = m_nntpServer->AddNewsgroupToList(line);
|
|
// NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
|
|
// since it's not fatal, don't let this error stop the LIST command.
|
|
rv = NS_OK;
|
|
}
|
|
else {
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (m_readNewsListCount == READ_NEWS_LIST_COUNT_MAX) {
|
|
m_readNewsListCount = 0;
|
|
if (mUpdateTimer) {
|
|
mUpdateTimer->Cancel();
|
|
mUpdateTimer = nsnull;
|
|
}
|
|
mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to create timer");
|
|
if (NS_FAILED(rv)) {
|
|
PR_Free(lineToFree);
|
|
return -1;
|
|
}
|
|
|
|
mInputStream = inputStream;
|
|
|
|
const PRUint32 kUpdateTimerDelay = READ_NEWS_LIST_TIMEOUT;
|
|
rv = mUpdateTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback*,this), kUpdateTimerDelay,
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to init timer");
|
|
if (NS_FAILED(rv)) {
|
|
PR_Free(lineToFree);
|
|
return -1;
|
|
}
|
|
|
|
m_nextState = NEWS_FINISHED;
|
|
|
|
// suspend necko request until timeout
|
|
// might not have a request if someone called CloseSocket()
|
|
// see bug #195440
|
|
if (m_request)
|
|
m_request->Suspend();
|
|
}
|
|
|
|
PR_Free(lineToFree);
|
|
if (NS_FAILED(rv))
|
|
return -1;
|
|
return(status);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNNTPProtocol::Notify(nsITimer *timer)
|
|
{
|
|
NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
|
|
mUpdateTimer = nsnull; // release my hold
|
|
TimerCallback();
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsNNTPProtocol::TimerCallback()
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("nsNNTPProtocol::TimerCallback\n"));
|
|
m_nextState = NNTP_READ_LIST;
|
|
|
|
// process whatever is already in the buffer at least once.
|
|
//
|
|
// NOTE: while downloading, it would almost be enough to just
|
|
// resume necko since it will call us again with data. however,
|
|
// if we are at the end of the data stream then we must call
|
|
// ProcessProtocolState since necko will not call us again.
|
|
//
|
|
// NOTE: this function may Suspend necko. Suspend is a reference
|
|
// counted (i.e., two suspends requires two resumes before the
|
|
// request will actually be resumed).
|
|
//
|
|
ProcessProtocolState(nsnull, mInputStream, 0,0);
|
|
|
|
// resume necko request
|
|
// might not have a request if someone called CloseSocket()
|
|
// see bug #195440
|
|
if (m_request)
|
|
m_request->Resume();
|
|
|
|
return;
|
|
}
|
|
|
|
void nsNNTPProtocol::HandleAuthenticationFailure()
|
|
{
|
|
PRBool userHasAuthenticatedInThisSession;
|
|
m_nntpServer->GetUserAuthenticated(&userHasAuthenticatedInThisSession);
|
|
|
|
/* login failed */
|
|
AlertError(MK_NNTP_AUTH_FAILED, m_responseText);
|
|
|
|
NS_ASSERTION(m_newsFolder, "no newsFolder");
|
|
if (m_newsFolder)
|
|
{
|
|
if (!userHasAuthenticatedInThisSession)
|
|
{
|
|
(void) m_newsFolder->ForgetGroupUsername();
|
|
(void) m_newsFolder->ForgetGroupPassword();
|
|
}
|
|
// we'll allow one failure before clearing out password,
|
|
// but we need to handle the case where the password has
|
|
// changed while the app is running, and
|
|
// reprompt the user for the (potentially) new password.
|
|
// So clear the userAuthenticated flag.
|
|
m_nntpServer->SetUserAuthenticated(PR_FALSE);
|
|
}
|
|
}
|
|
|
|
/* start the xover command
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::BeginReadXover()
|
|
{
|
|
PRInt32 count; /* Response fields */
|
|
nsresult rv = NS_OK;
|
|
|
|
rv = SetCurrentGroup();
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
/* Make sure we never close and automatically reopen the connection at this
|
|
point; we'll confuse libmsg too much... */
|
|
|
|
SetFlag(NNTP_SOME_PROTOCOL_SUCCEEDED);
|
|
|
|
/* We have just issued a GROUP command and read the response.
|
|
Now parse that response to help decide which articles to request
|
|
xover data for.
|
|
*/
|
|
PR_sscanf(m_responseText,
|
|
"%d %d %d",
|
|
&count,
|
|
&m_firstPossibleArticle,
|
|
&m_lastPossibleArticle);
|
|
|
|
m_newsgroupList = do_CreateInstance(NS_NNTPNEWSGROUPLIST_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
rv = m_newsgroupList->Initialize(m_runningURL, m_newsFolder);
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
rv = m_newsFolder->UpdateSummaryFromNNTPInfo(m_firstPossibleArticle, m_lastPossibleArticle, count);
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
m_numArticlesLoaded = 0;
|
|
|
|
// if the user sets max_articles to a bogus value, get them everything
|
|
m_numArticlesWanted = m_maxArticles > 0 ? m_maxArticles : 1L << 30;
|
|
|
|
m_nextState = NNTP_FIGURE_NEXT_CHUNK;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::FigureNextChunk()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRInt32 status = 0;
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (m_firstArticle > 0)
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) add to known articles: %d - %d", this, m_firstArticle, m_lastArticle));
|
|
|
|
if (NS_SUCCEEDED(rv) && m_newsgroupList) {
|
|
rv = m_newsgroupList->AddToKnownArticles(m_firstArticle,
|
|
m_lastArticle);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) return status;
|
|
}
|
|
|
|
if (m_numArticlesLoaded >= m_numArticlesWanted)
|
|
{
|
|
m_nextState = NEWS_PROCESS_XOVER;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
NS_ASSERTION(m_newsgroupList, "no newsgroupList");
|
|
if (!m_newsgroupList) return -1;
|
|
|
|
PRBool getOldMessages = PR_FALSE;
|
|
if (m_runningURL) {
|
|
rv = m_runningURL->GetGetOldMessages(&getOldMessages);
|
|
if (NS_FAILED(rv)) return status;
|
|
}
|
|
|
|
rv = m_newsgroupList->SetGetOldMessages(getOldMessages);
|
|
if (NS_FAILED(rv)) return status;
|
|
|
|
rv = m_newsgroupList->GetRangeOfArtsToDownload(m_msgWindow,
|
|
m_firstPossibleArticle,
|
|
m_lastPossibleArticle,
|
|
m_numArticlesWanted -
|
|
m_numArticlesLoaded,
|
|
&(m_firstArticle),
|
|
&(m_lastArticle),
|
|
&status);
|
|
|
|
if (NS_FAILED(rv)) return status;
|
|
|
|
if (m_firstArticle <= 0 || m_firstArticle > m_lastArticle)
|
|
{
|
|
/* Nothing more to get. */
|
|
m_nextState = NEWS_PROCESS_XOVER;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) Chunk will be (%d-%d)", this, m_firstArticle, m_lastArticle));
|
|
|
|
m_articleNumber = m_firstArticle;
|
|
|
|
/* was MSG_InitXOVER() */
|
|
if (m_newsgroupList) {
|
|
rv = m_newsgroupList->InitXOVER(m_firstArticle, m_lastArticle);
|
|
}
|
|
|
|
/* convert nsresult->status */
|
|
status = NS_FAILED(rv);
|
|
|
|
if (status < 0)
|
|
return status;
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
if (TestFlag(NNTP_NO_XOVER_SUPPORT))
|
|
m_nextState = NNTP_READ_GROUP;
|
|
else
|
|
m_nextState = NNTP_XOVER_SEND;
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::XoverSend()
|
|
{
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
PRInt32 status = 0;
|
|
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"XOVER %d-%d" CRLF,
|
|
m_firstArticle,
|
|
m_lastArticle);
|
|
|
|
NNTP_LOG_WRITE(outputBuffer);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_XOVER_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
return status;
|
|
}
|
|
|
|
/* see if the xover response is going to return us data
|
|
* if the proper code isn't returned then assume xover
|
|
* isn't supported and use
|
|
* normal read_group
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::ReadXoverResponse()
|
|
{
|
|
#ifdef TEST_NO_XOVER_SUPPORT
|
|
m_responseCode = MK_NNTP_RESPONSE_CHECK_ERROR; /* pretend XOVER generated an error */
|
|
#endif
|
|
|
|
if(m_responseCode != MK_NNTP_RESPONSE_XOVER_OK)
|
|
{
|
|
/* If we didn't get back "224 data follows" from the XOVER request,
|
|
then that must mean that this server doesn't support XOVER. Or
|
|
maybe the server's XOVER support is busted or something. So,
|
|
in that case, fall back to the very slow HEAD method.
|
|
|
|
But, while debugging here at HQ, getting into this state means
|
|
something went very wrong, since our servers do XOVER. Thus
|
|
the assert.
|
|
*/
|
|
/*NS_ASSERTION (0,"something went very wrong");*/
|
|
m_nextState = NNTP_READ_GROUP;
|
|
SetFlag(NNTP_NO_XOVER_SUPPORT);
|
|
}
|
|
else
|
|
{
|
|
m_nextState = NNTP_XOVER;
|
|
}
|
|
|
|
return(0); /* continue */
|
|
}
|
|
|
|
/* process the xover list as it comes from the server
|
|
* and load it into the sort list.
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::ReadXover(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
char *line, *lineToFree;
|
|
nsresult rv;
|
|
PRUint32 status = 1;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if(!line)
|
|
return(status); /* no line yet or TCP error */
|
|
|
|
if(line[0] == '.' && line[1] == '\0')
|
|
{
|
|
m_nextState = NNTP_FIGURE_NEXT_CHUNK;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_Free(lineToFree);
|
|
return(0);
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
/* almost correct
|
|
*/
|
|
if(status > 1)
|
|
{
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
}
|
|
|
|
rv = m_newsgroupList->ProcessXOVERLINE(line, &status);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to process the XOVERLINE");
|
|
|
|
m_numArticlesLoaded++;
|
|
PR_Free(lineToFree);
|
|
return NS_SUCCEEDED(rv) ? status : -1; /* keep going if no error */
|
|
}
|
|
|
|
/* Finished processing all the XOVER data.
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::ProcessXover()
|
|
{
|
|
nsresult rv;
|
|
|
|
/* xover_parse_state stored in MSG_Pane cd->pane */
|
|
NS_ASSERTION(m_newsgroupList, "no newsgroupList");
|
|
if (!m_newsgroupList) return -1;
|
|
|
|
PRInt32 status = 0;
|
|
rv = m_newsgroupList->FinishXOVERLINE(0,&status);
|
|
m_newsgroupList = nsnull;
|
|
if (NS_SUCCEEDED(rv) && status < 0) return status;
|
|
|
|
m_nextState = NEWS_DONE;
|
|
|
|
return(MK_DATA_LOADED);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ReadNewsgroup()
|
|
{
|
|
if(m_articleNumber > m_lastArticle)
|
|
{ /* end of groups */
|
|
|
|
m_nextState = NNTP_FIGURE_NEXT_CHUNK;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"HEAD %ld" CRLF,
|
|
m_articleNumber++);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_READ_GROUP_RESPONSE;
|
|
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
return SendData(mailnewsurl, outputBuffer);
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* See if the "HEAD" command was successful
|
|
*/
|
|
|
|
PRInt32 nsNNTPProtocol::ReadNewsgroupResponse()
|
|
{
|
|
nsresult rv;
|
|
|
|
if (m_responseCode == MK_NNTP_RESPONSE_ARTICLE_HEAD)
|
|
{ /* Head follows - parse it:*/
|
|
m_nextState = NNTP_READ_GROUP_BODY;
|
|
|
|
if(m_messageID)
|
|
*m_messageID = '\0';
|
|
|
|
m_key = nsMsgKey_None;
|
|
|
|
/* Give the message number to the header parser. */
|
|
rv = m_newsgroupList->ProcessNonXOVER(m_responseText);
|
|
/* convert nsresult->status */
|
|
return NS_FAILED(rv);
|
|
}
|
|
else
|
|
{
|
|
NNTP_LOG_NOTE(("Bad group header found!"));
|
|
m_nextState = NNTP_READ_GROUP;
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* read the body of the "HEAD" command
|
|
*/
|
|
PRInt32 nsNNTPProtocol::ReadNewsgroupBody(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
char *line, *lineToFree;
|
|
nsresult rv;
|
|
PRUint32 status = 1;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
line = lineToFree = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
/* if TCP error of if there is not a full line yet return
|
|
*/
|
|
if(!line)
|
|
return status;
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) read_group_body: got line: %s|",this,line));
|
|
|
|
/* End of body? */
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
m_nextState = NNTP_READ_GROUP;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
rv = m_newsgroupList->ProcessNonXOVER(line);
|
|
/* convert nsresult->status */
|
|
PR_Free(lineToFree);
|
|
return NS_FAILED(rv);
|
|
}
|
|
|
|
|
|
nsresult nsNNTPProtocol::GetNewsStringByID(PRInt32 stringID, PRUnichar **aString)
|
|
{
|
|
nsresult rv;
|
|
nsAutoString resultString(NS_LITERAL_STRING("???"));
|
|
|
|
if (!m_stringBundle)
|
|
{
|
|
char* propertyURL = NEWS_MSGS_URL;
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = bundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (m_stringBundle) {
|
|
PRUnichar *ptrv = nsnull;
|
|
rv = m_stringBundle->GetStringFromID(stringID, &ptrv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
resultString.AssignLiteral("[StringID");
|
|
resultString.AppendInt(stringID);
|
|
resultString.AppendLiteral("?]");
|
|
*aString = ToNewUnicode(resultString);
|
|
}
|
|
else {
|
|
*aString = ptrv;
|
|
}
|
|
}
|
|
else {
|
|
rv = NS_OK;
|
|
*aString = ToNewUnicode(resultString);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::GetNewsStringByName(const char *aName, PRUnichar **aString)
|
|
{
|
|
nsresult rv;
|
|
nsAutoString resultString(NS_LITERAL_STRING("???"));
|
|
if (!m_stringBundle)
|
|
{
|
|
char* propertyURL = NEWS_MSGS_URL;
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = bundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
|
|
}
|
|
|
|
if (m_stringBundle)
|
|
{
|
|
nsAutoString unicodeName; unicodeName.AssignWithConversion(aName);
|
|
|
|
PRUnichar *ptrv = nsnull;
|
|
rv = m_stringBundle->GetStringFromName(unicodeName.get(), &ptrv);
|
|
|
|
if (NS_FAILED(rv))
|
|
{
|
|
resultString.AssignLiteral("[StringName");
|
|
resultString.AppendWithConversion(aName);
|
|
resultString.AppendLiteral("?]");
|
|
*aString = ToNewUnicode(resultString);
|
|
}
|
|
else
|
|
{
|
|
*aString = ptrv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rv = NS_OK;
|
|
*aString = ToNewUnicode(resultString);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// sspitzer: PostMessageInFile is derived from nsSmtpProtocol::SendMessageInFile()
|
|
PRInt32 nsNNTPProtocol::PostMessageInFile(nsIFileSpec *aPostMessageFile)
|
|
{
|
|
nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
|
|
if (url && aPostMessageFile)
|
|
nsMsgProtocol::PostMessage(url, aPostMessageFile);
|
|
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
// for now, we are always done at this point..we aren't making multiple
|
|
// calls to post data...
|
|
|
|
// always issue a '.' and CRLF when we are done...
|
|
PL_strcpy(m_dataBuf, "." CRLF);
|
|
if (url)
|
|
SendData(url, m_dataBuf);
|
|
#ifdef UNREADY_CODE
|
|
NET_Progress(CE_WINDOW_ID,
|
|
XP_GetString(XP_MESSAGE_SENT_WAITING_MAIL_REPLY));
|
|
#endif /* UNREADY_CODE */
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
|
|
return(0);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::PostData()
|
|
{
|
|
/* returns 0 on done and negative on error
|
|
* positive if it needs to continue.
|
|
*/
|
|
NNTP_LOG_NOTE("nsNNTPProtocol::PostData()");
|
|
nsresult rv = NS_OK;
|
|
|
|
nsCOMPtr <nsINNTPNewsgroupPost> message;
|
|
rv = m_runningURL->GetMessageToPost(getter_AddRefs(message));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsIFileSpec> filePath;
|
|
rv = message->GetPostMessageFile(getter_AddRefs(filePath));
|
|
if (NS_SUCCEEDED(rv))
|
|
PostMessageInFile(filePath);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* interpret the response code from the server
|
|
* after the post is done
|
|
*/
|
|
PRInt32 nsNNTPProtocol::PostDataResponse()
|
|
{
|
|
if (m_responseCode != MK_NNTP_RESPONSE_POST_OK)
|
|
{
|
|
AlertError(MK_NNTP_ERROR_MESSAGE,m_responseText);
|
|
m_nextState = NEWS_ERROR;
|
|
return(MK_NNTP_ERROR_MESSAGE);
|
|
}
|
|
m_nextState = NEWS_POST_DONE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return(MK_DATA_LOADED);
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::CheckForArticle()
|
|
{
|
|
m_nextState = NEWS_ERROR;
|
|
if (m_responseCode >= 220 && m_responseCode <= 223) {
|
|
/* Yes, this article is already there, we're all done. */
|
|
return MK_DATA_LOADED;
|
|
}
|
|
else
|
|
{
|
|
/* The article isn't there, so the failure we had earlier wasn't due to
|
|
a duplicate message-id. Return the error from that previous
|
|
posting attempt (which is already in ce->URL_s->error_msg). */
|
|
return MK_NNTP_ERROR_MESSAGE;
|
|
}
|
|
}
|
|
|
|
#define NEWS_GROUP_DISPLAY_FREQ 1
|
|
|
|
nsresult
|
|
nsNNTPProtocol::SetCheckingForNewNewsStatus(PRInt32 current, PRInt32 total)
|
|
{
|
|
nsresult rv;
|
|
nsXPIDLString statusString;
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_nntpServer, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsXPIDLCString hostName;
|
|
rv = server->GetHostName(getter_Copies(hostName));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString thisGroupStr;
|
|
thisGroupStr.AppendInt(current);
|
|
|
|
nsAutoString totalGroupStr;
|
|
totalGroupStr.AppendInt(total);
|
|
|
|
nsAutoString hostNameStr;
|
|
hostNameStr.AssignWithConversion(hostName);
|
|
|
|
const PRUnichar *formatStrings[] = { thisGroupStr.get(), totalGroupStr.get(), hostNameStr.get() };
|
|
|
|
rv = bundle->FormatStringFromName(NS_LITERAL_STRING("checkingForNewNews").get(),
|
|
formatStrings, 3,
|
|
getter_Copies(statusString));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = SetProgressStatus(statusString);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
SetProgressBarPercent(current, total);
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::GetNextGroupNeedingCounts( nsISupports** pNextGroup, PRInt32* returnStatus )
|
|
{
|
|
|
|
nsresult rv = m_nntpServer->GetFirstGroupNeedingCounts( pNextGroup );
|
|
if (NS_FAILED(rv)) {
|
|
ClearFlag(NNTP_NEWSRC_PERFORMED);
|
|
*returnStatus = -1;
|
|
return rv;
|
|
}
|
|
else if (!*pNextGroup) {
|
|
ClearFlag(NNTP_NEWSRC_PERFORMED);
|
|
m_nextState = NEWS_DONE;
|
|
|
|
if (m_newsRCListCount) {
|
|
// clear the status text.
|
|
rv = SetProgressStatus(EmptyString().get());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
SetProgressBarPercent(0, -1);
|
|
m_newsRCListCount = 0;
|
|
*returnStatus = 0;
|
|
}
|
|
else if (m_responseCode == MK_NNTP_RESPONSE_LIST_OK) {
|
|
/*
|
|
* 5-9-96 jefft
|
|
* If for some reason the news server returns an empty
|
|
* newsgroups list with a nntp response code MK_NNTP_RESPONSE_LIST_OK -- list of
|
|
* newsgroups follows. We set status to MK_EMPTY_NEWS_LIST
|
|
* to end the infinite dialog loop.
|
|
*/
|
|
*returnStatus = MK_EMPTY_NEWS_LIST;
|
|
}
|
|
|
|
if(*returnStatus > -1)
|
|
*returnStatus = MK_DATA_LOADED;
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::DisplayNewsRC()
|
|
{
|
|
PRInt32 status = 0;
|
|
nsresult rv;
|
|
|
|
if(!TestFlag(NNTP_NEWSRC_PERFORMED)) {
|
|
SetFlag(NNTP_NEWSRC_PERFORMED);
|
|
rv = m_nntpServer->GetNumGroupsNeedingCounts(&m_newsRCListCount);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
nsCOMPtr <nsISupports> currChild;
|
|
|
|
PRInt32 groupsToAdvance = m_RCIndexToResumeAfterAuthRequest + 1;
|
|
m_RCIndexToResumeAfterAuthRequest = 0;
|
|
// if we entered this method with a m_RCIndexToResumeAfterAuthRequest > 0, then this is a
|
|
// re-incarnation: we already did a run before, but it was disrupted by
|
|
// an authorization request (see GotAuthorizationRequest)
|
|
while ( groupsToAdvance-- )
|
|
if ( NS_FAILED( GetNextGroupNeedingCounts( getter_AddRefs( currChild ), &status ) ) )
|
|
return status;
|
|
|
|
nsCOMPtr<nsIMsgFolder> currFolder = do_QueryInterface(currChild, &rv);
|
|
if (NS_FAILED(rv)) return -1;
|
|
if (!currFolder) return -1;
|
|
|
|
m_newsFolder = do_QueryInterface(currFolder, &rv);
|
|
if (NS_FAILED(rv)) return -1;
|
|
if (!m_newsFolder) return -1;
|
|
|
|
nsXPIDLCString name;
|
|
rv = m_newsFolder->GetRawName(name);
|
|
if (NS_FAILED(rv)) return -1;
|
|
if (!name) return -1;
|
|
|
|
/* send group command to server */
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
PR_snprintf(outputBuffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF, (const char *)name);
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl) {
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
}
|
|
|
|
/* only update every NEWS_GROUP_DISPLAY_FREQ groups for speed */
|
|
if ((m_newsRCListCount >= NEWS_GROUP_DISPLAY_FREQ) && ((m_newsRCListIndex % NEWS_GROUP_DISPLAY_FREQ) == 0 || ((m_newsRCListIndex+1) == m_newsRCListCount))) {
|
|
rv = SetCheckingForNewNewsStatus(m_newsRCListIndex+1, m_newsRCListCount);
|
|
if (NS_FAILED(rv)) return -1;
|
|
}
|
|
|
|
m_newsRCListIndex++;
|
|
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NEWS_DISPLAY_NEWS_RC_RESPONSE;
|
|
|
|
return status; /* keep going */
|
|
}
|
|
|
|
/* Parses output of GROUP command */
|
|
PRInt32 nsNNTPProtocol::DisplayNewsRCResponse()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRInt32 status = 0;
|
|
if(m_responseCode == MK_NNTP_RESPONSE_GROUP_SELECTED)
|
|
{
|
|
char *num_arts = 0, *low = 0, *high = 0, *group = 0;
|
|
PRInt32 first_art, last_art;
|
|
|
|
/* line looks like:
|
|
* 211 91 3693 3789 comp.infosystems
|
|
*/
|
|
|
|
num_arts = m_responseText;
|
|
low = PL_strchr(num_arts, ' ');
|
|
|
|
if(low)
|
|
{
|
|
first_art = atol(low);
|
|
*low++ = '\0';
|
|
high= PL_strchr(low, ' ');
|
|
}
|
|
if(high)
|
|
{
|
|
*high++ = '\0';
|
|
group = PL_strchr(high, ' ');
|
|
}
|
|
if(group)
|
|
{
|
|
*group++ = '\0';
|
|
/* the group name may be contaminated by "group selected" at
|
|
the end. This will be space separated from the group name.
|
|
If a space is found in the group name terminate at that
|
|
point. */
|
|
strtok(group, " ");
|
|
last_art = atol(high);
|
|
}
|
|
|
|
// this might save us a GROUP command, if the user reads a message in the
|
|
// last group we update.
|
|
m_currentGroup = group;
|
|
|
|
// prevent crash when
|
|
// if going offline in the middle of
|
|
// updating the unread counts on a news server
|
|
// (running a "news://host/*" url)
|
|
NS_ASSERTION(m_nntpServer,"no server");
|
|
if (!m_nntpServer) return -1;
|
|
|
|
rv = m_nntpServer->DisplaySubscribedGroup(m_newsFolder,
|
|
low ? atol(low) : 0,
|
|
high ? atol(high) : 0,
|
|
atol(num_arts));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"DisplaySubscribedGroup() failed");
|
|
if (NS_FAILED(rv)) status = -1;
|
|
|
|
if (status < 0) return status;
|
|
}
|
|
else if (m_responseCode == MK_NNTP_RESPONSE_GROUP_NO_GROUP)
|
|
{
|
|
nsXPIDLString name;
|
|
rv = m_newsFolder->GetUnicodeName(name);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
m_nntpServer->GroupNotFound(m_msgWindow, name, PR_FALSE);
|
|
}
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) NO_GROUP, so unset m_currentGroup", this));
|
|
m_currentGroup.Truncate();
|
|
}
|
|
/* it turns out subscribe ui depends on getting this displaysubscribedgroup call,
|
|
even if there was an error.
|
|
*/
|
|
if(m_responseCode != MK_NNTP_RESPONSE_GROUP_SELECTED)
|
|
{
|
|
/* only on news server error or when zero articles
|
|
*/
|
|
#ifdef DEBUG_seth
|
|
NS_ASSERTION(PR_FALSE,"check this code");
|
|
#endif
|
|
rv = m_nntpServer->DisplaySubscribedGroup(m_newsFolder, 0, 0, 0);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"DisplaySubscribedGroup() failed");
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) error, so unset m_currentGroup", this));
|
|
m_currentGroup.Truncate();
|
|
}
|
|
|
|
m_nextState = NEWS_DISPLAY_NEWS_RC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::StartCancel()
|
|
{
|
|
PRInt32 status = 0;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, NNTP_CMD_POST);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NEWS_DO_CANCEL;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return (status);
|
|
}
|
|
|
|
PRBool nsNNTPProtocol::CheckIfAuthor(nsISupports *aElement, void *data)
|
|
{
|
|
nsresult rv;
|
|
|
|
cancelInfoEntry *cancelInfo = (cancelInfoEntry*) data;
|
|
|
|
if (cancelInfo->from) {
|
|
// already found a match, no need to go any further
|
|
// keep going
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsCOMPtr<nsIMsgIdentity> identity = do_QueryInterface(aElement, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
// keep going
|
|
return PR_TRUE;
|
|
}
|
|
|
|
if (identity) {
|
|
identity->GetEmail(&cancelInfo->from);
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("from = %s", cancelInfo->from));
|
|
}
|
|
|
|
nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
PR_FREEIF(cancelInfo->from);
|
|
cancelInfo->from = nsnull;
|
|
|
|
// keep going
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsXPIDLCString us;
|
|
nsXPIDLCString them;
|
|
nsresult rv1 = parser->ExtractHeaderAddressMailboxes(nsnull, cancelInfo->from, getter_Copies(us));
|
|
nsresult rv2 = parser->ExtractHeaderAddressMailboxes(nsnull, cancelInfo->old_from, getter_Copies(them));
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("us = %s, them = %s", us.get(), them.get()));
|
|
|
|
if ((NS_FAILED(rv1) || NS_FAILED(rv2) || PL_strcasecmp(us, them))) {
|
|
//no match. don't set cancel email
|
|
PR_FREEIF(cancelInfo->from);
|
|
cancelInfo->from = nsnull;
|
|
|
|
// keep going
|
|
return PR_TRUE;
|
|
}
|
|
else {
|
|
|
|
// we have a match, stop.
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::DoCancel()
|
|
{
|
|
PRInt32 status = 0;
|
|
PRBool failure = PR_FALSE;
|
|
nsresult rv = NS_OK;
|
|
char *id = nsnull;
|
|
char *subject = nsnull;
|
|
char *newsgroups = nsnull;
|
|
char *distribution = nsnull;
|
|
char *body = nsnull;
|
|
cancelInfoEntry cancelInfo;
|
|
PRBool requireConfirmationForCancel = PR_TRUE;
|
|
PRBool showAlertAfterCancel = PR_TRUE;
|
|
|
|
int L;
|
|
|
|
/* #### Should we do a more real check than this? If the POST command
|
|
didn't respond with "MK_NNTP_RESPONSE_POST_SEND_NOW Ok", then it's not ready for us to throw a
|
|
message at it... But the normal posting code doesn't do this check.
|
|
Why?
|
|
*/
|
|
NS_ASSERTION (m_responseCode == MK_NNTP_RESPONSE_POST_SEND_NOW, "code != POST_SEND_NOW");
|
|
|
|
// These shouldn't be set yet, since the headers haven't been "flushed"
|
|
// "Distribution: " doesn't appear to be required, so
|
|
// don't assert on m_cancelDistribution
|
|
NS_ASSERTION (m_cancelID &&
|
|
m_cancelFromHdr &&
|
|
m_cancelNewsgroups, "null ptr");
|
|
|
|
newsgroups = m_cancelNewsgroups;
|
|
distribution = m_cancelDistribution;
|
|
id = m_cancelID;
|
|
cancelInfo.old_from = m_cancelFromHdr;
|
|
cancelInfo.from = nsnull;
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCOMPtr<nsIPrompt> dialog;
|
|
if (m_runningURL)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(m_runningURL));
|
|
rv = GetPromptDialogFromUrl(msgUrl, getter_AddRefs(dialog));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
NS_ASSERTION (id && newsgroups, "null ptr");
|
|
if (!id || !newsgroups) return -1; /* "unknown error"... */
|
|
|
|
m_cancelNewsgroups = nsnull;
|
|
m_cancelDistribution = nsnull;
|
|
m_cancelFromHdr = nsnull;
|
|
m_cancelID = nsnull;
|
|
|
|
L = PL_strlen (id);
|
|
|
|
subject = (char *) PR_Malloc (L + 20);
|
|
body = (char *) PR_Malloc (PL_strlen (XP_AppCodeName) + 100);
|
|
|
|
nsXPIDLString alertText;
|
|
nsXPIDLString confirmText;
|
|
|
|
PRInt32 confirmCancelResult = 0;
|
|
|
|
// A little early to declare, but the goto causes problems
|
|
nsCAutoString otherHeaders;
|
|
|
|
/* Make sure that this loser isn't cancelling someone else's posting.
|
|
Yes, there are occasionally good reasons to do so. Those people
|
|
capable of making that decision (news admins) have other tools with
|
|
which to cancel postings (like telnet.)
|
|
|
|
Don't do this if server tells us it will validate user. DMB 3/19/97
|
|
*/
|
|
PRBool cancelchk=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("CANCELCHK",&cancelchk);
|
|
if (NS_SUCCEEDED(rv) && !cancelchk) {
|
|
NNTP_LOG_NOTE("CANCELCHK not supported");
|
|
|
|
// get the current identity from the news session....
|
|
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && accountManager) {
|
|
nsCOMPtr<nsISupportsArray> identities;
|
|
rv = accountManager->GetAllIdentities(getter_AddRefs(identities));
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
// CheckIfAuthor will set cancelInfo.from if a match is found
|
|
identities->EnumerateForwards(CheckIfAuthor, (void *)&cancelInfo);
|
|
}
|
|
|
|
if (!cancelInfo.from) {
|
|
GetNewsStringByName("cancelDisallowed", getter_Copies(alertText));
|
|
rv = dialog->Alert(nsnull, alertText);
|
|
// XXX: todo, check rv?
|
|
|
|
/* After the cancel is disallowed, Make the status update to be the same as though the
|
|
cancel was allowed, otherwise, the newsgroup is not able to take further requests as
|
|
reported here */
|
|
status = MK_NNTP_CANCEL_DISALLOWED;
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
failure = PR_TRUE;
|
|
goto FAIL;
|
|
}
|
|
else {
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) CANCELCHK not supported, so post the cancel message as %s", this, cancelInfo.from));
|
|
}
|
|
}
|
|
else {
|
|
NNTP_LOG_NOTE("CANCELCHK supported, don't do the us vs. them test");
|
|
}
|
|
|
|
// QA needs to be able to disable this confirm dialog, for the automated tests. see bug #31057
|
|
rv = prefBranch->GetBoolPref(PREF_NEWS_CANCEL_CONFIRM, &requireConfirmationForCancel);
|
|
if (NS_FAILED(rv) || requireConfirmationForCancel) {
|
|
/* Last chance to cancel the cancel.*/
|
|
GetNewsStringByName("cancelConfirm", getter_Copies(confirmText));
|
|
rv = dialog->Confirm(nsnull, confirmText, &confirmCancelResult);
|
|
// XXX: todo, check rv?
|
|
}
|
|
else
|
|
{
|
|
confirmCancelResult = 1;
|
|
}
|
|
|
|
if (confirmCancelResult != 1) {
|
|
// they cancelled the cancel
|
|
status = MK_NNTP_NOT_CANCELLED;
|
|
failure = PR_TRUE;
|
|
goto FAIL;
|
|
}
|
|
|
|
if (!subject || !body)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
failure = PR_TRUE;
|
|
goto FAIL;
|
|
}
|
|
|
|
PL_strcpy (subject, "cancel ");
|
|
PL_strcat (subject, id);
|
|
|
|
otherHeaders.AppendLiteral("Control: cancel ");
|
|
otherHeaders += id;
|
|
otherHeaders.AppendLiteral(CRLF);
|
|
if (distribution) {
|
|
otherHeaders.AppendLiteral("Distribution: ");
|
|
otherHeaders += distribution;
|
|
otherHeaders.AppendLiteral(CRLF);
|
|
}
|
|
|
|
PL_strcpy (body, "This message was cancelled from within ");
|
|
PL_strcat (body, XP_AppCodeName);
|
|
PL_strcat (body, "." CRLF);
|
|
|
|
|
|
m_cancelStatus = 0;
|
|
|
|
{
|
|
/* NET_BlockingWrite() should go away soon? I think. */
|
|
/* The following are what we really need to cancel a posted message */
|
|
char *data;
|
|
data = PR_smprintf("From: %s" CRLF
|
|
"Newsgroups: %s" CRLF
|
|
"Subject: %s" CRLF
|
|
"References: %s" CRLF
|
|
"%s" /* otherHeaders, already with CRLF */
|
|
CRLF /* body separator */
|
|
"%s" /* body, already with CRLF */
|
|
"." CRLF, /* trailing message terminator "." */
|
|
cancelInfo.from, newsgroups, subject, id,
|
|
otherHeaders.get(), body);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, data);
|
|
PR_Free (data);
|
|
if (status < 0) {
|
|
nsCAutoString errorText;
|
|
errorText.AppendInt(status);
|
|
AlertError(MK_TCP_WRITE_ERROR, errorText.get());
|
|
failure = PR_TRUE;
|
|
goto FAIL;
|
|
}
|
|
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_SEND_POST_DATA_RESPONSE;
|
|
|
|
// QA needs to be able to turn this alert off, for the automate tests. see bug #31057
|
|
rv = prefBranch->GetBoolPref(PREF_NEWS_CANCEL_ALERT_ON_SUCCESS, &showAlertAfterCancel);
|
|
if (NS_FAILED(rv) || showAlertAfterCancel) {
|
|
GetNewsStringByName("messageCancelled", getter_Copies(alertText));
|
|
rv = dialog->Alert(nsnull, alertText);
|
|
// XXX: todo, check rv?
|
|
}
|
|
|
|
if (!m_runningURL) return -1;
|
|
|
|
// delete the message from the db here.
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && m_newsFolder && (m_key != nsMsgKey_None), "need more to remove this message from the db");
|
|
if ((m_key != nsMsgKey_None) && (m_newsFolder))
|
|
rv = m_newsFolder->RemoveMessage(m_key);
|
|
|
|
}
|
|
|
|
FAIL:
|
|
NS_ASSERTION(m_newsFolder,"no news folder");
|
|
if (m_newsFolder)
|
|
rv = ( failure ) ? m_newsFolder->CancelFailed()
|
|
: m_newsFolder->CancelComplete();
|
|
|
|
PR_Free (id);
|
|
PR_Free (cancelInfo.old_from);
|
|
PR_Free (cancelInfo.from);
|
|
PR_Free (subject);
|
|
PR_Free (newsgroups);
|
|
PR_Free (distribution);
|
|
PR_Free (body);
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::XPATSend()
|
|
{
|
|
int status = 0;
|
|
char *thisTerm = NULL;
|
|
|
|
if (m_searchData &&
|
|
(thisTerm = PL_strchr(m_searchData, '/')) != NULL) {
|
|
/* extract the XPAT encoding for one query term */
|
|
/* char *next_search = NULL; */
|
|
char *command = NULL;
|
|
char *unescapedCommand = NULL;
|
|
char *endOfTerm = NULL;
|
|
NS_MsgSACopy (&command, ++thisTerm);
|
|
endOfTerm = PL_strchr(command, '/');
|
|
if (endOfTerm)
|
|
*endOfTerm = '\0';
|
|
NS_MsgSACat(&command, CRLF);
|
|
|
|
unescapedCommand = MSG_UnEscapeSearchUrl(command);
|
|
|
|
/* send one term off to the server */
|
|
NNTP_LOG_WRITE(command);
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, unescapedCommand);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_XPAT_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
PR_Free(command);
|
|
PR_Free(unescapedCommand);
|
|
}
|
|
else
|
|
{
|
|
m_nextState = NEWS_DONE;
|
|
status = MK_DATA_LOADED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::XPATResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 1;
|
|
|
|
if (m_responseCode != MK_NNTP_RESPONSE_XPAT_OK)
|
|
{
|
|
AlertError(MK_NNTP_ERROR_MESSAGE,m_responseText);
|
|
m_nextState = NNTP_ERROR;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
long articleNumber;
|
|
PR_sscanf(line, "%ld", &articleNumber);
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
{
|
|
nsCOMPtr <nsIMsgSearchSession> searchSession;
|
|
nsCOMPtr <nsIMsgSearchAdapter> searchAdapter;
|
|
mailnewsurl->GetSearchSession(getter_AddRefs(searchSession));
|
|
if (searchSession)
|
|
{
|
|
searchSession->GetRunningAdapter(getter_AddRefs(searchAdapter));
|
|
if (searchAdapter)
|
|
searchAdapter->AddHit((PRUint32) articleNumber);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set up the next term for next time around */
|
|
char *nextTerm = PL_strchr(m_searchData, '/');
|
|
|
|
if (nextTerm)
|
|
m_searchData = ++nextTerm;
|
|
else
|
|
m_searchData = nsnull;
|
|
|
|
m_nextState = NNTP_XPAT_SEND;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
}
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ListPrettyNames()
|
|
{
|
|
|
|
nsXPIDLCString group_name;
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
PRInt32 status = 0;
|
|
|
|
nsresult rv = m_newsFolder->GetRawName(group_name);
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"LIST PRETTYNAMES %.512s" CRLF,
|
|
NS_SUCCEEDED(rv) ? (const char *) group_name : "");
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
NNTP_LOG_NOTE(outputBuffer);
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_LIST_PRETTY_NAMES_RESPONSE;
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ListPrettyNamesResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
if (m_responseCode != MK_NNTP_RESPONSE_LIST_OK)
|
|
{
|
|
m_nextState = DISPLAY_NEWSGROUPS;
|
|
/* m_nextState = NEWS_DONE; */
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
#if 0 // SetPrettyName is not yet implemented. No reason to bother
|
|
int i;
|
|
/* find whitespace separator if it exits */
|
|
for (i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
|
|
; /* null body */
|
|
|
|
char *prettyName;
|
|
if(line[i] == '\0')
|
|
prettyName = &line[i];
|
|
else
|
|
prettyName = &line[i+1];
|
|
|
|
line[i] = 0; /* terminate group name */
|
|
if (i > 0) {
|
|
nsCAutoString charset;
|
|
nsAutoString lineUtf16, prettyNameUtf16;
|
|
if (NS_FAILED(m_nntpServer->GetCharset(charset) ||
|
|
NS_FAILED(ConvertToUnicode(charset, line, lineUtf16)) ||
|
|
NS_FAILED(ConvertToUnicode(charset, prettyName, prettyNameUtf16)))) {
|
|
CopyUTF8toUTF16(line, lineUtf16);
|
|
CopyUTF8toUTF16(prettyName, prettyNameUtf16);
|
|
}
|
|
m_nntpServer->SetPrettyNameForGroup(lineUtf16, prettyNameUtf16);
|
|
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) adding pretty name %s", this,
|
|
NS_ConvertUTF16toUTF8(prettyNameUtf16).get()));
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_nextState = DISPLAY_NEWSGROUPS; /* this assumes we were doing a list */
|
|
/* m_nextState = NEWS_DONE; */ /* ### dmb - don't really know */
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
}
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ListXActive()
|
|
{
|
|
nsXPIDLCString group_name;
|
|
nsresult rv;
|
|
rv = m_newsFolder->GetRawName(group_name);
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
PRInt32 status = 0;
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"LIST XACTIVE %.512s" CRLF,
|
|
(const char *) group_name);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_LIST_XACTIVE_RESPONSE;
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::ListXActiveResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
nsresult rv;
|
|
|
|
NS_ASSERTION(m_responseCode == MK_NNTP_RESPONSE_LIST_OK, "code != LIST_OK");
|
|
if (m_responseCode != MK_NNTP_RESPONSE_LIST_OK)
|
|
{
|
|
m_nextState = DISPLAY_NEWSGROUPS;
|
|
/* m_nextState = NEWS_DONE; */
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
/* almost correct */
|
|
if(status > 1)
|
|
{
|
|
#ifdef UNREADY_CODE
|
|
ce->bytes_received += status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->content_length);
|
|
#else
|
|
mBytesReceived += status;
|
|
mBytesReceivedSinceLastStatusUpdate += status;
|
|
#endif
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
char *s = line;
|
|
/* format is "rec.arts.movies.past-films 7302 7119 csp"
|
|
*/
|
|
while (*s && !NET_IS_SPACE(*s))
|
|
s++;
|
|
if (*s)
|
|
{
|
|
char flags[32]; /* ought to be big enough */
|
|
*s = 0;
|
|
PR_sscanf(s + 1,
|
|
"%d %d %31s",
|
|
&m_firstPossibleArticle,
|
|
&m_lastPossibleArticle,
|
|
flags);
|
|
|
|
|
|
NS_ASSERTION(m_nntpServer, "no nntp incoming server");
|
|
if (m_nntpServer) {
|
|
rv = m_nntpServer->AddNewsgroupToList(line);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to add to subscribe ds");
|
|
}
|
|
|
|
/* we're either going to list prettynames first, or list
|
|
all prettynames every time, so we won't care so much
|
|
if it gets interrupted. */
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) got xactive for %s of %s", this, line, flags));
|
|
/* This isn't required, because the extra info is
|
|
initialized to false for new groups. And it's
|
|
an expensive call.
|
|
*/
|
|
/* MSG_SetGroupNeedsExtraInfo(cd->host, line, PR_FALSE); */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRBool xactive=PR_FALSE;
|
|
rv = m_nntpServer->QueryExtension("XACTIVE",&xactive);
|
|
if (m_typeWanted == NEW_GROUPS &&
|
|
NS_SUCCEEDED(rv) && xactive)
|
|
{
|
|
nsCOMPtr <nsIMsgNewsFolder> old_newsFolder;
|
|
old_newsFolder = m_newsFolder;
|
|
nsXPIDLCString groupName;
|
|
|
|
rv = m_nntpServer->GetFirstGroupNeedingExtraInfo(groupName);
|
|
if (NS_FAILED(rv)) return -1;
|
|
rv = m_nntpServer->FindGroup(groupName,
|
|
getter_AddRefs(m_newsFolder));
|
|
if (NS_FAILED(rv)) return -1;
|
|
|
|
// see if we got a different group
|
|
if (old_newsFolder && m_newsFolder &&
|
|
(old_newsFolder.get() != m_newsFolder.get()))
|
|
/* make sure we're not stuck on the same group */
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) listing xactive for %s", this, (const char *)groupName));
|
|
m_nextState = NNTP_LIST_XACTIVE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
m_newsFolder = nsnull;
|
|
}
|
|
}
|
|
PRBool listpname;
|
|
rv = m_nntpServer->QueryExtension("LISTPNAME",&listpname);
|
|
if (NS_SUCCEEDED(rv) && listpname)
|
|
m_nextState = NNTP_LIST_PRETTY_NAMES;
|
|
else
|
|
m_nextState = DISPLAY_NEWSGROUPS; /* this assumes we were doing a list - who knows? */
|
|
/* m_nextState = NEWS_DONE; */ /* ### dmb - don't really know */
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
}
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListGroup()
|
|
{
|
|
nsresult rv;
|
|
char outputBuffer[OUTPUT_BUFFER_SIZE];
|
|
PRInt32 status = 0;
|
|
|
|
NS_ASSERTION(m_newsFolder,"no newsFolder");
|
|
if (!m_newsFolder) return -1;
|
|
nsXPIDLCString newsgroupName;
|
|
|
|
rv = m_newsFolder->GetRawName(newsgroupName);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PR_snprintf(outputBuffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"listgroup %.512s" CRLF,
|
|
newsgroupName.get());
|
|
|
|
m_articleList = do_CreateInstance(NS_NNTPARTICLELIST_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = m_articleList->Initialize(m_newsFolder);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
status = SendData(mailnewsurl, outputBuffer);
|
|
|
|
m_nextState = NNTP_RESPONSE;
|
|
m_nextStateAfterResponse = NNTP_LIST_GROUP_RESPONSE;
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
return status;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SendListGroupResponse(nsIInputStream * inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 0;
|
|
|
|
NS_ASSERTION(m_responseCode == MK_NNTP_RESPONSE_GROUP_SELECTED, "code != GROUP_SELECTED");
|
|
if (m_responseCode != MK_NNTP_RESPONSE_GROUP_SELECTED)
|
|
{
|
|
m_nextState = NEWS_DONE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
nsresult rv;
|
|
if (line[0] != '.')
|
|
{
|
|
nsMsgKey found_id = nsMsgKey_None;
|
|
PR_sscanf(line, "%ld", &found_id);
|
|
rv = m_articleList->AddArticleKey(found_id);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "add article key failed");
|
|
}
|
|
else
|
|
{
|
|
rv = m_articleList->FinishAddingArticleKeys();
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "finish adding article key failed");
|
|
m_articleList = nsnull;
|
|
m_nextState = NEWS_DONE; /* ### dmb - don't really know */
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
}
|
|
PR_FREEIF(line);
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRInt32 nsNNTPProtocol::Search()
|
|
{
|
|
NS_ASSERTION(0,"Search not implemented");
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SearchResponse()
|
|
{
|
|
if (MK_NNTP_RESPONSE_TYPE(m_responseCode) == MK_NNTP_RESPONSE_TYPE_OK)
|
|
m_nextState = NNTP_SEARCH_RESULTS;
|
|
else
|
|
m_nextState = NEWS_DONE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
|
|
PRInt32 nsNNTPProtocol::SearchResults(nsIInputStream *inputStream, PRUint32 length)
|
|
{
|
|
PRUint32 status = 1;
|
|
|
|
PRBool pauseForMoreData = PR_FALSE;
|
|
char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
|
|
|
|
if(pauseForMoreData)
|
|
{
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
return 0;
|
|
}
|
|
if (!line)
|
|
return status; /* no line yet */
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
#ifdef UNREADY_CODE
|
|
MSG_AddNewsSearchHit (ce->window_id, line);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* all overview lines received */
|
|
m_nextState = NEWS_DONE;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
PR_FREEIF(line);
|
|
return status;
|
|
}
|
|
|
|
/* Sets state for the transfer. This used to be known as net_setup_news_stream */
|
|
PRInt32 nsNNTPProtocol::SetupForTransfer()
|
|
{
|
|
if (m_typeWanted == NEWS_POST)
|
|
{
|
|
m_nextState = NNTP_SEND_POST_DATA;
|
|
#ifdef UNREADY_CODE
|
|
NET_Progress(ce->window_id, XP_GetString(MK_MSG_DELIV_NEWS));
|
|
#endif
|
|
}
|
|
else if(m_typeWanted == LIST_WANTED)
|
|
{
|
|
if (TestFlag(NNTP_USE_FANCY_NEWSGROUP))
|
|
m_nextState = NNTP_LIST_XACTIVE_RESPONSE;
|
|
else
|
|
m_nextState = NNTP_READ_LIST_BEGIN;
|
|
}
|
|
else if(m_typeWanted == GROUP_WANTED)
|
|
m_nextState = NNTP_XOVER_BEGIN;
|
|
else if(m_typeWanted == NEW_GROUPS)
|
|
m_nextState = NNTP_NEWGROUPS_BEGIN;
|
|
else if(m_typeWanted == ARTICLE_WANTED ||
|
|
m_typeWanted== CANCEL_WANTED)
|
|
m_nextState = NNTP_BEGIN_ARTICLE;
|
|
else if (m_typeWanted== SEARCH_WANTED)
|
|
m_nextState = NNTP_XPAT_SEND;
|
|
else if (m_typeWanted == PRETTY_NAMES_WANTED)
|
|
m_nextState = NNTP_LIST_PRETTY_NAMES;
|
|
#ifdef UNREADY_CODE
|
|
else if (m_typeWanted == PROFILE_WANTED)
|
|
{
|
|
if (PL_strstr(ce->URL_s->address, "PROFILE NEW"))
|
|
m_nextState = NNTP_PROFILE_ADD;
|
|
else
|
|
m_nextState = NNTP_PROFILE_DELETE;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
NS_ASSERTION(0, "unexpected");
|
|
return -1;
|
|
}
|
|
|
|
return(0); /* good */
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// The following method is used for processing the news state machine.
|
|
// It returns a negative number (mscott: we'll change this to be an enumerated type which we'll coordinate
|
|
// with the netlib folks?) when we are done processing.
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
nsresult nsNNTPProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
|
|
PRUint32 sourceOffset, PRUint32 length)
|
|
{
|
|
PRInt32 status = 0;
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (!mailnewsurl)
|
|
return NS_OK; // probably no data available - it's OK.
|
|
|
|
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
|
|
while(!TestFlag(NNTP_PAUSE_FOR_READ))
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) Next state: %s",this, stateLabels[m_nextState]));
|
|
// examine our current state and call an appropriate handler for that state.....
|
|
switch(m_nextState)
|
|
{
|
|
case NNTP_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = NewsResponse(inputStream, length);
|
|
break;
|
|
|
|
// mscott: I've removed the states involving connections on the assumption
|
|
// that core netlib will now be managing that information.
|
|
|
|
case NNTP_LOGIN_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = LoginResponse();
|
|
break;
|
|
|
|
case NNTP_SEND_MODE_READER:
|
|
status = SendModeReader();
|
|
break;
|
|
|
|
case NNTP_SEND_MODE_READER_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendModeReaderResponse();
|
|
break;
|
|
|
|
case SEND_LIST_EXTENSIONS:
|
|
status = SendListExtensions();
|
|
break;
|
|
case SEND_LIST_EXTENSIONS_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendListExtensionsResponse(inputStream, length);
|
|
break;
|
|
case SEND_LIST_SEARCHES:
|
|
status = SendListSearches();
|
|
break;
|
|
case SEND_LIST_SEARCHES_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendListSearchesResponse(inputStream, length);
|
|
break;
|
|
case NNTP_LIST_SEARCH_HEADERS:
|
|
status = SendListSearchHeaders();
|
|
break;
|
|
case NNTP_LIST_SEARCH_HEADERS_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendListSearchHeadersResponse(inputStream, length);
|
|
break;
|
|
case NNTP_GET_PROPERTIES:
|
|
status = GetProperties();
|
|
break;
|
|
case NNTP_GET_PROPERTIES_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = GetPropertiesResponse(inputStream, length);
|
|
break;
|
|
case SEND_LIST_SUBSCRIPTIONS:
|
|
status = SendListSubscriptions();
|
|
break;
|
|
case SEND_LIST_SUBSCRIPTIONS_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendListSubscriptionsResponse(inputStream, length);
|
|
break;
|
|
|
|
case SEND_FIRST_NNTP_COMMAND:
|
|
status = SendFirstNNTPCommand(url);
|
|
break;
|
|
case SEND_FIRST_NNTP_COMMAND_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendFirstNNTPCommandResponse();
|
|
break;
|
|
|
|
case NNTP_SEND_GROUP_FOR_ARTICLE:
|
|
status = SendGroupForArticle();
|
|
break;
|
|
case NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendGroupForArticleResponse();
|
|
break;
|
|
case NNTP_SEND_ARTICLE_NUMBER:
|
|
status = SendArticleNumber();
|
|
break;
|
|
|
|
case SETUP_NEWS_STREAM:
|
|
status = SetupForTransfer();
|
|
break;
|
|
|
|
case NNTP_BEGIN_AUTHORIZE:
|
|
status = BeginAuthorization();
|
|
break;
|
|
|
|
case NNTP_AUTHORIZE_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = AuthorizationResponse();
|
|
break;
|
|
|
|
case NNTP_PASSWORD_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = PasswordResponse();
|
|
break;
|
|
|
|
// read list
|
|
case NNTP_READ_LIST_BEGIN:
|
|
status = BeginReadNewsList();
|
|
break;
|
|
case NNTP_READ_LIST:
|
|
status = ReadNewsList(inputStream, length);
|
|
break;
|
|
|
|
// news group
|
|
case DISPLAY_NEWSGROUPS:
|
|
status = DisplayNewsgroups();
|
|
break;
|
|
case NNTP_NEWGROUPS_BEGIN:
|
|
status = BeginNewsgroups();
|
|
break;
|
|
case NNTP_NEWGROUPS:
|
|
status = ProcessNewsgroups(inputStream, length);
|
|
break;
|
|
|
|
// article specific
|
|
case NNTP_BEGIN_ARTICLE:
|
|
status = BeginArticle();
|
|
break;
|
|
|
|
case NNTP_READ_ARTICLE:
|
|
status = ReadArticle(inputStream, length);
|
|
break;
|
|
|
|
case NNTP_XOVER_BEGIN:
|
|
status = BeginReadXover();
|
|
break;
|
|
|
|
case NNTP_FIGURE_NEXT_CHUNK:
|
|
status = FigureNextChunk();
|
|
break;
|
|
|
|
case NNTP_XOVER_SEND:
|
|
status = XoverSend();
|
|
break;
|
|
|
|
case NNTP_XOVER:
|
|
status = ReadXover(inputStream, length);
|
|
break;
|
|
|
|
case NNTP_XOVER_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = ReadXoverResponse();
|
|
break;
|
|
|
|
case NEWS_PROCESS_XOVER:
|
|
case NEWS_PROCESS_BODIES:
|
|
#ifdef UNREADY_CODE
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_SORT_ARTICLES));
|
|
#endif
|
|
status = ProcessXover();
|
|
break;
|
|
|
|
case NNTP_READ_GROUP:
|
|
status = ReadNewsgroup();
|
|
break;
|
|
|
|
case NNTP_READ_GROUP_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = ReadNewsgroupResponse();
|
|
break;
|
|
|
|
case NNTP_READ_GROUP_BODY:
|
|
status = ReadNewsgroupResponse();
|
|
break;
|
|
|
|
case NNTP_SEND_POST_DATA:
|
|
status = PostData();
|
|
break;
|
|
case NNTP_SEND_POST_DATA_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = PostDataResponse();
|
|
break;
|
|
|
|
case NNTP_CHECK_FOR_MESSAGE:
|
|
status = CheckForArticle();
|
|
break;
|
|
|
|
case NEWS_NEWS_RC_POST:
|
|
#ifdef UNREADY_CODE
|
|
status = net_NewsRCProcessPost(ce);
|
|
#endif
|
|
break;
|
|
|
|
case NEWS_DISPLAY_NEWS_RC:
|
|
status = DisplayNewsRC();
|
|
break;
|
|
case NEWS_DISPLAY_NEWS_RC_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = DisplayNewsRCResponse();
|
|
break;
|
|
|
|
// cancel
|
|
case NEWS_START_CANCEL:
|
|
status = StartCancel();
|
|
break;
|
|
|
|
case NEWS_DO_CANCEL:
|
|
status = DoCancel();
|
|
break;
|
|
|
|
// XPAT
|
|
case NNTP_XPAT_SEND:
|
|
status = XPATSend();
|
|
break;
|
|
case NNTP_XPAT_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = XPATResponse(inputStream, length);
|
|
break;
|
|
|
|
// search
|
|
case NNTP_SEARCH:
|
|
status = Search();
|
|
break;
|
|
case NNTP_SEARCH_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SearchResponse();
|
|
break;
|
|
case NNTP_SEARCH_RESULTS:
|
|
status = SearchResults(inputStream, length);
|
|
break;
|
|
|
|
|
|
case NNTP_LIST_PRETTY_NAMES:
|
|
status = ListPrettyNames();
|
|
break;
|
|
case NNTP_LIST_PRETTY_NAMES_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = ListPrettyNamesResponse(inputStream, length);
|
|
break;
|
|
case NNTP_LIST_XACTIVE:
|
|
status = ListXActive();
|
|
break;
|
|
case NNTP_LIST_XACTIVE_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = ListXActiveResponse(inputStream, length);
|
|
break;
|
|
case NNTP_LIST_GROUP:
|
|
status = SendListGroup();
|
|
break;
|
|
case NNTP_LIST_GROUP_RESPONSE:
|
|
if (inputStream == nsnull)
|
|
SetFlag(NNTP_PAUSE_FOR_READ);
|
|
else
|
|
status = SendListGroupResponse(inputStream, length);
|
|
break;
|
|
case NEWS_DONE:
|
|
m_nextState = NEWS_FREE;
|
|
break;
|
|
case NEWS_POST_DONE:
|
|
NNTP_LOG_NOTE("NEWS_POST_DONE");
|
|
mailnewsurl->SetUrlState(PR_FALSE, NS_OK);
|
|
m_nextState = NEWS_FREE;
|
|
break;
|
|
case NEWS_ERROR:
|
|
NNTP_LOG_NOTE("NEWS_ERROR");
|
|
if (m_responseCode == MK_NNTP_RESPONSE_ARTICLE_NOTFOUND || m_responseCode == MK_NNTP_RESPONSE_ARTICLE_NONEXIST)
|
|
mailnewsurl->SetUrlState(PR_FALSE, NS_MSG_NEWS_ARTICLE_NOT_FOUND);
|
|
else
|
|
mailnewsurl->SetUrlState(PR_FALSE, NS_ERROR_FAILURE);
|
|
m_nextState = NEWS_FREE;
|
|
break;
|
|
case NNTP_ERROR:
|
|
// XXX do we really want to remove the connection from
|
|
// the cache on error?
|
|
/* check if this connection came from the cache or if it was
|
|
* a new connection. If it was not new lets start it over
|
|
* again. But only if we didn't have any successful protocol
|
|
* dialog at all.
|
|
*/
|
|
FinishMemCacheEntry(PR_FALSE); // cleanup mem cache entry
|
|
if (m_responseCode != MK_NNTP_RESPONSE_ARTICLE_NOTFOUND && m_responseCode != MK_NNTP_RESPONSE_ARTICLE_NONEXIST)
|
|
return CloseConnection();
|
|
case NEWS_FREE:
|
|
m_lastActiveTimeStamp = PR_Now(); // remmeber when we last used this connection.
|
|
return CleanupAfterRunningUrl();
|
|
case NEWS_FINISHED:
|
|
return NS_OK;
|
|
break;
|
|
default:
|
|
/* big error */
|
|
return NS_ERROR_FAILURE;
|
|
|
|
} // end switch
|
|
|
|
if(status < 0 && m_nextState != NEWS_ERROR &&
|
|
m_nextState != NNTP_ERROR && m_nextState != NEWS_FREE)
|
|
{
|
|
m_nextState = NNTP_ERROR;
|
|
ClearFlag(NNTP_PAUSE_FOR_READ);
|
|
}
|
|
|
|
} /* end big while */
|
|
|
|
return NS_OK; /* keep going */
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::CloseConnection()
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ClosingConnection",this));
|
|
SendData(nsnull, NNTP_CMD_QUIT); // this will cause OnStopRequest get called, which will call CloseSocket()
|
|
// break some cycles
|
|
CleanupNewsgroupList();
|
|
|
|
if (m_nntpServer) {
|
|
m_nntpServer->RemoveConnection(this);
|
|
m_nntpServer = nsnull;
|
|
}
|
|
CloseSocket();
|
|
m_newsFolder = nsnull;
|
|
|
|
if (m_articleList) {
|
|
m_articleList->FinishAddingArticleKeys();
|
|
m_articleList = nsnull;
|
|
}
|
|
|
|
m_key = nsMsgKey_None;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::CleanupNewsgroupList()
|
|
{
|
|
nsresult rv;
|
|
if (!m_newsgroupList) return NS_OK;
|
|
PRInt32 status = 0;
|
|
rv = m_newsgroupList->FinishXOVERLINE(0,&status);
|
|
m_newsgroupList = nsnull;
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "FinishXOVERLINE failed");
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::CleanupAfterRunningUrl()
|
|
{
|
|
/* do we need to know if we're parsing xover to call finish xover? */
|
|
/* yes, I think we do! Why did I think we should??? */
|
|
/* If we've gotten to NEWS_FREE and there is still XOVER
|
|
data, there was an error or we were interrupted or
|
|
something. So, tell libmsg there was an abnormal
|
|
exit so that it can free its data. */
|
|
|
|
nsresult rv = NS_OK;
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) CleanupAfterRunningUrl()", this));
|
|
|
|
// send StopRequest notification after we've cleaned up the protocol
|
|
// because it can synchronously causes a new url to get run in the
|
|
// protocol - truly evil, but we're stuck at the moment.
|
|
if (m_channelListener)
|
|
rv = m_channelListener->OnStopRequest(this, m_channelContext, NS_OK);
|
|
|
|
if (m_loadGroup)
|
|
m_loadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull, NS_OK);
|
|
CleanupNewsgroupList();
|
|
|
|
// clear out mem cache entry so we're not holding onto it.
|
|
if (m_runningURL)
|
|
{
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
|
|
if (mailnewsurl)
|
|
{
|
|
mailnewsurl->SetUrlState(PR_FALSE, NS_OK);
|
|
mailnewsurl->SetMemCacheEntry(nsnull);
|
|
}
|
|
}
|
|
|
|
Cleanup();
|
|
|
|
mDisplayInputStream = nsnull;
|
|
mDisplayOutputStream = nsnull;
|
|
mProgressEventSink = nsnull;
|
|
SetOwner(nsnull);
|
|
|
|
m_channelContext = nsnull;
|
|
m_channelListener = nsnull;
|
|
m_loadGroup = nsnull;
|
|
mCallbacks = nsnull;
|
|
|
|
// disable timeout before caching.
|
|
nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport);
|
|
if (strans)
|
|
strans->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, PR_UINT32_MAX);
|
|
|
|
// don't mark ourselves as not busy until we are done cleaning up the connection. it should be the
|
|
// last thing we do.
|
|
SetIsBusy(PR_FALSE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsNNTPProtocol::CloseSocket()
|
|
{
|
|
PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) ClosingSocket()",this));
|
|
|
|
if (m_nntpServer) {
|
|
m_nntpServer->RemoveConnection(this);
|
|
m_nntpServer = nsnull;
|
|
}
|
|
|
|
CleanupAfterRunningUrl(); // is this needed?
|
|
return nsMsgProtocol::CloseSocket();
|
|
}
|
|
|
|
void nsNNTPProtocol::SetProgressBarPercent(PRUint32 aProgress, PRUint32 aProgressMax)
|
|
{
|
|
// XXX 64-bit
|
|
if (mProgressEventSink)
|
|
mProgressEventSink->OnProgress(this, m_channelContext, nsUint64(aProgress),
|
|
nsUint64(aProgressMax));
|
|
}
|
|
|
|
nsresult
|
|
nsNNTPProtocol::SetProgressStatus(const PRUnichar *aMessage)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (mProgressEventSink)
|
|
rv = mProgressEventSink->OnStatus(this, m_channelContext, NS_OK, aMessage);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::GetContentType(nsACString &aContentType)
|
|
{
|
|
|
|
// if we've been set with a content type, then return it....
|
|
// this happens when we go through libmime now as it sets our new content type
|
|
if (!m_ContentType.IsEmpty())
|
|
{
|
|
aContentType = m_ContentType;
|
|
return NS_OK;
|
|
}
|
|
|
|
// otherwise do what we did before...
|
|
|
|
if (m_typeWanted == GROUP_WANTED)
|
|
aContentType.AssignLiteral("x-application-newsgroup");
|
|
else if (m_typeWanted == IDS_WANTED)
|
|
aContentType.AssignLiteral("x-application-newsgroup-listids");
|
|
else
|
|
aContentType.AssignLiteral("message/rfc822");
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsNNTPProtocol::AlertError(PRInt32 errorCode, const char *text)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
// get the prompt from the running url....
|
|
if (m_runningURL) {
|
|
nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(m_runningURL));
|
|
nsCOMPtr<nsIPrompt> dialog;
|
|
rv = GetPromptDialogFromUrl(msgUrl, getter_AddRefs(dialog));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString alertText;
|
|
nsXPIDLString str;
|
|
rv = GetNewsStringByID(MK_NNTP_ERROR_MESSAGE, getter_Copies(str));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
alertText.Append(str);
|
|
|
|
if (text) {
|
|
alertText.Append(' ');
|
|
alertText.AppendWithConversion(text);
|
|
}
|
|
|
|
rv = dialog->Alert(nsnull, alertText.get());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsNNTPProtocol::GetCurrentFolder(nsIMsgFolder **aFolder)
|
|
{
|
|
nsresult rv = NS_ERROR_NULL_POINTER;
|
|
NS_ENSURE_ARG_POINTER(aFolder);
|
|
if (m_newsFolder)
|
|
rv = m_newsFolder->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) aFolder);
|
|
return rv;
|
|
}
|
|
|
|
void nsNNTPProtocol::GotAuthorizationRequest()
|
|
{
|
|
if ( m_nextStateAfterResponse == NEWS_DISPLAY_NEWS_RC_RESPONSE )
|
|
{
|
|
// the authorization request disrupted our NEWSRC reading.
|
|
// => restart it, to not lose groups
|
|
// (see http://bugzilla.mozilla.org/show_bug.cgi?id=111855)
|
|
ClearFlag( NNTP_NEWSRC_PERFORMED );
|
|
|
|
// with setting m_newsRCListIndex to 0, we could simply restart
|
|
// the whole process, starting over with the very first group.
|
|
// However, this may lead to problems in some rare scenarios:
|
|
// Consider, for instance, an account which has n newsgroups which
|
|
// all require different authentication, and a user which does not
|
|
// allow the password manager to remember the user/pwd settings. This
|
|
// would lead to constantly annoying this user with the authorization
|
|
// request for the first group.
|
|
// To prevent this (well, and to save some network traffic :), we go
|
|
// back one step only
|
|
NS_ASSERTION( m_newsRCListIndex > 0, "next state == NEWS_DISPLAY_NEWS_RC_RESPONSE, but no single group handled, yet?" );
|
|
if ( m_newsRCListIndex > 0 )
|
|
{ // yes, I'm paranoid :)
|
|
m_RCIndexToResumeAfterAuthRequest = --m_newsRCListIndex;
|
|
}
|
|
}
|
|
}
|