#
#  CDDB - Read the CDDB entry for an audio CD in your drive
#
#  This module/script gets the CDDB info for an audio cd. You need
#  LINUX, a cdrom drive and an active internet connection in order
#  to do that.
#
#  (c) Devon Jones <soulcatcher@evilsoft.org
#  Derived from code by (c) 2000 Armin Obersteiner <armin@xos.net>
#
#  LICENSE
#
#  This library is released under the same conditions as Perl, that
#  is, either of the following:
#
#  a) the GNU General Public License as published by the Free
#  Software Foundation; either version 1, or (at your option) any
#  later version.
#
#  b) the Artistic License.
#


package ObjCDDB;

# Necessary to be able to use Makefile.pl
require 5.002;
$VERSION = "0.01";

###############################################################################
#
# ObjCDDB
# Version 0.01
# Date: 06/29/2000
# Copyright (c) 1999, 2000 Devon Jones (soulcatcher@evilsoft.org).
# All rights reserved. This program is free software;
# you can redistribute it and/or modify it under the same terms as Perl itself. 
#
###############################################################################

#use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
use Fcntl;
use IO::Socket;
$IMPORTED_MAIL = 0;
#eval {
#  use Mail::Internet;
#  use Mail::Header;
#  $IMPORTED_MAIL = 1;
#};

require Exporter;

@ISA = qw(Exporter AutoLoader);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw(
  get_cddb
  get_discids
);

  
###############################################################################
# Public Methods                                                              #
###############################################################################

sub new {
###############################################################################
#          Name: ripenc->new (Public)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($this) = shift;
  my($host) = shift;
  my($port) = shift;
  my($device) = shift;
  my($username) = shift;
  my($domain) = shift;
  my($smtp) = shift;
  my($cddbsubmit) = shift;
  my($class) = $this;
  my($self) = {};
  bless $self, $class;
  $self->init($host, $port, $device, $username, $domain, $smtp, $cddbsubmit);
  return $self;
}

sub GetCDDBId {
###############################################################################
#          Name: ripenc->GetCDDBId (Public)
#    Parameters: None
#       Returns: 1/undef
#  Precondition: 
# Postcondition: 
#        Throws: ReadTOC - "cd: cannot open cdrom"
#                ReadTOC - "cd: cannot read toc"
#                ReadTOC - "cd: cannot read track (num) info"
#                PrintCDQuery - "socket: handshake error at cddb db: (Host):(Port)"
#                PrintCDQuery - "cddb: query error at cddb db: (Host):(Port)"
#                "cddb: cannot connect to cddb db: (Host):(Port)"
#                "cddb: not welcome at cddb db"
#                "cddb: no match"
#                "cddb: unknown: (Number Code)"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($socket, $return, $id2, $err, $i, $n, $n1, $tit) = "";
  my(@list) = ();
  
  $self->Reset;
  $self->ReadTOC();
  $self->GenerateDiscId();

  $self->OpenCDDBSocket();  
  $socket = $self{'socket'};

  $return = $self->PrintCDQuery();
  $self->CloseCDDBSocket;

  if($return =~ m/^202\s+/) {
    #No Match
    die "cddb: No Matching CD";
  }
  elsif($return =~ m/^211\s+/) {
    #Multiple Matches
    while(<$socket>) {
      last if(/^\./);
      push(@list, "200 ".$_);
    } 
 
    return @list;
  }
  elsif($return =~ m/^200\s+/) {
    push(@list, $return);
    return @list;
  } else {
    die "cddb: unknown error: $return";
  }
}

