/*
** Napster Protocol
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <proto/exec.h>
#include <proto/socket.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <bsdsocket/socketbasetags.h>

#include "napster.h"
#include "gui.h"
#include "download.h"
#include "prefs.h"
#include "info.h"
#include "msg.h"

struct Library *SocketBase=NULL;
unsigned long nap_sigmask;
long nap_sock= -1;

const char *nap_linktype[] = {
	"Unknown",
	"14.4 Modem",
	"28.8 Modem",
	"33.6 Modem",
	"57.6 Modem",
	"ISDN 64K",
	"ISDN 128K",
	"Cable",
	"DSL",
	"T1",
	"T3+",
	NULL
};

int _nap_lala(void);
void _nap_bibi(void);
long _nap_connect(char *server);
void _nap_send(unsigned int type, char *buf);
int _nap_read(unsigned int *type, char **buf);

void _nap_parsesearch(char *buf);
char *_nap_findserver(void);


int nap_login(void)
{
	char *srv=NULL;
	char buf[256];
	char *rep;
	unsigned int type;

	if(pref_server) srv=pref_server; else srv=_nap_findserver();
	if(!srv) return(0);

	nap_sock = _nap_connect(srv);
	if(nap_sock<0) return(0);

	gui_debug("Connected.");

	if(pref_regflag) {
		sprintf(buf,"%s",pref_user);
		_nap_send(NAP_CREATEUSER,buf);
	}

	sprintf(buf,"%s %s %d \"%s\" %d",pref_user,pref_pass,0,pref_napvers,pref_link);
	_nap_send(NAP_LOGIN,buf);
	if(!_nap_read(&type,&rep)) {
		gui_debug("recv() error!");
		nap_logout();
		return(0);
	}
	if(type==NAP_ERROR) {
		gui_debug("Login failed!");
		nap_logout();
		return(0);
	}

	gui_debug("Logged in.");
	gui_state(1);
	return(1);
}


void nap_logout(void)
{
	if(nap_sock>=0) CloseSocket(nap_sock);
	nap_sock = -1;
	_nap_bibi();
	gui_state(0);
}


int nap_listen(void)
{
	char *buf;
	unsigned int type;
	struct fd_set fds;
	struct timeval tv;

	dl_pollall();

	if(nap_sock== -1) return(1);

	while(1) {
		FD_ZERO(&fds);
		FD_SET(nap_sock,&fds);
		tv.tv_sec=0;
		tv.tv_usec=15;
		if(WaitSelect(nap_sock+1,&fds,NULL,NULL,&tv,0)<1) return(0);

		if(!_nap_read(&type,&buf)) {
			gui_debug("Network error!");
			nap_logout();
			return(1);
		}

		switch(type) {
			case NAP_SEARCHRESULT:
				_nap_parsesearch(buf);
				break;
			case NAP_SEARCHCOMPLETE:
				gui_searchstat(2);
				break;
			case NAP_FILECOUNT:
				{
				char *tmp,*tmp2;
				tmp=strtok(buf," ");
				tmp=strtok(NULL," ");
				tmp2=strtok(NULL,"");
				gui_srvstat(atoi(buf),atoi(tmp),atoi(tmp2));
				break;
				}
			case NAP_PRIVATEMSG:
				{
				char *nick,*msg;
				nick = strtok(buf," ");
				msg = strtok(NULL,"");
				msg_got(nick,msg);
				break;
				}
			case NAP_FILEINFO:
				{
				char title[256],user[20];
				unsigned long ip;
				int port;
				sscanf(buf, "%s %u %d \"%[^\"]\"",user,&ip,&port,title);
				dl_startq(title,user,NAP_SWAPIP(ip),port);
				}
				break;
			case NAP_SYSMSG:
				gui_debug(buf);
				break;
			default:
				gui_debugf("<<%d:%s>>",type,buf);
				break;
		}
		free(buf);
	}
}


void nap_search(char *keys, int max)
{
	char buf[1024];

	sprintf(buf,"FILENAME CONTAINS \"%s\" MAX_RESULTS %d",keys,max);
	_nap_send(NAP_SEARCH,buf);
}


void nap_request(songdata sd)
{
	char buf[1024];

	sprintf(buf,"\"%s\" \"%s\"",sd->user,sd->title);
	_nap_send(NAP_FILEINFOREQ,buf);
	dl_addq(sd);
}


void nap_msg(char *nick, char *msg)
{
	char buf[1024];

	sprintf(buf,"%s %s",nick,msg);
	_nap_send(NAP_PRIVATEMSG,buf);
}


char *nap_getlinktype(int link)
{
	return(nap_linktype[link]);
}


/* private functions */

