/*
** This software is Copyright (c) 1989, 1990, 1991 by Kent Landfield.
**
** Permission is hereby granted to copy, distribute or otherwise
** use any part of this package as long as you do not try to make
** money from it or pretend that you wrote it.  This copyright
** notice must be maintained in any copy made.
**
*/

#if !defined(lint) && !defined(SABER)
static char *SID = "@(#)nntpart.c	2.3 4/15/92";
#endif

/*LINTLIBRARY*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <nntp.h>
#include "cfg.h"
#include "maxart.h"
#include "article.h"

#define TMPDIR "/usr/tmp"

char *nntp_server;

int     server_init();
void    put_server();
int     get_server();
void    close_server();

static int server_connected = 0;

int atoi();
int fputs();

extern int verbose;

/*
 *
 * First digit:
 *
 *	1xx	Informative message
 *	2xx	Command ok
 *	3xx	Command ok so far, continue
 *	4xx	Command was correct, but couldn't be performed
 *		for some specified reason.
 *	5xx	Command unimplemented, incorrect, or a
 *		program error has occured.
 *
 * Second digit:
 *
 *	x0x	Connection, setup, miscellaneous
 *	x1x	Newsgroup selection
 *	x2x	Article selection
 *	x3x	Distribution
 *	x4x	Posting
 */

struct nntp_resp {
    int     resp;
    char    *msg;
};

struct nntp_resp resps[] = {
{	INF_HELP,	"Help text on way"			},
{	INF_DEBUG,	"Debug output"				},
{	OK_CANPOST,	"Hello; you can post"			},
{	OK_NOPOST,	"Hello; you can't post"			},
{	OK_SLAVE,	"Slave status noted"			},
{	OK_GOODBYE,	"Closing connection"			},
{	OK_GROUP,	"Group selected"			},
{	OK_GROUPS,	"Newsgroups follow"			},
{	OK_ARTICLE,	"Article (head & body) follows"		},
{	OK_HEAD,	"Head follows"				},
{	OK_BODY,	"Body follows"				},
{	OK_NOTEXT,	"No text sent -- stat, next, last"	},
{	OK_NEWNEWS,	"New articles by message-id follow"	},
{	OK_NEWGROUPS,	"New newsgroups follow"			},
{	OK_XFERED,	"Article transferred successfully"	},
{	OK_POSTED,	"Article posted successfully"		},
{	CONT_XFER,	"Continue to send article"		},
{	CONT_POST,	"Continue to post article"		},
{	ERR_GOODBYE,	"Have to hang up for some reason"	},
{	ERR_NOGROUP,	"No such newsgroup"			},
{	ERR_NCING,	"Not currently in newsgroup"		},
{	ERR_NOCRNT,	"No current article selected"		},
{	ERR_NONEXT,	"No next article in this group"		},
{	ERR_NOPREV,	"No previous article in this group"	},
{	ERR_NOARTIG,	"No such article in this group"		},
{	ERR_NOART,	"No such article at all"		},
{	ERR_GOTIT,	"Already got that article, don't send"	},
{	ERR_XFERFAIL,	"Transfer failed"			},
{	ERR_XFERRJCT,	"Article rejected, don't resend"	},
{	ERR_NOPOST,	"Posting not allowed"			},
{	ERR_POSTFAIL,	"Posting failed"			},
{	ERR_COMMAND,	"Command not recognized"		},
{	ERR_CMDSYN,	"Command syntax error"			},
{	ERR_ACCESS,	"Access to server denied"		},
{	ERR_FAULT,	"Program fault, command not performed"	},
{	-1,	        NULL					},
};

char *get_nntpstr(resp_line,number)
    char *resp_line;
    int *number;
 {
    struct nntp_resp *ct;

    *number = atoi(resp_line);

    ct = &resps[0];
    while ((ct->resp) != -1) {
        if (*number == ct->resp)
            return(ct->msg);
        ct++;
    }
    return(NULL);
}

