/*
**++
**  FACILITY:
**      NEWSADD
**
**  ABSTRACT:
**      This module parses input files according to the NEWS message format
**      standards and adds parsed items into the local NEWS database.
**
**      The local acceptance filter (as defined in NEWS.SYS) is applied
**      to the newsgroups and distribution control lines before adding to the
**      local database.
**
**      The module also passes items to the outgoing mailbox routines to
**      determine whether the item should be passed to downstream sites.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright, Geoff Huston © 1988,1989,1990,1991
**
**  VERSION:
**	V6.0-1	11/12/90	glass@mgi.com
**	The major conceptual change is that the headers stored in the itm array
**	are later used to actually write out the article.  This means that in
**	general, the itm data should NOT be modified after the scan_header reads
**	it in.  Specific examples of this were the newsgroup and control headers
**	which were modified for various purposes.  I create separate copies of this
**	stuff when necessary.  The body of the article is held in the array
**	artbuf (which is malloc'd and free'd with each batch file).  If the body is
**	more than ART_BUFSIZ bytes (I've set it to 8192), then the article will
**	be stored in scratchfile.
**
**	Here's a synopsis of the major changes:
**
**	1) mail_add_item has two new parameters.  ng is a char pointer to the
**	list of newsgroups to use for that item.  This list may be different
**	than itm[NEWSGROUPS] because of aliases and/or removing names without a
**	dot.  no_control is a flag that indicates that the control header should
**	not be processed.  This is used instead of setting the first byte of
**	itm[CONTROL] to zero.  For example, when mail_add_item recursively calls
**	itself, it passes a 1 as no_control to prevent a second processing of the
**	control header.
**
**	2) mail_add_item creates a duplicate copy of the newsgroup list passed
**	to it (*ng) so that it can change the newsgroup list to "control" or
**	"junk".
**
**	3) open_out_file is no longer used.
**
**	4) The routine out_header is not responsible for writing all the header
**	lines to the final article file.  It had a couple of minor changes to
**	deal with such things as the APPROVED header.
**
**	5) The routine out_body was created to write out the body of an article.
**	It gets the body from either the memory buffer or the scratchfile.
**
**	6) The routine out_data is no longer used.
**
**	7) A new extern routine called create_article was created.  This routine
**	is called when you want to create a file containing the news article.  The
**	routine takes two parameters.  fn is a pointer to a filename string.
**	This pointer should be NULL if you just want to create a scratch file version
**	of the article (used before calling parse_control_item or sys_local_accept
**	if the article exists only in memory).  The second parameter is xref, which
**	is a pointer to the xref header string if needed.  This is the one header
**	line that is not output by out_header.
**
**	8) The stripnodot routine got moved into the process_item routine because
**	of the constraints on modifying itm[NEWSGROUPS].
**
**	9) The routine append_line was created to handle incoming lines from the
**	body of an article.  It either appends the line to the memory buffer (artbuf)
**	or to the end of a temporary scratch file.
**
**	10) I added some code to get_line to try to handle lines that required
**	multiple calls to fgets.  It's not bullet proof, and only marginally better
**	than the old code.
**
**	11) I changed the nesting of some of the tests in scan to improve the
**	response for header lines that don't match one of the standard header lines.
**
**	12) I changed scan_header to use realloc instead of (malloc, strcpy, free).
**
**	V6.0-4	15-Apr-1991	glass@vivax.mgi.com
**	     1) I discovered a problem with adding batch files being created by the
**		NNTP_SERVER if /DELETE was enabled.  The problems was that the ADD routine
**		would close the file before deleting it.  There was a slight possibility
**		that the NNTP_SERVER might append another article to the file during that
**		time, and consequently the article would be lost.  The solution is to
**		delete the file before closing it.  With this change, once the ADD routine opens
**		the file for processing, there is no way that the NNTP_SERVER can ever
**		touch it again.
**	V6.1	 4-May-1991	gh
**		get_line now reads the !# rnews byte count and implements it
**	V6.1	 2-Feb-1992	rankin@eql.caltech.edu
**		lint cleanup from gcc -Wall
**	V6.1	 17-Feb-1992	mark@infocomm.com
**	      - bumped ART_BUFSIZE to 60000 since there is no particular reason
**		where another 100 pages of virtual memory will bother any
**		process which is doing add processing (i.e. the NEWS MANAGER
**		account and NOT general users)
**	      - corrected calls to fgets to make sure that the buffer read 
**		will always fit and be NUL terminated.
**	      - Added call to flush_downstream at the end of an ADD FILE
**		command.
**	      - Fixed bug dealing with input batch parsing when the input file
**		has been extracted from Mail and contained more than one
**		message.  This was fixed by moving the contents of the 
**		look_ahead buffer to the primary line buffer (add_inline)
**		in the routine init_context.
**	      - Timing test of various VAXCRTL I/O routines vs. file formats
**		reveals the following:
**		 1) RMS - Multi Buffer Count (Process or System) is not used
**		    when dealing with STREAM_LF files, so explicitly specifying
**		    a value for "mbc=" when opening a file is desired for more
**		    efficient file I/O.  This efficiency is realized by:
**			a) lower I/O counts
**			b) faster I/O throughput
**			c) lower CPU time.
**		 2) The VAX CRTL is MUCH more efficient at reading files which 
**		    are STREAM_LF in format.  For example, large file (1 mb)
**		    read with calls to fgets, with the file opened with a
**		    mbc=120, took 1.67 CPU seconds when the file as STREAM_LF
**		    and 6.64 seconds when it was Variable.
**		    Writing the same file with the same buffering, consumed 
**		    8.96 CPU seconds when the file was variable vs. 1.96 CPU
**		    seconds when the file was STREAM_LF.
**		 The general message is clear.  Stream_LF file format is 
**		 preferred, hence the files created here are no longer created
**		 as variable format files.
**
**	The general message is clear.  Stream_LF file format is preferred.
**
**	Well, I questioned this conclusion, and compared the VAXC RTL
**	I/O vs. direct calls to RMS $GET for the same files.  With a large
**	MBC, RMS could read the variable file in 3.82 CPU seconds vs the 
**	6.64 for VAXC RTL.  The Stream_LF file was read by RMS in 9.68 CPU
**	seconds verses 1.67.  Writing a variable format file with RMS took 
**	5.81 CPU seconds vs. the 8.96 for VAXCRTL.  Writing the Stream_LF 
**	file took 6.36 CPU seconds with RMS, vs the 1.96 CPU seconds for 
**	the VAXC RTL.
**
**	NOW, the real conclusion should be: depending on how you are 
**	going to read (most frequently), or write the data (VAXC RTL (fgets,
**	fputs)), or direct RMS calls (SYS$GET, SYS$PUT) will determine the
**	preferred file format.  VAXC RTL implies STREAM_LF, and RMS implies
**	VARIABLE/CR.
**
**	The changes I've implemented (in create_article), actually creates 
**	item files with Stream_LF format, since a cursory observation showed
**	that things use do_open_item to open an article followed by fgets to
**	read it.
**	
**	I have also explicitly specified values for mbc here in NEWSADD and
**	elsewhere, to help realize the performance gains noted above.  I may
**	have been too extravagant with my choices ov the mbc values in each
**	case, but I have selected values which seem appropriate to let the
**	runtime environment either read ahead (or write behind), enough so 
**	that most files are read in one or two I/O's.
**
**	V6.1	 26-Feb-1992	mark@infocomm.com
**	      - added status messages indicating why control messages may not
**		have been processed.
**	V6.1	23-Jul-1992	fenner@cmf.nrl.navy.mil
**	      - added sending system name to log file
**	V6.1	 9-Aug-1992	ewilts@galaxy.gov.bc.ca
**	      - 2 printf's to log file fixed
**	V6.1b8  10-Sep-1993	mark.martinec@ijs.si
**	      - instrument the I/O (VAXC RTL i/o) with
**	        error checking and reporting
**	V6.1b8	17-Sep-1993	bailey@genetics.upenn.edu
**	      - add /FeedThrough qualifier to Add File
**--
**/

#ifdef vaxc
#module NEWSADD "V6.1"
#endif

#define _NEWSADD_C
#define module_name "NEWSADD"

#define NEWCODE 0

#include "newsinclude.h"
#include "newsextern.h"

#if NNTP_CLIENT_ONLY

int do_add_net() { return(0); }

char *create_article(char *fn, char *xref) { return(NULL); }

#else

#define DEBUG 0
#define ART_BUFSIZE 60000

static
int scanline_m = 0,
    debug_switch = DEBUG,
    single_header_scan = 1;

