#include "mcdp.h"
#include <limits.h>

#define BUFSIZE 1024 /* complete lines of server must fit ! */
char port[6], ip[16];

static short cddb_parseentry(struct mcdp *cd);
static int cddb_getserver(const char *ipport, char *ip, char *port);
static int cddb_opensocket(char *ip, char *port);
static int cddb_readsocket(char *s, int size, int *socket);
static short cddb_abort(struct he *h);

/* searches a local entry, success returns 0 (needs CDB_PATH!)*/
static short cddb_parseentry(struct mcdp *cd) {
 char *fn=malloc(sizeof(char)*PATH_MAX);
 int p=0,tr=1,len,fd; /* p=parse;tr=track */
 char ch;

 len=_snprintf(fn,PATH_MAX-1,"%s/%08x",
  getenv("CDDB_PATH"),cd_discid(&cd[0]));
 fn[len]='\0';
 if ((fd=open(fn,O_RDONLY))<0) {
  free(fn); return -1; /* no entry (mostly *g*) */
 }
 len=0;
 while (len+=read(fd,&ch,1)>0) {
  fn[len-1]=ch;
  if (ch=='\n'||ch=='\r') { /* next one */
   p=1;fn[len-1]='\0';len=0;
  }
  if (p) { /* parse line */
   if (!strncmp(fn, "DTITLE=", 7))
    str_ncpy(cd->t[0].name, fn+7, str_len(fn));
   if (!strncmp(fn, "TTITLE", 6)) {
    str_ncpy(cd->t[tr].name, fn+8+(int)(tr/10), str_len(fn));
    tr++;
   }
   p=0;
  }
 } /* while() */
 free(fn);
 return 0;
}

/* gets server from environment, 0 means success
   (not always correct, but libc-stuff is to big) */
static int cddb_getserver(const char *ipport, char *ip, char *port) {
 register const char *s=ipport;
 register int i;
 if (!s) return -1;
 for (i=0;*s && *s!=':';s++,i++) ip[i]=*s; /* shall be an ip :} */
 if (i>15)  return -1; /* 12+3 */
 ip[i]='\0';s++;
 for (i=0;*s;s++,i++) port[i]=*s;
 if (i>5)  return -1;
 port[i]='\0';
 return 0;
}

/* close connection to server */
static short cddb_abort(struct he *h) {
 write(h->fd,"quit\n",5);
 close(h->fd);
 free(h->msg);
 return CDDB_NOENT;
}

/* returns socket of server */
static int cddb_opensocket(char *ip, char *port) {
 struct sockaddr_in addr;
 int sockfd;
 if ((sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
  return -1;
 addr.sin_family=AF_INET;
 addr.sin_port=htons(atoi(port));
 addr.sin_addr.s_addr=inet_addr(ip);
 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr))<0)
  return -1;
 return sockfd;
}

/* XXX + warning, can lock the programm, if server is ugly */
static int cddb_readsocket(char *s, int size, int *socket) {
 int i=0,r;
 char c;
 while ((r=recv(*socket, &c, 1, 0))>0) {
  if (i==size) break;
  s[i++]=c;
  if (c=='\r') break; /* \r\n -> '\n' after while */
 }
 if (r<0) return r;
 recv(*socket, &c, 1, 0);
 s[i]='\0';return i;
}

