/*
 * sockets - open a socket connection and read/write to nntp server
 *
 * Copyright (C) 1992/93/94 Stephen Hebditch <steveh@tqmcomms.co.uk>.
 * All rights reserved. TQM Communications, BCM Box 225, London, WC1N 3XX.
 *
 * See README for more information and disclaimers
 *
 * Obtain the current time from the remote server in standard unix time
 * format for use with the next NEWNEWS. If the client is unable to
 * connect to the time server, then local time is used instead.
 *
 * $Id: sockets.c,v 1.9 1995/01/10 13:09:21 root Exp $
 *
 * $Log: sockets.c,v $
 * Revision 1.9  1995/01/10  13:09:21  root
 * Moved includes from slurp.h.
 * Added additional parameter to tcp_open() to specify port number
 * if required.
 * Moved in do_authinfo() and do_mode_reader() from slurp.c.
 * General tidying-up.
 *
 * Revision 1.7  1993/06/23  10:15:37  root
 * Replaced bzero/bcopy definitions with ANSI memcpy and memset.
 * Duplicate fd before using fdopen separately on each fd for read
 * and write, fixing long-standing stdio memory problems.
 *
 * Revision 1.6  1993/04/22  18:25:16  root
 * If NOBUFFOUT defined then turn off stdio buffering for the
 * output stream to the server to get round problem with SVR3s.
 *
 * Revision 1.5  1993/03/01  18:00:18  root
 * Use ferror to detect erros, not return code.
 *
 * Revision 1.4  1993/02/14  16:22:42  root
 * No longer have get_server return a return code. Makes this module
 * no longer compatible with nntp 1.6 client library, but there ya go...
 * Changed error detection in put_server for no other reason than to
 * be consistent with elsewhere.
 *
 * Revision 1.3  1992/12/15
 * Removed unnecessary close() in close_server.
 * Syslog log level for connected message changed to LOG_INFO.
 *
 * Revision 1.1  1992/12/04
 * Print line before it is sent to server when debugging is on.
 *
 * Revision 1.0  1992/11/29
 * Adapted from nntpxfer-e code.
 * Incorporate code to set up a tcp connection, plus cleaned up the
 * existing code.
 */

#include "conf.h"

/* POSIX headers */

#undef _POSIX_SOURCE
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef NOUNISTD
	#include <unistd.h>
#endif

/* IP headers */

#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* Local headers */

#include "nntp.h"
#include "slurp.h"
#include "syslog.h"

/* Fixes */

#ifndef INADDR_NONE
  #define INADDR_NONE 0xffffffff
#endif

/* File-scope variables */

static struct sockaddr_in serv_addr;
static struct servent serv_info;
static struct hostent host_info;

static jmp_buf env_alrm;

static FILE *server_rd_fp;
static FILE *server_wr_fp;


/*
 * tcp_open - Open a tcp connection to 'host' for service 'service',
 * returning a file descriptor for the socket.
 */

	int
tcp_open (const char *host, const char *service, const int portno)
	{
	int on = 1;
	int sockfd;
	struct hostent *hp;
	struct servent *sp;
	unsigned long inaddr;

	(void) memset (&serv_addr, 0, sizeof (serv_addr));
	serv_addr.sin_family = AF_INET;

	/* Get service information if required*/
	if (portno == 0)
		{
		if ((sp = getservbyname (service, "tcp")) == NULL)
			{
			log_ret ("tcp_open: unknown service %s/tcp", service);
			return (-1);
			}
		serv_info = *sp;
		serv_addr.sin_port = sp -> s_port;
		}
	else
		{
		serv_addr.sin_port = htons (portno);
		}

	/* Try to convert host name as dotted decimal */
	if ((inaddr = inet_addr (host)) != INADDR_NONE)
		{
		(void) memcpy (&serv_addr.sin_addr, &inaddr, sizeof (inaddr));
		host_info.h_name = NULL;
		}
	/* If that failed, then look up the host name */
	else
		{
		if ((hp = gethostbyname (host)) == NULL)
			{
			log_ret ("tcp_open: host name error: %s", host);
			return (-1);
			}
		host_info = *hp;
		(void) memcpy (&serv_addr.sin_addr, hp -> h_addr, hp -> h_length);
		}

	if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
		{
		log_ret ("tcp_open: can't create TCP socket");
		return (-1);
		}

	if (connect (sockfd, (struct sockaddr *) &serv_addr,
		sizeof (serv_addr)) < 0)
		{
		log_ret ("tcp_open: can't connect to server %s", host);
		(void) close (sockfd);
		return (-1);
		}

	if (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, 
					(char *) &on, sizeof (on)) < 0)
		log_ret ("tcp_open: can't set KEEPALIVE on socket");

	return (sockfd);
	}


/*
 * server_init - Open a connection to the NNTP server. Returns -1 if an
 * error occurs, otherwise the server's initial response code.
 */

	int
