/*
       _             __
  ____(_)__ _______ / /____ ____
 / __/ (_-</ __(_-</ __/ -_) __/
/_/ /_/___/\__/___/\__/\__/_/

Napster client for RISC OS
Copyright (C) 2000 Robert Dimond

Portions are based on gnap by Ryan Dahl.

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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

Bransley Cottage, Cleobury Mortimer, Kidderminster, WORCS. DY14 0BZ
England. robdimond@cwcom.net

*/

#include "runimage.h"
#include "dns.h"


#define BUFFSIZE 0xffff

#define MAX_FILEATTS 10

extern protocolv pversion;

socket_s server;
socket_s redirserver;
socket_s listen;

char buffer[0xffff];

/* why oh why did I do it like this!!! */

extern char email[];
extern char username[];
extern char password[];
extern int connection;
extern int new_user;
extern connect_level connected;
//extern search_result * search_resblock;
extern int search_currentres;
extern int listening_port;
extern transfer downloads[];
extern incoming incon[];
extern search_result browsed_user;
extern char altserver_name[];
extern int altserver_port;

extern mp3file * mp3file_list;

char ip_bestserver[34];
char debugmsg[200];


int on=1;


message_status mess_stat=get_head;
int mess_recvd=0, mess_len=0, mess_command=0;
char mess_header[4];

struct in_addr ip_redir;

void napster_resolve_redir(void)
{
 connectedchange(nap_res);
 switch(dns_find_ip_address(HOSTNAME, &ip_redir))
 {
  case socket_EINPROGRESS: return; break;
  case 0:
  napster_connect(); break;
  default:
  ui_messalert("ERRCNRS");
  napster_close();
  break;
 }
}

void napster_resolve_alt(void)
{
 connectedchange(alt_res);
 switch(dns_find_ip_address(altserver_name, &ip_redir))
 {
  case socket_EINPROGRESS: return; break;
  case 0:
  napster_serverconnect(ip_redir.s_addr, portconv(altserver_port)); break;
  default:
  ui_messalert("ERRCNRSA");
  napster_close();
  break;
 }
}

int napster_connect(void)
{
 int b;
 socket_sockaddr serveraddress;
 mess_stat=get_head;

 if(error(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &redirserver))) return(-1);


 if(error(xsocket_ioctl(redirserver, socket_FIONBIO, (byte *) &on)))
 {
  xsocket_close(redirserver);
  return(-2);
 }
 /* connect socket to remote server */
 serveraddress.sockaddr_in.af=socket_AF_INET;
 serveraddress.sockaddr_in.port=portconv(HOSTPORT);
 serveraddress.sockaddr_in.addr=/*ipconv(HOSTIP)*/ip_redir.s_addr;
 for(b=0; b<8; b++) serveraddress.sockaddr_in.data[b]=0;
 connectedchange(tc_redir);
 motd_addline("Connecting to redirect server...");
 strcpy(ip_bestserver, "");
 if(nerror(xsocket_connect(redirserver, &serveraddress, sizeof(serveraddress))))
 {
  napster_close();
  return(-3);
 }
 return(0);
}

/*

Me, my thoughts are flower strewn, ocean storm, bayberry moon.
I've got to leave to find my way.  There is nothing left to go but
ginger lemon indigo, coriander stem and rose of hay.

*/

int napster_serverconnect(unsigned int ip, int port)
{
 int b;
 socket_sockaddr serveraddress;
 mess_stat=get_head;

 if(error(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &server))) return(-1);


 if(error(xsocket_ioctl(server, socket_FIONBIO, (byte *) &on)))
 {
  xsocket_close(server);
  return(-2);
 }
 /* connect socket to remote server */
 serveraddress.sockaddr_in.af=socket_AF_INET;
 serveraddress.sockaddr_in.port=port;
 serveraddress.sockaddr_in.addr=ip;
 for(b=0; b<8; b++) serveraddress.sockaddr_in.data[b]=0;
 connectedchange(tc_server);
 motd_addline("Connecting to server...");
 if(nerror(xsocket_connect(server, &serveraddress, sizeof(serveraddress))))
 {
  napster_close();
  return(-3);
 }
 return(0);
}