sub GetCDInfo {
###############################################################################
#          Name: ripenc->GetCDInfo (Public)
#    Parameters: $return - A reply from the cddb host
#       Returns: 1/undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($return) = shift;
  my($cat, $idn, $at, $artist, $title, $i, $t, $socket) = "";
  
  $self->ReadTOC();
  $self->GenerateDiscId();

  $self->OpenCDDBSocket;
  $socket = $self{'socket'};

  #200 misc 0a01e802 Meredith Brooks / Bitch Single 
  ($cat,$idn,$at) = $return =~ /^\d\d\d\s+(\S+)\s+(\S+)\s+(.*)/;
 
  if($at =~ /\//) {
    ($artist, $title) = $at =~ /(.*?)\s*\/\s*(.*)/;
  } else {
    $artist = $at;
    $title = $at;
  }
 
  $self->SetArtist($artist);
  chop $title;
  $self->SetTitle($title);
  $self->SetCategory($cat);
  $self{'id'} = $idn;
  
  print $socket "cddb read $cat $idn\n";
  

  $i = 1;
  while(<$socket>) {
    last if((/^\./) || (/^500 Command syntax error./));
    if(/^EXTD\=\s*(.*)/) {
      $self->SetExtd($1);
    }
    elsif(/^PLAYORDER\=\s*(.*)/) {
      $self{'playorder'} = $1;
    }
    elsif(/^EXTT(\d+)\=\s*(.*)/) {
      $t = $2;
      chop $t;
      unless (defined $self{'ext'}[$1 + 1]) {
        $self->SetTrackExt($t, $1 + 1);
      } else {
        $self->SetTrackExt($self{'ext'}[$1 + 1] . $t, $1 + 1);
      }
    }
    elsif(/^TTITLE(\d+)\=\s*(.*)/) {
      $t = $2;
      chop $t;
      unless (defined($self{'track'}[$1 + 1])) {
        $self->SetTrack($t, $1 + 1);
      } else {
        $self->SetTrack($cd{'track'}[$1 + 1] . $t, $1 + 1);
      }
    }
  }
  

  $self{'tno'} = $#{$self{track}};
  $self->CloseCDDBSocket;

  return 1;
}

sub WriteCDInfo {
###############################################################################
#          Name: ripenc->WriteCDInfo (Public)
#    Parameters: 
#       Returns: 1/undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################

##############################################
# Not fully functional yet.  Save for later. #
##############################################
  my($self) = shift;
  my($return, $err, $go, $i, $time, $mail, $from, $header, $subject) = 0;
  my(@message_body);
  
  $self->ReadTOC();
  $self->GenerateDiscId();

  if(!$IMPORTED_MAIL) {
    die "ObjCDDB: WriteCDInfo needs Mail::Internet and Mail::Header, which appear not to be installed";
  }

  unless(defined($self{'cat'}) && defined($self{'id'}) && defined($self{'artist'}) && defined($self{'title'})) {
    die "ObjCDDB: Category, Artist & Title must be set";
  }

  $from = $self{'LOGIN'} . "@" . $self{'DOMAIN'};
  $subject = "cddb $self{'cat'} $self{'id'}";

  $header = new Mail::Header;
  $header->add( From    => $from );
  $header->add( To      => $self{'CDDBMAIL'} );
  $header->add( Subject => $subject);
  
  @message_body = $self->GenerateCDDBWriteMessage();
  
  $mail = new Mail::Internet(Header => $header, Body => \@message_body);
  $mail->smtpsend(Host => $self{'SMTP'}) or die "smtp: could not send CDDB record (bad SMTP host $self{'SMTP'}?) $!\n";

}


sub ExitCDDB {
###############################################################################
#          Name: ripenc->ExitCDDB (Public)
#    Parameters: 
#       Returns: 1/undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  
  $self->CloseCDDBSocket;
}

sub ReturnArtist {
###############################################################################
#          Name: ripenc->ReturnArtist (Public)
#    Parameters: None
#       Returns: Artist Name
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'artist'};
}

sub ReturnTitle {
###############################################################################
#          Name: ripenc->ReturnTitle (Public)
#    Parameters: None
#       Returns: Title of CD
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'title'};
}

sub ReturnCategory {
###############################################################################
#          Name: ripenc->ReturnCategory (Public)
#    Parameters: None
#       Returns: CD Category
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'cat'};
}

