#!/usr/bin/perl
#
# This software may be used and distributed according to the terms
# of the GNU Public License, incorporated herein by reference
#
# MyNapster WebClient copyright 2000
# info@mynapster.com
#
# You are welcome to modify and distribute this code                            

package Napster;

use IO::Socket;
use FileHandle;
use CGI;
##use LWP::Simple; 
use strict;


sub new
{
  my $class = $_[0];
  bless {
          nick => $_[1],
          pass => $_[2], 
          socket => ""
        }, $class;
}

##sub get_servers_list
##{
##    my $content = get('http://www.napigator.com/servers.php?version=105');
##    my @servers = split /\n/, $content;
##    return @servers;
##}


sub register_new_user
{
  my ($object, $new_nick, $new_pass, $new_email) = @_;
  my ($type, $msg, $response, $ip, $port, $socket);

  $socket = IO::Socket::INET->new(PeerAddr => "server.napster.com",
                                  PeerPort => 8875,
                                  Type => SOCK_STREAM,
                                  Timeout => 15) 
    or return (-1, -1);
   $response = <$socket>;

   ($ip, $port) = split ":" , $response;
   close($socket);

   $socket = IO::Socket::INET->new(PeerAddr => $ip,
                                           PeerPort => $port,
                                           Type => SOCK_STREAM,
                                           Timeout => 15) 
     or  return (-2,-2);
   $socket->autoflush(1);

   unless(_sendpacket($socket, 7, $new_nick)) {
     close($socket);
     return (-3, -3);
   };

   ($type, $msg) = _recvpacket($socket);
   if ($type == 8) 
   {
     $msg = sprintf("%s %s %d \"%s\" %d %s", $new_nick, $new_pass,
                    9002, "v2.0 BETA 6", 10, $new_email);
     unless(_sendpacket($socket, 6, $msg)) {
       close($socket);
       return (-4, -4);
     };
     ($type, $msg) = _recvpacket($socket);
   };
   close($socket);
   return $type, $msg;
}



sub connect
{
  my ($type, $msg, $response, $ip, $port, $socket);

  if (@_ == 1) 
  {
    $socket = $_[0]->{socket}; 
    $socket = IO::Socket::INET->new(PeerAddr => "server.napster.com",
                                    PeerPort => 8875,
                                    Type => SOCK_STREAM,
                                    Timeout => 15) 
      or return (-1,-1);

    $response = <$socket>;

    ($ip, $port) = split ":" , $response;
    close($socket);
   }
   else 
   {
     $ip = $_[1];
     $port = $_[2];
   };

  $_[0]->{socket} = IO::Socket::INET->new(PeerAddr => $ip,
                                          PeerPort => $port,
                                          Type => SOCK_STREAM,
                                          Timeout => 15) 
    or return (-1,-1);
  $_[0]->{socket}->autoflush(1);

  $msg = sprintf("%s %s %d \"%s\" %d", $_[0]->{nick}, $_[0]->{pass},
                    9002, "v2.0 BETA 5", 10);
  unless(_sendpacket($_[0]->{socket}, 2, $msg)) {
    close($_[0]->{socket});
    return (-1,-1);
  };

  ($type, $msg) = _recvpacket($_[0]->{socket});

  if ($type == 3) {
   return ($ip, $port);
  }
  else {
    return (-1,-1);
  };
}

sub disconnect 
{
  close($_[0]->{socket}); 
}

sub search
{
  my %songs;
  my ($msg, $type);
  my ($object, %arg) = @_;
  $msg = sprintf("%s \"%s\" %s %d", 'FILENAME CONTAINS', 
                 $arg{FILENAME}, 'MAX_RESULTS', $arg{MAX_RESULTS});  
  unless(_sendpacket($object->{socket}, 200, $msg)) {
    return;
  };

  do {
    ($type, $_) = _recvpacket($object->{socket});
  } until (($type == 201) or ($type == 202));

 if ($type == 202)
 {
   return;
 };

  do {
    if ($type == 201) {
      s/(".*")//; 
      $songs{$1} = $_;  
      ($type, $_) = _recvpacket($object->{socket});
    };
  } until ($type == 202);  
  return %songs;
}


