package Net::FreeDB2::Connection::CDDBP; # Copyright 2002, Vincenzo Zocca. # See LICENSE section for usage and distribution rights. require 5.005_62; use strict; use warnings; use Error qw (:try); require Exporter; use AutoLoader qw(AUTOLOAD); #our @ISA = qw(Net::FreeDB2::Connection Exporter); use base qw(Net::FreeDB2::Connection Exporter); # 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. # This allows declaration use Net::FreeDB2::Connection::CDDBP ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our ( $VERSION ) = '$Revision: 0.8.2.4 $ ' =~ /\$Revision:\s+([^\s]+)/; my $FINAL_EOL_RX = '[\r\n]'; my $FINAL_DOT_RX = '[\r\n]\.[\r\n]'; sub new { # Call constructor of super class my $self = &Net::FreeDB2::Connection::new (@_); # Shift out this class specification shift; # Return object return ($self); } sub _initialize { my $self = shift; # Get options my $opt = shift || {}; # Proxy on CDDBP not yet defined defined ($opt->{proxy_host}) && throw Error::Simple ("ERROR: Net::FreeDB2::Connection::CDDBP::_initialize, CDDBP access through proxy not (yet) implemented."); # Initialize super class return ($self->SUPER::_initialize ($opt)); } sub connect { my $self = shift; # Make socket connection use IO::Socket::INET; my $connection = IO::Socket::INET->new ( PeerAddr => $self->getFreeDBHost (), PeerPort => $self->getFreeDBPort () || 8880, ); defined ($connection) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::connect, Failed to instanciate an \'IO::Socket::INET\' object.'); # Set the connection $self->setConnection ($connection); # Send command and wait for reply my $content_ref = $self->waitCommandReply (undef, { 200 => $FINAL_EOL_RX, 201 => $FINAL_EOL_RX, 432 => $FINAL_EOL_RX, 433 => $FINAL_EOL_RX, 434 => $FINAL_EOL_RX, }); # Parse the result and store it use Net::FreeDB2::Response::SignOn; $self->setSignOnResponse (Net::FreeDB2::Response::SignOn->new ({ content_ref => $content_ref })); # Send a hello my $res = $self->hello (); # Disconnect and throw exception if error if ($res->hasError ()) { $self->setConnection (); throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::connect, handshake failed.'); } } sub lscat { my $self = shift; # Send command and wait for reply my $content_ref = $self->waitCommandReply ('cddb lscat', { 210 => $FINAL_DOT_RX, }); # Parse the result my @content = split (/[\n\r]+/, ${$content_ref}); shift (@content); my @cat = (); foreach my $cat (@content) { last if ($cat eq '.'); push (@cat, $cat); } return (@cat); } sub query { my $self = shift; my $entity = shift; # Send command and wait for reply my $cmd = 'cddb query ' . $entity->mkQuery (); my $content_ref = $self->waitCommandReply ($cmd, { 200 => $FINAL_EOL_RX, 211 => $FINAL_DOT_RX, 202 => $FINAL_EOL_RX, 403 => $FINAL_EOL_RX, 409 => $FINAL_EOL_RX, }); # Parse the result use Net::FreeDB2::Response::Query; return Net::FreeDB2::Response::Query->new ({ content_ref => $content_ref, }); } sub read { my $self = shift; my $match = shift; # Send command and wait for reply my $cmd = 'cddb read ' . $match->getCateg () . ' ' . $match->getDiscid (); my $content_ref = $self->waitCommandReply ($cmd, { 210 => $FINAL_DOT_RX, 401 => $FINAL_EOL_RX, 402 => $FINAL_EOL_RX, 403 => $FINAL_EOL_RX, 409 => $FINAL_EOL_RX, }); # Parse the result use Net::FreeDB2::Response::Read; return Net::FreeDB2::Response::Read->new ({ content_ref => $content_ref, }); } sub write { throw Error::Simple ("ERROR: Net::FreeDB2::Connection::CDDBP::write, to be implemented."); } sub log { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::log, to be implemented."); } sub motd { my $self = shift; # Send command and wait for reply my $content_ref = $self->waitCommandReply ('motd', { 210 => $FINAL_DOT_RX, 401 => $FINAL_EOL_RX, }); # Parse the result my @content = split (/[\n\r]+/, ${$content_ref}); my $head = shift (@content); my @motd = (); foreach my $motd (@content) { last if ($motd eq '.'); push (@motd, $motd); } return (@motd); } sub discid { my $self = shift; my $entity = shift; # Send command and wait for reply my $cmd = 'discid ' . $entity->mkQuery (); my $content_ref = $self->waitCommandReply ($cmd, { 200 => $FINAL_EOL_RX, 500 => $FINAL_EOL_RX, }); # Parse the result my @content = split (/[\n\r]+/, ${$content_ref}); my $head = shift (@content); my ($code) = $head =~ /^\s*(\d{3})\s+/; $code == 500 && throw Error::Simple ("ERROR: Net::FreeDB2::Connection::CDDBP::discid, Command Syntax error."); my @head = split (/\s+/, $head); return ($head[4]); } sub proto { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::proto, to be implemented."); } sub sites { my $self = shift; # Send command and wait for reply my $content_ref = $self->waitCommandReply ('sites', { 210 => $FINAL_DOT_RX, 401 => $FINAL_EOL_RX, }); # Parse the result use Net::FreeDB2::Response::Sites; return Net::FreeDB2::Response::Sites->new ({ content_ref => $content_ref, }); } sub stat { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::stat, to be implemented."); } sub ver { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::ver, to be implemented."); } sub update { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::update, to be implemented."); } sub whom { throw Error::Simple ("ERROR: Net::FreeDB2::CDDBP::whom, to be implemented."); } sub setSignOnResponse { my $self = shift; # Set FreeDB/CDDB sign-on response $self->{Net_FreeDB2_Connection_CDDBP}{sign_on_response} = shift; } sub getSignOnResponse { my $self = shift; # Return FreeDB/CDDB sign-on response return ($self->{Net_FreeDB2_Connection_CDDBP}{sign_on_response}); } sub hello { my $self = shift; # Send command and wait for reply my $cmd = 'cddb ' . $self->mkHello (); my $content_ref = $self->waitCommandReply ($cmd, { 200 => $FINAL_EOL_RX, 431 => $FINAL_EOL_RX, 402 => $FINAL_EOL_RX, }); # Parse the result and return it use Net::FreeDB2::Response::Hello; return (Net::FreeDB2::Response::Hello->new ({ content_ref => $content_ref })); } sub quit { my $self = shift; # Send command and wait for reply my $content_ref = $self->waitCommandReply ('quit', { 230 => $FINAL_EOL_RX, }); # Disconnect $self->setConnection (); } sub waitCommandReply { my $self = shift; my $cmd = shift; my $rx = shift; # Check if connection is defined defined ($self->getConnection ()) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::waitCommandReply, no connection available.'); # Set blocking $self->getConnection->blocking (1); # Send command defined ($cmd) && $self->getConnection ()->write ($cmd . "\r\n"); # Wait for code $self->getConnection->read (my $head, 5); $head =~ s/^\s+//; my ($code) = $head =~ /(\d{3})/; exists ($rx->{$code}) || throw Error::Simple ("ERROR: Net::FreeDB2::Connection::CDDBP::waitCommandReply, unknown code '$code' returned."); # Wait for the final DOT or EOL my $content .= $head; $self->getConnection->blocking (0); while (1) { $self->getConnection->read (my $rest, 1024); $content .= $rest; $content =~ /$rx->{$code}/ && last; sleep (1); } # Return the content reference return (\$content); } sub mkHello { my $self = shift; defined ($self->getClientName ()) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::mkHello, \'client_name\' not set.'); defined ($self->getClientVersion ()) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::mkHello, \'client_version\' not set.'); defined ($self->getClientHost ()) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::mkHello, \'client_host\' not set.'); defined ($self->getClientUser ()) || throw Error::Simple ('ERROR: Net::FreeDB2::Connection::CDDBP::mkHello, \'client_user\' not set.'); return ('hello ' . join (' ', $self->getClientUser (), $self->getClientHost (), $self->getClientName (), $self->getClientVersion (), ) ); } 1; __END__ # Below is stub documentation for your module. You better edit it! =head1 NAME Net::FreeDB2::Connection::CDDBP - FreeDB/CDDB concrete connection class using the CDDBP protocol =head1 SYNOPSIS See L. =head1 DESCRIPTION C is an implementation of the abstract C connection class. It uses the CDDBP protocol to connect to C servers. =head1 CONSTRUCTOR =over =item new (OPT_HASH_REF) Implementation of C. Restriction: proxy_host may not be specified as CDDBP access through proxy is not (yet) implemented. Throws a C exception if a mandatory option is missing and passes exceptions from C. =back =head1 METHODS =over =item connect () Implementation of C. Stores a signo on response using C. =item lscat () Implementation of C. =item query (ENTRY) Implementation of C. =item read (MATCH) Implementation of C. =item write (ENTITY) Implementation of C. =item log () Implementation of C. =item motd () Implementation of C. =item discid (ENTRY) Implementation of C. =item proto () Implementation of C. =item sites () Implementation of C. =item stat () Implementation of C. =item ver () Implementation of C. =item update () Implementation of C. =item whom () Implementation of C. =item setSignOnResponse (VALUE) Set the FreeDB/CDDB sign-on response. C is the value. =item getSignOnResponse () Returns the FreeDB/CDDB sign-on response. =item setProxyHost (VALUE) Implementation of C. Throws an exception because proxy_host may not be specified as CDDBP access through proxy is not (yet) implemented. =item hello () B. Sends the C to C server. =item quit () B. Sends the C to C server. =item waitCommandReply (CMD, CODE_RX) B. Executes command in C and waits for a reply. C specifies 1) the allowed FreeDB/CDDB codes and 2) the regular expression that determins the termination of the returned output. =item mkHello () B. Makes the a FreeDB/CDDB CDDBP hello string. =back =head1 EXCEPTIONS FreeDB/CDDB command methods C, C, C, C, C, C, C, C, C, C, C, C and C throw an C exception in cases of connection errors and unexpected situations in the data format. =head1 SEE ALSO L, L, L, L and L =head1 BUGS Not all FreeDB/CDDB commands are implemented (yet). =head1 HISTORY First development: September 2002 =head1 AUTHOR Vincenzo Zocca EVincenzo@Zocca.comE =head1 COPYRIGHT Copyright 2002, Vincenzo Zocca. =head1 LICENSE This file is part of the C module hierarchy for Perl by Vincenzo Zocca. The Net::FreeDB2 module hierarchy 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. The Net::FreeDB2 module hierarchy 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 the Net::FreeDB2 module hierarchy; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =cut