sub ReturnCDDBid {
###############################################################################
#          Name: ripenc->ReturnCDDBid (Public)
#    Parameters: None
#       Returns: CD's cddb id
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'id'};
}

sub ReturnExtd {
###############################################################################
#          Name: ripenc->ReturnExtd (Public)
#    Parameters: None
#       Returns: CD Extended info
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'extd'};
}

sub ReturnNumTracks {
###############################################################################
#          Name: ripenc->ReturnNumTracks (Public)
#    Parameters: None
#       Returns: Number of Tracks on CD
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  return $self{'tno'};
}

sub ReturnTrack {
###############################################################################
#          Name: ripenc->ReturnTrack (Public)
#    Parameters: Track Number
#       Returns: Track title / undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($num) = shift;
  if($num <= $self{'tno'} && $num > 0) {
    return $self{'track'}[$num];
  } else {
    return undef;
  }
}

sub ReturnTrackFrames {
###############################################################################
#          Name: ripenc->ReturnTrack (Public)
#    Parameters: Track Number
#       Returns: Track title / undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($num) = shift;
  if($num <= $self{'tno'} && $num > 0) {
    return $self{'toc'}[$num]{'frames'};
  } else {
    return "0";
  }
}

sub ReturnTrackExt {
###############################################################################
#          Name: ripenc->ReturnTrackExt (Public)
#    Parameters: Track Number
#       Returns: Track Extended info
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($num) = shift;
  if($num <= $self{'tno'} && $num > 0) {
    return $self{'ext'}[$num];
  } else {
    return undef;
  }
}

sub SetArtist {
###############################################################################
#          Name: ripenc->SetArtist (Public)
#    Parameters: Artist
#       Returns: Artist Name
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($artist) = shift;
  
  $self{'artist'} = $artist;
  
  return 1;
}

sub SetTitle {
###############################################################################
#          Name: ripenc->SetTitle (Public)
#    Parameters: None
#       Returns: Title of CD
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($title) = shift;
  
  $self{'title'} = $title;
  
  return 1;
}

sub SetCategory{
###############################################################################
#          Name: ripenc->SetCategory (Public)
#    Parameters: None
#       Returns: CD Category
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($cat) = shift;
  
  foreach $CATEGORY (@{$self{"CATEGORIES"}}) {
    if($CATEGORY eq $cat) {
      $self{'cat'} = $cat;
      return 1;
    }
  }
  
  return undef;
}

sub SetExtd {
###############################################################################
#          Name: ripenc->SetExtd (Public)
#    Parameters: None
#       Returns: CD Extended info
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($extd) = shift;
  
  $self{'extd'} = $extd;
  
  return 1;
}

sub SetTrack {
###############################################################################
#          Name: ripenc->SetTrack (Public)
#    Parameters: Track Number
#       Returns: Track title / undef
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($track) = shift;
  my($num) = shift;
  
  if($num <= $self{'total'} && $num > 0) {
    $self{'track'}[$num] = $track;
    return 1;
  }

  return undef;
}

sub SetTrackExt {
###############################################################################
#          Name: ripenc->SetTrackExt (Public)
#    Parameters: Track Number
#       Returns: Track Extended info
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($ext) = shift;
  my($num) = shift;
  
  if($num <= $self{'total'} && $num > 0) {
    $self{'ext'}[$num] = $ext;
  }

  return undef;
}



###############################################################################
# Private Methods                                                         #
###############################################################################