server_init (const char *hostname, const int portno)
	{
	char line [NNTP_STRLEN];
	int server_rd_fd;
	int server_wr_fd;

	/* First try and make the connection */
	if ((server_rd_fd = tcp_open (hostname, "nntp", portno)) < 0)
		return (-1);

	server_wr_fd = dup (server_rd_fd);

	/* Now fdopen to enable buffering of data */
	if ((server_rd_fp = fdopen (server_rd_fd, "r")) == NULL)
		{
		log_ret ("server_init: can't fdopen socket for reading");
		return (-1);
		}

	if ((server_wr_fp = fdopen (server_wr_fd, "w")) == NULL)
		{
		log_ret ("server_init: can't fdopen socket for writing");
		return (-1);
		}
        
	/* Inform everyone that we're there */
#ifdef SYSLOG
	if (!debug_flag)
		syslog (LOG_INFO, "Connected to nntp server at %s", hostname);
	else
#endif
		(void) fprintf (stderr, "Connected to nntp server at %s\n", hostname);

	/* Get the greeting herald */
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);

	/* Return the banner code */
	return (atoi (line));
	}


/*
 * close_server - Close down the NNTP server connection, sending a
 * QUIT message to the server then closing the socket.
 */

	void
close_server (void)
	{
	char line [NNTP_STRLEN];

	if (debug_flag)
		(void) fprintf (stderr, "<- QUIT\n");
	put_server ("QUIT");
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);

	(void) fclose (server_rd_fp);
	(void) fclose (server_wr_fp);
	}


/*
 * sig_alrm - Signal handler for socket read timeout
 */

	static void
sig_alrm (int signo)
	{
	longjmp (env_alrm, 1);
	}


/*
 * get_server - Read a line up to CRLF from the socket into a buffer.
 * Will timeout after TIMEOUT seconds if nothing is read from the socket.
 */

	void
get_server (char *line, const int size)
	{
	int esave;
	char *pos;

	/* Set up an alarm to handle socket timeout */
	if (setjmp (env_alrm))
		{
		(void) alarm (0);					/* Reset alarm clock */
		(void) signal (SIGALRM, SIG_DFL);
		errno = EPIPE;
		log_sys ("get_server: read error on server socket");
		}

	(void) signal (SIGALRM, sig_alrm);
	(void) alarm (TIMEOUT);

	/* Read line */
	(void) fgets (line, size, server_rd_fp);

	/* Reset the alarm */
	esave = errno;
	(void) alarm (0);
	(void) signal (SIGALRM, SIG_DFL);
	errno = esave;

	/* Report any error */
	if (ferror (server_rd_fp))
		log_sys ("get_server: read error on server socket");

	/* Kill the CRLF */
	if ((pos = strchr (line, '\r')) != NULL)
		*pos = '\0';
	if ((pos = strchr (line, '\n')) != NULL)
		*pos = '\0';
	}


/*
 * put_server - write a line from a string to a socket
 */

	void
put_server (const char *line)
	{

	(void) fprintf (server_wr_fp, "%s\r\n", line);
	if (ferror (server_wr_fp))
		log_sys ("put_server: write error on server socket");
	(void) fflush (server_wr_fp);
	}


/*
 * do_authinfo - Check in the authinfo username and password with the
 * server.
 */

	void
do_authinfo (const char *username, const char *password)
	{
	char line [NNTP_STRLEN];

	/* Send the username to the server */
	(void) sprintf (line, "AUTHINFO USER %s", username);
	if (debug_flag)
		(void) fprintf (stderr, "<- %s\n", line);
	put_server (line);

	/* Get the response and check it's okay */
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);
	if (atoi (line) != NEED_AUTHDATA)
		{
		log_msg ("do_authinfo: NNTP protocol error: got '%s'", line);
		exit (4);
		}
	                
	/* Send the password to the server */
	(void) sprintf (line, "AUTHINFO PASS %s", password);
	if (debug_flag)
		(void) fprintf (stderr, "<- %s\n", line);
	put_server (line);

	/* Get the response and check it's okay */
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);
	if (atoi (line) != OK_AUTH)
		{
		log_msg ("do_authinfo: NNTP protocol error: got '%s'", line);
		exit (4);
		}
	}


/*
 * do_mode_reader - Send mode reader command to INN to switch to nnrpd
 * so we can do a NEWNEWS.
 */

	void
do_mode_reader (void)
	{
	char line [NNTP_STRLEN];

	/* Send the command to the server */
	if (debug_flag)
		(void) fprintf (stderr, "<- MODE READER\n");
	put_server ("MODE READER");

	/* Get the response and check it's okay */
	get_server (line, sizeof (line));
	if (debug_flag)
		(void) fprintf (stderr, "-> %s\n", line);
	switch (atoi (line))
		{
		case OK_CANPOST :
		case OK_NOPOST :
			break;
		default :
			log_msg ("do_mode_reader: NNTP protocol error: got '%s'", line);
			exit (4);
		}
	}

/* END-OF-FILE */


syntax highlighted by Code2HTML, v. 0.9.1