static 
char itm[NHEADERS][10240],
     add_file_call = 1,
     add_inline[sizeof(itm[0])] = "",
     scratchfile[256],
     def_newsgroup[256],
     force_newsgroup[256],
     *scanline = 0,
     junk[] = "junk",
     mod[256],
     *artbuf = 0,
     *artptr,
     *unk_headers[MAX_UNK],
     *headers[] = {"path",
                   "newsgroups",
                   "subject",
                   "message-id",
                   "from",
                   "date",
                   "reply-to",
                   "sender",
                   "followup-to",
                   "expires",
                   "references",
                   "control",
                   "distribution",
                   "organization",
                   "keywords",
                   "summary",
                   "approved",
                   "supersedes",
                   "lines",
                   "xref",
                   "relay-version"},
     *fheaders[] ={"Path: ",
                   "Newsgroups: ",
                   "Subject: ",
                   "Message-ID: ",
                   "From: ",
                   "Date: ",
                   "Reply-To: ",
                   "Sender: ",
                   "Followup-To: ",
                   "Expires: ",
                   "References: ",
                   "Control: ",
                   "Distribution: ",
                   "Organization: ",
                   "Keywords: ",
                   "Summary: ",
                   "Approved: ",
                   "Supersedes: ",
                   "Lines: ",
                   "Xref: ",
                   "Relay-Version: "},
     *known_headers[NHEADERS];

static
int next_eof = 0,
    infile_eof = 0,
    infile_eom = 0,
    infile_hdr = 0,
    exec_switch = 0,
    first_get_call = 1,
    skip_blank_lines = 0,
    line_count = 0,
    n_stripping = 0,
    del_after = 0,
    del_after_save,
    execute_control = 1,
    exec_opt = 0,
    skip_loop_test,
    junk_it = 1,
    accept_it = 0,
    unk_index = 0,
    no_add = 0,
    mod_add = 0,
    control_squelch = 0,
    genid = 0,
    feed_through = 0,
    temp_file_error = 0,
    loc_junk = 0;

static
FILE *fpr = 0,
     *fpt = 0;

static char look_ahead[sizeof(add_inline)] = "";

static char *controlmsgs[] = {"",
			    "Can't create scratch file",
			    "No mail return address",
			    "Error creating new newsgroup",
			    "Missing newsgroup field",
			    "Non Approved",
			    "Invalid for local newsgroup",
			    "No such newsgroup",
			    "Can't access file",
			    "Missing sender address",
			    "Invalid format message",
			    "ihave/sendme not \"to\" me",
			    "ihave/sendme missing remote system",
			    "Unknown control message"};

/*
 *  get_line
 *
 *  Read the next line from the input file - Scan for message
 *  end delimiters of various known formats
 */

static int get_line()
{
  static int rnews_active = 0, start_of_line = 1;
  int sol_p;

  if (debug_switch) printf(">> ");

	/*------- if lookahead buffer in use then return this buffer immediately */
  if (*look_ahead) {
    strcpy(add_inline,look_ahead);
    *look_ahead = '\0';
    }

	/*------ if end of file then cleanup the flags */
  else if (!fgets(add_inline,sizeof(add_inline)-1,fpr)) {
    if (debug_switch) printf("\\eof\\\n");
    rnews_active = 0;
    infile_eof = infile_eom = start_of_line = 1;
    return(0);
    }

	/*------ at the start of the file ignore leading FF chars */
  if (first_get_call) {
    first_get_call = 0;
    start_of_line = 1;
    while (!strcmp(add_inline,"\f\n")) {
      if (!fgets(add_inline,sizeof(add_inline)-1,fpr)) {
        if (debug_switch) printf("\\eof\\\n");
        rnews_active = 0;
        infile_eof = infile_eom = start_of_line = 1;
        return(0);
        }
      }
    single_header_scan = 0;
    }

	/*------ if N encapsulation is active then strip off leading 'N'
		 Of course if there is none then thats a good EOM flag.
		 The start_of_line variable refers to whether this line
		 is a continuation of the prev line or not */
  else if (n_stripping) {
    if (start_of_line) {
      if (*add_inline == 'N') strcpy(add_inline,&add_inline[1]);
      else {
        infile_eom = 1;
        rnews_active = 0;
        n_stripping = 0;
        if (debug_switch) printf("\\eom\\\n");
	return(0);
        }
      }
    }

  sol_p = start_of_line;
  start_of_line = strchr(add_inline,'\n') != 0;

	/*------ if this is in the middle of an rnews batch then simply subtract
		 this line size from the outstanding count.
		 Attempt to resync with the stream with a lead-in of 20 chars. */
  if (rnews_active) {
    rnews_active -= strlen(add_inline);
    if (rnews_active < 20) rnews_active = 0;
    else {
      if (debug_switch) printf("%s",add_inline);
      return(1);
      }
    }

	/*------ If this is an #! rnews line then this is an EOM point. */
  if (sol_p && !strncmp(add_inline,"#! rnews ",9)) {
    sscanf(add_inline,"#! rnews %d",&rnews_active);
    infile_eom = 1;
    single_header_scan = 1;
    if (debug_switch) printf("\\eom\\\n");
    return(0);
    }

	/*------ If this is an N#! rnews then ditto with N stripping auto enabled */
  if (!n_stripping && sol_p && !strncmp(add_inline,"N#! rnews ",10)) {
    sscanf(add_inline,"N#! rnews %d",&rnews_active);
    infile_eom = 1;
    n_stripping = 1;
    single_header_scan = 1;
    if (debug_switch) printf("\\eom\\\n");
    return(0);
    }

	/*------ If the line began with a (now stripped) 'N' then there
		 is no point looking for a VMS Mail delimiter sequence */
  if (n_stripping) {
    if (debug_switch) printf("%s\n",add_inline);
    return(1);
    }

	/*------ VMS Mail extract delimiters - the sequences this code looks
		 for are /f/nFrom: (extract/head) and /f/n/Path: (extract/nohead
		 from MAIL) and also /f/n/Relay-Version: (ditto). The N encapsulated
		 versions of these last two cases are also detected. */
  if (sol_p && !strcmp(add_inline,"\f\n")) {
    if (!fgets(look_ahead,sizeof(look_ahead)-1,fpr)) return(1);
    if (   !strncmp(look_ahead,"From:",5)
        || !strncmp(look_ahead,"Path:",5)
	|| !strncmp(look_ahead,"Relay-Version:",14)) {
      infile_eom = 1;
      if (debug_switch) printf("\\eom\\\n");
      single_header_scan = strncmp(look_ahead,"From:",5);
      return(0);
      }
    if (   !strncmp(look_ahead,"NPath:",6)
	|| !strncmp(look_ahead,"NRelay-Version:",15)) {
      infile_eom = 1;
      n_stripping = 1;
      if (debug_switch) printf("\\eom\\\n");
      single_header_scan = 0;
      return(0);
      }
    }

	/*------ Otherwise nothing of significance was found I assume */
  if (debug_switch) printf("%s",add_inline);
  return(1);
}

/*
 *  mail_add_item
 *
 *  Add the temporary output file into the News database, creating new
 *  newsgroups where required
 */