sub init {
###############################################################################
#          Name: ripenc->init (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($host) = shift;
  my($port) = shift;
  my($device) = shift;
  my($username) = shift;
  my($domain) = shift;
  my($smtp) = shift;
  my($cddbsubmit) = shift;

  # linux magic (from c headers)
  $self{'CDROMREADTOCHDR'} = 0x5305;
  $self{'CDROMREADTOCENTRY'} = 0x5306; 
  $self{'CDROM_MSF'} = 0x02;

  # set cddb hostname
  if(!$host) {
    $self{'CDDB_HOST'} = "cddb.cddb.com";
  } else {
    $self{'CDDB_HOST'} = $host;
  }

  # set cddb port  
  if(!$port) {
    $self{'CDDB_PORT'} = 888;
  } else {
    $self{'CDDB_PORT'} = $port;
  }

  # set cdrom device
  if(!$device) {
    $self{'CD_DEVICE'} = "/dev/cdrom";
  } else {
    $self{'CD_DEVICE'} = $device;
  }

  # set current username
  if(!$username) {
    $self{'USERNAME'} = "root";
  } else {
    $self{'USERNAME'} = $username;
  }

  # set your domain name
  if(!$domain) {
    $self{'DOMAIN'} = $ENV{'HOSTNAME'};
  } else {
    $self{'DOMAIN'} = $domain;
  }

  # set cddb hostname
  if(!$smtp) {
    $self{'SMTP'} = "mail";
  } else {
    $self{'SMTP'} = $smtp;
  }

  # set cddb hostname
  if(!$cddbsubmit) {
    $self{'CDDBSUBMIT'} = 'freedb-submit@freedb.org';
    #$self{'CDDBMAIL'} = 'cddb-test@submit.cddb.com';
  } else {
    $self{'CDDBSUBMIT'} = $cddbsubmit;
  }

  @{$self{'CATEGORIES'}} = qw( blues
                               classical
                               country
                               data
                               folk
                               jazz
                               misc
                               newage
                               reggae
                               rock
                               soundtrack );
  
  $self{'LOGIN'} = $ENV{'USER'};
  $self->ReadTOC();
  $self->GenerateDiscId;  
}

sub Reset {
###############################################################################
#          Name: ripenc->Reset (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;

  if(defined($self{'total'})) {
    for($i = 0; $i <= $self{'total'} + 1; $i++) {
      undef($self{'toc'}[$i]{'min'});
      undef($self{'toc'}[$i]{'sec'});
      undef($self{'toc'}[$i]{'frame'});
      undef($self{'toc'}[$i]{'frames'});
    }

    undef($self{'total'});
    undef($self{'id'});
    undef($self{'artist'});
    undef($self{'title'});
    undef($self{'cat'});
    undef($self{'id'});
    undef($self{'extd'});
    undef($self{'playorder'});

    if(defined($self{'tno'})) {
      for($i = 1; $i <= $self{'tno'}; $i++) {
        undef($self{'ext'}[$i]);
        undef($self{'track'}[$i]);
      }
      undef($self{'tno'});
      undef($self{'track'});
    }
  }
}

sub ReadTOC {
###############################################################################
#          Name: ripenc->ReadTOC (Private)
#    Parameters: None
#       Returns: Nothing
#  Precondition: 
# Postcondition: 
#        Throws: "cd: cannot open cdrom"
#                "cd: cannot read toc"
#                "cd: cannot read track (num) info"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  
  my($tochdr, $tocentry, $d, $min, $sec, $frame, $start, $end, $i, $j) = "";
  my(@tracks) = ();

  sysopen(CD, $self{'CD_DEVICE'}, O_RDONLY | O_NONBLOCK) or die "cd: cannot open cdrom";
  ioctl(CD, $self{'CDROMREADTOCHDR'}, $tochdr) or die "cd: cannot read toc";
  ($start, $end) = unpack("CC", $tochdr);

  for($i = $start; $i <= $end; $i++) {
    push(@tracks, $i);
  }
  push(@tracks, 0xAA);

  $j = 0;
  
  foreach $i (@tracks) {
    $tocentry = pack("CCC", $i, 0, $self{'CDROM_MSF'});
    ioctl(CD, $self{'CDROMREADTOCENTRY'}, $tocentry) or die "cd: cannot read track $i info";
    ($d, $d, $d, $d, $min, $sec, $frame) = unpack("CCCCCCC", $tocentry);
    $self{'toc'}[$j]{'min'} = $min;
    $self{'toc'}[$j]{'sec'} = $sec;
    $self{'toc'}[$j]{'frame'} = $frame;
    $self{'toc'}[$j]{'frames'} = $sec * 75 + $min * 60 * 75;
    $j++;
   
  }   
  close(CD);
 
  $self{'total'} = $j - 1;
  $self{'tno'} = $j - 1;
}                                      