int nerror(os_error * err)
{
 int e;
 if (err!=NULL)
 {
  e=err->errnum;
  if ((e==socket_EAGAIN)||(e==socket_EWOULDBLOCK)||
      (e==socket_EINPROGRESS))
     {
      return (0);
     }
     else
     {
      ui_alert(err->errmess);
      return(1);
     }
 }
}

void napster_handle_bestserver()
{
 char c;
 int cc=0, pos, b;
 unsigned int newip;
 int newport;

 if (nerror(xsocket_recv(redirserver, (byte *) &c, 1, 0, &cc))) return;
 pos=strlen(ip_bestserver);
 if (cc)
 {
  ip_bestserver[pos]=c;
  ip_bestserver[pos+1]='\0';
 }
 /*if (strlen(ip_bestserver)>=20)*/
 if(ip_bestserver[strlen(ip_bestserver)-5]==':')
 {
  if(strstr(ip_bestserver, "127.0.0.1"))
  {
   ui_messalert("ERRBUSY");
   napster_close();
  }
  else
  {
   nerror(xsocket_close(redirserver));
   sprintf(debugmsg, "Redirecting to: %s", ip_bestserver);
   napster_get_best_host(&newip, &newport, ip_bestserver);
   motd_addline(debugmsg);
   napster_serverconnect(newip, newport);
  }
 }
}

void napster_close()
{
 switch(connected)
 {
  case notc:
  case nap_res:
  case alt_res:
  break;
  case tc_redir:
  case get_redir:
  nerror(xsocket_close(redirserver));
  break;
  case tc_server:
  case try_login:
  case loggedin:
  nerror(xsocket_close(server));
  break;
 }
 connectedchange(notc);
}

int napster_mo_userlogin(char * username, char * password, int port, int connection, int new_user)
{
 char connect_string[200]="";
 if (!new_user)
 {
  sprintf(connect_string,"%s %s %i \"%s\" %i",username,password,port,VERSION_STRING,connection);

  napster_send(connect_string, 2, strlen(connect_string));


 }
 else
 {
  sprintf(connect_string,"%s %s %i \"%s\" %i %s",username,password,port,VERSION_STRING,connection, email);
  napster_send(connect_string, 6, strlen(connect_string));
 }
 return(0);
}

int napster_mo_newuser(char * username)
{
 napster_send(username, 7, strlen(username));
 return(0);
}


int napster_mo_dlrequest(search_result * toget)
{
 char get_string[300];
 sprintf(get_string, "%s \"%s\"", toget->user, toget->filename);
 napster_send(get_string, 203, strlen(get_string));
 return(0);
}

void napster_get_best_host(unsigned int * ipo, int * porto, char * string)
{
 char * ip, * port, * c;
 ip=string;
 port=strchr(string, ':');
 port[0]='\0';
 port+=sizeof(char);
 c=strchr(port, '\0');
 c[0]='\0';
 *porto=portconv(atoi(port));
 *ipo=ipconv(ip);
}

int portconv(int port)
{
 char *b, *c;
 b=(char *)&port;
 c=b+1;
 return(*c|(*b<<8));
}

int ipconv(char * ipin)
{
 char c[]=".";
 char ip[]="000.000.000.000";
 char * t;
 int out=0, n=0;
 strcpy(ip, ipin);
 t=strtok(ip, c);
 while ((t!=NULL)&&(n<4))
 {
  out+=atoi(t)<<(n*8);
  t=strtok(NULL, c);
  n++;
 }
 /*os_writec('\4');
 printf("%s %d\n", ip, out);*/
 return(out);
}