static void mail_add_item(fn,ng,linecount,no_control)
  char *fn;
  char *ng;
  int linecount;
  int no_control;
{
  char newsgroup[sizeof(itm[0])], *m_id_bl, *m_id_br, *m_id, recvfrom[50];
  int  i;
  unsigned int cre_grp[100];

  for (i = 0; i < NHEADERS; ++i) known_headers[i] = &(itm[i][0]);
  mail_add_expiry = parse_expiry_date(itm[EXPIRES]);
  if (!*itm[MESSAGE_ID] && genid) strcpy(itm[MESSAGE_ID],gen_id());

  /*   If the message id string includes '<' and '>' characters, then strip off all stuff
       outside these delimiters  - GH 17/4/91 */
  m_id_bl = m_id = itm[MESSAGE_ID];
  if ((*m_id == '<') || (m_id_bl = strchr(m_id,'<'))) {
    if (m_id_bl != m_id) {
      strcpy(newsgroup,m_id_bl);
      strcpy(itm[MESSAGE_ID],newsgroup);
      }
    if ((m_id_br = strrchr(m_id,'>')) != 0) *++m_id_br = '\0';
    }
  strncpy(recvfrom,itm[PATH],sizeof(recvfrom)-1);
  recvfrom[sizeof(recvfrom)-1]='\0';
  chop_str(recvfrom,'!');
 
  strcpy(newsgroup,ng);
  if (*itm[CONTROL] && !no_control && *itm[MESSAGE_ID]) {
    strcpy(newsgroup,"control");
    if (   !skip_loop_test
        && (
#if SELF_PATH_CHECK
            path_match(itm[PATH],news_pathname) ||
#endif
	       check_id(itm[MESSAGE_ID])))
      printf("\tControl: Ignored (Loop Detected)\n");
    else {
      if (sys_local_accept(ng,itm[DISTRIBUTION])) {
        if (!fn) fn = create_article(NULL,NULL);
        if (fn) {
	  int parse_sts;
	  parse_sts = parse_control_item(known_headers,fn,exec_switch);
	  if (!(parse_sts&1))
	    printf("\tControl: %s\n", controlmsgs[parse_sts>>1]);
	  }
        }
      else printf("\tControl: Ignored (SYS filter)\n");
      }
    }
  printf("Add %s %s %s",recvfrom,itm[MESSAGE_ID],newsgroup);
  if (!strcmp(newsgroup,"junk") && !junk_it) {
    printf(": REJECT (/NOJUNK)\n");
    return;
    }
  if (!*newsgroup) {
    if (!junk_it) {
      printf(": REJECT (No Newsgroup)\n");
      return;
      }
    printf(": JUNK (No Newsgroup)");
    strcpy(newsgroup,junk);
    }
  if (!*itm[MESSAGE_ID]) {
    printf(": REJECT (No Message-ID)\n");
    return;
    }

#if SELF_PATH_CHECK
  if (!skip_loop_test && !mod_add && path_match(itm[PATH],news_pathname)) {
    printf(": REJECT (Path Loop)\n");
    return;
    }
#endif

  if (!sys_local_accept(newsgroup,itm[DISTRIBUTION]) && strcmp(newsgroup,junk)) {
    if (!junk_it) {
      printf(": REJECT (SYS filter)\n");
      if (feed_through) {
        if (!fn) fn = create_article(NULL,NULL);
        if (fn) sys_remote_send(itm[PATH],itm[NEWSGROUPS],itm[DISTRIBUTION],
                                fn,itm[MESSAGE_ID],!mod_add);
        }
      return;
      }
    printf(": JUNK (SYS filter)");
    strcpy(newsgroup,junk);
    }
  do_new_group(newsgroup,0,cre_grp);
  if ((!*cre_grp) && (strcmp(newsgroup,junk))) {
    if (!junk_it) {
      printf(": REJECT (No local Newsgroup match)\n");
      if (feed_through) {
        if (!fn) fn = create_article(NULL,NULL);
        if (fn) sys_remote_send(itm[PATH],itm[NEWSGROUPS],itm[DISTRIBUTION],
                                fn,itm[MESSAGE_ID],!mod_add);
        }
      return;
      }
    printf(": JUNK (No local Newsgroup match)");
    strcpy(newsgroup,junk);
    do_new_group(newsgroup,0,cre_grp);
    }
  if ((!*cre_grp) && (!strcmp(newsgroup,junk))) printf(": REJECT (No JUNK)");
  *itm_fname = '\0';
  status = 0;
  if (*cre_grp) {
    status = do_new_item((unsigned int *) cre_grp,itm[MESSAGE_ID],itm[SUBJECT],
			 itm[FROM],fn,1,skip_loop_test,linecount,0);
    if (status != 0) {
      if (   (status == RMS$_DUP)
	  || (status == (RMS$_DUP|0XB0000000))
	  || (status == (RMS$_NORMAL|0XB0000000))
          || (status == RMS$_NORMAL)) {
          printf(": REJECT (Duplicate - Cannot add Locally: Loop?)\n");
        return;
        }
      else {
        if (strcmp(newsgroup,junk)) {
          if (!junk_it) {
            if (!*itm_fname && !fn && !(fn = create_article(NULL,NULL))) return;
            sys_remote_send(itm[PATH],itm[NEWSGROUPS],itm[DISTRIBUTION],
                            ((*itm_fname) ? itm_fname : fn),itm[MESSAGE_ID],!mod_add);
            printf(": REJECT (%s)\n",no_new_item);
            return;
            }
          printf(": JUNK (%s)\n",no_new_item);
          mail_add_item(fn,junk,linecount,1);
          return;
          }
        else printf(": REJECT (%s)",no_new_item);
        }
      }
    else if (*itm[SUPERSEDES]) {
      char *id,
           *ide,
           *mail_sender = itm[SENDER],
           net_sender[132],
	   save_itm_fname[256];

      strcpy(save_itm_fname,itm_fname);
      if ((id = strchr(itm[SUPERSEDES],'<')) != 0
       && (ide = strchr(id,'>')) != 0) {
        *(ide + 1) = '\0';
        if (!*mail_sender) mail_sender = itm[FROM];
        if ((ide = strchr(mail_sender,'<')) != 0) {
          strcpy(net_sender,++ide);
          chop_str(net_sender,'>');
          chop_str(net_sender,'\n');
          }
        else {
          char *cp = net_sender,
               *cp1 = mail_sender;

          while (isspace(*cp1)) cp1++;
          while ((*cp1) && (!isspace(*cp1))) *cp++ = *cp1++;
          *cp = '\0';
          chop_str(net_sender,'(');
          }
        if (!del_id(id,net_sender)) hist_add(id);
        printf(" (Supersedes %s)",itm[SUPERSEDES]);
        }
      strcpy(itm_fname,save_itm_fname);
      }
    }
  if (*itm_fname) sys_remote_send(itm[PATH],itm[NEWSGROUPS],itm[DISTRIBUTION],itm_fname,itm[MESSAGE_ID],!mod_add);
  else {
    if (strcmp(newsgroup,junk)) {
      if (!junk_it) {
        if (!fn && !(fn = create_article(NULL,NULL))) return;
        sys_remote_send(itm[PATH],itm[NEWSGROUPS],itm[DISTRIBUTION],fn,itm[MESSAGE_ID],!mod_add);
        printf(": REJECT\n");
        return;
        }
      printf("\n");
      mail_add_item(fn,junk,linecount,1);
      return;
      }
    }
  printf("\n");
}

/*
 *  out_header
 *
 *  Output header lines
 */

static void
fout_line(s,f)
  FILE *f;
  char *s;
{
  char *c, *cp, save_c;

  while (strlen(s) > 132) {
    save_c = s[78];
    s[78] = '\0';
    cp = &s[30];
    if (   (c = strrchr(cp,' '))
        || (c = strrchr(cp,'!'))
        || (c = strrchr(cp,','))
        || (c = strrchr(cp,';')))
      ++c;
    else c = &s[78];
    s[78] = save_c;
    save_c = *c;
    *c = '\0';
    fputs(s,f); _ck_put(f);
    fputs("\n ",f); _ck_put(f);
    *c = save_c;
    s = c;
    }
  fputs(s,f); _ck_put(f);
}

static void out_header(fpl)
  FILE *fpl;
{
  char oline[1024];
  int i;

  sprintf(oline,"%s%s %s VAX/VMS %s; site %s\n",fheaders[RELAY_VERSION],
        NEWS_VERSION,NEWS_DDATE, VMS_VERS,news_node);
  fout_line(oline,fpl);
  for (i = 0; i < OHEADERS; ++i) {
    if (i == PATH) {
      fputs(fheaders[PATH],fpl); _ck_put(fpl);
      if (!mod_add) {
        fputs(news_pathname,fpl); _ck_put(fpl);
        fputc('!',fpl); _ck_put(fpl);
        }
      fputs(itm[PATH],fpl); _ck_put(fpl);
      fputc('\n',fpl); _ck_put(fpl);
      continue;
      }
    if ((i == APPROVED) && mod_add && !itm_approved) continue;
    if (*itm[i]) {
      fputs(fheaders[i],fpl); _ck_put(fpl);
      if ( i == NEWSGROUPS || i == FOLLOWUP_TO )
	  { fputs(itm[i],fpl); _ck_put(fpl); }
      else
	  fout_line(itm[i],fpl);
      fputc('\n',fpl); _ck_put(fpl);
      }
    }
  for (i = 0; i < unk_index; ++i) {
    fout_line(unk_headers[i],fpl);
    fputc('\n',fpl); _ck_put(fpl);
    }
}

/*
 *  out_body
 *
 *  Write the body of an article out to a file
 */

static void out_body(fpl)
  FILE *fpl;
{
  char oline[1024];

  oline[sizeof(oline)-1] = '\0';
  if (!fpt) { fputs(artbuf,fpl); _ck_put(fpl); }
  else {
    fseek(fpt,0,0);
    while (fgets(oline,sizeof(oline)-1,fpt))
      { fputs(oline,fpl); _ck_put(fpl); }
    }
}

