зеркало из https://github.com/mozilla/pjs.git
5410 строки
140 KiB
C
5410 строки
140 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/*
|
|
* State machine to implement NNTP access
|
|
*
|
|
* Designed and originally implemented by Lou Montulli '94
|
|
* Heavily modified by libmsg folks
|
|
*/
|
|
|
|
/* Please leave outside of ifdef for windows precompiled headers */
|
|
#define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */
|
|
|
|
#include "rosetta.h"
|
|
#include "mkutils.h"
|
|
#include "netutils.h"
|
|
|
|
#ifdef MOZILLA_CLIENT
|
|
|
|
#include "merrors.h"
|
|
|
|
#include "mime.h"
|
|
#include "shist.h"
|
|
#include "glhist.h"
|
|
/*#include "xp_reg.h"*/
|
|
#include "mknews.h"
|
|
#include "mktcp.h"
|
|
#include "mkparse.h"
|
|
#include "mkformat.h"
|
|
#include "mkstream.h"
|
|
#include "mkaccess.h"
|
|
#include "mksort.h"
|
|
#include "netcache.h"
|
|
#include "mknewsgr.h"
|
|
#include "libi18n.h"
|
|
#include "gui.h"
|
|
#include "ssl.h"
|
|
#include "mknews.h"
|
|
#include "prerror.h"
|
|
#include "msgcom.h"
|
|
#include "msgnet.h"
|
|
#include "msg_srch.h"
|
|
#include "libmime.h"
|
|
|
|
#include "prtime.h"
|
|
#include "prlog.h"
|
|
|
|
#include "xp_error.h"
|
|
#include "secnav.h"
|
|
#include "prefapi.h"
|
|
#include "xplocale.h"
|
|
|
|
/*#include "xp_sock.h"*/
|
|
|
|
/*#define CACHE_NEWSGRP_PASSWORD*/
|
|
|
|
/* for XP_GetString() */
|
|
#include "xpgetstr.h"
|
|
extern int MK_MALFORMED_URL_ERROR;
|
|
extern int MK_NEWS_ERROR_FMT;
|
|
extern int MK_NNTP_CANCEL_CONFIRM;
|
|
extern int MK_NNTP_CANCEL_DISALLOWED;
|
|
extern int MK_NNTP_NOT_CANCELLED;
|
|
extern int MK_OUT_OF_MEMORY;
|
|
extern int XP_CONFIRM_SAVE_NEWSGROUPS;
|
|
extern int XP_HTML_ARTICLE_EXPIRED;
|
|
extern int XP_HTML_NEWS_ERROR;
|
|
extern int XP_PROGRESS_READ_NEWSGROUPINFO;
|
|
extern int XP_PROGRESS_RECEIVE_ARTICLE;
|
|
extern int XP_PROGRESS_RECEIVE_LISTARTICLES;
|
|
extern int XP_PROGRESS_RECEIVE_NEWSGROUP;
|
|
extern int XP_PROGRESS_SORT_ARTICLES;
|
|
extern int XP_PROGRESS_READ_NEWSGROUP_COUNTS;
|
|
extern int XP_THERMO_PERCENT_FORM;
|
|
extern int XP_PROMPT_ENTER_USERNAME;
|
|
extern int MK_BAD_NNTP_CONNECTION;
|
|
extern int MK_NNTP_AUTH_FAILED;
|
|
extern int MK_NNTP_ERROR_MESSAGE;
|
|
extern int MK_NNTP_NEWSGROUP_SCAN_ERROR;
|
|
extern int MK_NNTP_SERVER_ERROR;
|
|
extern int MK_NNTP_SERVER_NOT_CONFIGURED;
|
|
extern int MK_SECURE_NEWS_PROXY_ERROR;
|
|
extern int MK_TCP_READ_ERROR;
|
|
extern int MK_TCP_WRITE_ERROR;
|
|
extern int MK_NNTP_CANCEL_ERROR;
|
|
extern int XP_CONNECT_NEWS_HOST_CONTACTED_WAITING_FOR_REPLY;
|
|
extern int XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS;
|
|
extern int XP_GARBAGE_COLLECTING;
|
|
extern int XP_MESSAGE_SENT_WAITING_NEWS_REPLY;
|
|
extern int MK_MSG_DELIV_NEWS;
|
|
extern int MK_MSG_COLLABRA_DISABLED;
|
|
extern int MK_MSG_EXPIRE_NEWS_ARTICLES;
|
|
|
|
/* Logging stuff */
|
|
#ifndef NSPR20
|
|
PR_LOG_DEFINE(NNTP);
|
|
#define NNTP_LOG_READ(buf) PR_LOG(NNTP, out, ("Receiving: %s", buf))
|
|
#define NNTP_LOG_WRITE(buf) PR_LOG(NNTP, out, ("Sending: %s", buf))
|
|
#define NNTP_LOG_NOTE(buf) PR_LOG(NNTP, out, buf)
|
|
#else
|
|
PRLogModuleInfo* NNTP = NULL;
|
|
#define out PR_LOG_ALWAYS
|
|
|
|
#define NNTP_LOG_READ(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, ("Receiving: %s", buf)) ;
|
|
|
|
#define NNTP_LOG_WRITE(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, ("Sending: %s", buf)) ;
|
|
|
|
#define NNTP_LOG_NOTE(buf) \
|
|
if (NNTP==NULL) \
|
|
NNTP = PR_NewLogModule("NNTP"); \
|
|
PR_LOG(NNTP, out, buf) ;
|
|
|
|
#endif /* NSPR20 */
|
|
|
|
/* Forward declarations */
|
|
static int net_xpat_send (ActiveEntry*);
|
|
static int net_list_pretty_names(ActiveEntry*);
|
|
PRIVATE int32 net_ProcessNews (ActiveEntry *ce);
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile on
|
|
#endif
|
|
|
|
#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 */
|
|
|
|
/* Allow the user to open at most this many connections to one news host*/
|
|
#define kMaxConnectionsPerHost 3
|
|
|
|
/* Keep this many connections cached. The cache is global, not per host */
|
|
#define kMaxCachedConnections 2
|
|
|
|
/* globals
|
|
*/
|
|
PRIVATE char * NET_NewsHost = NULL;
|
|
PRIVATE XP_List * nntp_connection_list=0;
|
|
|
|
PRIVATE XP_Bool net_news_last_username_probably_valid=FALSE;
|
|
PRIVATE int32 net_NewsChunkSize=-1; /* default */
|
|
|
|
PRIVATE int32 net_news_timeout = 170; /* seconds that an idle NNTP conn can live */
|
|
|
|
#define PUTSTRING(s) (*cd->stream->put_block) \
|
|
(cd->stream, s, XP_STRLEN(s))
|
|
#define COMPLETE_STREAM (*cd->stream->complete) \
|
|
(cd->stream)
|
|
#define ABORT_STREAM(s) (*cd->stream->abort) \
|
|
(cd->stream, s)
|
|
#define PUT_STREAM(buf, size) (*cd->stream->put_block) \
|
|
(cd->stream, buf, size)
|
|
|
|
/* states of the machine
|
|
*/
|
|
typedef enum _StatesEnum {
|
|
NNTP_RESPONSE,
|
|
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
|
|
NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE,
|
|
NNTP_CONNECTIONS_ARE_AVAILABLE,
|
|
#endif
|
|
NNTP_CONNECT,
|
|
NNTP_CONNECT_WAIT,
|
|
HG86722
|
|
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,
|
|
NNTP_LIST_XACTIVE_RESPONSE,
|
|
NNTP_LIST_GROUP,
|
|
NNTP_LIST_GROUP_RESPONSE,
|
|
NEWS_DONE,
|
|
NEWS_ERROR,
|
|
NNTP_ERROR,
|
|
NEWS_FREE
|
|
} StatesEnum;
|
|
|
|
#ifdef DEBUG
|
|
char *stateLabels[] = {
|
|
"NNTP_RESPONSE",
|
|
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
|
|
"NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE",
|
|
"NNTP_CONNECTIONS_ARE_AVAILABLE",
|
|
#endif
|
|
"NNTP_CONNECT",
|
|
"NNTP_CONNECT_WAIT",
|
|
HG87489
|
|
"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_ERROR",
|
|
"NNTP_ERROR",
|
|
"NEWS_FREE"
|
|
};
|
|
|
|
#endif
|
|
|
|
/* structure to hold data about a tcp connection
|
|
* to a news host
|
|
*/
|
|
typedef struct _NNTPConnection {
|
|
char *hostname; /* hostname string (may contain :port) */
|
|
PRFileDesc *csock; /* control socket */
|
|
XP_Bool busy; /* is the connection in use already? */
|
|
XP_Bool prev_cache; /* did this connection come from the cache? */
|
|
XP_Bool posting_allowed;/* does this server allow posting */
|
|
XP_Bool default_host;
|
|
XP_Bool no_xover; /* xover command is not supported here */
|
|
XP_Bool secure; /* is it a secure connection? */
|
|
char *current_group; /* last GROUP command sent on this connection */
|
|
time_t last_used_time; /* last time this conn was used, for conn aging purposes */
|
|
} NNTPConnection;
|
|
|
|
|
|
/* structure to hold all the state and data
|
|
* for the state machine
|
|
*/
|
|
typedef struct _NewsConData {
|
|
MSG_Pane *pane;
|
|
MSG_NewsHost *host;
|
|
|
|
StatesEnum next_state;
|
|
StatesEnum next_state_after_response;
|
|
int type_wanted; /* Article, List, or Group */
|
|
int response_code; /* code returned from NNTP server */
|
|
char *response_txt; /* text returned from NNTP server */
|
|
Bool pause_for_read; /* should we pause for the next read? */
|
|
|
|
HG82332
|
|
XP_Bool proxy_auth_required; /* is auth required */
|
|
XP_Bool sent_proxy_auth; /* have we sent a proxy auth? */
|
|
|
|
XP_Bool newsrc_performed; /* have we done a newsrc update? */
|
|
XP_Bool mode_reader_performed; /* have we sent any cmds to the server yet? */
|
|
|
|
#ifdef XP_WIN
|
|
XP_Bool calling_netlib_all_the_time;
|
|
#endif
|
|
NET_StreamClass * stream;
|
|
|
|
NNTPConnection * control_con; /* all the info about a connection */
|
|
|
|
char *data_buf;
|
|
int32 data_buf_size;
|
|
|
|
/* for group command */
|
|
char *path; /* message id */
|
|
|
|
char *group_name;
|
|
int32 first_art;
|
|
int32 last_art;
|
|
int32 first_possible_art;
|
|
int32 last_possible_art;
|
|
|
|
int32 num_loaded; /* How many articles we got XOVER lines for. */
|
|
int32 num_wanted; /* How many articles we wanted to get XOVER
|
|
lines for. */
|
|
|
|
/* for cancellation: the headers to be used */
|
|
char *cancel_from;
|
|
char *cancel_newsgroups;
|
|
char *cancel_distribution;
|
|
char *cancel_id;
|
|
char *cancel_msg_file;
|
|
int cancel_status;
|
|
|
|
/* variables for ReadNewsRC */
|
|
int32 newsrc_list_index;
|
|
int32 newsrc_list_count;
|
|
|
|
XP_Bool use_fancy_newsgroup_listing; /* use LIST XACTIVE or LIST */
|
|
XP_Bool destroy_graph_progress; /* do we need to destroy
|
|
* graph progress?
|
|
*/
|
|
|
|
char *output_buffer; /* use it to write out lines */
|
|
|
|
int32 article_num; /* current article number */
|
|
|
|
char *message_id; /* for reading groups */
|
|
char *author;
|
|
char *subject;
|
|
|
|
int32 original_content_length; /* the content length at the time of
|
|
* calling graph progress
|
|
*/
|
|
|
|
/* random pointer for libmsg state */
|
|
void *xover_parse_state;
|
|
void *newsgroup_parse_state;
|
|
|
|
TCP_ConData * tcp_con_data;
|
|
|
|
void * write_post_data_data;
|
|
|
|
XP_Bool some_protocol_succeeded; /* We know we have done some protocol
|
|
with this newsserver recently, so don't
|
|
respond to an error by closing and
|
|
reopening it. */
|
|
char *command_specific_data;
|
|
char *current_search;
|
|
void *offlineState; /* offline news state machine for article retrieval */
|
|
int previous_response_code;
|
|
|
|
} NewsConData;
|
|
|
|
|
|
/* publicly available function to set the news host
|
|
* this will be a useful front end callable routine
|
|
*/
|
|
PUBLIC void NET_SetNewsHost (const char * value)
|
|
{
|
|
/* ### This routine is obsolete and should go away. */
|
|
}
|
|
|
|
|
|
|
|
/* set the default number of articles retrieved by news at any one time
|
|
*/
|
|
PUBLIC void
|
|
NET_SetNumberOfNewsArticlesInListing(int32 number)
|
|
{
|
|
net_NewsChunkSize = number;
|
|
}
|
|
|
|
/* Whether we cache XOVER data. NYI. */
|
|
PUBLIC void
|
|
NET_SetCacheXOVER(XP_Bool value)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
PUBLIC void NET_CleanTempXOVERCache(void)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
PUBLIC void net_graceful_shutdown(PRFileDesc *sock, XP_Bool isSecure)
|
|
{
|
|
static int32 int_pref = -1;
|
|
|
|
if (int_pref == -1)
|
|
PREF_GetIntPref("network.socket_shutdown", &int_pref);
|
|
if (int_pref > 0 && int_pref < 4)
|
|
{
|
|
HG32949
|
|
PR_Shutdown(sock, (int)(int_pref-1));
|
|
}
|
|
}
|
|
|
|
static void net_nntp_close (PRFileDesc *sock, int status)
|
|
{
|
|
if (status != MK_INTERRUPTED)
|
|
{
|
|
const char *command = "QUIT" CRLF;
|
|
NET_BlockingWrite (sock, command, XP_STRLEN(command));
|
|
NNTP_LOG_WRITE (command);
|
|
}
|
|
PR_Close(sock);
|
|
}
|
|
|
|
/* user has removed a news host from the UI.
|
|
* be sure it has also been removed from the
|
|
* connection cache so we renegotiate news host
|
|
* capabilities if we try to use it again
|
|
*/
|
|
PUBLIC void NET_OnNewsHostDeleted (const char *hostName)
|
|
{
|
|
NNTPConnection *conn;
|
|
XP_List *list = nntp_connection_list;
|
|
|
|
while ((conn = (NNTPConnection*) XP_ListNextObject(list)) != NULL)
|
|
{
|
|
if (!(XP_STRCASECMP(conn->hostname, hostName)))
|
|
net_nntp_close (conn->csock, conn->busy ? MK_INTERRUPTED : 0);
|
|
|
|
list = list->prev ? list->prev : nntp_connection_list;
|
|
XP_ListRemoveObject (nntp_connection_list, conn);
|
|
|
|
FREEIF(conn->current_group);
|
|
FREEIF(conn->hostname);
|
|
FREE(conn);
|
|
}
|
|
}
|
|
|
|
|
|
/* display HTML error state in the context window
|
|
* returns negative error status
|
|
*/
|
|
PRIVATE int
|
|
net_display_html_error_state (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
XP_Bool inMsgPane = FALSE;
|
|
int major_opcode = cd->response_code/100;
|
|
/* #### maybe make this a dialog box
|
|
to do that, just return an error.
|
|
*/
|
|
|
|
/* ERROR STATE
|
|
* Set up the HTML stream
|
|
*/
|
|
ce->format_out = CLEAR_CACHE_BIT(ce->format_out);
|
|
StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);
|
|
|
|
/* If we're not in a message pane, don't try to spew HTML
|
|
* This allows error reporting from the folder pane, subscribe UI, etc.
|
|
*/
|
|
if (ce->URL_s->msg_pane)
|
|
inMsgPane = (MSG_MESSAGEPANE == MSG_GetPaneType(ce->URL_s->msg_pane));
|
|
|
|
if (ce->format_out == FO_PRESENT && inMsgPane)
|
|
{
|
|
cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s,
|
|
ce->window_id);
|
|
if(!cd->stream)
|
|
{
|
|
ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
|
|
return MK_UNABLE_TO_CONVERT;
|
|
}
|
|
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
XP_GetString(XP_HTML_NEWS_ERROR),
|
|
cd->response_txt);
|
|
if(ce->status > -1)
|
|
ce->status = PUTSTRING(cd->output_buffer);
|
|
|
|
if(cd->type_wanted == ARTICLE_WANTED ||
|
|
cd->type_wanted == CANCEL_WANTED)
|
|
{
|
|
XP_STRCPY(cd->output_buffer,
|
|
XP_GetString(XP_HTML_ARTICLE_EXPIRED));
|
|
if(ce->status > -1)
|
|
PUTSTRING(cd->output_buffer);
|
|
|
|
if (cd->path && *cd->path && ce->status > -1)
|
|
{
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"<P><%.512s>", cd->path);
|
|
if (cd->article_num > 0)
|
|
{
|
|
PR_snprintf(cd->output_buffer+XP_STRLEN(cd->output_buffer),
|
|
OUTPUT_BUFFER_SIZE-XP_STRLEN(cd->output_buffer),
|
|
" (%lu)\n", (unsigned long) cd->article_num);
|
|
if (ce->URL_s->msg_pane)
|
|
MSG_MarkMessageKeyRead(ce->URL_s->msg_pane, cd->article_num, "");
|
|
}
|
|
PUTSTRING(cd->output_buffer);
|
|
PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "<P> <A HREF=\"news://%s/%s?list-ids\">%s</A> </P>\n", cd->control_con->hostname, cd->group_name, XP_GetString(MK_MSG_EXPIRE_NEWS_ARTICLES));
|
|
PUTSTRING(cd->output_buffer);
|
|
}
|
|
|
|
if(ce->status > -1)
|
|
{
|
|
COMPLETE_STREAM;
|
|
cd->stream = 0;
|
|
}
|
|
}
|
|
else
|
|
if (cd->type_wanted == SEARCH_WANTED)
|
|
ce->status = net_xpat_send(ce);
|
|
|
|
ce->URL_s->expires = 1;
|
|
/* ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
/* FORMAT_OUT is not FO_PRESENT - so instead of emitting an HTML
|
|
error message, present it in a dialog box. */
|
|
|
|
PR_snprintf(cd->output_buffer, /* #### i18n */
|
|
OUTPUT_BUFFER_SIZE,
|
|
XP_GetString(MK_NEWS_ERROR_FMT),
|
|
cd->response_txt);
|
|
ce->URL_s->error_msg = XP_STRDUP (cd->output_buffer);
|
|
}
|
|
|
|
/* 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 == 4)
|
|
cd->next_state = NEWS_ERROR;
|
|
else
|
|
cd->next_state = NNTP_ERROR;
|
|
|
|
/* If we ever get an error, let's re-issue the GROUP command
|
|
before the next ARTICLE command; the overhead isn't very high,
|
|
and weird stuff seems to be happening... */
|
|
FREEIF(cd->control_con->current_group);
|
|
|
|
/* cd->control_con->prev_cache = FALSE; to keep if from reconnecting */
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
|
|
/* gets the response code from the nntp server and the
|
|
* response line
|
|
*
|
|
* returns the TCP return code from the read
|
|
*/
|
|
PRIVATE int
|
|
net_news_response (ActiveEntry * ce)
|
|
{
|
|
char * line;
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
NNTP_LOG_READ(cd->data_buf);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
/* if TCP error of if there is not a full line yet return
|
|
*/
|
|
if(ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR,
|
|
SOCKET_ERRNO);
|
|
|
|
/* return TCP error
|
|
*/
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
else if(!line)
|
|
{
|
|
return ce->status;
|
|
}
|
|
|
|
cd->pause_for_read = FALSE; /* don't pause if we got a line */
|
|
|
|
if(ce->bytes_received == 0)
|
|
{
|
|
HG47352
|
|
}
|
|
|
|
/* almost correct
|
|
*/
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
|
|
}
|
|
|
|
StrAllocCopy(cd->response_txt, line+4);
|
|
|
|
cd->previous_response_code = cd->response_code;
|
|
|
|
sscanf(line, "%d", &cd->response_code);
|
|
|
|
/* authentication required can come at any time
|
|
*/
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
/*
|
|
* This is an effort of trying to cache the username/password
|
|
* per newsgroup. It is extremely hard to make it work along with
|
|
* nntp voluntary password checking mechanism. We are backing this
|
|
* feature out. Instead of touching various of backend msg files
|
|
* at this late Dogbert 4.0 beta4 game, the infrastructure will
|
|
* remain in the msg library. We only modify codes within this file.
|
|
* Maybe one day we will try to do it again. Zzzzz -- jht
|
|
*/
|
|
if(480 == cd->response_code || 450 == cd->response_code ||
|
|
502 == cd->response_code)
|
|
{
|
|
cd->next_state = NNTP_BEGIN_AUTHORIZE;
|
|
if (502 == cd->response_code) {
|
|
if (2 == cd->previous_response_code/100) {
|
|
if (net_news_last_username_probably_valid) {
|
|
net_news_last_username_probably_valid = FALSE;
|
|
}
|
|
else {
|
|
MSG_SetNewsgroupUsername(cd->pane, NULL);
|
|
MSG_SetNewsgroupPassword(cd->pane, NULL);
|
|
}
|
|
}
|
|
else {
|
|
net_news_last_username_probably_valid = FALSE;
|
|
if (NNTP_PASSWORD_RESPONSE == cd->next_state_after_response) {
|
|
MSG_SetNewsgroupUsername(cd->pane, NULL);
|
|
MSG_SetNewsgroupPassword(cd->pane, NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
#else
|
|
if (480 == cd->response_code || 450 == cd->response_code)
|
|
{
|
|
cd->next_state = NNTP_BEGIN_AUTHORIZE;
|
|
}
|
|
else if (502 == cd->response_code)
|
|
{
|
|
net_news_last_username_probably_valid = FALSE;
|
|
#if defined(SingleSignon)
|
|
SI_RemoveUser(ce->URL_s->address, NULL, TRUE);
|
|
#endif
|
|
return net_display_html_error_state(ce);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
cd->next_state = cd->next_state_after_response;
|
|
}
|
|
|
|
return(0); /* everything ok */
|
|
}
|
|
|
|
HG40560
|
|
|
|
|
|
/* interpret the server response after the connect
|
|
*
|
|
* returns negative if the server responds unexpectedly
|
|
*/
|
|
PRIVATE int
|
|
net_nntp_login_response (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
XP_Bool postingAllowed = cd->response_code == 200;
|
|
|
|
if((cd->response_code/100)!=2)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);
|
|
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->control_con->prev_cache = FALSE; /* to keep if from reconnecting */
|
|
return MK_BAD_NNTP_CONNECTION;
|
|
}
|
|
|
|
cd->control_con->posting_allowed = postingAllowed; /* ###phil dead code */
|
|
MSG_SetNewsHostPostingAllowed (cd->host, postingAllowed);
|
|
|
|
cd->next_state = NNTP_SEND_MODE_READER;
|
|
return(0); /* good */
|
|
}
|
|
|
|
PRIVATE int
|
|
net_nntp_send_mode_reader (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
XP_STRCPY(cd->output_buffer, "MODE READER" CRLF);
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_SEND_MODE_READER_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return(ce->status);
|
|
|
|
}
|
|
|
|
PRIVATE int
|
|
net_nntp_send_mode_reader_response (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
cd->mode_reader_performed = TRUE;
|
|
|
|
/* ignore the response code and continue
|
|
*/
|
|
|
|
if (MSG_GetNewsHostPushAuth(cd->host))
|
|
/* if the news host is set up to require volunteered (pushed) authentication,
|
|
* do that before we do anything else
|
|
*/
|
|
cd->next_state = NNTP_BEGIN_AUTHORIZE;
|
|
else
|
|
cd->next_state = SEND_LIST_EXTENSIONS;
|
|
|
|
return(0);
|
|
}
|
|
|
|
PRIVATE int net_nntp_send_list_extensions (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
XP_STRCPY(cd->output_buffer, "LIST EXTENSIONS" CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = SEND_LIST_EXTENSIONS_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_nntp_send_list_extensions_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
if (cd->response_code > 200 && cd->response_code < 300)
|
|
{
|
|
char *line = NULL;
|
|
MSG_NewsHost *host = cd->host;
|
|
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
MSG_AddNewsExtension (host, line);
|
|
else
|
|
{
|
|
/* tell libmsg that it's ok to ask this news host for extensions */
|
|
MSG_SupportsNewsExtensions (cd->host, TRUE);
|
|
|
|
/* all extensions received */
|
|
cd->next_state = SEND_LIST_SEARCHES;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
}
|
|
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.
|
|
*/
|
|
MSG_SupportsNewsExtensions (cd->host, FALSE);
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_nntp_send_list_searches (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
if (MSG_QueryNewsExtension (cd->host, "SEARCH"))
|
|
{
|
|
XP_STRCPY(cd->output_buffer, "LIST SEARCHES" CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = SEND_LIST_SEARCHES_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* since SEARCH isn't supported, move on to GET */
|
|
cd->next_state = NNTP_GET_PROPERTIES;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_nntp_send_list_searches_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
char *line = NULL;
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
MSG_AddSearchableGroup (cd->host, line);
|
|
}
|
|
else
|
|
{
|
|
/* all searchable groups received */
|
|
/* LIST SRCHFIELDS is legal if the server supports the SEARCH extension, which */
|
|
/* we already know it does */
|
|
cd->next_state = NNTP_LIST_SEARCH_HEADERS;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_send_list_search_headers (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
XP_STRCPY(cd->output_buffer, "LIST SRCHFIELDS" CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LIST_SEARCH_HEADERS_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_send_list_search_headers_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
MSG_NewsHost *host = cd->host;
|
|
|
|
char *line = NULL;
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
MSG_AddSearchableHeader (host, line);
|
|
else
|
|
{
|
|
cd->next_state = NNTP_GET_PROPERTIES;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int nntp_get_properties (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
if (MSG_QueryNewsExtension (cd->host, "SETGET"))
|
|
{
|
|
XP_STRCPY(cd->output_buffer, "GET" CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_GET_PROPERTIES_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* since GET isn't supported, move on LIST SUBSCRIPTIONS */
|
|
cd->next_state = SEND_LIST_SUBSCRIPTIONS;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int nntp_get_properties_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
char *line = NULL;
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
char *propertyName = XP_STRDUP(line);
|
|
if (propertyName)
|
|
{
|
|
char *space = XP_STRCHR(propertyName, ' ');
|
|
if (space)
|
|
{
|
|
char *propertyValue = space + 1;
|
|
*space = '\0';
|
|
MSG_AddPropertyForGet (cd->host,
|
|
propertyName, propertyValue);
|
|
}
|
|
XP_FREE(propertyName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* all GET properties received, move on to LIST SUBSCRIPTIONS */
|
|
cd->next_state = SEND_LIST_SUBSCRIPTIONS;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_send_list_subscriptions (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
#ifdef LATER
|
|
if (MSG_QueryNewsExtension (cd->host, "LISTSUBSCR"))
|
|
#else
|
|
if (0)
|
|
#endif
|
|
{
|
|
XP_STRCPY(cd->output_buffer, "LIST SUBSCRIPTIONS" CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = SEND_LIST_SUBSCRIPTIONS_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* since LIST SUBSCRIPTIONS isn't supported, move on to real work */
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_send_list_subscriptions_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
char *line = NULL;
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
{
|
|
#ifdef LATER
|
|
char *urlScheme = cd->control_con->secure ? "snews:" : "news:";
|
|
char *url = PR_smprintf ("%s//%s/%s", urlScheme, cd->control_con->hostname, line);
|
|
if (url)
|
|
MSG_AddSubscribedNewsgroup (cd->pane, url);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* all default subscriptions received */
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
/* figure out what the first command is and send it
|
|
*
|
|
* returns the status from the NETWrite
|
|
*/
|
|
PRIVATE int
|
|
net_send_first_nntp_command (ActiveEntry *ce)
|
|
{
|
|
char *command=0;
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->type_wanted == ARTICLE_WANTED)
|
|
{
|
|
const char *group = 0;
|
|
uint32 number = 0;
|
|
|
|
MSG_NewsGroupAndNumberOfID (cd->pane, cd->path,
|
|
&group, &number);
|
|
if (group && number)
|
|
{
|
|
cd->article_num = number;
|
|
StrAllocCopy (cd->group_name, group);
|
|
|
|
if (cd->control_con->current_group && !XP_STRCMP (cd->control_con->current_group, group))
|
|
cd->next_state = NNTP_SEND_ARTICLE_NUMBER;
|
|
else
|
|
cd->next_state = NNTP_SEND_GROUP_FOR_ARTICLE;
|
|
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(cd->type_wanted == NEWS_POST && !ce->URL_s->post_data)
|
|
{
|
|
XP_ASSERT(0);
|
|
return(-1);
|
|
}
|
|
else if(cd->type_wanted == NEWS_POST)
|
|
{ /* posting to the news group */
|
|
StrAllocCopy(command, "POST");
|
|
}
|
|
else if(cd->type_wanted == READ_NEWS_RC)
|
|
{
|
|
if(ce->URL_s->method == URL_POST_METHOD ||
|
|
XP_STRCHR(ce->URL_s->address, '?'))
|
|
cd->next_state = NEWS_NEWS_RC_POST;
|
|
else
|
|
cd->next_state = NEWS_DISPLAY_NEWS_RC;
|
|
return(0);
|
|
}
|
|
else if(cd->type_wanted == NEW_GROUPS)
|
|
{
|
|
time_t last_update =
|
|
MSG_NewsgroupsLastUpdatedTime(cd->host);
|
|
char small_buf[64];
|
|
#ifndef NSPR20
|
|
PRTime expandedTime;
|
|
#else
|
|
PRExplodedTime expandedTime;
|
|
#endif /* NSPR20 */
|
|
|
|
if(!last_update)
|
|
{
|
|
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_NEWSGROUP_SCAN_ERROR);
|
|
cd->next_state = NEWS_ERROR;
|
|
return(MK_INTERRUPTED);
|
|
}
|
|
|
|
/* subtract some hours just to be sure */
|
|
last_update -= NEWGROUPS_TIME_OFFSET;
|
|
|
|
{
|
|
int64 secToUSec, timeInSec, timeInUSec;
|
|
LL_I2L(timeInSec, last_update);
|
|
LL_I2L(secToUSec, PR_USEC_PER_SEC);
|
|
LL_MUL(timeInUSec, timeInSec, secToUSec);
|
|
#ifndef NSPR20
|
|
PR_ExplodeTime( &expandedTime, timeInUSec );
|
|
#else
|
|
PR_ExplodeTime(timeInUSec, PR_LocalTimeParameters, &expandedTime);
|
|
#endif /* NSPR20 */
|
|
}
|
|
PR_FormatTimeUSEnglish(small_buf, sizeof(small_buf),
|
|
"NEWGROUPS %y%m%d %H%M%S", &expandedTime);
|
|
|
|
StrAllocCopy(command, small_buf);
|
|
|
|
}
|
|
else if(cd->type_wanted == LIST_WANTED)
|
|
{
|
|
|
|
cd->use_fancy_newsgroup_listing = FALSE;
|
|
if (MSG_NewsgroupsLastUpdatedTime(cd->host))
|
|
{
|
|
cd->next_state = DISPLAY_NEWSGROUPS;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
#ifdef BUG_21013
|
|
if(!FE_Confirm(ce->window_id, XP_GetString(XP_CONFIRM_SAVE_NEWSGROUPS)))
|
|
{
|
|
cd->next_state = NEWS_ERROR;
|
|
return(MK_INTERRUPTED);
|
|
}
|
|
#endif /* BUG_21013 */
|
|
|
|
if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
|
|
{
|
|
StrAllocCopy(command, "LIST XACTIVE");
|
|
cd->use_fancy_newsgroup_listing = TRUE;
|
|
}
|
|
else
|
|
{
|
|
StrAllocCopy(command, "LIST");
|
|
}
|
|
}
|
|
}
|
|
else if(cd->type_wanted == GROUP_WANTED)
|
|
{
|
|
/* Don't use MKParse because the news: access URL doesn't follow traditional
|
|
* rules. For instance, if the article reference contains a '#',
|
|
* the rest of it is lost.
|
|
*/
|
|
char * slash;
|
|
|
|
StrAllocCopy(command, "GROUP ");
|
|
|
|
slash = XP_STRCHR(cd->group_name, '/'); /* look for a slash */
|
|
cd->first_art = 0;
|
|
cd->last_art = 0;
|
|
if (slash)
|
|
{
|
|
*slash = '\0';
|
|
#ifdef __alpha
|
|
(void) sscanf(slash+1, "%d-%d", &cd->first_art, &cd->last_art);
|
|
#else
|
|
(void) sscanf(slash+1, "%ld-%ld", &cd->first_art, &cd->last_art);
|
|
#endif
|
|
}
|
|
|
|
StrAllocCopy (cd->control_con->current_group, cd->group_name);
|
|
StrAllocCat(command, cd->control_con->current_group);
|
|
}
|
|
else if (cd->type_wanted == SEARCH_WANTED)
|
|
{
|
|
if (MSG_QueryNewsExtension(cd->host, "SEARCH"))
|
|
{
|
|
/* use the SEARCH extension */
|
|
char *slash = XP_STRCHR (cd->command_specific_data, '/');
|
|
if (slash)
|
|
{
|
|
char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
|
|
if (allocatedCommand)
|
|
{
|
|
StrAllocCopy (command, allocatedCommand);
|
|
XP_FREE(allocatedCommand);
|
|
}
|
|
}
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_SEARCH_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
/* for XPAT, we have to GROUP into the group before searching */
|
|
StrAllocCopy (command, "GROUP ");
|
|
StrAllocCat (command, cd->group_name);
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_XPAT_SEND;
|
|
}
|
|
}
|
|
else if (cd->type_wanted == PRETTY_NAMES_WANTED)
|
|
{
|
|
if (MSG_QueryNewsExtension(cd->host, "LISTPRETTY"))
|
|
{
|
|
cd->next_state = NNTP_LIST_PRETTY_NAMES;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
cd->next_state = NNTP_ERROR;
|
|
}
|
|
}
|
|
else if (cd->type_wanted == PROFILE_WANTED)
|
|
{
|
|
char *slash = XP_STRCHR (cd->command_specific_data, '/');
|
|
if (slash)
|
|
{
|
|
char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
|
|
if (allocatedCommand)
|
|
{
|
|
StrAllocCopy (command, allocatedCommand);
|
|
XP_FREE(allocatedCommand);
|
|
}
|
|
}
|
|
cd->next_state = NNTP_RESPONSE;
|
|
if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW"))
|
|
cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
|
|
else
|
|
cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
|
|
}
|
|
else if (cd->type_wanted == IDS_WANTED)
|
|
{
|
|
char *slash = XP_STRCHR(cd->group_name, '/'); /* look for a slash */
|
|
if (slash)
|
|
*slash = '\0';
|
|
|
|
cd->next_state = NNTP_LIST_GROUP;
|
|
return 0;
|
|
}
|
|
else /* article or cancel */
|
|
{
|
|
if (cd->type_wanted == CANCEL_WANTED)
|
|
StrAllocCopy(command, "HEAD ");
|
|
else
|
|
StrAllocCopy(command, "ARTICLE ");
|
|
if (*cd->path != '<')
|
|
StrAllocCat(command,"<");
|
|
StrAllocCat(command, cd->path);
|
|
if (XP_STRCHR(command+8, '>')==0)
|
|
StrAllocCat(command,">");
|
|
}
|
|
|
|
StrAllocCat(command, CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
|
|
NNTP_LOG_WRITE(command);
|
|
XP_FREE(command);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
if (cd->type_wanted != SEARCH_WANTED && cd->type_wanted != PROFILE_WANTED)
|
|
cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
return(ce->status);
|
|
|
|
} /* sent first command */
|
|
|
|
|
|
/* interprets the server response from the first command sent
|
|
*
|
|
* returns negative if the server responds unexpectedly
|
|
*/
|
|
PRIVATE int
|
|
net_send_first_nntp_command_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
int major_opcode = cd->response_code/100;
|
|
|
|
if((major_opcode == 3 && cd->type_wanted == NEWS_POST)
|
|
|| (major_opcode == 2 && cd->type_wanted != NEWS_POST) )
|
|
{
|
|
|
|
cd->next_state = SETUP_NEWS_STREAM;
|
|
cd->some_protocol_succeeded = TRUE;
|
|
|
|
return(0); /* good */
|
|
}
|
|
else
|
|
{
|
|
if (cd->response_code == 411 && cd->type_wanted == GROUP_WANTED)
|
|
MSG_GroupNotFound(cd->pane, cd->host,
|
|
cd->control_con->current_group, TRUE /* opening group */);
|
|
return net_display_html_error_state(ce);
|
|
}
|
|
|
|
/* start the graph progress indicator
|
|
*/
|
|
FE_GraphProgressInit(ce->window_id, ce->URL_s, ce->URL_s->content_length);
|
|
cd->destroy_graph_progress = TRUE; /* we will need to destroy it */
|
|
cd->original_content_length = ce->URL_s->content_length;
|
|
|
|
return(ce->status);
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_send_group_for_article (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
StrAllocCopy (cd->control_con->current_group, cd->group_name);
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"GROUP %.512s" CRLF,
|
|
cd->control_con->current_group);
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
|
|
XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return(ce->status);
|
|
}
|
|
|
|
PRIVATE int
|
|
net_send_group_for_article_response (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
/* ignore the response code and continue
|
|
*/
|
|
cd->next_state = NNTP_SEND_ARTICLE_NUMBER;
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_send_article_number (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"ARTICLE %lu" CRLF,
|
|
cd->article_num);
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
|
|
XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return(ce->status);
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
news_generate_news_url_fn (const char *dest, void *closure,
|
|
MimeHeaders *headers)
|
|
{
|
|
ActiveEntry *ce = (ActiveEntry *) closure;
|
|
/* NOTE: you can't use NewsConData here, because ce might refer
|
|
to a file in the disk cache rather than an NNTP connection. */
|
|
char *prefix = NET_ParseURL(ce->URL_s->address,
|
|
(GET_PROTOCOL_PART | GET_HOST_PART));
|
|
char *new_dest = NET_Escape (dest, URL_XALPHAS);
|
|
char *result = (char *) XP_ALLOC (XP_STRLEN (prefix) +
|
|
(new_dest ? XP_STRLEN (new_dest) : 0)
|
|
+ 40);
|
|
if (result && prefix)
|
|
{
|
|
XP_STRCPY (result, prefix);
|
|
if (prefix[XP_STRLEN(prefix) - 1] != ':')
|
|
/* If there is a host, it must be terminated with a slash. */
|
|
XP_STRCAT (result, "/");
|
|
XP_STRCAT (result, new_dest);
|
|
}
|
|
FREEIF (prefix);
|
|
FREEIF (new_dest);
|
|
return result;
|
|
}
|
|
|
|
static char *
|
|
news_generate_reference_url_fn (const char *dest, void *closure,
|
|
MimeHeaders *headers)
|
|
{
|
|
return news_generate_news_url_fn (dest, closure, headers);
|
|
}
|
|
|
|
|
|
|
|
/* Initiates the stream and sets state for the transfer
|
|
*
|
|
* returns negative if unable to setup stream
|
|
*/
|
|
PRIVATE int
|
|
net_setup_news_stream (ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->type_wanted == NEWS_POST)
|
|
{
|
|
NET_ClearReadSelect(ce->window_id, ce->socket);
|
|
NET_SetConnectSelect(ce->window_id, ce->socket);
|
|
#ifdef XP_WIN
|
|
cd->calling_netlib_all_the_time = TRUE;
|
|
NET_SetCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
#endif /* XP_WIN */
|
|
|
|
ce->con_sock = ce->socket;
|
|
cd->next_state = NNTP_SEND_POST_DATA;
|
|
|
|
NET_Progress(ce->window_id, XP_GetString(MK_MSG_DELIV_NEWS));
|
|
}
|
|
else if(cd->type_wanted == LIST_WANTED)
|
|
{
|
|
if (cd->use_fancy_newsgroup_listing)
|
|
cd->next_state = NNTP_LIST_XACTIVE_RESPONSE;
|
|
else
|
|
cd->next_state = NNTP_READ_LIST_BEGIN;
|
|
}
|
|
else if(cd->type_wanted == GROUP_WANTED)
|
|
cd->next_state = NNTP_XOVER_BEGIN;
|
|
else if(cd->type_wanted == NEW_GROUPS)
|
|
cd->next_state = NNTP_NEWGROUPS_BEGIN;
|
|
else if(cd->type_wanted == ARTICLE_WANTED ||
|
|
cd->type_wanted == CANCEL_WANTED)
|
|
cd->next_state = NNTP_BEGIN_ARTICLE;
|
|
else if (cd->type_wanted == SEARCH_WANTED)
|
|
cd->next_state = NNTP_XPAT_SEND;
|
|
else if (cd->type_wanted == PRETTY_NAMES_WANTED)
|
|
cd->next_state = NNTP_LIST_PRETTY_NAMES;
|
|
else if (cd->type_wanted == PROFILE_WANTED)
|
|
{
|
|
if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW"))
|
|
cd->next_state = NNTP_PROFILE_ADD;
|
|
else
|
|
cd->next_state = NNTP_PROFILE_DELETE;
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(0);
|
|
return -1;
|
|
}
|
|
|
|
return(0); /* good */
|
|
}
|
|
|
|
/* This doesn't actually generate HTML - but it is our hook into the
|
|
message display code, so that we can get some values out of it
|
|
after the headers-to-be-displayed have been parsed.
|
|
*/
|
|
static char *
|
|
news_generate_html_header_fn (const char *dest, void *closure,
|
|
MimeHeaders *headers)
|
|
{
|
|
ActiveEntry *ce = (ActiveEntry *) closure;
|
|
/* NOTE: you can't always use NewsConData here if, because ce might
|
|
refer to a file in the disk cache rather than an NNTP connection. */
|
|
NewsConData *cd = 0;
|
|
|
|
XP_ASSERT(ce->protocol == NEWS_TYPE_URL ||
|
|
ce->protocol == FILE_CACHE_TYPE_URL ||
|
|
ce->protocol == MEMORY_CACHE_TYPE_URL);
|
|
if (ce->protocol == NEWS_TYPE_URL)
|
|
cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd && cd->type_wanted == CANCEL_WANTED)
|
|
{
|
|
/* Save these for later (used in NEWS_DO_CANCEL state.) */
|
|
cd->cancel_from =
|
|
MimeHeaders_get(headers, HEADER_FROM, FALSE, FALSE);
|
|
cd->cancel_newsgroups =
|
|
MimeHeaders_get(headers, HEADER_NEWSGROUPS, FALSE, TRUE);
|
|
cd->cancel_distribution =
|
|
MimeHeaders_get(headers, HEADER_DISTRIBUTION, FALSE, FALSE);
|
|
cd->cancel_id =
|
|
MimeHeaders_get(headers, HEADER_MESSAGE_ID, FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
MSG_Pane *messagepane = ce->URL_s->msg_pane;
|
|
if (!messagepane)
|
|
{
|
|
NNTP_LOG_NOTE(("news_generate_html_header_fn: url->msg_pane NULL for URL: %s", ce->URL_s->address));
|
|
messagepane = MSG_FindPane(ce->window_id, MSG_MESSAGEPANE);
|
|
}
|
|
XP_ASSERT (!cd || cd->type_wanted == ARTICLE_WANTED);
|
|
|
|
if (messagepane)
|
|
MSG_ActivateReplyOptions (messagepane, headers);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
XP_Bool NET_IsNewsMessageURL (const char *url);
|
|
|
|
int
|
|
net_InitializeNewsFeData (ActiveEntry * ce)
|
|
{
|
|
MimeDisplayOptions *opt;
|
|
|
|
if (!NET_IsNewsMessageURL (ce->URL_s->address))
|
|
{
|
|
XP_ASSERT(0);
|
|
return -1;
|
|
}
|
|
|
|
if (ce->URL_s->fe_data)
|
|
{
|
|
XP_ASSERT(0);
|
|
return -1;
|
|
}
|
|
|
|
opt = XP_NEW (MimeDisplayOptions);
|
|
if (!opt) return MK_OUT_OF_MEMORY;
|
|
XP_MEMSET (opt, 0, sizeof(*opt));
|
|
|
|
opt->generate_reference_url_fn = news_generate_reference_url_fn;
|
|
opt->generate_news_url_fn = news_generate_news_url_fn;
|
|
opt->generate_header_html_fn = 0;
|
|
opt->generate_post_header_html_fn = news_generate_html_header_fn;
|
|
opt->html_closure = ce;
|
|
|
|
ce->URL_s->fe_data = opt;
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_begin_article(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
if (cd->type_wanted != ARTICLE_WANTED &&
|
|
cd->type_wanted != CANCEL_WANTED)
|
|
return 0;
|
|
|
|
/* Set up the HTML stream
|
|
*/
|
|
FREEIF (ce->URL_s->content_type);
|
|
ce->URL_s->content_type = XP_STRDUP (MESSAGE_RFC822);
|
|
|
|
#ifdef NO_ARTICLE_CACHEING
|
|
ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
|
|
#endif
|
|
|
|
if (cd->type_wanted == CANCEL_WANTED)
|
|
{
|
|
XP_ASSERT(ce->format_out == FO_PRESENT);
|
|
ce->format_out = FO_PRESENT;
|
|
}
|
|
|
|
/* Only put stuff in the fe_data if this URL is going to get
|
|
passed to MIME_MessageConverter(), since that's the only
|
|
thing that knows what to do with this structure. */
|
|
if (CLEAR_CACHE_BIT(ce->format_out) == FO_PRESENT)
|
|
{
|
|
ce->status = net_InitializeNewsFeData (ce);
|
|
if (ce->status < 0)
|
|
{
|
|
/* #### what error message? */
|
|
return ce->status;
|
|
}
|
|
}
|
|
|
|
cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id);
|
|
XP_ASSERT (cd->stream);
|
|
if (!cd->stream) return -1;
|
|
|
|
cd->next_state = NNTP_READ_ARTICLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_news_begin_authorize(ActiveEntry * ce)
|
|
{
|
|
char * command = 0;
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
char * username = 0, * munged_username = 0;
|
|
char * cp;
|
|
static char * last_username=0;
|
|
static char * last_username_hostname=0;
|
|
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
/* reuse cached username from newsgroup folder info*/
|
|
if (cd->pane &&
|
|
(!net_news_last_username_probably_valid ||
|
|
(last_username_hostname &&
|
|
strcasecomp(last_username_hostname, cd->control_con->hostname)))) {
|
|
username = SECNAV_UnMungeString(MSG_GetNewsgroupUsername(cd->pane));
|
|
if (username && last_username &&
|
|
!XP_STRCMP (username, last_username) &&
|
|
(cd->previous_response_code == 281 ||
|
|
cd->previous_response_code == 250 ||
|
|
cd->previous_response_code == 211)) {
|
|
FREEIF (username);
|
|
MSG_SetNewsgroupUsername(cd->pane, NULL);
|
|
MSG_SetNewsgroupPassword(cd->pane, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (cd->pane)
|
|
{
|
|
/* Following a snews://username:password@newhost.domain.com/newsgroup.topic
|
|
* backend calls MSG_Master::FindNewsHost() to locate the folderInfo and setting
|
|
* the username/password to the newsgroup folderInfo
|
|
*/
|
|
username = SECNAV_UnMungeString(MSG_GetNewsgroupUsername(cd->pane));
|
|
if (username && *username)
|
|
{
|
|
StrAllocCopy(last_username, username);
|
|
StrAllocCopy(last_username_hostname, cd->control_con->hostname);
|
|
/* use it for only once */
|
|
MSG_SetNewsgroupUsername(cd->pane, NULL);
|
|
}
|
|
else {
|
|
/* empty username; free and clear it so it will work with
|
|
* our logic
|
|
*/
|
|
FREEIF(username);
|
|
}
|
|
}
|
|
|
|
/* If the URL/cd->control_con->hostname contains @ this must be triggered
|
|
* from the bookmark. Use the embed username if we could.
|
|
*/
|
|
if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL)
|
|
{
|
|
/* in this case the username and possibly
|
|
* the password are in the URL
|
|
*/
|
|
char * colon;
|
|
*cp = '\0';
|
|
|
|
colon = XP_STRCHR(cd->control_con->hostname, ':');
|
|
if(colon)
|
|
*colon = '\0';
|
|
|
|
StrAllocCopy(username, cd->control_con->hostname);
|
|
StrAllocCopy(last_username, cd->control_con->hostname);
|
|
StrAllocCopy(last_username_hostname, cp+1);
|
|
|
|
*cp = '@';
|
|
|
|
if(colon)
|
|
*colon = ':';
|
|
}
|
|
/* reuse global saved username if we think it is
|
|
* valid
|
|
*/
|
|
if (!username && net_news_last_username_probably_valid)
|
|
{
|
|
if( last_username_hostname &&
|
|
!strcasecomp(last_username_hostname, cd->control_con->hostname) )
|
|
StrAllocCopy(username, last_username);
|
|
else
|
|
net_news_last_username_probably_valid = FALSE;
|
|
}
|
|
|
|
|
|
if (!username) {
|
|
#if defined(SingleSignon)
|
|
username = SI_Prompt(ce->window_id,
|
|
XP_GetString(XP_PROMPT_ENTER_USERNAME),
|
|
"",
|
|
cd->control_con->hostname);
|
|
|
|
#else
|
|
username = FE_Prompt(ce->window_id,
|
|
XP_GetString(XP_PROMPT_ENTER_USERNAME),
|
|
username ? username : "");
|
|
#endif
|
|
|
|
/* reset net_news_last_username_probably_valid to false */
|
|
net_news_last_username_probably_valid = FALSE;
|
|
if(!username) {
|
|
ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails( MK_NNTP_AUTH_FAILED, "Aborted by user");
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
else {
|
|
StrAllocCopy(last_username, username);
|
|
StrAllocCopy(last_username_hostname, cd->control_con->hostname);
|
|
}
|
|
}
|
|
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
if (cd->pane && !MSG_GetNewsgroupUsername(cd->pane)) {
|
|
munged_username = SECNAV_MungeString(username);
|
|
MSG_SetNewsgroupUsername(cd->pane, munged_username);
|
|
XP_FREEIF(munged_username);
|
|
}
|
|
#endif
|
|
|
|
StrAllocCopy(command, "AUTHINFO user ");
|
|
StrAllocCat(command, username);
|
|
StrAllocCat(command, CRLF);
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
|
|
NNTP_LOG_WRITE(command);
|
|
|
|
FREE(command);
|
|
FREE(username);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_AUTHORIZE_RESPONSE;;
|
|
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int
|
|
net_news_authorize_response(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
static char * last_password = 0;
|
|
static char * last_password_hostname = 0;
|
|
|
|
if (281 == cd->response_code || 250 == cd->response_code)
|
|
{
|
|
/* successful login */
|
|
|
|
/* If we're here because the host demanded authentication before we
|
|
* even sent a single command, then jump back to the beginning of everything
|
|
*/
|
|
if (!cd->mode_reader_performed)
|
|
cd->next_state = 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 (MSG_GetNewsHostPushAuth(cd->host))
|
|
cd->next_state = SEND_LIST_EXTENSIONS;
|
|
else
|
|
/* Normal authentication */
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
|
|
net_news_last_username_probably_valid = TRUE;
|
|
return(0);
|
|
}
|
|
else if (381 == cd->response_code)
|
|
{
|
|
/* password required
|
|
*/
|
|
char * command = 0;
|
|
char * password = 0, * munged_password = 0;
|
|
char * cp;
|
|
|
|
if (cd->pane)
|
|
{
|
|
password = SECNAV_UnMungeString(MSG_GetNewsgroupPassword(cd->pane));
|
|
/* use it only once */
|
|
MSG_SetNewsgroupPassword(cd->pane, NULL);
|
|
}
|
|
|
|
if (net_news_last_username_probably_valid
|
|
&& last_password
|
|
&& last_password_hostname
|
|
&& !strcasecomp(last_password_hostname, cd->control_con->hostname))
|
|
{
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
if (cd->pane)
|
|
password = SECNAV_UnMungeString(MSG_GetNewsgroupPassword(cd->pane));
|
|
#else
|
|
StrAllocCopy(password, last_password);
|
|
#endif
|
|
}
|
|
else if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL)
|
|
{
|
|
/* in this case the username and possibly
|
|
* the password are in the URL
|
|
*/
|
|
char * colon;
|
|
*cp = '\0';
|
|
|
|
colon = XP_STRCHR(cd->control_con->hostname, ':');
|
|
if(colon)
|
|
{
|
|
*colon = '\0';
|
|
|
|
StrAllocCopy(password, colon+1);
|
|
StrAllocCopy(last_password, colon+1);
|
|
StrAllocCopy(last_password_hostname, cp+1);
|
|
|
|
*colon = ':';
|
|
}
|
|
|
|
*cp = '@';
|
|
|
|
}
|
|
if (!password) {
|
|
#if defined(SingleSignon)
|
|
password =
|
|
SI_PromptPassword
|
|
(ce->window_id,
|
|
XP_GetString
|
|
(XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS),
|
|
cd->control_con->hostname,
|
|
TRUE);
|
|
#else
|
|
password =
|
|
FE_PromptPassword
|
|
(ce->window_id,
|
|
XP_GetString
|
|
(XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS));
|
|
#endif
|
|
net_news_last_username_probably_valid = FALSE;
|
|
}
|
|
|
|
if(!password) {
|
|
ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails(MK_NNTP_AUTH_FAILED, "Aborted by user");
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
else {
|
|
StrAllocCopy(last_password, password);
|
|
StrAllocCopy(last_password_hostname, cd->control_con->hostname);
|
|
}
|
|
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
if (cd->pane && !MSG_GetNewsgroupPassword(cd->pane)) {
|
|
munged_password = SECNAV_MungeString(password);
|
|
MSG_SetNewsgroupPassword(cd->pane, munged_password);
|
|
XP_FREEIF(munged_password);
|
|
}
|
|
#endif
|
|
|
|
StrAllocCopy(command, "AUTHINFO pass ");
|
|
StrAllocCat(command, password);
|
|
StrAllocCat(command, CRLF);
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
|
|
NNTP_LOG_WRITE(command);
|
|
|
|
FREE(command);
|
|
FREE(password);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_PASSWORD_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
return ce->status;
|
|
}
|
|
else
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(
|
|
MK_NNTP_AUTH_FAILED,
|
|
cd->response_txt ? cd->response_txt : "");
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
if (cd->pane)
|
|
MSG_SetNewsgroupUsername(cd->pane, NULL);
|
|
#endif
|
|
net_news_last_username_probably_valid = FALSE;
|
|
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
XP_ASSERT(0); /* should never get here */
|
|
return(-1);
|
|
|
|
}
|
|
|
|
PRIVATE int
|
|
net_news_password_response(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (281 == cd->response_code || 250 == cd->response_code)
|
|
{
|
|
/* successful login */
|
|
|
|
/* If we're here because the host demanded authentication before we
|
|
* even sent a single command, then jump back to the beginning of everything
|
|
*/
|
|
if (!cd->mode_reader_performed)
|
|
cd->next_state = 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 (MSG_GetNewsHostPushAuth(cd->host))
|
|
cd->next_state = SEND_LIST_EXTENSIONS;
|
|
else
|
|
/* Normal authentication */
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
|
|
net_news_last_username_probably_valid = TRUE;
|
|
MSG_ResetXOVER( ce->URL_s->msg_pane, &cd->xover_parse_state );
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(
|
|
MK_NNTP_AUTH_FAILED,
|
|
cd->response_txt ? cd->response_txt : "");
|
|
#ifdef CACHE_NEWSGRP_PASSWORD
|
|
if (cd->pane)
|
|
MSG_SetNewsgroupPassword(cd->pane, NULL);
|
|
#endif
|
|
return(MK_NNTP_AUTH_FAILED);
|
|
}
|
|
|
|
XP_ASSERT(0); /* should never get here */
|
|
return(-1);
|
|
}
|
|
|
|
PRIVATE int
|
|
net_display_newsgroups (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
cd->next_state = NEWS_DONE;
|
|
cd->pause_for_read = FALSE;
|
|
|
|
NNTP_LOG_NOTE(("about to display newsgroups. path: %s",cd->path));
|
|
|
|
#if 0
|
|
/* #### Now ignoring "news:alt.fan.*"
|
|
Need to open the root tree of the default news host and keep
|
|
opening one child at each level until we've exhausted the
|
|
wildcard...
|
|
*/
|
|
if(rv < 0)
|
|
return(rv);
|
|
else
|
|
#endif
|
|
return(MK_DATA_LOADED); /* all finished */
|
|
}
|
|
|
|
PRIVATE int
|
|
net_newgroups_begin (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
cd->next_state = NNTP_NEWGROUPS;
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));
|
|
|
|
ce->bytes_received = 0;
|
|
|
|
return(ce->status);
|
|
|
|
}
|
|
|
|
PRIVATE int
|
|
net_process_newgroups (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
char *line, *s, *s1=NULL, *s2=NULL, *flag=NULL;
|
|
int32 oldest, youngest;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if(!line)
|
|
return(ce->status); /* no line yet */
|
|
|
|
if(ce->status<0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
|
|
/* return TCP error
|
|
*/
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
/* End of list?
|
|
*/
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
cd->pause_for_read = FALSE;
|
|
if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
|
|
{
|
|
cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
|
|
if (cd->group_name)
|
|
{
|
|
cd->next_state = NNTP_LIST_XACTIVE;
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("listing xactive for %s\n", cd->group_name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
cd->next_state = NEWS_DONE;
|
|
|
|
if(ce->bytes_received == 0)
|
|
{
|
|
/* #### no new groups */
|
|
}
|
|
|
|
NET_SortNewsgroups(cd->control_con->hostname, cd->control_con->secure);
|
|
NET_SaveNewsgroupsToDisk(cd->control_con->hostname, cd->control_con->secure);
|
|
if(ce->status > 0)
|
|
return MK_DATA_LOADED;
|
|
else
|
|
return ce->status;
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
/* almost correct
|
|
*/
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
|
|
}
|
|
|
|
/* format is "rec.arts.movies.past-films 7302 7119 y"
|
|
*/
|
|
s = XP_STRCHR (line, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
s1 = s+1;
|
|
s = XP_STRCHR (s1, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
s2 = s+1;
|
|
s = XP_STRCHR (s2, ' ');
|
|
if (s)
|
|
{
|
|
*s = 0;
|
|
flag = s+1;
|
|
}
|
|
}
|
|
}
|
|
youngest = s2 ? atol(s1) : 0;
|
|
oldest = s1 ? atol(s2) : 0;
|
|
|
|
ce->bytes_received++; /* small numbers of groups never seem
|
|
* to trigger this
|
|
*/
|
|
|
|
MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
|
|
line, oldest, youngest, flag, FALSE);
|
|
if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
|
|
{
|
|
MSG_SetGroupNeedsExtraInfo(cd->host, line, TRUE);
|
|
}
|
|
return(ce->status);
|
|
}
|
|
|
|
|
|
/* Ahhh, this like print's out the headers and stuff
|
|
*
|
|
* always returns 0
|
|
*/
|
|
PRIVATE int
|
|
net_read_news_list_begin (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
cd->next_state = NNTP_READ_LIST;
|
|
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));
|
|
|
|
return(ce->status);
|
|
|
|
|
|
}
|
|
|
|
/* display a list of all or part of the newsgroups list
|
|
* from the news server
|
|
*/
|
|
PRIVATE int
|
|
net_read_news_list (ActiveEntry *ce)
|
|
{
|
|
char * line;
|
|
char * description;
|
|
int i=0;
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*) &cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if(!line)
|
|
return(ce->status); /* no line yet */
|
|
|
|
if(ce->status<0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
|
|
/* return TCP error
|
|
*/
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
/* End of list? */
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
|
|
{
|
|
cd->next_state = NNTP_LIST_PRETTY_NAMES;
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = DISPLAY_NEWSGROUPS;
|
|
}
|
|
cd->pause_for_read = FALSE;
|
|
|
|
NET_SortNewsgroups(cd->control_con->hostname, cd->control_con->secure);
|
|
NET_SaveNewsgroupsToDisk(cd->control_con->hostname, cd->control_con->secure);
|
|
return 0;
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
/* almost correct
|
|
*/
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
|
|
}
|
|
|
|
/* find whitespace seperator if it exits */
|
|
for(i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++)
|
|
; /* null body */
|
|
|
|
if(line[i] == '\0')
|
|
description = &line[i];
|
|
else
|
|
description = &line[i+1];
|
|
|
|
line[i] = 0; /* terminate group name */
|
|
|
|
/* store all the group names
|
|
*/
|
|
MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
|
|
line, 0, 0, "", FALSE);
|
|
|
|
|
|
return(ce->status);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* start the xover command
|
|
*/
|
|
PRIVATE int
|
|
net_read_xover_begin (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
int32 count; /* Response fields */
|
|
|
|
/* Make sure we never close and automatically reopen the connection at this
|
|
point; we'll confuse libmsg too much... */
|
|
|
|
cd->some_protocol_succeeded = TRUE;
|
|
|
|
/* 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.
|
|
*/
|
|
sscanf(cd->response_txt,
|
|
#ifdef __alpha
|
|
"%d %d %d",
|
|
#else
|
|
"%ld %ld %ld",
|
|
#endif
|
|
&count,
|
|
&cd->first_possible_art,
|
|
&cd->last_possible_art);
|
|
|
|
/* We now know there is a summary line there; make sure it has the
|
|
right numbers in it. */
|
|
ce->status = MSG_DisplaySubscribedGroup(cd->pane,
|
|
cd->host,
|
|
cd->group_name,
|
|
cd->first_possible_art,
|
|
cd->last_possible_art,
|
|
count, TRUE);
|
|
if (ce->status < 0) return ce->status;
|
|
|
|
cd->num_loaded = 0;
|
|
cd->num_wanted = net_NewsChunkSize > 0 ? net_NewsChunkSize : 1L << 30;
|
|
|
|
cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
PRIVATE int
|
|
net_figure_next_chunk(ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
XP_Bool secure_p = (ce->URL_s->address[0] == 's' ||
|
|
ce->URL_s->address[0] == 'S');
|
|
char *host_and_port = NET_ParseURL (ce->URL_s->address, GET_HOST_PART);
|
|
|
|
if (!host_and_port) return MK_OUT_OF_MEMORY;
|
|
|
|
if (cd->first_art > 0) {
|
|
ce->status = MSG_AddToKnownArticles(cd->pane, cd->host,
|
|
cd->group_name,
|
|
cd->first_art, cd->last_art);
|
|
if (ce->status < 0) {
|
|
FREEIF (host_and_port);
|
|
return ce->status;
|
|
}
|
|
}
|
|
|
|
|
|
if (cd->num_loaded >= cd->num_wanted) {
|
|
FREEIF (host_and_port);
|
|
cd->next_state = NEWS_PROCESS_XOVER;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
ce->status = MSG_GetRangeOfArtsToDownload(cd->pane,
|
|
&cd->xover_parse_state,
|
|
cd->host,
|
|
cd->group_name,
|
|
cd->first_possible_art,
|
|
cd->last_possible_art,
|
|
cd->num_wanted - cd->num_loaded,
|
|
&(cd->first_art),
|
|
&(cd->last_art));
|
|
|
|
if (ce->status < 0) {
|
|
FREEIF (host_and_port);
|
|
return ce->status;
|
|
}
|
|
|
|
|
|
if (cd->first_art <= 0 || cd->first_art > cd->last_art) {
|
|
/* Nothing more to get. */
|
|
FREEIF (host_and_port);
|
|
cd->next_state = NEWS_PROCESS_XOVER;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
NNTP_LOG_NOTE((" Chunk will be (%ld-%ld)", cd->first_art, cd->last_art));
|
|
|
|
cd->article_num = cd->first_art;
|
|
ce->status = MSG_InitXOVER (cd->pane,
|
|
cd->host, cd->group_name,
|
|
cd->first_art, cd->last_art,
|
|
cd->first_possible_art, cd->last_possible_art,
|
|
&cd->xover_parse_state);
|
|
FREEIF (host_and_port);
|
|
|
|
if (ce->status < 0) {
|
|
return ce->status;
|
|
}
|
|
|
|
cd->pause_for_read = FALSE;
|
|
if (cd->control_con->no_xover) cd->next_state = NNTP_READ_GROUP;
|
|
else cd->next_state = NNTP_XOVER_SEND;
|
|
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE int
|
|
net_xover_send (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"XOVER %ld-%ld" CRLF,
|
|
cd->first_art,
|
|
cd->last_art);
|
|
|
|
/* printf("XOVER %ld-%ld\n", cd->first_art, cd->last_art); */
|
|
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_XOVER_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_LISTARTICLES));
|
|
|
|
return((int) NET_BlockingWrite(ce->socket,
|
|
cd->output_buffer,
|
|
XP_STRLEN(cd->output_buffer)));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
PRIVATE int
|
|
net_read_xover_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
#ifdef TEST_NO_XOVER_SUPPORT
|
|
cd->response_code = 500; /* pretend XOVER generated an error */
|
|
#endif
|
|
|
|
if(cd->response_code != 224)
|
|
{
|
|
/* 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.
|
|
*/
|
|
/*XP_ASSERT (0);*/
|
|
cd->next_state = NNTP_READ_GROUP;
|
|
cd->control_con->no_xover = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NNTP_XOVER;
|
|
}
|
|
|
|
return(0); /* continue */
|
|
}
|
|
|
|
/* process the xover list as it comes from the server
|
|
* and load it into the sort list.
|
|
*/
|
|
PRIVATE int
|
|
net_read_xover (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
char *line;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
NNTP_LOG_NOTE(("received unexpected TCP EOF!!!! aborting!"));
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if(!line)
|
|
{
|
|
return(ce->status); /* no line yet or TCP error */
|
|
}
|
|
|
|
if(ce->status<0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR,
|
|
SOCKET_ERRNO);
|
|
|
|
/* return TCP error
|
|
*/
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if(line[0] == '.' && line[1] == '\0')
|
|
{
|
|
cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
|
|
cd->pause_for_read = FALSE;
|
|
return(0);
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
/* almost correct
|
|
*/
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status,
|
|
ce->URL_s->content_length);
|
|
|
|
}
|
|
|
|
ce->status = MSG_ProcessXOVER (cd->pane, line, &cd->xover_parse_state);
|
|
|
|
cd->num_loaded++;
|
|
|
|
return ce->status; /* keep going */
|
|
}
|
|
|
|
|
|
/* Finished processing all the XOVER data.
|
|
*/
|
|
PRIVATE int
|
|
net_process_xover (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
|
|
/* if (cd->xover_parse_state) { ### dmb - we need a different check */
|
|
ce->status = MSG_FinishXOVER (cd->pane, &cd->xover_parse_state, 0);
|
|
XP_ASSERT (!cd->xover_parse_state);
|
|
if (ce->status < 0) return ce->status;
|
|
|
|
cd->next_state = NEWS_DONE;
|
|
|
|
return(MK_DATA_LOADED);
|
|
}
|
|
|
|
|
|
PRIVATE int net_profile_add (ActiveEntry *ce)
|
|
{
|
|
#if 0
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
char *slash = XP_STRRCHR(ce->URL_s->address, '/');
|
|
if (slash)
|
|
{
|
|
XP_STRCPY (cd->output_buffer, slash + 1);
|
|
XP_STRCAT (cd->output_buffer, CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
ce->status = -1;
|
|
}
|
|
|
|
return ce->status;
|
|
#else
|
|
XP_ASSERT(FALSE);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
PRIVATE int net_profile_add_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
if (cd->response_code >= 200 && cd->response_code <= 299)
|
|
{
|
|
MSG_AddProfileGroup (cd->pane, cd->host, cd->response_txt);
|
|
cd->next_state = NEWS_DONE;
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE int net_profile_delete (ActiveEntry *ce)
|
|
{
|
|
#if 0
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
char *slash = XP_STRRCHR(ce->URL_s->address, '/');
|
|
if (slash)
|
|
{
|
|
XP_STRCPY (cd->output_buffer, slash + 1);
|
|
XP_STRCAT (cd->output_buffer, CRLF);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
ce->status = -1;
|
|
}
|
|
|
|
return ce->status;
|
|
#else
|
|
XP_ASSERT(FALSE);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
PRIVATE int net_profile_delete_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
if (cd->response_code >= 200 && cd->response_code <= 299)
|
|
cd->next_state = NEWS_DONE;
|
|
else
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
PRIVATE
|
|
int
|
|
net_read_news_group (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if(cd->article_num > cd->last_art)
|
|
{ /* end of groups */
|
|
|
|
cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
|
|
cd->pause_for_read = FALSE;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"HEAD %ld" CRLF,
|
|
cd->article_num++);
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_READ_GROUP_RESPONSE;
|
|
|
|
cd->pause_for_read = TRUE;
|
|
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, XP_STRLEN(cd->output_buffer)));
|
|
}
|
|
}
|
|
|
|
/* See if the "HEAD" command was successful
|
|
*/
|
|
PRIVATE int
|
|
net_read_news_group_response (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->response_code == 221)
|
|
{ /* Head follows - parse it:*/
|
|
cd->next_state = NNTP_READ_GROUP_BODY;
|
|
|
|
if(cd->message_id)
|
|
*cd->message_id = '\0';
|
|
|
|
/* Give the message number to the header parser. */
|
|
return MSG_ProcessNonXOVER (cd->pane, cd->response_txt,
|
|
&cd->xover_parse_state);
|
|
}
|
|
else
|
|
{
|
|
NNTP_LOG_NOTE(("Bad group header found!"));
|
|
cd->next_state = NNTP_READ_GROUP;
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* read the body of the "HEAD" command
|
|
*/
|
|
PRIVATE int
|
|
net_read_news_group_body (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
char *line;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
/* if TCP error of if there is not a full line yet return
|
|
*/
|
|
if(!line)
|
|
return ce->status;
|
|
|
|
if(ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
|
|
/* return TCP error
|
|
*/
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
NNTP_LOG_NOTE(("read_group_body: got line: %s|",line));
|
|
|
|
/* End of body? */
|
|
if (line[0]=='.' && line[1]=='\0')
|
|
{
|
|
cd->next_state = NNTP_READ_GROUP;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
else if (line [0] == '.' && line [1] == '.')
|
|
/* The NNTP server quotes all lines beginning with "." by doubling it. */
|
|
line++;
|
|
|
|
return MSG_ProcessNonXOVER (cd->pane, line, &cd->xover_parse_state);
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_send_news_post_data(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *) ce->con_data;
|
|
|
|
/* returns 0 on done and negative on error
|
|
* positive if it needs to continue.
|
|
*/
|
|
ce->status = NET_WritePostData(ce->window_id, ce->URL_s,
|
|
ce->socket,
|
|
&cd->write_post_data_data,
|
|
TRUE);
|
|
|
|
cd->pause_for_read = TRUE;
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
/* normal done
|
|
*/
|
|
XP_STRCPY(cd->output_buffer, CRLF "." CRLF);
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,
|
|
cd->output_buffer,
|
|
XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
NET_Progress(ce->window_id,
|
|
XP_GetString(XP_MESSAGE_SENT_WAITING_NEWS_REPLY));
|
|
|
|
NET_ClearConnectSelect(ce->window_id, ce->socket);
|
|
#ifdef XP_WIN
|
|
if(cd->calling_netlib_all_the_time)
|
|
{
|
|
cd->calling_netlib_all_the_time = FALSE;
|
|
NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
}
|
|
#endif
|
|
NET_SetReadSelect(ce->window_id, ce->socket);
|
|
ce->con_sock = 0;
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
|
|
return(0);
|
|
}
|
|
|
|
return(ce->status);
|
|
|
|
}
|
|
|
|
|
|
/* interpret the response code from the server
|
|
* after the post is done
|
|
*/
|
|
PRIVATE int
|
|
net_send_news_post_data_response(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *) ce->con_data;
|
|
|
|
|
|
if (cd->response_code != 240) {
|
|
ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE,
|
|
cd->response_txt ? cd->response_txt : "");
|
|
if (cd->response_code == 441
|
|
&& MSG_GetPaneType(cd->pane) == MSG_COMPOSITIONPANE
|
|
&& MSG_IsDuplicatePost(cd->pane) &&
|
|
MSG_GetCompositionMessageID(cd->pane)) {
|
|
/* The news server won't let us post. We suspect that we're submitting
|
|
a duplicate post, and that's why it's failing. So, let's go see
|
|
if there really is a message out there with the same message-id.
|
|
If so, we'll just silently pretend everything went well. */
|
|
PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "STAT %s" CRLF,
|
|
MSG_GetCompositionMessageID(cd->pane));
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_CHECK_FOR_MESSAGE;
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
return (int) NET_BlockingWrite(ce->socket, cd->output_buffer,
|
|
XP_STRLEN(cd->output_buffer));
|
|
}
|
|
|
|
MSG_ClearCompositionMessageID(cd->pane); /* So that if the user tries
|
|
to just post again, we
|
|
won't immediately decide
|
|
that this was a duplicate
|
|
message and ignore the
|
|
error. */
|
|
cd->next_state = NEWS_ERROR;
|
|
return(MK_NNTP_ERROR_MESSAGE);
|
|
}
|
|
cd->next_state = NEWS_ERROR; /* even though it worked */
|
|
cd->pause_for_read = FALSE;
|
|
return(MK_DATA_LOADED);
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_check_for_message(ActiveEntry* ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *) ce->con_data;
|
|
|
|
cd->next_state = NEWS_ERROR;
|
|
if (cd->response_code >= 220 && cd->response_code <= 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). */
|
|
MSG_ClearCompositionMessageID(cd->pane);
|
|
return MK_NNTP_ERROR_MESSAGE;
|
|
}
|
|
}
|
|
|
|
PRIVATE int
|
|
net_DisplayNewsRC(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *) ce->con_data;
|
|
|
|
if(!cd->newsrc_performed)
|
|
{
|
|
cd->newsrc_performed = TRUE;
|
|
|
|
cd->newsrc_list_count = MSG_GetNewsRCCount(cd->pane, cd->host);
|
|
}
|
|
|
|
|
|
|
|
FREEIF(cd->control_con->current_group);
|
|
cd->control_con->current_group = MSG_GetNewsRCGroup(cd->pane, cd->host);
|
|
|
|
|
|
if(cd->control_con->current_group)
|
|
{
|
|
/* send group command to server
|
|
*/
|
|
int32 percent;
|
|
char *statusText;
|
|
|
|
PR_snprintf(NET_Socket_Buffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF,
|
|
cd->control_con->current_group);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket, NET_Socket_Buffer,
|
|
XP_STRLEN(NET_Socket_Buffer));
|
|
NNTP_LOG_WRITE(NET_Socket_Buffer);
|
|
|
|
percent = (int32) (100 * (((double) cd->newsrc_list_index) /
|
|
((double) cd->newsrc_list_count)));
|
|
FE_SetProgressBarPercent (ce->window_id, percent);
|
|
statusText = PR_smprintf (XP_GetString(XP_PROGRESS_READ_NEWSGROUP_COUNTS),
|
|
(long) cd->newsrc_list_index, (long) cd->newsrc_list_count);
|
|
if (statusText)
|
|
{
|
|
FE_Progress (ce->window_id, statusText);
|
|
XP_FREE(statusText);
|
|
}
|
|
|
|
cd->newsrc_list_index++;
|
|
|
|
cd->pause_for_read = TRUE;
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NEWS_DISPLAY_NEWS_RC_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
if (cd->newsrc_list_count)
|
|
{
|
|
FE_SetProgressBarPercent (ce->window_id, -1);
|
|
cd->newsrc_list_count = 0;
|
|
}
|
|
else if (cd->response_code == 215)
|
|
{
|
|
/*
|
|
* 5-9-96 jefft
|
|
* If for some reason the news server returns an empty
|
|
* newsgroups list with a nntp response code 215 -- list of
|
|
* newsgroups follows. We set ce->status to MK_EMPTY_NEWS_LIST
|
|
* to end the infinite dialog loop.
|
|
*/
|
|
ce->status = MK_EMPTY_NEWS_LIST;
|
|
}
|
|
cd->next_state = NEWS_DONE;
|
|
|
|
if(ce->status > -1)
|
|
return MK_DATA_LOADED;
|
|
else
|
|
return(ce->status);
|
|
}
|
|
|
|
return(ce->status); /* keep going */
|
|
|
|
}
|
|
|
|
/* Parses output of GROUP command */
|
|
PRIVATE int
|
|
net_DisplayNewsRCResponse(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *) ce->con_data;
|
|
|
|
if(cd->response_code == 211)
|
|
{
|
|
char *num_arts = 0, *low = 0, *high = 0, *group = 0;
|
|
int32 first_art, last_art;
|
|
|
|
/* line looks like:
|
|
* 211 91 3693 3789 comp.infosystems
|
|
*/
|
|
|
|
num_arts = cd->response_txt;
|
|
low = XP_STRCHR(num_arts, ' ');
|
|
|
|
if(low)
|
|
{
|
|
first_art = atol(low);
|
|
*low++ = '\0';
|
|
high= XP_STRCHR(low, ' ');
|
|
}
|
|
if(high)
|
|
{
|
|
*high++ = '\0';
|
|
group = XP_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. */
|
|
XP_STRTOK(group, " ");
|
|
last_art = atol(high);
|
|
}
|
|
|
|
ce->status = MSG_DisplaySubscribedGroup(cd->pane,
|
|
cd->host,
|
|
group,
|
|
low ? atol(low) : 0,
|
|
high ? atol(high) : 0,
|
|
atol(num_arts), FALSE);
|
|
if (ce->status < 0)
|
|
return ce->status;
|
|
}
|
|
else if (cd->response_code == 411)
|
|
{
|
|
MSG_GroupNotFound(cd->pane, cd->host, cd->control_con->current_group, FALSE);
|
|
}
|
|
/* it turns out subscribe ui depends on getting this displaysubscribedgroup call,
|
|
even if there was an error.
|
|
*/
|
|
if(cd->response_code != 211)
|
|
{
|
|
/* only on news server error or when zero articles
|
|
*/
|
|
ce->status = MSG_DisplaySubscribedGroup(cd->pane,
|
|
cd->host,
|
|
cd->control_con->current_group,
|
|
0, 0, 0, FALSE);
|
|
|
|
}
|
|
|
|
cd->next_state = NEWS_DISPLAY_NEWS_RC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRIVATE int
|
|
net_NewsRCProcessPost(ActiveEntry *ce)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
net_cancel_done_cb (MWContext *context, void *data, int status,
|
|
const char *file_name)
|
|
{
|
|
ActiveEntry *ce = (ActiveEntry *) data;
|
|
NewsConData *cd = (NewsConData *) ce->con_data;
|
|
cd->cancel_status = status;
|
|
XP_ASSERT(status < 0 || file_name);
|
|
cd->cancel_msg_file = (status < 0 ? 0 : XP_STRDUP(file_name));
|
|
}
|
|
|
|
|
|
static int
|
|
net_start_cancel (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData *) ce->con_data;
|
|
char *command = "POST" CRLF;
|
|
|
|
ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
|
|
NNTP_LOG_WRITE(command);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NEWS_DO_CANCEL;
|
|
cd->pause_for_read = TRUE;
|
|
return (ce->status);
|
|
}
|
|
|
|
|
|
static int
|
|
net_do_cancel (ActiveEntry *ce)
|
|
{
|
|
int status = 0;
|
|
NewsConData *cd = (NewsConData *) ce->con_data;
|
|
char *id, *subject, *newsgroups, *distribution, *other_random_headers, *body;
|
|
char *from, *old_from, *news_url;
|
|
int L;
|
|
MSG_CompositionFields *fields = NULL;
|
|
|
|
|
|
/* #### Should we do a more real check than this? If the POST command
|
|
didn't respond with "340 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?
|
|
*/
|
|
XP_ASSERT (cd->response_code == 340);
|
|
|
|
/* These shouldn't be set yet, since the headers haven't been "flushed" */
|
|
XP_ASSERT (!cd->cancel_id &&
|
|
!cd->cancel_from &&
|
|
!cd->cancel_newsgroups &&
|
|
!cd->cancel_distribution);
|
|
|
|
/* Write out a blank line. This will tell mimehtml.c that the headers
|
|
are done, and it will call news_generate_html_header_fn which will
|
|
notice the fields we're interested in.
|
|
*/
|
|
XP_STRCPY (cd->output_buffer, CRLF); /* CRLF used to be LINEBREAK.
|
|
LINEBREAK is platform dependent
|
|
and is only <CR> on a mac. This
|
|
CRLF is the protocol delimiter
|
|
and not platform dependent -km */
|
|
ce->status = PUTSTRING(cd->output_buffer);
|
|
if (ce->status < 0) return ce->status;
|
|
|
|
/* Now news_generate_html_header_fn should have been called, and these
|
|
should have values. */
|
|
id = cd->cancel_id;
|
|
old_from = cd->cancel_from;
|
|
newsgroups = cd->cancel_newsgroups;
|
|
distribution = cd->cancel_distribution;
|
|
|
|
XP_ASSERT (id && newsgroups);
|
|
if (!id || !newsgroups) return -1; /* "unknown error"... */
|
|
|
|
cd->cancel_newsgroups = 0;
|
|
cd->cancel_distribution = 0;
|
|
cd->cancel_from = 0;
|
|
cd->cancel_id = 0;
|
|
|
|
L = XP_STRLEN (id);
|
|
|
|
from = MIME_MakeFromField ();
|
|
subject = (char *) XP_ALLOC (L + 20);
|
|
other_random_headers = (char *) XP_ALLOC (L + 20);
|
|
body = (char *) XP_ALLOC (XP_STRLEN (XP_AppCodeName) + 100);
|
|
|
|
/* 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
|
|
*/
|
|
if (!MSG_QueryNewsExtension(cd->host, "CANCELCHK"))
|
|
{
|
|
char *us = MSG_ExtractRFC822AddressMailboxes (from);
|
|
char *them = MSG_ExtractRFC822AddressMailboxes (old_from);
|
|
XP_Bool ok = (us && them && !strcasecomp (us, them));
|
|
FREEIF(us);
|
|
FREEIF(them);
|
|
if (!ok)
|
|
{
|
|
status = MK_NNTP_CANCEL_DISALLOWED;
|
|
ce->URL_s->error_msg = XP_STRDUP (XP_GetString(status));
|
|
cd->next_state = NEWS_ERROR; /* even though it worked */
|
|
cd->pause_for_read = FALSE;
|
|
goto FAIL;
|
|
}
|
|
}
|
|
|
|
/* Last chance to cancel the cancel.
|
|
*/
|
|
if (!FE_Confirm (ce->window_id, XP_GetString(MK_NNTP_CANCEL_CONFIRM)))
|
|
{
|
|
status = MK_NNTP_NOT_CANCELLED;
|
|
goto FAIL;
|
|
}
|
|
|
|
news_url = ce->URL_s->address; /* we can just post here. */
|
|
|
|
if (!from || !subject || !other_random_headers || !body)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
|
|
XP_STRCPY (subject, "cancel ");
|
|
XP_STRCAT (subject, id);
|
|
|
|
XP_STRCPY (other_random_headers, "Control: cancel ");
|
|
XP_STRCAT (other_random_headers, id);
|
|
XP_STRCAT (other_random_headers, CRLF);
|
|
if (distribution)
|
|
{
|
|
XP_STRCAT (other_random_headers, "Distribution: ");
|
|
XP_STRCAT (other_random_headers, distribution);
|
|
XP_STRCAT (other_random_headers, CRLF);
|
|
}
|
|
|
|
XP_STRCPY (body, "This message was cancelled from within ");
|
|
XP_STRCAT (body, XP_AppCodeName);
|
|
XP_STRCAT (body, "." CRLF);
|
|
|
|
/* #### Would it be a good idea to S/MIME-sign all "cancel" messages? */
|
|
|
|
fields = MSG_CreateCompositionFields(from, 0, 0, 0, 0, 0, newsgroups,
|
|
0, 0, subject, id, other_random_headers,
|
|
0, 0, news_url,
|
|
FALSE, FALSE);
|
|
if (!fields)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
MSG_SetCompFieldsBoolHeader(fields, MSG_ENCRYPTED_BOOL_HEADER_MASK, FALSE);
|
|
MSG_SetCompFieldsBoolHeader(fields, MSG_SIGNED_BOOL_HEADER_MASK, FALSE);
|
|
|
|
cd->cancel_status = 0;
|
|
MSG_StartMessageDelivery (cd->pane, (void *) ce,
|
|
fields,
|
|
FALSE, /* digest_p */
|
|
TRUE, /* dont_deliver_p */
|
|
TEXT_PLAIN, body, XP_STRLEN (body),
|
|
0, /* other attachments */
|
|
NULL, /* multipart/related chunk */
|
|
net_cancel_done_cb);
|
|
|
|
/* Since there are no attachments, MSG_StartMessageDelivery will run
|
|
net_cancel_done_cb right away (it will be called before the return.) */
|
|
|
|
if (!cd->cancel_msg_file)
|
|
{
|
|
status = cd->cancel_status;
|
|
XP_ASSERT (status < 0);
|
|
if (status >= 0) status = -1;
|
|
goto FAIL;
|
|
}
|
|
|
|
/* Now send the data - do it blocking, who cares; the message is known
|
|
to be very small. First read the whole file into memory. Then delete
|
|
the file. Then do a blocking write of the data.
|
|
|
|
(We could use file-posting, maybe, but I couldn't figure out how.)
|
|
*/
|
|
{
|
|
char *data;
|
|
uint32 data_size, data_fp;
|
|
XP_StatStruct st;
|
|
int nread = 0;
|
|
XP_File file = XP_FileOpen (cd->cancel_msg_file,
|
|
xpFileToPost, XP_FILE_READ);
|
|
if (! file) return -1; /* "unknown error"... */
|
|
XP_Stat (cd->cancel_msg_file, &st, xpFileToPost);
|
|
|
|
data_fp = 0;
|
|
data_size = st.st_size + 20;
|
|
data = (char *) XP_ALLOC (data_size);
|
|
if (! data)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
|
|
while ((nread = XP_FileRead (data + data_fp, data_size - data_fp, file))
|
|
> 0)
|
|
data_fp += nread;
|
|
data [data_fp] = 0;
|
|
XP_FileClose (file);
|
|
XP_FileRemove (cd->cancel_msg_file, xpFileToPost);
|
|
|
|
XP_STRCAT (data, CRLF "." CRLF CRLF);
|
|
status = NET_BlockingWrite(ce->socket, data, XP_STRLEN(data));
|
|
NNTP_LOG_WRITE(data);
|
|
XP_FREE (data);
|
|
if (status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR,
|
|
status);
|
|
goto FAIL;
|
|
}
|
|
|
|
cd->pause_for_read = TRUE;
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
|
|
}
|
|
|
|
FAIL:
|
|
FREEIF (id);
|
|
FREEIF (from);
|
|
FREEIF (old_from);
|
|
FREEIF (subject);
|
|
FREEIF (newsgroups);
|
|
FREEIF (distribution);
|
|
FREEIF (other_random_headers);
|
|
FREEIF (body);
|
|
FREEIF (cd->cancel_msg_file);
|
|
if (fields)
|
|
MSG_DestroyCompositionFields(fields);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static int net_xpat_send (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData *) ce->con_data;
|
|
int status = 0;
|
|
char *thisTerm = NULL;
|
|
|
|
if (cd->current_search &&
|
|
(thisTerm = XP_STRCHR(cd->current_search, '/')) != NULL)
|
|
{
|
|
/* extract the XPAT encoding for one query term */
|
|
/* char *next_search = NULL; */
|
|
char *command = NULL;
|
|
char *unescapedCommand = NULL;
|
|
char *endOfTerm = NULL;
|
|
StrAllocCopy (command, ++thisTerm);
|
|
endOfTerm = XP_STRCHR(command, '/');
|
|
if (endOfTerm)
|
|
*endOfTerm = '\0';
|
|
StrAllocCat (command, CRLF);
|
|
|
|
unescapedCommand = MSG_UnEscapeSearchUrl(command);
|
|
|
|
/* send one term off to the server */
|
|
NNTP_LOG_WRITE(command);
|
|
status = NET_BlockingWrite(ce->socket, unescapedCommand, XP_STRLEN(unescapedCommand));
|
|
NNTP_LOG_WRITE(unescapedCommand);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_XPAT_RESPONSE;
|
|
cd->pause_for_read = TRUE;
|
|
|
|
XP_FREE(command);
|
|
XP_FREE(unescapedCommand);
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NEWS_DONE;
|
|
status = MK_DATA_LOADED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static int net_list_pretty_names(ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"LIST PRETTYNAMES %.512s" CRLF,
|
|
cd->group_name ? cd->group_name : "");
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace(cd->output_buffer);
|
|
#endif
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LIST_PRETTY_NAMES_RESPONSE;
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
static int net_list_pretty_names_response(ActiveEntry *ce)
|
|
{
|
|
char *line;
|
|
char *prettyName;
|
|
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->response_code != 215)
|
|
{
|
|
cd->next_state = DISPLAY_NEWSGROUPS;
|
|
/* cd->next_state = NEWS_DONE; */
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line,
|
|
&cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
int i;
|
|
/* find whitespace seperator if it exits */
|
|
for (i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++)
|
|
; /* null body */
|
|
|
|
if(line[i] == '\0')
|
|
prettyName = &line[i];
|
|
else
|
|
prettyName = &line[i+1];
|
|
|
|
line[i] = 0; /* terminate group name */
|
|
if (i > 0)
|
|
MSG_AddPrettyName(cd->host, line, prettyName);
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("adding pretty name %s\n", prettyName);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = DISPLAY_NEWSGROUPS; /* this assumes we were doing a list */
|
|
/* cd->next_state = NEWS_DONE; */ /* ### dmb - don't really know */
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int net_list_xactive(ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"LIST XACTIVE %.512s" CRLF,
|
|
cd->group_name);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LIST_XACTIVE_RESPONSE;
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
|
|
static int net_list_xactive_response(ActiveEntry *ce)
|
|
{
|
|
char *line;
|
|
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
XP_ASSERT(cd->response_code == 215);
|
|
if (cd->response_code != 215)
|
|
{
|
|
cd->next_state = DISPLAY_NEWSGROUPS;
|
|
/* cd->next_state = NEWS_DONE; */
|
|
cd->pause_for_read = FALSE;
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line,
|
|
&cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
/* Bug fix (sfraser) -- show download progress here
|
|
*/
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
char *s = line;
|
|
/* format is "rec.arts.movies.past-films 7302 7119 csp"
|
|
*/
|
|
while (*s && !XP_IS_SPACE(*s))
|
|
s++;
|
|
if (s)
|
|
{
|
|
char flags[32]; /* ought to be big enough */
|
|
*s = 0;
|
|
sscanf(s + 1,
|
|
#ifdef __alpha
|
|
"%d %d %31s",
|
|
#else
|
|
"%ld %ld %31s",
|
|
#endif
|
|
&cd->first_possible_art,
|
|
&cd->last_possible_art,
|
|
flags);
|
|
MSG_AddNewNewsGroup(cd->pane, cd->host, line,
|
|
cd->first_possible_art,
|
|
cd->last_possible_art, flags, TRUE /* xactive flags */);
|
|
/* 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. */
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("got xactive for %s of %s\n", line, flags);
|
|
#endif
|
|
MSG_SetGroupNeedsExtraInfo(cd->host, line, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cd->type_wanted == NEW_GROUPS && MSG_QueryNewsExtension(cd->host, "XACTIVE"))
|
|
{
|
|
char *old_group_name = cd->group_name;
|
|
cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
|
|
/* make sure we're not stuck on the same group... */
|
|
if (old_group_name && cd->group_name && XP_STRCMP(old_group_name, cd->group_name))
|
|
{
|
|
XP_FREE(old_group_name);
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("listing xactive for %s\n", cd->group_name);
|
|
#endif
|
|
cd->next_state = NNTP_LIST_XACTIVE;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
FREEIF(old_group_name);
|
|
cd->group_name = NULL;
|
|
}
|
|
}
|
|
if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
|
|
cd->next_state = NNTP_LIST_PRETTY_NAMES;
|
|
else
|
|
cd->next_state = DISPLAY_NEWSGROUPS; /* this assumes we were doing a list - who knows? */
|
|
/* cd->next_state = NEWS_DONE; */ /* ### dmb - don't really know */
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int net_list_group(ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
PR_snprintf(cd->output_buffer,
|
|
OUTPUT_BUFFER_SIZE,
|
|
"listgroup %.512s" CRLF,
|
|
cd->group_name);
|
|
MSG_InitAddArticleKeyToGroup(cd->pane, cd->host, cd->group_name,
|
|
&cd->newsgroup_parse_state);
|
|
ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
|
|
NNTP_LOG_WRITE(cd->output_buffer);
|
|
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LIST_GROUP_RESPONSE;
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
static int net_list_group_response(ActiveEntry *ce)
|
|
{
|
|
char *line;
|
|
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
XP_ASSERT(cd->response_code == 211);
|
|
if (cd->response_code != 211)
|
|
{
|
|
cd->next_state = NEWS_DONE;
|
|
cd->pause_for_read = FALSE;
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line,
|
|
&cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
long found_id = MSG_MESSAGEKEYNONE;
|
|
sscanf(line, "%ld", &found_id);
|
|
MSG_AddArticleKeyToGroup(cd->newsgroup_parse_state, (int32) found_id);
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NEWS_DONE; /* ### dmb - don't really know */
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int net_xpat_response (ActiveEntry *ce)
|
|
{
|
|
char *line;
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->response_code != 221)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line,
|
|
&cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
NNTP_LOG_READ(line);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
if (line[0] != '.')
|
|
{
|
|
long articleNumber;
|
|
sscanf(line, "%ld", &articleNumber);
|
|
MSG_AddNewsXpatHit (ce->window_id, (uint32) articleNumber);
|
|
}
|
|
else
|
|
{
|
|
/* set up the next term for next time around */
|
|
char *nextTerm = XP_STRCHR(cd->current_search, '/');
|
|
if (nextTerm)
|
|
cd->current_search = ++nextTerm;
|
|
else
|
|
cd->current_search = NULL;
|
|
|
|
cd->next_state = NNTP_XPAT_SEND;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRIVATE int net_nntp_search(ActiveEntry *ce)
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE int net_nntp_search_response(ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->response_code >= 200 && cd->response_code <= 299)
|
|
cd->next_state = NNTP_SEARCH_RESULTS;
|
|
else
|
|
cd->next_state = NEWS_DONE;
|
|
cd->pause_for_read = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE int net_nntp_search_results (ActiveEntry *ce)
|
|
{
|
|
NewsConData *cd = (NewsConData*) ce->con_data;
|
|
|
|
char *line = NULL;
|
|
ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
|
|
&cd->data_buf_size, (Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return MK_NNTP_SERVER_ERROR;
|
|
}
|
|
if (!line)
|
|
return ce->status; /* no line yet */
|
|
if (ce->status < 0)
|
|
{
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
|
|
/* return TCP error */
|
|
return MK_TCP_READ_ERROR;
|
|
}
|
|
|
|
if ('.' != line[0])
|
|
MSG_AddNewsSearchHit (ce->window_id, line);
|
|
else
|
|
{
|
|
/* all overview lines received */
|
|
cd->next_state = NEWS_DONE;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
|
|
return ce->status;
|
|
}
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
int
|
|
NET_parse_news_url (const char *url,
|
|
char **host_and_portP,
|
|
XP_Bool *securepP,
|
|
char **groupP,
|
|
char **message_idP,
|
|
char **command_specific_dataP)
|
|
{
|
|
int status = 0;
|
|
XP_Bool secure_p = FALSE;
|
|
char *host_and_port = 0;
|
|
unsigned long port = 0;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
const char *path_part;
|
|
char *s;
|
|
|
|
if (url[0] == 's' || url[1] == 'S')
|
|
{
|
|
secure_p = TRUE;
|
|
url++;
|
|
}
|
|
|
|
host_and_port = NET_ParseURL (url, GET_HOST_PART);
|
|
if (!host_and_port || !*host_and_port)
|
|
{
|
|
char* defhost = NULL;
|
|
int32 defport = 0;
|
|
FREEIF(host_and_port);
|
|
PREF_CopyCharPref("network.hosts.nntp_server", &defhost);
|
|
if (defhost && *defhost == '\0') {
|
|
XP_FREE(defhost);
|
|
defhost = NULL;
|
|
}
|
|
if (defhost) {
|
|
PREF_GetIntPref("news.server_port", &defport);
|
|
if (defport == (secure_p ? SECURE_NEWS_PORT : NEWS_PORT)) {
|
|
defport = 0;
|
|
}
|
|
if (defport) {
|
|
host_and_port = PR_smprintf("%s:%ld", defhost, (long) defport);
|
|
XP_FREE(defhost);
|
|
} else {
|
|
host_and_port = defhost;
|
|
}
|
|
if (!host_and_port) {
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!host_and_port)
|
|
{
|
|
status = MK_NO_NEWS_SERVER;
|
|
goto FAIL;
|
|
}
|
|
|
|
/* If a port was specified, but it was the default port, pretend
|
|
it wasn't specified.
|
|
*/
|
|
s = XP_STRCHR (host_and_port, ':');
|
|
if (s && sscanf (s+1, " %lu ", &port) == 1 && port == (secure_p ? SECURE_NEWS_PORT : NEWS_PORT))
|
|
*s = 0;
|
|
|
|
path_part = XP_STRCHR (url, ':');
|
|
XP_ASSERT (path_part);
|
|
if (!path_part)
|
|
{
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
path_part++;
|
|
if (path_part[0] == '/' && path_part[1] == '/')
|
|
{
|
|
/* Skip over host name. */
|
|
path_part = XP_STRCHR (path_part + 2, '/');
|
|
if (path_part)
|
|
path_part++;
|
|
}
|
|
if (!path_part)
|
|
path_part = "";
|
|
|
|
group = XP_STRDUP (path_part);
|
|
if (!group)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
|
|
NET_UnEscape (group);
|
|
|
|
/* "group" now holds the part after the host name:
|
|
"message@id?search" or "/group/xxx?search" or "/message?id@xx?search"
|
|
|
|
If there is an @, this is a message ID; else it is a group.
|
|
Either way, there may be search data at the end.
|
|
*/
|
|
|
|
s = XP_STRCHR (group, '@');
|
|
if (s)
|
|
{
|
|
message_id = group;
|
|
group = 0;
|
|
}
|
|
else if (!*group)
|
|
{
|
|
XP_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.)
|
|
Seperate 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 || host_and_port)
|
|
{
|
|
char *start;
|
|
if (message_id)
|
|
{
|
|
/* Move past the @. */
|
|
XP_ASSERT (s && *s == '@');
|
|
start = s;
|
|
}
|
|
else
|
|
{
|
|
start = group ? group : host_and_port;
|
|
}
|
|
|
|
/* Take off the "?" or "#" search data */
|
|
for (s = start; *s; s++)
|
|
if (*s == '?' || *s == '#')
|
|
break;
|
|
if (*s)
|
|
{
|
|
command_specific_data = XP_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)
|
|
{
|
|
XP_FREE (message_id);
|
|
message_id = 0;
|
|
}
|
|
else if (group && !*group)
|
|
{
|
|
XP_FREE (group);
|
|
group = 0;
|
|
}
|
|
}
|
|
|
|
FAIL:
|
|
XP_ASSERT (!message_id || message_id != group);
|
|
if (status >= 0)
|
|
{
|
|
*host_and_portP = host_and_port;
|
|
*securepP = secure_p;
|
|
*groupP = group;
|
|
*message_idP = message_id;
|
|
*command_specific_dataP = command_specific_data;
|
|
}
|
|
else
|
|
{
|
|
FREEIF (host_and_port);
|
|
FREEIF (group);
|
|
FREEIF (message_id);
|
|
FREEIF (command_specific_data);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
#if 0
|
|
static void
|
|
test (const char *url)
|
|
{
|
|
char *host_and_port = 0;
|
|
XP_Bool secure_p = FALSE;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
int status = NET_parse_news_url (url, &host_and_port, &secure_p,
|
|
&group, &message_id, &command_specific_data);
|
|
if (status < 0)
|
|
fprintf (stderr, "%s: status %d\n", url, status);
|
|
else
|
|
{
|
|
fprintf (stderr,
|
|
"%s:\n"
|
|
" host \"%s\", %ssecure,\n"
|
|
" group=\"%s\",\n"
|
|
" id=\"%s\",\n"
|
|
" search=\"%s\"\n\n",
|
|
url,
|
|
(host_and_port ? host_and_port : "(none)"),
|
|
(secure_p ? "" : "in"),
|
|
(group ? group : "(none)"),
|
|
(message_id ? message_id : "(none)"),
|
|
(command_specific_data ? command_specific_data : "(none)"));
|
|
}
|
|
FREEIF (host_and_port);
|
|
FREEIF (group);
|
|
FREEIF (message_id);
|
|
FREEIF (command_specific_data);
|
|
}
|
|
|
|
static void
|
|
test2 (void)
|
|
{
|
|
test ("news:");
|
|
test ("news:?search");
|
|
test ("news:#anchor");
|
|
test ("news:?search#anchor");
|
|
test ("news:#anchor?search");
|
|
test ("news://HOST");
|
|
test ("news://HOST?search");
|
|
test ("news://HOST#anchor");
|
|
test ("news://HOST?search#anchor");
|
|
test ("news://HOST#anchor?search");
|
|
test ("news://HOST/");
|
|
test ("news://HOST/?search");
|
|
test ("news://HOST/#anchor");
|
|
test ("news://HOST/?search#anchor");
|
|
test ("news://HOST/#anchor?search");
|
|
test ("news:GROUP");
|
|
test ("news:GROUP?search");
|
|
test ("news:GROUP#anchor");
|
|
test ("news:GROUP?search#anchor");
|
|
test ("news:GROUP#anchor?search");
|
|
test ("news:/GROUP");
|
|
test ("news:/GROUP?search");
|
|
test ("news:/GROUP#anchor");
|
|
test ("news:/GROUP?search#anchor");
|
|
test ("news:/GROUP#anchor?search");
|
|
test ("news://HOST/GROUP");
|
|
test ("news://HOST/GROUP?search");
|
|
test ("news://HOST/GROUP#anchor");
|
|
test ("news://HOST/GROUP?search#anchor");
|
|
test ("news://HOST/GROUP#anchor?search");
|
|
|
|
test ("news:GROUP/N1-N2");
|
|
test ("news:GROUP/N1-N2?search");
|
|
test ("news:GROUP/N1-N2#anchor");
|
|
test ("news:GROUP/N1-N2?search#anchor");
|
|
test ("news:GROUP/N1-N2#anchor?search");
|
|
test ("news:/GROUP/N1-N2");
|
|
test ("news:/GROUP/N1-N2?search");
|
|
test ("news:/GROUP/N1-N2#anchor");
|
|
test ("news:/GROUP/N1-N2?search#anchor");
|
|
test ("news:/GROUP/N1-N2#anchor?search");
|
|
|
|
test ("news://HOST/GROUP/N1-N2");
|
|
test ("news://HOST/GROUP/N1-N2?search");
|
|
test ("news://HOST/GROUP/N1-N2#anchor");
|
|
test ("news://HOST/GROUP/N1-N2?search#anchor");
|
|
test ("news://HOST/GROUP/N1-N2#anchor?search");
|
|
test ("news:<ID??##??ID@HOST>");
|
|
test ("news:<ID??##??ID@HOST>?search");
|
|
test ("news:<ID??##??ID@HOST>#anchor");
|
|
test ("news:<ID??##??ID@HOST>?search#anchor");
|
|
test ("news:<ID??##??ID@HOST>#anchor?search");
|
|
|
|
test ("news:<ID/ID/ID??##??ID@HOST>");
|
|
test ("news:<ID/ID/ID??##??ID@HOST>?search");
|
|
test ("news:<ID/ID/ID??##??ID@HOST>#anchor");
|
|
test ("news:<ID/ID/ID??##??ID@HOST>?search#anchor");
|
|
test ("news:<ID/ID/ID??##??ID@HOST>#anchor?search");
|
|
test ("news:</ID/ID/ID??##??ID@HOST>");
|
|
test ("news:</ID/ID/ID??##??ID@HOST>?search");
|
|
test ("news:</ID/ID/ID??##??ID@HOST>#anchor");
|
|
test ("news:</ID/ID/ID??##??ID@HOST>?search#anchor");
|
|
test ("news:</ID/ID/ID??##??ID@HOST>#anchor?search");
|
|
|
|
test ("news://HOST/<ID??##??ID@HOST>");
|
|
test ("news://HOST/<ID??##??ID@HOST>?search");
|
|
test ("news://HOST/<ID??##??ID@HOST>#anchor");
|
|
test ("news://HOST/<ID??##??ID@HOST>?search#anchor");
|
|
test ("news://HOST/<ID??##??ID@HOST>#anchor?search");
|
|
test ("news://HOST/<ID/ID/ID??##??ID@HOST>");
|
|
test ("news://HOST/<ID/ID/ID??##??ID@HOST>?search");
|
|
test ("news://HOST/<ID/ID/ID??##??ID@HOST>#anchor");
|
|
test ("news://HOST/<ID/ID/ID??##??ID@HOST>?search#anchor");
|
|
test ("news://HOST/<ID/ID/ID??##??ID@HOST>#anchor?search");
|
|
|
|
test ("news:ID/ID/ID??##??ID@HOST");
|
|
test ("news:ID/ID/ID??##??ID@HOST?search");
|
|
test ("news:ID/ID/ID??##??ID@HOST#anchor");
|
|
test ("news:ID/ID/ID??##??ID@HOST?search#anchor");
|
|
test ("news:ID/ID/ID??##??ID@HOST#anchor?search");
|
|
test ("news:/ID/ID/ID??##??ID@HOST");
|
|
test ("news:/ID/ID/ID??##??ID@HOST?search");
|
|
test ("news:/ID/ID/ID??##??ID@HOST#anchor");
|
|
test ("news:/ID/ID/ID??##??ID@HOST?search#anchor");
|
|
test ("news:/ID/ID/ID??##??ID@HOST#anchor?search");
|
|
|
|
test ("news://HOST/ID??##??ID@HOST");
|
|
test ("news://HOST/ID??##??ID@HOST?search");
|
|
test ("news://HOST/ID??##??ID@HOST#anchor");
|
|
test ("news://HOST/ID??##??ID@HOST?search#anchor");
|
|
test ("news://HOST/ID??##??ID@HOST#anchor?search");
|
|
test ("news://HOST/ID/ID/ID??##??ID@HOST");
|
|
test ("news://HOST/ID/ID/ID??##??ID@HOST?search");
|
|
test ("news://HOST/ID/ID/ID??##??ID@HOST#anchor");
|
|
test ("news://HOST/ID/ID/ID??##??ID@HOST?search#anchor");
|
|
test ("news://HOST/ID/ID/ID??##??ID@HOST#anchor?search");
|
|
}
|
|
#endif /* 0 */
|
|
|
|
|
|
/* Returns true if this URL is a reference to a news message,
|
|
that is, the kind of news URL entity that can be displayed
|
|
in a regular browser window, and doesn't involve all kinds
|
|
of other magic state and callbacks like listing a group.
|
|
*/
|
|
XP_Bool
|
|
NET_IsNewsMessageURL (const char *url)
|
|
{
|
|
char *host_and_port = 0;
|
|
XP_Bool secure_p = FALSE;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
int status = NET_parse_news_url (url, &host_and_port, &secure_p,
|
|
&group, &message_id, &command_specific_data);
|
|
XP_Bool result = FALSE;
|
|
if (status >= 0 && message_id && *message_id)
|
|
result = TRUE;
|
|
FREEIF (host_and_port);
|
|
FREEIF (group);
|
|
FREEIF (message_id);
|
|
FREEIF (command_specific_data);
|
|
return result;
|
|
}
|
|
|
|
XP_Bool
|
|
NET_IsNewsServerURL( const char *url)
|
|
{
|
|
char *host_and_port = 0;
|
|
XP_Bool secure_p = FALSE;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
int status = NET_parse_news_url (url, &host_and_port, &secure_p,
|
|
&group, &message_id, &command_specific_data);
|
|
XP_Bool result = FALSE;
|
|
if (status >= 0 && host_and_port && !group && !message_id)
|
|
result = TRUE;
|
|
FREEIF (host_and_port);
|
|
FREEIF (group);
|
|
FREEIF (message_id);
|
|
FREEIF (command_specific_data);
|
|
return result;
|
|
}
|
|
|
|
static XP_Bool nntp_are_connections_available (NNTPConnection *conn)
|
|
{
|
|
int connCount = 0;
|
|
|
|
NNTPConnection *tmpConn;
|
|
XP_List *tmpList = nntp_connection_list;
|
|
|
|
while ((tmpConn = (NNTPConnection*) XP_ListNextObject(tmpList)) != NULL)
|
|
{
|
|
NNTP_LOG_NOTE(("nntp_are_connections_available: comparing %08lX %s", tmpConn, tmpConn->busy ? "busy" : "available"));
|
|
|
|
if (conn != tmpConn && tmpConn->busy && !strcasecomp(tmpConn->hostname, conn->hostname))
|
|
connCount++;
|
|
}
|
|
|
|
return connCount < kMaxConnectionsPerHost;
|
|
}
|
|
|
|
static XP_Bool isOnline = TRUE;
|
|
static XP_Bool prefInitialized = FALSE;
|
|
|
|
/* fix Mac warning of missing prototype */
|
|
MODULE_PRIVATE int PR_CALLBACK
|
|
NET_OnlinePrefChangedFunc(const char *pref, void *data);
|
|
|
|
MODULE_PRIVATE int PR_CALLBACK NET_OnlinePrefChangedFunc(const char *pref, void *data)
|
|
{
|
|
int status=0;
|
|
int32 port=0;
|
|
char * socksHost = NULL;
|
|
char text[MAXHOSTNAMELEN + 8];
|
|
|
|
if (!XP_STRCASECMP(pref,"network.online"))
|
|
status = PREF_GetBoolPref("network.online", &isOnline);
|
|
|
|
if ( isOnline ) {
|
|
CACHE_CloseAllOpenSARCache();
|
|
|
|
/* If the user wants to use a socks server set it up. */
|
|
if ( (NET_GetProxyStyle() == PROXY_STYLE_MANUAL) ) {
|
|
PREF_CopyCharPref("network.hosts.socks_server",&socksHost);
|
|
PREF_GetIntPref("network.hosts.socks_serverport",&port);
|
|
if (socksHost && *socksHost && port) {
|
|
PR_snprintf(text, sizeof(text), "%s:%d", socksHost, port);
|
|
NET_SetSocksHost(text);
|
|
}
|
|
else {
|
|
NET_SetSocksHost(socksHost); /* NULL is ok */
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
CACHE_OpenAllSARCache();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* fix Mac warning of missing prototype */
|
|
MODULE_PRIVATE int PR_CALLBACK
|
|
NET_NewsMaxArticlesChangedFunc(const char *pref, void *data);
|
|
|
|
MODULE_PRIVATE int PR_CALLBACK NET_NewsMaxArticlesChangedFunc(const char *pref, void *data)
|
|
{
|
|
int status=0;
|
|
if (!XP_STRCASECMP(pref,"news.max_articles"))
|
|
{
|
|
int32 maxArticles;
|
|
|
|
status = PREF_GetIntPref("news.max_articles", &maxArticles);
|
|
NET_SetNumberOfNewsArticlesInListing(maxArticles);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
MODULE_PRIVATE int PR_CALLBACK net_news_timeout_changed (const char *pref, void *data)
|
|
{
|
|
return PREF_GetIntPref ("news.timeout", &net_news_timeout);
|
|
}
|
|
|
|
MODULE_PRIVATE XP_Bool
|
|
NET_IsOffline()
|
|
{
|
|
/* Cache this value, and register a pref callback to
|
|
find out when it changes.
|
|
*/
|
|
if (!prefInitialized)
|
|
{
|
|
/*int status =*/ PREF_GetBoolPref("network.online", &isOnline);
|
|
PREF_RegisterCallback("network.online",NET_OnlinePrefChangedFunc, NULL);
|
|
|
|
/* because this routine gets called so often, we can register this callback here too. */
|
|
PREF_RegisterCallback("news.max_articles", NET_NewsMaxArticlesChangedFunc, NULL);
|
|
|
|
PREF_GetIntPref ("news.timeout", &net_news_timeout);
|
|
PREF_RegisterCallback ("news.timeout", net_news_timeout_changed, NULL);
|
|
|
|
prefInitialized = TRUE;
|
|
}
|
|
return !isOnline;
|
|
}
|
|
|
|
PRIVATE int32
|
|
net_NewsLoad (ActiveEntry *ce)
|
|
{
|
|
int status = 0;
|
|
NewsConData *cd = XP_NEW(NewsConData);
|
|
XP_Bool secure_p = FALSE;
|
|
XP_Bool default_host = FALSE;
|
|
char *url = ce->URL_s->address;
|
|
char *host_and_port = 0;
|
|
int32 port = 0;
|
|
char *group = 0;
|
|
char *message_id = 0;
|
|
char *command_specific_data = 0;
|
|
XP_Bool cancel_p = FALSE;
|
|
char* colon;
|
|
MSG_NewsHost* defhost = NULL;
|
|
static XP_Bool collabra_enabled = TRUE;
|
|
static XP_Bool got_enabled_pref = FALSE;
|
|
|
|
if (!got_enabled_pref)
|
|
{
|
|
PREF_GetBoolPref("news.enabled",&collabra_enabled);
|
|
got_enabled_pref = TRUE;
|
|
}
|
|
|
|
/* fail on protected url types */
|
|
if(ce->protocol == INTERNAL_NEWS_TYPE_URL
|
|
&& !ce->URL_s->internal_url)
|
|
{
|
|
status = MK_MALFORMED_URL_ERROR;
|
|
goto FAIL;
|
|
}
|
|
|
|
if (!collabra_enabled)
|
|
{
|
|
status = MK_MSG_COLLABRA_DISABLED;
|
|
goto FAIL;
|
|
}
|
|
|
|
|
|
#ifndef NSPR20
|
|
PR_LogInit (&NNTPLog);
|
|
#endif
|
|
|
|
if(!cd)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
|
|
XP_MEMSET(cd, 0, sizeof(NewsConData));
|
|
ce->URL_s->content_length = 0;
|
|
|
|
if(!ce->proxy_addr)
|
|
{
|
|
/* look for an HTTPS proxy and use it if available, */
|
|
/* but the passed in one takes precedence */
|
|
StrAllocCopy(ce->proxy_addr,
|
|
NET_FindProxyHostForUrl(SECURE_HTTP_TYPE_URL,
|
|
ce->URL_s->address));
|
|
}
|
|
HG38737
|
|
|
|
cd->pane = ce->URL_s->msg_pane;
|
|
if (!cd->pane)
|
|
{
|
|
NNTP_LOG_NOTE(("NET_NewsLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address));
|
|
cd->pane = MSG_FindPane(ce->window_id, MSG_ANYPANE);
|
|
}
|
|
if (!cd->pane || MSG_GetContext(cd->pane) != ce->window_id)
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
status = -1; /* ### */
|
|
goto FAIL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
char urlDate[64];
|
|
time_t now = XP_TIME();
|
|
XP_StrfTime(ce->window_id, urlDate, sizeof(urlDate), XP_DATE_TIME_FORMAT, localtime(&now));
|
|
NNTP_LOG_NOTE (("******** Begin loading news URL [ %s ] at %s", ce->URL_s->address, urlDate));
|
|
}
|
|
#endif
|
|
|
|
cd->output_buffer = (char *) XP_ALLOC(OUTPUT_BUFFER_SIZE);
|
|
if(!cd->output_buffer)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
|
|
ce->con_data = cd;
|
|
ce->socket = NULL;
|
|
cd->article_num = -1;
|
|
|
|
XP_ASSERT (url);
|
|
if (!url)
|
|
{
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
|
|
|
|
status = NET_parse_news_url (url, &host_and_port, &secure_p,
|
|
&group, &message_id, &command_specific_data);
|
|
if (status < 0)
|
|
goto FAIL;
|
|
|
|
colon = XP_STRCHR (host_and_port, ':');
|
|
if (colon) {
|
|
unsigned long naturalLong;
|
|
*colon = '\0';
|
|
sscanf (colon+1, " %lu ", &naturalLong); /* %l is 64 bits on OSF1 */
|
|
port = (int32) naturalLong;
|
|
}
|
|
cd->host = MSG_CreateNewsHost(MSG_GetMaster(cd->pane), host_and_port,
|
|
secure_p, port);
|
|
if (colon) *colon = ':';
|
|
|
|
XP_ASSERT(cd->host);
|
|
if (!cd->host) {
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
|
|
if (message_id && command_specific_data && !XP_STRCMP (command_specific_data, "?cancel"))
|
|
cancel_p = TRUE;
|
|
|
|
StrAllocCopy(cd->path, message_id);
|
|
StrAllocCopy(cd->group_name, group);
|
|
|
|
/* make sure the user has a news host configured */
|
|
|
|
|
|
defhost = MSG_GetDefaultNewsHost(MSG_GetMaster(cd->pane));
|
|
|
|
if (defhost == NULL)
|
|
{
|
|
char* alert = NET_ExplainErrorDetails(MK_NNTP_SERVER_NOT_CONFIGURED);
|
|
FE_Alert(ce->window_id, alert);
|
|
#ifdef XP_MAC
|
|
/* AFTER the alert, not before! */
|
|
FE_EditPreference(PREF_NewsHost);
|
|
#endif
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
|
|
default_host = (cd->host == defhost);
|
|
|
|
if (cancel_p && !ce->URL_s->internal_url)
|
|
{
|
|
/* Don't allow manual "news:ID?cancel" URLs, only internal ones. */
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
else if (ce->URL_s->method == URL_POST_METHOD)
|
|
{
|
|
/* news://HOST done with a POST instead of a GET;
|
|
this means a new message is being posted.
|
|
Don't allow this unless it's an internal URL.
|
|
*/
|
|
if (!ce->URL_s->internal_url)
|
|
{
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
XP_ASSERT (!group && !message_id && !command_specific_data);
|
|
cd->type_wanted = NEWS_POST;
|
|
StrAllocCopy(cd->path, "");
|
|
}
|
|
else if (message_id)
|
|
{
|
|
/* news:MESSAGE_ID
|
|
news:/GROUP/MESSAGE_ID (useless)
|
|
news://HOST/MESSAGE_ID
|
|
news://HOST/GROUP/MESSAGE_ID (useless)
|
|
*/
|
|
if (cancel_p)
|
|
cd->type_wanted = CANCEL_WANTED;
|
|
else
|
|
cd->type_wanted = ARTICLE_WANTED;
|
|
}
|
|
else if (command_specific_data)
|
|
{
|
|
if (XP_STRSTR (command_specific_data, "?newgroups"))
|
|
/* news://HOST/?newsgroups
|
|
news:/GROUP/?newsgroups (useless)
|
|
news:?newsgroups (default host)
|
|
*/
|
|
cd->type_wanted = NEW_GROUPS;
|
|
else
|
|
{
|
|
if (XP_STRSTR(command_specific_data, "?list-pretty"))
|
|
{
|
|
cd->type_wanted = PRETTY_NAMES_WANTED;
|
|
cd->command_specific_data = XP_STRDUP(command_specific_data);
|
|
}
|
|
else if (XP_STRSTR(command_specific_data, "?profile"))
|
|
{
|
|
cd->type_wanted = PROFILE_WANTED;
|
|
cd->command_specific_data = XP_STRDUP(command_specific_data);
|
|
}
|
|
else if (XP_STRSTR(command_specific_data, "?list-ids"))
|
|
{
|
|
cd->type_wanted = IDS_WANTED;
|
|
cd->command_specific_data = XP_STRDUP(command_specific_data);
|
|
}
|
|
else
|
|
{
|
|
cd->type_wanted = SEARCH_WANTED;
|
|
cd->command_specific_data = XP_STRDUP(command_specific_data);
|
|
cd->current_search = cd->command_specific_data;
|
|
}
|
|
}
|
|
}
|
|
else if (group)
|
|
{
|
|
/* news:GROUP
|
|
news:/GROUP
|
|
news://HOST/GROUP
|
|
*/
|
|
if (ce->window_id->type != MWContextNews && ce->window_id->type != MWContextNewsMsg
|
|
&& ce->window_id->type != MWContextMailMsg
|
|
&& ce->window_id->type != MWContextMail && ce->window_id->type != MWContextMailNewsProgress)
|
|
{
|
|
status = -1;
|
|
goto FAIL;
|
|
}
|
|
if (XP_STRCHR (group, '*'))
|
|
cd->type_wanted = LIST_WANTED;
|
|
else
|
|
cd->type_wanted = GROUP_WANTED;
|
|
}
|
|
else
|
|
{
|
|
/* news:
|
|
news://HOST
|
|
*/
|
|
cd->type_wanted = READ_NEWS_RC;
|
|
}
|
|
|
|
|
|
/* At this point, we're all done parsing the URL, and know exactly
|
|
what we want to do with it.
|
|
*/
|
|
|
|
#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(cd->type_wanted != ARTICLE_WANTED)
|
|
#endif
|
|
ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
|
|
|
|
if (cd->type_wanted == ARTICLE_WANTED)
|
|
{
|
|
const char *group = 0;
|
|
uint32 number = 0;
|
|
|
|
if (MSG_IsOfflineArticle (cd->pane, cd->path,
|
|
&group, &number))
|
|
{
|
|
ce->local_file = TRUE;
|
|
ce->socket = NULL;
|
|
NET_SetCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
MSG_StartOfflineRetrieval(cd->pane, group, number, &cd->offlineState);
|
|
}
|
|
}
|
|
if (NET_IsOffline() && !ce->local_file)
|
|
{
|
|
status = MK_OFFLINE;
|
|
goto FAIL;
|
|
}
|
|
if (cd->offlineState)
|
|
goto FAIL; /* we don't need to do any of this connection stuff */
|
|
|
|
/* check for established connection and use it if available
|
|
*/
|
|
if(nntp_connection_list)
|
|
{
|
|
NNTPConnection * tmp_con;
|
|
XP_List * list_entry = nntp_connection_list;
|
|
|
|
while((tmp_con = (NNTPConnection *)XP_ListNextObject(list_entry))
|
|
!= NULL)
|
|
{
|
|
/* if the hostnames match up exactly and the connection
|
|
* is not busy at the moment then reuse this connection.
|
|
*/
|
|
if(!XP_STRCMP(tmp_con->hostname, host_and_port)
|
|
&& secure_p == tmp_con->secure
|
|
&&!tmp_con->busy)
|
|
{
|
|
/* check to see if this connection can be reused, or if it should be aged away */
|
|
int32 con_age = XP_TIME() - tmp_con->last_used_time;
|
|
if (con_age <= net_news_timeout)
|
|
{
|
|
cd->control_con = tmp_con;
|
|
|
|
NNTP_LOG_NOTE(("Reusing control_con %08lX (age %ld secs) for %s", tmp_con, con_age, url));
|
|
|
|
/* set select on the control socket */
|
|
ce->socket = cd->control_con->csock;
|
|
NET_SetReadSelect(ce->window_id, cd->control_con->csock);
|
|
cd->control_con->prev_cache = TRUE; /* this was from the cache */
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
NNTP_LOG_NOTE(("Aging away control_con %08lX (age %ld secs)", tmp_con, con_age));
|
|
|
|
/* kill idle conn which has lived too long */
|
|
list_entry = list_entry->prev ? list_entry->prev : nntp_connection_list;
|
|
XP_ListRemoveObject(nntp_connection_list, tmp_con);
|
|
net_nntp_close(tmp_con->csock, ce->status);
|
|
FREEIF(tmp_con->current_group);
|
|
FREEIF(tmp_con->hostname);
|
|
XP_FREE(tmp_con);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* initialize the connection list
|
|
*/
|
|
nntp_connection_list = XP_ListNew();
|
|
}
|
|
|
|
|
|
if(cd->control_con)
|
|
{
|
|
cd->next_state = SEND_FIRST_NNTP_COMMAND;
|
|
/* set the connection busy
|
|
*/
|
|
cd->control_con->busy = TRUE;
|
|
NET_TotalNumberOfOpenConnections++;
|
|
}
|
|
else
|
|
{
|
|
/* build a control connection structure so we
|
|
* can store the data as we go along
|
|
*/
|
|
cd->control_con = XP_NEW(NNTPConnection);
|
|
if(!cd->control_con)
|
|
{
|
|
status = MK_OUT_OF_MEMORY;
|
|
goto FAIL;
|
|
}
|
|
NNTP_LOG_NOTE(("Created control_con %08lX for %s", cd->control_con, ce->URL_s->address));
|
|
XP_MEMSET(cd->control_con, 0, sizeof(NNTPConnection));
|
|
|
|
cd->control_con->default_host = default_host;
|
|
StrAllocCopy(cd->control_con->hostname, host_and_port);
|
|
NNTP_LOG_NOTE(("Set host string: %s", cd->control_con->hostname));
|
|
|
|
cd->control_con->secure = secure_p;
|
|
|
|
cd->control_con->prev_cache = FALSE; /* this wasn't from the cache */
|
|
|
|
cd->control_con->csock = NULL;
|
|
|
|
cd->control_con->last_used_time = XP_TIME();
|
|
|
|
/* define this to test support for older NNTP servers
|
|
* that don't support the XOVER command
|
|
*/
|
|
#ifdef TEST_NO_XOVER_SUPPORT
|
|
cd->control_con->no_xover = TRUE;
|
|
#endif /* TEST_NO_XOVER_SUPPORT */
|
|
|
|
/* add this structure to the connection list even
|
|
* though it's not really valid yet.
|
|
* we will fill it in as we go and if
|
|
* an error occurs will will remove it from the
|
|
* list. No one else will be able to use it since
|
|
* we will mark it busy.
|
|
*/
|
|
XP_ListAddObject(nntp_connection_list, cd->control_con);
|
|
|
|
/* set the connection busy
|
|
*/
|
|
cd->control_con->busy = TRUE;
|
|
|
|
/* gate the maximum number of cached connections
|
|
* but don't delete busy ones.
|
|
*/
|
|
if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
|
|
{
|
|
XP_List * list_ptr = nntp_connection_list->next;
|
|
NNTPConnection * con;
|
|
|
|
while(list_ptr)
|
|
{
|
|
con = (NNTPConnection *) list_ptr->object;
|
|
list_ptr = list_ptr->next;
|
|
if(!con->busy)
|
|
{
|
|
XP_ListRemoveObject(nntp_connection_list, con);
|
|
net_nntp_close (con->csock, status);
|
|
FREEIF(con->current_group);
|
|
FREEIF(con->hostname);
|
|
XP_FREE(con);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cd->next_state = NNTP_CONNECT;
|
|
}
|
|
|
|
FAIL:
|
|
|
|
FREEIF (host_and_port);
|
|
FREEIF (group);
|
|
FREEIF (message_id);
|
|
FREEIF (command_specific_data);
|
|
|
|
if (status < 0)
|
|
{
|
|
FREEIF (cd->output_buffer);
|
|
FREEIF (cd);
|
|
ce->URL_s->error_msg = NET_ExplainErrorDetails(status);
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
return (net_ProcessNews (ce));
|
|
}
|
|
}
|
|
/* process the offline news state machine, such as it is. */
|
|
|
|
PRIVATE int
|
|
NET_ProcessOfflineNews(ActiveEntry *ce, NewsConData *cd)
|
|
{
|
|
int32 read_size = 0;
|
|
int status;
|
|
|
|
cd->pause_for_read = TRUE;
|
|
|
|
if (cd->stream)
|
|
read_size = (*cd->stream->is_write_ready)
|
|
(cd->stream);
|
|
else
|
|
ce->status = net_begin_article(ce);
|
|
|
|
if(!read_size)
|
|
return(0); /* wait until we are ready to write */
|
|
else
|
|
read_size = MIN(read_size, NET_Socket_Buffer_Size);
|
|
|
|
status = MSG_ProcessOfflineNews(cd->offlineState, NET_Socket_Buffer, read_size);
|
|
if(status > 0)
|
|
{
|
|
ce->bytes_received += status;
|
|
PUT_STREAM(NET_Socket_Buffer, status); /* blow off error? ### dmb */
|
|
}
|
|
|
|
if (status == 0)
|
|
{
|
|
if (cd->stream)
|
|
COMPLETE_STREAM;
|
|
NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
|
|
if(cd->destroy_graph_progress)
|
|
FE_GraphProgressDestroy(ce->window_id,
|
|
ce->URL_s,
|
|
cd->original_content_length,
|
|
ce->bytes_received);
|
|
status = -1;
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* process the state machine
|
|
*
|
|
* returns negative when completely done
|
|
*/
|
|
PRIVATE int32
|
|
net_ProcessNews (ActiveEntry *ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->offlineState != NULL)
|
|
{
|
|
return NET_ProcessOfflineNews(ce, cd);
|
|
}
|
|
cd->pause_for_read = FALSE;
|
|
|
|
while(!cd->pause_for_read)
|
|
{
|
|
|
|
#if DEBUG
|
|
NNTP_LOG_NOTE(("Next state: %s",stateLabels[cd->next_state]));
|
|
#endif
|
|
|
|
switch(cd->next_state)
|
|
{
|
|
case NNTP_RESPONSE:
|
|
ce->status = net_news_response(ce);
|
|
break;
|
|
|
|
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
|
|
case NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE:
|
|
if (nntp_are_connections_available(cd->control_con))
|
|
{
|
|
/* haven't reached connection ceiling on this host; go to connect */
|
|
cd->next_state = NNTP_CONNECTIONS_ARE_AVAILABLE;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
else
|
|
{
|
|
NET_SetReadSelect(ce->window_id, ce->socket);
|
|
cd->pause_for_read = TRUE;
|
|
}
|
|
break;
|
|
case NNTP_CONNECTIONS_ARE_AVAILABLE:
|
|
/* release our hacky little lock and move on to connection */
|
|
NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
|
|
cd->next_state = NNTP_CONNECT;
|
|
cd->pause_for_read = FALSE;
|
|
break;
|
|
#endif
|
|
|
|
case NNTP_CONNECT:
|
|
if (nntp_are_connections_available(cd->control_con))
|
|
{
|
|
ce->status = NET_BeginConnect(
|
|
(HG29239 cd->control_con->hostname),
|
|
NULL,
|
|
"NNTP",
|
|
(HG28287 NEWS_PORT),
|
|
&ce->socket,
|
|
HG22999
|
|
&cd->tcp_con_data,
|
|
ce->window_id,
|
|
&ce->URL_s->error_msg,
|
|
ce->socks_host,
|
|
ce->socks_port);
|
|
|
|
if(ce->socket != NULL)
|
|
NET_TotalNumberOfOpenConnections++;
|
|
|
|
cd->pause_for_read = TRUE;
|
|
if(ce->status == MK_CONNECTED)
|
|
{
|
|
HG33086
|
|
{
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LOGIN_RESPONSE;
|
|
}
|
|
NET_SetReadSelect(ce->window_id, ce->socket);
|
|
cd->control_con->csock = ce->socket;
|
|
}
|
|
else if(ce->status > -1)
|
|
{
|
|
ce->con_sock = ce->socket; /* set con_sock so we can select on it */
|
|
cd->control_con->csock = ce->socket;
|
|
NET_SetConnectSelect(ce->window_id, ce->con_sock);
|
|
cd->next_state = NNTP_CONNECT_WAIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
|
|
/* ###phil this doesn't work yet, but the idea is that we've
|
|
* maxed out connections on this host; stay in this state
|
|
*/
|
|
ce->con_sock = ce->socket; /* set con_sock so we can select on it */
|
|
cd->control_con->csock = ce->socket;
|
|
NET_SetConnectSelect(ce->window_id, ce->con_sock);
|
|
cd->next_state = NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE;
|
|
#else
|
|
ce->status = -1;
|
|
cd->next_state = NNTP_ERROR;
|
|
ce->socket = cd->control_con->csock = NULL;
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case NNTP_CONNECT_WAIT:
|
|
ce->status = NET_FinishConnect(
|
|
(HG93230 cd->control_con->hostname),
|
|
"NNTP",
|
|
(HG29399 NEWS_PORT),
|
|
&ce->socket,
|
|
&cd->tcp_con_data,
|
|
ce->window_id,
|
|
&ce->URL_s->error_msg);
|
|
|
|
cd->pause_for_read = TRUE;
|
|
if(ce->status == MK_CONNECTED)
|
|
{
|
|
cd->control_con->csock = ce->socket;
|
|
HG17928
|
|
{
|
|
cd->next_state = NNTP_RESPONSE;
|
|
cd->next_state_after_response = NNTP_LOGIN_RESPONSE;
|
|
}
|
|
|
|
NET_ClearConnectSelect(ce->window_id, ce->con_sock);
|
|
ce->con_sock = NULL; /* reset con_sock so we don't select on it */
|
|
NET_SetReadSelect(ce->window_id, ce->socket);
|
|
}
|
|
else if(ce->status < 0)
|
|
{
|
|
NET_ClearConnectSelect(ce->window_id, ce->con_sock);
|
|
}
|
|
else
|
|
{
|
|
/* the not yet connected state */
|
|
|
|
/* unregister the old ce->socket from the select list
|
|
* and register the new value in the case that it changes
|
|
*/
|
|
if(ce->con_sock != ce->socket)
|
|
{
|
|
NET_ClearConnectSelect(ce->window_id, ce->con_sock);
|
|
ce->con_sock = ce->socket;
|
|
NET_SetConnectSelect(ce->window_id, ce->con_sock);
|
|
}
|
|
}
|
|
break;
|
|
HG68092
|
|
case NNTP_LOGIN_RESPONSE:
|
|
ce->status = net_nntp_login_response(ce);
|
|
break;
|
|
|
|
case NNTP_SEND_MODE_READER:
|
|
ce->status = net_nntp_send_mode_reader(ce);
|
|
break;
|
|
|
|
case NNTP_SEND_MODE_READER_RESPONSE:
|
|
ce->status = net_nntp_send_mode_reader_response(ce);
|
|
break;
|
|
|
|
case SEND_LIST_EXTENSIONS:
|
|
ce->status = net_nntp_send_list_extensions(ce);
|
|
break;
|
|
case SEND_LIST_EXTENSIONS_RESPONSE:
|
|
ce->status = net_nntp_send_list_extensions_response(ce);
|
|
break;
|
|
case SEND_LIST_SEARCHES:
|
|
ce->status = net_nntp_send_list_searches(ce);
|
|
break;
|
|
case SEND_LIST_SEARCHES_RESPONSE:
|
|
ce->status = net_nntp_send_list_searches_response(ce);
|
|
break;
|
|
case NNTP_LIST_SEARCH_HEADERS:
|
|
ce->status = net_send_list_search_headers(ce);
|
|
break;
|
|
case NNTP_LIST_SEARCH_HEADERS_RESPONSE:
|
|
ce->status = net_send_list_search_headers_response(ce);
|
|
break;
|
|
case NNTP_GET_PROPERTIES:
|
|
ce->status = nntp_get_properties(ce);
|
|
break;
|
|
case NNTP_GET_PROPERTIES_RESPONSE:
|
|
ce->status = nntp_get_properties_response(ce);
|
|
break;
|
|
|
|
case SEND_LIST_SUBSCRIPTIONS:
|
|
ce->status = net_send_list_subscriptions(ce);
|
|
break;
|
|
case SEND_LIST_SUBSCRIPTIONS_RESPONSE:
|
|
ce->status = net_send_list_subscriptions_response(ce);
|
|
break;
|
|
|
|
case SEND_FIRST_NNTP_COMMAND:
|
|
ce->status = net_send_first_nntp_command(ce);
|
|
break;
|
|
|
|
case SEND_FIRST_NNTP_COMMAND_RESPONSE:
|
|
ce->status = net_send_first_nntp_command_response(ce);
|
|
break;
|
|
|
|
case NNTP_SEND_GROUP_FOR_ARTICLE:
|
|
ce->status = net_send_group_for_article(ce);
|
|
break;
|
|
case NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE:
|
|
ce->status = net_send_group_for_article_response(ce);
|
|
break;
|
|
case NNTP_SEND_ARTICLE_NUMBER:
|
|
ce->status = net_send_article_number(ce);
|
|
break;
|
|
|
|
case SETUP_NEWS_STREAM:
|
|
ce->status = net_setup_news_stream(ce);
|
|
break;
|
|
|
|
case NNTP_BEGIN_AUTHORIZE:
|
|
ce->status = net_news_begin_authorize(ce);
|
|
break;
|
|
|
|
case NNTP_AUTHORIZE_RESPONSE:
|
|
ce->status = net_news_authorize_response(ce);
|
|
break;
|
|
|
|
case NNTP_PASSWORD_RESPONSE:
|
|
ce->status = net_news_password_response(ce);
|
|
break;
|
|
|
|
case NNTP_READ_LIST_BEGIN:
|
|
ce->status = net_read_news_list_begin(ce);
|
|
break;
|
|
|
|
case NNTP_READ_LIST:
|
|
ce->status = net_read_news_list(ce);
|
|
break;
|
|
|
|
case DISPLAY_NEWSGROUPS:
|
|
ce->status = net_display_newsgroups(ce);
|
|
break;
|
|
|
|
case NNTP_NEWGROUPS_BEGIN:
|
|
ce->status = net_newgroups_begin(ce);
|
|
break;
|
|
|
|
case NNTP_NEWGROUPS:
|
|
ce->status = net_process_newgroups(ce);
|
|
break;
|
|
|
|
case NNTP_BEGIN_ARTICLE:
|
|
ce->status = net_begin_article(ce);
|
|
break;
|
|
|
|
case NNTP_READ_ARTICLE:
|
|
{
|
|
char *line;
|
|
NewsConData * cd =
|
|
(NewsConData *)ce->con_data;
|
|
|
|
ce->status = NET_BufferedReadLine(ce->socket, &line,
|
|
&cd->data_buf,
|
|
&cd->data_buf_size,
|
|
(Bool*)&cd->pause_for_read);
|
|
|
|
if(ce->status == 0)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
ce->URL_s->error_msg =
|
|
NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
|
|
return(MK_NNTP_SERVER_ERROR);
|
|
}
|
|
|
|
if(ce->status > 1)
|
|
{
|
|
ce->bytes_received += ce->status;
|
|
/* We shouldn't graph progress if going offline - it messes
|
|
up our go offline status msgs. ### dmb
|
|
*/
|
|
FE_GraphProgress(ce->window_id, ce->URL_s,
|
|
ce->bytes_received, ce->status,
|
|
ce->URL_s->content_length);
|
|
|
|
}
|
|
|
|
if(!line)
|
|
return(ce->status); /* no line yet or error */
|
|
|
|
if (cd->type_wanted == CANCEL_WANTED &&
|
|
cd->response_code != 221)
|
|
{
|
|
/* HEAD command failed. */
|
|
return MK_NNTP_CANCEL_ERROR;
|
|
}
|
|
|
|
if (line[0] == '.' && line[1] == 0)
|
|
{
|
|
if (cd->type_wanted == CANCEL_WANTED)
|
|
cd->next_state = NEWS_START_CANCEL;
|
|
else
|
|
cd->next_state = NEWS_DONE;
|
|
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (line[0] == '.')
|
|
XP_STRCPY (cd->output_buffer, line + 1);
|
|
else
|
|
XP_STRCPY (cd->output_buffer, line);
|
|
|
|
/* When we're sending this line to a converter (ie,
|
|
it's a message/rfc822) use the local line termination
|
|
convention, not CRLF. This makes text articles get
|
|
saved with the local line terminators. Since SMTP
|
|
and NNTP mandate the use of CRLF, it is expected that
|
|
the local system will convert that to the local line
|
|
terminator as it is read.
|
|
*/
|
|
XP_STRCAT (cd->output_buffer, LINEBREAK);
|
|
/* Don't send content-type to mime parser if we're doing a cancel
|
|
because it confuses mime parser into not parsing.
|
|
*/
|
|
if (cd->type_wanted != CANCEL_WANTED || XP_STRNCMP(cd->output_buffer, "Content-Type:", 13))
|
|
ce->status = PUTSTRING(cd->output_buffer);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NNTP_XOVER_BEGIN:
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_READ_NEWSGROUPINFO));
|
|
ce->status = net_read_xover_begin(ce);
|
|
break;
|
|
|
|
case NNTP_FIGURE_NEXT_CHUNK:
|
|
ce->status = net_figure_next_chunk(ce);
|
|
break;
|
|
|
|
case NNTP_XOVER_SEND:
|
|
ce->status = net_xover_send(ce);
|
|
break;
|
|
|
|
case NNTP_XOVER:
|
|
ce->status = net_read_xover(ce);
|
|
break;
|
|
|
|
case NNTP_XOVER_RESPONSE:
|
|
ce->status = net_read_xover_response(ce);
|
|
break;
|
|
|
|
case NEWS_PROCESS_XOVER:
|
|
case NEWS_PROCESS_BODIES:
|
|
if (cd->group_name && XP_STRSTR (cd->group_name, ".emacs"))
|
|
/* This is a joke! Don't internationalize it... */
|
|
NET_Progress(ce->window_id, "Garbage collecting...");
|
|
else
|
|
NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_SORT_ARTICLES));
|
|
ce->status = net_process_xover(ce);
|
|
break;
|
|
|
|
case NNTP_PROFILE_ADD:
|
|
ce->status = net_profile_add (ce);
|
|
break;
|
|
case NNTP_PROFILE_ADD_RESPONSE:
|
|
ce->status = net_profile_add_response(ce);
|
|
break;
|
|
case NNTP_PROFILE_DELETE:
|
|
ce->status = net_profile_delete (ce);
|
|
break;
|
|
case NNTP_PROFILE_DELETE_RESPONSE:
|
|
ce->status = net_profile_delete_response(ce);
|
|
break;
|
|
|
|
case NNTP_READ_GROUP:
|
|
ce->status = net_read_news_group(ce);
|
|
break;
|
|
|
|
case NNTP_READ_GROUP_RESPONSE:
|
|
ce->status = net_read_news_group_response(ce);
|
|
break;
|
|
|
|
case NNTP_READ_GROUP_BODY:
|
|
ce->status = net_read_news_group_body(ce);
|
|
break;
|
|
|
|
case NNTP_SEND_POST_DATA:
|
|
ce->status = net_send_news_post_data(ce);
|
|
break;
|
|
|
|
case NNTP_SEND_POST_DATA_RESPONSE:
|
|
ce->status = net_send_news_post_data_response(ce);
|
|
break;
|
|
|
|
case NNTP_CHECK_FOR_MESSAGE:
|
|
ce->status = net_check_for_message(ce);
|
|
break;
|
|
|
|
case NEWS_NEWS_RC_POST:
|
|
ce->status = net_NewsRCProcessPost(ce);
|
|
break;
|
|
|
|
case NEWS_DISPLAY_NEWS_RC:
|
|
ce->status = net_DisplayNewsRC(ce);
|
|
break;
|
|
|
|
case NEWS_DISPLAY_NEWS_RC_RESPONSE:
|
|
ce->status = net_DisplayNewsRCResponse(ce);
|
|
break;
|
|
|
|
case NEWS_START_CANCEL:
|
|
ce->status = net_start_cancel(ce);
|
|
break;
|
|
|
|
case NEWS_DO_CANCEL:
|
|
ce->status = net_do_cancel(ce);
|
|
break;
|
|
|
|
case NNTP_XPAT_SEND:
|
|
ce->status = net_xpat_send(ce);
|
|
break;
|
|
case NNTP_XPAT_RESPONSE:
|
|
ce->status = net_xpat_response(ce);
|
|
break;
|
|
case NNTP_SEARCH:
|
|
ce->status = net_nntp_search(ce);
|
|
break;
|
|
case NNTP_SEARCH_RESPONSE:
|
|
ce->status = net_nntp_search_response(ce);
|
|
break;
|
|
case NNTP_SEARCH_RESULTS:
|
|
ce->status = net_nntp_search_results(ce);
|
|
break;
|
|
case NNTP_LIST_PRETTY_NAMES:
|
|
ce->status = net_list_pretty_names(ce);
|
|
break;
|
|
case NNTP_LIST_PRETTY_NAMES_RESPONSE:
|
|
ce->status = net_list_pretty_names_response(ce);
|
|
break;
|
|
case NNTP_LIST_XACTIVE:
|
|
ce->status = net_list_xactive(ce);
|
|
break;
|
|
case NNTP_LIST_XACTIVE_RESPONSE:
|
|
ce->status = net_list_xactive_response(ce);
|
|
break;
|
|
case NNTP_LIST_GROUP:
|
|
ce->status = net_list_group(ce);
|
|
break;
|
|
case NNTP_LIST_GROUP_RESPONSE:
|
|
ce->status = net_list_group_response(ce);
|
|
break;
|
|
case NEWS_DONE:
|
|
/* call into libmsg and see if the article counts
|
|
* are up to date. If they are not then we
|
|
* want to do a "news://host/group" URL so that we
|
|
* can finish up the article counts.
|
|
*/
|
|
if (cd->stream)
|
|
COMPLETE_STREAM;
|
|
|
|
cd->next_state = NEWS_FREE;
|
|
/* set the connection unbusy
|
|
*/
|
|
cd->control_con->busy = FALSE;
|
|
NET_TotalNumberOfOpenConnections--;
|
|
|
|
NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
|
|
NET_RefreshCacheFileExpiration(ce->URL_s);
|
|
break;
|
|
|
|
case NEWS_ERROR:
|
|
if(cd->stream)
|
|
ABORT_STREAM(ce->status);
|
|
cd->next_state = NEWS_FREE;
|
|
/* set the connection unbusy
|
|
*/
|
|
cd->control_con->busy = FALSE;
|
|
NET_TotalNumberOfOpenConnections--;
|
|
|
|
if(cd->control_con->csock != NULL)
|
|
{
|
|
NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
|
|
}
|
|
break;
|
|
|
|
case NNTP_ERROR:
|
|
if(cd->stream)
|
|
{
|
|
ABORT_STREAM(ce->status);
|
|
cd->stream=0;
|
|
}
|
|
|
|
if(cd->control_con && cd->control_con->csock != NULL)
|
|
{
|
|
NNTP_LOG_NOTE(("Clearing read and connect select on socket %d",
|
|
cd->control_con->csock));
|
|
NET_ClearConnectSelect(ce->window_id, cd->control_con->csock);
|
|
NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
|
|
#ifdef XP_WIN
|
|
if(cd->calling_netlib_all_the_time)
|
|
{
|
|
cd->calling_netlib_all_the_time = FALSE;
|
|
NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
|
|
}
|
|
#endif /* XP_WIN */
|
|
NET_ClearDNSSelect(ce->window_id, cd->control_con->csock);
|
|
net_nntp_close (cd->control_con->csock, ce->status); /* close the socket */
|
|
FREEIF(cd->control_con->current_group);
|
|
NET_TotalNumberOfOpenConnections--;
|
|
ce->socket = NULL;
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
if(cd->control_con && cd->control_con->prev_cache &&
|
|
!cd->some_protocol_succeeded)
|
|
{
|
|
cd->control_con->prev_cache = FALSE;
|
|
cd->next_state = NNTP_CONNECT;
|
|
ce->status = 0; /* keep going */
|
|
}
|
|
else
|
|
{
|
|
cd->next_state = NEWS_FREE;
|
|
|
|
/* remove the connection from the cache list
|
|
* and free the data
|
|
*/
|
|
XP_ListRemoveObject(nntp_connection_list, cd->control_con);
|
|
if(cd->control_con)
|
|
{
|
|
FREEIF(cd->control_con->hostname);
|
|
FREE(cd->control_con);
|
|
cd->control_con = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NEWS_FREE:
|
|
|
|
/* 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. */
|
|
if (cd->xover_parse_state != NULL)
|
|
{
|
|
int status;
|
|
/* XP_ASSERT (ce->status < 0);*/
|
|
status = MSG_FinishXOVER (cd->pane,
|
|
&cd->xover_parse_state,
|
|
ce->status);
|
|
XP_ASSERT (!cd->xover_parse_state);
|
|
if (ce->status >= 0 && status < 0)
|
|
ce->status = status;
|
|
}
|
|
if (cd->newsgroup_parse_state)
|
|
MSG_FinishAddArticleKeyToGroup(cd->pane, &cd->newsgroup_parse_state);
|
|
|
|
FREEIF(cd->path);
|
|
FREEIF(cd->response_txt);
|
|
FREEIF(cd->data_buf);
|
|
|
|
FREEIF(cd->output_buffer);
|
|
HG20900
|
|
FREEIF(cd->stream); /* don't forget the stream */
|
|
if(cd->tcp_con_data)
|
|
NET_FreeTCPConData(cd->tcp_con_data);
|
|
|
|
FREEIF(cd->group_name);
|
|
|
|
FREEIF (cd->cancel_id);
|
|
FREEIF (cd->cancel_from);
|
|
FREEIF (cd->cancel_newsgroups);
|
|
FREEIF (cd->cancel_distribution);
|
|
|
|
if(cd->destroy_graph_progress)
|
|
FE_GraphProgressDestroy(ce->window_id,
|
|
ce->URL_s,
|
|
cd->original_content_length,
|
|
ce->bytes_received);
|
|
XP_FREE(cd);
|
|
|
|
/* gate the maximum number of cached connections
|
|
* but don't delete busy ones.
|
|
*/
|
|
if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
|
|
{
|
|
XP_List * list_ptr = nntp_connection_list->next;
|
|
NNTPConnection * con;
|
|
|
|
NNTP_LOG_NOTE(("killing cached news connection"));
|
|
|
|
while(list_ptr)
|
|
{
|
|
con = (NNTPConnection *) list_ptr->object;
|
|
list_ptr = list_ptr->next;
|
|
if(!con->busy)
|
|
{
|
|
XP_ListRemoveObject(nntp_connection_list, con);
|
|
net_nntp_close(con->csock, ce->status);
|
|
FREEIF(con->current_group);
|
|
FREEIF(con->hostname);
|
|
XP_FREE(con);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(-1); /* all done */
|
|
|
|
default:
|
|
/* big error */
|
|
return(-1);
|
|
}
|
|
|
|
if(ce->status < 0 && cd->next_state != NEWS_ERROR &&
|
|
cd->next_state != NNTP_ERROR && cd->next_state != NEWS_FREE)
|
|
{
|
|
cd->next_state = NNTP_ERROR;
|
|
cd->pause_for_read = FALSE;
|
|
}
|
|
} /* end big while */
|
|
|
|
return(0); /* keep going */
|
|
}
|
|
|
|
/* abort the connection in progress
|
|
*/
|
|
PRIVATE int32
|
|
net_InterruptNews(ActiveEntry * ce)
|
|
{
|
|
NewsConData * cd = (NewsConData *)ce->con_data;
|
|
|
|
if (cd->offlineState != NULL)
|
|
return MSG_InterruptOfflineNews(cd->offlineState);
|
|
|
|
cd->next_state = NNTP_ERROR;
|
|
ce->status = MK_INTERRUPTED;
|
|
|
|
if (cd->control_con)
|
|
cd->control_con->prev_cache = FALSE; /* to keep if from reconnecting */
|
|
|
|
return(net_ProcessNews(ce));
|
|
}
|
|
|
|
/* Free any memory used up
|
|
* close cached connections and
|
|
* free newsgroup listings
|
|
*/
|
|
PRIVATE void
|
|
net_CleanupNews(void)
|
|
{
|
|
NNTP_LOG_NOTE(("NET_CleanupNews called"));
|
|
|
|
/* this is a small leak but since I don't have another function to
|
|
* clear my connections I need to call this one alot and I don't
|
|
* want to free the newshost
|
|
*
|
|
* FREE_AND_CLEAR(NET_NewsHost);
|
|
*/
|
|
|
|
if(nntp_connection_list)
|
|
{
|
|
NNTPConnection * tmp_con;
|
|
|
|
while((tmp_con = (NNTPConnection *)XP_ListRemoveTopObject(nntp_connection_list)) != NULL)
|
|
{
|
|
if(!tmp_con->busy)
|
|
{
|
|
FREE(tmp_con->hostname);
|
|
FREEIF(tmp_con->current_group);
|
|
if(tmp_con->csock != NULL)
|
|
{
|
|
net_nntp_close(tmp_con->csock, 0 /* status? */);
|
|
}
|
|
FREE(tmp_con);
|
|
}
|
|
}
|
|
|
|
if(XP_ListIsEmpty(nntp_connection_list))
|
|
{
|
|
XP_ListDestroy(nntp_connection_list);
|
|
nntp_connection_list = 0;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
MODULE_PRIVATE void
|
|
NET_InitNewsProtocol(void)
|
|
{
|
|
static NET_ProtoImpl news_proto_impl;
|
|
|
|
news_proto_impl.init = net_NewsLoad;
|
|
news_proto_impl.process = net_ProcessNews;
|
|
news_proto_impl.interrupt = net_InterruptNews;
|
|
news_proto_impl.cleanup = net_CleanupNews;
|
|
|
|
NET_RegisterProtocolImplementation(&news_proto_impl, NEWS_TYPE_URL);
|
|
NET_RegisterProtocolImplementation(&news_proto_impl, INTERNAL_NEWS_TYPE_URL);
|
|
}
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile off
|
|
#endif
|
|
|
|
#endif /* MOZILLA_CLIENT */
|
|
|