int napster_poll(void)
{
 int i;
 socket_s newsocket;
 os_error * sele;
 socket_timeval timeout;
 long readfd=0, writefd=0;
 int nfound=0;

 readfd=readfd|(long)1<<(int)listen;
 switch(connected)
 {
  case notc:
  break;
  case nap_res:
  break;
  case tc_redir:
  writefd=writefd|(long)1<<(int)redirserver;
  break;
  case get_redir:
  readfd=readfd|(long)1<<(int)redirserver;
  break;
  case tc_server:
  writefd=writefd|(long)1<<(int)server;
  break;
  default:
  if (connected>=try_login)
  {
   readfd=readfd|(long)1<<(int)server;
   for(i=0; i<C_DOWNLOADS; i++)
   {
    if(downloads[i].active==tc) writefd=writefd|(long)1<<(int)downloads[i].socket;
    if(downloads[i].active>=get_one)
                               readfd=readfd|(long)1<<(int)downloads[i].socket;
   }
  }
 }

 for(i=0; i<C_INCOMING; i++)
 {
  if(incon[i].active) readfd=readfd|(long)1<<(int)incon[i].socket;
 }

 timeout.sec=0; timeout.usec=0;
 sele=xsocket_select(32, (socket_fdset *) &readfd,
                          (socket_fdset *) &writefd,
                               NULL, &timeout, &nfound);
 if((sele==NULL)&&((connected<tc_redir)||(nfound)))
 {
  if(readfd&((long)1<<(int)listen))
  {
  if(!nerror(xsocket_accept(listen, NULL, NULL, &newsocket)))
                                             napster_incoming(newsocket);
  }
  switch(connected)
  {
   case notc:
   break;
   case nap_res:
   napster_resolve_redir();
   break;
   case alt_res:
   napster_resolve_alt();
   break;
   case tc_redir:
   if(writefd&((long)1<<(int)redirserver)) connectedchange(get_redir);
   break;
   case get_redir:
   if(readfd&((long)1<<(int)redirserver)) napster_handle_bestserver();
   break;
   case tc_server:
   if(writefd&((long)1<<(int)server))
   {
    motd_addline("Connected, trying to log in...");
    if(!new_user)
    napster_mo_userlogin(username, password, listening_port, connection, 0);
    else napster_mo_newuser(username);
    connectedchange(try_login);
   }
   break;
   default:
   if(connected>=try_login)
   {
    if(readfd&((long)1<<(int)server))
    {
     napster_handle_input();
    }
    for(i=0; i<C_DOWNLOADS; i++)
    {
     if((downloads[i].active==tc)&&(writefd&((long)1<<(int)downloads[i].socket)))
       napster_handle_nfconnected(i);
     if((downloads[i].active>=get_one)&&(readfd&((long)1<<(int)downloads[i].socket)))
     {

      switch(downloads[i].active)
      {
       case get_one:
       napster_handle_one(i);
       break;
       case get_header:
       napster_handle_header(i);
       break;
       case download:
       napster_handle_download(i);
       break;
       default: break;
      }
     }
    }
   }
   break;
  }
  for(i=0; i<C_INCOMING; i++)
  {
   if((incon[i].active)&&(readfd&((long)1<<(int)incon[i].socket)))
   {
    napster_handle_inconheader(i);
   }
  }
 }
 else
 {
  switch(sele->errnum)
  {
   case socket_EBADF:
   /* pretty much fatal so we have to close the connection */
   napster_close();
   break;
   default:
   nerror(sele);
   break;
  }
 }
 return(nfound);
}

void napster_incoming(socket_s newsocket)
{
 int cc=0;
 int in_slot=0;
 while((in_slot<=C_INCOMING)&&(incon[in_slot].active!=0))
 {
  in_slot++;
 }
 if(incon[in_slot].active!=0)
 {
  xsocket_close(newsocket);
  return;
 }
 if (nerror(xsocket_send(newsocket, (byte *) "1", 1, 0, &cc)))
 {
  ui_messalert("ERRCOMC");
  xsocket_close(newsocket);
  return;
 }
 incon[in_slot].socket=newsocket;
 incon[in_slot].active=1;
 incon[in_slot].hpos=0;
 incon[in_slot].header[0]='\0';
 incon[in_slot].command=0;
}

