mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4119 lines
106 KiB
4119 lines
106 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Jim Seidman [email protected]
|
|
|
|
Portions of this file were derived from
|
|
the CERN libwww, version 2.15.
|
|
*/
|
|
|
|
#include "all.h"
|
|
#include "dlg_post.h"
|
|
|
|
#ifdef FEATURE_NEWSREADER
|
|
|
|
#define NEWS_PORT 119 /* See rfc977 */
|
|
#define MAX_CHUNK 40 /* Largest number of articles in one window */
|
|
#define CHUNK_SIZE 20 /* Number of articles for quick display */
|
|
#define LINK_COUNT 200 /* BUGBUG: Make this adjustable by user */
|
|
|
|
#define BIG 1024 /* @@@ */
|
|
#define CACHE_CHUNK 250 /* Number of lines to process from cache file at a time */
|
|
#define MAX_QUOTE_SIZE 4096 /* Amount of bytes of article to quote in response */
|
|
#define PUTS(s) (*pParams->target->isa->put_string)(pParams->target, s)
|
|
#define START(e) (*pParams->target->isa->start_element)(pParams->target, e, 0, 0)
|
|
#define END(e) (*pParams->target->isa->end_element)(pParams->target, e)
|
|
|
|
|
|
struct _HTStructured
|
|
{
|
|
CONST HTStructuredClass *isa;
|
|
/* ... */
|
|
};
|
|
|
|
#define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */
|
|
#define GROUP_NAME_LENGTH 256 /* Maximum length of group name */
|
|
|
|
// BUGBUG bogus value has lots of slop
|
|
#define FIXED_HEADER_SPACE 500
|
|
|
|
|
|
|
|
/*
|
|
* NNTP Server Response Codes
|
|
*
|
|
* The NNTP Server gives us a numerical response
|
|
* to each command. The response is returned in
|
|
* ASCII but is converted to integer.
|
|
*
|
|
*/
|
|
#define NNTP_OK_POSTING_ALLOWED 200
|
|
#define NNTP_OK_NO_POSTING 201
|
|
#define NNTP_POST_SUCCESS 240 // Posting Successful
|
|
#define NNTP_AUTHINFO_SUCCESS 281 // AUTHINFO user and password success
|
|
#define NNTP_CONTINUE 340 // Send more data
|
|
#define NNTP_SEND_PASSWORD 381
|
|
#define NNTP_POST_NOT_PERMITTED 440 // Posting Not Allowed
|
|
#define NNTP_POST_FAILURE 441 // Post Failure
|
|
#define NNTP_PERMISSION_DENIED 502
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* NNTP Settings Change
|
|
*
|
|
* When the NNTP settings are changed this is signaled so that
|
|
* we can punt any existing connection
|
|
*/
|
|
BOOL bNNTP_Changed = TRUE;
|
|
|
|
|
|
/*
|
|
* Global Hashed Newsgroup
|
|
*
|
|
* One newsgroup at a time is hashed such that when reading an article
|
|
* in the group we can display next article and prev article links.
|
|
* This is the name of the currently hashed newsgroup
|
|
*/
|
|
CHAR szGlobalHashedGroup[GROUP_NAME_LENGTH + 1];
|
|
|
|
|
|
|
|
/*
|
|
* Hash Table
|
|
*
|
|
* This global hash table will contain pointers to prev and next
|
|
* articles given a particular Message-ID
|
|
*/
|
|
struct hash_table *hashArticlePointers = NULL;
|
|
|
|
// Structure to hold prev/next links in hash table
|
|
struct article_links {
|
|
char *next;
|
|
char *prev;
|
|
int id; // Article ID Number
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void News_DisposeNewsConnection(struct _CachedConn *pCon)
|
|
{
|
|
XX_Assert((pCon->type == CONN_NNTP), ("News_DisposeNewsConnection: connection type is %d!", pCon->type));
|
|
XX_Assert((pCon->addr != 0), ("News_DisposeNewsConnection: connection has no address!"));
|
|
pCon->addr = 0;
|
|
Net_Close(pCon->socket);
|
|
pCon->socket = -1;
|
|
pCon->type = CONN_NONE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* This routine sends a NNTP command and retrieves the status return,
|
|
as per PFC 977 */
|
|
struct Params_News_Command {
|
|
HTInputSocket * isoc;
|
|
char * cmd; /* Command to send - will be freed! */
|
|
int * pResult; /* Place to store response */
|
|
char ** ppResText; /* Where to return response text (can be NULL) */
|
|
|
|
/* Used internally */
|
|
int net_status; /* Network operation result */
|
|
char text[LINE_LENGTH + 1];
|
|
int index; /* index into text[] */
|
|
};
|
|
|
|
#define STATE_COMMAND_SENT (STATE_OTHER)
|
|
#define STATE_COMMAND_GOTDATA (STATE_OTHER+1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ C O M M A N D _ A S Y N C ( )
|
|
*
|
|
* Routine: News_Command_Async()
|
|
*
|
|
* Purpose: Send command to NNTP to server and get reply
|
|
*
|
|
*/
|
|
|
|
|
|
static int News_Command_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_News_Command *pParams;
|
|
char ch;
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
pParams->index = 0;
|
|
|
|
/* Send command */
|
|
if (pParams->cmd)
|
|
{
|
|
struct Params_Send *pps;
|
|
|
|
XX_DMsg(DBG_LOAD, ("News: sending command %s", pParams->cmd));
|
|
|
|
pps = GTR_CALLOC(sizeof(*pps),1);
|
|
pps->socket = pParams->isoc->input_file_number;
|
|
pps->pBuf = pParams->cmd;
|
|
pps->nBufLen = strlen(pParams->cmd);
|
|
pps->pStatus = &pParams->net_status;
|
|
Async_DoCall(Net_Send_Async, pps);
|
|
return STATE_COMMAND_SENT;
|
|
}
|
|
/* Otherwise we're just reading response.
|
|
Fall through */
|
|
|
|
case STATE_COMMAND_SENT:
|
|
if (pParams->cmd)
|
|
{
|
|
GTR_FREE(pParams->cmd);
|
|
pParams->cmd = NULL;
|
|
if (pParams->net_status < 0)
|
|
{
|
|
*pParams->pResult = -1;
|
|
return STATE_DONE;
|
|
}
|
|
}
|
|
pParams->net_status = 0;
|
|
/* fall through */
|
|
|
|
case STATE_COMMAND_GOTDATA:
|
|
if (pParams->net_status < 0)
|
|
{
|
|
*pParams->pResult = -1;
|
|
return STATE_DONE;
|
|
}
|
|
ch = 0;
|
|
for ( ; pParams->isoc->input_pointer < pParams->isoc->input_limit; pParams->isoc->input_pointer++)
|
|
{
|
|
ch = *pParams->isoc->input_pointer;
|
|
if (ch == CR)
|
|
continue;
|
|
else if (ch == LF)
|
|
break;
|
|
else if (pParams->index < LINE_LENGTH)
|
|
{
|
|
pParams->text[pParams->index++] = ch;
|
|
}
|
|
}
|
|
/* Step past the character we just read in the isoc */
|
|
pParams->isoc->input_pointer++;
|
|
|
|
/* If we didn't quit the loop because of finding an LF, get more */
|
|
if (ch != LF)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = &pParams->net_status;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_COMMAND_GOTDATA;
|
|
}
|
|
|
|
/* Terminate this line of stuff */
|
|
pParams->text[pParams->index] = '\0';
|
|
if (pParams->ppResText)
|
|
{
|
|
*pParams->ppResText = GTR_MALLOC(pParams->index + 1);
|
|
strcpy(*pParams->ppResText, pParams->text);
|
|
}
|
|
XX_DMsg(DBG_LOAD, ("News: Other side sent %s\n", pParams->text));
|
|
*pParams->pResult = atoi(pParams->text);
|
|
if (*pParams->pResult == 0)
|
|
{
|
|
/* Something must be wrong */
|
|
*pParams->pResult = -1;
|
|
}
|
|
return STATE_DONE;
|
|
|
|
case STATE_ABORT:
|
|
if (pParams->cmd)
|
|
{
|
|
GTR_FREE(pParams->cmd);
|
|
}
|
|
*pParams->pResult = -1;
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Utility functions */
|
|
|
|
|
|
|
|
/*
|
|
* T A B L E
|
|
*
|
|
* Routine: Table()
|
|
*
|
|
* Purpose: Helper function for generating a table
|
|
*
|
|
*
|
|
*/
|
|
VOID
|
|
TableStart( HTStructured *target, char *szWidth )
|
|
{
|
|
BOOL tbl_attrib[HTML_TABLE_ATTRIBUTES];
|
|
CHAR *tbl_value[HTML_TABLE_ATTRIBUTES];
|
|
|
|
memset( tbl_attrib, 0, sizeof(tbl_attrib) );
|
|
memset( tbl_value, 0, sizeof(tbl_value) );
|
|
|
|
/*
|
|
* Default Cellpadding and Cellspacing make
|
|
* table with too much whitespace, so we'll
|
|
* go ahead and minimize them
|
|
*/
|
|
tbl_attrib[HTML_TABLE_CELLPADDING] = TRUE;
|
|
tbl_value [HTML_TABLE_CELLPADDING] = "0";
|
|
tbl_attrib[HTML_TABLE_CELLSPACING] = TRUE;
|
|
tbl_value [HTML_TABLE_CELLSPACING] = "2";
|
|
|
|
|
|
|
|
if (szWidth) {
|
|
tbl_attrib[HTML_TABLE_WIDTH] = TRUE;
|
|
tbl_value[ HTML_TABLE_WIDTH] = szWidth;
|
|
}
|
|
(*target->isa->start_element)(target, HTML_TABLE, tbl_attrib, tbl_value );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* R O W
|
|
*
|
|
* Routine: Row()
|
|
*
|
|
* Purpose: Helper function for generating a table row
|
|
*
|
|
*/
|
|
VOID
|
|
RowStart( HTStructured *target )
|
|
{
|
|
BOOL tr_attrib[HTML_TR_ATTRIBUTES];
|
|
CHAR *tr_value[HTML_TR_ATTRIBUTES];
|
|
|
|
memset( tr_attrib, 0, sizeof(tr_attrib) );
|
|
memset( tr_value, 0, sizeof(tr_value) );
|
|
|
|
(*target->isa->start_element)(target, HTML_TR, tr_attrib, tr_value );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* C E L L S T A R T
|
|
*
|
|
* Routine: Cell()
|
|
*
|
|
* Purpose: Helper function for generating a table column
|
|
*
|
|
*/
|
|
VOID
|
|
CellStart( HTStructured *target, char *szAlign, char *szWidth, char *szValign )
|
|
{
|
|
BOOL td_attrib[HTML_TD_ATTRIBUTES];
|
|
CHAR *td_value[HTML_TD_ATTRIBUTES];
|
|
|
|
memset( td_attrib, 0, sizeof(td_attrib) );
|
|
memset( td_value, 0, sizeof(td_value) );
|
|
|
|
if (szAlign) {
|
|
td_attrib[HTML_TD_ALIGN] = TRUE;
|
|
td_value[ HTML_TD_ALIGN] = szAlign;
|
|
}
|
|
|
|
|
|
|
|
if (szWidth) {
|
|
td_attrib[HTML_TD_WIDTH] = TRUE;
|
|
td_value[ HTML_TD_WIDTH] = szWidth;
|
|
}
|
|
|
|
|
|
if (szValign) {
|
|
td_attrib[HTML_TD_VALIGN] = TRUE;
|
|
td_value[ HTML_TD_VALIGN] = szValign;
|
|
}
|
|
|
|
(*target->isa->start_element)(target, HTML_TD, td_attrib, td_value );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* F O N T
|
|
*
|
|
* Routine: Font()
|
|
*
|
|
* Purpose: Helper function for resizing a font
|
|
*
|
|
*/
|
|
VOID
|
|
FontStart( HTStructured *target, char *szFontSize )
|
|
{
|
|
BOOL font_attrib[HTML_FONT_ATTRIBUTES];
|
|
CHAR *font_value[HTML_FONT_ATTRIBUTES];
|
|
|
|
memset( font_attrib, 0, sizeof(font_attrib) );
|
|
memset( font_value, 0, sizeof(font_value) );
|
|
|
|
font_attrib[HTML_FONT_SIZE] = TRUE;
|
|
font_value[ HTML_FONT_SIZE] = szFontSize;
|
|
(*target->isa->start_element)(target, HTML_FONT, font_attrib, font_value );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* B O D Y
|
|
*
|
|
* Routine: BodyStart()
|
|
*
|
|
* Purpose: Adjust Body Background Color and other body attribs
|
|
*
|
|
*/
|
|
VOID
|
|
BodyStart( HTStructured *target )
|
|
{
|
|
BOOL body_attrib[HTML_BODY_ATTRIBUTES];
|
|
CHAR *body_value[HTML_BODY_ATTRIBUTES];
|
|
|
|
|
|
memset( body_attrib, 0, sizeof(body_attrib));
|
|
memset( body_value, 0, sizeof(body_value ));
|
|
|
|
body_attrib[HTML_BODY_BGCOLOR] = TRUE;
|
|
body_value[ HTML_BODY_BGCOLOR] = "#FFFFFF";
|
|
body_attrib[HTML_BODY_TEXT] = TRUE;
|
|
body_value[ HTML_BODY_TEXT] = "#000000";
|
|
body_attrib[HTML_BODY_LINK] = TRUE;
|
|
body_value[ HTML_BODY_LINK] = "#000066";
|
|
body_attrib[HTML_BODY_VLINK] = TRUE;
|
|
body_value[ HTML_BODY_VLINK] = "#000066";
|
|
(*target->isa->start_element)(target, HTML_BODY, body_attrib, body_value);
|
|
}
|
|
|
|
|
|
/*
|
|
* H O R I Z O N T A L B A R
|
|
*
|
|
* Routine: HorizontalBar()
|
|
*
|
|
* Purpose: Draw a horizontal Bar (what else!)
|
|
*
|
|
*/
|
|
VOID
|
|
HorizontalBar( HTStructured *target )
|
|
{
|
|
BOOL hr_attrib[HTML_HR_ATTRIBUTES];
|
|
CHAR *hr_value[HTML_HR_ATTRIBUTES];
|
|
|
|
memset(hr_attrib, 0, sizeof(hr_attrib) );
|
|
memset(hr_value, 0, sizeof(hr_value ) );
|
|
(*target->isa->start_element)(target, HTML_HR, hr_attrib, hr_value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Case insensitive string comparisons
|
|
** -----------------------------------
|
|
**
|
|
** On entry,
|
|
** template must be already un upper case.
|
|
** unknown may be in upper or lower or mixed case to match.
|
|
*/
|
|
PRIVATE BOOL match(CONST char *unknown, CONST char *template)
|
|
{
|
|
CONST char *u = unknown;
|
|
CONST char *t = template;
|
|
for (; *u && *t && (TOUPPER(*u) == *t); u++, t++) /* Find mismatch or end */ ;
|
|
return (BOOL) (*t == 0); /* OK if end of template */
|
|
}
|
|
|
|
/* Find Author's name in mail address
|
|
** ----------------------------------
|
|
**
|
|
** On exit,
|
|
** THE EMAIL ADDRESS IS CORRUPTED
|
|
**
|
|
** For example, returns "Tim Berners-Lee" if given any of
|
|
** " Tim Berners-Lee <[email protected]> "
|
|
** or " [email protected] ( Tim Berners-Lee ) "
|
|
**
|
|
** OHBUG(436): We are seeing lots of names of the form:
|
|
** " "Jeff Webber (Volt Computer)" <[email protected]>"
|
|
** In this case we really want: "Jeff Webber"
|
|
*/
|
|
|
|
PRIVATE char *author_name(char *email)
|
|
{
|
|
char *qs = NULL;
|
|
char *qe = NULL;
|
|
char *bs = NULL;
|
|
char *be = NULL;
|
|
char *ps = NULL;
|
|
char *pe = NULL;
|
|
int cQCount = 0;
|
|
char *curr;
|
|
|
|
|
|
curr = email;
|
|
while (*curr) {
|
|
switch (*curr) {
|
|
case '"':
|
|
if (cQCount == 0) {
|
|
qs = curr;
|
|
cQCount++;
|
|
} else if (cQCount == 1) {
|
|
qe = curr;
|
|
cQCount++;
|
|
}
|
|
break;
|
|
case '<':
|
|
bs = curr;
|
|
break;
|
|
case '>':
|
|
be = curr;
|
|
break;
|
|
case '(':
|
|
ps = curr;
|
|
break;
|
|
case ')':
|
|
pe = curr;
|
|
break;
|
|
}
|
|
curr++;
|
|
}
|
|
|
|
|
|
if ((!qe) || (!qs) || (qe <= qs))
|
|
qe = qs = NULL;
|
|
|
|
if ((!be) || (!bs) || (be <= bs))
|
|
be = bs = NULL;
|
|
|
|
if ((!pe) || (!ps) || (pe <= ps))
|
|
pe = ps = NULL;
|
|
|
|
|
|
/*
|
|
* If there is a quoted part then we always use that.
|
|
*/
|
|
if (qe) {
|
|
*(qe-1) = 0;
|
|
return( HTStrip(qs + 1) );
|
|
}
|
|
|
|
|
|
/*
|
|
* If there is a <...> and a (...) then we remove the
|
|
* <...> section and display everything else in the line
|
|
*/
|
|
if (be && (bs != email)) {
|
|
strcpy(bs, be + 1); /* Remove <...> */
|
|
return HTStrip(email); /* Remove leading and trailing spaces */
|
|
}
|
|
|
|
/*
|
|
* If there is a '(' and a ')'
|
|
*/
|
|
if (pe) {
|
|
*pe = 0; /* Chop off everything after the ')' */
|
|
return HTStrip(ps + 1); /* Remove leading and trailing spaces */
|
|
}
|
|
|
|
|
|
/*
|
|
* No paren section.... This is probably the case where
|
|
* all we have is <foo@blech>
|
|
*/
|
|
return HTStrip(email); /* Default to the whole thing */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* S T A R T A N C H O R
|
|
*
|
|
* Routine: StartAnchor
|
|
*
|
|
* Purpose: Start a hyperlink within the page
|
|
*
|
|
*/
|
|
|
|
PRIVATE void start_anchor(HTStructured *target, CONST char *href)
|
|
{
|
|
BOOL present[HTML_A_ATTRIBUTES];
|
|
CONST char *value[HTML_A_ATTRIBUTES];
|
|
int i;
|
|
|
|
for (i = 0; i < HTML_A_ATTRIBUTES; i++)
|
|
present[i] = FALSE;
|
|
present[HTML_A_HREF] = TRUE;
|
|
value[HTML_A_HREF] = href;
|
|
(*target->isa->start_element) (target, HTML_A, present, value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Paste in an Anchor
|
|
** ------------------
|
|
**
|
|
**
|
|
** On entry,
|
|
** HT has a selection of zero length at the end.
|
|
** text points to the text to be put into the file, 0 terminated.
|
|
** addr points to the hypertext refernce address,
|
|
** terminated by white space, comma, NULL or '>'
|
|
*/
|
|
PRIVATE void write_anchor(HTStructured *target, CONST char *text, CONST char *addr)
|
|
{
|
|
char href[LINE_LENGTH + 1];
|
|
|
|
{
|
|
CONST char *p;
|
|
strcpy(href, "news:");
|
|
for (p = addr; *p && (*p != '>') && !WHITE(*p) && (*p != ','); p++) ;
|
|
strncat(href, addr, p - addr); /* Make complete hypertext reference */
|
|
}
|
|
|
|
start_anchor(target, href);
|
|
(target->isa->put_string)(target, text);
|
|
(target->isa->end_element)(target, HTML_A);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* W R I T E _ M A I L T O
|
|
*
|
|
* Routine: write_mailto
|
|
*
|
|
* Purpose: Place a "mailto:" link in the page
|
|
*
|
|
*/
|
|
|
|
PRIVATE void write_mailto(HTStructured *target, CONST char *text, CONST char *addr, int len)
|
|
{
|
|
char href[LINE_LENGTH + 1];
|
|
|
|
strcpy(href, "mailto:");
|
|
strncat(href, addr, len );
|
|
|
|
|
|
start_anchor(target, href);
|
|
(target->isa->put_string)(target, text);
|
|
(target->isa->end_element)(target, HTML_A);
|
|
}
|
|
|
|
/* Write list of anchors
|
|
** ---------------------
|
|
**
|
|
** We take a pointer to a list of objects, and write out each,
|
|
** generating an anchor for each.
|
|
**
|
|
** On entry,
|
|
** HT has a selection of zero length at the end.
|
|
** text points to a comma or space separated list of addresses.
|
|
** On exit,
|
|
** *text is NOT any more chopped up into substrings.
|
|
*/
|
|
PRIVATE void write_anchors(HTStructured *target, char *text)
|
|
{
|
|
char *start = text;
|
|
char *end;
|
|
char c;
|
|
for (;;)
|
|
{
|
|
for (; *start && (WHITE(*start)); start++) ; /* Find start */
|
|
if (!*start)
|
|
return; /* (Done) */
|
|
for (end = start; *end && (*end != ' ') && (*end != ','); end++) ; /* Find end */
|
|
if (*end)
|
|
end++; /* Include comma or space but not NULL */
|
|
c = *end;
|
|
*end = 0;
|
|
write_anchor(target, start, start);
|
|
(*target->isa->start_element)(target, HTML_BR, 0, 0);
|
|
*end = c;
|
|
start = end; /* Point to next one */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* GetDateTime()
|
|
*
|
|
* Get system date and time in nicely formatted string
|
|
*/
|
|
|
|
VOID
|
|
GetDateTime( CHAR *szDateTime, DWORD dwSize )
|
|
{
|
|
SYSTEMTIME time;
|
|
|
|
GetLocalTime( &time );
|
|
|
|
GetDateFormat( 0, DATE_LONGDATE, &time, NULL, szDateTime, dwSize - 3); // leave room for the strcat below
|
|
strcat(szDateTime, " ");
|
|
GetTimeFormat( 0, TIME_NOSECONDS, &time, NULL, &(szDateTime[strlen(szDateTime)]), dwSize - (strlen(szDateTime) + 1));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Code to handle reading the list of available newsgroups */
|
|
|
|
struct Params_ReadList {
|
|
enum { CACHED, READING } type;
|
|
HTInputSocket * isoc;
|
|
int * pStatus;
|
|
HTStructured * target;
|
|
FILE *fhNewsFile;
|
|
char abCacheFileName[MAX_PATH];
|
|
BOOL bWaiting;
|
|
|
|
/* Used internally */
|
|
char line[LINE_LENGTH + 1];
|
|
int index; /* Index into line[] */
|
|
};
|
|
|
|
#define STATE_LIST_GOTDATA (STATE_OTHER)
|
|
#define STATE_GET_FROM_FILE (STATE_OTHER+1)
|
|
#define STATE_CONT_FROM_FILE (STATE_OTHER+2)
|
|
#define STATE_READ_FROM_SERVER (STATE_OTHER+3)
|
|
|
|
|
|
|
|
|
|
/*
|
|
* E X I S T S N E W S C A C H E F I L E
|
|
*
|
|
* Routine: ExistsNewsCacheFile()
|
|
*
|
|
* Purpose: Return true if the news cache file exists
|
|
*/
|
|
|
|
static BOOL
|
|
ExistsNewsCacheFile()
|
|
{
|
|
if (GetFileAttributes( gPrefs.szNNTP_CacheFile ) != 0xFFFFFFFF)
|
|
return( TRUE );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
/*
|
|
* C R E A T E N E W S C A C H E F I L E
|
|
*
|
|
* Routine: CreateNewsCacheFile()
|
|
*
|
|
* Purpose: Create a file in the cache directory using a
|
|
* a name generated by the system as a file to
|
|
* store the list of newsgroups in
|
|
*
|
|
*/
|
|
|
|
FILE *
|
|
CreateNewsCacheFile( struct Params_ReadList *pParams)
|
|
{
|
|
CHAR szFileName[MAX_PATH];
|
|
FILE *fp;
|
|
|
|
/*
|
|
* On first time startup of IE the cache directory might
|
|
* not exist.....so we make it....
|
|
*/
|
|
if (GetFileAttributes( gPrefs.szCacheLocation ) == (DWORD) -1) {
|
|
if (! CreateDirectory( gPrefs.szCacheLocation, NULL )) {
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
|
|
if (! ExistsNewsCacheFile()) {
|
|
if (GetTempFileName( gPrefs.szCacheLocation, "NWS", 0, szFileName ) == 0) {
|
|
XX_Assert((0),("CreateNewsCacheFile: Unable to create temporary file for news cache"));
|
|
return( NULL );
|
|
}
|
|
} else {
|
|
strcpy( szFileName, gPrefs.szNNTP_CacheFile);
|
|
}
|
|
|
|
fp = fopen( szFileName, "w" );
|
|
if (fp == NULL) {
|
|
XX_Assert((0),("CreateNewsCacheFile: Unable to open file %s", szFileName ));
|
|
return( NULL );
|
|
}
|
|
|
|
strcpy( pParams->abCacheFileName, szFileName );
|
|
|
|
return( fp );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* G R O U P L I S T H E A D E R
|
|
*
|
|
* Routine: GroupListHeader()
|
|
*
|
|
* Purpose: Common stuff to do header for group list for both cached
|
|
* file and server read listing
|
|
*
|
|
*/
|
|
|
|
static void GroupListHeader( struct Params_ReadList *pParams, BOOL bUpdateNow)
|
|
{
|
|
char szDateTime[200];
|
|
char szString[256];
|
|
|
|
/*
|
|
* Window Title Bar: Available Newsgroups
|
|
*/
|
|
START( HTML_TITLE );
|
|
GTR_formatmsg(RES_STRING_AVAIL_GROUPS,szString,sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_TITLE );
|
|
|
|
|
|
/*
|
|
* All of newsgroup listing header will be centered, bold, font size 3
|
|
*/
|
|
FontStart(pParams->target, "3" ); // Larger font for header
|
|
START( HTML_CENTER ); // Center Header
|
|
START( HTML_B ); // And Bold
|
|
|
|
/*
|
|
* Header: (font size 3, bold, centered)
|
|
* "Available newsgroups from server <foo>"
|
|
*/
|
|
GTR_formatmsg(RES_STRING_FROM_SERVER, szString, sizeof(szString)); // Header String
|
|
PUTS(szString);
|
|
if (bUpdateNow) // If we are getting list from server, reset server name in case it changed
|
|
strcpy(gPrefs.szNNTP_CacheServer, gPrefs.szNNTP_Server );
|
|
PUTS( gPrefs.szNNTP_CacheServer ); // Display Server Name
|
|
PUTS(">"); // End of <servername>
|
|
START( HTML_BR );
|
|
|
|
/*
|
|
* Next Line (still centered and bold and font size 3)
|
|
* "Last Update on (date here)"
|
|
*/
|
|
PUTS( GTR_formatmsg(RES_STRING_LAST_UPDATED, szString, sizeof(szString)));
|
|
if (bUpdateNow) { // If updated now, then reset the global date information
|
|
GetDateTime( szDateTime, sizeof(szDateTime) );
|
|
strcpy( gPrefs.szNNTP_CacheDate, szDateTime );
|
|
}
|
|
PUTS( gPrefs.szNNTP_CacheDate ); // Print the Date
|
|
START( HTML_BR );
|
|
|
|
|
|
/*
|
|
* Next Line (Still centered and bold and font size 3)
|
|
* "Press refresh button to ...."
|
|
*/
|
|
PUTS(GTR_formatmsg(RES_STRING_REFRESH_DIRECTIONS, szString, sizeof(szString)));
|
|
|
|
|
|
/*
|
|
* End the Bold, center, and font size
|
|
*/
|
|
END( HTML_B );
|
|
END( HTML_CENTER );
|
|
END( HTML_FONT );
|
|
|
|
/*
|
|
* Draw a horizontal bar to separate the header from
|
|
* the listing
|
|
*/
|
|
HorizontalBar( pParams->target );
|
|
START( HTML_DL );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ R E A D L I S T _ A S Y N C
|
|
*
|
|
* Routine: News_ReadList_Async()
|
|
*
|
|
* Purpose: Read and display the list of all newsgroups
|
|
*
|
|
* Since this operation may take a very long time
|
|
* depending on the number of groups on the server
|
|
* we will cache this information.
|
|
*/
|
|
|
|
static int News_ReadList_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_ReadList *pParams;
|
|
char ch;
|
|
char *p;
|
|
char *pNext;
|
|
int i;
|
|
char szString[256];
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
if (pParams->type == CACHED)
|
|
return( STATE_GET_FROM_FILE );
|
|
else
|
|
return( STATE_READ_FROM_SERVER );
|
|
break;
|
|
|
|
|
|
/*
|
|
* Reading News From Server:
|
|
* (STATE_READ_FROM_SERVER and STATE_LIST_GOTDATA)
|
|
*
|
|
* Create file to cache contents with
|
|
* Read groups from server and save in file and display
|
|
*/
|
|
case STATE_READ_FROM_SERVER:
|
|
pParams->bWaiting = TRUE;
|
|
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_LOADING_GROUPLIST, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
|
|
pParams->fhNewsFile = CreateNewsCacheFile(pParams);
|
|
if (pParams->fhNewsFile == NULL) {
|
|
XX_Assert((0),("Unable to create news file"));
|
|
return( STATE_ABORT );
|
|
}
|
|
/*
|
|
* Display the big header
|
|
*/
|
|
GroupListHeader(pParams, TRUE );
|
|
|
|
pParams->index = 0;
|
|
if (pParams->isoc->input_buffer >= pParams->isoc->input_limit)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_LIST_GOTDATA;
|
|
}
|
|
/* Otherwise just hack a status value and fall through */
|
|
*pParams->pStatus = 1;
|
|
|
|
case STATE_LIST_GOTDATA:
|
|
WAIT_Update(tw, waitSameInteract, GTR_formatmsg(RES_STRING_LOADING_GROUPLIST_NNTP, szString, sizeof(szString)));
|
|
// WAIT_Update(tw, waitSameInteract, "Loading Grouplist from NNTP Server");
|
|
if (*pParams->pStatus > 0)
|
|
{
|
|
for (pNext = pParams->isoc->input_pointer; pNext < pParams->isoc->input_limit; pNext++)
|
|
{
|
|
ch = *pNext;
|
|
if (ch == EOF)
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
}
|
|
else if (ch == CR)
|
|
{
|
|
continue;
|
|
}
|
|
else if (ch != LF)
|
|
{
|
|
pParams->line[pParams->index] = ch; /* Put character in line */
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
}
|
|
else
|
|
{
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
if (pParams->line[0] == '.' && pParams->line[1] < ' ')
|
|
{
|
|
/* End of data */
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
/* Figure out where the newsgroup name ends */
|
|
for (p = pParams->line; *p; p++)
|
|
{
|
|
if (isspace(*p))
|
|
break;
|
|
}
|
|
if (*p)
|
|
{
|
|
// Punt the description stuff
|
|
*p = '\0';
|
|
p = NULL;
|
|
}
|
|
fputs(pParams->line, pParams->fhNewsFile );
|
|
fputc('\n', pParams->fhNewsFile );
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_DT, 0, 0);
|
|
start_anchor(pParams->target, pParams->line);
|
|
(*pParams->target->isa->put_string)(pParams->target, pParams->line);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
if (p && *p != '?')
|
|
{
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_DD, 0, 0);
|
|
(*pParams->target->isa->put_string)(pParams->target, p);
|
|
}
|
|
}
|
|
}
|
|
if (ch != EOF)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
/* Show what we have so far */
|
|
(*pParams->target->isa->block_done)(pParams->target);
|
|
|
|
/* Get next block of data */
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_LIST_GOTDATA;
|
|
}
|
|
}
|
|
|
|
/* Clean up */
|
|
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_DL);
|
|
*pParams->pStatus = 1;
|
|
fclose( pParams->fhNewsFile );
|
|
strcpy( gPrefs.szNNTP_CacheFile, pParams->abCacheFileName );
|
|
regWritePrivateProfileString("Services", "NNTP_CacheFile", gPrefs.szNNTP_CacheFile, HKEY_CURRENT_USER );
|
|
regWritePrivateProfileString("Services", "NNTP_CacheDate", gPrefs.szNNTP_CacheDate, HKEY_CURRENT_USER );
|
|
regWritePrivateProfileString("Services", "NNTP_CacheServer", gPrefs.szNNTP_CacheServer, HKEY_CURRENT_USER );
|
|
if (pParams->bWaiting) {
|
|
WAIT_Pop(tw);
|
|
pParams->bWaiting = FALSE;
|
|
}
|
|
return STATE_DONE;
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Get the list of newsgroups from a file
|
|
*/
|
|
|
|
case STATE_GET_FROM_FILE:
|
|
|
|
/*
|
|
* If we can't open the cached file then bail out and
|
|
* read directly from server
|
|
*/
|
|
pParams->fhNewsFile = fopen(gPrefs.szNNTP_CacheFile, "r");
|
|
if (pParams->fhNewsFile == NULL) {
|
|
XX_Assert((0),("Unable to open NEWSRC file for reading"));
|
|
return STATE_ABORT;
|
|
}
|
|
|
|
pParams->bWaiting = TRUE;
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_LOADING_GROUPLIST_CACHE, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
|
|
/*
|
|
* Display the big header
|
|
*/
|
|
GroupListHeader( pParams, FALSE );
|
|
|
|
/* fall through */
|
|
|
|
|
|
case STATE_CONT_FROM_FILE:
|
|
WAIT_Update(tw, waitSameInteract, GTR_formatmsg(RES_STRING_LOADING_GROUPLIST_CACHE, szString, sizeof(szString)));
|
|
// WAIT_Update(tw, waitSameInteract, "Loading Grouplist from NNTP Server");
|
|
i = 0;
|
|
while ((i < CACHE_CHUNK) && (fgets(pParams->line, sizeof(pParams->line), pParams->fhNewsFile))) {
|
|
pParams->line[strlen(pParams->line)-1] = '\0';
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_DT, 0, 0);
|
|
start_anchor(pParams->target, pParams->line);
|
|
(*pParams->target->isa->put_string)(pParams->target, pParams->line);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
i++;
|
|
}
|
|
/* show what we've got so far */
|
|
(*pParams->target->isa->block_done)(pParams->target);
|
|
|
|
if (i < CACHE_CHUNK) {
|
|
if (pParams->bWaiting) {
|
|
WAIT_Pop(tw);
|
|
pParams->bWaiting = FALSE;
|
|
}
|
|
fclose( pParams->fhNewsFile );
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_DL);
|
|
*pParams->pStatus = 1;
|
|
return STATE_DONE;
|
|
} else {
|
|
return( STATE_CONT_FROM_FILE );
|
|
}
|
|
/*NOTREACHED*/
|
|
break;
|
|
|
|
case STATE_ABORT:
|
|
if (pParams->bWaiting) {
|
|
WAIT_Pop(tw);
|
|
}
|
|
if (pParams->fhNewsFile != NULL)
|
|
fclose( pParams->fhNewsFile );
|
|
*pParams->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* D E S T R O Y _ L I N K S
|
|
*
|
|
* Routine: DestroyLinks()
|
|
*
|
|
* Purpose: Free up the memory used by a struct article_links
|
|
* structure. A pointer to this function is provided to
|
|
* the hashtable for article linkage so that it can clean up
|
|
* memory
|
|
*/
|
|
|
|
void
|
|
DestroyLinks( void *item )
|
|
{
|
|
struct article_links *links = item;
|
|
|
|
if (links->next)
|
|
GTR_FREE( links->next );
|
|
|
|
if (links->prev)
|
|
GTR_FREE( links->prev );
|
|
|
|
GTR_FREE( links );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* A D D A R T I C L E L I N K A G E
|
|
*
|
|
* Routine: AddArticleLinkage()
|
|
*
|
|
* Purpose: Add associations for previous and next article to
|
|
* a specific article message ID in the global linkage
|
|
* hash table
|
|
*
|
|
* Comments: PREV and NEXT can be NULL (it doesn't make much sense
|
|
* for both to be NULL but the single case occurs at the
|
|
* beginning and ending boundry of the article info information
|
|
*/
|
|
|
|
static void
|
|
AddArticleLinkage( char *curr, char *next, char *prev, int idnum )
|
|
{
|
|
struct article_links *links;
|
|
|
|
links = GTR_MALLOC( sizeof( *links ) );
|
|
|
|
if (next)
|
|
links->next = GTR_strdup( next );
|
|
else
|
|
links->next = NULL;
|
|
|
|
if (prev)
|
|
links->prev = GTR_strdup( prev );
|
|
else
|
|
links->prev = NULL;
|
|
|
|
links->id = idnum;
|
|
|
|
// Hash it
|
|
Hash_Add( hashArticlePointers, curr, NULL, (void *) links );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef OLDSTUFF
|
|
|
|
/*
|
|
* G E T I D
|
|
*
|
|
* Routine: GetID()
|
|
*
|
|
* Purpose: Get Article ID out of XOVER information line
|
|
*
|
|
* Comments: Article ID number is item 5 in the tab separated information
|
|
* for each article returned by the XOVER NNTP command.
|
|
*
|
|
* This function allocates the storage for a copy of the
|
|
* ID that has been stripped of the '<' and '>' delimeters
|
|
*/
|
|
|
|
static BOOL
|
|
GetID( char *line, char **szID, int *idnum )
|
|
{
|
|
char *pch;
|
|
char *start = NULL;
|
|
char *end = NULL;
|
|
int cTabs = 0;
|
|
|
|
cTabs = 0;
|
|
pch = line;
|
|
|
|
|
|
/*
|
|
* Go through the string character by character....
|
|
*
|
|
* When we get to the first tab, grab the article number
|
|
* by turning the tab into a string terminator and atoi()'ing
|
|
* the line which now contains only the article id. After
|
|
* getting it restore the tab character and continue
|
|
*
|
|
* when we get to the 4th tab set the beginning of the
|
|
* article ID string (if valid) with 'start'. When we
|
|
* reach the 5th tab set the end of the article ID string
|
|
*/
|
|
for (pch = line; *pch; pch++) {
|
|
if (*pch == '\t') {
|
|
cTabs++;
|
|
if (cTabs == 1) {
|
|
*pch = '\0';
|
|
*idnum = atoi( line );
|
|
*pch = '\t';
|
|
}
|
|
if ((cTabs == 4) && (*(pch+1)) && (*(pch+2)) )
|
|
start = pch + 2;
|
|
if (cTabs == 5) {
|
|
end = pch - 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we found what seems like a valid article ID string
|
|
* then allocate space, copy it, terminate it, and return TRUE
|
|
*
|
|
* If we didn't find a valid article ID string then return FALSE
|
|
*/
|
|
if ((start && end) && (end > start)) {
|
|
*szID = (char *) GTR_MALLOC( end - start + 2 );
|
|
strncpy( *szID, start, end - start + 1 );
|
|
(*szID)[end - start + 1] = '\0';
|
|
return( TRUE );
|
|
} else {
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // OLDSTUFF
|
|
|
|
|
|
/*
|
|
* G E T I D
|
|
*
|
|
* Routine: GetID()
|
|
*
|
|
* Purpose: Get Article ID out of XHDR MESSAGE-ID information line
|
|
*
|
|
* Comments: Article ID number is item 5 in the tab separated information
|
|
* for each article returned by the XOVER NNTP command.
|
|
*
|
|
* This function allocates the storage for a copy of the
|
|
* ID that has been stripped of the '<' and '>' delimeters
|
|
*/
|
|
|
|
static BOOL
|
|
GetID( char *line, char **szID, int *idnum )
|
|
{
|
|
char *start = NULL;
|
|
char *end = NULL;
|
|
|
|
|
|
// ID Number is first thing in line
|
|
*idnum = atoi( line );
|
|
|
|
start = strchr( line, '<') + 1;
|
|
end = strrchr( line, '>') - 1;
|
|
if (start && end && (end > start)) {
|
|
*szID = (char *) GTR_MALLOC( end - start + 2 );
|
|
if (*szID) {
|
|
strncpy( *szID, start, end - start + 1 );
|
|
(*szID)[end - start + 1] = '\0';
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Code to handle reading a list of articles from a newsgroup */
|
|
|
|
struct Params_ReadGroup {
|
|
HTInputSocket * isoc;
|
|
int * pStatus;
|
|
HTStructured * target;
|
|
int nFirst; /* First article desired for list */
|
|
int nLast; /* Last article desired */
|
|
int nFirstInGroup;
|
|
int nLastInGroup;
|
|
int nArticleCount;
|
|
char szGroup[256];
|
|
|
|
/* Used internally */
|
|
char line[LINE_LENGTH + 1];
|
|
int index; /* Index into line[] */
|
|
|
|
char *szIdNext;
|
|
char *szIdCurr;
|
|
char *szIdPrev;
|
|
|
|
struct hash_table hashArticles; /* Used to match up article IDs and subjects */
|
|
BOOL bNeedHashFree;
|
|
BOOL bSkip; // Signal to skip one line from server
|
|
};
|
|
|
|
#define STATE_GROUP_GOTIDS (STATE_OTHER)
|
|
#define STATE_GROUP_GOTIDDATA (STATE_OTHER+1)
|
|
#define STATE_GROUP_GOTSUBJECTS (STATE_OTHER+2)
|
|
#define STATE_GROUP_GOTSUBJECTDATA (STATE_OTHER+3)
|
|
#define STATE_GROUP_GOTAUTHORS (STATE_OTHER+4)
|
|
#define STATE_GROUP_GOTAUTHORDATA (STATE_OTHER+5)
|
|
#define STATE_GROUP_START (STATE_OTHER+6)
|
|
#define STATE_GROUP_XOVER (STATE_OTHER+7)
|
|
#define STATE_GROUP_GOT_XOVER (STATE_OTHER+8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ R E A D G R O U P _ A S Y N C ( )
|
|
*
|
|
* Routine: News_ReadGroup_Async()
|
|
*
|
|
* Purpose: Read and display list of available articles in a newsgroup
|
|
*
|
|
*
|
|
* GLOBALS: hashArticlePointers (pointer to hash table to store prev,next)
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
static int News_ReadGroup_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_ReadGroup *pParams;
|
|
char ch;
|
|
char *pNext;
|
|
char *p;
|
|
int n;
|
|
int ndx;
|
|
char *pID;
|
|
char *pSubj;
|
|
char buf[512+1];
|
|
int linkbegin;
|
|
char *szId;
|
|
char szString[256];
|
|
int artid;
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState)
|
|
{
|
|
|
|
/*
|
|
* In order to support <prev> and <next> article buttons
|
|
* we need to keep a hash table that has a (prev, next)
|
|
* article ID for each article in a group.
|
|
*
|
|
* On Group Change we go and build a new hashtable of
|
|
* this information
|
|
*/
|
|
|
|
case STATE_INIT:
|
|
if (pParams->nLast == 0)
|
|
return( STATE_GROUP_START );
|
|
|
|
if (strcmp( pParams->szGroup, szGlobalHashedGroup) != 0) {
|
|
struct Params_News_Command *pnc;
|
|
|
|
strcpy( szGlobalHashedGroup, pParams->szGroup);
|
|
|
|
pParams->szIdNext = pParams->szIdPrev = pParams->szIdCurr = NULL;
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pParams->isoc;
|
|
pnc->cmd = GTR_MALLOC(100);
|
|
pnc->pResult = pParams->pStatus;
|
|
pnc->ppResText = NULL;
|
|
linkbegin = pParams->nLast - LINK_COUNT;
|
|
if (linkbegin < pParams->nFirstInGroup )
|
|
linkbegin = pParams->nFirstInGroup;
|
|
sprintf(pnc->cmd, "XHDR Message-ID %d-%d\015\012", linkbegin, pParams->nLast);
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
return STATE_GROUP_XOVER;
|
|
} else {
|
|
return STATE_GROUP_START;
|
|
}
|
|
|
|
/*
|
|
* Initial Version - Just eat up all the XOVER
|
|
* data that the server sends to us....don't do
|
|
* anything useful with it yet.
|
|
*/
|
|
case STATE_GROUP_XOVER:
|
|
if (*pParams->pStatus < 0) {
|
|
return STATE_GROUP_START;
|
|
}
|
|
|
|
// If line returned is not 2XX then we had an error
|
|
if (*pParams->pStatus / 100 != 2) {
|
|
return STATE_GROUP_START;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set up hash table (destroy old one if it existed)
|
|
*/
|
|
if (hashArticlePointers)
|
|
Hash_Destroy( hashArticlePointers);
|
|
hashArticlePointers = Hash_Create();
|
|
Hash_SetFreeFunc( hashArticlePointers, DestroyLinks );
|
|
|
|
pParams->index = 0;
|
|
ch = 0;
|
|
// Suck up the Server Status Line
|
|
pParams->bSkip = TRUE;
|
|
|
|
// FALL THROUGH
|
|
|
|
case STATE_GROUP_GOT_XOVER:
|
|
if (*pParams->pStatus <= 0) {
|
|
*pParams->pStatus = -1;
|
|
return STATE_GROUP_START;
|
|
} else {
|
|
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++) {
|
|
ch = *pNext;
|
|
|
|
if (ch == EOF) {
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
} else if (ch == CR) {
|
|
continue;
|
|
} else if (ch != LF) {
|
|
pParams->line[pParams->index] = ch; /* Put character in line */
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
} else {
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
|
|
if (pParams->bSkip) {
|
|
pParams->bSkip = FALSE;
|
|
continue;
|
|
}
|
|
|
|
// Did We Reach End of Response?
|
|
if (pParams->line[0] == '.' && pParams->line[1] < ' ') {
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
AddArticleLinkage( pParams->szIdNext, NULL, pParams->szIdCurr, -1);
|
|
break;
|
|
}
|
|
|
|
// Get the article ID
|
|
if (GetID( pParams->line, &szId, &artid )) {
|
|
if (pParams->szIdPrev)
|
|
GTR_FREE( pParams->szIdPrev );
|
|
pParams->szIdPrev = pParams->szIdCurr;
|
|
pParams->szIdCurr = pParams->szIdNext;
|
|
pParams->szIdNext = szId;
|
|
|
|
if (pParams->szIdCurr)
|
|
AddArticleLinkage( pParams->szIdCurr, pParams->szIdNext, pParams->szIdPrev, artid );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get Next Batch full of XOVER data
|
|
if (ch != EOF) {
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_GROUP_GOT_XOVER;
|
|
} else {
|
|
if (pParams->szIdPrev)
|
|
GTR_FREE( pParams->szIdPrev );
|
|
if (pParams->szIdCurr)
|
|
GTR_FREE( pParams->szIdCurr );
|
|
if (pParams->szIdNext)
|
|
GTR_FREE( pParams->szIdNext );
|
|
}
|
|
}
|
|
return STATE_GROUP_START;
|
|
|
|
|
|
case STATE_GROUP_START:
|
|
// Set Iexplore Window Title
|
|
// "Newsgroup %s (Articles %d - %d)"
|
|
GTR_formatmsg(RES_STRING_NEWSGROUP_HEADER, szString, sizeof(szString));
|
|
|
|
sprintf(buf, szString, pParams->szGroup, pParams->nFirst, pParams->nLast);
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_TITLE, 0, 0);
|
|
(*pParams->target->isa->put_string)(pParams->target, buf);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_TITLE);
|
|
|
|
|
|
/*
|
|
* Change Colors as per ArthurBl recommendations
|
|
*/
|
|
// BodyStart( pParams->target );
|
|
|
|
|
|
|
|
|
|
/*
|
|
* LAYOUT: (table)
|
|
*
|
|
* Newgroup: news.name See list of newsgroups
|
|
* Earlier articles | Later articles 309 articles
|
|
*/
|
|
START( HTML_B );
|
|
FontStart(pParams->target, "3" );
|
|
TableStart( pParams->target, "100%" );
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, NULL, NULL );
|
|
|
|
PUTS( GTR_formatmsg( RES_STRING_NEWSGROUP, szString, sizeof(szString)));
|
|
PUTS( pParams->szGroup );
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL );
|
|
GTR_formatmsg( RES_STRING_SEE_LIST_NEWSGROUPS, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, "*" );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
|
|
|
|
|
|
|
|
START( HTML_B );
|
|
FontStart( pParams->target, "2");
|
|
TableStart( pParams->target, "100%");
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, NULL, NULL );
|
|
|
|
/*
|
|
* Earlier articles | Later Articles
|
|
*/
|
|
/* Link to earlier articles */
|
|
if (pParams->nFirst > pParams->nFirstInGroup)
|
|
{
|
|
int start;
|
|
|
|
if (pParams->nFirst - CHUNK_SIZE <= pParams->nFirstInGroup)
|
|
start = pParams->nFirstInGroup;
|
|
else
|
|
start = pParams->nFirst - CHUNK_SIZE;
|
|
sprintf(buf, "%s/%d-%d", pParams->szGroup, start, pParams->nFirst - 1);
|
|
start_anchor(pParams->target, buf);
|
|
GTR_formatmsg(RES_STRING_EARLIER_ARTICLES, szString, sizeof(szString));
|
|
(*pParams->target->isa->put_string)(pParams->target, szString);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
|
|
if (pParams->nLast < pParams->nLastInGroup) {
|
|
PUTS(" | ");
|
|
}
|
|
}
|
|
|
|
/* Link to later articles */
|
|
if (pParams->nLast < pParams->nLastInGroup)
|
|
{
|
|
int end;
|
|
|
|
if (pParams->nLast + CHUNK_SIZE >= pParams->nLastInGroup)
|
|
end = pParams->nLastInGroup;
|
|
else
|
|
end = pParams->nLast + CHUNK_SIZE;
|
|
sprintf(buf, "%s/%d-%d", pParams->szGroup, pParams->nLast + 1, end);
|
|
start_anchor(pParams->target, buf);
|
|
GTR_formatmsg(RES_STRING_LATER_ARTICLES, szString, sizeof(szString));
|
|
// "Later articles"
|
|
(*pParams->target->isa->put_string)(pParams->target, szString);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
}
|
|
END( HTML_TD );
|
|
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL);
|
|
sprintf(szString, "newspost:%s", pParams->szGroup );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_POST, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
PUTS(" | ");
|
|
GTR_formatmsg(RES_STRING_ARTICLE_COUNT, szString, sizeof(szString));
|
|
sprintf(buf, szString, pParams->nArticleCount );
|
|
PUTS( buf );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
|
|
|
|
|
|
/* Horizontal Bar */
|
|
HorizontalBar( pParams->target );
|
|
|
|
/* Table for article info */
|
|
// Table
|
|
TableStart(pParams->target, "100%");
|
|
|
|
// Row
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, "15%", "TOP" );
|
|
|
|
GTR_formatmsg( RES_STRING_ARTICLE, szString, sizeof(szString));
|
|
// "Article"
|
|
PUTS(szString);
|
|
|
|
END( HTML_TD );
|
|
// Col
|
|
CellStart( pParams->target, NULL, "65%", "TOP" );
|
|
GTR_formatmsg(RES_STRING_SUBJECT, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_TD );
|
|
|
|
// Col
|
|
CellStart( pParams->target, NULL, "20%", "TOP" );
|
|
GTR_formatmsg( RES_STRING_AUTHOR, szString, sizeof(szString));
|
|
// "Author"
|
|
PUTS(szString);
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
|
|
|
|
|
|
Hash_Init(&pParams->hashArticles);
|
|
pParams->bNeedHashFree = TRUE;
|
|
|
|
/* Get all of the message IDs for these articles. We'll later match them up
|
|
with the subjects. */
|
|
{
|
|
struct Params_News_Command *pnc;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pParams->isoc;
|
|
pnc->cmd = GTR_MALLOC(100);
|
|
pnc->pResult = pParams->pStatus;
|
|
pnc->ppResText = NULL;
|
|
sprintf(pnc->cmd, "XHDR Message-ID %d-%d\015\012", pParams->nFirst, pParams->nLast);
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
}
|
|
return STATE_GROUP_GOTIDS;
|
|
|
|
case STATE_GROUP_GOTIDS:
|
|
if (*pParams->pStatus < 0)
|
|
{
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
if (*pParams->pStatus / 100 != 2)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
ERR_ReportError(tw, errNewsNoXHDR, NULL, NULL);
|
|
return STATE_DONE;
|
|
}
|
|
pParams->index = 0;
|
|
ch = 0;
|
|
/* Fall through */
|
|
case STATE_GROUP_GOTIDDATA:
|
|
if (*pParams->pStatus <= 0)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++)
|
|
{
|
|
ch = *pNext;
|
|
if (ch == EOF)
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
}
|
|
else if (ch == CR)
|
|
{
|
|
continue;
|
|
}
|
|
else if (ch != LF)
|
|
{
|
|
pParams->line[pParams->index] = ch; /* Put character in line */
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
}
|
|
else
|
|
{
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
|
|
if (pParams->line[0] == '.' && pParams->line[1] < ' ')
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
p = strchr(pParams->line, '>');
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
}
|
|
else
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
n = atoi(pParams->line);
|
|
if (!n)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p = strchr(pParams->line, '<');
|
|
if (!p)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p++; /* Now p points to message-id */
|
|
/* Add this message-id to the hash along with its article number */
|
|
Hash_Add(&pParams->hashArticles, p, NULL, (void *) n);
|
|
}
|
|
}
|
|
|
|
if (ch != EOF)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
/* Get next block of data */
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_GROUP_GOTIDDATA;
|
|
}
|
|
}
|
|
|
|
/* Now get all of the subjects for these articles */
|
|
{
|
|
struct Params_News_Command *pnc;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pParams->isoc;
|
|
pnc->cmd = GTR_MALLOC(100);
|
|
pnc->pResult = pParams->pStatus;
|
|
pnc->ppResText = NULL;
|
|
sprintf(pnc->cmd, "XHDR Subject %d-%d\015\012", pParams->nFirst, pParams->nLast);
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
}
|
|
return STATE_GROUP_GOTSUBJECTS;
|
|
|
|
case STATE_GROUP_GOTSUBJECTS:
|
|
if (*pParams->pStatus < 0)
|
|
{
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
if (*pParams->pStatus / 100 != 2)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
ERR_ReportError(tw, errNewsNoXHDR, NULL, NULL);
|
|
return STATE_DONE;
|
|
}
|
|
pParams->index = 0;
|
|
ch = 0;
|
|
/* Fall through */
|
|
case STATE_GROUP_GOTSUBJECTDATA:
|
|
if (*pParams->pStatus <= 0)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++)
|
|
{
|
|
ch = *pNext;
|
|
if (ch == EOF)
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
}
|
|
else if (ch == CR)
|
|
{
|
|
continue;
|
|
}
|
|
else if (ch != LF)
|
|
{
|
|
pParams->line[pParams->index] = ch; /* Put character in line */
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
}
|
|
else
|
|
{
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
|
|
if (pParams->line[0] == '.' && pParams->line[1] < ' ')
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
n = atoi(pParams->line);
|
|
if (!n)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p = strchr(pParams->line, ' ');
|
|
if (!p)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p++; /* Now p points to the subject */
|
|
/* Match up this subject with its message-id */
|
|
ndx = Hash_FindByData(&pParams->hashArticles, NULL, NULL, (void *) n);
|
|
if (ndx < 0)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Subject has no message-id: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
Hash_SetString2(&pParams->hashArticles, ndx, p);
|
|
}
|
|
}
|
|
|
|
if (ch != EOF)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
/* Get next block of data */
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_GROUP_GOTSUBJECTDATA;
|
|
}
|
|
}
|
|
|
|
/* Get the authors for these articles */
|
|
{
|
|
struct Params_News_Command *pnc;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pParams->isoc;
|
|
pnc->cmd = GTR_MALLOC(100);
|
|
pnc->pResult = pParams->pStatus;
|
|
pnc->ppResText = NULL;
|
|
sprintf(pnc->cmd, "XHDR From %d-%d\015\012", pParams->nFirst, pParams->nLast);
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
}
|
|
return STATE_GROUP_GOTAUTHORS;
|
|
|
|
case STATE_GROUP_GOTAUTHORS:
|
|
if (*pParams->pStatus < 0)
|
|
{
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
if (*pParams->pStatus / 100 != 2)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
ERR_ReportError(tw, errNewsNoXHDR, NULL, NULL);
|
|
return STATE_DONE;
|
|
}
|
|
pParams->index = 0;
|
|
ch = 0;
|
|
/* Fall through */
|
|
case STATE_GROUP_GOTAUTHORDATA:
|
|
if (*pParams->pStatus <= 0)
|
|
{
|
|
*pParams->pStatus = -1;
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
return STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++)
|
|
{
|
|
ch = *pNext;
|
|
if (ch == EOF)
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
}
|
|
else if (ch == CR)
|
|
{
|
|
continue;
|
|
}
|
|
else if (ch != LF)
|
|
{
|
|
pParams->line[pParams->index] = ch; /* Put character in line */
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
}
|
|
else
|
|
{
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
|
|
if (pParams->line[0] == '.' && pParams->line[1] < ' ')
|
|
{
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
n = atoi(pParams->line);
|
|
if (!n)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p = strchr(pParams->line, ' ');
|
|
if (!p)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Strange XHDR response: %s\n", pParams->line));
|
|
continue;
|
|
}
|
|
p++; /* Now p points to author */
|
|
p = author_name(p);
|
|
if (Hash_FindByData(&pParams->hashArticles, &pID, &pSubj, (void *) n) < 0)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("No hash entry for article %d by %s\n", n, p));
|
|
continue;
|
|
}
|
|
|
|
|
|
/* Put together the string to display */
|
|
// Row
|
|
RowStart( pParams->target );
|
|
|
|
// Col
|
|
CellStart( pParams->target, NULL, "15%", "TOP");
|
|
start_anchor(pParams->target, pID);
|
|
sprintf(buf, "%d", n );
|
|
PUTS(buf );
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
END( HTML_TD );
|
|
|
|
// Col
|
|
CellStart( pParams->target, NULL, "65%", "TOP");
|
|
start_anchor(pParams->target, pID);
|
|
GTR_formatmsg( RES_STRING_NO_SUBJECT, szString, sizeof(szString));
|
|
// "(No subject)"
|
|
PUTS( pSubj ? pSubj : szString);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
END( HTML_TD );
|
|
|
|
// Col
|
|
CellStart( pParams->target, NULL, "20%", "TOP");
|
|
start_anchor(pParams->target, pID);
|
|
PUTS(p);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
|
|
/* Start the anchor with the message-id as the reference */
|
|
#ifdef DEPRECATED
|
|
start_anchor(pParams->target, pID);
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_LI, 0, 0);
|
|
GTR_formatmsg( RES_STRING_NO_SUBJECT, szString, sizeof(szString));
|
|
// "(No Subject)"
|
|
sprintf(buf, "#%d \"%s\" - %s", n, pSubj ? pSubj : szString, p);
|
|
(*pParams->target->isa->put_string)(pParams->target, buf);
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
(*pParams->target->isa->start_element)(pParams->target, HTML_BR, 0, 0);
|
|
#endif // DEPRECATED
|
|
}
|
|
}
|
|
|
|
if (ch != EOF)
|
|
{
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
/* Get next block of data */
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_GROUP_GOTAUTHORDATA;
|
|
}
|
|
}
|
|
|
|
/* Clean up */
|
|
|
|
END( HTML_TABLE );
|
|
|
|
/* Horizontal Bar */
|
|
HorizontalBar( pParams->target );
|
|
START( HTML_B );
|
|
|
|
// Font
|
|
FontStart( pParams->target, "2");
|
|
|
|
// Table
|
|
TableStart(pParams->target, "100%");
|
|
|
|
// Row
|
|
RowStart( pParams->target );
|
|
// Col
|
|
CellStart( pParams->target, NULL, NULL, NULL );
|
|
|
|
|
|
/*
|
|
* Earlier articles | Later Articles
|
|
*/
|
|
/* Link to earlier articles */
|
|
if (pParams->nFirst > pParams->nFirstInGroup)
|
|
{
|
|
int start;
|
|
|
|
if (pParams->nFirst - CHUNK_SIZE <= pParams->nFirstInGroup)
|
|
start = pParams->nFirstInGroup;
|
|
else
|
|
start = pParams->nFirst - CHUNK_SIZE;
|
|
sprintf(buf, "%s/%d-%d", pParams->szGroup, start, pParams->nFirst - 1);
|
|
start_anchor(pParams->target, buf);
|
|
GTR_formatmsg( RES_STRING_EARLIER_ARTICLES, szString, sizeof(szString));
|
|
// "Earlier articles");
|
|
(*pParams->target->isa->put_string)(pParams->target, szString );
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
|
|
if (pParams->nLast < pParams->nLastInGroup) {
|
|
PUTS(" | ");
|
|
}
|
|
}
|
|
|
|
/* Link to later articles */
|
|
if (pParams->nLast < pParams->nLastInGroup)
|
|
{
|
|
int end;
|
|
|
|
if (pParams->nLast + CHUNK_SIZE >= pParams->nLastInGroup)
|
|
end = pParams->nLastInGroup;
|
|
else
|
|
end = pParams->nLast + CHUNK_SIZE;
|
|
sprintf(buf, "%s/%d-%d", pParams->szGroup, pParams->nLast + 1, end);
|
|
start_anchor(pParams->target, buf);
|
|
GTR_formatmsg( RES_STRING_LATER_ARTICLES, szString, sizeof(szString));
|
|
// "Later Articles"
|
|
(*pParams->target->isa->put_string)(pParams->target, szString );
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_A);
|
|
}
|
|
END( HTML_TD );
|
|
|
|
|
|
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL);
|
|
sprintf(szString, "newspost:%s", pParams->szGroup );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_POST, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
PUTS(" | ");
|
|
GTR_formatmsg(RES_STRING_ARTICLE_COUNT, szString, sizeof(szString));
|
|
sprintf(buf, szString, pParams->nArticleCount );
|
|
PUTS( buf );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
|
|
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
pParams->bNeedHashFree = FALSE;
|
|
*pParams->pStatus = 1;
|
|
return STATE_DONE;
|
|
|
|
case STATE_ABORT:
|
|
if (pParams->bNeedHashFree)
|
|
Hash_FreeContents(&pParams->hashArticles);
|
|
*pParams->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* State Structure for News_ReadArticle_Async()
|
|
*/
|
|
|
|
struct Params_ReadArticle {
|
|
HTInputSocket * isoc;
|
|
int * pStatus;
|
|
HTStructured * target;
|
|
|
|
/* Used internally */
|
|
char line[LINE_LENGTH + 1];
|
|
int index; /* Index into line[] */
|
|
BOOL bInHead;
|
|
char * newsgroups;
|
|
char * references;
|
|
char * subject;
|
|
char * date;
|
|
char * from;
|
|
char * organization;
|
|
char * mid;
|
|
struct article_links *links;
|
|
int articleindex; // Index into Buffer storing article in TW
|
|
};
|
|
|
|
|
|
|
|
#define STATE_ARTICLE_GOTDATA (STATE_OTHER)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* D I S P L A Y H E A D E R
|
|
*
|
|
* Routine: Display_Header()
|
|
*
|
|
* Purpose: Display a news article header
|
|
*
|
|
* Remarks: This function does almost all of the formatting for a News
|
|
* message.
|
|
*
|
|
*
|
|
* GLOBALS: This routine looks up the Message-ID in the article
|
|
* linkage hash table (global) so that we can display
|
|
* next, prev links
|
|
*/
|
|
|
|
void
|
|
display_header( struct Params_ReadArticle *pParams, struct article_links **retlinks)
|
|
{
|
|
struct article_links *links = NULL;
|
|
char *at;
|
|
char *start;
|
|
char *end;
|
|
char szString[256];
|
|
|
|
/*
|
|
* Check Hash Table for links to previous and next articles
|
|
*/
|
|
if ((pParams->mid) && (hashArticlePointers)) {
|
|
Hash_Find( hashArticlePointers, pParams->mid, NULL, (void **) &links);
|
|
*retlinks = links;
|
|
}
|
|
|
|
|
|
/*
|
|
* Window Title
|
|
*/
|
|
if (pParams->subject) {
|
|
START( HTML_TITLE );
|
|
PUTS( pParams->subject );
|
|
END( HTML_TITLE );
|
|
}
|
|
|
|
// White backround, Black Text, Blue Links (like all other news stuff)
|
|
// BodyStart( pParams->target );
|
|
|
|
// Top Line: "Newsgroup (if we know it) See list of groups"
|
|
// Using font size 3, bold
|
|
START( HTML_B );
|
|
FontStart( pParams->target, "3" );
|
|
TableStart( pParams->target, "100%" );
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, NULL, NULL);
|
|
if (links) {
|
|
PUTS(GTR_formatmsg(RES_STRING_NEWSGROUP, szString, sizeof(szString)));
|
|
// "Newsgroup: "
|
|
PUTS( szGlobalHashedGroup );
|
|
}
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL);
|
|
GTR_formatmsg(RES_STRING_SEE_LIST_NEWSGROUPS, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, "*" );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
|
|
|
|
|
|
// 2nd Line: "Prev | Next Post Response | See List of Articles"
|
|
// Bold, tabled, and font size 2
|
|
START( HTML_B );
|
|
FontStart( pParams->target, "2");
|
|
TableStart( pParams->target, "100%");
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, NULL, NULL);
|
|
if (links) {
|
|
if (links->prev) {
|
|
GTR_formatmsg(RES_STRING_PREVIOUS_ARTICLE, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, links->prev );
|
|
}
|
|
if (links->prev && links->next)
|
|
PUTS(" | ");
|
|
if (links->next) {
|
|
GTR_formatmsg(RES_STRING_NEXT_ARTICLE, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, links->next);
|
|
}
|
|
}
|
|
END( HTML_TD );
|
|
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL );
|
|
sprintf(szString, "newsfollowup:%s", pParams->mid );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_POST_RESPONSE, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
if (links) {
|
|
PUTS(" | ");
|
|
if (links->id > 0)
|
|
sprintf(szString, "news:%s/%d-%d", szGlobalHashedGroup, links->id - (CHUNK_SIZE / 2), links->id + (CHUNK_SIZE / 2));
|
|
else
|
|
sprintf(szString, "news:%s", szGlobalHashedGroup );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_SEE_LIST_ARTICLES, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
}
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
|
|
|
|
|
|
|
|
// Message Header
|
|
HorizontalBar(pParams->target);
|
|
|
|
/*
|
|
* Do Message Header As Table
|
|
*/
|
|
TableStart( pParams->target, NULL );
|
|
FontStart( pParams->target, "2");
|
|
|
|
|
|
/*
|
|
* ROW: Article ID Number
|
|
*/
|
|
if (links && (links->id > 0)) {
|
|
char szId[25];
|
|
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, "LEFT", NULL, "MIDDLE");
|
|
START( HTML_I );
|
|
PUTS("Article #:");
|
|
END(HTML_I);
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, NULL, NULL, "MIDDLE");
|
|
sprintf(szId, "%d", links->id);
|
|
PUTS( szId );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
}
|
|
|
|
|
|
/*
|
|
* ROW: Article Subject
|
|
*/
|
|
if (pParams->subject) {
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, "LEFT", NULL, "MIDDLE");
|
|
START( HTML_I );
|
|
PUTS("Subject: ");
|
|
END( HTML_I );
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, NULL, NULL, "MIDDLE");
|
|
FontStart( pParams->target, "3");
|
|
START( HTML_B );
|
|
PUTS( pParams->subject );
|
|
END( HTML_B );
|
|
END( HTML_FONT );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
GTR_FREE(pParams->subject);
|
|
pParams->subject = NULL;
|
|
}
|
|
// Row: From Line
|
|
if (pParams->from) {
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, "LEFT", NULL, "TOP");
|
|
START( HTML_I );
|
|
PUTS("From: ");
|
|
END( HTML_I );
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, NULL, NULL, "TOP" );
|
|
at = strchr( pParams->from, '@' );
|
|
// Make User Address into mailto: link
|
|
if (at != NULL) {
|
|
start = at;
|
|
end = at;
|
|
while (((start - 1) >= pParams->from) && (*(start-1) != ' ') && (*(start-1) != '<'))
|
|
start--;
|
|
while ((*(end + 1) != '\0') && (*(end+1) != ' ') && (*(end+1) != '>'))
|
|
end++;
|
|
write_mailto( pParams->target, pParams->from, start, end - start + 1);
|
|
} else {
|
|
PUTS( pParams->from );
|
|
}
|
|
// Append organization to user name
|
|
if (pParams->organization) {
|
|
PUTS(", ");
|
|
PUTS(pParams->organization);
|
|
GTR_FREE(pParams->organization);
|
|
pParams->organization = NULL;
|
|
}
|
|
END( HTML_TD );
|
|
GTR_FREE( pParams->from );
|
|
pParams->from = NULL;
|
|
END( HTML_TR );
|
|
}
|
|
|
|
|
|
// Row 3 - Date
|
|
if (pParams->date) {
|
|
// Row
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, "LEFT", NULL, "TOP" );
|
|
START( HTML_I );
|
|
PUTS("Date: ");
|
|
END( HTML_I );
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, NULL, NULL, "TOP" );
|
|
PUTS( pParams->date );
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
GTR_FREE( pParams->date );
|
|
pParams->date = NULL;
|
|
}
|
|
END( HTML_FONT );
|
|
END( HTML_TABLE );
|
|
|
|
|
|
|
|
/*
|
|
* Note that the following fields are not
|
|
* currently displayed....but lets free the
|
|
* memory allocated by em
|
|
*/
|
|
|
|
//BUGBUG - we should probably display this one!
|
|
if (pParams->newsgroups) {
|
|
GTR_FREE( pParams->newsgroups );
|
|
pParams->newsgroups = NULL;
|
|
}
|
|
|
|
if (pParams->references) {
|
|
GTR_FREE(pParams->references);
|
|
pParams->references = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* D I S P L A Y A R T I C L E L I N E
|
|
*
|
|
* Routine: DisplayArticleLine()
|
|
*
|
|
* Purpose: Display a line of text from an article body,
|
|
* picking out article reference links and URLs
|
|
* and turning them into links
|
|
*
|
|
*/
|
|
|
|
struct tag_urltype {
|
|
char *szURLType;
|
|
int cbURLType;
|
|
} URLTypes[] = {
|
|
{ "HTTP", 4 },
|
|
{ "HTTPS", 5 },
|
|
{ "FTP", 3 },
|
|
{ "GOPHER", 6 }
|
|
};
|
|
|
|
int cURLTypes = sizeof(URLTypes) / sizeof(struct tag_urltype);
|
|
|
|
|
|
|
|
|
|
DisplayArticleLine( HTStructured *target, char *l )
|
|
{
|
|
char *curr;
|
|
char *s;
|
|
char *e;
|
|
char temp_e;
|
|
char temp_s;
|
|
int i;
|
|
|
|
curr = l;
|
|
while (*curr) {
|
|
if ((*curr == ':') && (strncmp(curr, "://", 3 ) == 0)) { // note: first test is done for efficiency
|
|
for (i = 0;i < cURLTypes;i++) {
|
|
s = curr - URLTypes[i].cbURLType;
|
|
if ((s >= l) && (_strnicmp( s, URLTypes[i].szURLType, URLTypes[i].cbURLType ) == 0)) {
|
|
e = curr + 2;
|
|
while (*e && (! isspace(*e)) && (*e != ')') && (*e != '\"') && (*e != '>'))
|
|
e++;
|
|
// Display stuff up to the located URL
|
|
temp_s = *s;
|
|
*s = '\0';
|
|
(target->isa->put_string)( target, l);
|
|
*s = temp_s;
|
|
// Display located URL as anchor
|
|
temp_e = *e;
|
|
*e = '\0';
|
|
start_anchor( target, s );
|
|
(target->isa->put_string)( target, s);
|
|
(target->isa->end_element)( target, HTML_A);
|
|
*e = temp_e;
|
|
l = e;
|
|
curr = l;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
curr++;
|
|
}
|
|
|
|
if (*l)
|
|
(target->isa->put_string)( target, l);
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ R E A D A R T I C L E A S Y N C
|
|
*
|
|
*
|
|
* Routine: News_ReadArticle_Async()
|
|
*
|
|
* Purpose: Read and display an individual news article
|
|
*/
|
|
|
|
|
|
static int News_ReadArticle_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_ReadArticle *pParams;
|
|
char *pNext;
|
|
char ch;
|
|
char *p;
|
|
char szString[256];
|
|
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState) {
|
|
case STATE_INIT:
|
|
pParams->index = 0;
|
|
pParams->bInHead = TRUE;
|
|
pParams->newsgroups = NULL;
|
|
pParams->references = NULL;
|
|
pParams->mid = NULL;
|
|
pParams->subject = NULL;
|
|
pParams->date = NULL;
|
|
pParams->from = NULL;
|
|
pParams->organization = NULL;
|
|
|
|
|
|
if (pParams->isoc->input_buffer >= pParams->isoc->input_limit) {
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_ARTICLE_GOTDATA;
|
|
}
|
|
/* Otherwise just hack a status value and fall through */
|
|
*pParams->pStatus = 1;
|
|
|
|
case STATE_ARTICLE_GOTDATA:
|
|
if (*pParams->pStatus <= 0)
|
|
return( STATE_DONE );
|
|
|
|
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++) {
|
|
ch = *pNext;
|
|
|
|
if (ch == EOF) {
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
break;
|
|
} else if (ch == CR) {
|
|
continue; // skip CR
|
|
} else if (ch != LF) {
|
|
/* Put character in line */
|
|
pParams->line[pParams->index] = ch;
|
|
if (pParams->index < LINE_LENGTH - 1)
|
|
pParams->index++;
|
|
} else { // LF
|
|
// pParams->line[pParams->index++] = LF;
|
|
pParams->line[pParams->index] = '\0'; /* Terminate line */
|
|
pParams->index = 0; /* For next time through loop */
|
|
|
|
/*
|
|
* If we were in the header and the next line is blank
|
|
* then we are done with header info
|
|
*/
|
|
if (pParams->bInHead) {
|
|
if (pParams->line[0] == '\0') {
|
|
pParams->bInHead = FALSE;
|
|
|
|
|
|
/*
|
|
* Lets start saving this article in the
|
|
* W3doc in case were going to post a followup
|
|
*
|
|
* Save the subject and newsgroups line as well, but
|
|
* modify the subject line to have a "Re" in front
|
|
* of it if its not already there
|
|
*
|
|
*/
|
|
XX_Assert((tw->w3doc->szArticle == NULL),("W3Doc article text pointer unexpectedly non-null"));
|
|
if (tw->w3doc->szArticle == NULL) {
|
|
tw->w3doc->szArtNewsgroups = GTR_strdup(pParams->newsgroups);
|
|
if (pParams->subject) {
|
|
if (tw->w3doc->szArtSubject = GTR_MALLOC(strlen(pParams->subject) + 10)) {
|
|
if (_strnicmp(pParams->subject, "Re:", 3) != 0) {
|
|
sprintf(tw->w3doc->szArtSubject, "Re: %s", pParams->subject);
|
|
} else {
|
|
strcpy( tw->w3doc->szArtSubject, pParams->subject );
|
|
}
|
|
}
|
|
}
|
|
if (tw->w3doc->szArticle = GTR_MALLOC( MAX_QUOTE_SIZE )) {
|
|
GTR_formatmsg( RES_STRING_QUOTE, szString, sizeof(szString));
|
|
pParams->articleindex = 0;
|
|
// Sum will be a little more than real size, but thats good enough to protect against buffer overflow
|
|
if ((strlen(szString) + strlen(pParams->from)) < MAX_QUOTE_SIZE)
|
|
pParams->articleindex = sprintf(tw->w3doc->szArticle, szString, pParams->from);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* display the header nicely formatted
|
|
*/
|
|
pParams->links = NULL;
|
|
display_header( pParams, &(pParams->links));
|
|
|
|
/*
|
|
* Get ready for article body letting
|
|
* fixed width formatting take over
|
|
*/
|
|
START( HTML_PRE );
|
|
|
|
/*
|
|
* Still in header - load up header lines
|
|
* for later formatting
|
|
*/
|
|
} else if (match(pParams->line, "SUBJECT:")) {
|
|
if (pParams->subject) {
|
|
GTR_FREE(pParams->subject);
|
|
}
|
|
pParams->subject = GTR_strdup(pParams->line + 9);
|
|
} else if (match(pParams->line, "DATE:")) {
|
|
if (pParams->date) {
|
|
GTR_FREE(pParams->date);
|
|
}
|
|
pParams->date = GTR_strdup(pParams->line + 6);
|
|
} else if (match(pParams->line, "ORGANIZATION:")) {
|
|
if (pParams->organization) {
|
|
GTR_FREE(pParams->organization);
|
|
}
|
|
pParams->organization = GTR_strdup(pParams->line + 13 );
|
|
} else if (match(pParams->line, "FROM:")) {
|
|
if (pParams->from) {
|
|
GTR_FREE(pParams->from);
|
|
}
|
|
pParams->from = GTR_strdup(pParams->line + 6);
|
|
} else if (match(pParams->line, "NEWSGROUPS:")) {
|
|
if (pParams->newsgroups) {
|
|
GTR_FREE(pParams->newsgroups);
|
|
}
|
|
pParams->newsgroups = GTR_strdup(HTStrip(strchr(pParams->line, ':') + 1));
|
|
} else if (match(pParams->line, "REFERENCES:")) {
|
|
/*
|
|
* < and > characters can confuse the parser, since they're
|
|
* HTML markup. They're illegal in a message-id anyway,
|
|
* since they're used as delimiters.
|
|
*/
|
|
while ((p = strchr(pParams->line, '<')) != NULL)
|
|
*p = ' ';
|
|
while ((p = strchr(pParams->line, '>')) != NULL)
|
|
*p = ' ';
|
|
if (pParams->references) {
|
|
GTR_FREE(pParams->references);
|
|
}
|
|
pParams->references = GTR_strdup(HTStrip(strchr(pParams->line, ':') + 1));
|
|
} else if (match(pParams->line, "MESSAGE-ID:")) {
|
|
p = strchr(pParams->line, '@');
|
|
if (p) {
|
|
p = strchr(pParams->line, '>');
|
|
if (p)
|
|
*p = '\0';
|
|
p = strchr(pParams->line, '<');
|
|
if (p)
|
|
pParams->mid = GTR_strdup(p+1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Article Body
|
|
*/
|
|
} else {
|
|
char *l;
|
|
/* We're in the article body */
|
|
l = pParams->line;
|
|
|
|
|
|
/*
|
|
* Under RFC 977 section 2.4.1, a line starting with a period which
|
|
* has other stuff on it should have the period ignored.
|
|
*/
|
|
if ((*l == '.') && (*(++l) < ' ')) {
|
|
/* End of message */
|
|
pParams->isoc->input_pointer = pNext + 1;
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Display the line of text from the article
|
|
* and convert and http:// ftp:// etc. to links
|
|
*/
|
|
if ((pParams->articleindex + strlen(l) + 4) < MAX_QUOTE_SIZE) {
|
|
pParams->articleindex += sprintf(&(tw->w3doc->szArticle[pParams->articleindex]), "> %s\r\n", l);
|
|
}
|
|
DisplayArticleLine(pParams->target, l);
|
|
START( HTML_BR );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ch != EOF) {
|
|
struct Params_Isoc_Fill *pif;
|
|
|
|
/* Update display */
|
|
(*pParams->target->isa->block_done)(pParams->target);
|
|
|
|
/* Get next block of data */
|
|
pif = GTR_MALLOC(sizeof(*pif));
|
|
pif->isoc = pParams->isoc;
|
|
pif->pStatus = pParams->pStatus;
|
|
Async_DoCall(Isoc_Fill_Async, pif);
|
|
return STATE_ARTICLE_GOTDATA;
|
|
}
|
|
if (! pParams->bInHead) {
|
|
(*pParams->target->isa->end_element)(pParams->target, HTML_PRE);
|
|
|
|
/*
|
|
* Article Body Output Begins Here
|
|
*/
|
|
HorizontalBar( pParams->target );
|
|
|
|
/*
|
|
* "Previous Article | Next Article See list of articles | Post"
|
|
*/
|
|
START( HTML_B );
|
|
FontStart( pParams->target, "2" );
|
|
TableStart( pParams->target, "100%" );
|
|
RowStart( pParams->target );
|
|
CellStart( pParams->target, NULL, NULL, NULL );
|
|
if (pParams->links) {
|
|
if (pParams->links->prev) {
|
|
GTR_formatmsg( RES_STRING_PREVIOUS_ARTICLE, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, pParams->links->prev );
|
|
}
|
|
if (pParams->links->prev && pParams->links->next)
|
|
PUTS(" | ");
|
|
if (pParams->links->next) {
|
|
GTR_formatmsg( RES_STRING_NEXT_ARTICLE, szString, sizeof(szString));
|
|
write_anchor( pParams->target, szString, pParams->links->next);
|
|
}
|
|
}
|
|
END( HTML_TD );
|
|
CellStart( pParams->target, "RIGHT", NULL, NULL );
|
|
sprintf(szString, "newsfollowup:%s", pParams->mid );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_POST_RESPONSE, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
if (pParams->links) {
|
|
PUTS(" | ");
|
|
if (pParams->links->id > 0)
|
|
sprintf(szString, "news:%s/%d-%d", szGlobalHashedGroup, pParams->links->id - (CHUNK_SIZE / 2), pParams->links->id + (CHUNK_SIZE / 2));
|
|
else
|
|
sprintf(szString, "news:%s", szGlobalHashedGroup );
|
|
start_anchor( pParams->target, szString );
|
|
GTR_formatmsg(RES_STRING_SEE_LIST_ARTICLES, szString, sizeof(szString));
|
|
PUTS(szString);
|
|
END( HTML_A );
|
|
}
|
|
END( HTML_TD );
|
|
END( HTML_TR );
|
|
END( HTML_TABLE );
|
|
END( HTML_FONT );
|
|
END( HTML_B );
|
|
}
|
|
return STATE_DONE;
|
|
|
|
|
|
|
|
case STATE_ABORT:
|
|
if (pParams->references)
|
|
GTR_FREE(pParams->references);
|
|
if (pParams->newsgroups)
|
|
GTR_FREE(pParams->newsgroups);
|
|
if (pParams->subject)
|
|
GTR_FREE(pParams->subject);
|
|
if (pParams->mid)
|
|
GTR_FREE(pParams->mid);
|
|
if (pParams->date)
|
|
GTR_FREE(pParams->date);
|
|
if (pParams->from)
|
|
GTR_FREE(pParams->from);
|
|
if (pParams->organization)
|
|
GTR_FREE(pParams->organization);
|
|
*pParams->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Main protocol/loading code */
|
|
|
|
struct Data_LoadNews {
|
|
HTRequest * request;
|
|
int * pStatus;
|
|
|
|
int response; /* RFC 977 numerical response */
|
|
struct MultiAddress address;
|
|
unsigned short port;
|
|
unsigned long where; /* Where we connected to */
|
|
int net_status;
|
|
int s;
|
|
enum {AUTHUSER, AUTHPASS, ARTICLE, GROUP, LIST, LISTCACHED, POST } reqtype;
|
|
int nFirst; /* First article desired for list */
|
|
int nLast; /* Last article desired */
|
|
char * pResText; /* Response text from server */
|
|
HTInputSocket * isoc;
|
|
BOOL bWaiting;
|
|
HTStructured * target;
|
|
BOOL fArticleOK;
|
|
CHAR *szMsg;
|
|
CHAR *szSubject;
|
|
CHAR *szNewsgroups;
|
|
BOOL fNeedPush;
|
|
};
|
|
|
|
#define STATE_NEWS_GOTHOST (STATE_OTHER)
|
|
#define STATE_NEWS_CONNECTED (STATE_OTHER + 1)
|
|
#define STATE_NEWS_GOTGREETING (STATE_OTHER + 2)
|
|
#define STATE_NEWS_READY (STATE_OTHER + 3)
|
|
#define STATE_NEWS_SENTCOMMAND (STATE_OTHER + 4)
|
|
#define STATE_NEWS_READDONE (STATE_OTHER + 5)
|
|
#define STATE_AUTH_USER (STATE_OTHER + 6)
|
|
#define STATE_AUTH_PASS (STATE_OTHER + 7)
|
|
|
|
// Posting Only
|
|
#define STATE_POST_GETMESSAGE (STATE_OTHER + 8)
|
|
#define STATE_POST_CONNECT (STATE_OTHER + 9)
|
|
#define STATE_POST_SENTCOMMAND (STATE_OTHER + 10)
|
|
#define STATE_POST_SENTARTICLE (STATE_OTHER + 11)
|
|
#define STATE_POST_SENTHEADER (STATE_OTHER + 12)
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ C L E A N U P
|
|
*
|
|
*/
|
|
|
|
static void News_CleanUp(struct Mwin *tw, struct Data_LoadNews *pData)
|
|
{
|
|
XX_Assert((!pData->target), ("News_CleanUp: target not freed!"));
|
|
if (pData->bWaiting)
|
|
WAIT_Pop(tw);
|
|
if (pData->pResText)
|
|
GTR_FREE(pData->pResText);
|
|
if (pData->isoc)
|
|
HTInputSocket_free(pData->isoc);
|
|
}
|
|
|
|
|
|
/*
|
|
* N E W S _ D O _ I N I T
|
|
*
|
|
* Routine: News_DoInit()
|
|
*
|
|
*/
|
|
|
|
static int News_DoInit(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Params_LoadAsync *pParams;
|
|
struct Data_LoadNews *pData;
|
|
struct Params_MultiParseInet *ppi;
|
|
|
|
pParams = *ppInfo;
|
|
|
|
/* Copy the parameters we were passed into our own, larger structure. */
|
|
pData = GTR_MALLOC(sizeof(struct Data_LoadNews));
|
|
memset(pData, 0, sizeof(*pData));
|
|
pData->request = pParams->request;
|
|
pData->pStatus = pParams->pStatus;
|
|
GTR_FREE(pParams);
|
|
*ppInfo = pData;
|
|
|
|
#ifdef MAC
|
|
/* TODO: Restore this functionality after we switch to using
|
|
long rectangles for formatting. Right now it's very
|
|
likely that a newsgroup list would be too long to
|
|
display. */
|
|
{
|
|
char *arg;
|
|
|
|
arg = pData->request->destination->szActualURL;
|
|
if (!strchr(arg, '@') && strchr(arg, '*'))
|
|
{
|
|
ERR_ReportError(tw, errSpecify, "You must specify a newsgroup.", NULL);
|
|
return HT_LOADED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* BOGUS: We have to check for NNTP setup changes now that its
|
|
* available from the View.Settings pages
|
|
*/
|
|
|
|
/* See if we have a cached NNTP connection whose socket is still open.
|
|
We don't really check here to see if it's the correct host, since
|
|
we assume that the host changes rarely, if ever. */
|
|
if (tw->cached_conn.type == CONN_NNTP)
|
|
{
|
|
if ((!Net_FlushSocket(tw->cached_conn.socket)) && (! bNNTP_Changed))
|
|
{
|
|
/* Great! Let's go with it... */
|
|
pData->isoc = HTInputSocket_new(tw->cached_conn.socket);
|
|
return STATE_NEWS_READY;
|
|
}
|
|
else
|
|
{
|
|
/* We had a news connection, but it shut down. */
|
|
TW_DisposeConnection(&tw->cached_conn);
|
|
}
|
|
}
|
|
|
|
|
|
bNNTP_Changed = FALSE;
|
|
|
|
if (!gPrefs.szNNTP_Server[0])
|
|
{
|
|
/* We have no news server configured */
|
|
ERR_ReportError(tw, errNewsHost, NULL, NULL);
|
|
*pData->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Figure out address for news host. */
|
|
pData->port = WS_HTONS(NEWS_PORT);
|
|
ppi = GTR_MALLOC(sizeof(*ppi));
|
|
ppi->pAddress = &pData->address;
|
|
ppi->pPort = &pData->port;
|
|
ppi->str = gPrefs.szNNTP_Server;
|
|
ppi->pStatus = &pData->net_status;
|
|
ppi->request = pData->request;
|
|
Async_DoCall(Net_MultiParse_Async, ppi);
|
|
return STATE_SECURITY_CHECK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ D o G o t H o s t
|
|
*
|
|
*/
|
|
|
|
static int News_DoGotHost(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char szString[256];
|
|
|
|
pData = *ppInfo;
|
|
|
|
if ( pData->net_status < 0
|
|
|| pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT)
|
|
{
|
|
XX_DMsg(DBG_LOAD, ("Net_Parse_Async returned %d\n", pData->net_status));
|
|
*pData->pStatus = ( pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT
|
|
? HT_REDIRECTION_DCACHE_TIMEOUT
|
|
: -1);
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Try to establish a new connection */
|
|
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_CONNECTING_NNTP, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
{
|
|
/* Do connect call */
|
|
struct Params_MultiConnect *ppc;
|
|
|
|
ppc = GTR_MALLOC(sizeof(*ppc));
|
|
#ifdef FEATURE_KEEPALIVE
|
|
ppc->pszHost = NULL;
|
|
#endif
|
|
ppc->pSocket = &pData->s;
|
|
ppc->pAddress = &pData->address;
|
|
ppc->nPort = pData->port;
|
|
ppc->pWhere = &pData->where;
|
|
ppc->pStatus = &pData->net_status;
|
|
#ifdef HTTPS_ACCESS_TYPE
|
|
ppc->paramsConnectBase.dwSslFlags = 0;
|
|
#endif
|
|
Async_DoCall(Net_MultiConnect_Async, ppc);
|
|
}
|
|
return STATE_NEWS_CONNECTED;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* N e w s _ D o C o n n e c t e d
|
|
*
|
|
*/
|
|
|
|
static int News_DoConnected(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
|
|
pData = *ppInfo;
|
|
|
|
if (pData->bWaiting)
|
|
{
|
|
WAIT_Pop(tw);
|
|
pData->bWaiting = FALSE;
|
|
}
|
|
|
|
if ( pData->net_status < 0
|
|
|| pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT)
|
|
{
|
|
*pData->pStatus = ( pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT
|
|
? HT_REDIRECTION_DCACHE_TIMEOUT
|
|
: -1);
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
|
|
pData->isoc = HTInputSocket_new(pData->s);
|
|
|
|
/* Get initial response from server */
|
|
{
|
|
struct Params_News_Command *pnc;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = NULL;
|
|
pnc->pResult = &pData->response;
|
|
pnc->ppResText = NULL;
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
return STATE_NEWS_GOTGREETING;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* N e w s _ D o G o t G r e e t i n g ( )
|
|
*
|
|
*/
|
|
|
|
static int News_DoGotGreeting(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
|
|
pData = *ppInfo;
|
|
|
|
/* Let's see what the server had to say about us connecting... */
|
|
switch (pData->response)
|
|
{
|
|
case NNTP_OK_POSTING_ALLOWED:
|
|
case NNTP_OK_NO_POSTING:
|
|
/* Dispose our old cached connection and cache this one instead */
|
|
TW_DisposeConnection(&tw->cached_conn);
|
|
tw->cached_conn.type = CONN_NNTP;
|
|
tw->cached_conn.addr = pData->where;
|
|
tw->cached_conn.socket = pData->s;
|
|
/* Now that we've cached it, we don't want to close the socket
|
|
independently of the cached connection. */
|
|
pData->s = 0;
|
|
return STATE_AUTH_USER;
|
|
|
|
case NNTP_PERMISSION_DENIED:
|
|
ERR_ReportError(tw, errNewsDenied, gPrefs.szNNTP_Server, NULL);
|
|
default:
|
|
/* There was some sort of an error. Regardless of what it was,
|
|
shut down our connection and give up. */
|
|
Net_Close(pData->s);
|
|
*pData->pStatus = -1;
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("News: shouldn't get here!"));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ D O _ R E A D Y
|
|
*
|
|
*/
|
|
|
|
static int News_DoReady(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char *arg;
|
|
char *command;
|
|
char szString[256];
|
|
|
|
|
|
pData = *ppInfo;
|
|
|
|
/* What was it the user wanted in the first place? */
|
|
arg = pData->request->destination->szActualURL;
|
|
arg += 5; /* skip over "news:" part */
|
|
|
|
command = GTR_MALLOC(512); /* Will be freed by News_Command_Async. */
|
|
memset(command, 0, 512);
|
|
if (strchr(arg, '@'))
|
|
{
|
|
/* Person is looking for an article */
|
|
pData->reqtype = ARTICLE;
|
|
strcpy(command, "ARTICLE ");
|
|
if (!strchr(arg, '<'))
|
|
strcat(command, "<");
|
|
strcat(command, arg);
|
|
if (!strchr(arg, '>'))
|
|
strcat(command, ">");
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_RETRIEVING_ARTICLE, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* NEWS:* List all newsgroups
|
|
*/
|
|
if (strchr(arg, '*'))
|
|
{
|
|
/*
|
|
* If ((request is a reload request or the newsgroup
|
|
* list file doesn't exist then we actually need to
|
|
* have the server send us the list
|
|
*
|
|
* Else have the newsgroup list handler use the
|
|
* file, bail out here and pretend like the server
|
|
* gave us a positive command completion
|
|
*/
|
|
if ((pData->request->bReload) || (! ExistsNewsCacheFile()) || (strcmp( gPrefs.szNNTP_CacheServer, gPrefs.szNNTP_Server) != 0)) {
|
|
pData->reqtype = LIST;
|
|
strcpy(command, "LIST");
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_RETRIEVING_NEWSGROUP_LIST, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
} else {
|
|
pData->reqtype = LISTCACHED;
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_RETRIEVING_NEWSGROUP_LIST, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
pData->response = 200; // NOTE: Fake Out positive command completion
|
|
return STATE_NEWS_SENTCOMMAND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The user wants group contents */
|
|
char *p;
|
|
pData->reqtype = GROUP;
|
|
/* You can specify a numerical range in a group with
|
|
the form "news:group/start-end" */
|
|
p = strchr(arg, '/');
|
|
strcpy(command, "GROUP ");
|
|
if (p)
|
|
{
|
|
/* Parse out the group name, and first and last articles */
|
|
strncat(command, arg, p - arg);
|
|
/* (The command will still be null-terminated since we memset
|
|
it to 0 before we started */
|
|
pData->nFirst = atoi(p + 1);
|
|
p = strchr(p, '-');
|
|
if (p)
|
|
pData->nLast = atoi(p + 1);
|
|
}
|
|
else
|
|
{
|
|
strcat(command, arg);
|
|
}
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_RETRIEVING_ARTICLE_LIST, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
}
|
|
}
|
|
strcat(command, "\015\012");
|
|
|
|
/* We've composed the command. Send it off to the server */
|
|
{
|
|
struct Params_News_Command *pnc;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = command;
|
|
pnc->pResult = &pData->response;
|
|
if (pData->reqtype == GROUP)
|
|
pnc->ppResText = &pData->pResText;
|
|
else
|
|
pnc->ppResText = NULL;
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
return STATE_NEWS_SENTCOMMAND;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ D O A U T H U S E R
|
|
*
|
|
* Routine: News_DoAuthUser()
|
|
*
|
|
* Purpose: Do AUTHINFO conversation with NNTP Server
|
|
*
|
|
*/
|
|
|
|
static int News_DoAuthUser(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char *command;
|
|
struct Params_News_Command *pnc;
|
|
char szString[256];
|
|
|
|
|
|
pData = *ppInfo;
|
|
|
|
/*
|
|
* Skip AUTHINFO stuff if user doesn't want it
|
|
*/
|
|
if (! gPrefs.bNNTP_Use_Authorization)
|
|
return( STATE_NEWS_READY );
|
|
|
|
/*
|
|
* Build up AUTHINFO user command
|
|
*/
|
|
command = GTR_MALLOC( 512 ); // freed by News_command_async()
|
|
strcpy( command, "AUTHINFO user ");
|
|
strcat( command, gPrefs.szNNTP_UserId );
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg( RES_STRING_AUTHINFO_USER, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
pData->reqtype = AUTHUSER;
|
|
strcat(command, "\015\012");
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = command;
|
|
pnc->pResult = &pData->response;
|
|
pnc->ppResText = NULL;
|
|
|
|
/*
|
|
* Send it
|
|
*/
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
|
|
return STATE_NEWS_SENTCOMMAND;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ D O A U T H P A S S
|
|
*
|
|
* Routine: News_DoAuthPass()
|
|
*
|
|
* Purpose: Do AUTHINFO password conversation with NNTP Server
|
|
*
|
|
*/
|
|
|
|
static int News_DoAuthPass(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char *command;
|
|
struct Params_News_Command *pnc;
|
|
char szString[256];
|
|
|
|
|
|
pData = *ppInfo;
|
|
|
|
/*
|
|
* Build up AUTHINFO pass command
|
|
*/
|
|
command = GTR_MALLOC( 512 ); // freed by News_command_async()
|
|
strcpy( command, "AUTHINFO pass ");
|
|
strcat( command, gPrefs.szNNTP_Pass );
|
|
WAIT_Push(tw, waitPartialInteract, GTR_formatmsg(RES_STRING_AUTHINFO_PASS, szString, sizeof(szString)));
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
pData->reqtype = AUTHPASS;
|
|
strcat(command, "\015\012");
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = command;
|
|
pnc->pResult = &pData->response;
|
|
pnc->ppResText = NULL;
|
|
|
|
/*
|
|
* Send it
|
|
*/
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
|
|
return STATE_NEWS_SENTCOMMAND;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* N E W S _ D O S E N T C O M M A N D
|
|
*
|
|
*/
|
|
|
|
static int News_DoSentCommand(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
|
|
pData = *ppInfo;
|
|
|
|
|
|
if (pData->bWaiting)
|
|
{
|
|
WAIT_Pop(tw);
|
|
pData->bWaiting = FALSE;
|
|
}
|
|
|
|
/*
|
|
* BUGBUG: No Error Handling for authorization stuff
|
|
*/
|
|
if ((pData->reqtype == AUTHUSER) && (pData->response == NNTP_SEND_PASSWORD))
|
|
return STATE_AUTH_PASS;
|
|
|
|
if ((pData->reqtype == AUTHPASS) && (pData->response == NNTP_AUTHINFO_SUCCESS))
|
|
return STATE_NEWS_READY;
|
|
|
|
|
|
|
|
if ((pData->response / 100) != 2)
|
|
{
|
|
/* An error occurred. -- Maybe */
|
|
if (pData->reqtype == GROUP && pData->response == 411)
|
|
{
|
|
ERR_ReportError(tw, errNewsGroupNotCarried, NULL, NULL);
|
|
}
|
|
if (pData->response < 0 || pData->response == 400)
|
|
{
|
|
/* Either an error occured or service was disconnected.
|
|
Either way, close our connection. */
|
|
TW_DisposeConnection(&tw->cached_conn);
|
|
}
|
|
*pData->pStatus = -1;
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Successful response. Read data from server. */
|
|
switch (pData->reqtype)
|
|
{
|
|
case ARTICLE:
|
|
{
|
|
struct Params_ReadArticle *pra;
|
|
|
|
pData->target = HTML_new(tw, pData->request, NULL, WWW_HTML,
|
|
pData->request->output_format, pData->request->output_stream);
|
|
pra = GTR_MALLOC(sizeof(*pra));
|
|
pra->isoc = pData->isoc;
|
|
pra->pStatus = &pData->net_status;
|
|
pra->target = pData->target;
|
|
Async_DoCall(News_ReadArticle_Async, pra);
|
|
return STATE_NEWS_READDONE;
|
|
}
|
|
case GROUP:
|
|
{
|
|
struct Params_ReadGroup *prg;
|
|
char szGroup[256];
|
|
int n, f, l;
|
|
|
|
/* Response to group command gives count and first/last articles numbers.
|
|
See section 3.2.2 of RFC 977. */
|
|
sscanf(pData->pResText, "%*d %d %d %d %s ", &n, &f, &l, szGroup);
|
|
if (pData->nFirst && pData->nLast)
|
|
{
|
|
if (pData->nFirst < f)
|
|
pData->nFirst = f;
|
|
if (pData->nLast > l)
|
|
pData->nLast = l;
|
|
}
|
|
else
|
|
{
|
|
pData->nLast = l;
|
|
if (l - f + 1 > CHUNK_SIZE)
|
|
pData->nFirst = pData->nLast - CHUNK_SIZE + 1;
|
|
else
|
|
pData->nFirst = f;
|
|
}
|
|
GTR_FREE(pData->pResText);
|
|
pData->pResText = NULL;
|
|
|
|
// if (l < f || l == 0)
|
|
// {
|
|
// ERR_ReportError(tw, errNewsNoArticles, szGroup, NULL);
|
|
// }
|
|
// else
|
|
if (pData->nLast < pData->nFirst) // || pData->nLast == 0)
|
|
{
|
|
ERR_ReportError(tw, errNewsBadRange, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
pData->target = HTML_new(tw, pData->request, NULL, WWW_HTML,
|
|
pData->request->output_format, pData->request->output_stream);
|
|
|
|
prg = GTR_MALLOC(sizeof(*prg));
|
|
prg->isoc = pData->isoc;
|
|
prg->pStatus = &pData->net_status;
|
|
prg->target = pData->target;
|
|
prg->nFirst = pData->nFirst;
|
|
prg->nLast = pData->nLast;
|
|
prg->nFirstInGroup = f;
|
|
prg->nLastInGroup = l;
|
|
prg->nArticleCount = n;
|
|
prg->bNeedHashFree = FALSE;
|
|
strcpy(prg->szGroup, szGroup);
|
|
Async_DoCall(News_ReadGroup_Async, prg);
|
|
}
|
|
return STATE_NEWS_READDONE;
|
|
}
|
|
|
|
case LIST:
|
|
case LISTCACHED:
|
|
{
|
|
struct Params_ReadList *prl;
|
|
|
|
pData->target = HTML_new(tw, pData->request, NULL, WWW_HTML,
|
|
pData->request->output_format, pData->request->output_stream);
|
|
|
|
prl = GTR_MALLOC(sizeof(*prl));
|
|
|
|
if (pData->reqtype == LISTCACHED)
|
|
prl->type = CACHED;
|
|
else
|
|
prl->type = READING;
|
|
prl->isoc = pData->isoc;
|
|
prl->pStatus = &pData->net_status;
|
|
prl->target = pData->target;
|
|
Async_DoCall(News_ReadList_Async, prl);
|
|
return STATE_NEWS_READDONE;
|
|
}
|
|
|
|
default:
|
|
XX_Assert((0), ("Illegal news type: %d", pData->reqtype));
|
|
return STATE_DONE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* N E W S R E A D D O N E
|
|
*
|
|
*/
|
|
|
|
static int News_DoReadDone(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
|
|
pData = *ppInfo;
|
|
|
|
if (pData->target)
|
|
{
|
|
(*pData->target->isa->free)(pData->target);
|
|
pData->target = NULL;
|
|
}
|
|
|
|
if (pData->net_status < 0)
|
|
{
|
|
/* A network error occured. Discard our connection. */
|
|
TW_DisposeConnection(&tw->cached_conn);
|
|
*pData->pStatus = -1;
|
|
}
|
|
else
|
|
{
|
|
*pData->pStatus = HT_LOADED;
|
|
}
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
|
|
static int News_DoAbort(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
|
|
pData = *ppInfo;
|
|
|
|
if (pData->target)
|
|
{
|
|
(*pData->target->isa->free)(pData->target);
|
|
pData->target = NULL;
|
|
}
|
|
News_CleanUp(tw, pData);
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
|
|
static int HTLoadNews_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
return News_DoInit(tw, ppInfo);
|
|
case STATE_SECURITY_CHECK:
|
|
return SecurityCheck(tw, ((struct Data_LoadNews*) (*ppInfo))->request, FALSE, FALSE, STATE_NEWS_GOTHOST);
|
|
case STATE_NEWS_GOTHOST:
|
|
return News_DoGotHost(tw, ppInfo);
|
|
case STATE_NEWS_CONNECTED:
|
|
return News_DoConnected(tw, ppInfo);
|
|
case STATE_NEWS_GOTGREETING:
|
|
return News_DoGotGreeting(tw, ppInfo);
|
|
case STATE_AUTH_USER:
|
|
return News_DoAuthUser(tw, ppInfo);
|
|
case STATE_AUTH_PASS:
|
|
return News_DoAuthPass(tw, ppInfo);
|
|
case STATE_NEWS_READY:
|
|
return News_DoReady(tw, ppInfo);
|
|
case STATE_NEWS_SENTCOMMAND:
|
|
return News_DoSentCommand(tw, ppInfo);
|
|
case STATE_NEWS_READDONE:
|
|
return News_DoReadDone(tw, ppInfo);
|
|
case STATE_ABORT:
|
|
return News_DoAbort(tw, ppInfo);
|
|
}
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
GLOBALDEF PUBLIC HTProtocol HTNews = {"news", NULL, HTLoadNews_Async};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* P O S T I N G S U P P O R T B E L O W
|
|
*
|
|
* We develop our own protocol "newspost" for
|
|
* posting.
|
|
*/
|
|
|
|
const char szNewsPost[] = "newspost:";
|
|
const char szNewsFollowup[] = "newsfollowup:";
|
|
|
|
|
|
/*
|
|
* NewsPost_DoInit()
|
|
*
|
|
* Pop up dialog to get news message from user.
|
|
* we do this before connecting since we may lose
|
|
* the connection during the time it takes the user
|
|
* to enter the article
|
|
*/
|
|
|
|
|
|
static int NewsPost_DoInit(struct Mwin *tw, void **ppInfo)
|
|
{
|
|
struct Params_LoadAsync *pParams;
|
|
struct Data_LoadNews *pData;
|
|
char *url;
|
|
char *groupname = NULL;
|
|
char *subject = NULL;
|
|
char *body = NULL;
|
|
|
|
|
|
pParams = *ppInfo;
|
|
|
|
/*
|
|
* Build up our state structure
|
|
*
|
|
* jeffwe: Caller of engine frees up *ppInfo??
|
|
*/
|
|
if (! (pData = GTR_MALLOC(sizeof(struct Data_LoadNews)))) {
|
|
// Memory Allocation Failure!! Lets just bail
|
|
return( STATE_DONE );
|
|
}
|
|
memset(pData, 0, sizeof(*pData));
|
|
pData->request = pParams->request;
|
|
pData->pStatus = pParams->pStatus;
|
|
GTR_FREE(pParams);
|
|
*ppInfo = pData;
|
|
|
|
|
|
/*
|
|
* Were not loading any pages, so lets just
|
|
* pretend it loaded OK to keep the browser
|
|
* from poping up an error
|
|
*/
|
|
*pData->pStatus = HT_LOADED;
|
|
|
|
|
|
/*
|
|
* Get Newsgroup Name and Subject
|
|
* If were posting a new article, then the newsgroup should
|
|
* be provided in the URL, no subject will be available
|
|
*
|
|
* If were doing a followup then we look at the
|
|
* newsgroup line and subject line that was saved
|
|
* in the w3doc
|
|
*
|
|
* URL was forced to lowercase by document loader
|
|
*/
|
|
url = pData->request->destination->szActualURL;
|
|
if (strncmp( url, szNewsPost, sizeof(szNewsPost) - 1) == 0) {
|
|
groupname = url + (sizeof(szNewsPost)-1);
|
|
}
|
|
if (strncmp( url, szNewsFollowup, sizeof(szNewsFollowup) - 1) == 0) {
|
|
groupname = tw->w3doc->szArtNewsgroups;
|
|
subject = tw->w3doc->szArtSubject;
|
|
body = tw->w3doc->szArticle;
|
|
}
|
|
|
|
|
|
/*
|
|
* Put up Dialog
|
|
*/
|
|
pData->fNeedPush = WAIT_Pop(tw);
|
|
pData->fArticleOK = FALSE;
|
|
if (PostDialog( tw->win, tw->w3doc->szArticle, groupname, subject, &(pData->fArticleOK), &(pData->szMsg), &(pData->szSubject), &(pData->szNewsgroups)) == TRUE) {
|
|
return STATE_POST_GETMESSAGE;
|
|
} else {
|
|
return STATE_ABORT;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* NewsPost_DoConnect()
|
|
*
|
|
* Connect to the news server (if we aren't connected already)
|
|
*/
|
|
|
|
static int NewsPost_DoConnect(struct Mwin *tw, void **ppinfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
struct Params_MultiParseInet *ppi;
|
|
|
|
pData = *ppinfo;
|
|
|
|
|
|
/*
|
|
* Reuse current connection if possible
|
|
*
|
|
* NNTP_Changed == TRUE means that user changed
|
|
* one of the News settings and we shouldn't stick
|
|
* with the cached connection - we should also no
|
|
* longer use the cached list of newsgroups
|
|
*/
|
|
|
|
if (tw->cached_conn.type == CONN_NNTP)
|
|
{
|
|
if ((!Net_FlushSocket(tw->cached_conn.socket)) && (! bNNTP_Changed))
|
|
{
|
|
/* Great! Let's go with it... */
|
|
pData->isoc = HTInputSocket_new(tw->cached_conn.socket);
|
|
return STATE_NEWS_READY;
|
|
}
|
|
else
|
|
{
|
|
/* We had a news connection, but it shut down. */
|
|
TW_DisposeConnection(&tw->cached_conn);
|
|
}
|
|
}
|
|
|
|
|
|
bNNTP_Changed = FALSE;
|
|
|
|
if (!gPrefs.szNNTP_Server[0])
|
|
{
|
|
/* We have no news server configured */
|
|
ERR_ReportError(tw, errNewsHost, NULL, NULL);
|
|
*pData->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Figure out address for news host. */
|
|
pData->port = WS_HTONS(NEWS_PORT);
|
|
ppi = GTR_MALLOC(sizeof(*ppi));
|
|
ppi->pAddress = &pData->address;
|
|
ppi->pPort = &pData->port;
|
|
ppi->str = gPrefs.szNNTP_Server;
|
|
ppi->pStatus = &pData->net_status;
|
|
ppi->request = pData->request;
|
|
Async_DoCall(Net_MultiParse_Async, ppi);
|
|
return STATE_SECURITY_CHECK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* D O P O S T C O M M A N D
|
|
*
|
|
* Routien: News_DoPostCommand()
|
|
*
|
|
* Purpose: Send POST command to server
|
|
*
|
|
*/
|
|
|
|
static int News_DoPostCommand(struct Mwin *tw, void **ppinfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char *command;
|
|
struct Params_News_Command *pnc;
|
|
CHAR szPostCmd[] = "POST\015\012";
|
|
CHAR szString[256];
|
|
|
|
|
|
pData = *ppinfo;
|
|
|
|
/*
|
|
* Assemble Command String
|
|
* will be free'd by News_Command_Async()
|
|
*/
|
|
command = GTR_MALLOC(sizeof(szPostCmd));
|
|
pData->reqtype = POST;
|
|
strcpy(command, szPostCmd );
|
|
|
|
/*
|
|
* Set status bar to show were waiting for response
|
|
* from server
|
|
*/
|
|
GTR_formatmsg( RES_STRING_SENDING_POST_CMD, szString, sizeof(szString));
|
|
WAIT_Push(tw, waitPartialInteract, szString );
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
|
|
/*
|
|
* Put together command-response and send it
|
|
*/
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = command;
|
|
pnc->pResult = &pData->response;
|
|
pnc->ppResText = NULL;
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
|
|
/*
|
|
* Next Step:
|
|
* Get Post Status
|
|
*/
|
|
return STATE_POST_SENTCOMMAND;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* D O S E N D A R T I C L E
|
|
*
|
|
* Routine: News_DoSendArticle
|
|
*
|
|
* Purpose: Assemble and send article to NNTP Server
|
|
*
|
|
*/
|
|
|
|
static int News_DoSendMessage(struct Mwin *tw, void **ppinfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
struct Params_News_Command *pnc;
|
|
char *mess;
|
|
char *mi; // index into message
|
|
char szString[256];
|
|
|
|
|
|
pData = *ppinfo;
|
|
|
|
if (pData->bWaiting)
|
|
{
|
|
WAIT_Pop(tw);
|
|
pData->bWaiting = FALSE;
|
|
}
|
|
|
|
|
|
switch (pData->response) {
|
|
case NNTP_CONTINUE:
|
|
mess = GTR_MALLOC( FIXED_HEADER_SPACE + strlen(gPrefs.szNNTP_MailName) + strlen(gPrefs.szNNTP_MailAddr) + strlen( pData->szNewsgroups) + strlen( pData->szSubject) + strlen( pData->szMsg ));
|
|
mi = mess;
|
|
mi += sprintf(mi, "From: %s <%s>\r\n", gPrefs.szNNTP_MailName, gPrefs.szNNTP_MailAddr);
|
|
mi += sprintf(mi, "Newsgroups: %s\r\n", pData->szNewsgroups );
|
|
mi += sprintf(mi, "Subject: %s\r\n\r\n", pData->szSubject );
|
|
// mi += sprintf(mi, "X-Mailer: Microsoft Internet Explorer V2.0");
|
|
mi += sprintf(mi, "%s\r\n.\r\n", pData->szMsg );
|
|
|
|
GTR_formatmsg( RES_STRING_POSTING_ARTICLE, szString, sizeof(szString));
|
|
WAIT_Push(tw, waitPartialInteract, szString );
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pData->bWaiting = TRUE;
|
|
|
|
/*
|
|
* Free the memory used to hold the article as was
|
|
* malloced in the dialog box
|
|
*/
|
|
GTR_FREE( pData->szMsg ); pData->szMsg = NULL;
|
|
GTR_FREE( pData->szNewsgroups); pData->szNewsgroups = NULL;
|
|
GTR_FREE( pData->szSubject); pData->szSubject = NULL;
|
|
|
|
pnc = GTR_MALLOC(sizeof(*pnc));
|
|
pnc->isoc = pData->isoc;
|
|
pnc->cmd = mess;
|
|
pnc->pResult = &pData->response;
|
|
if (pData->reqtype == GROUP)
|
|
pnc->ppResText = &pData->pResText;
|
|
else
|
|
pnc->ppResText = NULL;
|
|
Async_DoCall(News_Command_Async, pnc);
|
|
return( STATE_POST_SENTARTICLE );
|
|
|
|
case NNTP_POST_NOT_PERMITTED:
|
|
ERR_ReportError(tw, errNNTP_Post_Not_Permitted, NULL, NULL );
|
|
return( STATE_ABORT );
|
|
|
|
case NNTP_POST_FAILURE:
|
|
ERR_ReportError(tw, errNNTP_Post_Failed, NULL, NULL);
|
|
return( STATE_ABORT );
|
|
|
|
default:
|
|
ERR_ReportError(tw, errNNTP_Unexpected, NULL, NULL );
|
|
return( STATE_ABORT );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* D O _ P O S T _ R E S U L T
|
|
*
|
|
* Routine: News_DoPostResult()
|
|
*
|
|
* Purpose: Get response from server for aricle post command
|
|
*
|
|
*/
|
|
|
|
|
|
static int News_DoPostResult(struct Mwin *tw, void **ppinfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char szString[256];
|
|
char szTitle[100];
|
|
|
|
|
|
pData = *ppinfo;
|
|
|
|
if (pData->bWaiting)
|
|
{
|
|
WAIT_Pop(tw);
|
|
pData->bWaiting = FALSE;
|
|
}
|
|
|
|
switch (pData->response) {
|
|
case NNTP_POST_SUCCESS:
|
|
GTR_formatmsg( RES_STRING_NNTP_POST_SUCCESS, szString, sizeof(szString));
|
|
GTR_formatmsg( RES_STRING_NNTP_POSTING, szTitle, sizeof(szTitle));
|
|
MessageBox(tw ? tw->win : wg.hWndHidden, szString, szTitle, MB_OK);
|
|
return( STATE_DONE );
|
|
|
|
case NNTP_POST_NOT_PERMITTED:
|
|
ERR_ReportError(tw, errNNTP_Post_Not_Permitted, NULL, NULL );
|
|
return( STATE_ABORT );
|
|
|
|
case NNTP_POST_FAILURE:
|
|
ERR_ReportError(tw, errNNTP_Post_Failed, NULL, NULL );
|
|
return( STATE_ABORT );
|
|
|
|
default:
|
|
ERR_ReportError(tw, errNNTP_Unexpected, NULL, NULL );
|
|
XX_Assert((0),("POST: Unknown Return from server"));
|
|
return( STATE_ABORT );
|
|
}
|
|
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* E S C A P E L E A D I N G D O T S
|
|
*
|
|
* Routine: EscapeLeadingDots()
|
|
*
|
|
* Purpose: RFC977 states that any line beginning with
|
|
* a '.' must have an additional '.' inserted.
|
|
*
|
|
* Note that a line break in RFC 977 is CR-LF
|
|
*/
|
|
|
|
static
|
|
char *
|
|
EscapeLeadDots( char *message )
|
|
{
|
|
char *body;
|
|
char *c, *out;
|
|
enum {NORMAL, SAWCR, SAWCRLF} state;
|
|
|
|
|
|
/*
|
|
* The article body text must be massaged a bit
|
|
* according to RFC 977. We need to turn any
|
|
* lines that have a leading '.' on them to have
|
|
* a '..' at the lead
|
|
*
|
|
* What we will do is simply
|
|
* allocate a buffer twice the size of what we
|
|
* already have and copy and convert.
|
|
*/
|
|
body = GTR_MALLOC( strlen( message ) * 2 + 1);
|
|
if (! body)
|
|
return( NULL );
|
|
|
|
c = message;
|
|
out = body;
|
|
state = SAWCRLF;
|
|
while (*c) {
|
|
*out++ = *c;
|
|
switch (*c) {
|
|
case '.':
|
|
if (state == SAWCRLF)
|
|
*out++ = *c;
|
|
state = NORMAL;
|
|
break;
|
|
case '\r':
|
|
if (state == NORMAL)
|
|
state = SAWCR;
|
|
break;
|
|
case '\n':
|
|
if (state == SAWCR)
|
|
state = SAWCRLF;
|
|
else
|
|
state = NORMAL;
|
|
break;
|
|
default:
|
|
state = NORMAL;
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
*out = *c; // Terminate Buffer
|
|
|
|
return(body);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* NEWS POST ASYNCHRONOUS ENGINE
|
|
*/
|
|
|
|
static int HTPostNews_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Data_LoadNews *pData;
|
|
char *newbody;
|
|
|
|
switch (nState)
|
|
{
|
|
/*
|
|
* Pop Up Dialog for user to enter posting in
|
|
*/
|
|
case STATE_INIT:
|
|
return NewsPost_DoInit(tw, ppInfo);
|
|
|
|
|
|
|
|
/*
|
|
* If user bailed then were done, else
|
|
* connect up to the news server
|
|
*/
|
|
case STATE_POST_GETMESSAGE:
|
|
pData = *ppInfo;
|
|
|
|
/*
|
|
* We popped the wait so the flag wasn't
|
|
* spinning while the user was entering info
|
|
* into the article dialog. Now we need to
|
|
* restore this so that stack is in original
|
|
* state
|
|
*/
|
|
|
|
if (pData->fNeedPush) {
|
|
WAIT_Push( tw, waitNoInteract, "");
|
|
pData->fNeedPush = 0;
|
|
}
|
|
if ((! pData->fArticleOK) || (! pData->szMsg)) {
|
|
*pData->pStatus = HT_LOADED;
|
|
return( STATE_ABORT );
|
|
}
|
|
|
|
/*
|
|
* As per RFC977 we have to convert any leading
|
|
* periods to double periods
|
|
*/
|
|
newbody = EscapeLeadDots( pData->szMsg );
|
|
if (newbody == NULL)
|
|
return( STATE_ABORT );
|
|
|
|
/*
|
|
* If it worked, free up the original buffer and
|
|
* save the new one
|
|
*/
|
|
GTR_FREE( pData->szMsg );
|
|
pData->szMsg = newbody;
|
|
|
|
return NewsPost_DoConnect(tw, ppInfo );
|
|
|
|
|
|
/*
|
|
* Perform Security Check
|
|
*/
|
|
case STATE_SECURITY_CHECK:
|
|
return SecurityCheck(tw, ((struct Data_LoadNews*) (*ppInfo))->request, FALSE, FALSE, STATE_NEWS_GOTHOST);
|
|
|
|
case STATE_NEWS_GOTHOST:
|
|
return News_DoGotHost(tw, ppInfo);
|
|
|
|
case STATE_NEWS_CONNECTED:
|
|
return News_DoConnected(tw, ppInfo);
|
|
|
|
case STATE_NEWS_GOTGREETING:
|
|
return News_DoGotGreeting(tw, ppInfo);
|
|
|
|
case STATE_AUTH_USER:
|
|
return News_DoAuthUser(tw, ppInfo);
|
|
|
|
case STATE_AUTH_PASS:
|
|
return News_DoAuthPass(tw, ppInfo);
|
|
|
|
case STATE_NEWS_SENTCOMMAND:
|
|
return News_DoSentCommand(tw, ppInfo);
|
|
|
|
case STATE_NEWS_READY:
|
|
return News_DoPostCommand(tw, ppInfo);
|
|
|
|
case STATE_POST_SENTCOMMAND:
|
|
return News_DoSendMessage(tw, ppInfo);
|
|
|
|
case STATE_POST_SENTARTICLE:
|
|
return News_DoPostResult(tw, ppInfo);
|
|
|
|
case STATE_ABORT:
|
|
pData = *ppInfo;
|
|
|
|
if (pData->fNeedPush)
|
|
WAIT_Push(tw, waitNoInteract, "");
|
|
if (pData->bWaiting)
|
|
WAIT_Pop(tw);
|
|
if (pData->isoc) {
|
|
HTInputSocket_free(pData->isoc);
|
|
pData->isoc = NULL;
|
|
}
|
|
if (pData->szNewsgroups) {
|
|
GTR_FREE( pData->szNewsgroups);
|
|
pData->szNewsgroups = NULL;
|
|
}
|
|
if (pData->szSubject) {
|
|
GTR_FREE( pData->szSubject);
|
|
pData->szSubject = NULL;
|
|
}
|
|
if (pData->szMsg) {
|
|
GTR_FREE( pData->szMsg );
|
|
pData->szMsg = NULL;
|
|
}
|
|
return( STATE_DONE );
|
|
|
|
default:
|
|
XX_Assert((0),("POST: Unhandled State: %d"));
|
|
return STATE_DONE;
|
|
}
|
|
return STATE_DONE;
|
|
}
|
|
|
|
|
|
GLOBALDEF PUBLIC HTProtocol HTNewsPost = {"newspost", NULL, HTPostNews_Async};
|
|
GLOBALDEF PUBLIC HTProtocol HTNewsFollowup = {"newsfollowup", NULL, HTPostNews_Async};
|
|
|
|
#endif FEATURE_NEWSREADER
|