/*
 *  create_article
 *
 *  Create an article file from the headers and body stored in memory
 *
 *  return NULL on error, otherwise return ptr to filename
 */

char *create_article(fn,xref)
  char *fn;			/* filename - use scratchfile if NULL */
  char *xref;			/* xref header line for article */
{
  FILE *fpw;

  if (!fn) fn = scratchfile;
  sysprv();
  if (!(fpw = fopen(fn,"w","mbc=20"))) {
    nosysprv();
    _ck_open_w(fpw,fn);
    printf("\tError: Add - Cannot open output file %s\n",fn);
    del_after = 0;
    return(NULL);
    }
  if (fn != scratchfile) chmod(fn,0755);
  nosysprv();
  out_header(fpw);
  fout_line(fheaders[LINES],fpw);
  fout_line(itm[LINES],fpw);
  fputc('\n',fpw); _ck_put(fpw);
  if (xref) {
    fout_line(xref,fpw);
    fputc('\n',fpw); _ck_put(fpw);
    }
  fputc('\n',fpw); _ck_put(fpw);
  out_body(fpw);
  if (fclose(fpw)) _ck_close(fpw);
  return(fn);
}

/*
 *  process_item
 *
 *  Rebuild an item file with all necessary headers
 */

#if JUNKNODOT
static void stripnodot(s)
  char *s;
{
  char *rtnnames, *comma, *nxtname = s;

  rtnnames = (char *) news_malloc(strlen(s) + 1);
  *rtnnames = '\0';
  do {
    comma = chop_str_plus(nxtname,',');
    if (strchr(nxtname,'.') || !strcmp(nxtname,"test")) {
      if (*rtnnames) strcat(rtnnames,",");
      strcat(rtnnames,nxtname);
      }
    } while ((nxtname = comma) != 0);
  strcpy(s,rtnnames);
  news_free(rtnnames);
}
#endif

static void process_item()
{
  char *xx,
       *cp1,
       *cp2,
       lg[132],
       *pa,
       newsgroups[sizeof(itm[0])],
       post_address[132],
       mod_group[132];
  unsigned int    g;
  int     self_mod = 0;
  int     no_post = 0;

  *post_address = '\0';
  strcpy(newsgroups,itm[NEWSGROUPS]);
  lower_case(newsgroups);
  strcpy(newsgroups,(char *) aliases(newsgroups,-1));
#if JUNKNODOT
  if (net_news && !*force_newsgroup) {
    stripnodot(newsgroups);
    if (!*newsgroups) {
      printf("Item rejected <JUNKNODOT>\n");
      if (!mod_add) return;
      }
    }
#endif
  if (mod_add) {
    strcpy((xx = (char *) news_malloc(strlen(newsgroups) + 1)),newsgroups);
    cp1 = xx;
    while ((cp1) && (*cp1)) {
      cp2 = chop_str_plus(cp1,',');
      util_cvrt(lg,cp1);
      if ((g = ga_exact_name(lg))
          && (ga[g]->grp_flags & NEWS_M_MAILMODERATE)) {
        if (strcmp(mod,(pa = moderator_address(ga[g]->grp_name)))) {
          strcpy(mod_group,ga[g]->grp_name);
          strcpy(post_address,pa);
          }
        else {
          self_mod = 1;
          *post_address = '\0';
          strcpy(itm[APPROVED],mod);
          itm_approved = 1;
          }
        }
      else if (g && (ga[g]->grp_flags & NEWS_M_MOD_ACCESS)) {
        self_mod = 1;
        *post_address = '\0';
        strcpy(itm[APPROVED],mod);
        itm_approved = 1;
        }
      cp1 = cp2;
      }
    news_free(xx);
    if (!strcmp(newsgroups,"junk")) {
      *post_address = '\0';
      no_add = 1;
      no_post = 1;
      }
    else if (!self_mod) {
      printf("Add %s, %s\n",itm[FROM],itm[MESSAGE_ID]);
      printf("Item does not reference moderated newsgroup:\n");
      printf("  Newsgroups: %s\n",newsgroups);
      printf("Item NOT added to News\n");
      no_add = 1;
      *post_address = '\0';
      no_post = 1;
      }
    else if (*post_address)  {
      printf("Add %s, %s\n",itm[FROM],itm[MESSAGE_ID]);
      printf("Additional moderated newsgroup specified: %s\n",mod_group);
      printf("  Newsgroups: %s\n",newsgroups);
      printf("  Next Moderator: %s\n",post_address);
      sprintf(err_oline,"Post item to next moderator ? [y]");
      status = get_input(&usr_inp_dsc,c$dscl(&err_oline_d),&usr_inp_l);
      if (status == SMG$_EOF) *usr_inp = 'n';
      if (!((!usr_inp_l) || (tolower(*usr_inp) == 'y'))) {
        *post_address = '\0';
        no_post = 1;
        }
      no_add = 1;
      }
    }
  if (!no_post) {
    sprintf(itm[LINES],"%d",line_count);
    if (!no_add) mail_add_item(NULL,newsgroups,line_count,control_squelch);
    else if (mod_add && *post_address) {
      if (create_article(scratchfile,NULL)) {
        sprintf(err_oline,
          "Post to next Moderator (%s)\nSpawning MAIL task ..\n",
          post_address);
        call_mail(scratchfile,itm[SUBJECT],add_transform(post_address),
          err_oline);
        }
      }
    }
}

/*
 *  init_strings, init_context
 *
 *  Reset all globals at the start of each item
 */

static void init_strings()
{
  struct tm *stm;
  int cur_time;
  char *p;

  for (cur_time = 0; cur_time < NHEADERS; ++cur_time) *itm[cur_time] = '\0';
  strcpy(itm[FROM],"** Sender Unknown **");
  time((time_t *) &cur_time);
  p = ctime((time_t *) &cur_time);
  p += 4;
  stm = localtime((time_t *) &cur_time);
  sprintf(itm[DATE],"%d %.3s %d %02d:%02d:%02d %s",stm->tm_mday,p,stm->tm_year,
      stm->tm_hour,stm->tm_min,stm->tm_sec,news_timezone);
  if (*force_newsgroup) strcpy(itm[NEWSGROUPS],force_newsgroup);
  else strcpy(itm[NEWSGROUPS],def_newsgroup);
  no_add = loc_junk;
  strcpy(itm[SUBJECT],"<None>");
  *itm[MESSAGE_ID] = '\0';
  if (!mod_add) strcpy(itm[PATH],usr_username);
  else sprintf(itm[PATH],"%s!%s",news_pathname,usr_username);
}

static void init_context()
{
  int i;

  init_strings();
  artptr = artbuf;
  *artptr = '\0';
  temp_file_error = 0;
  for (i = 0; i < unk_index; ++i) news_free(unk_headers[i]);
  unk_index = 0;
  skip_blank_lines = 1;
  line_count = 0;
  n_stripping = 0;
  itm_approved = accept_it;

	/*------- if lookahead buffer in use then return this buffer immediately */
  if (*look_ahead) {
    strcpy(add_inline,look_ahead);;
    *look_ahead = '\0';
    }

  if (fpt) {
    if (fclose(fpt)) _ck_close(fpt);
    fpt = 0;
    if (delete(scratchfile)) _ck_delete(scratchfile);
    }
}

/*
 * append_line
 *
 * append a line from the body of an article to either the memory buffer
 * or the temporary file
 */

static int append_line(buf)
  char *buf;			/* pointer to stuff to append to article */
{
  int len;
  char *c;

  if (temp_file_error) return(1);

  if (skip_blank_lines) {
    c = buf;
    while (isspace(*c)) c++;
    if (!*c) return (0);
    skip_blank_lines = 0;
    }

	/*------- if lookahead buffer in use then return this buffer immediately */
  if (*look_ahead) {
    strcpy(add_inline,look_ahead);;
    *look_ahead = '\0';
    }

  if (fpt) {
    fputs(buf,fpt); _ck_put(fpt);
    return(0);
    }

  len = strlen(buf);
  if (ART_BUFSIZE - (artptr - artbuf) < len + 1) {
    if (!(fpt = fopen(scratchfile,"w+", "mbc=32"))) {
      _ck_open_w(fpt,scratchfile);
      printf("\tError: Add - Cannot open scratch file %s\n",scratchfile);
      temp_file_error = 1;
      del_after = 0;
      return(1);
      }
    fputs(artbuf,fpt); _ck_put(fpt);
    artptr = artbuf;
    *artptr = '\0';
    fputs(buf,fpt); _ck_put(fpt);
    return(0);
    }
  strcpy(artptr,buf);
  artptr += len;
  return(0);
}

