/*
**++
**  FACILITY:
**      NEWSREMCLIENT
**
**  ABSTRACT:
**      Interface between local NEWS reader and a remote NNTP implementation.
**      This module contains all necessary calls to access the remote site
**      using NNTP.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright Geoff Huston © 1988,1989,1990,1991
**
**  CONTRIBUTIONS:
**	  - Add support for Excelan - Pekka Kyt|laakso
**
**  MODIFICATION HISTORY:
**      V5.5     7-Oct-1988     GIH
**        - Included modifications for WIN TCP/IP V3.0 support by Jim Patterson
**          (jimp@cognos.uucp)
**          NOTE: I have NOT tested the WIN support modifications.
**        - Changed DECnet object name to "NNTP".
**        - Allow task definition by the logical name NEWS_node_TASK
**          i.e. DEFINE NEWS_CSC_TASK "NEWS="
**      V5.6    11-Nov-1988     GIH
**        - Add get_server_header to get the remote item header only
**      V5.7     1-Dec-1988     PK
**        - Changes to support WIN TCP 3.2 - Pekka Kytolaakso
**        - Add XHDR call to speed up title search if supported at remote end
**        - Bug fix in open_net() - Pekka Kytolaasko
**      V5.8    19-Jan-1989     GIH
**        - Bug fix in open_net() - Pekka Kytolaasko
**        - Added conditional compilation for socket stream emulation in the
**          DECnet QIO interface - "DECNETSTREAM" controls inclusion of stream
**          QIO calls.
**        - If a control-c is typed while NEWS is making a connection to
**          a remote server, the program terminates and all items in the
**          database at that time become inaccessible, even for subsequent
**          NEWS invocations (connection attempts to the remote server are
**          lost.) net_write() - Stuart McGraw
**        - Add support for MULTINET Multinet - Mats Sundvall
**	V6.0	12-Jun-1989	GIH
**	  - Add support for UCX
**	V6.0-1	 1-Nov-1990	SD
**	  - Add support for NNTP-II posting authorization - Sean Donelan
**	V6.0-1	 5-Nov-1990	glass@mgi.com
**	  - Alter response in post-to-server to display server's response
**	V6.0-3	 4-Feb-1991	glass@mgi.com
**	  - Alter nntp_one_call to detect no answer response
**	V6.0-3	11-Mar-1991	GIH
**	  - Add support for the LIST NEWSGROUPS call instead of XGTITLE
**	V6.0-4	20-Mar-1991      Volker C. Huck huck@linac.kernphysik.th-darmstadt.de
**	  - UCX bug fixes
**	V6.0-5	25-May-1991	volz@process.com
**	  - Added TCPware support
**	V6.1	 9-Jun-1991	Andrew Greer (Andrew.Greer@vuw.ac.nz)
**	  - Fix ARTICLE retrieve not reestablishing GROUP context
**	V6.1	 8-Feb-1992	rankin@eql.caltech.edu
**	  - Lint cleanup
**	V6.1b7	15-May-1993	Charles Bailey  bailey@genetics.upenn.edu
**	  - optimize item number lookup and message-ID handling
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
**	V6.1b9	23-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - replace testing macros MULTINET, UCX, TWG, ... for value
**	    with a test for defined(), to prevent evaluating an undefined macro
**	V6.1b9	20-Oct-1994     Mark Martinec   mark.martinec@ijs.si
**	  - nntp_read() and nntp_read_line():  use memcpy for efficiency,
**	    be more careful and avoid writing beyond buffer size
**	  - nntp_write(): bump obuf buffer size to accomodate
**	    inpline buffer in a call to nntp_write() from post_to_server()
**	  - remove definition of MAX_NNTP_SIZE as it is not used anymore
**	V6.1b10	 5-Sep-1996     Mark Martinec   mark.martinec@ijs.si
**	  - allow for the possibility that node name might be NULL
**	V6.1b11	21-Feb-1997     Mark Martinec   mark.martinec@ijs.si
**	  - change parameter name tmp_name to file_opened and its type to int,
**	    clean logic behind it
**	  - remove \n at the end of generated error messages
**	  - comment out 'Connecting to NNTP server' messages
**	  - change wmg() from macro to a routine to minimize code
**	  - add 'const' attribute to passed read-only strings in:
**	    get_host_adrs, open_net, get_group_topnum,
**	    server_get_newsgroups, nntp_call, post_to_server
**	  - change %s to %.<n>s in sprintf to guard against overwriting buffer
**	    (somewhat arbitrary limits: limit node name to 48 chars;
**	    group name and message id to 200 chars)
**	  - return more informative diagnostics in client_msg
**	V6.1b11 17-Mar-1997	Mark Martinec   mark.martinec@ijs.si
**	  - add procedure getsyserrmsg;
**	  - make NNTP error messages more informative;
**	  - add error messages to nntp_call, nntp_one_call, nntp_one_call
**	  - remove variable cmd_msg, now client_msg contains either
**	    low-level diagnostics or server response. This simplified
**	    and unified error reporting.
**	  - replaced all remaining printf with calls to wmg()
**	  - allow NNTP server to be specified by IP address as well
**	    (not only by dns name) (transports: MULTINET,WIN,TCPWARE)
**	V6.1b11	28-Mar-1997	Mark Martinec   mark.martinec@ijs.si
**	  - new procedures available_proto, proto_name and name_to_proto
**	    to replace frequent code patterns and to make possible
**	    using ANU with different TCP transport library
**	    without changing NEWSRC user files (automatically changes
**	    requested and unavailable TCP library to the one
**	    that is compiled-in with the program).
**	  - clean-up syntactic errors when using UCXSOCKETS
**	V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace several calls to strcpy with safer strnncpy
**	  - replace pattern strcpy(news_malloc(strlen(x)+1),x) with news_strdup
**	  - fix the year 2000 problem when generating NNTP commands
**	    NEWNEWS, NEWGROUPS and XUPDGROUP.  Prefer GMT over local time
**	    in these commands.
**	V6.1b11  3-Jun-1997	Mark Martinec   mark.martinec@ijs.si
**	  - add parameters to nntp_read() and nntp_read_line()
**	    (b_m == sizeof(b);  returns length in b_len)
**	  - nntp_read() now reads directly into caller's buffer
**	  - several routines: reestablish NNTP connection
**	    in case NNTP server timed out and closed down our link.
**	    
**--
**/

#ifdef vaxc
#module NEWSREMCLIENT "V6.2"
#endif

#define _NEWSREMCLIENT_C
#define module_name "NEWSREMCLIENT"

#include "newsinclude.h"
#include "newsextern.h"
#include "nntpinclude.h"  /* must follow #include "newsextern.h" */

/* #define DEBUG_NNTP_PROTOCOL_ENABLE 1 */

#ifdef DEBUG_NNTP_PROTOCOL_ENABLE
#define DEBUG_NNTP_PROTOCOL(block) { \
  if (!fdebug) fdebug = fopen("NNTP_DEBUG.LOG","w"); \
  block \
  }
#else
#define DEBUG_NNTP_PROTOCOL(block)
#endif

#define WEEK		(60*60*24*7)

#define NNTP_PORT       (119)

#define AUTH 1

#if defined(TWG) || defined(MULTINET) || defined(TCPWARE)
struct sockaddr_in data_socket = {0};
struct hostent *dest_host;
#endif

#ifdef UCX

#ifdef UCXQIOS
/*   VCH001 changed sockaddr to socketaddr for name cludge with UCX V1.3  */
struct socketaddr {
  short inet_family;
  short inet_port;
  int inet_adrs;
  char bklb[8];
  };

struct itlist { int lgth; struct socketaddr *hst; };

static short sck_parm[2];
static struct socketaddr local_host, remote_host;
struct itlist lhst_adrs, rhst_adrs;
#endif
#endif

#ifdef EXOS
#define EXOS 1
#include        "exos_etc:ttcp.h"
struct ex_iosb iosb;	      	/* I/O status block */
struct SOioctl sock;    	/* Socket control structure     */
struct SOioctl remsock;         /* remote socket control structure */
$DESCRIPTOR(exos, "EX");    	/* EXOS device name descriptor */
long  addr;                   	/* Internet address */

static struct sockaddr_in send_socket = { AF_INET };
static struct sockaddr_in recv_socket = { AF_INET };
#endif

#define CLIENT_TIMER 500
#define RESP_TIMER 300
#define TMP_FILE_NAME "SYS$SCRATCH:NEWS_%X_NNTP.TMP"
#define TMP_HEAD_NAME "SYS$SCRATCH:NEWS_%X_NNTPH.TMP"

char client_msg[256]; /* will contain low-level diagnostic message from:
                           nntp_write, nntp_read, nntp_read_line, toggle_link,
                           cancel_nntp_read, open_net, open_rem_chan;
                         or the full NNTP server's response message
                         after a successful call to wait_net_response(*,1) */