void print_server_response(line, command)
char *line;
char *command;
{
	int rrc;
	static char *str;

        /*
        ** Verify the first character as a valid response code.
        */
        if ((*line != CHAR_INF) && (*line != CHAR_OK) 
            && (*line != CHAR_CONT) && (*line != CHAR_ERR)
            && (*line != CHAR_FATAL))  {
            (void) fprintf(logfp,"[%s]: Xxx : unknown response",line);
            return;
        }

        /*
        ** Verify the last two characters are 
        ** within range of valid responses.
        */
        
        if ((*(line+1) < '0') || (*(line+1) > '9')) {
            (void) fprintf(logfp,"[%s]: xXx : unknown response",line);
            return;
        }
        if ((*(line+2) < '0') || (*(line+2) > '9')) {
            (void) fprintf(logfp,"[%s]: xxX : unknown response",line);
            return;
        }

        if ((str = get_nntpstr(line,&rrc)) != NULL) 
            (void) fprintf(logfp,"%d: %s", rrc, str);
        else
            (void) fprintf(logfp,"%d: unknown response", rrc);

	if (command)
	     (void) fprintf(logfp, " [%s]\n", command);
	else
	     (void) fprintf(logfp, "\n");

        /* If the response in within a range (190-199) it is not handled.. */
        /* fix later, I am too lazy now....*/
        return;
}

/*
** nntp_tmp_path[]
**
** Holds the full pathname of the nntp transfer file.
** For use with external command archiving...
*/

char nntp_tmp_path[MAXNAMLEN];   
static int nntp_article;

static int nntp_output_command(command, filename)
char *command;
char *filename;
{
     FILE *tfp;
     char ser_line[512];
     int rc;
     
     put_server(command);
     if (get_server(ser_line, sizeof(ser_line)) < 0) {
	  close_server();
	  server_connected = 0;
	  return ERROR_ENCOUNTERED;
     }
     if (*ser_line != CHAR_OK) {
	  if ((rc = atoi(ser_line)) != ERR_NOART && rc != ERR_NOARTIG) {
	       if (verbose) {
		    print_server_response(ser_line, command);
	       }
	  }
	  return ERROR_ENCOUNTERED;
     }

     /* 
     ** Here we have a valid article. Create a temporary file
     ** to transfer the article from the nntp server to the
     ** local system into.
     */
     if ((tfp = fopen(filename, "w+")) == NULL)
	  return(ERROR_ENCOUNTERED);

     
     /* 
     ** Read a line from the server and write it out to
     ** the file to be used for archiving. This file is
     ** removed when it is no longer needed.
     */
     while ((rc = get_server(ser_line, sizeof(ser_line))) >= 0) {  /* while */
	  if (ser_line[0] == '.' && ser_line[1] == '\0')  /* valid input */
	       break;                          /* get it and write it  */
          /* 
          ** If the text contained a period as the first character of the 
          ** text line in the original, that first period is doubled.  
          ** Therefore, the client must examine the first character of 
          ** each line received, and for those beginning with a period, 
          ** determine either that this is the end of the text or whether 
          ** to collapse the doubled period to a single one.
          */
 
	  if (ser_line[0] == '.')
	       (void) fputs(ser_line+1, tfp);  /* doubled '.'  - collapse */
	  else
	       (void) fputs(ser_line, tfp);    /* general textual input   */
	  (void) fputs("\n", tfp);
     }
     (void) fclose(tfp);
     if (rc < 0) {
	  server_connected = 0;
	  close_server();
	  return(ERROR_ENCOUNTERED);
     }
     return(RETRIEVED);
}


int nntp_retrieve_article()
{
     char buf[256];
     int rc;

     if (article.article_retrieved == 1) {
	  sprintf(buf, "ARTICLE %ld", nntp_article);
	  if (verbose)
	       (void) fprintf(logfp,"Retrieving <%d> article from %s\n",
			      nntp_article, nntp_server);
	  if ((rc = nntp_output_command(buf, nntp_tmp_path)) !=
	      ERROR_ENCOUNTERED) {
	       get_header(nntp_tmp_path);
	       article.article_retrieved = 2;
	       return(rc);
	  }
	  else {
	       return(ERROR_ENCOUNTERED);
	  }
     }
     else {
	  return(RETRIEVED);
     }
}