/*
 *  scan
 *
 *  Parse a header line, storing only acceptable NEWS headers
 */

static int news_header;

static int scan(s)
  char *s;
{
  char head_word[132], *p = s, *h = head_word;
  int i;

  while (*p != ':' && isgraph(*p)) p++;
/*	The following check is deliberately omitted - the strict check
	for valid header keywords: "<word><colon><space>" is violated
	by some backward systems who omit the space following the colon.
	The code as it stands now gently ignores the problem rather than
	junking on a malformed header  - gh  15/4/91		

  if ((*p == ':') && (isspace(*(p+1)))) {

  is changed to:...
*/

  if (*p == ':') {
    *p = '\0';
    strcpy(h,s);
    *p++ = ':';
    if (!*h) return(0);
    while (isspace(*p)) p++;
    lower_case(h);
    i = 0;
    while (i < NHEADERS) {
      if (!strcmp(h,headers[i])) {
        if (i == NEWSGROUPS) news_header = 1;
        if ((!(*force_newsgroup && (i == NEWSGROUPS))) &&
            ((i != PATH) || !mod_add)) {
          news_assert(strlen(p) < sizeof(itm[0]));
          strcpy(itm[i],p);
          }
        if (i == NEWSGROUPS) no_add = 0;
        break;
        }
      ++i;
      }
    if (i == NHEADERS) {
      if (!strcmp(h,"subj") && !strcmp(itm[SUBJECT],"<None>")) {
        i = SUBJECT;
        news_assert(strlen(p) < sizeof(itm[0]));
        strcpy(itm[i],p);
        }
      else if (*s == 'N') {
        ++h;
        i = 0;
        while (i < NHEADERS) {
          if (!strcmp(h,headers[i])) break;
          ++i;
          }
        if (i == NEWSGROUPS) news_header = 1;
        if (i < NHEADERS) {
          int j,k = 0;
          char *c;
           init_strings();
          for (j = 0; j < unk_index; ++j) {
            c = &(unk_headers[j][0]);
            if (*c++ == 'N') {
              do *(c - 1) = *c; while (*c++);
              unk_headers[k++] = unk_headers[j];
              }
            else news_free(unk_headers[j]);
            }
          unk_index = k;
          if ((!(*force_newsgroup && (i == NEWSGROUPS)))
              && ((i != PATH) || !mod_add)) {
            news_assert(strlen(p) < sizeof(itm[0]));
            strcpy(itm[i],p);
            }
          if (i == NEWSGROUPS) no_add = 0;
          n_stripping = 1;
          }
        }
      }
    if (i == APPROVED) itm_approved = 1;
    if ((i == NHEADERS) && (unk_index < MAX_UNK)) {
      if ((unk_index) && (strlen(s) >= strlen(unk_headers[unk_index - 1]))
          && (!strncmp(unk_headers[unk_index-1],s,strlen(unk_headers[unk_index - 1]))))
        news_free(unk_headers[--unk_index]);
      strcpy((unk_headers[unk_index++] = (char *) news_malloc(strlen(s) + 1)),s);
      }
    return(1);
    }
  else {
    if (!strncmp(s,"From ",5)) return(1);
    else if ((*p == ':') && !*(p+1)) return(1);
    return(0);
    }
}

/*
 *  
scan_header
 *
 *  pick off a set of header lines
 */

static int scan_header()
{
  char *cp1;
  int recall = 0;

  if (infile_eof || infile_eom) return(0);

  cp1 = chop_str(add_inline,'\n');
  if (scanline_m) news_free(scanline);
  scanline = news_malloc(scanline_m = (strlen(add_inline)+3));
  strcpy(scanline,add_inline);
  if (*scanline && !scan(scanline)) {
    if (cp1) strcat(scanline,"\n");
    append_line(scanline);
    scanline_m = 0;
    news_free(scanline);
    return(0);
    }
  else if (*scanline) ++recall;

  while (*add_inline) {
    int s_n_s = n_stripping,
        scan_res;

    get_line();
    if (infile_eof || infile_eom) break;
    scan_res = (*scanline && !scan(scanline));
    if (!s_n_s && n_stripping && *add_inline == 'N') {
      cp1 = &add_inline[1];
      do *(cp1 - 1) = *cp1; while (*cp1++);
      }
    cp1 = chop_str(add_inline,'\n');
    if ((*add_inline == ' ') || (*add_inline == '\t')) {
      if (scan_res) {
        strcat(scanline,"\n");
        append_line(scanline);
        if (cp1) strcat(add_inline,"\n");
        append_line(add_inline);
        scanline_m = 0;
        news_free(scanline);
        return(0);
        }
      else if (*scanline) ++recall;
      else {
        if (cp1) strcat(add_inline,"\n");
        append_line(add_inline);
        scanline_m = 0;
        news_free(scanline);
        return(0);
        }

      cp1 = add_inline;
      while (isspace(*cp1)) cp1++;
      if (*cp1) {
        scanline = news_realloc(scanline,(scanline_m += (strlen(cp1) + 3)));
        strcat(scanline,cp1);
        }
/*		deleted  - BITNET-fed sites please note this will need
                to be reinserted again if your feed site thinks that an empty
		line is sent as 80 blanks
                              
      else {
        scanline_m = 0;
        news_free(scanline);
        return(news_header ? 0 : recall);
        }
*/
      }
    else {
      if (scan_res) {
        strcat(scanline,"\n");
        append_line(scanline);
        if (cp1) strcat(add_inline,"\n");
        append_line(add_inline);
        return(0);
        }
      else if (*scanline) ++recall;
      news_free(scanline);
      scanline = news_malloc(scanline_m = (strlen(add_inline) + 3));
      strcpy(scanline,add_inline);
      }
    }
  if (*scanline) scan(scanline);
  scanline_m = 0;
  news_free(scanline);
  return(news_header ? 0 : recall);
}

/*
 *  scan_data
 *
 *  pick off text body of message
 */

static void
scan_data()
{
  if (!infile_eom && !infile_eof) {
    for (;;) {
      get_line();
      if (infile_eom || infile_eof) break;
      if (strchr(add_inline,'\n')) line_count++;
      append_line(add_inline);
      }
    }
}

#if !NEWCODE
/*
 *  add_file
 *
 *  Read in file "fnam" into the news system.
 */

static int add_file(fnam, exec_switch)
  char *fnam;
  int exec_switch;
{
  struct stat statb;
  int retval = 1;	/* changed from 0 to force auto deletion of
			   empty batch files - gh 8/2/91 V6.0-3 */

  if (!*fnam) return(printf("\tError: Add - No filename specified\n"),0);
  if (!(fpr = fopen(fnam,"r","mbc=120"))) {
    _ck_open_r(fpr,fnam);
    printf("\tError: Add - Cannot read %s\n",fnam);
    return(0);
    }

  fstat(fileno(fpr), &statb);
  printf("\nAdd - Reading file: %s (%d bytes)\n\n",fnam, statb.st_size);
  *add_inline = *look_ahead = '\0';
  infile_eof = next_eof = n_stripping = 0;
  artbuf = news_malloc(ART_BUFSIZE);
  first_get_call = 1;
  get_line();
  while (!infile_eof) {
    infile_eom = 0;
    init_context();
    if (!strncmp(add_inline,"N#! rnews ",10)) {
      n_stripping = 1;
      get_line();
      }
    if (!strncmp(add_inline,"#! rnews ",9)) get_line();
    news_header = 0;

    if ( debug_switch ) printf("<Headers:>\n");

    while (scan_header()) get_line();

    if ( debug_switch ) printf("<Body:>\n");

    skip_blank_lines = 0;
    scan_data();

    if ( debug_switch ) printf("<EOM>\n");

    if (!temp_file_error) {
      if (!no_add || mod_add) process_item();
      else {

        if ( debug_switch ) printf("<<null msg>>\n");
        }
      }

    if (fpt) {
      if (fclose(fpt)) _ck_close(fpt);
      fpt = 0;
      }
    if (delete(scratchfile)) /* _ck_delete(scratchfile) */ ;
    ++retval;
    }
  if (retval && del_after)
    if (delete(fnam)) _ck_delete(fnam);
  if (fclose(fpr)) _ck_close(fpr);
  if (fpt) {
    if (fclose(fpt)) _ck_close(fpt);
    fpt = 0;
    }
  if (delete(scratchfile)) /* _ck_delete(scratchfile) */;
  news_free(artbuf);
  return(retval);
}
#endif