void _nap_parsesearch(char *buf)
{
	songdata sd;
	int tmp;

	sd = malloc(sizeof(_songdata));
	if(!sd) return;
	memset(sd,0,sizeof(_songdata));
	sscanf(buf,"\"%[^\"]\" %s %lu %d %d %d %s %d %d",sd->title,sd->chksum,&sd->size,&sd->bitrate,&sd->freq,&sd->time,sd->user,&tmp,&sd->link);
	gui_foundsong(sd);
}


char *_nap_findserver(void)
{
	long s;
	static char buf[256];
	char *col;
	int len;

	s = _nap_connect(pref_mainserver);
	if(s<0) return(NULL);

	len = recv(s,buf,255,0);
	CloseSocket(s);
	if(len<=0) {
		gui_debug("recv() error!");
		_nap_bibi();
		return(NULL);
	}

	buf[len]=0;
	col = strchr(buf,'\n');
	if(col) *col=0;

	if (strncmp(buf,"wait",4)==0 || strncmp(buf,"busy",4)==0) {
		gui_debug("Server is busy. Try again later.");
		_nap_bibi();
		return(NULL);
	}

	gui_debugf("Got '%s' from napster.",buf);
	return(buf);
}




/* low level private functions */

int _nap_lala(void)
{
	if(SocketBase) return(1);
	SocketBase = OpenLibrary("bsdsocket.library",0);
	if(!SocketBase) {
		gui_debug("TCP/IP stack needed!");
		return(0);
	}
	SocketBaseTags(SBTM_SETVAL(SBTC_SIGIOMASK),(char *)nap_sigmask,TAG_DONE);
	return(1);
}


void _nap_bibi(void)
{
	if(dl_list!=NULL) return;
	if(SocketBase) CloseLibrary(SocketBase);
	SocketBase = NULL;
}


long _nap_connect(char *server)
{
	long s;
	struct hostent *host;
	struct sockaddr_in sin;
	char *col,*addr;
	int port;
	long temp=1;

	if(!_nap_lala()) return(-1);

	addr = strdup(server);
	col = strtok(addr,":");
	if(!col) {
		gui_debug("Port number needed (hostname:port)!");
		_nap_bibi();
		return(-1);
	}
	col = strtok(NULL,"");
	port = atoi(col);

	host = gethostbyname(addr);
	if(!host) {
		gui_debug("Hostname lookup failed!");
		_nap_bibi();
		return(-1);
	}
	memcpy(&sin.sin_addr,host->h_addr,host->h_length);
	sin.sin_family = host->h_addrtype;
	sin.sin_port = port;
	sin.sin_len = sizeof(sin);

	s = socket(AF_INET,SOCK_STREAM,0);
	if(s<0) {
		gui_debug("socket() error!");
		_nap_bibi();
		return(-1);
	}

	if(connect(s,(struct sockaddr *)&sin,sizeof(struct sockaddr_in))<0) {
		gui_debug("Cannot connect!");
		_nap_bibi();
		CloseSocket(s);
		return(-1);
	}

	IoctlSocket(s,FIOASYNC,(char *)&temp);
	return(s);
}


int _nap_read(unsigned int *type, char **buf)
{
	static u_char hdr[4];
	char *tmp;
	unsigned int len,rlen=0,i;
	len = recv(nap_sock,hdr,4,0);
	if(len!=4) return(0);
	len = hdr[0] + (hdr[1]<<8);
	*type = hdr[2] + (hdr[3]<<8);

	tmp = malloc(len+2);
	if(!tmp) return(0);
	while(rlen<len) {
		i = recv(nap_sock,tmp+rlen,len-rlen,0);
		if(i<0) {
			free(tmp);
			return(0);
		}
		rlen += i;
	}
	tmp[rlen]=0;
	*buf = tmp;
	return(1);
}


void _nap_send(unsigned int type, char *buf)
{
	static u_char hdr[4];
	int len;

	len = strlen(buf);
	hdr[0] = len&0xFF;
	hdr[1] = (len&0xFF00)>>8;
	hdr[2] = type&0xFF;
	hdr[3] = (type&0xFF00)>>8;
	send(nap_sock,hdr,4,0);
	send(nap_sock,buf,len,0);
}