void napster_handle_inconheader(int ino)
{
 char c;
 int cc=0, i;
 if (nerror(xsocket_recv(incon[ino].socket, (byte *) &c, 1, 0, &cc)))
 {
  ui_messalert("ERRCOMC");
  incon[ino].active=0;
  xsocket_close(incon[ino].socket);
  return;
 }
 incon[ino].header[incon[ino].hpos]=c;
 incon[ino].hpos++;
 incon[ino].header[incon[ino].hpos]='\0';
 if(incon[ino].active==1)
 {
  // incoming upload request (for firewalled downloading)
  if(!strcmp(incon[ino].header, "SEND"))
  {
   incon[ino].command=1;
   incon[ino].hpos=0;
   incon[ino].active=2;
   return;
  }
  // incoming download request
  else if(!strcmp(incon[ino].header, "GET"))
  {
   os_writec('\4');
   printf("download request\n");
   incon[ino].command=2;
   incon[ino].hpos=0;
   incon[ino].active=2;
  }
 }
 if(incon[ino].active==2)
 {
  if (c=='\"')
  {
   os_writec('\4');
   printf("got \" ");
   incon[ino].active=3;
   return;
  }
 }
 if(incon[ino].active==3)
 {
  if (c==' ')
  {
   if (incon[ino].command==1)
   {
    for(i=0; i<C_DOWNLOADS; i++)
    {
     if((downloads[i].active==getinfo)&&
        (strstr(incon[ino].header, downloads[i].filename)))
     {
      cc=0;
      if(nerror(xsocket_send(incon[ino].socket, (byte *) "0", 1, 0, &cc)))
      {
       incon[ino].active=0;
       xsocket_close(incon[ino].socket);
       ui_messalert("ERRCOMC");
       return;
      }
      if(cc)
      {
       incon[ino].active=0;
       downloads[i].socket=incon[ino].socket;
       downloads[i].active=get_header;
       downloads[i].headcount=0;
      }
     }
    }
   } else if (incon[ino].command==2)
   {
    // download request (sharing)
    sprintf(buffer, "~rRequest for: %s", incon[ino].header);
    motd_addline(buffer);
    incon[ino].active=0;
   }
  }
 }
}

void napster_handle_header(int tno)
{
 int cc;
 char c;
 if(nerror(xsocket_recv(downloads[tno].socket, (byte *) &c, 1,
                                              socket_MSG_PEEK, &cc)))
 {
  ui_messalert("ERRCOMC");
  napster_download_end(tno);
 }

 if (!((c>='0')&&(c<='9')))
 {
  downloads[tno].header[downloads[tno].headcount]='\0';
  //sprintf(debugmsg, "Got size header:  %s bytes", downloads[tno].header);
  //motd_addline(debugmsg);
  napster_send((char *) "", 218, 0);
  downloads[tno].totalsize=atoi((downloads[tno].header));
  downloads[tno].active=download;
  search_updatebar(tno);
 }
 else
 {
  if(nerror(xsocket_recv(downloads[tno].socket, (byte *) &c, 1, 0, &cc)))
  {
   ui_messalert("ERRCOMC");
   napster_download_end(tno);
  }
  downloads[tno].header[downloads[tno].headcount]=c;
  downloads[tno].headcount++;
 }
}

void napster_handle_download(int tno)
{
 char buffer[BUFFSIZE];
 int amount_recv, i;
 //for (i=0; i<2048; i++) buffer[i]=0;
 /*amount_recv = recv(t_current.socket,buffer,2048,0);*/
 nerror(xsocket_recv(downloads[tno].socket, (byte *)buffer,
                                               BUFFSIZE, 0, &amount_recv));
 fwrite(buffer, sizeof(char), amount_recv, downloads[tno].filehandle);
 downloads[tno].size += amount_recv;
 if (downloads[tno].size>=downloads[tno].totalsize) napster_download_end(tno);
}