int nntp_retrieve_header(filename,which_time)
char *filename;
int which_time;
{
     int sscanf();
     int fclose();
     int chdir();

     char buf[256];
     char ser_line[512];
     int  response;
     int  rc;               /* response code */
     static int n;          /* extimated number of articles */
     static int f;          /* first article number */
     static int l;          /* last article number */
     static int maxart;

     if (which_time == 1) {
	 open_maxart();
         if (chdir(TMPDIR) != 0) {
             (void) fprintf(errfp,"can't cd to %s.\n",TMPDIR);
             return(ERROR_ENCOUNTERED);
         }

         /* 
         ** Need to establish the connection with the nntp_server.
         */

         if (! server_connected) {
	      if ((response = server_init(nntp_server)) < 0) {
		   (void) fprintf(errfp,"can't connect to %s.\n",nntp_server);
		   return(ERROR_ENCOUNTERED);
	      }

	      /* 
	      ** Assure that the connection succeeded...
	      */

	      if (response == ERR_ACCESS) {
		   (void) fprintf(errfp,
		       "Connection permission to the %s news server denied.\n",
				  nntp_server);
		   return(ERROR_ENCOUNTERED);
	      }
	      else if ((response != OK_NOPOST) && (response != OK_CANPOST)) {
		   (void) fprintf(errfp,
			  "Unexpected response code from %s news server: %d\n",
				  nntp_server, response);
		   return(ERROR_ENCOUNTERED);
	      }
	      server_connected++;
	 }
	 
         /* 
         ** Need to tell the server which newsgroup to process.
         */

         (void) sprintf(buf,"GROUP %s", newsgrp->ng_name);
         put_server(buf);    /* tell server we want to select the group */

         if (get_server(ser_line, sizeof(ser_line)) < 0) {
             (void) fprintf(errfp,
               "Unexpected close of %s connection\n", nntp_server);
	     close_server();
	     server_connected = 0;
             return(ERROR_ENCOUNTERED);
         }

         if (*ser_line != CHAR_OK) {      /* and then see if that's ok */
             print_server_response(ser_line, buf);
             return(ERROR_ENCOUNTERED);
         }

         /* 
         ** Get the newsgroup article information from the returned buffer 
         ** 
         ** rc    = response code (211 or 411)
         **  n    = estimated number of articles in group,
         **  f    = first article number in the group,
         **  l    = last article number in the group,
         **  smsg = name of the group.)
         */

         (void) sscanf(ser_line,"%d%d%d%d%s",  &rc, &n, &f, &l, buf);
         if (verbose) {
             print_server_response(ser_line, 0);
     	     (void) fprintf(logfp,"Newsgroup\t%s\n",buf);
     	     (void) fprintf(logfp,"First\t%d\tLast \t%d\n",f,l);
     	     (void) fprintf(logfp,"Est. No. Articles\t%d\n",n);
         }

         maxart = get_maxart(newsgrp->ng_name);
	 if (f <= maxart)
	      f = maxart;
	 else
	      f--;
     }

     for (f++ ; f <= l; f++) {
        /* 
        ** Store the article number as the file name for the
        ** temporary file transfer. This is mainly to support
        ** the Article-Number form of archiving.
        */
        (void) sprintf(filename,"%d",f);
        (void) sprintf(nntp_tmp_path,"%s/%d",TMPDIR,f);
	nntp_article = f;

        /* 
        ** Build the nntp command string 
        ** designating which article to transfer.
        */
        (void) sprintf(buf,"HEAD %ld", f);
	set_maxart(newsgrp->ng_name, f);
	if (verbose)
	     (void) fprintf(logfp,"Retrieving <%d> header from %s\n", f,
			    nntp_server);

	if (nntp_output_command(buf, filename) == RETRIEVED) {
	     article.article_retrieved = 1;
	     return(RETRIEVED);
	}
     }
     if (get_maxart(newsgrp->ng_name) > maxart) {
	  save_maxart();
     }
     return(DONE);
}
