/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: MBSE BBS Internet Library
 *
 *****************************************************************************
 * Copyright (C) 1997-2005
 *   
 * Michiel Broek		FIDO:		2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "../config.h"
#include "mbselib.h"
#include "mbinet.h"


static int		nntpsock = -1;	/* TCP/IP socket		*/
struct hostent		*nhp;		/* Host info remote		*/
struct servent		*nsp;		/* Service information		*/
struct sockaddr_in	nntp_loc;	/* For local socket address	*/
struct sockaddr_in	nntp_rem;	/* For remote socket address	*/




int nntp_connect(void)
{
	int	addrlen;
	char	*p;

	if (nntpsock != -1)
		return nntpsock;

	if (!strlen(CFG.nntpnode)) {
		WriteError("NNTP: host not configured");
		return -1;
	}

	Syslog('+', "NNTP: connecting host: %s", CFG.nntpnode);
	memset(&nntp_loc, 0, sizeof(struct sockaddr_in));
	memset(&nntp_rem, 0, sizeof(struct sockaddr_in));

	nntp_rem.sin_family = AF_INET;

	if ((nhp = gethostbyname(CFG.nntpnode)) == NULL) {
		WriteError("NNTP: can't find host %s", CFG.nntpnode);
		return -1;
	}

	nntp_rem.sin_addr.s_addr = ((struct in_addr *)(nhp->h_addr))->s_addr;

	if ((nsp = getservbyname("nntp", "tcp")) == NULL) {
		WriteError("NNTP: can't find service port for nntp/tcp");
		return -1;
	}
	nntp_rem.sin_port = nsp->s_port;

	if ((nntpsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		WriteError("$NNTP: unable to create tcp socket");
		return -1;
	}

	if (connect(nntpsock, (struct sockaddr *)&nntp_rem, sizeof(struct sockaddr_in)) == -1) {
		WriteError("$NNTP: cannot connect tcp socket");
		return -1;
	}

	addrlen = sizeof(struct sockaddr_in);

	if (getsockname(nntpsock, (struct sockaddr *)&nntp_loc, &addrlen) == -1) {
		WriteError("$NNTP: unable to read socket address");
		return -1;
	}

	p = nntp_receive();
	if (strlen(p) == 0) {
		WriteError("NNTP: no response");
		nntp_close();
		return -1;
	}
	Syslog('+', "NNTP: %s", p);

	if (strncmp(p, "480", 3) == 0) {
		/*
		 *  Must login with username and password
		 */
		if (nntp_auth() == FALSE) {
			WriteError("Authorisation failure");
			nntp_close();
			return -1;
		}
	} else if (strncmp(p, "200", 3)) {
		WriteError("NNTP: bad response: %s", p);
//		nntp_close();  FIXME: Don't close, the other end might have done that already
		//		      If we do also, this program hangs. Must be fixed!
		return -1;
	}

	if (CFG.modereader) {
		Syslog('+', "NNTP: setting mode reader");
		nntp_send((char *)"MODE READER\r\n");
		p = nntp_receive();
		Syslog('+', "NNTP: %s", p);
		if (strncmp(p, "480", 3) == 0) {
			/*
			 *  Must login with username and password
			 */
			Syslog('+', "NNTP: %s", p);
			if (nntp_auth() == FALSE) {
				WriteError("NNTP: authorisation failure");
				nntp_close();
				return -1;
			}
		} else if (strncmp(p, "200", 3)) {
			WriteError("NNTP: bad response: %s", p);
			nntp_close();
			return -1;
		}
	}
	return nntpsock;
}



int nntp_send(char *buf)
{
    if (nntpsock == -1)
	return -1;

    if (send(nntpsock, buf, strlen(buf), 0) != strlen(buf)) {
	WriteError("$NNTP: socket send failed");
	if (errno == ENOTCONN || errno == EPIPE) {
	    WriteError("NNTP: closing local side");
	    nntpsock = -1;
	}
	return -1;
    }
    return 0;
}



/*
 *  Return empty buffer if something went wrong, else the complete
 *  dataline is returned
 */
char *nntp_receive(void)
{
    static char	buf[SS_BUFSIZE];
    int		i = 0, j;

    if (nntpsock == -1)
	return NULL;

    memset((char *)&buf, 0, SS_BUFSIZE);
    while (TRUE) {
	j = recv(nntpsock, &buf[i], 1, 0);
	if (j == -1) {
	    WriteError("$NNTP: error reading socket");
	    memset((char *)&buf, 0, SS_BUFSIZE);
	    if (errno == ENOTCONN || errno == EPIPE) {
		WriteError("NNTP: closing local side");
		nntpsock = -1;
	    }
	    return buf;
	}
	if (buf[i] == '\n')
	    break;
	i += j;
    }

    for (i = 0; i < strlen(buf); i++) {
	if (buf[i] == '\n')
	    buf[i] = '\0';
	if (buf[i] == '\r')
	    buf[i] = '\0';
    }

    return buf;
}



int nntp_close(void)
{
	if (nntpsock == -1)
		return 0;

	nntp_cmd((char *)"QUIT\r\n", 205);

	if (shutdown(nntpsock, 1) == -1) {
		WriteError("$NNTP: can't close socket");
		return -1;
	}

	nntpsock = -1;
	Syslog('+', "NNTP: closed");
	return 0;
}



/*
 *  Send NNTP command, check response code. 
 *  If the code not matches, the value is returned, else zer.
 */
int nntp_cmd(char *cmd, int resp)
{
	char	*p, rsp[6];

	if (nntp_send(cmd) == -1)
		return -1;

	snprintf(rsp, 6, "%d", resp);
	p = nntp_receive();

        if (strncmp(p, "480", 3) == 0) {
                /*
                 *  Must login with username and password
                 */
		Syslog('+', "NNTP: %s", p);
                if (nntp_auth() == FALSE) {
                        WriteError("Authorisation failure");
                        nntp_close();
                        return -1;
                }
		/*
		 *  Now send command again, we are now authorized.
		 */
		if (nntp_send(cmd) == -1)
			return -1;
		p = nntp_receive();
	}

	if (strncmp(p, rsp, strlen(rsp))) {
		WriteError("NNTP> %s", cmd);
		WriteError("NNTP< %s", p);
		memset(&resp, 0, sizeof(rsp));
		strncpy(rsp, p, 3);
		return atoi(rsp);
	}
	return 0;
}



int nntp_auth(void)
{
	char	*cmd;

	if (!(strlen(CFG.nntpuser) && strlen(CFG.nntppass))) {
		WriteError("NNTP: password required but not configured");
		return FALSE;
	}
	cmd = calloc(128, sizeof(char));

	snprintf(cmd, 128, "AUTHINFO USER %s\r\n", CFG.nntpuser);
	if (nntp_cmd(cmd, 381))
		return FALSE;

	snprintf(cmd, 128, "AUTHINFO PASS %s\r\n", CFG.nntppass);
	if (nntp_cmd(cmd, 281) == 0) {
		free(cmd);
		Syslog('+', "NNTP: logged in");
		return TRUE;
	} else {
		free(cmd);
		return FALSE;
	}
}