char get_server_title[132];
char get_server_from[132];
char get_server_id[132];
int  get_server_size;

static
char net_open_chan[128] = {""},
     ibuf[8192+1],/* should accomodate the longest response to a NNTP command */
     sav_id[256] = {""},
     sav_idh[256] = {""},
     scratch_file[256] = {""},
     scratch_head[256] = {""};

static
unsigned short net_chan;

extern char nntp_currgroup[];
#ifdef UCXSOCKETS
static int socket_1;					/* socket */
static struct sockaddr_in socket_2_name;         	/* Address struct for remote server */
static struct in_addr inetadd;
static struct hostent hostentstruct, *hostentptr;
#endif

static
int net_read_status = 0,
    cmd_code,
    net_proto = 0,
    sav_itm = 0,
    sav_grp = 0,
    ibuf_l = 0; /* strlen(ibuf) */

#ifdef DEBUG_NNTP_PROTOCOL_ENABLE
static FILE *fdebug = NULL;
#endif

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - this is a VMS structure */
#endif

struct iosb {
    unsigned short iostatus;
    unsigned short iosize;
    int netinfo;
    };
#ifndef UCXSOCKETS
static struct iosb read_iosb;
#endif

#ifdef __DECC
#pragma member_alignment restore
#endif

static
struct nol {
    char *nodename;
    char *taskname;
    struct nol *next;
    } *nh = 0;


const char *proto_name(int proto)
{
  switch (proto) {
    case DECNET:
      return "DECnet";
    case CMUTCP:
      return "CMU";
    case WINTCP:
      return "TWG";
    case MULTINETTCP:
      return "Multinet";
    case UCXTCP:
      return "UCX";
    case EXOSTCP:
      return "EXOS";
    case TCPWARETCP:
      return "TCPware";
    }
  news_assert(0);
  return "unknown";
}

int available_proto(int requested_proto)
{
  int proto, def_proto = DECNET;

#if defined(CMU)
  def_proto = CMUTCP;
#elif defined(TWG)
  def_proto = WINTCP;
#elif defined(MULTINET)
  def_proto = MULTINETTCP;
#elif defined(UCX)
  def_proto = UCXTCP;
#elif defined(EXOS)
  def_proto = EXOSTCP;
#elif defined(TCPWARE)
  def_proto = TCPWARETCP;
#endif

  if (requested_proto == DECNET) return DECNET;
  if (requested_proto < 0) return def_proto; /* any TCP protocol */

/* if requested TCP library is not available, *
 * switch to another one that is available    */

  proto = requested_proto;
  switch (requested_proto) {
    case DECNET:
      proto = DECNET;
      break;
    case CMUTCP:
#ifndef CMU
      proto = def_proto;
#endif
      break;
    case WINTCP:
#ifndef TWG
      proto = def_proto;
#endif
      break;
    case MULTINETTCP:
#ifndef MULTINET
      proto = def_proto;
#endif
      break;
    case UCXTCP:
#ifndef UCX
      proto = def_proto;
#endif
      break;
    case EXOSTCP:
#ifndef EXOS
      proto = def_proto;
#endif
      break;
    case TCPWARETCP:
#ifndef TCPWARE
      proto = def_proto;
#endif
      break;
    default:
      news_assert(0);
    }
  if (proto != requested_proto) {
    struct dsc$descriptor_const_s nm1_dsc = { 0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0 };
    struct dsc$descriptor_const_s nm2_dsc = { 0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0 };
    nm1_dsc.dsc$a_pointer = proto_name(requested_proto);
    nm2_dsc.dsc$a_pointer = proto_name(proto);
    nm1_dsc.dsc$w_length = strlen(nm1_dsc.dsc$a_pointer);
    nm2_dsc.dsc$w_length = strlen(nm2_dsc.dsc$a_pointer);
/*    lib$signal(NEWS$_NEWTCP,2,(int)&nm1_dsc,(int)&nm2_dsc); */
    }
  return proto;
}

int name_to_proto(const char *name, int name_l)
{
  int proto = DECNET;
  name_l = (name_l >= 0) ? name_l : strlen(name);
  if      (!news_strncasecmp(name,"DECNET",name_l))  proto = DECNET;
  else if (!news_strncasecmp(name,"TCP",name_l)) proto = available_proto(-1);
  else if (!news_strncasecmp(name,"CMUTCP",name_l))  proto = CMUTCP;
  else if (!news_strncasecmp(name,"WINTCP",name_l))  proto = WINTCP;
  else if (!news_strncasecmp(name,"MULTINETTCP",name_l)) proto = MULTINETTCP;
  else if (!news_strncasecmp(name,"UCXTCP",name_l))  proto = UCXTCP;
  else if (!news_strncasecmp(name,"EXOSTCP",name_l)) proto = EXOSTCP;
  else if (!news_strncasecmp(name,"TCPWARE",name_l)) proto = TCPWARETCP;
  else proto = -1;
/*
   Since string "TCP" is a leading substring in "TCPWARE"
   a match for generic "TCP" should be made before trying to match "TCPWARE" .
*/
  return proto;
}


/* Return pointer to a statically allocated string
   containing VMS error message associated with the given status.
   Stat1 and stat2 should belong to the facility SYSTEM.
   Usually stat1 is status returned by the system call (e.g. $QIOW)
   as the function value and stat2 is the IOSB status.
 */
char *getsyserrmsg(int stat1, int stat2)
{
  static char msg[128];
  static struct dsc$descriptor_s msg_dsc =
    { 128-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,msg };
  unsigned short msg_l;
  int status;

  _c$cks(sys$getmsg((stat1&1)?stat2:stat1, &msg_l, &msg_dsc, 3, 0));
  msg[msg_l] = '\0';
  return msg;
}

static void wmg(const char *msg, const char *node, const char *aux_msg)
{
  int status;
  int str_l;
  char str[512];

  if (smg_active) {
    _c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$begin_pasteboard_update(&pid));
  }
  str_l = 0;
  if (!smg_active) str_l += sprintf(&str[str_l], "\n");
  if (node)        str_l += sprintf(&str[str_l], "Server %.48s", node);
  if (node && msg) str_l += sprintf(&str[str_l], ", ");
  if (msg)         str_l += sprintf(&str[str_l], "%.128s",    msg);
  if (aux_msg)     str_l += sprintf(&str[str_l], " - %.256s", aux_msg);
  err_line(str);
  if (smg_active) {
    _c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$begin_pasteboard_update(&pid));
  }
}

#ifdef UCXQIOS
static unsigned int get_local_adrs(void)
{
  static unsigned int localhost = 0;

  if (!localhost) {
    const char *gotenv;
    short nam_chan, retlen;
    int comm = INETACP$C_TRANS * 256 + INETACP_FUNC$C_GETHOSTBYNAME;
    struct dsc$descriptor command = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&comm};
    struct dsc$descriptor host_ad = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&localhost};
    struct iosb nam_iosb;

    if (!(gotenv = news_getenv("UCX$INET_HOST",1))) gotenv = "localhost";
    if (sys$assign(c$dsc("BG0:"),&nam_chan,0,0) & 1) {
      if (!(   (sys$qiow(0,nam_chan,IO$_ACPCONTROL,&nam_iosb,0,0,
                       &command,c$dsc(gotenv),&retlen,&host_ad,0,0) & 1)
            && (nam_iosb.iostatus & 1))) localhost = (1 << 24) + 127;
      sys$dassgn(nam_chan);
      }
    }
  return(localhost);
}

static unsigned int get_host_adrs(host_name)
  const char *host_name;
{
  unsigned int hostadrs = 0;
  short nam_chan, retlen;
  int comm = INETACP$C_TRANS * 256 + INETACP_FUNC$C_GETHOSTBYNAME;
  struct dsc$descriptor command = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&comm};
  struct dsc$descriptor host_ad = {4,DSC$K_CLASS_S, DSC$K_DTYPE_T,(char *)&hostadrs};
  struct iosb nam_iosb;

  if (sys$assign(c$dsc("BG0:"),&nam_chan,0,0) & 1) {
    if (!(   (sys$qiow(0,nam_chan,IO$_ACPCONTROL,&nam_iosb,0,0,
                       &command,c$dsc(host_name),&retlen,&host_ad,0,0) & 1)
          && (nam_iosb.iostatus & 1))) hostadrs = 0;
    sys$dassgn(nam_chan);
    }
  return(hostadrs);
}
#endif	/*UCXQIOS*/

/*
 *  nntp_write
 *
 *  Synchronous write of a string to the net channel
 */