void napster_download_end(int tno)
{
 if (downloads[tno].active==download)
 {
  napster_send((char *) "", 219, 0);
 }
 if (downloads[tno].active>=tc)
 {
  error(xsocket_close(downloads[tno].socket));
 }
 if ((downloads[tno].active>=window)&&(downloads[tno].filehandle!=NULL))
 {
  if(downloads[tno].size<downloads[tno].totalsize)
                                   lib_writefooter(&downloads[tno]);
  if (fclose(downloads[tno].filehandle)==EOF)
  {
   ui_messalert("ERRCSC");
  }
 }
 get_closewin(tno);
 downloads[tno].active=freesl;
}

void napster_send(char * data, int type, int length)
{
 char msb, lsb;
 char head[4];
 //char motd[255];
 int cc;
 if (pversion==proto_old) {
   msb=(length>>8);
   lsb=length-(msb<<8);
   head[0]=lsb; head[1]=msb;
   msb=(type>>8);
   lsb=type-(msb<<8);
   head[2]=lsb; head[3]=msb;
 } else {
   msb=(length>>8);
   lsb=length-(msb<<8);
   head[0]=msb; head[1]=lsb;
   msb=(type>>8);
   lsb=type-(msb<<8);
   head[2]=msb; head[3]=lsb;
 }
 if((nerror(xsocket_send(server, (byte *)head, 4, 0, &cc)))
     ||(nerror(xsocket_send(server, (byte *)data, length, 0, &cc))))
 {
  ui_messalert("ERRCOMS");
  napster_close();
  return;
 }
 //sprintf(motd, "> Type:%d Len:%d  %s", type, length, data);
 //motd_addline(motd);
}

void napster_mess_loginerror(char * message)
{
 /*printf("Error from server: %s\n", message);*/
 ui_alert(message);
 napster_close();
}

void napster_mess_error(char * message)
{
 ui_alert(message);
 search_ungrey();
}

void napster_mess_loginack(char * email)
{
 mp3file * l;
 char tune[500];
 /*printf("Logged in, email is %s\n", email);*/
 ui_messalert("SMLOGIN");
/* napster_init_server();*/
 l=mp3file_list;
 while(l!=NULL)
 {
  if (pversion==proto_old) {
    sprintf(tune, "\"%s.mp3\" %s %d %d %d %d",
                  l->filename, l->checksum, l->size, l->bitrate,
                  l->frequency, l->length);
    napster_send(tune, 100, strlen(tune));
    //os_writec('\4');
    //printf("%s\n", tune);
  } else {
    sprintf(tune, " \"mp3s\" \"%s.mp3\" %s %d %d %d %d",
                  l->filename, l->checksum, l->size, l->bitrate,
                  l->frequency, l->length);
    napster_send(tune, 870, strlen(tune));
  }
  l=l->next;
 }
 connectedchange(loggedin);
}

void napster_mess_regsuccess(void)
{
 /*printf("New user registration success\n");*/
 ui_messalert("SMREGS");
 napster_mo_userlogin(username, password, 6699, connection, 1);
}

void napster_mess_alreadyreg(void)
{
 /*printf("Nickname is already in use by another user\n");*/
 ui_messalert("SMNICK");
 napster_close();
}

void napster_mess_invalidreg(void)
{
 /*printf("Nickname is invalid\n");*/
 ui_messalert("SMINV");
 napster_close();
}