int do_add_mail()
{
  return(0);
}

#if NEWCODE
/*
 *    get_head: get one header line from the input file. Continuation lines
 *    are concatenated into one long line.
 */
get_head( char *line, FILE *infile ) {
    int ch;
    char cline[1025];
    extern char *mygets();
/*
 *    get the next header line from the input file.
 */
    if ( !mygets(cline,1024,infile) ) {
	infile_eom = 1;                   /* end of file reading headers */
        infile_eof = 1;
        infile_hdr = 0;  
	news_free(line);
        return 0;      
    }
/*
 *    if this line is null, it marks the end of the headers.
 */
    if ( *cline == '\n' ) {
	infile_hdr = 0;
	news_free(line);
	return 0;   /* return false if no more headers */
    }
/*
 *    we have a real header line, so put it in the output buffer.
 */
    strcpy(line=news_realloc(line,strlen(cline)+1),cline);
/*
 *    if the next input line starts with a blank or tab, it is a
 *    continuation of this line, so concatenate it onto the end of this line.
 */
    for ( ungetc(ch=fgetc(infile),infile);
          ch == ' ' || ch == '\t';
          ungetc(ch=fgetc(infile),infile) ) {
	if ( !mygets(cline,1024,infile) ) {
	    infile_eom = 1;                   /* end of file reading headers */
	    infile_eof = 1;
	    infile_hdr = 0;  
	    return line;      
	}
	chop_str(line,'\n');                  /* trim off old newline */
	strcat(line=news_realloc(line,strlen(line)+strlen(cline)+1),cline);
    }
    return line;
}
/*
 *    You may have been wondering why I used mygets rather than the standard
 *    C library routine gets.  I started out that way, but there seems to be
 *    a problem in the C library when using ungetc, getc, and gets together
 *    on the same file.  It was easier to just write my own than try to find
 *    out what the problem was with DEC's routine.
 */
char *mygets( char *line, int max, FILE *fd ) {
    int i,ch;
/*
 *    read up to max characters from the input file
 */
    for ( i=0; i<max-1; ++i ) {
/*
 *    if there are no more characters, exit
 */
	if ( (ch=fgetc(fd)) == -1 ) {
	    line[i] = '\0';
	    infile_eof = 1;
            infile_eom = 1;
	    return i;
	}
/*
 *    stuff the current character in the users line buffer
 */
	line[i] = ch;
/*
 *    if this is a newline, then we are done for now, so return.
 */
	if ( ch == '\n' ) {
	    line[i+1] = '\0';          /* put EOS on the end of the string */
	    return line;
	}
    }
/*
 *    if we get here, the line was longer than the users buffer.
 *    put and EOS on the string, and return.
 */
    line[max] = '\0';
    return line;
}

static char *scanmsgs[] = {"Accepted",
		   "PARSE-ERR: No input filename",
		   "PARSE-ERR: Cannot read input file",
		   "PARSE-ERR: Cannot open scratch output file",
		   "PARSE-ERR: Headers start with continuation (space) character",
		   "PARSE-ERR: Header keyword line does not contain colon delimiter",
		   "PARSE-ERR: Header keyword contains spaces",
		   "PARSE-ERR: Repeated Header keyword",
		   "PARSE-ERR: Input file empty"};

static int o_line_count, o_body_line_count;