static int nntp_write(b)
  const char *b;
{
  static char obuf[1026];
  int obuf_l; /* strlen(obuf) */
#ifndef UCXSOCKETS
  struct iosb write_iosb;
  int sts;
#endif

  client_msg[0] = '\0';
  strnncpy(obuf,sizeof(obuf), b,-1, &obuf_l);
  DEBUG_NNTP_PROTOCOL( fprintf(fdebug,"> %s\n",obuf); )
  strnncat(obuf,sizeof(obuf),obuf_l, "\r\n",2, &obuf_l);

#ifdef UCXSOCKETS
  if (send(socket_1,obuf,obuf_l,0) < 0) {
    sprintf(client_msg,"NNTP send error - %.200s",strerror(errno));
    close_net();
    net_read_status = 0;
    return(0);
    }
#else
  sts = sys$qiow(0,net_chan,
#if defined(DECNETSTREAM)
                 IO$_WRITEVBLK | IO$M_MULTIPLE,
#elif defined(EXOS)
                 EX__WRITE,
#else
                 IO$_WRITEVBLK,
#endif
                 &write_iosb, 0,0, obuf,obuf_l,0, (net_proto == CMUTCP),0,0);
  if (!(sts & 1) || !(write_iosb.iostatus & 1)) {
    sprintf(client_msg,"NNTP wr err: %.200s",
            getsyserrmsg(sts, write_iosb.iostatus) );
    close_net();
    net_read_status = 0;
    return(0);
    }
#endif
  return(1);
}

/*
 *  nntp_read
 *
 *  Timed read of the net channel for a full line
 */

#ifndef UCXSOCKETS
static jmp_buf nntp_env;

static void cancel_nntp_read(sig)
  int sig;	/* not used */
{
  sprintf(client_msg,"NNTP connection timed out, canceling read");
  close_net();
  longjmp(nntp_env,1);
}
#endif

static int nntp_read(b,b_m,b_len,timer)
  char *b;
  int b_m;    /* sizeof(b) */
  int *b_len; /* returns strlen(b) */
  int timer;
{
  struct iosb r_iosb;
  int sts;
  int b_l; /* strlen(b) */

  if (b_len) *b_len = 0;
  *b = '\0'; b_l = 0; client_msg[0] = '\0';

#ifdef UCXSOCKETS
  if ((r_iosb.iosize = recv(socket_1,b,b_m-1,0)) <= 0) {
    sprintf(client_msg,"NNTP recv error - %.200s",strerror(errno));
    close_net();
    return(0);
    }
  else {
    alarm(0);
    sts = 1; r_iosb.iostatus = 1;
    }
#else
  if (setjmp(nntp_env)) return(0);
  signal(SIGALRM,cancel_nntp_read);
  alarm(timer);
  sts = sys$qiow(0,net_chan,
#if defined(DECNETSTREAM)
                 IO$_READVBLK | IO$M_MULTIPLE,
#elif defined(EXOS)
                 EX__READ,
#else
                 IO$_READVBLK,
#endif
                 &r_iosb, 0,0, b,b_m-1, 0,0,0,0);
  alarm(0);
  if (!(sts & 1) || !(r_iosb.iostatus & 1)) {
    sprintf(client_msg,"NNTP read error - %.200s",
            getsyserrmsg(sts, r_iosb.iostatus) );
    close_net();
    return(0);
    }
  if (!r_iosb.iosize) {
    sprintf(client_msg,"NNTP null response, connection dropped");
    close_net();
    return(0);
    }
#endif
  b_l = r_iosb.iosize; b[b_l] = '\0';

  { char *from, *cp;
    int from_l, ll;

 /* replace all <CR><LF> pairs with <LF> */
    for (from=b, from_l=b_l, b_l=0;  from_l>0; ) {
      cp = memchr(from,'\n',from_l);  ll = !cp ? from_l : cp-from+1;
      if (cp && ll>1 && *(cp-1)=='\r') { *(cp-1) = '\n'; ll--; }
      if (b+b_l < from) memmove(b+b_l,from,ll);
      b_l += ll;
      if (!cp) { from_l = 0; from = NULL; }
      else { from_l -= (cp-from)+1; from = cp+1; }
      }
 /* cheating a bit: if the last char in the buffer is <CR> discard it
    in the hope that <LF> will follow at the start of the next read   */
    if (b_l>0 && b[b_l-1]=='\r') b_l--;

    b[b_l] = '\0';
    if (b_len) *b_len = b_l;
    return(1);
  }
}

static char lbuf[2048] = {""};  /* works with any size, bigger for efficiency */
static int  lbuf_l = 0;     /* actual length of string in lbuf */
static int  lbuf_ind = 0;   /* index of the next character in lbuf to be read */

static int nntp_read_line(b,b_m,b_len,timer)
  char *b;
  int b_m;    /* sizeof(b) */
  int *b_len; /* returns strlen(b) */
  int timer;
{
  char *cp;
  int b_l,ll;

  *b = '\0'; client_msg[0] = '\0';
  for (b_l = 0;  b_l < b_m-1; ) {
    if (lbuf_l-lbuf_ind <= 0) {
      lbuf_ind = 0;
      if (!nntp_read(lbuf,sizeof(lbuf), &lbuf_l, timer)) {
        if (b_len) *b_len = b_l;
        return(0);
        }
      }
    cp = memchr(&lbuf[lbuf_ind], '\n',
                ll = min(lbuf_l-lbuf_ind, b_m-b_l-1) );
    if (cp) ll = cp - &lbuf[lbuf_ind] + 1;   /* take only up to and incl '\n' */
    memcpy(&b[b_l],&lbuf[lbuf_ind],ll); b_l += ll; b[b_l] = '\0';
    lbuf_ind += ll;
    if (cp) {                            /* return if we have a complete line */
      if (b_len) *b_len = b_l;
      return(1);
      }
    }
  if (b_len) *b_len = b_l;
  return(1);/* return full buffer even if it does not contain a complete line */
}

/*
 *  wait_net_response
 *
 *  Wait for a response from remote system - cmd indicates that all text
 *  should be skipped until NNTP command response is obtained
 *
 *  Returns response in global variable ibuf and its strlen in ibuf_l.
 */

int wait_net_response(secs,cmd)
  int secs,
      cmd;
{
  cmd_code = 0;
  if (nntp_read_line(ibuf,sizeof(ibuf),&ibuf_l, secs)) {
    DEBUG_NNTP_PROTOCOL( fputs("< ",fdebug); fputs(ibuf,fdebug); )
    if (!cmd) cmd_code = 1;
    else {
      int line_terminated = ibuf_l > 0 && ibuf[ibuf_l-1] == '\n';
      int ll = line_terminated ? ibuf_l-1 : ibuf_l;

     /* skip the rest of the returned response (to a command) in case *
      * the returned line is longer than the available buffer size    */
      while (!line_terminated) {
        int ib_l; char ib[128];
        if (!nntp_read_line(ib,sizeof(ib),&ib_l, secs)) break;
        else {
          line_terminated = ib_l > 0 && ib[ib_l-1] == '\n';
          DEBUG_NNTP_PROTOCOL( fputs(ib,fdebug); )
          }
        }

      ll = min(ll, sizeof(client_msg)-1);
      memcpy(client_msg, ibuf, ll); client_msg[ll] = '\0';
      if (! (sscanf(ibuf,"%d",&cmd_code) == 1) )
        cmd_code = 1;
      else if ((cmd_code == 400) || (cmd_code == 205)) {
        cmd_code = 0;
        close_net();
        }
      }
    }
  return(cmd_code);
}

/*
 *  open_net
 *
 *  Open network link
 */