void napster_mess_searchresponse(char * line)
{
 /*"<filename>" <md5> <size> <bitrate> <frequency>
                              <length> <nick> <ip> <link-type>*/
 search_result * result;
 char *filename,user[40],id[100];
 int number,size,bitrate,frequency,speed;
 unsigned int ip;
 filename = line;
 filename ++;
 line = (char*)strchr(filename,'\"');
 if(!line) {
 /* printf("strange filename error\n");
  os_writec('\4');
  printf("%s\n", line);  */
  ui_messalert("ERRSFILE");
  return;
 }
 line[0] = '\0';
 line ++;
 result = malloc(sizeof(search_result));
 if (result==NULL) return;
 sscanf(line," %s %u %d %d %d %s %u %d",id,&size,&bitrate,&frequency,&number,user,&ip,&speed);

 strcpy(result->filename, filename);
 strcpy(result->id, id);
 result->number = number;
 result->ip = ip;
 strcpy(result->user, user);
 result->size = size;
 result->bitrate = bitrate;
 result->frequency = frequency;
 result->speed = speed;
 result->type = search;
 result->next = NULL;
 result->prev = NULL;
 if (speed<0) return;
 search_update(result);
}

void napster_mess_browseresponse(char * line)
{
 /* <nick> "<filename>" <md5> <size> <bitrate> <frequency> <time> */
 char * end, * rest;
 search_result * result;
 result=malloc(sizeof(search_result));
 end = strchr(line, (int) '\"');
 *(end-1)='\0';
 strcpy(result->user, line);
 rest=strchr(end+1, (int) '\"');
 *(rest)='\0';
 strcpy(result->filename, end+1);
 sscanf(rest+1," %s %u %d %d %d ", result->id, &result->size,
                                   &result->bitrate, &result->frequency,
                                   &result->number);
 result->next=NULL;
 result->type=browse;
 result->speed=browsed_user.speed;
 result->ip=browsed_user.ip;
 search_update(result);
}

void napster_mess_completeresponse(char * line)
{
 os_writec('\4');
 printf("%s\n", line);
}

/*

What did Big Ben say to the leaning tower of Pisa?

I've got the time if you've got the inclination.

*/

void napster_mess_searchend()
{
 search_finish();
}

void napster_mess_dlack(char * buffer)
{
 /*char motdt[100];*/
 int attempts = 0;
 char getstring[300];
 char fn[300], fnt[255];
 char *filename,*rest,user[40],id[40];
 int port,speed;
 unsigned int ip;
 int tno=-1, i;
 int b, used=0;
 char dot[]=".";
 socket_sockaddr otheruser;
 filename = strchr(buffer,'\"');
 filename[0] = '\0';
 filename ++;
 rest = strchr(filename,'\"');
 rest[0] = '\0';
 rest++;
 sscanf(buffer,"%s %u %d ",user,&ip,&port);
 sscanf(rest," %s %d",id,&speed);
 //sprintf(debugmsg, "Connect to port %d", port);
 //motd_addline(debugmsg);

 if(error(xos_read_var_val("Riscster$MusicDir", fn, 300, 0, os_VARTYPE_STRING,
                                                          &used, NULL, NULL)))
 {
  ui_messalert("ERRMPNS");
  return;
 }
 fn[used]='\0';
 for (i=0; i<C_DOWNLOADS; i++)
 {
  if (downloads[i].active==getinfo)
  {
   if (strcmp(downloads[i].filename, filename)==0) tno=i;
  }
 }
 if (tno==-1)
 {
  return;
 }
 strcpy(fnt, filename);
 strcat(fn, dot);
 strcat(fn, napster_sanefile(fnt));
 /*os_writec('\4');
 printf("%s\n", fn);*/
 downloads[tno].filehandle=NULL;
 while (attempts<MAX_FILEATTS && downloads[tno].filehandle==NULL)
 {
  sprintf(fnt, "%s(%d)", fn, attempts);
  downloads[tno].filehandle=fopen(fnt, "wb");
  attempts++;
 }
 if (downloads[tno].filehandle==NULL)
 {
  ui_messalert("ERRSCRAP");
  napster_download_end(tno);
  return;
 }
 error(xosfile_set_type(fnt, 0x1ad));
 /*downloads[tno].totalsize=(search_resblock+search_currentres)->size;*/

 if (port==0)
 {
  sprintf(getstring, "%s \"%s\" 0", downloads[tno].user, filename);
  napster_send(getstring, 500, strlen(getstring));
  return;
 }
 if(error(xsocket_creat(socket_AF_INET, socket_SOCK_STREAM, socket_IPPROTO_IP,
                                                 &downloads[tno].socket)))
 {
  ui_messalert("ERRCNCN");
  napster_download_end(tno);
  return;
 }
 error(xsocket_ioctl(downloads[tno].socket, socket_FIONBIO, (byte *) &on));
 otheruser.sockaddr_in.af=socket_AF_INET;
 otheruser.sockaddr_in.port=portconv(port);
 otheruser.sockaddr_in.addr=ip;
 for(b=0; b<8; b++) otheruser.sockaddr_in.data[b]=0;

 if(nerror(xsocket_connect(downloads[tno].socket, &otheruser, sizeof(otheruser))))
 {
  ui_messalert("ERRCNCN");
  /*xsocket_close(downloads[tno].socket);*/
  napster_download_end(tno);
  return;
 }
 downloads[tno].active=tc;
 downloads[tno].headcount=0;
}

