/*
    litenntp.* - ngetlite nntp protocol handler
    Copyright (C) 2000-2003  Matthew Mueller <donut AT dakotacom.net>

    This program 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 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "lite.h"
#include "litenntp.h"
#include "log.h"
#include <stdio.h>
#include <unistd.h>
#include "strreps.h"
//needed for getpid on mingw32
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif

int c_prot_nntp::putline(int echo,const char * str,...){
	va_list ap;
	va_start(ap,str);
	int i=doputline(echo,str,ap);
	va_end(ap);
	return i;
}
int c_prot_nntp::stdputline(int echo,const char * str,...){
	va_list ap;
	int i;
	va_start(ap,str);
	doputline(echo,str,ap);
	va_end(ap);
	i=getreply(echo);
	if (i==450 || i==480) {
		nntp_auth();
		va_start(ap,str);
		doputline(echo,str,ap);
		va_end(ap);
		i=getreply(echo);
	}
	return i;
}
int c_prot_nntp::doputline(int echo,const char * str,va_list ap){
	int i,l;
	char *fpbuf;
	l=vasprintf(&fpbuf,str,ap);
	try {
		i = cursock->write(fpbuf, l) + cursock->write("\r\n",2);
	} catch (FileEx &e) {
		free(fpbuf);
		doclose();
		throw TransportExError(Ex_INIT,"nntp_putline: %s:%i: %s",e.getExFile(), e.getExLine(), e.getExStr());
	}
	if (echo){
		printf(">%s\n",fpbuf);
	}
//	time(&lasttime);
	free(fpbuf);
	return i;
}

int c_prot_nntp::getline(int echo){
	int i;
	try {
		i=cursock->bgets();
	} catch (FileEx &e) {
		doclose();
		throw TransportExError(Ex_INIT,"nntp_getline: %s:%i: %s",e.getExFile(), e.getExLine(), e.getExStr());
	}
	if (i==0) {
		doclose();
		throw TransportExError(Ex_INIT,"nntp_getline: connection closed unexpectedly");
	}
	cbuf = cursock->rbufp();
	if (echo){
		printf("%s\n",cbuf);
		fflush(stdout);
	}
	return i;
}

int c_prot_nntp::chkreply(int reply) {
//	int i=getreply(echo);
	if (reply==400) {
		// 400 response is used on connection for "Service temporarily unavailable" or during a session if the server has to terminate the connection for some reason.
		doclose();
		throw ProtocolExError(Ex_INIT,"server says byebye: %s", cbuf);
	}
	if (reply/100!=2)
		throw ProtocolExFatal(Ex_INIT,"bad reply %i: %s",reply,cbuf);
	return reply;
}

int c_prot_nntp::getreply(int echo){
	int code;
	if ((code=getline(echo))>=0)
		code=atoi(cbuf);
//	if (cbuf[3]=='-')
//		do{
//			ftp_getline(cbuf,cbuf_size);
//		}while((atoi(cbuf)!=code)||(cbuf[3]!=' '));
	return code;
}

#define tempfilename_base "ngetlite"

void c_prot_nntp::doarticle(const char *article,ulong bytes,ulong lines,const char *outfile){
	chkreply(stdputline(debug>=DEBUG_MED,"ARTICLE %s",article));
	printf(".");fflush(stdout);
	ulong rbytes=0,rlines=0,hlines=0;
	time_t starttime,donetime;
	time(&starttime);
	int header=1;
	long glr;
	char *lp;
	char tempfilename[100];
	sprintf(tempfilename,"%s.%i",tempfilename_base,
#ifdef HAVE_GETPID
			getpid()
#else
			rand()
#endif
			);
	c_file_fd f(tempfilename,"w");
	while(1) {
		glr=getline(debug>=DEBUG_ALL);
		if (cbuf[0]=='.'){
			if(cbuf[1]==0)
				break;
			lp=cbuf+1;
		}else
			lp=cbuf;
		rlines++;
		rbytes+=glr;
		if (header && lp[0]==0){
			header=0;
			hlines=rlines;
			rlines=0;
		}
		f.putf("%s\n",lp);
	}
	f.close();
	xxrename(tempfilename,outfile);
	time(&donetime);
	if (!quiet){
		long d=donetime-starttime;
		printf("got article %s in %li sec, %li B/s (%lu/%lu lines, %lu/%lu bytes, %s)",article,d,d?rbytes/(d):0,rlines,lines,rbytes,bytes,outfile);
		if (rlines!=lines)printf(" Warning! lines not equal to expected!");
		//if (rbytes!=bytes)printf(" bne!");
		if ((rbytes > bytes + 3) || (rbytes + 3 < bytes)) printf(" bne!");
		printf("\n");
		fflush(stdout);
	}
}

void c_prot_nntp::dogroup(const char *group){
	if (curgroup && strcmp(group,curgroup)==0)
		return;
	chkreply(stdputline(!quiet,"GROUP %s",group));
	newstrcpy(curgroup,group);
}
void c_prot_nntp::doclose(void){
	if (cursock.get())
		try {
			cursock->close();
		} catch (FileEx &e) {//ignore transport errors while closing
			printCaughtEx_nnl(e);printf(" (ignored)\n");
		}
	safefree(curhost);
	safefree(curgroup);
	safefree(curuser);
	safefree(curpass);
}
void c_prot_nntp::doopen(const char *host, const char *user, const char *pass){
	if (cursock.get() && cursock->isopen() && safestrcmp(host,curhost)==0 && safestrcmp(user,curuser)==0 && safestrcmp(pass,curpass)==0)
		return;

	doclose();
	try {
		cursock.reset(new c_file_tcp(host,"nntp"));
		cursock->initrbuf();
	} catch (FileEx &e) {
		throw TransportExError(Ex_INIT,"nntp_doopen: %s",e.getExStr());
	}
	chkreply(getreply(!quiet));
	putline(debug>=DEBUG_MED,"MODE READER");
	getline(debug>=DEBUG_MED);
	newstrcpy(curhost,host);
	newstrcpy(curuser,user);
	newstrcpy(curpass,pass);
}

void c_prot_nntp::nntp_auth(void){
	nntp_doauth(curuser,curpass);
}
void c_prot_nntp::nntp_doauth(const char *user, const char *pass){
	int i;

	if(!user || !*user){
		throw TransportExFatal(Ex_INIT,"nntp_doauth: no authorization info known");
	}
	putline(quiet<2,"AUTHINFO USER %s",user);
	i=getreply(quiet<2);
	if (i==350 || i==381){
		if(!pass || !*pass){
			throw TransportExFatal(Ex_INIT,"nntp_doauth: no password known");
		}
		if (quiet<2)
			printf(">AUTHINFO PASS *\n");
		putline(0,"AUTHINFO PASS %s",pass);
		i=getreply(quiet<2);
	}
	chkreply(i);
}

c_prot_nntp::c_prot_nntp(){
	curhost=NULL;
	curgroup=NULL;
	curuser=NULL;
	curpass=NULL;
}
c_prot_nntp::~c_prot_nntp(){
	doclose();
}