sub GenerateSum {
###############################################################################
#          Name: ripenc->GenerateSum (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($n) = shift;
  
  my($ret) = 0;

  while ($n > 0) {
    $ret += ($n % 10);
    $n = int $n / 10;
  }
  
  return $ret;
}                       

sub GenerateDiscId {
###############################################################################
#          Name: ripenc->GenerateDiscId (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: Nothing
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($i) = 0;
  my($t) = 0;
  my($n) = 0;

  while ($i < $self{'total'}) {
    $n = $n + $self->GenerateSum(($self{'toc'}->[$i]->{min} * 60) + $self{'toc'}->[$i]->{sec});
    $i++;
  }
  
  $t = (($self{'toc'}->[$self{'total'}]->{min} * 60) + $self{'toc'}->[$self{'total'}]->{sec}) - (($self{'toc'}->[0]->{min} * 60) + $self{'toc'}->[0]->{sec});

  $self{'id'} = (($n % 0xff) << 24 | $t << 8 | $self{'total'});
  
  $self{'tno'} = $self{'total'};
  return 1;
}                                       

sub GenerateCDDBWriteMessage {
###############################################################################
#          Name: ripenc->OpenCDDBSocket (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: "cddb: cannot connect to cddb db: (Host):$self(Port)"
#                "cddb: not welcome at cddb db"
#                "cddb: handshake error at cddb db: (Host):(Port)"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my(@message_body);
  
  push(@message_body, "# xmcd CD database file\n");
  push(@message_body, "#\n");
  push(@message_body, "# Track frame offsets:\n");

  # Send frames for each track
  for($i = 0; $i < $self{'total'}; $i++) {
    push(@message_body, "# $self{'toc'}->[$i]->{frames}\n");
  }
  $time = ($self{'toc'}[$self{'total'}]{'min'} * 60) + $self{'toc'}[$self{'total'}]{'sec'};
  push(@message_body, "#\n");
  push(@message_body, "# Disc length: $time seconds\n");
  push(@message_body, "#\n");
  push(@message_body, "# Submitted via: ObjCDDB.pm\n");
  push(@message_body, "#\n");
  push(@message_body, "DISCID=$self{'id'}\n");
  push(@message_body, "DTITLE=$self{'artist'} / $self{'title'}\n");

  for($i = 0; $i < $self{'total'}; $i++) {
    push(@message_body, "TTITLE$i=$self{'track'}[$i+1]\n");
  }
  
  push(@message_body, "EXTD=$self{'EXTD'}\n");

  for($i = 0; $i < $self{'total'}; $i++) {
    push(@message_body, "EXTT$i=$self{'ext'}[$i+1]\n");
  }
  
  push(@message_body, "PLAYORDER=\n");
  
  return @message_body;
}

sub OpenCDDBSocket {
###############################################################################
#          Name: ripenc->OpenCDDBSocket (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: "cddb: cannot connect to cddb db: (Host):$self(Port)"
#                "cddb: not welcome at cddb db"
#                "cddb: handshake error at cddb db: (Host):(Port)"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  
  unless(defined($self{'socket'})) {

    my($socket) = IO::Socket::INET->new(PeerAddr=>$self{'CDDB_HOST'}, PeerPort=>$self{'CDDB_PORT'}, Proto=>"tcp", Type=>SOCK_STREAM)
      or die "cannot connect to cddb db: $self{'CDDB_HOST'}:$self{'CDDB_PORT'}";
    
    if($socket) {
      $return = <$socket>;
    }
    else {
      die "cddb: could not connect to cddb db: $self{'CDDB_HOST'}:$self{'CDDB_PORT'}";
    }
    
    unless ($return =~ /^2\d\d\s+/) {
      die "cddb: not welcome on cddb db: $self{'CDDB_HOST'}:$self{'CDDB_PORT'}";
    }
  
    # Send CDDB Header Line
    print $socket "cddb hello $self{'USERNAME'} $self{'DOMAIN'} xmcd $VERSION\n";
    $return = <$socket>;
    unless ($return =~ /^2\d\d\s+/) {
      die "cddb: handshake error on cddb db: $self{'CDDB_HOST'}:$self{'CDDB_PORT'}";
    }
    $self{'socket'} = $socket;
  }
}