void napster_handle_nfconnected(int tno)
{
 char getstring[300];
 int cc=0;
 nerror(xsocket_send(downloads[tno].socket, (byte *) "GET", 3, 0, &cc));
 sprintf(getstring, "%s \"%s\" 0", username, downloads[tno].filename);
 cc=0;
 nerror(xsocket_send(downloads[tno].socket,
                            (byte *) getstring, strlen(getstring), 0, &cc));
 downloads[tno].active=get_one;
 search_updatebar(tno);
}

void napster_handle_one(int tno)
{
 int cc=0;
 char mustbeone;
 nerror(xsocket_recv(downloads[tno].socket, (byte *) &mustbeone, 1, 0, &cc));
 if (mustbeone!='1')
 {
  ui_messalert("ERRIHE");
  napster_download_end(tno);
 }
 downloads[tno].headcount=0;
 downloads[tno].active=get_header;
 search_updatebar(tno);
}

char * napster_sanefile(char * file)
{
 char * nfile;
 nfile=strrchr(file, (int) '\\');
 if (nfile!=NULL) file=nfile+1;
 nfile=file;
 while (*nfile!='\0')
 {
  /*if (*nfile=='.') *nfile='/';
  if (*nfile==' ') *nfile='_';*/
  switch(*nfile)
  {
   case '.': *nfile='/'; break;
   case ' ': *nfile=''; break;
   case ':': *nfile='-'; break;
   case '*': *nfile='x'; break;
   case '#': *nfile='H'; break;
   case '$': *nfile='S'; break;
   case '&': *nfile='+'; break;
   case '@': *nfile='a'; break;
   case '^': *nfile='\''; break;
   case '%': *nfile='/'; break;
   case '\\': *nfile='/'; break;
   default: break;
  }
  nfile++;
 }
 return(file);
}

void napster_mess_motd(char * buffer)
{
 /*os_writec('\4');
 printf("%s\n", buffer);*/
 motd_addline(buffer);
}