int open_net(node,proto)
  const char *node;
  int proto;
{
  int sts;
  char netobj[64];
  char dummynode = '\0';

  if (!node) node = &dummynode;
  client_msg[0] = '\0';
  strnncpy(netobj,sizeof(netobj), node,-1, 0);
  switch (proto) {

  case CMUTCP:
#ifdef CMU
                   /* was 'IP:', now use logical 'INET$DEVICE' */
    if ( !((sts=sys$assign(c$dsc("INET$DEVICE"),&net_chan,0,0)) & 1) ) {
      sprintf(client_msg,"Error assigning channel to INET$DEVICE - %.200s",
              getsyserrmsg(sts,1) );
      return(0);
      }
    else if (!((sts=sys$qiow(0,net_chan,IO$_SOCKET,&read_iosb,
                             0,0,netobj,NNTP_PORT,0,1,0,300) & 1) ||
             !(read_iosb.iostatus & 1)) {
      sprintf(client_msg,"Error creating socket - %.200s",
              getsyserrmsg(sts, read_iosb.iostatus) );
      sys$dassgn(net_chan);
      return(0);
      }
    return(1);
#else
    strcpy(client_msg,"CMU TCP/IP support not available");
    return(0);
#endif

  case WINTCP:
  case MULTINETTCP:
  case TCPWARETCP:
#if defined(MULTINET) || defined(TCPWARE) || defined(TWG)
    dest_host = NULL;
    if ( !((sts=sys$assign(c$dsc("INET:"),&net_chan,0,0)) & 1) ) {
      sprintf(client_msg,"Error assigning channel to INET: - %.200s",
              getsyserrmsg(sts,1) );
      return(0);
      }
    else if (!((sts=sys$qiow(0,net_chan,IO$_SOCKET,&read_iosb,
                             0,0,AF_INET,SOCK_STREAM,0,0,0,0)) & 1) ||
             !(read_iosb.iostatus & 1)) {
      sprintf(client_msg,"Error creating socket - %.200s",
              getsyserrmsg(sts, read_iosb.iostatus) );
      sys$dassgn(net_chan);
      return(0);
      }
    else if ( !(dest_host = gethostbyname(netobj)) &&
              ((data_socket.sin_addr.s_addr = inet_addr(netobj)) == -1) ) {
      sprintf(client_msg,"Cannot resolve host name '%.48s'", netobj);
      sys$dassgn(net_chan);
      return(0);
      }
    else {
      if (dest_host)
        data_socket.sin_addr.s_addr = *((u_long *)dest_host->h_addr);
      data_socket.sin_family = AF_INET;
      data_socket.sin_port = htons(NNTP_PORT);
      if (!((sts=sys$qiow(0,net_chan,IO$_CONNECT,&read_iosb,0,0,
                         &data_socket,sizeof(data_socket),0,0,0,0)) & 1) ||
          !(read_iosb.iostatus & 1)) {
        sprintf(client_msg,"Connect error - %.200s",
                getsyserrmsg(sts, read_iosb.iostatus) );
        sys$dassgn(net_chan);
        return(0);
        }
      }
    return(1);
#else
    if (proto == MULTINETTCP)
      strcpy(client_msg,"MultiNet TCP/IP support not available");
    else if (proto == TCPWARETCP)
      strcpy(client_msg,"TCPware TCP/IP support not available");
    else if (proto == WINTCP)
      strcpy(client_msg,"WIN TCP/IP support not available");
    else
      strcpy(client_msg,"Requested TCP/IP support not available");
    return(0);
#endif

  case UCXTCP:
#ifdef UCX
#ifdef UCXQIOS
    if ( !((sts=sys$assign(c$dsc("BG:"),&net_chan,0,0)) & 1) ) {
      sprintf(client_msg,"Error assigning channel to BG: - %.200s",
              getsyserrmsg(sts,1) );
      return(0);
      }
    else {
      sck_parm[0] = INET$C_TCP;
      sck_parm[1] = INET_PROTYP$C_STREAM;
      local_host.inet_family = INET$C_AF_INET;
      local_host.inet_port = 0;  /* dynamic port assignment */
      local_host.inet_adrs = get_local_adrs();
      lhst_adrs.lgth = sizeof local_host;
      lhst_adrs.hst = &local_host;
      if (!((sts=sys$qiow(0,net_chan,IO$_SETMODE,&read_iosb,0,0,
                          &sck_parm,0x01000000|SOCKOPT$M_KEEPALIVE,
                          &lhst_adrs,0,0,0)) & 1) ||
          !(read_iosb.iostatus & 1)) {
        sprintf(client_msg,"IO$_SETMODE error - %.200s",
                getsyserrmsg(sts, read_iosb.iostatus) );
        sys$dassgn(net_chan);
        return(0);
        }
      else {
        remote_host.inet_family = INET$C_AF_INET;
        remote_host.inet_port = htons(NNTP_PORT);
        if (!(remote_host.inet_adrs = get_host_adrs(netobj)))  {
          char *c, *cd, resnum[4];
          unsigned int indx = 0, *i;

          for (c = netobj; c && (indx <= 3); c = cd) {
	    if ((cd = strchr(c,'.')) != 0) *cd = '\0';
            resnum[indx++] = atoi(c);
            if (!cd) { c = 0; break; }
            *cd++ = '.';
            }
          if (c || indx != 4) {
            sprintf(client_msg,"Error parsing NNTP server's IP address");
            sys$qiow(0,net_chan, IO$_DEACCESS | IO$M_SHUTDOWN,read_iosb,
                     0,0,0,0,0,INET$C_DSC_ALL,0,0);
	    sys$dassgn(net_chan);
	    return(0);
            }
          i = (unsigned int *) &resnum[0];
          remote_host.inet_adrs = *i;
          }
        rhst_adrs.lgth = sizeof remote_host;
        rhst_adrs.hst = &remote_host;
        if (!((sts=sys$qiow(0,net_chan,IO$_ACCESS,&read_iosb,
                            0,0,0,0,&rhst_adrs,0,0,0)) & 1) ||
            !(read_iosb.iostatus & 1)) {
          sprintf(client_msg,"IO$_ACCESS error - %.200s",
                  getsyserrmsg(sts, read_iosb.iostatus) );
          sys$qiow(0,net_chan, IO$_DEACCESS | IO$M_SHUTDOWN,read_iosb,
                   0,0,0,0,0,INET$C_DSC_ALL,0,0);
          sys$dassgn(net_chan);
          return(0);
          }
        }
      }
    return(1);
#else	/* !UCXQIOS */
    if ((socket_1 = socket(AF_INET,SOCK_STREAM,0)) == -1) {
      sprintf(client_msg,"Error creating socket - %.200s",strerror(errno));
      return(0);
      }
    socket_2_name.sin_port = htons(NNTP_PORT);
    if ((inetadd.S_un.S_addr = inet_addr(netobj)) != -1) {
      socket_2_name.sin_family = AF_INET;
      socket_2_name.sin_addr = inetadd;
      }
    else {
      if ((hostentptr = gethostbyname(netobj)) == NULL) {
        sprintf(client_msg,"Cannot resolve host name %.48s",netobj);
        close(socket_1);
        return(0);
        }
      hostentstruct = *hostentptr;
      socket_2_name.sin_family = hostentstruct.h_addrtype;
      socket_2_name.sin_addr = *((struct in_addr *) hostentstruct.h_addr);
      }
    if (connect(socket_1,
                (struct sockaddr *) &socket_2_name, sizeof(socket_2_name))) {
      sprintf(client_msg,"Connect error - %.200s",strerror(errno));
      close(socket_1);
      return(0);
      }
    return(1);	/* success */
#endif	/* ?UCXQIOS */
#else   /* UCX */
    strcpy(client_msg,"UCX TCP/IP support not available");
    return(0);   
#endif

  case EXOSTCP:
#ifdef EXOS
    char *host=netobj;

    if ((addr = rhost(&host)) == -1) return(0);

    /* set up data structure here   */
    send_socket.sin_port = 0;       /* use any port address for local */
    recv_socket.sin_port = htons(NNTP_PORT);
    recv_socket.sin_addr.s_addr = addr;
    sock.hassa = 1;
    sock.sa.sin = send_socket;
    sock.hassp = 0;
    sock.type = SOCK_STREAM;
    sock.options = 0;
    remsock.hassa = 1;
    remsock.sa.sin = recv_socket;
    remsock.hassp = 0;
    remsock.type = 0;
    remsock.options = 0;

    if (   (sys$assign(&exos,&net_chan,0,0) & 1)
        && (sys$qiow(0,net_chan,EX__SOCKET,&iosb,0,0,0,0,&sock,0,0,0) & 1)
        && (iosb.ex_stat & 1)
	&& (sys$qiow(0,net_chan,EX__CONNECT,&iosb,0,0,0,0,&remsock,0,0,0) & 1)
        && (iosb.ex_stat & 1))
      return(1);
#else
    strcpy(client_msg,"EXOS TCP/IP support not available");
#endif
    return(0);

  default:	/* DECnet */
  {
    struct nol *tmp;

 /* lower_case(node);   already lowercased by the caller; don't modify const */
    for (tmp = nh; tmp; tmp = tmp->next) {
      if (!strcmp(node,tmp->nodename)) break;
      }
    if (!tmp) {
      char logname[64],
           *gotenv;

      sprintf(logname,"NEWS_%.53s_TASK",node);
      s_to_upper(logname);
      if ( (gotenv = news_getenv(logname,0)) )
        strnncpy(logname,sizeof(logname), gotenv,-1, 0);
      else sprintf(logname,"TASK=NNTP");
      s_to_upper(logname);

      tmp = (struct nol *) news_malloc(sizeof *tmp);
      tmp->nodename = news_strdup(node,-1);
      tmp->taskname = news_strdup(logname,-1);
      tmp->next = nh;
      nh = tmp;
      }
    sprintf(netobj,"%.7s::\"%.50s\"",node,tmp->taskname);
    if ( !((sts=sys$assign(c$dsc(netobj),&net_chan,0,0)) & 1) ) {
      if (sts == SS$_NOSUCHNODE)
        sprintf(client_msg,"Remote DECnet node %.7s is not known", node);
      else
        sprintf(client_msg,"Error opening %.64s - %.150s",
                netobj,getsyserrmsg(sts,1) );
      return(0);
      }
    return(1);	/* success */
    }
  } /*switch(proto)*/
  return(0);
}

/*
 *  close_net
 *
 *  Close network link
 */

void close_net()
{
/* close_net should not destroy client_msg with it's own diagnostics */
#ifdef UCX
  if (net_proto == UCXTCP) {
#ifdef UCXSOCKETS
    shutdown(socket_1,2);
    close(socket_1);
#else
    sys$cancel(net_chan);
    sys$qiow(0,net_chan,IO$_DEACCESS,0,0,0,0,0,0,0,0,0);
    sys$dassgn(net_chan);
#endif
    }
  else 
#endif
#ifdef EXOS
  if (net_proto == EXOSTCP) {
    int portion=1;

    sys$qiow(0,net_chan,EX__IOCTL,0,0,0,0,0,&portion,SIOCDONE,0,0);
    sys$dassgn(net_chan);
    }
  else
#endif
#if defined(MULTINET) || defined(TWG) || defined(CMU)
  if ((net_proto == MULTINETTCP) || (net_proto == WINTCP) || (net_proto ==CMUTCP)) {
    sys$cancel(net_chan);
    sys$qiow(0,net_chan,IO$_DELETE,0,0,0,0,0,0,0,0,0);
    sys$dassgn(net_chan);
    }
  else
#endif
  sys$dassgn(net_chan);
  *net_open_chan = '\0';
  *lbuf = '\0'; lbuf_l = 0;
}

/*
 *  toggle_link
 *
 *  Write a non-command to the net chan and wait for the command-not-recognised
 *  response from the server.
 */

int toggle_link()
{
  if (!nntp_write("STAT") || !wait_net_response(RESP_TIMER,1)) {
    if (*net_open_chan) close_net();
    return(0);
    }
  return(1);
}

/*
 *  open_rem_chan
 *
 *  Open channel to remote NNTP server on host name node, using protocol
 *  proto. Values for proto are:
 *    0 - DECNET
 *    1 - CMUTCP
 *    2 - WINTCP
 *    3 - MULTINETTCP
 *    4 - UCXTCP
 *    5 - EXOSTCP
 *    6 - TCPWARETCP
 */

int open_rem_chan(node,proto)
  const char *node;
  int proto;
{
  char *p;
  char dummynode = '\0';
  if (!node) node = &dummynode;
  client_msg[0] = '\0';

  if (!*scratch_file) sprintf(scratch_file,TMP_FILE_NAME,getpid());
  if (!*scratch_head) sprintf(scratch_head,TMP_HEAD_NAME,getpid());
  if (*net_open_chan) {
    if ((proto != net_proto) || (news_strncasecmp(node,net_open_chan,0))) {
      close_net();
      net_read_status = 0;
      }
/*  toggle_link();  */
    }

  if (!*net_open_chan) {
    strnncpy(net_open_chan,sizeof(net_open_chan), node,-1, 0);
    lower_case(net_open_chan);
    if (!open_net(net_open_chan,proto)) {
      net_read_status = 0; *net_open_chan = '\0';
      return(0);
      }
    net_proto = proto;
    if (!wait_net_response(CLIENT_TIMER,1)) {
      net_read_status = 0;
      close_net();
      return(0);
      }
    nntp_no_posting_allowed = (cmd_code == 201);
    nntp_anu_news_server = (strncmp(&ibuf[4],"ANU NEWS/NNTP",13) == 0);
    if ( (p=strchr(&ibuf[4],' ')) ) {
      if ( !strncmp(p+1,"InterNetNews server",19) ) {
        if (!nntp_write("mode reader") || !wait_net_response(CLIENT_TIMER,1)) {
          net_read_status = 0;
          close_net();
          return(0);
          }
        nntp_no_posting_allowed = (cmd_code == 201);
        }
      }
    }
  return(1);
}

/*
 *  get_group_topnum
 */

int get_group_topnum(node,proto,group)
  const char *node, *group;
  int proto;
{
  int count,first,topnum;
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  int resp = 0, retries = 0;

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return(-4);
    }
  sprintf(cmd,"GROUP %.200s",group);
  if (   !nntp_write(cmd)
      || (   (resp=wait_net_response(CLIENT_TIMER,1)) != 211
          && resp != 411 )) {
    wmg(0,node,client_msg);
    return(-4);
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  if (resp == 411) return(-2);
  if (sscanf(ibuf,"%*d %d %d %d %*s",&count,&first,&topnum) < 3) return(-3);
  if (!count) return(-1);
  return(topnum);
}

/*
 *  get_server_file
 *
 *  Get a newsitem text from remote system
 */

FILE *get_server_file(id,node,file_opened,put_file,proto,g,i)
  const char *id, *node, *put_file;
  int *file_opened;     /* returned file descriptor is valid and file is open */
  int proto, g, i;
{
  FILE *fpw;
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  int data = 0;
  struct rms_file *ofile, *ifile;
  int resp = 0, retries = 0;

  if (file_opened) *file_opened = 0;
  if (!strcmp(id,sav_id) && (fpw = fopen(scratch_file,"r"))) {
    if (file_opened) *file_opened = 1;
    return(fpw);
    }
  *sav_id = '\0';
  sav_itm = sav_grp = 0;
  while (!delete(scratch_file));

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return(0);
    }
  sprintf(cmd,"ARTICLE %.200s",id);
  if (!nntp_write(cmd) || (resp=wait_net_response(CLIENT_TIMER,1)) != 220) {
    char *nntp_response;
    unsigned int inum;

    strcpy(err_oline,"GROUP "); err_oline_cat(ga[g]->grp_name);
    if (nntp_one_call(node,proto,err_oline,&nntp_response) != 211) {
      wmg("Failed to get info on newsgroup",node,ga[g]->grp_name);
      return(0);
      }
    strnncpy(nntp_currgroup,nntp_currgroup_m, ga[g]->grp_name,-1, 0);
    if (!find_itm_by_id(g,id,&inum)) {
      wmg("Item search failed for id",0,id);
      return(0);
      }
    sprintf(cmd,"ARTICLE %d",inum);
    if (!nntp_write(cmd) || (resp=wait_net_response(CLIENT_TIMER,1)) != 220) {
      wmg("ARTICLE not returned",node,client_msg);
      return(0);
      }
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  fpw = fopen(scratch_file,"w");
  get_server_size = 0;
  *get_server_title = '\0';
  *get_server_from = '\0';
  *get_server_id = 0;
  while (wait_net_response(RESP_TIMER,0) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    if (ibuf_l > 0 && *ibuf == '.') memmove(ibuf,ibuf+1,ibuf_l--);
    ++data;
    fputs(ibuf,fpw);
    if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
    if (!get_server_size && !strncmp(ibuf,"Lines:",6))
      { if (!sscanf(ibuf,"Lines: %d",&get_server_size)) get_server_size = 0; }
    else if ((!*get_server_title) && !strncmp(ibuf,"Subject:",8))
      strnncpy(get_server_title,sizeof(get_server_title), ibuf+9,ibuf_l-9, 0);
    else if ((!*get_server_from) && !strncmp(ibuf,"From:",5))
      strnncpy(get_server_from,sizeof(get_server_from), ibuf+6,ibuf_l-6, 0);
    }
  fclose(fpw);
  if (!data || (strcmp(ibuf,".\n"))) {
    while (!delete(scratch_file));
    wmg("Item not located",node,0);
    return(0);
    }
  if (put_file) {
    sysprv();
    if (   (!(ifile = rms_open(scratch_file,0,0)))
        || (!(ofile = rms_create(put_file,0xa000,0)))) {
      if (ifile) rms_close(ifile);
      nosysprv();
      if ((fpw = fopen(scratch_file,"r")) != 0) {
        strnncpy(sav_id,sizeof(sav_id), id,-1, 0);
        if (file_opened) *file_opened = 1;
        }
      else while (!delete(scratch_file));
      clear_err_line();
      return(fpw);
      }
    while (rms_get(ibuf,sizeof(ibuf),ifile)) rms_put(ibuf,ofile);
    rms_close(ifile);
    rms_close(ofile);
    nosysprv();
    while (!delete(scratch_file));
    if ((fpw = fopen(put_file,"r")) != 0)
      if (file_opened) *file_opened = 1;
    clear_err_line();
    return(fpw);
    }
  if ((fpw = fopen(scratch_file,"r")) != 0) {
    strnncpy(sav_id,sizeof(sav_id), id,-1, 0);
    if (file_opened) *file_opened = 1;
    }
  else while (!delete(scratch_file));
  clear_err_line();
  return(fpw);
}

FILE *getno_server(itm,grp,node,file_opened,put_file,proto)
  int itm,grp;
  const char *node, *put_file;
  int *file_opened;     /* returned file descriptor is valid and file is open */
  int proto;
{
  FILE *fpw;
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  struct rms_file *ifile, *ofile;
  int data = 0;
  int netresp = 0, retries = 0;

  if (file_opened) *file_opened = 0;
  if ((itm == sav_itm) && (grp == sav_grp)
   && ((fpw = fopen(scratch_file,"r")) != 0)) {
    if (file_opened) *file_opened = 1;
    return(fpw);
    }
  *sav_id = '\0';
  sav_itm = sav_grp = 0;
  while (!delete(scratch_file));

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return(0);
    }
  sprintf(cmd,"ARTICLE %d",itm);
  if (!nntp_write(cmd))
    wmg("Error sending ARTICLE command",node,client_msg);
  else {
    netresp = wait_net_response(CLIENT_TIMER,1);
    if (netresp == 412) { /* 412 is no group specified so set group then try to get article */
      sprintf(cmd,"GROUP %.200s",ga[curr_g]->grp_name);
      if (!nntp_write(cmd) || (wait_net_response(CLIENT_TIMER,1) != 211)) {
        wmg("GROUP info not returned",node,client_msg);
        return(0);
        }
      sprintf(cmd,"ARTICLE %d",itm);
      if (nntp_write(cmd))
        netresp = wait_net_response(CLIENT_TIMER,1);
      else {
        wmg("ARTICLE not located",node,client_msg);
        return(0);
        }
      }
    }
  if (netresp != 220) {
    if (netresp == 503) {  /* NNTP server timed out, retry */
      wmg("Timed out, retrying",node,client_msg);
      close_net();
      if (retries++ < 1) goto retry;
      }
    else {
      wmg("ARTICLE not located",node,client_msg);
      return(0);
      }
    }
  fpw = fopen(scratch_file,"w");
  get_server_size = 0;
  *get_server_title = '\0';
  *get_server_from = '\0';
  *get_server_id = '\0';
  while (wait_net_response(RESP_TIMER,0) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    if (*ibuf == '.') memmove(ibuf,ibuf+1,ibuf_l--);
    ++data;
    fputs(ibuf,fpw);
    if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
    if (!get_server_size && !strncmp(ibuf,"Lines:",6))
      { if (!sscanf(ibuf,"Lines: %d",&get_server_size)) get_server_size = 0; }
    else if ((!*get_server_title) && !strncmp(ibuf,"Subject:",8))
      strnncpy(get_server_title,sizeof(get_server_title), ibuf+9,ibuf_l-9, 0);
    else if ((!*get_server_from) && !strncmp(ibuf,"From:",5))
      strnncpy(get_server_from,sizeof(get_server_from), ibuf+6,ibuf_l-6, 0);
    else if ((!*get_server_id) && !strncmp(ibuf,"Message-Id:",11))
      strnncpy(get_server_id,sizeof(get_server_id), ibuf+12,ibuf_l-12, 0);
    }
  fclose(fpw);
  if (!data || (strcmp(ibuf,".\n"))) {
    while (!delete(scratch_file));
    wmg("Item not located",node,0);
    return(0);
    }
  if (put_file) {
    sysprv();
    if (   (!(ifile = rms_open(scratch_file,(char *)0,0)))
        || (!(ofile = rms_create(put_file,0xa000,0)))) {
      if (ifile) rms_close(ifile);
      nosysprv();
      if ((fpw = fopen(scratch_file,"r")) != 0) {
        sav_itm = itm; sav_grp = grp;
        if (file_opened) *file_opened = 1;
        }
      else while (!delete(scratch_file));
      clear_err_line();
      return(fpw);
      }
    while (rms_get(ibuf,sizeof(ibuf),ifile)) rms_put(ibuf,ofile);
    rms_close(ifile);
    rms_close(ofile);
    nosysprv();
    while (!delete(scratch_file));
    if ((fpw = fopen(put_file,"r")) != 0)
      if (file_opened) *file_opened = 1;
    clear_err_line();
    return(fpw);
    }
  if ((fpw = fopen(scratch_file,"r")) != 0) {
    sav_itm = itm; sav_grp = grp;
    if (file_opened) *file_opened = 1;
    }
  else while (!delete(scratch_file));
  clear_err_line();
  return(fpw);
}