/* the global call, fills cd->t[].name, returns CDDB_MODES (mcdp.h) */
int cddb_getentries(struct mcdp *cd) {
 char *hostname=getenv("HOSTNAME");
 char *logname=getenv("LOGNAME");
 char tmp[16]; /* tracks to str */
 struct he h;
 int len,i;

 /* if this is not set, we do nothing! */
 if (getenv("CDDB_PATH")==NULL)
  return CDDB_NOENT;

 /* if local entry is here, we take it */
 if (!cddb_parseentry(&cd[0]))
  return CDDB_LOCAL;

 /* now, lets get new entry (handles only exact matches) */
 if ((cddb_getserver(getenv("CDDB_SERVER"),ip,port)<0) ||
  ((h.fd=cddb_opensocket(ip,port))<0) || /* can't connect */
  (hostname==NULL) || (logname==NULL)) /* kiss */
  return CDDB_NOENT;

 h.msg=malloc(sizeof(char)*BUFSIZE+1);
 if (!h.msg) return CDDB_NOENT;

 /* small implementation of this protocoll (l8er more?) */
 /* 201 foobar server version ready */
 if ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))<0)
  return cddb_abort(&h);
 if (strncmp("201",h.msg,3)) return cddb_abort(&h);
 len=_snprintf(h.msg,BUFSIZE,"cddb hello %s %s mcdp %s\n",
  logname,hostname,MCDP_VERSION);
 write(h.fd,h.msg,len);

 /* 200 Hello and welcome user@hostname running mcdp version */
 if ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))<0)
  return cddb_abort(&h);
 if (strncmp("200",h.msg,3)) return cddb_abort(&h);
 len=_snprintf(h.msg,BUFSIZE,"cddb query %08x %d ",
  cd_discid(&cd[0]),cd->title[1]);
 for (i=1;i<=cd->title[1];i++) {
  len+=_snprintf(tmp, 15, "%d ", cd->t[i].cddb);
  strncat(h.msg,tmp,str_len(tmp));
 }
 len+=_snprintf(tmp, 15, "%d\n", cd->t[0].cddb);
 strncat(h.msg,tmp,str_len(tmp));
 write(h.fd,h.msg,len);

 /* 200 rock a90c9d0c Queen / Innuendo */
 if ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))<0) return cddb_abort(&h);
 if (strncmp("200",h.msg,3)) return cddb_abort(&h);
 for (i=0;h.msg[i+4]!=' ';i++) tmp[i]=h.msg[i+4]; /* 200 foobar' ' */
 tmp[i]='\0';
 len=_snprintf(h.msg,BUFSIZE,"cddb read %s %08x\n",
  tmp,cd_discid(&cd[0]));
 write(h.fd,h.msg,len);

 /* 210 rock a90c9d0c CD database entry follows (until terminating `.') */
 if ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))<0) return cddb_abort(&h);
 if (strncmp("210",h.msg,3)) return cddb_abort(&h);
 len=_snprintf(h.msg,BUFSIZE,"%s/%08x",getenv("CDDB_PATH"),cd_discid(&cd[0]));

 i=open(h.msg,O_WRONLY|O_CREAT,CDDB_CREATMODE);
 while ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))>0 && (h.msg[0]!='.')) {
  write(i,h.msg,len-1); /* [len]='\r' */
  write(i,"\n",1);
 }
 if (len<0) return cddb_abort(&h);
 close(i);

 /* 230 foobar Goodbye. */
 write(h.fd,"quit\n",5);
 /* last recv() isn't usefull, but seems correct :) */
 if ((len=cddb_readsocket(h.msg, BUFSIZE, &h.fd))<0)
  return cddb_abort(&h);
  if (strncmp("230",h.msg,3))
 return cddb_abort(&h);

 close(h.fd);
 free(h.msg);
 if (!cddb_parseentry(&cd[0]))
  return CDDB_REMOTE;
 return CDDB_NOENT;
}

/*
TODO:

201 whisky.wu-wien.ac.at CDDBP server v1.4b42PL0 ready at Sat Jul 28 01:08:01 2001

cddb hello mcmilk darkwood mcd 0.3b
200 Hello and welcome mcmilk@darkwood running mcd 0.3b.

cddb query 8910cf1c 28 182 11982 26962 40150 54342 65580 76077 85992 95200 108477 123820 135037 145632 157072 167537
187660 199132 206732 218450 228770 240150 248750 260525 268050 275925 287920 305385 311842 4305
211 Found inexact matches, list follows (until terminating `.')

rock 7510961c Böhse Onkelz / Der Nette Mann + Demos

rock 9f10d71c Böhse Onkelz / Der nette Mann & Demos

rock 9711051c Böhse Onkelz / Der nette Mann

.

...

quit
*/
