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.