# Copyright (c) 2006 CentralNic Ltd. All rights reserved. This program is # free software; you can redistribute it and/or modify it under the same # terms as Perl itself. # # $Id: Frame.pm,v 1.11 2006/10/17 15:18:13 gavin Exp $ package Net::EPP::Frame; use Carp; use Net::EPP::Frame::Command; use Net::EPP::Frame::Greeting; use Net::EPP::Frame::Hello; use Net::EPP::Frame::ObjectSpec; use Net::EPP::Frame::Response; use POSIX qw(strftime); use XML::LibXML; use base qw(XML::LibXML::Document); use vars qw($VERSION $EPP_URN $SCHEMA_URI); use strict; our $VERSION = '0.07'; our $EPP_URN = 'urn:ietf:params:xml:ns:epp-1.0'; our $SCHEMA_URI = 'http://www.w3.org/2001/XMLSchema-instance'; =pod =head1 NAME Net::EPP::Frame - An EPP XML frame system built on top of L. =head1 SYNOPSIS #!/usr/bin/perl use Net::EPP::Client; use Net::EPP::Frame; use Net::EPP::ObjectSpec; use Digest::MD5 qw(md5_hex); use Time::HiRes qw(time); use strict; # # establish a connection to an EPP server: # my $epp = Net::EPP::Client->new( host => 'epp.registry.tld', port => 700, ssl => 1, dom => 1, ); my $greeting = $epp->connect; # # log in: # my $login = Net::EPP::Frame::Command::Login->new; $login->clID->appendText($userid); $login->pw->appendText($passwd); # # set the client transaction ID: # $login->clTRID->appendText(md5_hex(Time::HiRes::time().$$)); # # check the response from the log in: # my $answer = $epp->request($login); my $result = ($answer->getElementsByTagName('result'))[0]; if ($result->getAttribute('code') != 1000) { die("Login failed!"); } # # OK, let's do a domain name check: # my $check = Net::EPP::Frame::Command::Check->new; # # get the spec from L: # my @spec = Net::EPP::Frame::ObjectSpec->spec('domain'); # # create a domain object using the spec: # my $domain = $check->addObject(@spec); # # set the domain name we want to check: # my $name = $check->createElement('domain:name'); $name->appendText('example.tld'); # # set the client transaction ID: # $check->clTRID->appendText(md5_hex(time().$$)); # # assemble the frame: # $domain->addChild($name); # # send the request: # my $answer = $epp->request($check); # and so on... =head1 DESCRIPTION EPP is the Extensible Provisioning Protocol. EPP (defined in RFC 3730) is an application layer client-server protocol for the provisioning and management of objects stored in a shared central repository. Specified in XML, the protocol defines generic object management operations and an extensible framework that maps protocol operations to objects. As of writing, its only well-developed application is the provisioning of Internet domain names, hosts, and related contact details. EPP uses XML documents called "frames" send data to and from clients and servers. This module implements a subclass of the L module that simplifies the process of creation of these frames. It is designed to be used alongside the L module. =head1 OBJECT HIERARCHY L +----L +----L =head1 USAGE As a rule, you will not need to create Net::EPP::Frame objects directly. Instead, you should use one of the subclasses included with the distribution. The subclasses all inherit from Net::EPP::Frame. Net::EPP::Frame is itself a subclass of L so all the methods available from that class are also available to instances of Net::EPP::Frame. The available subclasses of Net::EPP::Frame exist to add any additional elements required by the EPP specification. For example, the EloginE frame must contain the EclIDE and EpwE frames, so when you create a new L object, you get these already defined. These classes also have convenience methods, so for the above example, you can call the C<$login-EclID> and C<$login-Epw> methods to get the L objects correesponding to those elements. =head2 Rationale You could just as easily construct your EPP frames from templates or just lots of C calls. But using a programmatic approach such as this strongly couples the validity of your XML to the validity of your program. If the process by which your XML is built is broken, I. This has to be a win. =cut sub new { my ($package, $type) = @_; if ($type eq '') { my @parts = split(/::/, $package); $type = lc(pop(@parts)); } if ($type !~ /^(hello|greeting|command|response)$/) { croak("'type' parameter to Net::EPP::Frame::new() must be one of: hello, greeting, command, response ('$type' given)."); return undef; } my $self = $package->SUPER::new('1.0', 'UTF-8'); bless($self, $package); my $epp = $self->createElementNS($EPP_URN, 'epp'); $epp->setNamespace($SCHEMA_URI, 'xsi', 0); $epp->setAttributeNS($SCHEMA_URI, 'schemaLocation', "$EPP_URN epp-1.0.xsd"); $self->addChild($epp); my $el = $self->createElement(lc($type)); $epp->addChild($el); $self->_addExtraElements; return $self; } sub _addExtraElements { } =pod =head1 ADDITIONAL METHODS my $str = $frame->formatTimeStamp($timestamp); This method returns a scalar in the required format (defined in RFC 3339). This is a convenience method. =cut sub formatTimeStamp { my ($self, $stamp) = @_; return strftime('%Y-%m-%dT%H:%M:%S.0Z', gmtime($stamp)); } =pod my $node = $frame->getNode($id); my $node = $frame->getNode($ns, $id); This is another convenience method. It uses C<$id> with the I method to get a list of nodes with that element name, and simply returns the first L from the list. If C<$ns> is provided, then I is used. =cut sub getNode { my ($self, @args) = @_; if (scalar(@args) == 2) { return ($self->getElementsByTagNameNS(@args))[0]; } elsif (scalar(@args) == 1) { return ($self->getElementsByTagName($args[0]))[0]; } else { croak('Invalid number of arguments to getNode()'); } } =pod my $binary = $frame->header; Returns a scalar containing the frame length packed into binary. This is only useful for low-level protocol stuff. =cut sub header { my $self = shift; return pack('N', length($self->toString) + 4); } =pod my $data = $frame->frame; Returns a scalar containing the frame header (see the I method above) concatenated with the XML frame itself. This is only useful for low-level protocol stuff. =cut sub frame { my $self = shift; return $self->header.$self->toString; } =pod =head1 AVAILABLE SUBCLASSES =over =item L, the base class =item L, for EPP client command frames =item L, for EPP EcheckE client commands =item L, for EPP EcreateE client commands =item L, for EPP EdeleteE client commands =item L, for EPP EinfoE client commands =item L, for EPP EloginE client commands =item L, for EPP ElogoutE client commands =item L, for EPP EpollE client commands =item L, for EPP ErenewW client commands =item L, for EPP EtransferE client commands =item L, for EupdateE client commands =item L, for EPP server greetings =item L, for EPP client greetings =item L, for EPP server response frames =back =head1 AUTHOR Gavin Brown (L) for CentralNic Ltd (http://www.centralnic.com/). =head1 COPYRIGHT This module is (c) 2006 CentralNic Ltd. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO =over =item * L, the Perl bindings to the libxml library =item * The libxml website at L =item * the L module, for communicating with EPP servers. =item * the L module, for managing EP object metadata. =item * RFCs 3730 and RFC 3734, available from L. =item * The CentralNic EPP site at L. =back =cut 1;