This is nntp.c in view mode; [Download] [Up]
/* Copyright (c) 1995 John E. Davis (davis@space.mit.edu)
* All rights reserved.
*/
#include "config.h"
#include "features.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <slang.h>
#include "jdmacros.h"
#include "slrn.h"
#include "server.h"
#include "misc.h"
#if SLRN_USE_SLTCP
static int client_put_server (char *);
static int client_get_server (char *, unsigned int);
static void client_close_server (void);
static int client_server_init (char *, int, char *, unsigned int);
static SLTCP_Type NNTP_SLtcp;
#endif
static Slrn_Server_Obj_Type NNTP_Server_Obj;
static Slrn_Post_Obj_Type NNTP_Post_Obj;
char *Slrn_NNTP_Server_Name;
int Slrn_Query_Reconnect = 1;
static int NNTP_Port = -1;
static int nntp_select_group (char *, int *, int *);
static char *nntp_read_line (char *, unsigned int);
static void nntp_close_server (void);
static int nntp_initialize_server (void);
static int nntp_select_article (int, char *);
static char *nntp_head_from_msgid (char *, char *, unsigned int);
static char *nntp_read_xover (char *, unsigned int);
static int nntp_open_xover (int, int);
static void nntp_close_xover (void);
static int nntp_xpat_cmd (char *, int, int, char *);
static int nntp_printf (char *, ...);
static int nntp_puts (char *);
static int nntp_xhdr_command (char *, int, char *, unsigned int);
static char *nntp_get_extra_xover_header (char *);
static void nntp_close_suspend_xover (void);
static void nntp_open_suspend_xover (void);
static int nntp_xgtitle_cmd (char *);
static int nntp_has_cmd (char *);
static int nntp_list_newsgroups (void);
static int nntp_list_active (void);
static int NNTP_Has_XPat = -1; /* -1 means probe needs performed */
static int NNTP_Has_Xhdr = -1;
static int NNTP_Has_Xgtitle = -1;
static char *nntp_read_line (char *, unsigned int);
static int NNTP_Reconnect_Ok;
static char NNTP_Last_Reconnect_Command[256];
static int slrn_get_server (char *, unsigned int, int);
/* This function does not attempt a reconnection. A reconnection can be
* achieved only in a definite context.
*/
static int slrn_put_server_cmd (char *line, char *buf, unsigned int len)
{
NNTP_Reconnect_Ok = 0;
if (-1 == client_put_server (line))
{
slrn_exit_error ("Server closed connection on %s command.", line);
}
*buf = 0;
slrn_get_server (buf, len, 1);
return atoi (buf);
}
static int slrn_get_server (char *line, unsigned int len, int fatal)
{
int ret;
*line = 0;
while (1)
{
errno = 0;
ret = client_get_server (line, (int) len);
if (ret == -1)
{
#ifdef EAGAIN
if (errno == EAGAIN)
{
sleep (1);
continue;
}
#endif
#ifdef EINTR
if (errno == EINTR)
{
slrn_error ("read aborted.");
return -1;
}
#endif
if (fatal) slrn_exit_error ("Read failed from server. (%d)", errno);
return -1;
}
else return ret;
}
}
static int nntp_attempt_reconnect (void)
{
char buf[256];
int max_tries_without_query = 1;
while (1)
{
if ((Slrn_Query_Reconnect || (max_tries_without_query == 0))
&& (slrn_get_yesno (1, "NNTP connection dropped -- reconnect") <= 0))
return -1;
if (max_tries_without_query != 0) max_tries_without_query--;
if (nntp_initialize_server () != 0)
{
continue;
}
if ((NULL != Slrn_Current_Group_Name) && ('\0' != *Slrn_Current_Group_Name))
{
sprintf (buf, "GROUP %s", Slrn_Current_Group_Name);
if ((-1 == client_put_server (buf))
|| (-1 == slrn_get_server (buf, sizeof (buf) - 1, 0)))
continue;
}
break;
}
return 0;
}
static int put_server_with_reconnect (char *cmd, char *buf, unsigned int len)
{
int status;
NNTP_Reconnect_Ok = 1;
strcpy (NNTP_Last_Reconnect_Command, cmd);
while (1)
{
*buf = 0;
if ((-1 == client_put_server (NNTP_Last_Reconnect_Command))
|| (-1 == slrn_get_server (buf, len - 1, 0)))
status = -1;
else
status = atoi (buf);
if ((status == ERR_FAULT)
&& !slrn_case_strncmp ((unsigned char *) "503 Timeout",
(unsigned char *) buf, 11))
status = -1;
if ((status != -1)
&& (status != ERR_GOODBYE))
return status;
if (-1 == nntp_attempt_reconnect ())
{
slrn_exit_error ("Server connection closed. (%s)", NNTP_Last_Reconnect_Command);
}
}
}
static void nntp_discard_output (char *line, unsigned int len)
{
while (NULL != nntp_read_line (line, len))
{
;
}
}
int nntp_select_group (char *name, int *min, int *max)
{
char group[256];
int estim;
int code;
sprintf (group, "GROUP %s", name);
code = put_server_with_reconnect (group, group, sizeof (group));
/* responses 211 and 411 as well as 502 */
if (code != OK_GROUP)
{
if (code == ERR_ACCESS)
{
*min = *max = 0;
return 0;
}
return -1;
}
if (3 != sscanf(group + 4, "%d %d %d", &estim, min, max)) return -1;
return 0;
}
static int NNTP_Server_Inited = 0;
void nntp_close_server (void)
{
client_close_server ();
NNTP_Server_Inited = 0;
}
static int nntp_authorization (void)
{
char line[NNTP_STRLEN];
sprintf (line, "authinfo user %s", Slrn_User_Info.nnrpname);
if (NEED_AUTHDATA == slrn_put_server_cmd (line, line, sizeof (line)))
{
sprintf (line, "authinfo pass %s", Slrn_User_Info.nnrppass);
switch (slrn_put_server_cmd (line, line, sizeof (line)))
{
case ERR_ACCESS:
return -1;
case OK_AUTH:
Slrn_Post_Obj->po_can_post = 1;
return 0;
}
}
return 0;
}
static int probe_server (char *cmd)
{
char line[256];
if (ERR_COMMAND == put_server_with_reconnect (cmd, line, sizeof (line)))
return 0;
return 1;
}
#define PROBE_XCMD(var, cmd) (((var) != -1) ? (var) : ((var) = probe_server(cmd)))
int nntp_has_cmd (char *cmd)
{
if (!strcmp (cmd, "XHDR"))
return PROBE_XCMD(NNTP_Has_Xhdr, cmd);
if (!strcmp (cmd, "XPAT"))
return PROBE_XCMD(NNTP_Has_XPat, cmd);
if (!strcmp (cmd, "XGTITLE"))
return PROBE_XCMD(NNTP_Has_Xgtitle, cmd);
return probe_server (cmd);
}
static int nntp_initialize_server (void)
{
char line[256];
int code;
char *str;
char msg1[] = "Connecting to server at %s ...";
char msg2[] = "Unable to initialize server: %s";
char msg4[] = "Authenticating %s...";
int tt_inited = (Slrn_TT_Initialized & 1);
char *host = Slrn_NNTP_Server_Name;
if (NNTP_Server_Inited) nntp_close_server ();
if (tt_inited == 0)
slrn_tty_message (0, msg1, host);
else
{
slrn_message ("Connecting to server at %s ...", host);
slrn_smg_refresh ();
}
if ((code = client_server_init (host, NNTP_Port, line, sizeof(line))) == -1)
{
if (tt_inited == 0)
slrn_tty_message (2, "failed!");
else
{
slrn_message ("Connecting to server at %s ... failed!", host);
slrn_smg_refresh ();
}
return -1;
}
NNTP_Server_Inited = 1;
switch (code)
{
case OK_CANPOST:
Slrn_Post_Obj->po_can_post = 1;
str = "Posting ok.";
break;
case OK_NOPOST:
Slrn_Post_Obj->po_can_post = 0;
str = "Posting NOT ok.";
break;
default:
if (tt_inited)
{
slrn_message (msg2, line);
slrn_smg_refresh ();
}
else slrn_tty_message (1 | 2, msg2, line);
return -1;
}
if (tt_inited)
{
slrn_message (str);
slrn_smg_refresh ();
}
else slrn_tty_message (1 | 2, str);
/* Apparantly mode reader should occur before the authorization code. The
* reason is that after receiving mode reader, the server will fork a
* process that actually performs the role of server. It is what needs
* authorization.
*/
if (ERR_ACCESS == slrn_put_server_cmd ("mode reader", line, sizeof (line)))
{
char msg3[] = "NNTP: 'mode reader' failed: %s";
if (tt_inited)
{
slrn_message (msg3, line);
slrn_smg_refresh ();
}
else slrn_tty_message (2, msg3);
return (-1);
}
if ((Slrn_User_Info.nnrpname != NULL)
&& (*Slrn_User_Info.nnrpname != 0))
{
if (tt_inited == 0)
slrn_tty_message (0, msg4, Slrn_User_Info.nnrpname);
else
{
slrn_message (msg4, host);
slrn_smg_refresh ();
}
/* Authenticate ourselves. If we fail the server will disconnect us
so we need to reconnect if we were disconnected */
if (nntp_authorization() < 0)
{
/* The server probably hung up on us. Check and see.
* If it did, then we will login again as a default user.
* Send the date command to see if the server is still alive.
*/
if ((-1 == client_put_server("date"))
|| (-1 == slrn_get_server(line,sizeof(line),0)))
{
/* Yup it hung up on us. */
if (tt_inited)
{
slrn_message ("Failed!! Using Default.");
slrn_smg_refresh ();
}
else
{
slrn_tty_message (0, "Failed!! Using Default.\r\n");
}
line[0] = '\0';
if (-1 == client_server_init (host, NNTP_Port, line, sizeof(line) ))
{
return -1;
}
}
}
else
{
if (tt_inited)
{
slrn_message ("Succeeded");
slrn_smg_refresh ();
}
else slrn_tty_message (0, "Succeeded");
}
}
if (ERR_COMMAND == slrn_put_server_cmd ("XOVER", line, sizeof (line)))
{
char msg5[] = "Server %s does not implement the XOVER command.";
NNTP_Server_Obj.sv_has_xover = 0;
if (tt_inited)
{
slrn_message (msg5, host);
slrn_smg_refresh();
}
else slrn_tty_message (2, msg5, host);
}
else NNTP_Server_Obj.sv_has_xover = 1;
return 0;
}
static char *nntp_read_line (char *line, unsigned int len)
{
/* This needs to be modified if Reconnection_Ok is non-zero. Somehow,
* the calling routine needs to be told to re-issue the NNTP command
* and restart.
*/
slrn_get_server (line, len, 1);
if ((*line == '.') && (*(line + 1) == 0)) return NULL;
return line;
}
static int nntp_xpat_cmd (char *hdr, int rmin, int rmax, char *pat)
{
char buf[512];
if (0 == PROBE_XCMD(NNTP_Has_XPat, "XPAT"))
return -1;
sprintf (buf, "XPAT %s %d-%d *%s*", hdr, rmin, rmax, pat);
if (OK_HEAD != put_server_with_reconnect (buf, buf, sizeof (buf)))
return -1;
return 0;
}
static int nntp_xgtitle_cmd (char *pattern)
{
char buf[512];
/* XGTITLE appears broken on some servers. So, do not probe for it. */
if (NNTP_Has_Xgtitle == 0) return -1;
if (NNTP_Has_Xgtitle == -1)
{
slrn_message ("Sending Query to Server...");
slrn_smg_refresh ();
Slrn_Message_Present = 0;
}
sprintf (buf, "XGTITLE %s", pattern);
if (OK_XGTITLE != put_server_with_reconnect (buf, buf, sizeof (buf)))
{
slrn_message ("Server does not support XGTITLE command.");
NNTP_Has_Xgtitle = 0;
return -1;
}
NNTP_Has_Xgtitle = 1;
return 0;
}
static int nntp_select_article (int n, char *msgid)
{
char cmd[256];
if (n != -1)
sprintf (cmd, "ARTICLE %d", n);
else
sprintf (cmd, "ARTICLE %s", msgid);
if (OK_ARTICLE == put_server_with_reconnect (cmd, cmd, sizeof (cmd)))
return 0;
else return -1;
}
static int NNTP_XOver_Done;
static int NNTP_XOver_Min;
static int NNTP_XOver_Max;
static int NNTP_XOver_Next;
static int NNTP_Suspend_XOver_For_Kill = 0;
/* We cannot reconnect on this because of the context may be too complex. */
static int nntp_open_xover (int min, int max)
{
char buf[512];
if (NNTP_Server_Obj.sv_has_xover && !NNTP_Suspend_XOver_For_Kill)
{
sprintf (buf, "XOVER %d-%d", min, max);
if (OK_XOVER != slrn_put_server_cmd (buf, buf, sizeof (buf)))
{
NNTP_XOver_Done = 1;
slrn_error ("XOVER failed.");
return -1;
}
}
NNTP_XOver_Next = NNTP_XOver_Min = min;
NNTP_XOver_Max = max;
NNTP_XOver_Done = 0;
return 0;
}
/* RFC850: The required headers are
* Relay-Version, Posting-Version, From, Date, Newsgroups,
* Subject, Message-ID, Path. */
#define NNTP_MAX_XOVER_SIZE 1024
typedef struct
{
char name[25];
unsigned int colon;
char buf[NNTP_MAX_XOVER_SIZE + 16];
}
NNTP_Header_Xover_Type;
/* These MUST be arranged in XOver format:
* id|subj|from|date|msgid|refs|bytes|line|misc stuff
*
* !!!DO NOT REARRANGE OR ADD ANYTHING WITHOUT UPDATING THE ARRAY REFERENCES!!!
*/
#define NUM_XOVER_HEADERS 7
static NNTP_Header_Xover_Type NNTP_Xover_Headers[NUM_XOVER_HEADERS] =
{
{"Subject:", 8, ""},
{"From:", 5, ""},
{"Date:", 5, ""},
{"Message-ID:", 11, ""},
{"References:", 11, ""},
/* {"Bytes:", 6, ""}, */
{"Lines:", 6, ""},
{"Xref:", 5, ""}
};
/* The rest of these are for scoring/killing */
#define NNTP_MAX_EXTRA_XOVER_HEADERS 20
static NNTP_Header_Xover_Type *NNTP_Extra_Xover_Headers[NNTP_MAX_EXTRA_XOVER_HEADERS];
static unsigned int NNTP_Next_Extra_Xover_Header;
static void nntp_open_suspend_xover (void)
{
NNTP_Suspend_XOver_For_Kill = 1;
NNTP_Next_Extra_Xover_Header = 0;
}
static void nntp_close_suspend_xover (void)
{
unsigned int i;
if (NNTP_Suspend_XOver_For_Kill == 0) return;
NNTP_Suspend_XOver_For_Kill = 0;
for (i = 0; i < NNTP_MAX_EXTRA_XOVER_HEADERS; i++)
{
if (NNTP_Extra_Xover_Headers[i] != NULL)
SLFREE (NNTP_Extra_Xover_Headers[i]);
NNTP_Extra_Xover_Headers[i] = NULL;
}
NNTP_Next_Extra_Xover_Header = 0;
}
static char *nntp_get_extra_xover_header (char *hdr)
{
unsigned int i;
unsigned int colon = strlen (hdr) + 1;
for (i = 0; i < NNTP_Next_Extra_Xover_Header; i++)
{
NNTP_Header_Xover_Type *xov = NNTP_Extra_Xover_Headers[i];
if ((colon == xov->colon)
&& (!slrn_case_strcmp ((unsigned char *)xov->name,
(unsigned char *)hdr)))
return xov->buf;
}
return NULL;
}
static char *read_create_xover_line (int id, char *the_buf)
{
char *hbuf, *b;
char buf[8 * NNTP_MAX_XOVER_SIZE];
unsigned int colon = 0;
char *p, ch;
unsigned int group_len;
int i, headers_not_found;
NNTP_Header_Xover_Type *xov = NULL;
for (i = 0; i < NUM_XOVER_HEADERS; i++)
{
*NNTP_Xover_Headers[i].buf = 0;
}
NNTP_Next_Extra_Xover_Header = 0;
headers_not_found = NUM_XOVER_HEADERS;
hbuf = NULL;
while (NULL != nntp_read_line (buf, sizeof(buf)))
{
unsigned int maxlen;
ch = *buf;
if ((ch == ' ') || (ch == '\t'))
{
if (hbuf == NULL) continue;
if (hbuf - xov->buf >= NNTP_MAX_XOVER_SIZE - 2)
{
hbuf = NULL;
continue;
}
*hbuf++ = ' '; /* one blank to separate */
*hbuf = 0;
colon = 0;
/* Drop to add continuation */
}
else
{
if (headers_not_found) i = 0;
else i = NUM_XOVER_HEADERS;
while (i < NUM_XOVER_HEADERS)
{
if ((ch == *NNTP_Xover_Headers[i].name)
&& !slrn_case_strncmp ((unsigned char *)NNTP_Xover_Headers[i].name,
(unsigned char *)buf, NNTP_Xover_Headers[i].colon))
{
headers_not_found--;
xov = NNTP_Xover_Headers + i;
colon = xov->colon;
hbuf = xov->buf;
break;
}
i++;
}
if (i == NUM_XOVER_HEADERS)
{
if ((!NNTP_Suspend_XOver_For_Kill)
|| (NNTP_Next_Extra_Xover_Header == NNTP_MAX_EXTRA_XOVER_HEADERS))
{
hbuf = NULL;
continue;
}
xov = NNTP_Extra_Xover_Headers[NNTP_Next_Extra_Xover_Header];
if (xov == NULL)
{
xov = (NNTP_Header_Xover_Type *) SLMALLOC (sizeof(NNTP_Header_Xover_Type));
if (xov == NULL)
{
hbuf = NULL;
continue;
}
NNTP_Extra_Xover_Headers[NNTP_Next_Extra_Xover_Header] = xov;
}
b = buf;
while (*b && (*b != ':')) b++;
if (*b != ':')
{
hbuf = NULL;
continue;
}
*b++ = 0;
colon = (b - buf);
strncpy (xov->name, buf, sizeof (xov->name) - 1);
xov->name[sizeof(xov->name) - 1] = 0;
xov->colon = colon;
NNTP_Next_Extra_Xover_Header += 1;
hbuf = xov->buf;
}
}
/* trim trailing whitespace */
(void) slrn_trim_string (buf);
/* trim leading whitespace */
b = slrn_skip_whitespace (buf + colon);
maxlen = (NNTP_MAX_XOVER_SIZE - (unsigned int)(hbuf - xov->buf)) - 1;
strncpy (hbuf, b, maxlen);
hbuf[maxlen] = 0;
while (*hbuf)
{
if (*hbuf == '\t') *hbuf = ' ';
hbuf++;
}
}
/* we should try to extract the id from the Xref header if possible */
if (id == -1)
{
char *xref = NNTP_Xover_Headers[6].buf;
if (*xref != 0)
{
group_len = strlen (Slrn_Current_Group_Name);
p = xref;
while ((ch = *p) != 0)
{
if (ch == ' ')
{
p++;
continue;
}
if (!strncmp (p, Slrn_Current_Group_Name, group_len))
{
p += group_len;
if (*p == ':')
{
p++;
id = atoi (p);
break;
}
}
/* skip to next space */
while (((ch = *p) != 0) && (ch != ' ')) p++;
}
}
if ((id == -1) && PROBE_XCMD(NNTP_Has_XPat, "XPAT"))
{
char xpatbuf[256];
char *msgid = NNTP_Xover_Headers[3].buf;
if (-1 != nntp_xpat_cmd ("Message-Id", Slrn_Server_Min, Slrn_Server_Max, msgid))
{
/* This should only loop once. */
while (nntp_read_line (xpatbuf, sizeof (xpatbuf) - 1) != NULL)
{
id = atoi (xpatbuf);
}
}
}
}
/* put in standard XOVER format:
* id|subj|from|date|msgid|refs|bytes|line|misc stuff
*/
sprintf (the_buf, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\tXref: %s",
id,
NNTP_Xover_Headers[0].buf, /* subject */
NNTP_Xover_Headers[1].buf, /* from */
NNTP_Xover_Headers[2].buf, /* date */
NNTP_Xover_Headers[3].buf, /* msgid */
NNTP_Xover_Headers[4].buf, /* references */
"", /* bytes */
NNTP_Xover_Headers[5].buf, /* lines */
NNTP_Xover_Headers[6].buf /* xref */
);
return the_buf;
}
static char *nntp_read_xover (char *the_buf, unsigned int len)
{
char buf[512];
int id;
int already_tried = 0;
if (NNTP_Server_Obj.sv_has_xover && !NNTP_Suspend_XOver_For_Kill)
{
char *ret;
while (NULL != (ret = nntp_read_line (the_buf, len)))
{
id = atoi (ret);
if ((id >= NNTP_XOver_Min) && (id <= NNTP_XOver_Max))
break;
/* Server screwed up and gave bad response. Ignore it. */
}
return ret;
}
if (NNTP_XOver_Next > NNTP_XOver_Max) return NULL;
while (NNTP_XOver_Done == 0)
{
sprintf (buf, "HEAD %d", NNTP_XOver_Next);
if (OK_HEAD != slrn_put_server_cmd (buf, buf, sizeof(buf)))
{
already_tried = 0;
/* This is ugly */
/* If a head fails even though server says it has article, jump
* back and go on.
*/
server_is_messed_up_head_failed:
do
{
switch (slrn_put_server_cmd ("NEXT", buf, sizeof (buf)))
{
case ERR_NONEXT:
NNTP_XOver_Done = 1;
return NULL;
case OK_NOTEXT:
/* Ok, goto next article */
break;
default:
slrn_exit_error ("Server failed NEXT request.");
}
id = atoi (buf + 4);
/* Try going back 5 articles */
if ((already_tried == 0) && (id < NNTP_XOver_Next - 5))
{
sprintf (buf, "HEAD %d", NNTP_XOver_Next - 5);
if (OK_HEAD == slrn_put_server_cmd (buf, buf, sizeof(buf)))
{
/* read all header */
nntp_discard_output (buf, sizeof (buf));
}
already_tried = 1;
}
}
while (id < NNTP_XOver_Next);
if (OK_HEAD != slrn_put_server_cmd ("HEAD", buf, sizeof(buf)))
{
sprintf (buf, "HEAD %d", id);
if (OK_HEAD != slrn_put_server_cmd (buf, buf, sizeof(buf)))
{
already_tried++;
if (already_tried > 10)
{
slrn_exit_error ("Server Error: Head failed.");
}
goto server_is_messed_up_head_failed;
}
}
}
/* extract article number */
id = atoi(buf + 4);
if (id >= NNTP_XOver_Min) break;
/* we just did a head. Read it and do next until range is ok */
nntp_discard_output (buf, sizeof (buf));
while (id < NNTP_XOver_Min)
{
if (OK_NOTEXT == slrn_put_server_cmd ("NEXT", buf, sizeof (buf)))
{
id = atoi(buf + 4);
continue;
}
NNTP_XOver_Done = 1;
break;
}
}
if (NNTP_XOver_Done) return NULL;
if (id > NNTP_XOver_Max)
{
/* clear out header text */
NNTP_XOver_Done = 1;
while (nntp_read_line (buf, sizeof(buf)) != NULL) ;
return NULL;
}
read_create_xover_line (id, the_buf);
if (id > NNTP_XOver_Max)
{
NNTP_XOver_Done = 1;
return NULL;
}
if (OK_NOTEXT == slrn_put_server_cmd ("NEXT", buf, sizeof (buf)))
{
NNTP_XOver_Next = atoi(buf + 4);
if (NNTP_XOver_Next > NNTP_XOver_Max)
{
NNTP_XOver_Done = 1;
}
}
else NNTP_XOver_Done = 1;
return the_buf;
}
static void nntp_close_xover ()
{
NNTP_XOver_Done = 1;
}
static char *nntp_head_from_msgid (char *msgid, char *buf, unsigned int len)
{
int id, status;
if ((msgid == NULL) || (*msgid == 0)) return NULL;
sprintf (buf, "HEAD %s", msgid);
status = put_server_with_reconnect (buf, buf, len);
if (OK_HEAD != status)
{
if (ERR_FAULT == status)
{
slrn_error ("Server does not provide this capability.");
}
return NULL;
}
id = atoi(buf + 4);
if (id == 0) id = -1;
return read_create_xover_line (id, buf);
}
static int nntp_start_post (void)
{
char line[256];
/* Make sure we're connected to a server -- won't be the case the first
* time we post if we're reading news from a local spool. In this case
* a late connect is better as we don't bother the server until we need
* to.
*/
if (!NNTP_Server_Inited)
{
/* nntp_initialize_server gives an error if it fails, so we don't */
if (nntp_initialize_server () != 0)
return -1;
}
if (CONT_POST != put_server_with_reconnect ("POST", line, sizeof (line)))
{
slrn_error ("Posting not allowed.");
return -1;
}
return 0;
}
static int nntp_end_post (void)
{
char line[256];
#ifdef VMS
netwrite (SERVER_WRITE_FP, "\r\n.\r\n", 5);
#else
# if SLRN_USE_SLTCP
sltcp_fputs (&NNTP_SLtcp, "\r\n.\r\n");
sltcp_fflush (&NNTP_SLtcp);
# else
fputs("\r\n.\r\n", SERVER_WRITE_FP);
fflush (SERVER_WRITE_FP);
# endif
#endif
slrn_get_server (line, 255, 1);
if (atoi(line) != 240)
{
slrn_error ("Article rejected: %s", line);
return -1;
}
return 0;
}
static int nntp_printf (char *fmt, ...)
{
va_list ap;
#if SLRN_USE_SLTCP
va_start (ap, fmt);
sltcp_vfprintf (&NNTP_SLtcp, fmt, ap);
va_end (ap);
#else
# ifdef VMS
char buf[512];
va_start (ap, fmt);
vsprintf (buf, fmt, ap);
netwrite (SERVER_WRITE_FP, buf, strlen (buf));
# else
va_start (ap, fmt);
vfprintf (SERVER_WRITE_FP, fmt, ap);
va_end (ap);
# endif
#endif
return 0;
}
static int nntp_puts (char *s)
{
#if SLRN_USE_SLTCP
(void) sltcp_fputs (&NNTP_SLtcp, s);
#else
# ifndef VMS
fputs (s, SERVER_WRITE_FP);
# else
netwrite (SERVER_WRITE_FP, s, strlen (s));
# endif
#endif
return 0;
}
static int nntp_xhdr_command (char *hdr, int num, char *buf, unsigned int buflen)
{
char cmd[256], tmpbuf[1024];
int found;
unsigned int colon;
if (PROBE_XCMD(NNTP_Has_Xhdr, "XHDR"))
{
char *b, ch;
sprintf (cmd, "XHDR %s %d", hdr, num);
if (OK_HEAD != put_server_with_reconnect (cmd, buf, buflen))
return -1;
if (NULL == nntp_read_line (tmpbuf, sizeof(tmpbuf))) return -1;
/* skip past article number */
b = tmpbuf;
while (((ch = *b++) >= '0') && (ch <= '9'))
;
strncpy (buf, b, buflen - 1);
buf[buflen - 1] = 0;
/* I should handle multi-line returns but I doubt that there will be
* any for our use of xhdr
*/
nntp_discard_output (tmpbuf, sizeof (tmpbuf));
return 0;
}
sprintf (cmd, "HEAD %d", num);
if (OK_HEAD != put_server_with_reconnect (cmd, buf, buflen))
return -1;
found = 0;
colon = strlen (hdr);
while (NULL != nntp_read_line (tmpbuf, sizeof (tmpbuf)))
{
char *b;
if (found
|| slrn_case_strncmp ((unsigned char *) tmpbuf, (unsigned char *) hdr, colon)
|| (tmpbuf[colon] != ':'))
continue;
found = 1;
b = tmpbuf + colon;
if (*b == ' ') b++;
strncpy (buf, b, buflen - 1);
buf[buflen - 1] = 0;
}
return 0;
}
static int nntp_list_newsgroups (void)
{
char buf[256];
if (OK_GROUPS != slrn_put_server_cmd ("LIST NEWSGROUPS", buf, sizeof (buf)))
{
slrn_exit_error ("Server failed to return proper response to LIST NEWGROUPS");
}
return 0;
}
static int nntp_list_active (void)
{
char buf[256];
if (OK_GROUPS != slrn_put_server_cmd ("LIST", buf, sizeof (buf)))
{
slrn_exit_error("List failed: %s", buf);
}
return 0;
}
static char *nntp_getserverbyfile (char *file)
{
static char buf[256];
register FILE *fp;
register char *cp;
if (NULL != (cp = getenv("NNTPSERVER")))
{
strcpy (buf, cp);
return buf;
}
if (file == NULL)
{
#ifdef NNTPSERVER_NAME
strcpy (buf, NNTPSERVER_NAME);
return buf;
#else
return NULL;
#endif
}
fp = fopen(file, "r");
if (fp == NULL)
return NULL;
while (fgets(buf, sizeof (buf), fp) != NULL)
{
if (*buf == '\n' || *buf == '#')
continue;
cp = slrn_strchr (buf, '\n');
if (cp)
*cp = '\0';
(void) fclose(fp);
return buf;
}
(void) fclose(fp);
return NULL; /* No entry */
}
static int nntp_init_objects (void)
{
NNTP_Post_Obj.po_start = nntp_start_post;
NNTP_Post_Obj.po_end = nntp_end_post;
NNTP_Post_Obj.po_printf = nntp_printf;
NNTP_Post_Obj.po_puts = nntp_puts;
NNTP_Post_Obj.po_can_post = 1;
NNTP_Server_Obj.sv_select_group = nntp_select_group;
NNTP_Server_Obj.sv_read_line = nntp_read_line;
NNTP_Server_Obj.sv_close = nntp_close_server;
NNTP_Server_Obj.sv_initialize = nntp_initialize_server;
NNTP_Server_Obj.sv_select_article = nntp_select_article;
NNTP_Server_Obj.sv_head_from_msgid = nntp_head_from_msgid;
NNTP_Server_Obj.sv_read_xover = nntp_read_xover;
NNTP_Server_Obj.sv_open_xover = nntp_open_xover;
NNTP_Server_Obj.sv_close_xover = nntp_close_xover;
NNTP_Server_Obj.sv_put_server_cmd = slrn_put_server_cmd;
NNTP_Server_Obj.sv_xpat_cmd = nntp_xpat_cmd;
NNTP_Server_Obj.sv_xhdr_command = nntp_xhdr_command;
NNTP_Server_Obj.sv_get_extra_xover_header = nntp_get_extra_xover_header;
NNTP_Server_Obj.sv_close_suspend_xover = nntp_close_suspend_xover;
NNTP_Server_Obj.sv_open_suspend_xover = nntp_open_suspend_xover;
NNTP_Server_Obj.sv_xgtitle_cmd = nntp_xgtitle_cmd;
NNTP_Server_Obj.sv_has_xover = 0;
NNTP_Server_Obj.sv_has_cmd = nntp_has_cmd;
NNTP_Server_Obj.sv_list_newsgroups = nntp_list_newsgroups;
NNTP_Server_Obj.sv_list_active = nntp_list_active;
return 0;
}
static char *nntp_port_change (char *hostname)
{
char *colon;
char *h;
colon = slrn_strchr (hostname, ':');
if (colon == NULL) return hostname;
h = SLmake_string (hostname);
if (h == NULL)
slrn_exit_error ("Out of memory.");
colon = h + (colon - hostname);
*colon++ = 0;
NNTP_Port = atoi (colon);
return h;
}
#ifndef NNTPSERVER_FILE
# define NNTPSERVER_FILE NULL
#endif
static int nntp_get_servername (void)
{
char *host = Slrn_NNTP_Server_Name;
if ((host == NULL)
&& (NULL == (host = nntp_getserverbyfile(NNTPSERVER_FILE))))
{
fprintf (stderr, "You need to set the NNTPSERVER environment variable to your server name.\r\n");
#ifdef VMS
fprintf (stderr, "Example: $ define/job NNTPSERVER my.news.server\r\n");
#else
# ifdef __os2__
fprintf (stderr, "Example: set NNTPSERVER=my.news.server\r\n");
# else
fprintf (stderr, "Example (csh): setenv NNTPSERVER my.news.server\r\n");
# endif
#endif
return -1;
}
Slrn_NNTP_Server_Name = nntp_port_change (host);
if (NNTP_Server_Obj.sv_name != NULL)
SLFREE (NNTP_Server_Obj.sv_name);
NNTP_Server_Obj.sv_name = slrn_make_startup_string (host);
return 0;
}
static int nntp_select_server_object (void)
{
Slrn_Server_Obj = &NNTP_Server_Obj;
if (NNTP_Server_Obj.sv_name == NULL)
return nntp_get_servername ();
return 0;
}
static int nntp_select_post_object (void)
{
Slrn_Post_Obj = &NNTP_Post_Obj;
if (NNTP_Server_Obj.sv_name == NULL)
return nntp_get_servername ();
return 0;
}
static void nntp_usage (void)
{
fputs ("--nntp options:\n\
-h nntp-host Host name to connect to. This overrides NNTPSERVER variable.\n\
-p NNTP-PORT Set the NNTP port to NNTP-PORT. The default value is 119.\n\
Note: This option has no effect on some systems.\n\
",
stdout);
exit (0);
}
/* returns number parsed */
static int nntp_parse_args (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
{
if (!strcmp (argv[i], "--help"))
nntp_usage ();
else if (i + 1 < argc)
{
if (!strcmp ("-p", argv[i]))
{
NNTP_Port = atoi (argv[++i]);
}
else if (!strcmp ("-h", argv[i]))
{
Slrn_NNTP_Server_Name = argv[++i];
}
else break;
}
else break;
}
return i;
}
#if SLRN_USE_SLTCP
static int Server_Has_Been_Closed = 1;
static int client_put_server (char *s)
{
if (Server_Has_Been_Closed) return -1;
if ((EOF == sltcp_fputs (&NNTP_SLtcp, s))
|| (EOF == sltcp_fputs (&NNTP_SLtcp, "\r\n"))
|| (EOF == sltcp_fflush (&NNTP_SLtcp)))
return -1;
return 0;
}
static int client_get_server (char *buf, unsigned int len)
{
if (Server_Has_Been_Closed) return -1;
if (NULL == sltcp_fgets (&NNTP_SLtcp, buf, len))
return -1;
len = strlen (buf);
if (len && (buf[len - 1] == '\n'))
{
len--;
buf[len] = 0;
if (len && (buf[len - 1] == '\r'))
buf[len - 1] = 0;
}
return 0;
}
static void client_close_server (void)
{
char buf[256];
if (Server_Has_Been_Closed) return;
if (-1 != client_put_server("QUIT"))
(void) client_get_server(buf, sizeof(buf));
sltcp_close (&NNTP_SLtcp);
Server_Has_Been_Closed = 1;
}
static int client_server_init (char *host, int port, char *line, unsigned int len)
{
if (Server_Has_Been_Closed == 0)
client_close_server ();
if (port < 0)
{
port = sltcp_map_service_to_port ("nntp");
if (port == -1)
port = 119;
}
if (-1 == sltcp_open_connection (&NNTP_SLtcp, host, port))
return -1;
Server_Has_Been_Closed = 0;
if (-1 == client_get_server (line, len))
return -1;
return atoi (line);
}
#endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.