/*
 *  get_server_header
 *
 *  Get a newsitem header from remote system
 */

FILE *get_server_header(id,node,file_opened,proto,grp_name,itm_num)
  const char *id, *node, *grp_name;
  int *file_opened;     /* returned file descriptor is valid and file is open */
  int proto, itm_num;
{
  FILE *fpw;
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  int data = 0, no_retrieve = 1;
  int resp = 0, retries = 0;

  if (file_opened) *file_opened = 0;
  if (*id && !strcmp(id,sav_id) && (fpw = fopen(scratch_file,"r")) != 0) {
    if (file_opened) *file_opened = 1;
    return(fpw);
    }
  if (*id && !strcmp(id,sav_idh) && (fpw = fopen(scratch_head,"r")) != 0) {
    if (file_opened) *file_opened = 1;
    return(fpw);
    }
  *sav_idh = '\0';
  while (!delete(scratch_head));

/*
  wmg("Connecting to NNTP server ...",node,0);
*/
retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return(0);
    }
  if (*id) {
    sprintf(cmd,"HEAD %.200s",id);
    no_retrieve = !nntp_write(cmd) ||
                  (resp=wait_net_response(CLIENT_TIMER,1)) != 221;
    }
  if (no_retrieve && nntp_client && *grp_name) {
    *nntp_currgroup = '\0';
    sprintf(cmd,"GROUP %.200s",grp_name);
    if (!(no_retrieve = (!nntp_write(cmd) ||
                         (resp=wait_net_response(CLIENT_TIMER,1)) != 211))) {
      strnncpy(nntp_currgroup,nntp_currgroup_m, grp_name,-1, 0);
      sprintf(cmd,"HEAD %d",itm_num);
      no_retrieve = !nntp_write(cmd) ||
                    (resp=wait_net_response(CLIENT_TIMER,1)) != 221;
      }
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  if (no_retrieve && !nntp_client) {
    char *nntp_response;
    unsigned int inum;
    int gidx;

    if (!(gidx = ga_exact_name(grp_name))) {
      wmg("Cannot locate group",0,grp_name);
      return(0);
      }
    strcpy(err_oline,"GROUP "); err_oline_cat(grp_name);
    if (nntp_one_call(node,proto,err_oline,&nntp_response) == 211) {
      strnncpy(nntp_currgroup,nntp_currgroup_m, grp_name,-1, 0);
      if (!find_itm_by_id(gidx,id,&inum)) {
        wmg("Cannot locate item",0,id);
        return(0);
        }
      sprintf(cmd,"HEAD %d",inum);
      no_retrieve = (!nntp_write(cmd) || (wait_net_response(CLIENT_TIMER,1) != 221));
      }
    }
  if (no_retrieve) {
    wmg("NNTP HEAD Error: ",node,client_msg);
    return(0);
    }
  fpw = fopen(scratch_head,"w");
  get_server_size = 0;
  *get_server_title = '\0';
  *get_server_from = '\0';
  while (wait_net_response(RESP_TIMER,0) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    if (*ibuf == '.') memmove(ibuf,ibuf+1,ibuf_l--);
    ++data;
    fputs(ibuf,fpw);
    if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
    if (!get_server_size && !strncmp(ibuf,"Lines:",6))
      { if (!sscanf(ibuf,"Lines: %d",&get_server_size)) get_server_size = 0; }
    else if ((!*get_server_title) && !strncmp(ibuf,"Subject:",8))
      strnncpy(get_server_title,sizeof(get_server_title), ibuf+9,ibuf_l-9, 0);
    else if ((!*get_server_from) && !strncmp(ibuf,"From:",5))
      strnncpy(get_server_from,sizeof(get_server_from), ibuf+6,ibuf_l-6, 0);
    }
  fclose(fpw);
  if (!data || (strcmp(ibuf,".\n"))) {
    while (!delete(scratch_head));
    wmg("Lost link",node,0);
    return(0);
    }
  if ((fpw = fopen(scratch_head,"r")) != 0) {
    strnncpy(sav_idh,sizeof(sav_idh), id,-1, 0);
    if (file_opened) *file_opened = 1;
    }
  else while (!delete(scratch_head));
  clear_err_line();
  return(fpw);
}