void napster_handle_input(void)
{
 char buffer2[0xffff];
 switch(mess_stat)
 {
  case get_head:
  {
   int cc=0, i=0;
   FILE * messydebug;
   if(nerror(xsocket_recv(server, (byte *) &mess_header[mess_recvd],
                                        1, 0, &cc)))
   {
    ui_messalert("ERRCOMS");
    napster_close();
    return;
   }

   mess_recvd+=cc;
   if(mess_recvd==4)
   {
    if (pversion==proto_old) {
      mess_len=mess_header[0]|(mess_header[1]<<8);
      mess_command=mess_header[2]|(mess_header[3]<<8);
    } else {
      mess_len=mess_header[1]|(mess_header[0]<<8);
      mess_command=mess_header[3]|(mess_header[2]<<8);
    }
    for(i=0; i<sizeof(buffer); i++) buffer[i]='\0';
    if (mess_len>0) mess_stat=get_data;
      else napster_handle_message(mess_command, buffer);
    mess_recvd=0;
    /*messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
    fprintf(messydebug, "Length: %d  Type: %d\n", mess_len, mess_command);
    fclose(messydebug);*/
   }
  }
  break;
  case get_data:
  {
   int cc=0, i=0;
   /*FILE * messydebug;
   messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
   fprintf(messydebug, "Got Already: %d\n", mess_recvd);
   fclose(messydebug);*/
   for (i=0; i<sizeof(buffer2); i++) buffer2[i]='\0';
   if(nerror(xsocket_recv(server, (byte *) buffer2,
                            (mess_len-mess_recvd), 0, &cc)))
   {
    ui_messalert("ERRCOMS");
    napster_close();
   }
   /*messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
   fprintf(messydebug, "Just Got: %d\n", cc);
   fclose(messydebug);*/
   memcpy(buffer+mess_recvd, buffer2, cc);
   mess_recvd+=cc;

   if(mess_recvd==mess_len)
   {
    napster_handle_message(mess_command, buffer);
    mess_stat=get_head;
    mess_len=0;
    mess_command=0;
    mess_recvd=0;
   }
  }
  break;
 }
}

void napster_handle_message(int mc, char * buffer)
{
 #ifdef DEBUG
  FILE * messydebug;
  messydebug=fopen("RAM::RamDisc0.$.messdebug", "a");
  fprintf(messydebug, "%d: %s\n", mc, buffer);
  fclose(messydebug);
 #endif
 switch(mc)
 {
  case 0:
  napster_mess_loginerror(buffer); break;
  case 3:
  napster_mess_loginack(buffer); break;
  case 8:
  napster_mess_regsuccess(); break;
  case 9:
  napster_mess_alreadyreg(); break;
  case 10:
  napster_mess_invalidreg(); break;
  case 201:
  napster_mess_searchresponse(buffer); break;
  case 202:
  case 213:
  napster_mess_searchend(); break;
  case 205:
  napster_mess_privatemessage(buffer); break;
  case 212:
  napster_mess_browseresponse(buffer); break;
  case 204:
  napster_mess_dlack(buffer); break;
  case 216:
  napster_mess_completeresponse(buffer); break;
  case 217:
  /*
  os_writec('\4');
  printf("Done!\n"); break;
  */
  case 404:
  napster_mess_error(buffer); break;
  case 621:
  napster_mess_motd(buffer); break;
  case 751:
  napster_send("", 752, 0); /*ping response (pong)*/
  break;
 }
}

int napster_init_server()
{
 socket_sockaddr localport;
 int b;
 int tryport=6698;
 if(nerror(xsocket_creat(socket_AF_INET,
                  socket_SOCK_STREAM,
                  socket_IPPROTO_IP, &listen))) return(-1);
 nerror(xsocket_ioctl(listen, socket_FIONBIO, (byte *) &on));
 do
 {
  tryport++;
  localport.sockaddr_in.af=socket_AF_INET;
  localport.sockaddr_in.port=portconv(tryport);
  localport.sockaddr_in.addr=0;
  for(b=0; b<8; b++) localport.sockaddr_in.data[b]=0;

 }while(xsocket_bind(listen, &localport, sizeof(localport))&&
                                                     (tryport<6800));
 listening_port=tryport;
 if(nerror(xsocket_listen(listen, 5))) return(-2);
 return(0);
}

int napster_end_server()
{
 return(nerror(xsocket_close(listen)));
}

void napster_mess_privatemessage(char * buffer)
{
 //os_writec('\4');
 //printf("%s", buffer);
 motd_addline("~rPrivate message received:");
 motd_addline(buffer);
}