static int add_item(infile)
  FILE *infile;
{
  int i_line_count = 0, o_byte_count = 0,
      retflags = 0, i;
  char *tmpstr, *inbuf, *cp, *cp1, recvfrom[50];
  time_t cur_time;
  struct tm *stm;
  FILE *outfile;

  /*----- init local variables */
  o_line_count = o_body_line_count = 0;
  for (i = 0; i < NHEADERS; ++i) {
    if ( known_headers[i] )
      news_free(known_headers[i]);
    known_headers[i] = 0;
    }

  sprintf(scratchfile,"SYS$SCRATCH:ADD_%X.ITM",getpid());
  if (!(outfile = fopen(scratchfile,"w","mbc=32"))) {
    _ck_open_w(outfile,scratchfile);
    return(NOSCRFILEACCESS);
    }

  /*----- write out the relay version immediately */
  inbuf = news_malloc(strlen(NEWS_VERSION)+1
                    + strlen(NEWS_DDATE)+1
                + 8 + strlen(VMS_VERS)+1
                + 6 + strlen(news_node)+1);
  sprintf(inbuf,"%s %s VAX/VMS %s; site %s",NEWS_VERSION,NEWS_DDATE, VMS_VERS,news_node);
  strcpy((known_headers[RELAY_VERSION] = news_malloc(strlen(inbuf) + 1)),inbuf);
  if (fprintf(outfile,"%s%s\n",fheaders[RELAY_VERSION],inbuf) < 0) _ck_put(outfile);
  ++o_line_count;
  o_byte_count += strlen(fheaders[RELAY_VERSION]) + strlen(inbuf) + 1;

  /*----- read the input file */
  while ( (inbuf=get_head(inbuf,infile)) ) {
    ++i_line_count;

    /*----- remove trailing newline */
    chop_str(inbuf,'\n');

    /*----- line should be of the format "word: field" */
    if (!(cp = strchr(inbuf,':'))) {

      /*----- if not, then thats an error */
      if (fclose(outfile)) _ck_close(outfile);
      if (delete(scratchfile)) _ck_delete(scratchfile);
      news_free(inbuf);
      return(BADHD_NOCOLON);
      }

    /*----- clear off the colon temporarily */
    *cp = '\0';

    /*----- map the keyword to lower case and check for recognised header */
    strcpy(tmpstr=news_malloc(strlen(inbuf)+1),inbuf);
    lower_case(tmpstr);
    for (i = 0; i < NHEADERS; ++i) if (!strcmp(tmpstr,headers[i])) break;
    news_free(tmpstr);

    /*----- unknown keyword ? */
    if (i >= NHEADERS) {
      /*----- check for a blank char in the keyword */
      cp1 = inbuf;
      while (*cp1) {
	if (!isprint(*cp1)) {
	  if (fclose(outfile)) _ck_close(outfile);
	  if (delete(scratchfile)) _ck_delete(scratchfile);
 	  news_free(inbuf);
	  return(BADHD_NOTAKEYWORD);
	  }
	++cp1;
	}

      /*----- put back the colon - and write the line out */
      *cp = ':';
      fputs(inbuf,outfile); _ck_put(outfile);
      fputc('\n',outfile); _ck_put(outfile);
      o_byte_count += strlen(inbuf) + 1;
      ++o_line_count;
      continue;
      }	/*----- end: non recognised keyword */

    if (known_headers[i]) {
      news_free(known_headers[i]); /* use last copy of duplicate headers */
      }

    /*----- ignore relay-version and xref headers */
    if ((i == RELAY_VERSION) || (i == XREF)) {
      continue;
      }

    /*----- strip off leading & tailing spaces */
    ++cp;
    strip_compress(cp);

    /*----- insert local system in path line */
    if (i == PATH) {
      known_headers[i] = (char *) news_malloc(strlen(news_pathname) + strlen(cp) + 2);
      sprintf(known_headers[i],"%s!%s",news_pathname,cp);
      }
    /*----- write out header */
    else {
      strcpy((known_headers[i] = (char *) news_malloc(strlen(cp) + 1)),cp);
      if (fprintf(outfile,"%s%s\n",fheaders[i],known_headers[i]) < 0) _ck_put(outfile);
      o_byte_count += strlen(fheaders[i]) + strlen(known_headers[i]) + 1;
      ++o_line_count;
      }
    } /*----- end: read header while loop */

  /*----- finished reading the headers - now supply any missing headers using local defaults */

  /*----- first compress and strip the header strings */
  for (i = 0; i < NHEADERS; ++i) {
    if (known_headers[i]) {

      /*----- certain headers need all blanks removed */
      if ((i == PATH) || (i == NEWSGROUPS) || (i == MESSAGE_ID) || (i == DISTRIBUTION))
        blank_strip(known_headers[i]);

      else strip_compress(known_headers[i]);
      }
    }

  /*----- Use locally generated default values for missing strings */
  if (!known_headers[PATH]) {
    retflags |= NOPATH;
    tmpstr=news_malloc(strlen(news_pathname)+6);
    sprintf(tmpstr,"%s!news",news_pathname);
    if (fprintf(outfile,"%s%s\n",fheaders[PATH],tmpstr) < 0) _ck_put(outfile);
    o_byte_count += strlen(fheaders[PATH]) + strlen(tmpstr) + 1;
    ++o_line_count;
    strcpy((known_headers[PATH] = (char *) news_malloc(strlen(inbuf) + 1)),tmpstr);
    news_free(tmpstr);
    }

  if (!known_headers[NEWSGROUPS]) {
    retflags |= NOGROUPS;
    strcpy(inbuf,"junk");
    strcpy((known_headers[NEWSGROUPS] = (char *) news_malloc(strlen(inbuf) + 1)),inbuf);
    }

  if (!known_headers[SUBJECT]) {
    retflags |= NOSUBJ;
    strcpy((known_headers[SUBJECT] = (char *) news_malloc(1)),"");
    if (fprintf(outfile,"%s\n",fheaders[SUBJECT]) < 0) _ck_put(outfile);
    o_byte_count += strlen(fheaders[SUBJECT]) + 1;
    ++o_line_count;
    }

  if (!known_headers[MESSAGE_ID] && genid) {
    retflags |= NOID;
    tmpstr = news_malloc(MAXIDLEN);
    strcpy(tmpstr,gen_id());
    strcpy((known_headers[MESSAGE_ID] = (char *) news_malloc(strlen(tmpstr) + 1)),tmpstr);
    if (fprintf(outfile,"%s%s\n",fheaders[MESSAGE_ID],tmpstr) < 0) _ck_put(outfile);
    o_byte_count += strlen(fheaders[MESSAGE_ID]) + strlen(tmpstr) + 1;
    ++o_line_count;
    news_free(tmpstr);
    }
  else if (known_headers[MESSAGE_ID]) {
    char *cp = known_headers[MESSAGE_ID];

    if (*cp != '<') retflags |= BADID;
    if (strchr(cp,' ') || strchr(cp,'\t')) retflags |= BADID;
    if ((!(cp = strrchr(cp,'>'))) || *++cp) retflags |= BADID;
    }
  else {
    strcpy((known_headers[MESSAGE_ID] = (char *) news_malloc(1)),"");
    retflags |= BADID;
    }
  if (!known_headers[FROM]) {
    retflags |= NOFROM;
    tmpstr = news_malloc(strlen(Node_address)+6);
    sprintf(tmpstr,"news@%s",Node_address);
    strcpy((known_headers[FROM] = (char *) news_malloc(strlen(tmpstr) + 1)),tmpstr);
    if (fprintf(outfile,"%s%s\n",fheaders[FROM],tmpstr) < 0) _ck_put(outfile);
    o_byte_count += strlen(fheaders[FROM]) + strlen(tmpstr) + 1;
    ++o_line_count;
    news_free(tmpstr);
    }

  if (!known_headers[DATE]) {
    retflags |= NODATE;
    time(&cur_time);
    cp = ctime(&cur_time);
    cp += 4;
    stm = localtime(&cur_time);
    tmpstr = news_malloc(IDLEN);
    sprintf(tmpstr,"%d %.3s %d %02d:%02d:%02d %s",stm->tm_mday,cp,stm->tm_year,
      stm->tm_hour,stm->tm_min,stm->tm_sec,news_timezone);
    strcpy((known_headers[DATE] = (char *) news_malloc(strlen(tmpstr) + 1)),tmpstr);
    if (fprintf(outfile,"%s%s\n",fheaders[DATE],tmpstr) < 0) _ck_put(outfile);
    o_byte_count += strlen(fheaders[DATE]) + strlen(tmpstr) + 1;
    ++o_line_count;
    news_free(tmpstr);
    }

  fputc('\n',outfile); _ck_put(outfile);
  ++o_line_count;
  ++o_byte_count;
  inbuf = news_malloc(1025);
  while ( mygets(inbuf,1024,infile) ) {
    ++i_line_count;
    ++o_line_count;
    ++o_body_line_count;
    o_byte_count += strlen(inbuf);
    fputs(inbuf,outfile); _ck_put(outfile);
    }
  news_free(inbuf);

  if (fclose(outfile)) _ck_close(outfile);

  if (!i_line_count) {
    if (delete(scratchfile)) _ck_delete(scratchfile);
    return(EMPTYINFILE);
    }

  /*--- newsgroup names and path names are treated as lower case */
  if (known_headers[PATH]) {
    lower_case(known_headers[PATH]);
    strncpy(recvfrom,known_headers[PATH],sizeof(recvfrom)-1);
    recvfrom[sizeof(recvfrom)-1]='\0';
    chop_str(recvfrom,'!');
    }
  else
    strcpy(recvfrom,"Unknown!");

  if (known_headers[NEWSGROUPS]) {
    lower_case(known_headers[NEWSGROUPS]);
    cp = aliases(known_headers[NEWSGROUPS]);
    known_headers[NEWSGROUPS] = news_realloc(known_headers[NEWSGROUPS],strlen(cp)+1);
    strcpy(known_headers[NEWSGROUPS],cp);
    }

  if (known_headers[FOLLOWUP_TO]) lower_case(known_headers[FOLLOWUP_TO]);
  if (known_headers[DISTRIBUTION]) lower_case(known_headers[DISTRIBUTION]);

  printf("Add %s %s %s (%d bytes)\n",
              recvfrom,
              known_headers[MESSAGE_ID]?known_headers[MESSAGE_ID]:"",
              known_headers[NEWSGROUPS],
              o_byte_count);

  return(SCAN1OK + retflags);
}

int add_posting(infile,switches)
  FILE *infile;
  int switches;
{
  int add_sts, parse_sts, post_net_sts, rhead;
  unsigned int cre_grp[100];
  char inbuf[2048];
  FILE *outfile;

  /*----- perform parse of file, extracting headers */
  add_sts = add_item(infile);
  if (!(add_sts & 1)) {
    return(add_sts);
    }

  /*----- if a known message id then we can junk it */
  if (known_headers[MESSAGE_ID] && check_id(known_headers[MESSAGE_ID])) return(KNOWNID);


  if (!strcmp(known_headers[NEWSGROUPS],"junk")) {
    return(JUNKED);
    }

  /*----- interpret expiry date [6.1b3: comment terminator has been missing!] */
  mail_add_expiry =  parse_expiry_date(known_headers[EXPIRES]);

  /*----- act on control posting */
  if (known_headers[CONTROL]) {
    if (sys_local_accept(known_headers[NEWSGROUPS],known_headers[DISTRIBUTION]))
      parse_sts = parse_control_item(known_headers,scratchfile,switches);

      /*+++++ add this to the local database with a "control" newsgroup field */
    else parse_sts = NOTLOCALLYACCEPTED;
    }

  /*----- load file into local database */
  if (sys_local_accept(known_headers[NEWSGROUPS],known_headers[DISTRIBUTION])) {
    load_newsgroups(known_headers[NEWSGROUPS],cre_grp);
    if (*cre_grp)
      do_new_item(cre_grp, known_headers[MESSAGE_ID], known_headers[SUBJECT], known_headers[FROM], scratchfile, 1, 0, o_body_line_count);

    else add_sts = NOLOCALNEWSGROUPS;
    }
  else add_sts = NOTLOCALLYACCEPTED;

#if SEND_ACCEPTED_ONLY
  if (add_sts & 1)
#endif
    post_net_sts = post_net(known_headers,scratchfile);
}

int post_net(argv,filename)
  char *argv[], *filename;
{
  char *path;

  if (path = strchr(argv[PATH],'!')) path++;
  else path = "";
  chop_str(path,'!');
  sys_remote_send(path,argv[NEWSGROUPS],argv[DISTRIBUTION],filename,argv[MESSAGE_ID],!mod_add);
  return 1;
}

err(int err_code)
{
  static char *err_msg[] = {"No message",
			    "No filename specified",
			    "No read access to file",
		            "No write accfess to sys$scratch",
			    "Bad header format - continuation lines start with a space",
			    "Bad header format - no colon",
			    "Bad header format - not a recognised keyword",
			    "Bad header format - repeated header keyword",
			    "Input file is empty",
			    "Item junked - no locally accepted newsgroups referenced",
			    "Item junked",
			    "Message-Id already received",
			    "Not locally accepted"};

  if (!(err_code & 1)) return;
  err_code = err_code >> 1;
  strcpy(err_oline,err_msg[err_code]);
  err_line(err_oline);
}