/*
 *  server_check_ids
 *
 *  Obtain a directory from the remote system for a newsgroup
 */

void server_check_ids(check_id_head)
  struct grps *check_id_head;
{
  time_t daytime;
  int resp_val;
  struct grps *reply;
  struct itms *tmp;
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  struct tm *stm;
  int gmt;  /* time in stm is GMT */

  for (reply = check_id_head; reply; reply = reply->grpsnext) {
    if (open_rem_chan(reply->grpnode,reply->nproto)) {
      time(&daytime);
      if (   (reply->grptime > daytime)
          || (reply->grptime < 0))
	 reply->grptime = daytime - WEEK;
   /* stm = localtime(&(reply->grptime)); gmt = 0; */
      { time_t tt = reply->grptime - gmt_offset; stm = localtime(&tt); gmt = 1;}
      sprintf(cmd,"XUPDGROUP %.200s %02d%02d%02d %02d%02d%02d%s",
              reply->grpname,
	      stm->tm_year % 100, stm->tm_mon + 1, stm->tm_mday,
              stm->tm_hour, stm->tm_min, stm->tm_sec,
              (gmt ? " GMT" : "") );
      if (!NNTP_USE_XUPDGROUP || nntp_write(cmd)) {
        if (!NNTP_USE_XUPDGROUP) resp_val = 500;
        else if ((resp_val = wait_net_response(CLIENT_TIMER,1)) == 480) {
          wmg("Newsgroup not defined on server",reply->grpnode,client_msg);
          reply = reply->grpsnext;
          continue;
          }
        if (resp_val == 380) {
          for (tmp = reply->ids; tmp; tmp = tmp->itmsnext) {
            sprintf(cmd,"%.200s",tmp->itmid);
            if (!nntp_write(cmd)) {
              wmg("While sending",reply->grpnode,client_msg);
              reply = reply->grpsnext;
              continue;
              }
            }
          if (!nntp_write(".")) {
            wmg("While sending terminating dot",reply->grpnode,client_msg);
            reply = reply->grpsnext;
            continue;
            }
          if (!(wait_net_response(CLIENT_TIMER,1) == 280)) {
            wmg(0,reply->grpnode,client_msg);
            reply = reply->grpsnext;
            continue;
            }
          while (wait_net_response(RESP_TIMER,0) == 1) {
            if (!strcmp(ibuf,".\n")) break;
            tmp = (struct itms *) news_malloc(sizeof *tmp);
            if (*ibuf == 'D') {
              ibuf[--ibuf_l] = '\0';
              util_idcpy(tmp->itmid,&ibuf[2]);
              tmp->itmdate = 0;
              tmp->itmsnext = reply->idr;
              reply->idr = tmp;
              }
            else if (sscanf(ibuf,"%s %d %d %[^\n]",
                            tmp->itmid, &(tmp->itmdate),
                            &(tmp->itmsize), tmp->itmtitle) == 4) {
              tmp->itmsnext = reply->idr;
              reply->idr = tmp;
              }
            else news_free(tmp);
            }
          }
        else {
          sprintf(cmd,"NEWNEWS %.200s %02d%02d%02d %02d%02d%02d%s",
                  reply->grpname,
                  stm->tm_year % 100, stm->tm_mon + 1, stm->tm_mday,
                  stm->tm_hour, stm->tm_min, stm->tm_sec,
                  (gmt ? " GMT" : "") );
          if (nntp_write(cmd) && (wait_net_response(CLIENT_TIMER,1) == 230)) {
            while (wait_net_response(RESP_TIMER,0) == 1) {
              if (!strcmp(ibuf,".\n")) break;
              tmp = (struct itms *) news_malloc(sizeof *tmp);
              tmp->itmsnext = reply->idr;
              reply->idr = tmp;
              sscanf(ibuf,"%s",tmp->itmid);
              time((time_t *) &tmp->itmdate);
              tmp->itmsize = 0;
              }
            }
          else wmg(0,reply->grpnode,client_msg);
          }
        }
      else wmg(0,reply->grpnode,client_msg);
      }
    else wmg(0,reply->grpnode,client_msg);
    }
}