sub get_song
{
  my ($msg, $type, $ip, $port, $client_socket, $buf, $server, $dtype);
  my ($object, $song, $nick, $size) = @_;
  my $filename = (split(/\\/, $song))[-1];
     $filename =~ s/"//g;

  $msg = sprintf("%s %s", $nick, $song);
  unless(_sendpacket($object->{socket}, 203, $msg)) 
  {
     close($object->{socket});
     return -203;
  };

  do {
    ($type, $_) = _recvpacket($object->{socket});
    if (($type != 214) and ($type != 621) and ($type != 204))
    {
      close($object->{socket});
      return -$type;
    };
  } until ($type == 204);


  ($nick, $ip, $port) = split;
  $ip = inet_ntoa pack "L", $ip;

  if ($port != 0) {
    $dtype = 0;
    $client_socket = IO::Socket::INET->new(PeerAddr => $ip,
                                           PeerPort => $port,
                                           Type => SOCK_STREAM,
                                           Timeout => 60) 
    or return -1;

    unless ($client_socket->sysread($buf, 1) == 1) {
      close($object->{socket});
      close($client_socket);    
      return -2;
    }

    unless ($buf eq '1') {
      close($object->{socket});
      close($client_socket);
      return -3;
    }

    unless ($client_socket->syswrite("GET", 3) == 3) {
      close($object->{socket});
      close($client_socket);
      return -4;
    }

    $buf = sprintf "%s %s %d", $_[0]->{nick}, $song, 0;
  
    unless ($client_socket->syswrite($buf, length $buf)) {
      close($object->{socket});
      close($client_socket);
      return -5;
    }

    unless ($client_socket->sysread($size, 1024) && $size =~ /^\d+$/) {
      close($object->{socket});
      close($client_socket);
      return -6;
    }

    my $buf;
  
    print "Content-type: music/mp3\n", "Content-Disposition: attachment; filename=$filename\n\n"; 

    while ($size > 0) {
      unless ($client_socket->sysread($buf, 10240)) {
        close($object->{socket});
        close($client_socket);
        return -7;
      }
      $size -= length $buf;
      print $buf;
    }

  }
  else
  {  
    $port = 9001;
    $dtype = 1;
    while (!($server))
    { 
      $server = IO::Socket::INET->new(LocalPort => $port,
                                      Type => SOCK_STREAM, 
                                      Reuse => 1, 
                                      Listen => 1 ) 
       or $port = $port + 1;
       if ($port > 9100)
       {
         return (-$port);
       };
    };


    $msg = sprintf("%s %s", $nick, $song);
    unless(_sendpacket($object->{socket}, 500, $msg)) {
      close($object->{socket});
      close($server);
      return -8;
    };

    $client_socket = $server->accept(); 
    unless ($client_socket->syswrite("1", 1) == 1) {
      close($object->{socket});
      close($client_socket);
      close($server);
      return -9;
    }

    unless ($client_socket->sysread($buf, 4) == 4 && $buf eq 'SEND') {
      close($client_socket);
      return -91;
    }

    unless ($client_socket->sysread($buf, 2048)) {
      close($object->{socket});
      close($client_socket);
      close($server);
      return -10;
    }


    my $buf;

    unless ($client_socket->syswrite(0, 1) == 1) {
      close($object->{socket});
      close($server);
      close($client_socket);
      return -11;
    }

    print "Content-type: music/mp3\n", "Content-Disposition: attachment; filename=$filename\n\n"; 

    while ($size > 0) {
      unless ($client_socket->sysread($buf, 10240)) {
        close($client_socket);
        close($server);
        return -12;
      }

      $size -= length $buf;
      print $buf;
    }
    close($server);
  };
  close($object->{socket});
  close($client_socket);
  return $dtype;
}


sub _sendpacket
{
  my ($socket, $type, $msg) = @_;
  my $header = pack("vv", length $msg, $type);
  unless($socket->syswrite($header, 4)) {
    return;
  };
  unless($socket->syswrite($msg, length $msg)) {
    return;
  };
}
 
sub _recvpacket
{
  my $socket = shift;
  my ($header, $msg);
  unless($socket->sysread($header, 4)) {
    return;
  };

  my ($len, $type) = unpack("vv", $header);
  return ($type, $msg) if $len == 0;

  until (defined $msg && length $msg == $len) {
    my $msg_len = defined $msg ? length $msg : 0;
    unless($socket->sysread($msg, ($len - $msg_len), $msg_len))
    {
      return;
    };
  }
  return ($type, $msg);
}


1;