sub CloseCDDBSocket {
###############################################################################
#          Name: ripenc->OpenCDDBSocket (Private)
#    Parameters: 
#       Returns: 
#  Precondition: 
# Postcondition: 
#        Throws: "cddb: cannot connect to cddb db: (Host):$self(Port)"
#                "cddb: not welcome at cddb db"
#                "cddb: handshake error at cddb db: (Host):(Port)"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  
  if(defined($self{'socket'})) {
    my($socket) = $self{'socket'};
    print $socket "quit\n";
    undef $self{'socket'};
  }
}

sub PrintCDQuery {
###############################################################################
#          Name: ripenc->PrintQuery (Private)
#    Parameters: $socket - An opened socket to a cddb host
#       Returns: $return - A reply from the cddb host
#  Precondition: 
# Postcondition: 
#        Throws: "cddb: query error at cddb db: (Host):(Port)"
#        Author: Devon Jones (soulcatcher@evilsoft.org)
#          Date: 06/29/2000
###############################################################################
  my($self) = shift;
  my($socket) = $self{'socket'};
  my($return, $id2, $i, $err) = "";
  
  # Send CD line with ID number, & total tracks
  $id2 = sprintf "%08x", $self{'id'};
  print $socket "cddb query $id2 $self{'total'}";

  # Send frames for each track
  for ($i = 0; $i < $self{'total'}; $i++) {
    print $socket " $self{'toc'}->[$i]->{frames}";
  }

  # Send Frame offset
  print $socket " ". int(($self{'toc'}->[$self{'total'}]->{frames}-$self{'toc'}->[0]->{frames})/75) ."\n";

  # Get query response (yes we know the cd/no we don't/is it one of these)
  $return = <$socket>;
  $err = $return =~ /^(\d\d\d)\s+/;
  unless ($err) {
    die "cddb: query error at cddb db: $self{'CDDB_HOST'}:$self{'CDDB_PORT'}";
  }
  
  return $return;
}

1;
__END__

# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

ObjCDDB - Read the CDDB entry for an audio CD in your drive

=head1 SYNOPSIS

 use ObjCDDB;

 $cd = new ObjCDDB("cddb.cddb.org", "888", "/dev/cdrom");
 $cd->GetCDInfo($cdid);
  
 $artist = $cd->ReturnArtist;
 print $artist;
 

 # The following are all of the public methods that can be used.
 GetCDDBId
 GetCDInfo
 ExitCDDB
 ReturnArtist
 ReturnTitle
 ReturnCategory
 ReturnCDDBid
 ReturnExtd
 ReturnNumTracks
 ReturnTrack
 ReturnTrackFrames
 ReturnTrackExt

 # The following are public methods that are partially implemented.
 # These are intended for the eventual use of writing CDs back to CDDB.
 # Basically - don't use them.
 WriteCDInfo
 SetArtist
 SetTitle
 SetCategory
 SetExtd
 SetTrack
 SetTrackExt

#  (c) Devon Jones <soulcatcher@evilsoft.org
#  Derived from code (c) 2000 Armin Obersteiner <armin@xos.net>
#
#  LICENSE
#
#  This library is released under the same conditions as Perl, that
#  is, either of the following:
#
#  a) the GNU General Public License as published by the Free
#  Software Foundation; either version 1, or (at your option) any
#  later version.
#
#  b) the Artistic License.
#