void server_get_titles(reply,irecheck)
  struct grps *reply;
  struct itms *irecheck;
{
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  char *cp, *nntp_response;
  int count, first, last,
      try_xhdr = NNTP_USE_XHDR, try_head, resp_code;

  if (!irecheck) return;
  wmg("Title search",reply->grpnode,reply->grpname);
  strcpy(err_oline,"GROUP "); err_oline_cat(reply->grpname);
  if ((nntp_one_call(reply->grpnode,reply->nproto,err_oline,&nntp_response) != 211)  ||
      (sscanf(nntp_response,"211 %d %d %d",&count,&first,&last) != 3)) {
    strcpy(err_oline,"NNTP: Group "); err_oline_cat(reply->grpnode);
    err_oline_cat(" -> "); err_oline_cat(nntp_response);
    wmg(err_oline,0,0);
    return;
    }
  strnncpy(nntp_currgroup,nntp_currgroup_m, reply->grpname,-1, 0);
  for (; irecheck; irecheck = irecheck->itmsnext) {
    *(irecheck->itmtitle) = '\0';
    *(irecheck->itmfrom) = '\0';
    irecheck->itmvalid = 0;
    try_head = 1;
    if (try_xhdr) {
      resp_code = 0;
      sprintf(cmd,"XHDR Subject %.200s",irecheck->itmid);
      if (nntp_write(cmd) &&
          ((resp_code = wait_net_response(CLIENT_TIMER,1)) == 221)) {
        irecheck->itmvalid = 1;
        while (wait_net_response(RESP_TIMER,0) == 1) {
          if (!strcmp(ibuf,".\n")) break;
          if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
	  if ((cp = strchr(ibuf,'>')) != 0) {
            cp += 2;
            util_subjcpy(irecheck->itmtitle,cp);
            try_head = 0;
            }
          }
        sprintf(cmd,"XHDR From %.200s",irecheck->itmid);
        if ((nntp_write(cmd)) && ((resp_code = wait_net_response(CLIENT_TIMER,1)) == 221)) {
          while (wait_net_response(RESP_TIMER,0) == 1) {
            if (!strcmp(ibuf,".\n")) break;
            if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
	    if ((cp = strchr(ibuf,'>')) != 0) {
              cp += 2;
              util_fromcpy(irecheck->itmfrom,cp);
              }
            }
          }
        sprintf(cmd,"XHDR Date %.200s",irecheck->itmid);
        if ((nntp_write(cmd)) && ((resp_code = wait_net_response(CLIENT_TIMER,1)) == 221)) {
          while (wait_net_response(RESP_TIMER,0) == 1) {
            if (!strcmp(ibuf,".\n")) break;
            if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
	    if ((cp = strchr(ibuf,'>')) != 0) {
              cp += 2;
              irecheck->itmdate = parse_usenet_date(cp);
              }
            }
          }
        if (!(irecheck->itmsize)) {
          sprintf(cmd,"XHDR lines %.200s",irecheck->itmid);
          if ((nntp_write(cmd)) && (wait_net_response(CLIENT_TIMER,1) == 221)) {
            while (wait_net_response(RESP_TIMER,0) == 1) {
              if (!strcmp(ibuf,".\n")) break;
              if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
	      if ((cp = strchr(ibuf,'>')) != 0) {
                cp += 2;
                if (!sscanf(cp,"%d",&(irecheck->itmsize))) irecheck->itmsize= 0;
                }
              } /* while */
            } /* if */
          } /* if (!(irecheck->itmsize)) */
        } /* if */
      else if (resp_code == 500) try_xhdr = 0;
      } /* if (try_xhdr) */
    if (try_head) {
      unsigned int inum;
      int gidx;

      sprintf(cmd,"HEAD %.200s",irecheck->itmid);
      if (!nntp_write(cmd))
        wmg("Sending HEAD comand",0,client_msg);
      if ((resp_code = wait_net_response(CLIENT_TIMER,1)) != 221) {
      if (!(gidx = ga_exact_name(reply->grpname))) {
        wmg("Cannot locate group",0,reply->grpname);
        return;
        }
      if (!find_itm_by_id(gidx,irecheck->itmid,&inum)) {
        wmg("Cannot locate item",0,irecheck->itmid);
        return;
        }
      sprintf(cmd,"HEAD %d",inum);
      if (!nntp_write(cmd)) {
        wmg("Sending HEAD command",0,client_msg);
        return;
        }
      resp_code = wait_net_response(CLIENT_TIMER,1);
      } /* if (try_head) */
    if (resp_code == 221) {
      irecheck->itmvalid = 1;
      while (wait_net_response(RESP_TIMER,0) == 1) {
        if (!strcmp(ibuf,".\n")) break;
        if (*ibuf == '.') memmove(ibuf,ibuf+1,ibuf_l--);
        if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
        if (!strncmp(ibuf,"Subject:",8))
          util_subjcpy(irecheck->itmtitle,ibuf + 9);
        else if (!strncmp(ibuf,"From:",5))
          util_fromcpy(irecheck->itmfrom,ibuf + 6);
        else if (!strncmp(ibuf,"Date:",5))
          irecheck->itmdate = parse_usenet_date(ibuf + 6);
        else if (!strncmp(ibuf,"Lines:",6)) {
          if (!sscanf(ibuf,"Lines: %d",&(irecheck->itmsize))) irecheck->itmsize = 0;
          }
        } /* while */
      } /* if (resp_code == 221) */
    }
    if (!*(irecheck->itmtitle)) irecheck->itmvalid = 0;
    } /* for */
}