static int add_file(char *fnam, int switches)
{
  struct stat statb;

  if (!fnam || !*fnam) return(err(NOFILENAME));
  if (!(fpr = fopen(fnam,"r","mbc=120"))) {
    _ck_open_r(fpr,fnam);
    return(err(NOREADACCESS));
    }

  fstat(fileno(fpr), &statb);
  printf("\nAdd - Reading file: %s (%d bytes)\n\n",fnam, statb.st_size);

  infile_eof = infile_eom = 0;
  infile_hdr = first_get_call = 1;
  while (!infile_eof) add_posting(fpr,switches);
  if (fclose(fpr)) _ck_close(fpr);
  if (del_after)
    { if (delete(fnam)) _ck_delete(fnam); }
  return;
}
#endif

/*
 *  do_add_net -> calls dan_1 at execution level 1
 *
 * Add network forwarded news into the local database
 */

static int dan_1()
{
  char fnam[256], rnam[255], exec[255], force_news[255];
  unsigned short fnam_len, defn_len, forcn_len, exec_len;
  int savscrn = 0, mfo = mailfile_open;
  struct FAB *context = 0;
  $DESCRIPTOR(defn_dsc,def_newsgroup);
  $DESCRIPTOR(forcn_dsc,force_news);
  $DESCRIPTOR(forcnw_dsc,force_newsgroup);
  $DESCRIPTOR(fnam_dsc,fnam);
  $DESCRIPTOR(rnam_dsc,rnam);
  $DESCRIPTOR(add_dsc,fnam);
  $DESCRIPTOR(exec_opt_dsc,exec);

  add_file_call = 1;

  debug_switch = (DEBUG || (cli$present(c$dsc("DEBUG")) & 1));

  if ((mod_add = (cli$present(c$dsc("MODERATOR")) & 1)) != 0) {
    sprintf(mod,"%s@%s",usr_username,Node_address);
    if (!check_moderator(mod))
      return(err_line("Error: Add/mod - Not a moderator"),0);
    }
  else if (no_priv())
    return(err_line("Error: Add - No privilege for operation"),0);

#if FAST_LOAD
   if (fast_loading) {

      _ck_sys(NEWS$_NOADDWITHFASTLD,0,0);    /* see problem description below */

      set_level(1);  /* ensure the directory is shown */
      if (! all_loaded ) {
        if (!nntp_client) {
          sysprv();
          sys_close(&itmfab);
          sys_close(&grpfab);
          nosysprv();               /* note this code is taken from the */
          close_hist_file();        /* closefiles function in the       */
          };                        /* NEWSFILES module.                */
        close_mail_file();  
        close_nntp_file();
        write_reg_file();
        all_loaded = 1;
        first_retr_call = 0;
        openfiles(nntp_client ? 4 : -1);
/*
   mark.martinec@ijs.si  23 Feb 1993
   This still needs to be fixed !

   Problem description:
     A call to CLI$DCL_PARSE from within profile_read (called from
     openfiles) overwrites the current CLI context, which is still
     required for obtaining values of ADD qualifiers and parameters.
     This leads to CLI complainting about undefined qualifiers
     and in consequance the command 'ADD BATCH' does nothing.

     Problem occurs when fast loading is used ('>' in line 1 of NEWSRC)
     and DIRECTORY or PRINT options are specified in profile.

  Suggested solution:
     Move all calls to cli$present and cli$get_value in this function
     before this 'if (fast_loading)' block.
*/
        }
      }                                       
#endif
  if (smg_active) {
    savscrn = 1;
    noscreen();
    }
  set_level(1);

  if (mfo) do_close_mail();

  if (!*scratchfile) sprintf(scratchfile,"SYS$SCRATCH:ADD_%X.ITM",getpid());
  auto_cre_grp = cli$present(c$dsc("CREGRP")) & 1;
  del_after = (cli$present(c$dsc("DELETE")) & 1);
  skip_loop_test = cli$present(c$dsc("RETRY")) & 1;
  genid = cli$present(c$dsc("GENID")) & 1;
  feed_through = (cli$present(c$dsc("FEEDTHROUGH")) & 1);

  exec_opt = 0;
  exec_switch = 0;
  if ((execute_control = cli$present(c$dsc("EXECUTE")) & 1) != 0) {
    exec_switch = EXECUTE_CONTROL;
    if (cli$get_value(c$dsc("EXECUTE"),&exec_opt_dsc,&exec_len) & 1) {
      exec[exec_len] = '\0';
      if (!strcmp(exec,"DELETE")) {
        exec_opt = 1;
        exec_switch |= EXECUTE_DELETE;
	}
      else if (!strcmp(exec,"LOCAL")) {
	exec_opt = 2;
        exec_switch |= EXECUTE_LOCAL;
	}
      }
    }

  loc_junk = 0;
  if (cli$get_value(c$dsc("DEFNEWSGROUP"),&defn_dsc,&defn_len) & 1)
    def_newsgroup[defn_len] = '\0';
  else {
    strcpy(def_newsgroup,junk);
    loc_junk = 1;
    }

  if (cli$get_value(c$dsc("NEWSGROUP"),&forcn_dsc,&forcn_len) & 1) {
    loc_junk = 0;
    force_news[forcn_len] = '\0';
    }
  else *force_news = '\0';

  net_news = (cli$present(c$dsc("NETFEED")) & 1);
  control_squelch = (cli$present(c$dsc("NOCONTROL")) & 1);

  junk_it = 1;
  if (cli$present(c$dsc("JUNK")) == CLI$_NEGATED) junk_it = 0;

  accept_it = 0;
  if (cli$present(c$dsc("ACCEPT")) & 1) accept_it = 1;

  if (cli$get_value(c$dsc("FILE"),&fnam_dsc,&fnam_len) == CLI$_ABSENT)
    get_input(&fnam_dsc,c$dsc("Add File: "),&fnam_len);

  do {
    fnam[fnam_len] = '\0';
    auto_cre_grp = cli$present(c$dsc("CREGRP")) & 1;
    del_after = (cli$present(c$dsc("DELETE")) & 1);
    skip_loop_test = cli$present(c$dsc("RETRY")) & 1;

    exec_opt = 0;
    exec_switch = 0;
    if ((execute_control = cli$present(c$dsc("EXECUTE")) & 1) != 0) {
      exec_switch = EXECUTE_CONTROL;
      if (cli$get_value(c$dsc("EXECUTE"),&exec_opt_dsc,&exec_len) & 1) {
        exec[exec_len] = '\0';
        if (!strcmp(exec,"DELETE")) {
	  exec_opt = 1;
	  exec_switch |= EXECUTE_DELETE;
	  }
        else if (!strcmp(exec,"LOCAL")) {
	  exec_opt = 2;
	  exec_switch |= EXECUTE_LOCAL;
	  }
        }
      }

    net_news = (cli$present(c$dsc("NETFEED")) & 1);
    junk_it = 1;
    if (((status = cli$present(c$dsc("JUNK"))) == CLI$_NEGATED) ||
              (status == CLI$_LOCNEG)) junk_it = 0;
      accept_it = 0;
    if (cli$present(c$dsc("ACCEPT")) & 1) accept_it = 1;
    if (cli$get_value(c$dsc("DEFNEWSGROUP"),&defn_dsc,&defn_len) & 1) {
      loc_junk = 0;
      def_newsgroup[defn_len] = '\0';
      }
    else {
      strcpy(def_newsgroup,junk);
      loc_junk = 1;
      }
    lower_case(def_newsgroup);

    if (cli$get_value(c$dsc("NEWSGROUP"),&forcnw_dsc,&forcn_len) & 1) {
      loc_junk = 0;
      force_newsgroup[forcn_len] = '\0';
      }
    else strcpy(force_newsgroup,force_news);
    lower_case(force_newsgroup);

    feed_through = (cli$present(c$dsc("FEEDTHROUGH")) & 1);

    add_dsc.dsc$w_length = strlen(fnam);
    del_after_save = del_after; /* RRS save /DELETE flag for wildcards */
    while ((status = lib$find_file(&add_dsc,&rnam_dsc,&context,0,0,0,0)) & 1) {
      rnam[254] = '\0';
      chop_str(rnam,' ');
      add_file(rnam,exec_switch);
      del_after = del_after_save; /* RRS save /DELETE flag for wildcards */
      }
    lib$find_file_end(&context);
    } while (cli$get_value(c$dsc("FILE"),&fnam_dsc,&fnam_len) & 1);
  no_more_news = 0;
  mail_add_expiry = 0;
  flush_downstream(1);
  if (savscrn) init_screen();
  if (mfo) do_open_mail();
  return(0);
}

int do_add_net()
{
  if (nntp_client) return(0);
  return(unwind_display(OUTER_LOOP,dan_1));
}
#endif