char *server_get_newsgroups(node,proto,sincetime)
  const char *node;
  int proto;
  time_t sincetime;
{
  char cmd[256];  /* RFC977: Command lines shall not exceed
                     512 characters in length, including trailing CR-LF */
  int return_buf_m = 0,  /* mallocated sizeof return_buf */
      return_buf_l = 0;  /* strlen(return_buf) */
  char *return_buf = 0;
  struct tm *stm;
  int gmt;  /* time in stm is GMT */
  int resp = 0, retries = 0;

/*
  wmg("Connecting to NNTP server ...",node,0);
*/
retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return(0);
    }
  if (sincetime) {
 /* stm = localtime(&sincetime); gmt = 0; */
    { time_t tt = sincetime - gmt_offset; stm = localtime(&tt); gmt = 1; }
    sprintf(cmd,"NEWGROUPS %02d%02d%02d %02d%02d%02d%s",
            stm->tm_year % 100, stm->tm_mon + 1, stm->tm_mday,
            stm->tm_hour, stm->tm_min, stm->tm_sec,
            (gmt ? " GMT" : "") );
    }
  else sprintf(cmd,"LIST");
  if (!nntp_write(cmd) || (resp=wait_net_response(CLIENT_TIMER,1)) != 215) {
    wmg(0,node,client_msg);
    return(0);
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  return_buf = (char *) news_malloc(return_buf_m = 512);
  *return_buf = '\0'; return_buf_l = 0;
  while ((resp=wait_net_response(RESP_TIMER,0)) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    if (return_buf_l + ibuf_l + 10 >= return_buf_m)
      return_buf = (char *) news_realloc(return_buf,
                              (return_buf_m += (return_buf_l + ibuf_l + 512)));
    strnncat(return_buf,return_buf_m,return_buf_l, ibuf,ibuf_l, &return_buf_l);
    }
  if (strcmp(ibuf,".\n"))
    wmg("Lost link to NNTP server",node,0);
  return(return_buf);
}

/*
 *  s_to_upper
 *
 *  convert a string to lower case
 */

char *s_to_upper(s)
    char *s;
{
  char *cp;

  for (cp=s; *cp; cp++)
    *cp = toupper(*cp);
  return(s);
}

void close_nntp_file()
{
  if (*scratch_file) while (!delete(scratch_file));
  if (*scratch_head) while (!delete(scratch_head));
}

static char *nc_resp = NULL;
static int nc_len = 0, nc_malloc = 0;


int nntp_call(node,proto,xcmd,retbuf)
  const char *node; int proto; const char *xcmd; char **retbuf;
{
  int resp = 0, retries = 0;

  if (nc_resp) { news_free(nc_resp); nc_resp = NULL; }
  nc_len = nc_malloc = 0;
  *retbuf = NULL;

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (!nntp_write(xcmd)) {
    wmg("Sending command",node,client_msg);
    return 0;
    }
  if (!(resp=wait_net_response(CLIENT_TIMER,1))) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  nc_resp = news_malloc(nc_malloc = ibuf_l+512);
  strnncpy(nc_resp,nc_malloc, ibuf,ibuf_l, &nc_len);
  if ((resp / 100) != 2) {	/* galss@mgi.com  V6.0-3  4/2/91 */
    *retbuf = nc_resp;
    return(resp);
    }
  while ((resp=wait_net_response(RESP_TIMER,0)) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    if (nc_len + ibuf_l + 10 >= nc_malloc)
      nc_resp = news_realloc(nc_resp,
                             (nc_malloc += nc_len + ibuf_l + 512));
    strnncat(nc_resp,nc_malloc,nc_len, ibuf,ibuf_l, &nc_len);
    }
  if (strcmp(ibuf,".\n"))
    wmg("Lost link to NNTP server",node,0);
  *retbuf = nc_resp;
  return(resp);
}

int nntp_one_call(node,proto,xcmd,retbuf)
  const char *node; int proto; const char *xcmd; char **retbuf;
{
  int resp = 0, retries = 0;

  if (nc_resp) { news_free(nc_resp); nc_resp = NULL; }
  nc_len = nc_malloc = 0;
  *retbuf = NULL;

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (!nntp_write(xcmd)) {
    wmg("Sending command",node,client_msg);
    return 0;
    }
  if (!(resp=wait_net_response(CLIENT_TIMER,1))) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  *retbuf = nc_resp = news_strdup(ibuf,ibuf_l);
  return(resp);
}


int nntp_get_info(node,proto,xcmd,code,fcall,infocall)
  const char *node;
  int proto;
  const char *xcmd;
  int code;
  int (*fcall)();
  int (*infocall)();
{
  int resp = 0, retries = 0;

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (!nntp_write(xcmd)) {
    wmg("Sending command",node,client_msg);
    return 0;
    }
  if (!(resp=wait_net_response(CLIENT_TIMER,1))) {
    wmg(0,node,client_msg);
    return 0;
    }
  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  if (resp != code) return(0);
  if (fcall) (*fcall)(ibuf);
  while (wait_net_response(RESP_TIMER,0) == 1) {
    if (!strcmp(ibuf,".\n")) break;
    (*infocall)(ibuf);
    }
  return(1);
}

/*
 * post_to_server
 *
 * Post the item in "filename" to the server using the POST command
 */

int  post_to_server(node,proto,filename)
  const char *node; int proto; const char *filename;
{
  int status;
  int resp = 0, retries = 0;
  FILE *fpr;
  char inpline[1024];

  if (!(fpr = fopen(filename,"r"))) {
    wmg("NNTP-POST: Cannot open file",0,filename);
    return(0);
    }

retry:
  if (!open_rem_chan(node,proto)) {
    wmg(0,node,client_msg);
    fclose(fpr);
    return(0);
    }
again:
  if (!nntp_write("POST") || !(resp = wait_net_response(CLIENT_TIMER,1))) {
    wmg("POST error",node,client_msg);
    fclose(fpr);
    return(0);
    }
#ifdef AUTH
  if (resp == 381 || resp == 480) {
    if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
    wmg("POST failed",node,client_msg);
    status = get_input(&usr_inp_dsc,c$dsc("NNTP-Username: "),&usr_inp_l);
    if ((status == RMS$_EOF) || (status == SMG$_EOF)) {
      fclose(fpr);
      return(0);
      }
    if ((status & 1) && usr_inp_l) {
      usr_inp[usr_inp_l] = '\0';
      sprintf(inpline,"AUTHINFO USER %.255s\n",usr_inp);
      }
    if (!nntp_write(inpline) || !(resp = wait_net_response(CLIENT_TIMER,1))) {
      wmg("AUTHINFO error",node,client_msg);
      fclose(fpr);
      return(0);
      }
    if (resp == 381) {
      if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
      wmg("POST error",node,client_msg);
      status = get_input(&usr_inp_dsc,c$dsc("NNTP-Password: "),&usr_inp_l);
      if ((status == RMS$_EOF) || (status == SMG$_EOF)) {
        fclose(fpr);
        return(0);
        }
      if ((status & 1) && (usr_inp_l)) {
        usr_inp[usr_inp_l] = '\0';
        sprintf(inpline,"AUTHINFO PASS %.255s\n",usr_inp);
        }
      if (!nntp_write(inpline) || !(resp = wait_net_response(CLIENT_TIMER,1))) {
        wmg("AUTHINFO error",node,client_msg);
        fclose(fpr);
        return(0);
        }
    if (resp == 281)
      goto again;
    }
    wmg("POST error",node,ibuf);
    fclose(fpr);
    return(0);
    }
#endif

  if (resp == 503) {  /* NNTP server timed out, retry */
    wmg("Timed out, retrying",node,client_msg);
    close_net();
    if (retries++ < 1) goto retry;
    }
  if (resp != 340) {
    if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
    wmg("POST Refused",node,ibuf);
    fclose(fpr);
    return(0);
    }
  while (fgets(inpline,sizeof(inpline)-1,fpr)) {
    int inpline_l = strlen(inpline);
    if (inpline_l>0 && inpline[inpline_l-1] == '\n') inpline[--inpline_l]='\0';
    if (*inpline == '.') memmove(inpline+1, inpline, ++inpline_l);
    if (!nntp_write(inpline)) {
      wmg("While sending message contents",node,client_msg);
      fclose(fpr);
      return(0);
      }
    }
  if (!nntp_write(".")) {
    wmg("While sending message terminating dot",node,client_msg);
    fclose(fpr);
    return(0);
    }
  if (!(resp = wait_net_response(CLIENT_TIMER,1))) {
    wmg("POST error",node,client_msg);
    fclose(fpr);
    return(0);
    }
  if (ibuf_l>0 && ibuf[ibuf_l-1] == '\n') ibuf[--ibuf_l] = '\0';
  if (resp != 240) {
    wmg("POST refused",node,ibuf);
    fclose(fpr);
    return(0);
    }
  wmg("POST accepted",node,ibuf);
  fclose(fpr);
  return(1);
}
