| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::EPP::Simple; | 
| 2 | 1 |  |  | 1 |  | 8 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 61 |  | 
| 3 | 1 |  |  | 1 |  | 6 | use Config; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 56 |  | 
| 4 | 1 |  |  | 1 |  | 554 | use Digest::SHA qw(sha1_hex); | 
|  | 1 |  |  |  |  | 2751 |  | 
|  | 1 |  |  |  |  | 79 |  | 
| 5 | 1 |  |  | 1 |  | 7 | use List::Util qw(any); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 115 |  | 
| 6 | 1 |  |  | 1 |  | 7 | use Net::EPP; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 22 |  | 
| 7 | 1 |  |  | 1 |  | 4 | use Net::EPP::Frame; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 18 |  | 
| 8 | 1 |  |  | 1 |  | 4 | use Net::EPP::ResponseCodes; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 127 |  | 
| 9 | 1 |  |  | 1 |  | 599 | use Time::HiRes qw(time); | 
|  | 1 |  |  |  |  | 1375 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 10 | 1 |  |  | 1 |  | 192 | use base qw(Net::EPP::Client); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 175 |  | 
| 11 |  |  |  |  |  |  | use constant { | 
| 12 | 1 |  |  |  |  | 87 | EPP_XMLNS	=> 'urn:ietf:params:xml:ns:epp-1.0', | 
| 13 |  |  |  |  |  |  | LOGINSEC_XMLNS => 'urn:ietf:params:xml:ns:epp:loginSec-1.0', | 
| 14 | 1 |  |  | 1 |  | 11 | }; | 
|  | 1 |  |  |  |  | 2 |  | 
| 15 | 1 |  |  | 1 |  | 7 | use vars qw($Error $Code $Message @Log); | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 77 |  | 
| 16 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 17 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 9891 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | our $Error	= ''; | 
| 20 |  |  |  |  |  |  | our $Code	= OK; | 
| 21 |  |  |  |  |  |  | our $Message	= ''; | 
| 22 |  |  |  |  |  |  | our @Log	= (); | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =pod | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 Name | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | Net::EPP::Simple - a simple EPP client interface for the most common jobs. | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | =head1 Synopsis | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | #!/usr/bin/perl | 
| 33 |  |  |  |  |  |  | use Net::EPP::Simple; | 
| 34 |  |  |  |  |  |  | use strict; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | my $epp = Net::EPP::Simple->new( | 
| 37 |  |  |  |  |  |  | host	=> 'epp.nic.tld', | 
| 38 |  |  |  |  |  |  | user	=> 'my-id', | 
| 39 |  |  |  |  |  |  | pass	=> 'my-password', | 
| 40 |  |  |  |  |  |  | ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | my $domain = 'example.tld'; | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | if ($epp->check_domain($domain) == 1) { | 
| 45 |  |  |  |  |  |  | print "Domain is available\n" ; | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | } else { | 
| 48 |  |  |  |  |  |  | my $info = $epp->domain_info($domain); | 
| 49 |  |  |  |  |  |  | printf("Domain was registered on %s by %s\n", $info->{crDate}, $info->{crID}); | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | =head1 Description | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | This module provides a high level interface to EPP. It hides all the boilerplate | 
| 56 |  |  |  |  |  |  | of connecting, logging in, building request frames and parsing response frames behind | 
| 57 |  |  |  |  |  |  | a simple, Perlish interface. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | It is based on the L module and uses L | 
| 60 |  |  |  |  |  |  | to build request frames. | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | =head1 Constructor | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | The constructor for C has the same general form as the | 
| 65 |  |  |  |  |  |  | one for L, but with the following exceptions: | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =over | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | =item * Unless otherwise set, C defaults to 700 | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | =item * Unless the C parameter is set, SSL is always on | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | =item * You can use the C and C parameters to supply authentication | 
| 74 |  |  |  |  |  |  | information. | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =item * You can use the C parameter to specify a new password. | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | =item * The C parameter can be used to force the use of the | 
| 79 |  |  |  |  |  |  | Login Security Extension (see RFC8807). C will automatically | 
| 80 |  |  |  |  |  |  | use this extension if the server supports it, but clients may wish to force | 
| 81 |  |  |  |  |  |  | this behaviour to prevent downgrade attacks. | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | =item * The C parameter can be used to specify the value of the | 
| 84 |  |  |  |  |  |  | Capp> element in the Login Security extension (if used). Unless | 
| 85 |  |  |  |  |  |  | specified, the name and current version of C will be used. | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | =item * The C parameter controls how long the client waits for a | 
| 88 |  |  |  |  |  |  | response from the server before returning an error. | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | =item * if C is set, C will output verbose debugging | 
| 91 |  |  |  |  |  |  | information on C, including all frames sent to and received from the | 
| 92 |  |  |  |  |  |  | server. | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =item * C can be used to disable automatic reconnection (it is | 
| 95 |  |  |  |  |  |  | enabled by default). Before sending a frame to the server, C | 
| 96 |  |  |  |  |  |  | will send a ChelloE> to check that the connection is up, if not, it | 
| 97 |  |  |  |  |  |  | will try to reconnect, aborting after the Ith time, where I is the value | 
| 98 |  |  |  |  |  |  | of C (the default is 3). | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =item * C can be used to disable automatic logins. If you set it | 
| 101 |  |  |  |  |  |  | to C<0>, you can manually log in using the C<$epp-E_login()> method. | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | =item * C is a reference to an array of the EPP object schema | 
| 104 |  |  |  |  |  |  | URIs that the client requires. | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =item * C is a flag saying the client only requires the | 
| 107 |  |  |  |  |  |  | standard EPP C, C, and C schemas. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =item * If neither C nor C is specified then the | 
| 110 |  |  |  |  |  |  | client will echo the server's object schema list. | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | =item * C is a reference to an array of the EPP extension | 
| 113 |  |  |  |  |  |  | schema URIs that the client requires. | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =item * C is a flag saying the client only requires the | 
| 116 |  |  |  |  |  |  | standard EPP C DNSSEC extension schema. | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =item * If neither C nor C is specified then the | 
| 119 |  |  |  |  |  |  | client will echo the server's extension schema list. | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | =item * The C parameter can be used to specify the language. The | 
| 122 |  |  |  |  |  |  | default is "C". | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =back | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | The constructor will establish a connection to the server and retrieve the | 
| 127 |  |  |  |  |  |  | greeting (which is available via C<$epp-E{greeting}>) and then send a | 
| 128 |  |  |  |  |  |  | CloginE> request. | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | If the login fails, the constructor will return C and set | 
| 131 |  |  |  |  |  |  | C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>. | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =head2 Client and Server SSL options | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | RFC 5730 requires that all EPP instances must be protected using "mutual, | 
| 136 |  |  |  |  |  |  | strong client-server authentication". In practice, this means that both | 
| 137 |  |  |  |  |  |  | client and server must present an SSL certificate, and that they must | 
| 138 |  |  |  |  |  |  | both verify the certificate of their peer. | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | =head3 Server Certificate Verification | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | C will verify the certificate presented by a server if | 
| 143 |  |  |  |  |  |  | the C, and either C or C are passed to the | 
| 144 |  |  |  |  |  |  | constructor: | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | my $epp = Net::EPP::Simple->new( | 
| 147 |  |  |  |  |  |  | host	=> 'epp.nic.tld', | 
| 148 |  |  |  |  |  |  | user	=> 'my-id', | 
| 149 |  |  |  |  |  |  | pass	=> 'my-password', | 
| 150 |  |  |  |  |  |  | verify	=> 1, | 
| 151 |  |  |  |  |  |  | ca_file	=> '/etc/pki/tls/certs/ca-bundle.crt', | 
| 152 |  |  |  |  |  |  | ca_path	=> '/etc/pki/tls/certs', | 
| 153 |  |  |  |  |  |  | ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | C will fail to connect to the server if the | 
| 156 |  |  |  |  |  |  | certificate is not valid. | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | You can disable SSL certificate verification by omitting the C | 
| 159 |  |  |  |  |  |  | argument or setting it to C. This is strongly discouraged, | 
| 160 |  |  |  |  |  |  | particularly in production environments. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =head3 SSL Cipher Selection | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | You can restrict the ciphers that you will use to connect to the server | 
| 165 |  |  |  |  |  |  | by passing a C parameter to the constructor. This is a colon- | 
| 166 |  |  |  |  |  |  | separated list of cipher names and aliases. See L | 
| 167 |  |  |  |  |  |  | for further details. As an example, the following cipher list is | 
| 168 |  |  |  |  |  |  | suggested for clients who wish to ensure high-security connections to | 
| 169 |  |  |  |  |  |  | servers: | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | HIGH:!ADH:!MEDIUM:!LOW:!SSLv2:!EXP | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | =head3 Client Certificates | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | If you are connecting to an EPP server which requires a client | 
| 176 |  |  |  |  |  |  | certificate, you can configure C to use one as | 
| 177 |  |  |  |  |  |  | follows: | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | my $epp = Net::EPP::Simple->new( | 
| 180 |  |  |  |  |  |  | host        => 'epp.nic.tld', | 
| 181 |  |  |  |  |  |  | user        => 'my-id', | 
| 182 |  |  |  |  |  |  | pass        => 'my-password', | 
| 183 |  |  |  |  |  |  | key         => '/path/to/my.key', | 
| 184 |  |  |  |  |  |  | cert        => '/path/to/my.crt', | 
| 185 |  |  |  |  |  |  | passphrase  => 'foobar123', | 
| 186 |  |  |  |  |  |  | ); | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | C is the filename of the private key, C is the filename of | 
| 189 |  |  |  |  |  |  | the certificate. If the private key is encrypted, the C | 
| 190 |  |  |  |  |  |  | parameter will be used to decrypt it. | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | =head2 Configuration File | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | C supports the use of a simple configuration file. To | 
| 195 |  |  |  |  |  |  | use this feature, you need to install the L module. | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | When starting up, C will look for | 
| 198 |  |  |  |  |  |  | C<$HOME/.net-epp-simple-rc>. This file is an ini-style configuration | 
| 199 |  |  |  |  |  |  | file. | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | =head3 Default Options | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | You can specify default options for all EPP servers using the C<[default]> | 
| 204 |  |  |  |  |  |  | section: | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | [default] | 
| 207 |  |  |  |  |  |  | default=epp.nic.tld | 
| 208 |  |  |  |  |  |  | debug=1 | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | =head3 Server Specific Options | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | You can specify options for for specific EPP servers by giving each EPP server | 
| 213 |  |  |  |  |  |  | its own section: | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | [epp.nic.tld] | 
| 216 |  |  |  |  |  |  | user=abc123 | 
| 217 |  |  |  |  |  |  | pass=foo2bar | 
| 218 |  |  |  |  |  |  | port=777 | 
| 219 |  |  |  |  |  |  | ssl=0 | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | This means that when you write a script that uses C, you can | 
| 222 |  |  |  |  |  |  | do the following: | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | # config file has a default server: | 
| 225 |  |  |  |  |  |  | my $epp = Net::EPP::Simple->new; | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | # config file has connection options for this EPP server: | 
| 228 |  |  |  |  |  |  | my $epp = Net::EPP:Simple->new('host' => 'epp.nic.tld'); | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | Any parameters provided to the constructor will override those in the config | 
| 231 |  |  |  |  |  |  | file. | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | =cut | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | sub new { | 
| 236 | 0 |  |  | 0 | 1 |  | my ($package, %params) = @_; | 
| 237 | 0 |  |  |  |  |  | $params{dom}		= 1; | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 0 | 0 |  |  |  |  | my $load_config = (defined($params{load_config}) ? $params{load_config} : 1); | 
| 240 | 0 | 0 |  |  |  |  | $package->_load_config(\%params) if ($load_config); | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 0 | 0 | 0 |  |  |  | $params{port}		= (defined($params{port}) && int($params{port}) > 0 ? $params{port} : 700); | 
| 243 | 0 | 0 |  |  |  |  | $params{ssl}		= ($params{no_ssl} ? undef : 1); | 
| 244 |  |  |  |  |  |  |  | 
| 245 | 0 |  |  |  |  |  | my $self = $package->SUPER::new(%params); | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 0 |  |  |  |  |  | $self->{user}			= $params{user}; | 
| 248 | 0 |  |  |  |  |  | $self->{pass}			= $params{pass}; | 
| 249 | 0 |  |  |  |  |  | $self->{newPW}			= $params{newPW}; | 
| 250 | 0 | 0 |  |  |  |  | $self->{debug} 			= (defined($params{debug}) ? int($params{debug}) : undef); | 
| 251 | 0 | 0 | 0 |  |  |  | $self->{timeout}		= (defined($params{timeout}) && int($params{timeout}) > 0 ? $params{timeout} : 5); | 
| 252 | 0 | 0 |  |  |  |  | $self->{reconnect}		= (defined($params{reconnect}) ? int($params{reconnect}) : 3); | 
| 253 | 0 |  |  |  |  |  | $self->{'connected'}		= undef; | 
| 254 | 0 |  |  |  |  |  | $self->{'authenticated'}	= undef; | 
| 255 | 0 | 0 |  |  |  |  | $self->{connect}		= (exists($params{connect}) ? $params{connect} : 1); | 
| 256 | 0 | 0 |  |  |  |  | $self->{login}			= (exists($params{login}) ? $params{login} : 1); | 
| 257 | 0 |  |  |  |  |  | $self->{key}			= $params{key}; | 
| 258 | 0 |  |  |  |  |  | $self->{cert}			= $params{cert}; | 
| 259 | 0 |  |  |  |  |  | $self->{passphrase}		= $params{passphrase}; | 
| 260 | 0 |  |  |  |  |  | $self->{verify}			= $params{verify}; | 
| 261 | 0 |  |  |  |  |  | $self->{ca_file}		= $params{ca_file}; | 
| 262 | 0 |  |  |  |  |  | $self->{ca_path}		= $params{ca_path}; | 
| 263 | 0 |  |  |  |  |  | $self->{ciphers}		= $params{ciphers}; | 
| 264 | 0 |  |  |  |  |  | $self->{objects}		= $params{objects}; | 
| 265 | 0 |  |  |  |  |  | $self->{stdobj}			= $params{stdobj}; | 
| 266 | 0 |  |  |  |  |  | $self->{extensions}		= $params{extensions}; | 
| 267 | 0 |  |  |  |  |  | $self->{stdext}			= $params{stdext}; | 
| 268 | 0 |  | 0 |  |  |  | $self->{lang}			= $params{lang} || 'en'; | 
| 269 | 0 |  |  |  |  |  | $self->{login_security}	= $params{login_security}; | 
| 270 | 0 |  |  |  |  |  | $self->{appname}	    = $params{appname}; | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 0 |  |  |  |  |  | bless($self, $package); | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 0 | 0 |  |  |  |  | if ($self->{connect}) { | 
| 275 | 0 | 0 |  |  |  |  | return ($self->_connect($self->{login}) ? $self : undef); | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | } else { | 
| 278 | 0 |  |  |  |  |  | return $self; | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | sub _load_config { | 
| 284 | 0 |  |  | 0 |  |  | my ($package, $params_ref) = @_; | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 0 |  |  |  |  |  | eval 'use Config::Simple'; | 
| 287 | 0 | 0 |  |  |  |  | if (!$@) { | 
| 288 |  |  |  |  |  |  | # we have Config::Simple, so let's try to parse the RC file: | 
| 289 | 0 |  |  |  |  |  | my $rcfile = $ENV{'HOME'}.'/.net-epp-simple-rc'; | 
| 290 | 0 | 0 |  |  |  |  | if (-e $rcfile) { | 
| 291 | 0 |  |  |  |  |  | my $config = Config::Simple->new($rcfile); | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | # if no host was defined in the constructor, use the default (if specified): | 
| 294 | 0 | 0 | 0 |  |  |  | if (!defined($params_ref->{'host'}) && $config->param('default.default')) { | 
| 295 | 0 |  |  |  |  |  | $params_ref->{'host'} = $config->param('default.default'); | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | # if no debug level was defined in the constructor, use the default (if specified): | 
| 299 | 0 | 0 | 0 |  |  |  | if (!defined($params_ref->{'debug'}) && $config->param('default.debug')) { | 
| 300 | 0 |  |  |  |  |  | $params_ref->{'debug'} = $config->param('default.debug'); | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | # grep through the file's values for settings for the selected host: | 
| 304 | 0 |  |  |  |  |  | my %vars = $config->vars; | 
| 305 | 0 |  |  |  |  |  | foreach my $key (grep { /^$params_ref->{'host'}\./ } keys(%vars)) { | 
|  | 0 |  |  |  |  |  |  | 
| 306 | 0 |  |  |  |  |  | my $value = $vars{$key}; | 
| 307 | 0 |  |  |  |  |  | $key =~ s/^$params_ref->{'host'}\.//; | 
| 308 | 0 | 0 |  |  |  |  | $params_ref->{$key} = $value unless (defined($params_ref->{$key})); | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | sub _connect { | 
| 315 | 0 |  |  | 0 |  |  | my ($self, $login) = @_; | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 0 |  |  |  |  |  | my %params; | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 0 | 0 | 0 |  |  |  | $params{SSL_cipher_list} = $self->{ciphers} if (defined($self->{ssl}) && defined($self->{ciphers})); | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 0 | 0 | 0 |  |  |  | if (defined($self->{key}) && defined($self->{cert}) && defined($self->{ssl})) { | 
|  |  |  | 0 |  |  |  |  | 
| 322 | 0 |  |  |  |  |  | $self->debug('configuring client certificate parameters'); | 
| 323 | 0 |  |  |  |  |  | $params{SSL_key_file}	= $self->{key}; | 
| 324 | 0 |  |  |  |  |  | $params{SSL_cert_file}	= $self->{cert}; | 
| 325 | 0 |  |  | 0 |  |  | $params{SSL_passwd_cb}	= sub { $self->{passphrase} }; | 
|  | 0 |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 | 0 | 0 | 0 |  |  |  | if (defined($self->{ssl}) && defined($self->{verify})) { | 
|  |  | 0 |  |  |  |  |  | 
| 329 | 0 |  |  |  |  |  | $self->debug('configuring server verification'); | 
| 330 | 0 |  |  |  |  |  | $params{SSL_verify_mode}	= 1; | 
| 331 | 0 |  |  |  |  |  | $params{SSL_ca_file}		= $self->{ca_file}; | 
| 332 | 0 |  |  |  |  |  | $params{SSL_ca_path}		= $self->{ca_path}; | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | } elsif (defined($self->{ssl})) { | 
| 335 | 0 |  |  |  |  |  | $params{SSL_verify_mode} = 0; | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 0 |  |  |  |  |  | $self->debug(sprintf('Attempting to connect to %s:%d', $self->{host}, $self->{port})); | 
| 340 | 0 |  |  |  |  |  | eval { | 
| 341 | 0 |  |  |  |  |  | $params{no_greeting} = 1; | 
| 342 | 0 |  |  |  |  |  | $self->connect(%params); | 
| 343 |  |  |  |  |  |  | }; | 
| 344 | 0 | 0 |  |  |  |  | if ($@ ne '') { | 
| 345 | 0 |  |  |  |  |  | chomp($@); | 
| 346 | 0 |  |  |  |  |  | $@ =~ s/ at .+ line .+$//; | 
| 347 | 0 |  |  |  |  |  | $self->debug($@); | 
| 348 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 349 | 0 |  |  |  |  |  | $Error = $Message = "Error connecting: ".$@; | 
| 350 | 0 |  |  |  |  |  | return undef; | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | } else { | 
| 353 | 0 |  |  |  |  |  | $self->{'connected'} = 1; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 0 |  |  |  |  |  | $self->debug('Connected OK, retrieving greeting frame'); | 
| 356 | 0 |  |  |  |  |  | $self->{greeting} = $self->get_frame; | 
| 357 | 0 | 0 |  |  |  |  | if (ref($self->{greeting}) ne 'Net::EPP::Frame::Response') { | 
| 358 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 359 | 0 |  |  |  |  |  | $Error = $Message = "Error retrieving greeting: ".$@; | 
| 360 | 0 |  |  |  |  |  | return undef; | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | } else { | 
| 363 | 0 |  |  |  |  |  | $self->debug('greeting frame retrieved OK'); | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | } | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  |  | 
| 368 | 0 |  |  |  |  |  | map { $self->debug('S: '.$_) } split(/\n/, $self->{greeting}->toString(1)); | 
|  | 0 |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 0 | 0 |  |  |  |  | if ($login) { | 
| 371 | 0 |  |  |  |  |  | $self->debug('attempting login'); | 
| 372 | 0 |  |  |  |  |  | return $self->_login; | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | } else { | 
| 375 | 0 |  |  |  |  |  | return 1; | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | sub _login { | 
| 381 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 382 |  |  |  |  |  |  |  | 
| 383 | 0 |  |  |  |  |  | $self->debug(sprintf("Attempting to login as client ID '%s'", $self->{user})); | 
| 384 | 0 |  |  |  |  |  | my $response = $self->request($self->_prepare_login_frame()); | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 387 | 0 |  |  |  |  |  | $Error = $Message = "Error getting response to login request: ".$Error; | 
| 388 | 0 |  |  |  |  |  | return undef; | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | } else { | 
| 391 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 392 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 393 |  |  |  |  |  |  |  | 
| 394 | 0 |  |  |  |  |  | $self->debug(sprintf('%04d: %s', $Code, $Message)); | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 0 | 0 |  |  |  |  | if ($Code > 1999) { | 
| 397 | 0 |  |  |  |  |  | $Error = "Error logging in (response code $Code, message $Message)"; | 
| 398 | 0 |  |  |  |  |  | return undef; | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | } else { | 
| 401 | 0 |  |  |  |  |  | $self->{'authenticated'} = 1; | 
| 402 | 0 |  |  |  |  |  | return 1; | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | sub _get_option_uri_list { | 
| 409 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 410 | 0 |  |  |  |  |  | my $tag = shift; | 
| 411 | 0 |  |  |  |  |  | my $list = []; | 
| 412 | 0 |  |  |  |  |  | my $elems = $self->{greeting}->getElementsByTagNameNS(EPP_XMLNS, $tag); | 
| 413 | 0 |  |  |  |  |  | while (my $elem = $elems->shift) { | 
| 414 | 0 |  |  |  |  |  | push @$list, $elem->firstChild->data; | 
| 415 |  |  |  |  |  |  | } | 
| 416 | 0 |  |  |  |  |  | return $list; | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | sub _prepare_login_frame { | 
| 420 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 0 |  |  |  |  |  | $self->debug('preparing login frame'); | 
| 423 | 0 |  |  |  |  |  | my $login = Net::EPP::Frame::Command::Login->new; | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 0 |  |  |  |  |  | my @extensions; | 
| 426 | 0 | 0 |  |  |  |  | if ($self->{'stdext'}) { | 
|  |  | 0 |  |  |  |  |  | 
| 427 | 0 |  |  |  |  |  | push(@extensions, (Net::EPP::Frame::ObjectSpec->spec('secDNS'))[1]); | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | } elsif ($self->{'extensions'}) { | 
| 430 | 0 |  |  |  |  |  | @extensions = @{$self->{'extensions'}}; | 
|  | 0 |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | } else { | 
| 433 | 0 |  |  |  |  |  | @extensions = @{$self->_get_option_uri_list('extURI')}; | 
|  | 0 |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  |  | 
| 437 | 0 |  |  |  |  |  | $login->clID->appendText($self->{'user'}); | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 0 | 0 | 0 | 0 |  |  | if ($self->{'login_security'} || any { LOGINSEC_XMLNS eq $_ } @extensions) { | 
|  | 0 |  |  |  |  |  |  | 
| 440 | 0 | 0 |  | 0 |  |  | push(@extensions, LOGINSEC_XMLNS) unless (any { LOGINSEC_XMLNS eq $_ } @extensions); | 
|  | 0 |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 0 |  |  |  |  |  | $login->pw->appendText('[LOGIN-SECURITY]'); | 
| 443 |  |  |  |  |  |  |  | 
| 444 | 0 |  |  |  |  |  | my $loginSec = $login->createElementNS(LOGINSEC_XMLNS, 'loginSec'); | 
| 445 |  |  |  |  |  |  |  | 
| 446 | 0 |  |  |  |  |  | my $userAgent = $login->createElement('userAgent'); | 
| 447 | 0 |  |  |  |  |  | $loginSec->appendChild($userAgent); | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 0 |  |  |  |  |  | my $app = $login->createElement('app'); | 
| 450 | 0 |  | 0 |  |  |  | $app->appendText($self->{'appname'} || sprintf('%s %s', __PACKAGE__, $Net::EPP::VERSION)); | 
| 451 | 0 |  |  |  |  |  | $userAgent->appendChild($app); | 
| 452 |  |  |  |  |  |  |  | 
| 453 | 0 |  |  |  |  |  | my $tech = $login->createElement('tech'); | 
| 454 | 0 |  |  |  |  |  | $tech->appendText(sprintf('Perl %s', $Config{'version'})); | 
| 455 | 0 |  |  |  |  |  | $userAgent->appendChild($tech); | 
| 456 |  |  |  |  |  |  |  | 
| 457 | 0 |  |  |  |  |  | my $os = $login->createElement('os'); | 
| 458 | 0 |  |  |  |  |  | $os->appendText(sprintf('%s %s', ucfirst($Config{'osname'}), $Config{'osvers'})); | 
| 459 | 0 |  |  |  |  |  | $userAgent->appendChild($os); | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 0 |  |  |  |  |  | my $pw = $login->createElement('pw'); | 
| 462 | 0 |  |  |  |  |  | $pw->appendText($self->{'pass'}); | 
| 463 | 0 |  |  |  |  |  | $loginSec->appendChild($pw); | 
| 464 |  |  |  |  |  |  |  | 
| 465 | 0 | 0 |  |  |  |  | if ($self->{'newPW'}) { | 
| 466 | 0 |  |  |  |  |  | my $newPW = $login->createElement('newPW'); | 
| 467 | 0 |  |  |  |  |  | $newPW->appendText('[LOGIN-SECURITY]'); | 
| 468 | 0 |  |  |  |  |  | $login->getNode('login')->insertAfter($newPW, $login->pw); | 
| 469 |  |  |  |  |  |  |  | 
| 470 | 0 |  |  |  |  |  | $newPW = $login->createElement('newPW'); | 
| 471 | 0 |  |  |  |  |  | $newPW->appendText($self->{'newPW'}); | 
| 472 | 0 |  |  |  |  |  | $loginSec->appendChild($newPW); | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 0 |  |  |  |  |  | my $extension = $login->createElement('extension'); | 
| 476 | 0 |  |  |  |  |  | $extension->appendChild($loginSec); | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 0 |  |  |  |  |  | $login->getCommandNode()->parentNode()->insertAfter($extension, $login->getCommandNode()); | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | } else { | 
| 481 | 0 |  |  |  |  |  | $login->pw->appendText($self->{pass}); | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 0 | 0 |  |  |  |  | if ($self->{newPW}) { | 
| 484 | 0 |  |  |  |  |  | my $newPW = $login->createElement('newPW'); | 
| 485 | 0 |  |  |  |  |  | $newPW->appendText($self->{newPW}); | 
| 486 | 0 |  |  |  |  |  | $login->getNode('login')->insertAfter($newPW, $login->pw); | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  | } | 
| 489 |  |  |  |  |  |  |  | 
| 490 | 0 |  |  |  |  |  | $login->version->appendText($self->{greeting}->getElementsByTagNameNS(EPP_XMLNS, 'version')->shift->firstChild->data); | 
| 491 | 0 |  |  |  |  |  | $login->lang->appendText($self->{lang}); | 
| 492 |  |  |  |  |  |  |  | 
| 493 | 0 |  |  |  |  |  | my $objects = $self->{objects}; | 
| 494 | 0 |  |  |  |  |  | $objects = [map { (Net::EPP::Frame::ObjectSpec->spec($_))[1] } | 
| 495 | 0 | 0 |  |  |  |  | qw(contact domain host)] if $self->{stdobj}; | 
| 496 | 0 | 0 |  |  |  |  | $objects = _get_option_uri_list($self,'objURI') if not $objects; | 
| 497 | 0 |  |  |  |  |  | $login->svcs->appendTextChild('objURI', $_) for @$objects; | 
| 498 |  |  |  |  |  |  |  | 
| 499 | 0 | 0 |  |  |  |  | if (scalar(@extensions) > 0) { | 
| 500 | 0 |  |  |  |  |  | my $svcext = $login->createElement('svcExtension'); | 
| 501 | 0 |  |  |  |  |  | $login->svcs->appendChild($svcext); | 
| 502 | 0 |  |  |  |  |  | $svcext->appendTextChild('extURI', $_) for @extensions; | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | } | 
| 505 |  |  |  |  |  |  |  | 
| 506 | 0 |  |  |  |  |  | return $login; | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | =pod | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | =head1 Availability Checks | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | You can do a simple CcheckE> request for an object like so: | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | my $result = $epp->check_domain($domain); | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | my $result = $epp->check_host($host); | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | my $result = $epp->check_contact($contact); | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | Each of these methods has the same profile. They will return one of the | 
| 522 |  |  |  |  |  |  | following: | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =over | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | =item * C in the case of an error (check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>). | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | =item * C<0> if the object is already provisioned. | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | =item * C<1> if the object is available. | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | =back | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =cut | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | sub check_domain { | 
| 537 | 0 |  |  | 0 | 0 |  | my ($self, $domain) = @_; | 
| 538 | 0 |  |  |  |  |  | return $self->_check('domain', $domain); | 
| 539 |  |  |  |  |  |  | } | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | sub check_host { | 
| 542 | 0 |  |  | 0 | 0 |  | my ($self, $host) = @_; | 
| 543 | 0 |  |  |  |  |  | return $self->_check('host', $host); | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | sub check_contact { | 
| 547 | 0 |  |  | 0 | 0 |  | my ($self, $contact) = @_; | 
| 548 | 0 |  |  |  |  |  | return $self->_check('contact', $contact); | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | sub _check { | 
| 552 | 0 |  |  | 0 |  |  | my ($self, $type, $identifier) = @_; | 
| 553 | 0 |  |  |  |  |  | my $frame; | 
| 554 | 0 | 0 |  |  |  |  | if ($type eq 'domain') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 555 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Check::Domain->new; | 
| 556 | 0 |  |  |  |  |  | $frame->addDomain($identifier); | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 559 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Check::Contact->new; | 
| 560 | 0 |  |  |  |  |  | $frame->addContact($identifier); | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | } elsif ($type eq 'host') { | 
| 563 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Check::Host->new; | 
| 564 | 0 |  |  |  |  |  | $frame->addHost($identifier); | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | } else { | 
| 567 | 0 |  |  |  |  |  | $Error = "Unknown object type '$type'"; | 
| 568 | 0 |  |  |  |  |  | return undef; | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  |  | 
| 571 | 0 |  |  |  |  |  | my $response = $self->_request($frame); | 
| 572 |  |  |  |  |  |  |  | 
| 573 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 574 | 0 |  |  |  |  |  | return undef; | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | } else { | 
| 577 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 578 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 579 |  |  |  |  |  |  |  | 
| 580 | 0 | 0 |  |  |  |  | if ($Code > 1999) { | 
| 581 | 0 |  |  |  |  |  | $Error = $self->_get_error_message($response); | 
| 582 | 0 |  |  |  |  |  | return undef; | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | } else { | 
| 585 | 0 |  |  |  |  |  | my $xmlns = (Net::EPP::Frame::ObjectSpec->spec($type))[1]; | 
| 586 | 0 |  |  |  |  |  | my $key; | 
| 587 | 0 | 0 | 0 |  |  |  | if ($type eq 'domain' || $type eq 'host') { | 
|  |  | 0 |  |  |  |  |  | 
| 588 | 0 |  |  |  |  |  | $key = 'name'; | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 591 | 0 |  |  |  |  |  | $key = 'id'; | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | } | 
| 594 | 0 |  |  |  |  |  | return $response->getNode($xmlns, $key)->getAttribute('avail'); | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | } | 
| 597 |  |  |  |  |  |  | } | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | =pod | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | =head1 Retrieving Object Information | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | =head2 Domain Objects | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | my $info = $epp->domain_info($domain, $authInfo, $follow); | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | This method constructs an CinfoE> frame and sends | 
| 609 |  |  |  |  |  |  | it to the server, then parses the response into a simple hash ref. If | 
| 610 |  |  |  |  |  |  | there is an error, this method will return C, and you can then | 
| 611 |  |  |  |  |  |  | check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>. | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | If C<$authInfo> is defined, it will be sent to the server as per RFC | 
| 614 |  |  |  |  |  |  | 5731, Section 3.1.2. | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | If the C<$follow> parameter is true, then C will also | 
| 617 |  |  |  |  |  |  | retrieve the relevant host and contact details for a domain: instead of | 
| 618 |  |  |  |  |  |  | returning an object name or ID for the domain's registrant, contact | 
| 619 |  |  |  |  |  |  | associations, DNS servers or subordinate hosts, the values will be | 
| 620 |  |  |  |  |  |  | replaced with the return value from the appropriate C or | 
| 621 |  |  |  |  |  |  | C command (unless there was an error, in which case the | 
| 622 |  |  |  |  |  |  | original object ID will be used instead). | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | =cut | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | sub domain_info { | 
| 627 | 0 |  |  | 0 | 0 |  | my ($self, $domain, $authInfo, $follow, $hosts) = @_; | 
| 628 | 0 |  | 0 |  |  |  | $hosts = $hosts || 'all'; | 
| 629 |  |  |  |  |  |  |  | 
| 630 | 0 |  |  |  |  |  | my $result = $self->_info('domain', $domain, $authInfo, $hosts); | 
| 631 | 0 | 0 | 0 |  |  |  | return $result if (ref($result) ne 'HASH' || !$follow); | 
| 632 |  |  |  |  |  |  |  | 
| 633 | 0 | 0 | 0 |  |  |  | if (defined($result->{'ns'}) && ref($result->{'ns'}) eq 'ARRAY') { | 
| 634 | 0 |  |  |  |  |  | for (my $i = 0 ; $i < scalar(@{$result->{'ns'}}) ; $i++) { | 
|  | 0 |  |  |  |  |  |  | 
| 635 | 0 |  |  |  |  |  | my $info = $self->host_info($result->{'ns'}->[$i]); | 
| 636 | 0 | 0 |  |  |  |  | $result->{'ns'}->[$i] = $info if (ref($info) eq 'HASH'); | 
| 637 |  |  |  |  |  |  | } | 
| 638 |  |  |  |  |  |  | } | 
| 639 |  |  |  |  |  |  |  | 
| 640 | 0 | 0 | 0 |  |  |  | if (defined($result->{'hosts'}) && ref($result->{'hosts'}) eq 'ARRAY') { | 
| 641 | 0 |  |  |  |  |  | for (my $i = 0 ; $i < scalar(@{$result->{'hosts'}}) ; $i++) { | 
|  | 0 |  |  |  |  |  |  | 
| 642 | 0 |  |  |  |  |  | my $info = $self->host_info($result->{'hosts'}->[$i]); | 
| 643 | 0 | 0 |  |  |  |  | $result->{'hosts'}->[$i] = $info if (ref($info) eq 'HASH'); | 
| 644 |  |  |  |  |  |  | } | 
| 645 |  |  |  |  |  |  | } | 
| 646 |  |  |  |  |  |  |  | 
| 647 | 0 |  |  |  |  |  | my $info = $self->contact_info($result->{'registrant'}); | 
| 648 | 0 | 0 |  |  |  |  | $result->{'registrant'} = $info if (ref($info) eq 'HASH'); | 
| 649 |  |  |  |  |  |  |  | 
| 650 | 0 |  |  |  |  |  | foreach my $type (keys(%{$result->{'contacts'}})) { | 
|  | 0 |  |  |  |  |  |  | 
| 651 | 0 |  |  |  |  |  | my $info = $self->contact_info($result->{'contacts'}->{$type}); | 
| 652 | 0 | 0 |  |  |  |  | $result->{'contacts'}->{$type} = $info if (ref($info) eq 'HASH'); | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 | 0 |  |  |  |  |  | return $result; | 
| 656 |  |  |  |  |  |  | } | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | =pod | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | =head2 Host Objects | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | my $info = $epp->host_info($host); | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | This method constructs an CinfoE> frame and sends | 
| 665 |  |  |  |  |  |  | it to the server, then parses the response into a simple hash ref. If | 
| 666 |  |  |  |  |  |  | there is an error, this method will return C, and you can then | 
| 667 |  |  |  |  |  |  | check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>. | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | =cut | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | sub host_info { | 
| 672 | 0 |  |  | 0 | 0 |  | my ($self, $host) = @_; | 
| 673 | 0 |  |  |  |  |  | return $self->_info('host', $host); | 
| 674 |  |  |  |  |  |  | } | 
| 675 |  |  |  |  |  |  |  | 
| 676 |  |  |  |  |  |  | =pod | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | =head2 Contact Objects | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | my $info = $epp->contact_info($contact, $authInfo, $roid); | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | This method constructs an CinfoE> frame and sends | 
| 683 |  |  |  |  |  |  | it to the server, then parses the response into a simple hash ref. If | 
| 684 |  |  |  |  |  |  | there is an error, this method will return C, and you can then | 
| 685 |  |  |  |  |  |  | check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>. | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | If C<$authInfo> is defined, it will be sent to the server as per RFC | 
| 688 |  |  |  |  |  |  | RFC 5733, Section 3.1.2. | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | If the C<$roid> parameter to C is set, then the C | 
| 691 |  |  |  |  |  |  | attribute will be set on the CauthInfoE> element. | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | =cut | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | sub contact_info { | 
| 696 | 0 |  |  | 0 | 0 |  | my ($self, $contact, $authInfo, $roid) = @_; | 
| 697 | 0 |  |  |  |  |  | return $self->_info('contact', $contact, $authInfo, $roid); | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | sub _info { | 
| 701 |  |  |  |  |  |  | # $opt is the "hosts" attribute value for domains or the "roid" | 
| 702 |  |  |  |  |  |  | # attribute for contacts | 
| 703 | 0 |  |  | 0 |  |  | my ($self, $type, $identifier, $authInfo, $opt) = @_; | 
| 704 | 0 |  |  |  |  |  | my $frame; | 
| 705 | 0 | 0 |  |  |  |  | if ($type eq 'domain') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 706 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Info::Domain->new; | 
| 707 | 0 |  | 0 |  |  |  | $frame->setDomain($identifier, $opt || 'all'); | 
| 708 |  |  |  |  |  |  |  | 
| 709 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 710 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Info::Contact->new; | 
| 711 | 0 |  |  |  |  |  | $frame->setContact($identifier); | 
| 712 |  |  |  |  |  |  |  | 
| 713 |  |  |  |  |  |  | } elsif ($type eq 'host') { | 
| 714 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Info::Host->new; | 
| 715 | 0 |  |  |  |  |  | $frame->setHost($identifier); | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | } else { | 
| 718 | 0 |  |  |  |  |  | $Error = "Unknown object type '$type'"; | 
| 719 | 0 |  |  |  |  |  | return undef; | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | } | 
| 722 |  |  |  |  |  |  |  | 
| 723 | 0 | 0 | 0 |  |  |  | if (defined($authInfo) && $authInfo ne '') { | 
| 724 | 0 |  |  |  |  |  | $self->debug('adding authInfo element to request frame'); | 
| 725 | 0 |  |  |  |  |  | my $el = $frame->createElement((Net::EPP::Frame::ObjectSpec->spec($type))[0].':authInfo'); | 
| 726 | 0 |  |  |  |  |  | my $pw = $frame->createElement((Net::EPP::Frame::ObjectSpec->spec($type))[0].':pw'); | 
| 727 | 0 |  |  |  |  |  | $pw->appendChild($frame->createTextNode($authInfo)); | 
| 728 | 0 | 0 | 0 |  |  |  | $pw->setAttribute('roid', $opt) if ($type eq 'contact' && $opt); | 
| 729 | 0 |  |  |  |  |  | $el->appendChild($pw); | 
| 730 | 0 |  |  |  |  |  | $frame->getNode((Net::EPP::Frame::ObjectSpec->spec($type))[1], 'info')->appendChild($el); | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  |  | 
| 733 | 0 |  |  |  |  |  | my $response = $self->_request($frame); | 
| 734 |  |  |  |  |  |  |  | 
| 735 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 736 | 0 |  |  |  |  |  | return undef; | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | } else { | 
| 739 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 740 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 741 |  |  |  |  |  |  |  | 
| 742 | 0 | 0 |  |  |  |  | if ($Code > 1999) { | 
| 743 | 0 |  |  |  |  |  | $Error = $self->_get_error_message($response); | 
| 744 | 0 |  |  |  |  |  | return undef; | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | } else { | 
| 747 | 0 |  |  |  |  |  | return $self->parse_object_info($type, $response); | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  | } | 
| 750 |  |  |  |  |  |  | } | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | # An easy-to-subclass method for parsing object info | 
| 753 |  |  |  |  |  |  | sub parse_object_info { | 
| 754 | 0 |  |  | 0 | 0 |  | my ($self, $type, $response) = @_; | 
| 755 |  |  |  |  |  |  |  | 
| 756 | 0 |  |  |  |  |  | my $infData = $response->getNode((Net::EPP::Frame::ObjectSpec->spec($type))[1], 'infData'); | 
| 757 |  |  |  |  |  |  |  | 
| 758 | 0 | 0 |  |  |  |  | if ($type eq 'domain') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | # secDNS extension only applies to domain objects | 
| 760 | 0 |  |  |  |  |  | my $secinfo = $response->getNode((Net::EPP::Frame::ObjectSpec->spec('secDNS'))[1], 'infData'); | 
| 761 | 0 |  |  |  |  |  | return $self->_domain_infData_to_hash($infData, $secinfo); | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 764 | 0 |  |  |  |  |  | return $self->_contact_infData_to_hash($infData); | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | } elsif ($type eq 'host') { | 
| 767 | 0 |  |  |  |  |  | return $self->_host_infData_to_hash($infData); | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | } else { | 
| 770 | 0 |  |  |  |  |  | $Error = "Unknown object type '$type'"; | 
| 771 | 0 |  |  |  |  |  | return undef; | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | } | 
| 774 |  |  |  |  |  |  | } | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | sub _get_common_properties_from_infData { | 
| 777 | 0 |  |  | 0 |  |  | my ($self, $infData, @extra) = @_; | 
| 778 | 0 |  |  |  |  |  | my $hash = {}; | 
| 779 |  |  |  |  |  |  |  | 
| 780 | 0 |  |  |  |  |  | my @default = qw(roid clID crID crDate upID upDate trDate); | 
| 781 |  |  |  |  |  |  |  | 
| 782 | 0 |  |  |  |  |  | foreach my $name (@default, @extra) { | 
| 783 | 0 |  |  |  |  |  | my $els = $infData->getElementsByLocalName($name); | 
| 784 | 0 | 0 |  |  |  |  | $hash->{$name} = $els->shift->textContent if ($els->size > 0); | 
| 785 |  |  |  |  |  |  | } | 
| 786 |  |  |  |  |  |  |  | 
| 787 | 0 |  |  |  |  |  | my $codes = $infData->getElementsByLocalName('status'); | 
| 788 | 0 |  |  |  |  |  | while (my $code = $codes->shift) { | 
| 789 | 0 |  |  |  |  |  | push(@{$hash->{status}}, $code->getAttribute('s')); | 
|  | 0 |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | } | 
| 791 |  |  |  |  |  |  |  | 
| 792 | 0 |  |  |  |  |  | return $hash; | 
| 793 |  |  |  |  |  |  | } | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | =pod | 
| 796 |  |  |  |  |  |  |  | 
| 797 |  |  |  |  |  |  | =head2 Domain Information | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | The hash ref returned by C will usually look something | 
| 800 |  |  |  |  |  |  | like this: | 
| 801 |  |  |  |  |  |  |  | 
| 802 |  |  |  |  |  |  | $info = { | 
| 803 |  |  |  |  |  |  | 'contacts' => { | 
| 804 |  |  |  |  |  |  | 'admin' => 'contact-id' | 
| 805 |  |  |  |  |  |  | 'tech' => 'contact-id' | 
| 806 |  |  |  |  |  |  | 'billing' => 'contact-id' | 
| 807 |  |  |  |  |  |  | }, | 
| 808 |  |  |  |  |  |  | 'registrant' => 'contact-id', | 
| 809 |  |  |  |  |  |  | 'clID' => 'registrar-id', | 
| 810 |  |  |  |  |  |  | 'roid' => 'tld-12345', | 
| 811 |  |  |  |  |  |  | 'status' => [ | 
| 812 |  |  |  |  |  |  | 'ok' | 
| 813 |  |  |  |  |  |  | ], | 
| 814 |  |  |  |  |  |  | 'authInfo' => 'abc-12345', | 
| 815 |  |  |  |  |  |  | 'name' => 'example.tld', | 
| 816 |  |  |  |  |  |  | 'trDate' => '2011-01-18T11:08:03.0Z', | 
| 817 |  |  |  |  |  |  | 'ns' => [ | 
| 818 |  |  |  |  |  |  | 'ns0.example.com', | 
| 819 |  |  |  |  |  |  | 'ns1.example.com', | 
| 820 |  |  |  |  |  |  | ], | 
| 821 |  |  |  |  |  |  | 'crDate' => '2011-02-16T12:06:31.0Z', | 
| 822 |  |  |  |  |  |  | 'exDate' => '2011-02-16T12:06:31.0Z', | 
| 823 |  |  |  |  |  |  | 'crID' => 'registrar-id', | 
| 824 |  |  |  |  |  |  | 'upDate' => '2011-08-29T04:02:12.0Z', | 
| 825 |  |  |  |  |  |  | hosts => [ | 
| 826 |  |  |  |  |  |  | 'ns0.example.tld', | 
| 827 |  |  |  |  |  |  | 'ns1.example.tld', | 
| 828 |  |  |  |  |  |  | ], | 
| 829 |  |  |  |  |  |  | }; | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | Members of the C hash ref may be strings or, if there are | 
| 832 |  |  |  |  |  |  | multiple associations of the same type, an anonymous array of strings. | 
| 833 |  |  |  |  |  |  | If the server uses the Host Attribute model instead of the Host Object | 
| 834 |  |  |  |  |  |  | model, then the C member will look like this: | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | $info->{ns} = [ | 
| 837 |  |  |  |  |  |  | { | 
| 838 |  |  |  |  |  |  | name => 'ns0.example.com', | 
| 839 |  |  |  |  |  |  | addrs => [ | 
| 840 |  |  |  |  |  |  | version => 'v4', | 
| 841 |  |  |  |  |  |  | addr => '10.0.0.1', | 
| 842 |  |  |  |  |  |  | ], | 
| 843 |  |  |  |  |  |  | }, | 
| 844 |  |  |  |  |  |  | { | 
| 845 |  |  |  |  |  |  | name => 'ns1.example.com', | 
| 846 |  |  |  |  |  |  | addrs => [ | 
| 847 |  |  |  |  |  |  | version => 'v4', | 
| 848 |  |  |  |  |  |  | addr => '10.0.0.2', | 
| 849 |  |  |  |  |  |  | ], | 
| 850 |  |  |  |  |  |  | }, | 
| 851 |  |  |  |  |  |  | ]; | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | Note that there may be multiple members in the C section and that | 
| 854 |  |  |  |  |  |  | the C attribute is optional. | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | =cut | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  | sub _domain_infData_to_hash { | 
| 859 | 0 |  |  | 0 |  |  | my ($self, $infData, $secinfo) = @_; | 
| 860 |  |  |  |  |  |  |  | 
| 861 | 0 |  |  |  |  |  | my $hash = $self->_get_common_properties_from_infData($infData, 'registrant', 'name', 'exDate'); | 
| 862 |  |  |  |  |  |  |  | 
| 863 | 0 |  |  |  |  |  | my $contacts = $infData->getElementsByLocalName('contact'); | 
| 864 | 0 |  |  |  |  |  | while (my $contact = $contacts->shift) { | 
| 865 | 0 |  |  |  |  |  | my $type	= $contact->getAttribute('type'); | 
| 866 | 0 |  |  |  |  |  | my $id		= $contact->textContent; | 
| 867 |  |  |  |  |  |  |  | 
| 868 | 0 | 0 |  |  |  |  | if (ref($hash->{contacts}->{$type}) eq 'STRING') { | 
|  |  | 0 |  |  |  |  |  | 
| 869 | 0 |  |  |  |  |  | $hash->{contacts}->{$type} = [ $hash->{contacts}->{$type}, $id ]; | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | } elsif (ref($hash->{contacts}->{$type}) eq 'ARRAY') { | 
| 872 | 0 |  |  |  |  |  | push(@{$hash->{contacts}->{$type}}, $id); | 
|  | 0 |  |  |  |  |  |  | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | } else { | 
| 875 | 0 |  |  |  |  |  | $hash->{contacts}->{$type} = $id; | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | } | 
| 878 |  |  |  |  |  |  |  | 
| 879 |  |  |  |  |  |  | } | 
| 880 |  |  |  |  |  |  |  | 
| 881 | 0 |  |  |  |  |  | my $ns = $infData->getElementsByLocalName('ns'); | 
| 882 | 0 | 0 |  |  |  |  | if ($ns->size == 1) { | 
| 883 | 0 |  |  |  |  |  | my $el = $ns->shift; | 
| 884 | 0 |  |  |  |  |  | my $hostObjs = $el->getElementsByLocalName('hostObj'); | 
| 885 | 0 |  |  |  |  |  | while (my $hostObj = $hostObjs->shift) { | 
| 886 | 0 |  |  |  |  |  | push(@{$hash->{ns}}, $hostObj->textContent); | 
|  | 0 |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | } | 
| 888 |  |  |  |  |  |  |  | 
| 889 | 0 |  |  |  |  |  | my $hostAttrs = $el->getElementsByLocalName('hostAttr'); | 
| 890 | 0 |  |  |  |  |  | while (my $hostAttr = $hostAttrs->shift) { | 
| 891 | 0 |  |  |  |  |  | my $host = {}; | 
| 892 | 0 |  |  |  |  |  | $host->{name} = $hostAttr->getElementsByLocalName('hostName')->shift->textContent; | 
| 893 | 0 |  |  |  |  |  | my $addrs = $hostAttr->getElementsByLocalName('hostAddr'); | 
| 894 | 0 |  |  |  |  |  | while (my $addr = $addrs->shift) { | 
| 895 | 0 |  |  |  |  |  | push(@{$host->{addrs}}, { version => $addr->getAttribute('ip'), addr => $addr->textContent }); | 
|  | 0 |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | } | 
| 897 | 0 |  |  |  |  |  | push(@{$hash->{ns}}, $host); | 
|  | 0 |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | } | 
| 899 |  |  |  |  |  |  | } | 
| 900 |  |  |  |  |  |  |  | 
| 901 | 0 |  |  |  |  |  | my $hosts = $infData->getElementsByLocalName('host'); | 
| 902 | 0 |  |  |  |  |  | while (my $host = $hosts->shift) { | 
| 903 | 0 |  |  |  |  |  | push(@{$hash->{hosts}}, $host->textContent); | 
|  | 0 |  |  |  |  |  |  | 
| 904 |  |  |  |  |  |  | } | 
| 905 |  |  |  |  |  |  |  | 
| 906 | 0 |  |  |  |  |  | my $auths = $infData->getElementsByLocalName('authInfo'); | 
| 907 | 0 | 0 |  |  |  |  | if ($auths->size == 1) { | 
| 908 | 0 |  |  |  |  |  | my $authInfo = $auths->shift; | 
| 909 | 0 |  |  |  |  |  | my $pw = $authInfo->getElementsByLocalName('pw'); | 
| 910 | 0 | 0 |  |  |  |  | $hash->{authInfo} = $pw->shift->textContent if ($pw->size == 1); | 
| 911 |  |  |  |  |  |  | } | 
| 912 |  |  |  |  |  |  |  | 
| 913 | 0 | 0 |  |  |  |  | if (defined $secinfo) { | 
| 914 | 0 | 0 |  |  |  |  | if (my $maxSigLife = $secinfo->getElementsByLocalName('maxSigLife')) { | 
| 915 | 0 |  |  |  |  |  | $hash->{maxSigLife} = $maxSigLife->shift->textContent; | 
| 916 |  |  |  |  |  |  | } | 
| 917 | 0 |  |  |  |  |  | my $dslist = $secinfo->getElementsByTagName('secDNS:dsData'); | 
| 918 | 0 |  |  |  |  |  | while (my $ds = $dslist->shift) { | 
| 919 | 0 |  |  |  |  |  | my @ds = map { $ds->getElementsByLocalName($_)->string_value() } | 
|  | 0 |  |  |  |  |  |  | 
| 920 |  |  |  |  |  |  | qw(keyTag alg digestType digest); | 
| 921 | 0 |  |  |  |  |  | push @{ $hash->{DS} }, "@ds"; | 
|  | 0 |  |  |  |  |  |  | 
| 922 |  |  |  |  |  |  | } | 
| 923 | 0 |  |  |  |  |  | my $keylist = $secinfo->getElementsByLocalName('keyData'); | 
| 924 | 0 |  |  |  |  |  | while (my $key = $keylist->shift) { | 
| 925 | 0 |  |  |  |  |  | my @key = map { $key->getElementsByLocalName($_)->string_value() } | 
|  | 0 |  |  |  |  |  |  | 
| 926 |  |  |  |  |  |  | qw(flags protocol alg pubKey); | 
| 927 | 0 |  |  |  |  |  | push @{ $hash->{DNSKEY} }, "@key"; | 
|  | 0 |  |  |  |  |  |  | 
| 928 |  |  |  |  |  |  | } | 
| 929 |  |  |  |  |  |  | } | 
| 930 |  |  |  |  |  |  |  | 
| 931 | 0 |  |  |  |  |  | return $hash; | 
| 932 |  |  |  |  |  |  | } | 
| 933 |  |  |  |  |  |  |  | 
| 934 |  |  |  |  |  |  |  | 
| 935 |  |  |  |  |  |  | =pod | 
| 936 |  |  |  |  |  |  |  | 
| 937 |  |  |  |  |  |  | =head2 Host Information | 
| 938 |  |  |  |  |  |  |  | 
| 939 |  |  |  |  |  |  | The hash ref returned by C will usually look something like | 
| 940 |  |  |  |  |  |  | this: | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | $info = { | 
| 943 |  |  |  |  |  |  | 'crDate' => '2011-09-17T15:38:56.0Z', | 
| 944 |  |  |  |  |  |  | 'clID' => 'registrar-id', | 
| 945 |  |  |  |  |  |  | 'crID' => 'registrar-id', | 
| 946 |  |  |  |  |  |  | 'roid' => 'tld-12345', | 
| 947 |  |  |  |  |  |  | 'status' => [ | 
| 948 |  |  |  |  |  |  | 'linked', | 
| 949 |  |  |  |  |  |  | 'serverDeleteProhibited', | 
| 950 |  |  |  |  |  |  | ], | 
| 951 |  |  |  |  |  |  | 'name' => 'ns0.example.tld', | 
| 952 |  |  |  |  |  |  | 'addrs' => [ | 
| 953 |  |  |  |  |  |  | { | 
| 954 |  |  |  |  |  |  | 'version' => 'v4', | 
| 955 |  |  |  |  |  |  | 'addr' => '10.0.0.1' | 
| 956 |  |  |  |  |  |  | } | 
| 957 |  |  |  |  |  |  | ] | 
| 958 |  |  |  |  |  |  | }; | 
| 959 |  |  |  |  |  |  |  | 
| 960 |  |  |  |  |  |  | Note that hosts may have multiple addresses, and that C is | 
| 961 |  |  |  |  |  |  | optional. | 
| 962 |  |  |  |  |  |  |  | 
| 963 |  |  |  |  |  |  | =cut | 
| 964 |  |  |  |  |  |  |  | 
| 965 |  |  |  |  |  |  | sub _host_infData_to_hash { | 
| 966 | 0 |  |  | 0 |  |  | my ($self, $infData) = @_; | 
| 967 |  |  |  |  |  |  |  | 
| 968 | 0 |  |  |  |  |  | my $hash = $self->_get_common_properties_from_infData($infData, 'name'); | 
| 969 |  |  |  |  |  |  |  | 
| 970 | 0 |  |  |  |  |  | my $addrs = $infData->getElementsByLocalName('addr'); | 
| 971 | 0 |  |  |  |  |  | while (my $addr = $addrs->shift) { | 
| 972 | 0 |  |  |  |  |  | push(@{$hash->{addrs}}, { version => $addr->getAttribute('ip'), addr => $addr->textContent }); | 
|  | 0 |  |  |  |  |  |  | 
| 973 |  |  |  |  |  |  | } | 
| 974 |  |  |  |  |  |  |  | 
| 975 | 0 |  |  |  |  |  | return $hash; | 
| 976 |  |  |  |  |  |  | } | 
| 977 |  |  |  |  |  |  |  | 
| 978 |  |  |  |  |  |  | =pod | 
| 979 |  |  |  |  |  |  |  | 
| 980 |  |  |  |  |  |  | =head2 Contact Information | 
| 981 |  |  |  |  |  |  |  | 
| 982 |  |  |  |  |  |  | The hash ref returned by C will usually look something | 
| 983 |  |  |  |  |  |  | like this: | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | $VAR1 = { | 
| 986 |  |  |  |  |  |  | 'id' => 'contact-id', | 
| 987 |  |  |  |  |  |  | 'postalInfo' => { | 
| 988 |  |  |  |  |  |  | 'int' => { | 
| 989 |  |  |  |  |  |  | 'name' => 'John Doe', | 
| 990 |  |  |  |  |  |  | 'org' => 'Example Inc.', | 
| 991 |  |  |  |  |  |  | 'addr' => { | 
| 992 |  |  |  |  |  |  | 'street' => [ | 
| 993 |  |  |  |  |  |  | '123 Example Dr.' | 
| 994 |  |  |  |  |  |  | 'Suite 100' | 
| 995 |  |  |  |  |  |  | ], | 
| 996 |  |  |  |  |  |  | 'city' => 'Dulles', | 
| 997 |  |  |  |  |  |  | 'sp' => 'VA', | 
| 998 |  |  |  |  |  |  | 'pc' => '20116-6503' | 
| 999 |  |  |  |  |  |  | 'cc' => 'US', | 
| 1000 |  |  |  |  |  |  | } | 
| 1001 |  |  |  |  |  |  | } | 
| 1002 |  |  |  |  |  |  | }, | 
| 1003 |  |  |  |  |  |  | 'clID' => 'registrar-id', | 
| 1004 |  |  |  |  |  |  | 'roid' => 'CNIC-HA321983', | 
| 1005 |  |  |  |  |  |  | 'status' => [ | 
| 1006 |  |  |  |  |  |  | 'linked', | 
| 1007 |  |  |  |  |  |  | 'serverDeleteProhibited' | 
| 1008 |  |  |  |  |  |  | ], | 
| 1009 |  |  |  |  |  |  | 'voice' => '+1.7035555555x1234', | 
| 1010 |  |  |  |  |  |  | 'fax' => '+1.7035555556', | 
| 1011 |  |  |  |  |  |  | 'email' => 'jdoe@example.com', | 
| 1012 |  |  |  |  |  |  | 'crDate' => '2011-09-23T03:51:29.0Z', | 
| 1013 |  |  |  |  |  |  | 'upDate' => '1999-11-30T00:00:00.0Z' | 
| 1014 |  |  |  |  |  |  | }; | 
| 1015 |  |  |  |  |  |  |  | 
| 1016 |  |  |  |  |  |  | There may be up to two members of the C hash, corresponding | 
| 1017 |  |  |  |  |  |  | to the C and C internationalised and localised types. | 
| 1018 |  |  |  |  |  |  |  | 
| 1019 |  |  |  |  |  |  | =cut | 
| 1020 |  |  |  |  |  |  |  | 
| 1021 |  |  |  |  |  |  | sub _contact_infData_to_hash { | 
| 1022 | 0 |  |  | 0 |  |  | my ($self, $infData) = @_; | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 | 0 |  |  |  |  |  | my $hash = $self->_get_common_properties_from_infData($infData, 'email', 'id'); | 
| 1025 |  |  |  |  |  |  |  | 
| 1026 |  |  |  |  |  |  | # remove this as it gets in the way: | 
| 1027 | 0 |  |  |  |  |  | my $els = $infData->getElementsByLocalName('disclose'); | 
| 1028 | 0 | 0 |  |  |  |  | if ($els->size > 0) { | 
| 1029 | 0 |  |  |  |  |  | while (my $el = $els->shift) { | 
| 1030 | 0 |  |  |  |  |  | $el->parentNode->removeChild($el); | 
| 1031 |  |  |  |  |  |  | } | 
| 1032 |  |  |  |  |  |  | } | 
| 1033 |  |  |  |  |  |  |  | 
| 1034 | 0 |  |  |  |  |  | foreach my $name ('voice', 'fax') { | 
| 1035 | 0 |  |  |  |  |  | my $els = $infData->getElementsByLocalName($name); | 
| 1036 | 0 | 0 | 0 |  |  |  | if (defined($els) && $els->size == 1) { | 
| 1037 | 0 |  |  |  |  |  | my $el = $els->shift; | 
| 1038 | 0 | 0 |  |  |  |  | if (defined($el)) { | 
| 1039 | 0 |  |  |  |  |  | $hash->{$name} = $el->textContent; | 
| 1040 | 0 | 0 | 0 |  |  |  | $hash->{$name} .= 'x'.$el->getAttribute('x') if (defined($el->getAttribute('x')) && $el->getAttribute('x') ne ''); | 
| 1041 |  |  |  |  |  |  | } | 
| 1042 |  |  |  |  |  |  | } | 
| 1043 |  |  |  |  |  |  | } | 
| 1044 |  |  |  |  |  |  |  | 
| 1045 | 0 |  |  |  |  |  | my $postalInfo = $infData->getElementsByLocalName('postalInfo'); | 
| 1046 | 0 |  |  |  |  |  | while (my $info = $postalInfo->shift) { | 
| 1047 | 0 |  |  |  |  |  | my $ref = {}; | 
| 1048 |  |  |  |  |  |  |  | 
| 1049 | 0 |  |  |  |  |  | foreach my $name (qw(name org)) { | 
| 1050 | 0 |  |  |  |  |  | my $els = $info->getElementsByLocalName($name); | 
| 1051 | 0 | 0 |  |  |  |  | $ref->{$name} = $els->shift->textContent if ($els->size == 1); | 
| 1052 |  |  |  |  |  |  | } | 
| 1053 |  |  |  |  |  |  |  | 
| 1054 | 0 |  |  |  |  |  | my $addrs = $info->getElementsByLocalName('addr'); | 
| 1055 | 0 | 0 |  |  |  |  | if ($addrs->size == 1) { | 
| 1056 | 0 |  |  |  |  |  | my $addr = $addrs->shift; | 
| 1057 | 0 |  |  |  |  |  | foreach my $child ($addr->childNodes) { | 
| 1058 | 0 | 0 |  |  |  |  | next if (XML::LibXML::XML_ELEMENT_NODE != $child->nodeType); | 
| 1059 | 0 | 0 |  |  |  |  | if ($child->localName eq 'street') { | 
| 1060 | 0 |  |  |  |  |  | push(@{$ref->{addr}->{$child->localName}}, $child->textContent); | 
|  | 0 |  |  |  |  |  |  | 
| 1061 |  |  |  |  |  |  |  | 
| 1062 |  |  |  |  |  |  | } else { | 
| 1063 | 0 |  |  |  |  |  | $ref->{addr}->{$child->localName} = $child->textContent; | 
| 1064 |  |  |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  | } | 
| 1066 |  |  |  |  |  |  | } | 
| 1067 |  |  |  |  |  |  | } | 
| 1068 |  |  |  |  |  |  |  | 
| 1069 | 0 |  |  |  |  |  | $hash->{postalInfo}->{$info->getAttribute('type')} = $ref; | 
| 1070 |  |  |  |  |  |  | } | 
| 1071 |  |  |  |  |  |  |  | 
| 1072 | 0 |  |  |  |  |  | my $auths = $infData->getElementsByLocalName('authInfo'); | 
| 1073 | 0 | 0 |  |  |  |  | if ($auths->size == 1) { | 
| 1074 | 0 |  |  |  |  |  | my $authInfo = $auths->shift; | 
| 1075 | 0 |  |  |  |  |  | my $pw = $authInfo->getElementsByLocalName('pw'); | 
| 1076 | 0 | 0 |  |  |  |  | $hash->{authInfo} = $pw->shift->textContent if ($pw->size == 1); | 
| 1077 |  |  |  |  |  |  | } | 
| 1078 |  |  |  |  |  |  |  | 
| 1079 | 0 |  |  |  |  |  | return $hash; | 
| 1080 |  |  |  |  |  |  | } | 
| 1081 |  |  |  |  |  |  |  | 
| 1082 |  |  |  |  |  |  | =pod | 
| 1083 |  |  |  |  |  |  |  | 
| 1084 |  |  |  |  |  |  | =head1 Object Transfers | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | The EPP CtransferE> command suppots five different operations: | 
| 1087 |  |  |  |  |  |  | query, request, cancel, approve, and reject. C makes | 
| 1088 |  |  |  |  |  |  | these available using the following methods: | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | # For domain objects: | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | $epp->domain_transfer_query($domain); | 
| 1093 |  |  |  |  |  |  | $epp->domain_transfer_cancel($domain); | 
| 1094 |  |  |  |  |  |  | $epp->domain_transfer_request($domain, $authInfo, $period); | 
| 1095 |  |  |  |  |  |  | $epp->domain_transfer_approve($domain); | 
| 1096 |  |  |  |  |  |  | $epp->domain_transfer_reject($domain); | 
| 1097 |  |  |  |  |  |  |  | 
| 1098 |  |  |  |  |  |  | # For contact objects: | 
| 1099 |  |  |  |  |  |  |  | 
| 1100 |  |  |  |  |  |  | $epp->contact_transfer_query($contact); | 
| 1101 |  |  |  |  |  |  | $epp->contact_transfer_cancel($contact); | 
| 1102 |  |  |  |  |  |  | $epp->contact_transfer_request($contact, $authInfo); | 
| 1103 |  |  |  |  |  |  | $epp->contact_transfer_approve($contact); | 
| 1104 |  |  |  |  |  |  | $epp->contact_transfer_reject($contact); | 
| 1105 |  |  |  |  |  |  |  | 
| 1106 |  |  |  |  |  |  | Most of these methods will just set the value of C<$Net::EPP::Simple::Code> | 
| 1107 |  |  |  |  |  |  | and return either true or false. However, the C, | 
| 1108 |  |  |  |  |  |  | C, C and C | 
| 1109 |  |  |  |  |  |  | methods will return a hash ref that looks like this: | 
| 1110 |  |  |  |  |  |  |  | 
| 1111 |  |  |  |  |  |  | my $trnData = { | 
| 1112 |  |  |  |  |  |  | 'name' => 'example.tld', | 
| 1113 |  |  |  |  |  |  | 'reID' => 'losing-registrar', | 
| 1114 |  |  |  |  |  |  | 'acDate' => '2011-12-04T12:24:53.0Z', | 
| 1115 |  |  |  |  |  |  | 'acID' => 'gaining-registrar', | 
| 1116 |  |  |  |  |  |  | 'reDate' => '2011-11-29T12:24:53.0Z', | 
| 1117 |  |  |  |  |  |  | 'trStatus' => 'pending' | 
| 1118 |  |  |  |  |  |  | }; | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  | =cut | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 |  |  |  |  |  |  | sub _transfer_request { | 
| 1123 | 0 |  |  | 0 |  |  | my ($self, $op, $type, $identifier, $authInfo, $period) = @_; | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 | 0 |  |  |  |  |  | my $class = sprintf('Net::EPP::Frame::Command::Transfer::%s', ucfirst(lc($type))); | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 | 0 |  |  |  |  |  | my $frame; | 
| 1128 | 0 |  |  |  |  |  | eval("\$frame = $class->new"); | 
| 1129 | 0 | 0 | 0 |  |  |  | if ($@ || ref($frame) ne $class) { | 
| 1130 | 0 |  |  |  |  |  | $Error = "Error building request frame: $@"; | 
| 1131 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 1132 | 0 |  |  |  |  |  | return undef; | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | } else { | 
| 1135 | 0 |  |  |  |  |  | $frame->setOp($op); | 
| 1136 | 0 | 0 |  |  |  |  | if ($type eq 'domain') { | 
|  |  | 0 |  |  |  |  |  | 
| 1137 | 0 |  |  |  |  |  | $frame->setDomain($identifier); | 
| 1138 | 0 | 0 |  |  |  |  | $frame->setPeriod(int($period)) if ($op eq 'request'); | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 1141 | 0 |  |  |  |  |  | $frame->setContact($identifier); | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | } | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 | 0 | 0 | 0 |  |  |  | if ($op eq 'request' || $op eq 'query') { | 
| 1146 | 0 | 0 |  |  |  |  | $frame->setAuthInfo($authInfo) if ($authInfo ne ''); | 
| 1147 |  |  |  |  |  |  | } | 
| 1148 |  |  |  |  |  |  |  | 
| 1149 |  |  |  |  |  |  | } | 
| 1150 |  |  |  |  |  |  |  | 
| 1151 | 0 |  |  |  |  |  | my $response = $self->_request($frame); | 
| 1152 |  |  |  |  |  |  |  | 
| 1153 |  |  |  |  |  |  |  | 
| 1154 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 1155 | 0 |  |  |  |  |  | return undef; | 
| 1156 |  |  |  |  |  |  |  | 
| 1157 |  |  |  |  |  |  | } else { | 
| 1158 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 1159 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 1160 |  |  |  |  |  |  |  | 
| 1161 | 0 | 0 | 0 |  |  |  | if ($Code > 1999) { | 
|  |  | 0 |  |  |  |  |  | 
| 1162 | 0 |  |  |  |  |  | $Error = $response->msg; | 
| 1163 | 0 |  |  |  |  |  | return undef; | 
| 1164 |  |  |  |  |  |  |  | 
| 1165 |  |  |  |  |  |  | } elsif ($op eq 'query' || $op eq 'request') { | 
| 1166 | 0 |  |  |  |  |  | my $trnData = $response->getElementsByLocalName('trnData')->shift; | 
| 1167 | 0 |  |  |  |  |  | my $hash = {}; | 
| 1168 | 0 |  |  |  |  |  | foreach my $child ($trnData->childNodes) { | 
| 1169 | 0 |  |  |  |  |  | $hash->{$child->localName} = $child->textContent; | 
| 1170 |  |  |  |  |  |  | } | 
| 1171 |  |  |  |  |  |  |  | 
| 1172 | 0 |  |  |  |  |  | return $hash; | 
| 1173 |  |  |  |  |  |  |  | 
| 1174 |  |  |  |  |  |  | } else { | 
| 1175 | 0 |  |  |  |  |  | return 1; | 
| 1176 |  |  |  |  |  |  |  | 
| 1177 |  |  |  |  |  |  | } | 
| 1178 |  |  |  |  |  |  | } | 
| 1179 |  |  |  |  |  |  | } | 
| 1180 |  |  |  |  |  |  |  | 
| 1181 |  |  |  |  |  |  | sub domain_transfer_query { | 
| 1182 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('query', 'domain', $_[1]); | 
| 1183 |  |  |  |  |  |  | } | 
| 1184 |  |  |  |  |  |  |  | 
| 1185 |  |  |  |  |  |  | sub domain_transfer_cancel { | 
| 1186 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('cancel', 'domain', $_[1]); | 
| 1187 |  |  |  |  |  |  | } | 
| 1188 |  |  |  |  |  |  |  | 
| 1189 |  |  |  |  |  |  | sub domain_transfer_request { | 
| 1190 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('request', 'domain', $_[1], $_[2], $_[3]); | 
| 1191 |  |  |  |  |  |  | } | 
| 1192 |  |  |  |  |  |  |  | 
| 1193 |  |  |  |  |  |  | sub domain_transfer_approve { | 
| 1194 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('approve', 'domain', $_[1]); | 
| 1195 |  |  |  |  |  |  | } | 
| 1196 |  |  |  |  |  |  |  | 
| 1197 |  |  |  |  |  |  | sub domain_transfer_reject { | 
| 1198 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('reject', 'domain', $_[1]); | 
| 1199 |  |  |  |  |  |  | } | 
| 1200 |  |  |  |  |  |  |  | 
| 1201 |  |  |  |  |  |  | sub contact_transfer_query { | 
| 1202 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('query', 'contact', $_[1]); | 
| 1203 |  |  |  |  |  |  | } | 
| 1204 |  |  |  |  |  |  |  | 
| 1205 |  |  |  |  |  |  | sub contact_transfer_cancel { | 
| 1206 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('cancel', 'contact', $_[1]); | 
| 1207 |  |  |  |  |  |  | } | 
| 1208 |  |  |  |  |  |  |  | 
| 1209 |  |  |  |  |  |  | sub contact_transfer_request { | 
| 1210 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('request', 'contact', $_[1], $_[2]); | 
| 1211 |  |  |  |  |  |  | } | 
| 1212 |  |  |  |  |  |  |  | 
| 1213 |  |  |  |  |  |  | sub contact_transfer_approve { | 
| 1214 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('approve', 'contact', $_[1]); | 
| 1215 |  |  |  |  |  |  | } | 
| 1216 |  |  |  |  |  |  |  | 
| 1217 |  |  |  |  |  |  | sub contact_transfer_reject { | 
| 1218 | 0 |  |  | 0 | 0 |  | return $_[0]->_transfer_request('reject', 'contact', $_[1]); | 
| 1219 |  |  |  |  |  |  | } | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 |  |  |  |  |  |  | =pod | 
| 1222 |  |  |  |  |  |  |  | 
| 1223 |  |  |  |  |  |  | =head1 Creating Objects | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 |  |  |  |  |  |  | The following methods can be used to create a new object at the server: | 
| 1226 |  |  |  |  |  |  |  | 
| 1227 |  |  |  |  |  |  | $epp->create_domain($domain); | 
| 1228 |  |  |  |  |  |  | $epp->create_host($host); | 
| 1229 |  |  |  |  |  |  | $epp->create_contact($contact); | 
| 1230 |  |  |  |  |  |  |  | 
| 1231 |  |  |  |  |  |  | The argument for these methods is a hash ref of the same format as that | 
| 1232 |  |  |  |  |  |  | returned by the info methods above. As a result, cloning an existing | 
| 1233 |  |  |  |  |  |  | object is as simple as the following: | 
| 1234 |  |  |  |  |  |  |  | 
| 1235 |  |  |  |  |  |  | my $info = $epp->contact_info($contact); | 
| 1236 |  |  |  |  |  |  |  | 
| 1237 |  |  |  |  |  |  | # set a new contact ID to avoid clashing with the existing object | 
| 1238 |  |  |  |  |  |  | $info->{id} = $new_contact; | 
| 1239 |  |  |  |  |  |  |  | 
| 1240 |  |  |  |  |  |  | # randomize authInfo: | 
| 1241 |  |  |  |  |  |  | $info->{authInfo} = $random_string; | 
| 1242 |  |  |  |  |  |  |  | 
| 1243 |  |  |  |  |  |  | $epp->create_contact($info); | 
| 1244 |  |  |  |  |  |  |  | 
| 1245 |  |  |  |  |  |  | C will ignore object properties that it does not recognise, | 
| 1246 |  |  |  |  |  |  | and those properties (such as server-managed status codes) that clients are | 
| 1247 |  |  |  |  |  |  | not permitted to set. | 
| 1248 |  |  |  |  |  |  |  | 
| 1249 |  |  |  |  |  |  | =head2 Creating New Domains | 
| 1250 |  |  |  |  |  |  |  | 
| 1251 |  |  |  |  |  |  | When creating a new domain object, you may also specify a C key, like so: | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 |  |  |  |  |  |  | my $domain = { | 
| 1254 |  |  |  |  |  |  | 'name' => 'example.tld', | 
| 1255 |  |  |  |  |  |  | 'period' => 2, | 
| 1256 |  |  |  |  |  |  | 'registrant' => 'contact-id', | 
| 1257 |  |  |  |  |  |  | 'contacts' => { | 
| 1258 |  |  |  |  |  |  | 'tech' => 'contact-id', | 
| 1259 |  |  |  |  |  |  | 'admin' => 'contact-id', | 
| 1260 |  |  |  |  |  |  | 'billing' => 'contact-id', | 
| 1261 |  |  |  |  |  |  | }, | 
| 1262 |  |  |  |  |  |  | 'status' => [ | 
| 1263 |  |  |  |  |  |  | 'clientTransferProhibited', | 
| 1264 |  |  |  |  |  |  | ], | 
| 1265 |  |  |  |  |  |  | 'ns' => { | 
| 1266 |  |  |  |  |  |  | 'ns0.example.com', | 
| 1267 |  |  |  |  |  |  | 'ns1.example.com', | 
| 1268 |  |  |  |  |  |  | }, | 
| 1269 |  |  |  |  |  |  | }; | 
| 1270 |  |  |  |  |  |  |  | 
| 1271 |  |  |  |  |  |  | $epp->create_domain($domain); | 
| 1272 |  |  |  |  |  |  |  | 
| 1273 |  |  |  |  |  |  | The C key is assumed to be in years rather than months. C | 
| 1274 |  |  |  |  |  |  | assumes the registry uses the host object model rather than the host attribute model. | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 |  |  |  |  |  |  | =cut | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 |  |  |  |  |  |  | sub create_domain { | 
| 1279 | 0 |  |  | 0 | 0 |  | my ($self, $domain) = @_; | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 | 0 |  |  |  |  |  | return $self->_get_response_result( | 
| 1282 |  |  |  |  |  |  | $self->_request( | 
| 1283 |  |  |  |  |  |  | $self->_prepare_create_domain_frame($domain) | 
| 1284 |  |  |  |  |  |  | ) | 
| 1285 |  |  |  |  |  |  | ); | 
| 1286 |  |  |  |  |  |  | } | 
| 1287 |  |  |  |  |  |  |  | 
| 1288 |  |  |  |  |  |  | sub _prepare_create_domain_frame { | 
| 1289 | 0 |  |  | 0 |  |  | my ($self, $domain) = @_; | 
| 1290 |  |  |  |  |  |  |  | 
| 1291 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Create::Domain->new; | 
| 1292 | 0 |  |  |  |  |  | $frame->setDomain($domain->{'name'}); | 
| 1293 | 0 | 0 | 0 |  |  |  | $frame->setPeriod($domain->{'period'}) if (defined($domain->{period}) && $domain->{period} > 0); | 
| 1294 | 0 | 0 | 0 |  |  |  | $frame->setNS(@{$domain->{'ns'}}) if $domain->{'ns'} and @{$domain->{'ns'}}; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 1295 | 0 | 0 | 0 |  |  |  | $frame->setRegistrant($domain->{'registrant'}) if (defined($domain->{registrant}) && $domain->{registrant} ne ''); | 
| 1296 | 0 |  |  |  |  |  | $frame->setContacts($domain->{'contacts'}); | 
| 1297 | 0 | 0 | 0 |  |  |  | $frame->setAuthInfo($domain->{authInfo}) if (defined($domain->{authInfo}) && $domain->{authInfo} ne ''); | 
| 1298 | 0 |  |  |  |  |  | return $frame; | 
| 1299 |  |  |  |  |  |  | } | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | =head2 Creating Hosts | 
| 1302 |  |  |  |  |  |  |  | 
| 1303 |  |  |  |  |  |  | my $host = { | 
| 1304 |  |  |  |  |  |  | name  => 'ns1.example.tld', | 
| 1305 |  |  |  |  |  |  | addrs => [ | 
| 1306 |  |  |  |  |  |  | { ip => '123.45.67.89', version => 'v4' }, | 
| 1307 |  |  |  |  |  |  | { ip => '98.76.54.32',  version => 'v4' }, | 
| 1308 |  |  |  |  |  |  | ], | 
| 1309 |  |  |  |  |  |  | }; | 
| 1310 |  |  |  |  |  |  | $epp->create_host($host); | 
| 1311 |  |  |  |  |  |  |  | 
| 1312 |  |  |  |  |  |  | =cut | 
| 1313 |  |  |  |  |  |  |  | 
| 1314 |  |  |  |  |  |  | sub create_host { | 
| 1315 | 0 |  |  | 0 | 0 |  | my ($self, $host) = @_; | 
| 1316 |  |  |  |  |  |  |  | 
| 1317 | 0 |  |  |  |  |  | return $self->_get_response_result( | 
| 1318 |  |  |  |  |  |  | $self->_request( | 
| 1319 |  |  |  |  |  |  | $self->_prepare_create_host_frame($host) | 
| 1320 |  |  |  |  |  |  | ) | 
| 1321 |  |  |  |  |  |  | ); | 
| 1322 |  |  |  |  |  |  | } | 
| 1323 |  |  |  |  |  |  |  | 
| 1324 |  |  |  |  |  |  | sub _prepare_create_host_frame { | 
| 1325 | 0 |  |  | 0 |  |  | my ($self, $host) = @_; | 
| 1326 |  |  |  |  |  |  |  | 
| 1327 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Create::Host->new; | 
| 1328 | 0 |  |  |  |  |  | $frame->setHost($host->{name}); | 
| 1329 | 0 |  |  |  |  |  | $frame->setAddr(@{$host->{addrs}}); | 
|  | 0 |  |  |  |  |  |  | 
| 1330 | 0 |  |  |  |  |  | return $frame; | 
| 1331 |  |  |  |  |  |  | } | 
| 1332 |  |  |  |  |  |  |  | 
| 1333 |  |  |  |  |  |  | sub create_contact { | 
| 1334 | 0 |  |  | 0 | 0 |  | my ($self, $contact) = @_; | 
| 1335 |  |  |  |  |  |  |  | 
| 1336 | 0 |  |  |  |  |  | return $self->_get_response_result( | 
| 1337 |  |  |  |  |  |  | $self->_request( | 
| 1338 |  |  |  |  |  |  | $self->_prepare_create_contact_frame($contact) | 
| 1339 |  |  |  |  |  |  | ) | 
| 1340 |  |  |  |  |  |  | ); | 
| 1341 |  |  |  |  |  |  | } | 
| 1342 |  |  |  |  |  |  |  | 
| 1343 |  |  |  |  |  |  |  | 
| 1344 |  |  |  |  |  |  | sub _prepare_create_contact_frame { | 
| 1345 | 0 |  |  | 0 |  |  | my ($self, $contact) = @_; | 
| 1346 |  |  |  |  |  |  |  | 
| 1347 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Create::Contact->new; | 
| 1348 |  |  |  |  |  |  |  | 
| 1349 | 0 |  |  |  |  |  | $frame->setContact($contact->{id}); | 
| 1350 |  |  |  |  |  |  |  | 
| 1351 | 0 | 0 |  |  |  |  | if (ref($contact->{postalInfo}) eq 'HASH') { | 
| 1352 | 0 |  |  |  |  |  | foreach my $type (keys(%{$contact->{postalInfo}})) { | 
|  | 0 |  |  |  |  |  |  | 
| 1353 |  |  |  |  |  |  | $frame->addPostalInfo( | 
| 1354 |  |  |  |  |  |  | $type, | 
| 1355 |  |  |  |  |  |  | $contact->{postalInfo}->{$type}->{name}, | 
| 1356 |  |  |  |  |  |  | $contact->{postalInfo}->{$type}->{org}, | 
| 1357 |  |  |  |  |  |  | $contact->{postalInfo}->{$type}->{addr} | 
| 1358 | 0 |  |  |  |  |  | ); | 
| 1359 |  |  |  |  |  |  | } | 
| 1360 |  |  |  |  |  |  | } | 
| 1361 |  |  |  |  |  |  |  | 
| 1362 | 0 | 0 | 0 |  |  |  | $frame->setVoice($contact->{voice}) if (defined($contact->{voice}) && $contact->{voice} ne ''); | 
| 1363 | 0 | 0 | 0 |  |  |  | $frame->setFax($contact->{fax}) if (defined($contact->{fax}) && $contact->{fax} ne ''); | 
| 1364 | 0 |  |  |  |  |  | $frame->setEmail($contact->{email}); | 
| 1365 | 0 | 0 | 0 |  |  |  | $frame->setAuthInfo($contact->{authInfo}) if (defined($contact->{authInfo}) && $contact->{authInfo} ne ''); | 
| 1366 |  |  |  |  |  |  |  | 
| 1367 | 0 | 0 |  |  |  |  | if (ref($contact->{status}) eq 'ARRAY') { | 
| 1368 | 0 |  |  |  |  |  | foreach my $status (grep { /^client/ } @{$contact->{status}}) { | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 1369 | 0 |  |  |  |  |  | $frame->appendStatus($status); | 
| 1370 |  |  |  |  |  |  | } | 
| 1371 |  |  |  |  |  |  | } | 
| 1372 | 0 |  |  |  |  |  | return $frame; | 
| 1373 |  |  |  |  |  |  | } | 
| 1374 |  |  |  |  |  |  |  | 
| 1375 |  |  |  |  |  |  |  | 
| 1376 |  |  |  |  |  |  | # Process response code and return result | 
| 1377 |  |  |  |  |  |  | sub _get_response_result { | 
| 1378 | 0 |  |  | 0 |  |  | my ($self, $response) = @_; | 
| 1379 |  |  |  |  |  |  |  | 
| 1380 | 0 | 0 |  |  |  |  | return undef if !$response; | 
| 1381 |  |  |  |  |  |  |  | 
| 1382 |  |  |  |  |  |  | # If there was a response... | 
| 1383 | 0 |  |  |  |  |  | $Code    = $self->_get_response_code($response); | 
| 1384 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 1385 | 0 | 0 |  |  |  |  | if ($Code > 1999) { | 
| 1386 | 0 |  |  |  |  |  | $Error = $response->msg; | 
| 1387 | 0 |  |  |  |  |  | return undef; | 
| 1388 |  |  |  |  |  |  | } | 
| 1389 | 0 |  |  |  |  |  | return 1; | 
| 1390 |  |  |  |  |  |  | } | 
| 1391 |  |  |  |  |  |  |  | 
| 1392 |  |  |  |  |  |  |  | 
| 1393 |  |  |  |  |  |  | =head1 Updating Objects | 
| 1394 |  |  |  |  |  |  |  | 
| 1395 |  |  |  |  |  |  | The following methods can be used to update an object at the server: | 
| 1396 |  |  |  |  |  |  |  | 
| 1397 |  |  |  |  |  |  | $epp->update_domain($domain); | 
| 1398 |  |  |  |  |  |  | $epp->update_host($host); | 
| 1399 |  |  |  |  |  |  | $epp->update_contact($contact); | 
| 1400 |  |  |  |  |  |  |  | 
| 1401 |  |  |  |  |  |  | Each of these methods has the same profile. They will return one of the following: | 
| 1402 |  |  |  |  |  |  |  | 
| 1403 |  |  |  |  |  |  | =over | 
| 1404 |  |  |  |  |  |  |  | 
| 1405 |  |  |  |  |  |  | =item * undef in the case of an error (check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>). | 
| 1406 |  |  |  |  |  |  |  | 
| 1407 |  |  |  |  |  |  | =item * 1 if the update request was accepted. | 
| 1408 |  |  |  |  |  |  |  | 
| 1409 |  |  |  |  |  |  | =back | 
| 1410 |  |  |  |  |  |  |  | 
| 1411 |  |  |  |  |  |  | You may wish to check the value of $Net::EPP::Simple::Code to determine whether the response code was 1000 (OK) or 1001 (action pending). | 
| 1412 |  |  |  |  |  |  |  | 
| 1413 |  |  |  |  |  |  | =cut | 
| 1414 |  |  |  |  |  |  |  | 
| 1415 |  |  |  |  |  |  |  | 
| 1416 |  |  |  |  |  |  | =head2 Updating Domains | 
| 1417 |  |  |  |  |  |  |  | 
| 1418 |  |  |  |  |  |  | Use update_domain() method to update domains' data. | 
| 1419 |  |  |  |  |  |  |  | 
| 1420 |  |  |  |  |  |  | The update info parameter may look like: | 
| 1421 |  |  |  |  |  |  | $update_info = { | 
| 1422 |  |  |  |  |  |  | name => $domain, | 
| 1423 |  |  |  |  |  |  | chg  => { | 
| 1424 |  |  |  |  |  |  | registrant => $new_registrant_id, | 
| 1425 |  |  |  |  |  |  | authInfo   => $new_domain_password, | 
| 1426 |  |  |  |  |  |  | }, | 
| 1427 |  |  |  |  |  |  | add => { | 
| 1428 |  |  |  |  |  |  | # DNS info with "hostObj" or "hostAttr" model, see create_domain() | 
| 1429 |  |  |  |  |  |  | ns       => [ ns1.example.com ns2.example.com ], | 
| 1430 |  |  |  |  |  |  | contacts => { | 
| 1431 |  |  |  |  |  |  | tech    => 'contact-id', | 
| 1432 |  |  |  |  |  |  | billing => 'contact-id', | 
| 1433 |  |  |  |  |  |  | admin   => 'contact-id', | 
| 1434 |  |  |  |  |  |  | }, | 
| 1435 |  |  |  |  |  |  |  | 
| 1436 |  |  |  |  |  |  | # Status info, simple form: | 
| 1437 |  |  |  |  |  |  | status => [ qw/ clientUpdateProhibited clientHold / ], | 
| 1438 |  |  |  |  |  |  |  | 
| 1439 |  |  |  |  |  |  | # Status info may be in more detailed form: | 
| 1440 |  |  |  |  |  |  | # status => { | 
| 1441 |  |  |  |  |  |  | #    clientUpdateProbhibited  => 'Avoid accidental change', | 
| 1442 |  |  |  |  |  |  | #    clientHold               => 'This domain is not delegated', | 
| 1443 |  |  |  |  |  |  | # }, | 
| 1444 |  |  |  |  |  |  | }, | 
| 1445 |  |  |  |  |  |  | rem => { | 
| 1446 |  |  |  |  |  |  | ns       => [ ... ], | 
| 1447 |  |  |  |  |  |  | contacts => { | 
| 1448 |  |  |  |  |  |  | tech    => 'old_tech_id', | 
| 1449 |  |  |  |  |  |  | billing => 'old_billing_id', | 
| 1450 |  |  |  |  |  |  | admin   => 'old_admin_id', | 
| 1451 |  |  |  |  |  |  | }, | 
| 1452 |  |  |  |  |  |  | status => [ qw/ clientTransferProhibited ... / ], | 
| 1453 |  |  |  |  |  |  | }, | 
| 1454 |  |  |  |  |  |  | } | 
| 1455 |  |  |  |  |  |  |  | 
| 1456 |  |  |  |  |  |  | All fields except 'name' in $update_info hash are optional. | 
| 1457 |  |  |  |  |  |  |  | 
| 1458 |  |  |  |  |  |  | =cut | 
| 1459 |  |  |  |  |  |  |  | 
| 1460 |  |  |  |  |  |  | sub update_domain { | 
| 1461 | 0 |  |  | 0 | 0 |  | my ($self, $domain) = @_; | 
| 1462 | 0 |  |  |  |  |  | return $self->_update('domain', $domain); | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 |  |  |  |  |  |  |  | 
| 1465 |  |  |  |  |  |  | =head2 Updating Contacts | 
| 1466 |  |  |  |  |  |  |  | 
| 1467 |  |  |  |  |  |  | Use update_contact() method to update contact's data. | 
| 1468 |  |  |  |  |  |  |  | 
| 1469 |  |  |  |  |  |  | The $update_info for contacts may look like this: | 
| 1470 |  |  |  |  |  |  |  | 
| 1471 |  |  |  |  |  |  | $update_info = { | 
| 1472 |  |  |  |  |  |  | id  => $contact_id, | 
| 1473 |  |  |  |  |  |  | add => { | 
| 1474 |  |  |  |  |  |  | status => [ qw/ clientDeleteProhibited / ], | 
| 1475 |  |  |  |  |  |  | # OR | 
| 1476 |  |  |  |  |  |  | # status => { | 
| 1477 |  |  |  |  |  |  | #    clientDeleteProhibited  => 'Avoid accidental removal', | 
| 1478 |  |  |  |  |  |  | # }, | 
| 1479 |  |  |  |  |  |  | }, | 
| 1480 |  |  |  |  |  |  | rem => { | 
| 1481 |  |  |  |  |  |  | status => [ qw/ clientUpdateProhibited / ], | 
| 1482 |  |  |  |  |  |  | }, | 
| 1483 |  |  |  |  |  |  | chg => { | 
| 1484 |  |  |  |  |  |  | postalInfo => { | 
| 1485 |  |  |  |  |  |  | int => { | 
| 1486 |  |  |  |  |  |  | name => 'John Doe', | 
| 1487 |  |  |  |  |  |  | org => 'Example Inc.', | 
| 1488 |  |  |  |  |  |  | addr => { | 
| 1489 |  |  |  |  |  |  | street => [ | 
| 1490 |  |  |  |  |  |  | '123 Example Dr.' | 
| 1491 |  |  |  |  |  |  | 'Suite 100' | 
| 1492 |  |  |  |  |  |  | ], | 
| 1493 |  |  |  |  |  |  | city => 'Dulles', | 
| 1494 |  |  |  |  |  |  | sp => 'VA', | 
| 1495 |  |  |  |  |  |  | pc => '20116-6503' | 
| 1496 |  |  |  |  |  |  | cc => 'US', | 
| 1497 |  |  |  |  |  |  | }, | 
| 1498 |  |  |  |  |  |  | }, | 
| 1499 |  |  |  |  |  |  | }, | 
| 1500 |  |  |  |  |  |  | voice => '+1.7035555555x1234', | 
| 1501 |  |  |  |  |  |  | fax   => '+1.7035555556', | 
| 1502 |  |  |  |  |  |  | email => 'jdoe@example.com', | 
| 1503 |  |  |  |  |  |  | authInfo => 'new-contact-password', | 
| 1504 |  |  |  |  |  |  | }, | 
| 1505 |  |  |  |  |  |  | } | 
| 1506 |  |  |  |  |  |  |  | 
| 1507 |  |  |  |  |  |  | All fields except 'id' in $update_info hash are optional. | 
| 1508 |  |  |  |  |  |  |  | 
| 1509 |  |  |  |  |  |  | =cut | 
| 1510 |  |  |  |  |  |  |  | 
| 1511 |  |  |  |  |  |  | sub update_contact { | 
| 1512 | 0 |  |  | 0 | 0 |  | my ($self, $contact) = @_; | 
| 1513 | 0 |  |  |  |  |  | return $self->_update('contact', $contact); | 
| 1514 |  |  |  |  |  |  | } | 
| 1515 |  |  |  |  |  |  |  | 
| 1516 |  |  |  |  |  |  | =head2 Updating Hosts | 
| 1517 |  |  |  |  |  |  |  | 
| 1518 |  |  |  |  |  |  | Use update_host() method to update EPP hosts. | 
| 1519 |  |  |  |  |  |  |  | 
| 1520 |  |  |  |  |  |  | The $update_info for hosts may look like this: | 
| 1521 |  |  |  |  |  |  |  | 
| 1522 |  |  |  |  |  |  | $update_info = { | 
| 1523 |  |  |  |  |  |  | name => 'ns1.example.com', | 
| 1524 |  |  |  |  |  |  | add  => { | 
| 1525 |  |  |  |  |  |  | status => [ qw/ clientDeleteProhibited / ], | 
| 1526 |  |  |  |  |  |  | # OR | 
| 1527 |  |  |  |  |  |  | # status => { | 
| 1528 |  |  |  |  |  |  | #    clientDeleteProhibited  => 'Avoid accidental removal', | 
| 1529 |  |  |  |  |  |  | # }, | 
| 1530 |  |  |  |  |  |  |  | 
| 1531 |  |  |  |  |  |  | addrs  => [ | 
| 1532 |  |  |  |  |  |  | { ip => '123.45.67.89', version => 'v4' }, | 
| 1533 |  |  |  |  |  |  | { ip => '98.76.54.32',  version => 'v4' }, | 
| 1534 |  |  |  |  |  |  | ], | 
| 1535 |  |  |  |  |  |  | }, | 
| 1536 |  |  |  |  |  |  | rem => { | 
| 1537 |  |  |  |  |  |  | status => [ qw/ clientUpdateProhibited / ], | 
| 1538 |  |  |  |  |  |  | addrs  => [ | 
| 1539 |  |  |  |  |  |  | { ip => '1.2.3.4', version => 'v4' }, | 
| 1540 |  |  |  |  |  |  | { ip => '5.6.7.8', version => 'v4' }, | 
| 1541 |  |  |  |  |  |  | ], | 
| 1542 |  |  |  |  |  |  | }, | 
| 1543 |  |  |  |  |  |  | chg => { | 
| 1544 |  |  |  |  |  |  | name => 'ns2.example.com', | 
| 1545 |  |  |  |  |  |  | }, | 
| 1546 |  |  |  |  |  |  | } | 
| 1547 |  |  |  |  |  |  |  | 
| 1548 |  |  |  |  |  |  | All fields except first 'name' in $update_info hash are optional. | 
| 1549 |  |  |  |  |  |  |  | 
| 1550 |  |  |  |  |  |  | =cut | 
| 1551 |  |  |  |  |  |  |  | 
| 1552 |  |  |  |  |  |  | sub update_host { | 
| 1553 | 0 |  |  | 0 | 0 |  | my ($self, $host) = @_; | 
| 1554 | 0 |  |  |  |  |  | return $self->_update('host', $host); | 
| 1555 |  |  |  |  |  |  | } | 
| 1556 |  |  |  |  |  |  |  | 
| 1557 |  |  |  |  |  |  |  | 
| 1558 |  |  |  |  |  |  | # Update domain/contact/host information | 
| 1559 |  |  |  |  |  |  | sub _update { | 
| 1560 | 0 |  |  | 0 |  |  | my ($self, $type, $info) = @_; | 
| 1561 |  |  |  |  |  |  |  | 
| 1562 | 0 |  |  |  |  |  | my %frame_generator = ( | 
| 1563 |  |  |  |  |  |  | 'domain'  => \&_generate_update_domain_frame, | 
| 1564 |  |  |  |  |  |  | 'contact' => \&_generate_update_contact_frame, | 
| 1565 |  |  |  |  |  |  | 'host'	  => \&_generate_update_host_frame, | 
| 1566 |  |  |  |  |  |  | ); | 
| 1567 |  |  |  |  |  |  |  | 
| 1568 | 0 | 0 |  |  |  |  | if ( !exists $frame_generator{$type} ) { | 
| 1569 | 0 |  |  |  |  |  | $Error = "Unknown object type: '$type'"; | 
| 1570 | 0 |  |  |  |  |  | return undef; | 
| 1571 |  |  |  |  |  |  | } | 
| 1572 |  |  |  |  |  |  |  | 
| 1573 | 0 |  |  |  |  |  | my $generator = $frame_generator{$type}; | 
| 1574 | 0 |  |  |  |  |  | my $frame     = $self->$generator($info); | 
| 1575 | 0 |  |  |  |  |  | return $self->_get_response_result( $self->request($frame) ); | 
| 1576 |  |  |  |  |  |  | } | 
| 1577 |  |  |  |  |  |  |  | 
| 1578 |  |  |  |  |  |  |  | 
| 1579 |  |  |  |  |  |  | sub _generate_update_domain_frame { | 
| 1580 | 0 |  |  | 0 |  |  | my ($self, $info) = @_; | 
| 1581 |  |  |  |  |  |  |  | 
| 1582 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Update::Domain->new; | 
| 1583 | 0 |  |  |  |  |  | $frame->setDomain( $info->{name} ); | 
| 1584 |  |  |  |  |  |  |  | 
| 1585 |  |  |  |  |  |  | # 'add' element | 
| 1586 | 0 | 0 | 0 |  |  |  | if ( exists $info->{add} && ref $info->{add} eq 'HASH' ) { | 
| 1587 |  |  |  |  |  |  |  | 
| 1588 | 0 |  |  |  |  |  | my $add = $info->{add}; | 
| 1589 |  |  |  |  |  |  |  | 
| 1590 |  |  |  |  |  |  | # Add DNS | 
| 1591 | 0 | 0 | 0 |  |  |  | if ( exists $add->{ns} && ref $add->{ns} eq 'ARRAY' ) { | 
| 1592 | 0 |  |  |  |  |  | $frame->addNS( @{ $add->{ns} } ); | 
|  | 0 |  |  |  |  |  |  | 
| 1593 |  |  |  |  |  |  | } | 
| 1594 |  |  |  |  |  |  |  | 
| 1595 |  |  |  |  |  |  | # Add contacts | 
| 1596 | 0 | 0 | 0 |  |  |  | if ( exists $add->{contacts} && ref $add->{contacts} eq 'HASH' ) { | 
| 1597 |  |  |  |  |  |  |  | 
| 1598 | 0 |  |  |  |  |  | my $contacts = $add->{contacts}; | 
| 1599 | 0 |  |  |  |  |  | foreach my $type ( keys %{ $contacts } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1600 | 0 |  |  |  |  |  | $frame->addContact( $type, $contacts->{$type} ); | 
| 1601 |  |  |  |  |  |  | } | 
| 1602 |  |  |  |  |  |  | } | 
| 1603 |  |  |  |  |  |  |  | 
| 1604 |  |  |  |  |  |  | # Add status info | 
| 1605 | 0 | 0 | 0 |  |  |  | if ( exists $add->{status} && ref $add->{status} ) { | 
| 1606 | 0 | 0 |  |  |  |  | if ( ref $add->{status} eq 'HASH' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1607 | 0 |  |  |  |  |  | while ( my ($type, $info) = each %{ $add->{status} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1608 | 0 |  |  |  |  |  | $frame->addStatus($type, $info); | 
| 1609 |  |  |  |  |  |  | } | 
| 1610 |  |  |  |  |  |  | } | 
| 1611 |  |  |  |  |  |  | elsif ( ref $add->{status} eq 'ARRAY' ) { | 
| 1612 | 0 |  |  |  |  |  | $frame->addStatus($_) for @{ $add->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1613 |  |  |  |  |  |  | } | 
| 1614 |  |  |  |  |  |  | } | 
| 1615 |  |  |  |  |  |  | } | 
| 1616 |  |  |  |  |  |  |  | 
| 1617 |  |  |  |  |  |  | # 'rem' element | 
| 1618 | 0 | 0 | 0 |  |  |  | if ( exists $info->{rem} && ref $info->{rem} eq 'HASH' ) { | 
| 1619 |  |  |  |  |  |  |  | 
| 1620 | 0 |  |  |  |  |  | my $rem = $info->{rem}; | 
| 1621 |  |  |  |  |  |  |  | 
| 1622 |  |  |  |  |  |  | # DNS | 
| 1623 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{ns} && ref $rem->{ns} eq 'ARRAY' ) { | 
| 1624 | 0 |  |  |  |  |  | $frame->remNS( @{ $rem->{ns} } ); | 
|  | 0 |  |  |  |  |  |  | 
| 1625 |  |  |  |  |  |  | } | 
| 1626 |  |  |  |  |  |  |  | 
| 1627 |  |  |  |  |  |  | # Contacts | 
| 1628 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{contacts} && ref $rem->{contacts} eq 'HASH' ) { | 
| 1629 | 0 |  |  |  |  |  | my $contacts = $rem->{contacts}; | 
| 1630 |  |  |  |  |  |  |  | 
| 1631 | 0 |  |  |  |  |  | foreach my $type ( keys %{ $contacts } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1632 | 0 |  |  |  |  |  | $frame->remContact( $type, $contacts->{$type} ); | 
| 1633 |  |  |  |  |  |  | } | 
| 1634 |  |  |  |  |  |  | } | 
| 1635 |  |  |  |  |  |  |  | 
| 1636 |  |  |  |  |  |  | # Status info | 
| 1637 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{status} && ref $rem->{status} eq 'ARRAY' ) { | 
| 1638 | 0 |  |  |  |  |  | $frame->remStatus($_) for @{ $rem->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1639 |  |  |  |  |  |  | } | 
| 1640 |  |  |  |  |  |  | } | 
| 1641 |  |  |  |  |  |  |  | 
| 1642 |  |  |  |  |  |  | # 'chg' element | 
| 1643 | 0 | 0 | 0 |  |  |  | if ( exists $info->{chg} && ref $info->{chg} eq 'HASH' ) { | 
| 1644 |  |  |  |  |  |  |  | 
| 1645 | 0 |  |  |  |  |  | my $chg	= $info->{chg}; | 
| 1646 |  |  |  |  |  |  |  | 
| 1647 | 0 | 0 |  |  |  |  | if ( defined $chg->{registrant} ) { | 
| 1648 | 0 |  |  |  |  |  | $frame->chgRegistrant( $chg->{registrant} ); | 
| 1649 |  |  |  |  |  |  | } | 
| 1650 |  |  |  |  |  |  |  | 
| 1651 | 0 | 0 |  |  |  |  | if ( defined $chg->{authInfo} ) { | 
| 1652 | 0 |  |  |  |  |  | $frame->chgAuthInfo( $chg->{authInfo} ); | 
| 1653 |  |  |  |  |  |  | } | 
| 1654 |  |  |  |  |  |  | } | 
| 1655 |  |  |  |  |  |  |  | 
| 1656 | 0 |  |  |  |  |  | return $frame; | 
| 1657 |  |  |  |  |  |  | } | 
| 1658 |  |  |  |  |  |  |  | 
| 1659 |  |  |  |  |  |  |  | 
| 1660 |  |  |  |  |  |  | sub _generate_update_contact_frame { | 
| 1661 | 0 |  |  | 0 |  |  | my ($self, $info) = @_; | 
| 1662 |  |  |  |  |  |  |  | 
| 1663 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Update::Contact->new; | 
| 1664 | 0 |  |  |  |  |  | $frame->setContact( $info->{id} ); | 
| 1665 |  |  |  |  |  |  |  | 
| 1666 |  |  |  |  |  |  | # Add | 
| 1667 | 0 | 0 | 0 |  |  |  | if ( exists $info->{add} && ref $info->{add} eq 'HASH' ) { | 
| 1668 | 0 |  |  |  |  |  | my $add = $info->{add}; | 
| 1669 |  |  |  |  |  |  |  | 
| 1670 | 0 | 0 | 0 |  |  |  | if ( exists $add->{status} && ref $add->{status} ) { | 
| 1671 | 0 | 0 |  |  |  |  | if ( ref $add->{status} eq 'HASH' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1672 | 0 |  |  |  |  |  | while ( my ($type, $info) = each %{ $add->{status} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1673 | 0 |  |  |  |  |  | $frame->addStatus($type, $info); | 
| 1674 |  |  |  |  |  |  | } | 
| 1675 |  |  |  |  |  |  | } | 
| 1676 |  |  |  |  |  |  | elsif ( ref $add->{status} eq 'ARRAY' ) { | 
| 1677 | 0 |  |  |  |  |  | $frame->addStatus($_) for @{ $add->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1678 |  |  |  |  |  |  | } | 
| 1679 |  |  |  |  |  |  | } | 
| 1680 |  |  |  |  |  |  | } | 
| 1681 |  |  |  |  |  |  |  | 
| 1682 |  |  |  |  |  |  | # Remove | 
| 1683 | 0 | 0 | 0 |  |  |  | if ( exists $info->{rem} && ref $info->{rem} eq 'HASH' ) { | 
| 1684 |  |  |  |  |  |  |  | 
| 1685 | 0 |  |  |  |  |  | my $rem = $info->{rem}; | 
| 1686 |  |  |  |  |  |  |  | 
| 1687 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{status} && ref $rem->{status} eq 'ARRAY' ) { | 
| 1688 | 0 |  |  |  |  |  | $frame->remStatus($_) for @{ $rem->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1689 |  |  |  |  |  |  | } | 
| 1690 |  |  |  |  |  |  | } | 
| 1691 |  |  |  |  |  |  |  | 
| 1692 |  |  |  |  |  |  | # Change | 
| 1693 | 0 | 0 | 0 |  |  |  | if ( exists $info->{chg} && ref $info->{chg} eq 'HASH' ) { | 
| 1694 |  |  |  |  |  |  |  | 
| 1695 | 0 |  |  |  |  |  | my $chg	= $info->{chg}; | 
| 1696 |  |  |  |  |  |  |  | 
| 1697 |  |  |  |  |  |  | # Change postal info | 
| 1698 | 0 | 0 |  |  |  |  | if ( ref $chg->{postalInfo} eq 'HASH' ) { | 
| 1699 | 0 |  |  |  |  |  | foreach my $type ( keys %{ $chg->{postalInfo} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1700 |  |  |  |  |  |  | $frame->chgPostalInfo( | 
| 1701 |  |  |  |  |  |  | $type, | 
| 1702 |  |  |  |  |  |  | $chg->{postalInfo}->{$type}->{name}, | 
| 1703 |  |  |  |  |  |  | $chg->{postalInfo}->{$type}->{org}, | 
| 1704 |  |  |  |  |  |  | $chg->{postalInfo}->{$type}->{addr} | 
| 1705 | 0 |  |  |  |  |  | ); | 
| 1706 |  |  |  |  |  |  | } | 
| 1707 |  |  |  |  |  |  | } | 
| 1708 |  |  |  |  |  |  |  | 
| 1709 |  |  |  |  |  |  | # Change voice / fax / email | 
| 1710 | 0 |  |  |  |  |  | for my $contact_type ( qw/ voice fax email / ) { | 
| 1711 | 0 | 0 |  |  |  |  | if ( defined $chg->{$contact_type} ) { | 
| 1712 | 0 |  |  |  |  |  | my $el = $frame->createElement("contact:$contact_type"); | 
| 1713 | 0 |  |  |  |  |  | $el->appendText( $chg->{$contact_type} ); | 
| 1714 | 0 |  |  |  |  |  | $frame->chg->appendChild($el); | 
| 1715 |  |  |  |  |  |  | } | 
| 1716 |  |  |  |  |  |  | } | 
| 1717 |  |  |  |  |  |  |  | 
| 1718 |  |  |  |  |  |  | # Change auth info | 
| 1719 | 0 | 0 |  |  |  |  | if ( $chg->{authInfo} ) { | 
| 1720 | 0 |  |  |  |  |  | $frame->chgAuthInfo( $chg->{authInfo} ); | 
| 1721 |  |  |  |  |  |  | } | 
| 1722 |  |  |  |  |  |  |  | 
| 1723 |  |  |  |  |  |  | # 'disclose' option is still unimplemented | 
| 1724 |  |  |  |  |  |  | } | 
| 1725 |  |  |  |  |  |  |  | 
| 1726 | 0 |  |  |  |  |  | return $frame; | 
| 1727 |  |  |  |  |  |  | } | 
| 1728 |  |  |  |  |  |  |  | 
| 1729 |  |  |  |  |  |  | sub _generate_update_host_frame { | 
| 1730 | 0 |  |  | 0 |  |  | my ($self, $info) = @_; | 
| 1731 |  |  |  |  |  |  |  | 
| 1732 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Update::Host->new; | 
| 1733 | 0 |  |  |  |  |  | $frame->setHost($info->{name}); | 
| 1734 |  |  |  |  |  |  |  | 
| 1735 | 0 | 0 | 0 |  |  |  | if ( exists $info->{add} && ref $info->{add} eq 'HASH' ) { | 
| 1736 | 0 |  |  |  |  |  | my $add = $info->{add}; | 
| 1737 |  |  |  |  |  |  | # Process addresses | 
| 1738 | 0 | 0 | 0 |  |  |  | if ( exists $add->{addrs} && ref $add->{addrs} eq 'ARRAY' ) { | 
| 1739 | 0 |  |  |  |  |  | $frame->addAddr( @{ $add->{addrs} } ); | 
|  | 0 |  |  |  |  |  |  | 
| 1740 |  |  |  |  |  |  | } | 
| 1741 |  |  |  |  |  |  | # Process statuses | 
| 1742 | 0 | 0 | 0 |  |  |  | if ( exists $add->{status} && ref $add->{status} ) { | 
| 1743 | 0 | 0 |  |  |  |  | if ( ref $add->{status} eq 'HASH' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1744 | 0 |  |  |  |  |  | while ( my ($type, $info) = each %{ $add->{status} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1745 | 0 |  |  |  |  |  | $frame->addStatus($type, $info); | 
| 1746 |  |  |  |  |  |  | } | 
| 1747 |  |  |  |  |  |  | } | 
| 1748 |  |  |  |  |  |  | elsif ( ref $add->{status} eq 'ARRAY' ) { | 
| 1749 | 0 |  |  |  |  |  | $frame->addStatus($_) for @{ $add->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1750 |  |  |  |  |  |  | } | 
| 1751 |  |  |  |  |  |  | } | 
| 1752 |  |  |  |  |  |  | } | 
| 1753 |  |  |  |  |  |  |  | 
| 1754 | 0 | 0 | 0 |  |  |  | if ( exists $info->{rem} && ref $info->{rem} eq 'HASH' ) { | 
| 1755 | 0 |  |  |  |  |  | my $rem = $info->{rem}; | 
| 1756 |  |  |  |  |  |  | # Process addresses | 
| 1757 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{addrs} && ref $rem->{addrs} eq 'ARRAY' ) { | 
| 1758 | 0 |  |  |  |  |  | $frame->remAddr( @{ $rem->{addrs} } ); | 
|  | 0 |  |  |  |  |  |  | 
| 1759 |  |  |  |  |  |  | } | 
| 1760 |  |  |  |  |  |  | # Process statuses | 
| 1761 | 0 | 0 | 0 |  |  |  | if ( exists $rem->{status} && ref $rem->{status} ) { | 
| 1762 | 0 | 0 |  |  |  |  | if ( ref $rem->{status} eq 'HASH' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1763 | 0 |  |  |  |  |  | while ( my ($type, $info) = each %{ $rem->{status} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 1764 | 0 |  |  |  |  |  | $frame->remStatus($type, $info); | 
| 1765 |  |  |  |  |  |  | } | 
| 1766 |  |  |  |  |  |  | } | 
| 1767 |  |  |  |  |  |  | elsif ( ref $rem->{status} eq 'ARRAY' ) { | 
| 1768 | 0 |  |  |  |  |  | $frame->remStatus($_) for @{ $rem->{status} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1769 |  |  |  |  |  |  | } | 
| 1770 |  |  |  |  |  |  | } | 
| 1771 |  |  |  |  |  |  | } | 
| 1772 |  |  |  |  |  |  |  | 
| 1773 | 0 | 0 | 0 |  |  |  | if ( exists $info->{chg} && ref $info->{chg} eq 'HASH' ) { | 
| 1774 | 0 | 0 |  |  |  |  | if ( $info->{chg}->{name} ) { | 
| 1775 | 0 |  |  |  |  |  | $frame->chgName( $info->{chg}->{name} ); | 
| 1776 |  |  |  |  |  |  | } | 
| 1777 |  |  |  |  |  |  | } | 
| 1778 |  |  |  |  |  |  |  | 
| 1779 | 0 |  |  |  |  |  | return $frame; | 
| 1780 |  |  |  |  |  |  | } | 
| 1781 |  |  |  |  |  |  |  | 
| 1782 |  |  |  |  |  |  |  | 
| 1783 |  |  |  |  |  |  | =pod | 
| 1784 |  |  |  |  |  |  |  | 
| 1785 |  |  |  |  |  |  | =head1 Deleting Objects | 
| 1786 |  |  |  |  |  |  |  | 
| 1787 |  |  |  |  |  |  | The following methods can be used to delete an object at the server: | 
| 1788 |  |  |  |  |  |  |  | 
| 1789 |  |  |  |  |  |  | $epp->delete_domain($domain); | 
| 1790 |  |  |  |  |  |  | $epp->delete_host($host); | 
| 1791 |  |  |  |  |  |  | $epp->delete_contact($contact); | 
| 1792 |  |  |  |  |  |  |  | 
| 1793 |  |  |  |  |  |  | Each of these methods has the same profile. They will return one of the following: | 
| 1794 |  |  |  |  |  |  |  | 
| 1795 |  |  |  |  |  |  | =over | 
| 1796 |  |  |  |  |  |  |  | 
| 1797 |  |  |  |  |  |  | =item * undef in the case of an error (check C<$Net::EPP::Simple::Error> and C<$Net::EPP::Simple::Code>). | 
| 1798 |  |  |  |  |  |  |  | 
| 1799 |  |  |  |  |  |  | =item * 1 if the deletion request was accepted. | 
| 1800 |  |  |  |  |  |  |  | 
| 1801 |  |  |  |  |  |  | =back | 
| 1802 |  |  |  |  |  |  |  | 
| 1803 |  |  |  |  |  |  | You may wish to check the value of $Net::EPP::Simple::Code to determine whether the response code was 1000 (OK) or 1001 (action pending). | 
| 1804 |  |  |  |  |  |  |  | 
| 1805 |  |  |  |  |  |  | =cut | 
| 1806 |  |  |  |  |  |  |  | 
| 1807 |  |  |  |  |  |  | sub delete_domain { | 
| 1808 | 0 |  |  | 0 | 0 |  | my ($self, $domain) = @_; | 
| 1809 | 0 |  |  |  |  |  | return $self->_delete('domain', $domain); | 
| 1810 |  |  |  |  |  |  | } | 
| 1811 |  |  |  |  |  |  |  | 
| 1812 |  |  |  |  |  |  | sub delete_host { | 
| 1813 | 0 |  |  | 0 | 0 |  | my ($self, $host) = @_; | 
| 1814 | 0 |  |  |  |  |  | return $self->_delete('host', $host); | 
| 1815 |  |  |  |  |  |  | } | 
| 1816 |  |  |  |  |  |  |  | 
| 1817 |  |  |  |  |  |  | sub delete_contact { | 
| 1818 | 0 |  |  | 0 | 0 |  | my ($self, $contact) = @_; | 
| 1819 | 0 |  |  |  |  |  | return $self->_delete('contact', $contact); | 
| 1820 |  |  |  |  |  |  | } | 
| 1821 |  |  |  |  |  |  |  | 
| 1822 |  |  |  |  |  |  | sub _delete { | 
| 1823 | 0 |  |  | 0 |  |  | my ($self, $type, $identifier) = @_; | 
| 1824 | 0 |  |  |  |  |  | my $frame; | 
| 1825 | 0 | 0 |  |  |  |  | if ($type eq 'domain') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1826 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Delete::Domain->new; | 
| 1827 | 0 |  |  |  |  |  | $frame->setDomain($identifier); | 
| 1828 |  |  |  |  |  |  |  | 
| 1829 |  |  |  |  |  |  | } elsif ($type eq 'contact') { | 
| 1830 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Delete::Contact->new; | 
| 1831 | 0 |  |  |  |  |  | $frame->setContact($identifier); | 
| 1832 |  |  |  |  |  |  |  | 
| 1833 |  |  |  |  |  |  | } elsif ($type eq 'host') { | 
| 1834 | 0 |  |  |  |  |  | $frame = Net::EPP::Frame::Command::Delete::Host->new; | 
| 1835 | 0 |  |  |  |  |  | $frame->setHost($identifier); | 
| 1836 |  |  |  |  |  |  |  | 
| 1837 |  |  |  |  |  |  | } else { | 
| 1838 | 0 |  |  |  |  |  | $Error = "Unknown object type '$type'"; | 
| 1839 | 0 |  |  |  |  |  | return undef; | 
| 1840 |  |  |  |  |  |  |  | 
| 1841 |  |  |  |  |  |  | } | 
| 1842 |  |  |  |  |  |  |  | 
| 1843 | 0 |  |  |  |  |  | my $response = $self->_request($frame); | 
| 1844 |  |  |  |  |  |  |  | 
| 1845 |  |  |  |  |  |  |  | 
| 1846 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 1847 | 0 |  |  |  |  |  | return undef; | 
| 1848 |  |  |  |  |  |  |  | 
| 1849 |  |  |  |  |  |  | } else { | 
| 1850 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 1851 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 1852 |  |  |  |  |  |  |  | 
| 1853 | 0 | 0 |  |  |  |  | if ($Code > 1999) { | 
| 1854 | 0 |  |  |  |  |  | $Error = $self->_get_error_message($response); | 
| 1855 | 0 |  |  |  |  |  | return undef; | 
| 1856 |  |  |  |  |  |  |  | 
| 1857 |  |  |  |  |  |  | } else { | 
| 1858 | 0 |  |  |  |  |  | return 1; | 
| 1859 |  |  |  |  |  |  |  | 
| 1860 |  |  |  |  |  |  | } | 
| 1861 |  |  |  |  |  |  | } | 
| 1862 |  |  |  |  |  |  | } | 
| 1863 |  |  |  |  |  |  |  | 
| 1864 |  |  |  |  |  |  | =head1 Domain Renewal | 
| 1865 |  |  |  |  |  |  |  | 
| 1866 |  |  |  |  |  |  | You can extend the validity period of the domain object by issuing a | 
| 1867 |  |  |  |  |  |  | renew_domain() command. | 
| 1868 |  |  |  |  |  |  |  | 
| 1869 |  |  |  |  |  |  | my $result = $epp->renew_domain({ | 
| 1870 |  |  |  |  |  |  | name         => 'example.com', | 
| 1871 |  |  |  |  |  |  | cur_exp_date => '2011-02-05',  # current expiration date | 
| 1872 |  |  |  |  |  |  | period       => 2,             # prolongation period in years | 
| 1873 |  |  |  |  |  |  | }); | 
| 1874 |  |  |  |  |  |  |  | 
| 1875 |  |  |  |  |  |  | Return value is C<1> on success and C on error. | 
| 1876 |  |  |  |  |  |  | In the case of error C<$Net::EPP::Simple::Error> contains the appropriate | 
| 1877 |  |  |  |  |  |  | error message. | 
| 1878 |  |  |  |  |  |  |  | 
| 1879 |  |  |  |  |  |  | =cut | 
| 1880 |  |  |  |  |  |  |  | 
| 1881 |  |  |  |  |  |  | sub renew_domain { | 
| 1882 | 0 |  |  | 0 | 0 |  | my ($self, $info) = @_; | 
| 1883 |  |  |  |  |  |  |  | 
| 1884 | 0 |  |  |  |  |  | return $self->_get_response_result( | 
| 1885 |  |  |  |  |  |  | $self->request( | 
| 1886 |  |  |  |  |  |  | $self->_generate_renew_domain_frame($info) | 
| 1887 |  |  |  |  |  |  | ) | 
| 1888 |  |  |  |  |  |  | ); | 
| 1889 |  |  |  |  |  |  | } | 
| 1890 |  |  |  |  |  |  |  | 
| 1891 |  |  |  |  |  |  | sub _generate_renew_domain_frame { | 
| 1892 | 0 |  |  | 0 |  |  | my ($self, $info) = @_; | 
| 1893 |  |  |  |  |  |  |  | 
| 1894 | 0 |  |  |  |  |  | my $frame = Net::EPP::Frame::Command::Renew::Domain->new; | 
| 1895 | 0 |  |  |  |  |  | $frame->setDomain( $info->{name} ); | 
| 1896 | 0 |  |  |  |  |  | $frame->setCurExpDate( $info->{cur_exp_date} ); | 
| 1897 | 0 | 0 |  |  |  |  | $frame->setPeriod( $info->{period} ) if $info->{period}; | 
| 1898 |  |  |  |  |  |  |  | 
| 1899 | 0 |  |  |  |  |  | return $frame; | 
| 1900 |  |  |  |  |  |  | } | 
| 1901 |  |  |  |  |  |  |  | 
| 1902 |  |  |  |  |  |  | =pod | 
| 1903 |  |  |  |  |  |  |  | 
| 1904 |  |  |  |  |  |  | =head1 Miscellaneous Methods | 
| 1905 |  |  |  |  |  |  |  | 
| 1906 |  |  |  |  |  |  | =cut | 
| 1907 |  |  |  |  |  |  |  | 
| 1908 | 0 |  |  | 0 | 1 |  | sub error { $Error } | 
| 1909 |  |  |  |  |  |  |  | 
| 1910 | 0 |  |  | 0 | 0 |  | sub code { $Code } | 
| 1911 |  |  |  |  |  |  |  | 
| 1912 | 0 |  |  | 0 | 0 |  | sub message { $Message } | 
| 1913 |  |  |  |  |  |  |  | 
| 1914 |  |  |  |  |  |  | =pod | 
| 1915 |  |  |  |  |  |  |  | 
| 1916 |  |  |  |  |  |  | my $greeting = $epp->greeting; | 
| 1917 |  |  |  |  |  |  |  | 
| 1918 |  |  |  |  |  |  | Returns the a L object representing the greeting returned by the server. | 
| 1919 |  |  |  |  |  |  |  | 
| 1920 |  |  |  |  |  |  | =cut | 
| 1921 |  |  |  |  |  |  |  | 
| 1922 | 0 |  |  | 0 | 0 |  | sub greeting { $_[0]->{greeting} } | 
| 1923 |  |  |  |  |  |  |  | 
| 1924 |  |  |  |  |  |  | =pod | 
| 1925 |  |  |  |  |  |  |  | 
| 1926 |  |  |  |  |  |  | $epp->ping; | 
| 1927 |  |  |  |  |  |  |  | 
| 1928 |  |  |  |  |  |  | Checks that the connection is up by sending a ChelloE> to the server. Returns false if no | 
| 1929 |  |  |  |  |  |  | response is received. | 
| 1930 |  |  |  |  |  |  |  | 
| 1931 |  |  |  |  |  |  | =cut | 
| 1932 |  |  |  |  |  |  |  | 
| 1933 |  |  |  |  |  |  | sub ping { | 
| 1934 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 1935 | 0 |  |  |  |  |  | my $hello = Net::EPP::Frame::Hello->new; | 
| 1936 | 0 |  |  |  |  |  | my $response = $self->request($hello); | 
| 1937 |  |  |  |  |  |  |  | 
| 1938 | 0 | 0 |  |  |  |  | if (UNIVERSAL::isa($response, 'XML::LibXML::Document')) { | 
| 1939 | 0 |  |  |  |  |  | $Code    = 1000; | 
| 1940 | 0 |  |  |  |  |  | $Message = 'Command completed successfully.'; | 
| 1941 | 0 |  |  |  |  |  | return 1; | 
| 1942 |  |  |  |  |  |  |  | 
| 1943 |  |  |  |  |  |  | } else { | 
| 1944 | 0 |  |  |  |  |  | $Code    = 2400; | 
| 1945 | 0 |  |  |  |  |  | $Message = 'Error getting greeting from server.'; | 
| 1946 | 0 |  |  |  |  |  | return undef; | 
| 1947 |  |  |  |  |  |  | } | 
| 1948 |  |  |  |  |  |  | } | 
| 1949 |  |  |  |  |  |  |  | 
| 1950 |  |  |  |  |  |  | sub _request { | 
| 1951 | 0 |  |  | 0 |  |  | my ($self, $frame) = @_; | 
| 1952 |  |  |  |  |  |  |  | 
| 1953 | 0 | 0 |  |  |  |  | if ($self->{reconnect} > 0) { | 
| 1954 | 0 |  |  |  |  |  | $self->debug("reconnect is $self->{reconnect}, pinging"); | 
| 1955 | 0 | 0 |  |  |  |  | if (!$self->ping) { | 
| 1956 | 0 |  |  |  |  |  | $self->debug('connection seems dead, trying to reconnect'); | 
| 1957 | 0 |  |  |  |  |  | for (1..$self->{reconnect}) { | 
| 1958 | 0 |  |  |  |  |  | $self->debug("attempt #$_"); | 
| 1959 | 0 | 0 |  |  |  |  | if ($self->_connect) { | 
| 1960 | 0 |  |  |  |  |  | $self->debug("attempt #$_ succeeded"); | 
| 1961 | 0 |  |  |  |  |  | return $self->request($frame); | 
| 1962 |  |  |  |  |  |  |  | 
| 1963 |  |  |  |  |  |  | } else { | 
| 1964 | 0 |  |  |  |  |  | $self->debug("attempt #$_ failed, sleeping"); | 
| 1965 | 0 |  |  |  |  |  | sleep($self->{timeout}); | 
| 1966 |  |  |  |  |  |  |  | 
| 1967 |  |  |  |  |  |  | } | 
| 1968 |  |  |  |  |  |  | } | 
| 1969 | 0 |  |  |  |  |  | $self->debug('unable to reconnect!'); | 
| 1970 | 0 |  |  |  |  |  | return undef; | 
| 1971 |  |  |  |  |  |  |  | 
| 1972 |  |  |  |  |  |  | } else { | 
| 1973 | 0 |  |  |  |  |  | $self->debug("Connection is up, sending frame"); | 
| 1974 | 0 |  |  |  |  |  | return $self->request($frame); | 
| 1975 |  |  |  |  |  |  |  | 
| 1976 |  |  |  |  |  |  | } | 
| 1977 |  |  |  |  |  |  |  | 
| 1978 |  |  |  |  |  |  | } else { | 
| 1979 | 0 |  |  |  |  |  | return $self->request($frame); | 
| 1980 |  |  |  |  |  |  |  | 
| 1981 |  |  |  |  |  |  | } | 
| 1982 |  |  |  |  |  |  | } | 
| 1983 |  |  |  |  |  |  |  | 
| 1984 |  |  |  |  |  |  | =pod | 
| 1985 |  |  |  |  |  |  |  | 
| 1986 |  |  |  |  |  |  | =head1 Overridden Methods From L | 
| 1987 |  |  |  |  |  |  |  | 
| 1988 |  |  |  |  |  |  | C overrides some methods inherited from | 
| 1989 |  |  |  |  |  |  | L. These are described below: | 
| 1990 |  |  |  |  |  |  |  | 
| 1991 |  |  |  |  |  |  | =head2 The C Method | 
| 1992 |  |  |  |  |  |  |  | 
| 1993 |  |  |  |  |  |  | C overrides this method so it can automatically populate | 
| 1994 |  |  |  |  |  |  | the CclTRIDE> element with a unique string. It then passes the | 
| 1995 |  |  |  |  |  |  | frame back up to L. | 
| 1996 |  |  |  |  |  |  |  | 
| 1997 |  |  |  |  |  |  | =cut | 
| 1998 |  |  |  |  |  |  |  | 
| 1999 |  |  |  |  |  |  | sub request { | 
| 2000 | 0 |  |  | 0 | 1 |  | my ($self, $frame) = @_; | 
| 2001 |  |  |  |  |  |  | # Make sure we start with blank variables | 
| 2002 | 0 |  |  |  |  |  | $Code		= undef; | 
| 2003 | 0 |  |  |  |  |  | $Error		= ''; | 
| 2004 | 0 |  |  |  |  |  | $Message	= ''; | 
| 2005 |  |  |  |  |  |  |  | 
| 2006 | 0 | 0 |  |  |  |  | if (!$self->connected) { | 
|  |  | 0 |  |  |  |  |  | 
| 2007 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 2008 | 0 |  |  |  |  |  | $Error = $Message = 'Not connected'; | 
| 2009 | 0 |  |  |  |  |  | $self->debug('cannot send frame if not connected'); | 
| 2010 | 0 |  |  |  |  |  | return undef; | 
| 2011 |  |  |  |  |  |  |  | 
| 2012 |  |  |  |  |  |  | } elsif (!$frame) { | 
| 2013 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 2014 | 0 |  |  |  |  |  | $Error = $Message = 'Invalid frame'; | 
| 2015 | 0 |  |  |  |  |  | $self->debug($Message); | 
| 2016 | 0 |  |  |  |  |  | return undef; | 
| 2017 |  |  |  |  |  |  |  | 
| 2018 |  |  |  |  |  |  | } else { | 
| 2019 | 0 | 0 |  |  |  |  | $frame->clTRID->appendText(sha1_hex(ref($self).time().$$)) if (UNIVERSAL::isa($frame, 'Net::EPP::Frame::Command')); | 
| 2020 |  |  |  |  |  |  |  | 
| 2021 | 0 |  |  |  |  |  | my $type = ref($frame); | 
| 2022 | 0 | 0 |  |  |  |  | if ($frame =~ /^\//) { | 
| 2023 | 0 |  |  |  |  |  | $type = 'file'; | 
| 2024 |  |  |  |  |  |  |  | 
| 2025 |  |  |  |  |  |  | } else { | 
| 2026 | 0 |  |  |  |  |  | $type = 'string'; | 
| 2027 |  |  |  |  |  |  |  | 
| 2028 |  |  |  |  |  |  | } | 
| 2029 | 0 |  |  |  |  |  | $self->debug(sprintf('sending a %s to the server', $type)); | 
| 2030 | 0 | 0 |  |  |  |  | if (UNIVERSAL::isa($frame, 'XML::LibXML::Document')) { | 
| 2031 | 0 |  |  |  |  |  | map { $self->debug('C: '.$_) } split(/\n/, $frame->toString(2)); | 
|  | 0 |  |  |  |  |  |  | 
| 2032 |  |  |  |  |  |  |  | 
| 2033 |  |  |  |  |  |  | } else { | 
| 2034 | 0 |  |  |  |  |  | map { $self->debug('C: '.$_) } split(/\n/, $frame); | 
|  | 0 |  |  |  |  |  |  | 
| 2035 |  |  |  |  |  |  |  | 
| 2036 |  |  |  |  |  |  | } | 
| 2037 |  |  |  |  |  |  |  | 
| 2038 | 0 |  |  |  |  |  | my $response = $self->SUPER::request($frame); | 
| 2039 |  |  |  |  |  |  |  | 
| 2040 | 0 | 0 |  |  |  |  | map { $self->debug('S: '.$_) } split(/\n/, $response->toString(2)) if (UNIVERSAL::isa($response, 'XML::LibXML::Document')); | 
|  | 0 |  |  |  |  |  |  | 
| 2041 |  |  |  |  |  |  |  | 
| 2042 | 0 |  |  |  |  |  | return $response; | 
| 2043 |  |  |  |  |  |  | } | 
| 2044 |  |  |  |  |  |  | } | 
| 2045 |  |  |  |  |  |  |  | 
| 2046 |  |  |  |  |  |  | =pod | 
| 2047 |  |  |  |  |  |  |  | 
| 2048 |  |  |  |  |  |  | =head2 The C Method | 
| 2049 |  |  |  |  |  |  |  | 
| 2050 |  |  |  |  |  |  | C overrides this method so it can catch timeouts and | 
| 2051 |  |  |  |  |  |  | network errors. If such an error occurs it will return C. | 
| 2052 |  |  |  |  |  |  |  | 
| 2053 |  |  |  |  |  |  | =cut | 
| 2054 |  |  |  |  |  |  |  | 
| 2055 |  |  |  |  |  |  | sub get_frame { | 
| 2056 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 2057 | 0 | 0 |  |  |  |  | if (!$self->connected) { | 
| 2058 | 0 |  |  |  |  |  | $self->debug('cannot send frame if not connected'); | 
| 2059 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 2060 | 0 |  |  |  |  |  | $Error = $Message = 'Not connected'; | 
| 2061 | 0 |  |  |  |  |  | return undef; | 
| 2062 |  |  |  |  |  |  |  | 
| 2063 |  |  |  |  |  |  | } else { | 
| 2064 | 0 |  |  |  |  |  | my $frame; | 
| 2065 | 0 |  |  |  |  |  | $self->debug(sprintf('reading frame, waiting %d seconds before timeout', $self->{timeout})); | 
| 2066 | 0 |  |  |  |  |  | eval { | 
| 2067 | 0 |  |  | 0 |  |  | local $SIG{ALRM} = sub { die 'timeout' }; | 
|  | 0 |  |  |  |  |  |  | 
| 2068 | 0 |  |  |  |  |  | $self->debug('setting timeout alarm for receiving frame'); | 
| 2069 | 0 |  |  |  |  |  | alarm($self->{timeout}); | 
| 2070 | 0 |  |  |  |  |  | $frame = $self->SUPER::get_frame(); | 
| 2071 | 0 |  |  |  |  |  | $self->debug('unsetting timeout alarm after successful receive'); | 
| 2072 | 0 |  |  |  |  |  | alarm(0); | 
| 2073 |  |  |  |  |  |  | }; | 
| 2074 | 0 | 0 |  |  |  |  | if ($@ ne '') { | 
| 2075 | 0 |  |  |  |  |  | chomp($@); | 
| 2076 | 0 |  |  |  |  |  | $@ =~ s/ at .+ line .+$//; | 
| 2077 | 0 |  |  |  |  |  | $self->debug("unsetting timeout alarm after alarm was triggered ($@)"); | 
| 2078 | 0 |  |  |  |  |  | alarm(0); | 
| 2079 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 2080 | 0 | 0 |  |  |  |  | if ($@ =~ /^timeout/) { | 
| 2081 | 0 |  |  |  |  |  | $Error = $Message = "get_frame() timed out after $self->{timeout} seconds"; | 
| 2082 |  |  |  |  |  |  |  | 
| 2083 |  |  |  |  |  |  | } else { | 
| 2084 | 0 |  |  |  |  |  | $Error = $Message = "get_frame() received an error: $@"; | 
| 2085 |  |  |  |  |  |  |  | 
| 2086 |  |  |  |  |  |  | } | 
| 2087 | 0 |  |  |  |  |  | return undef; | 
| 2088 |  |  |  |  |  |  |  | 
| 2089 |  |  |  |  |  |  | } else { | 
| 2090 | 0 |  |  |  |  |  | return bless($frame, 'Net::EPP::Frame::Response'); | 
| 2091 |  |  |  |  |  |  |  | 
| 2092 |  |  |  |  |  |  | } | 
| 2093 |  |  |  |  |  |  | } | 
| 2094 |  |  |  |  |  |  | } | 
| 2095 |  |  |  |  |  |  |  | 
| 2096 |  |  |  |  |  |  | sub send_frame { | 
| 2097 | 0 |  |  | 0 | 0 |  | my ($self, $frame, $wfcheck) = @_; | 
| 2098 | 0 | 0 |  |  |  |  | if (!$self->connected) { | 
| 2099 | 0 |  |  |  |  |  | $self->debug('cannot get frame if not connected'); | 
| 2100 | 0 |  |  |  |  |  | $Code = 2400; | 
| 2101 | 0 |  |  |  |  |  | $Message = 'Not connected'; | 
| 2102 | 0 |  |  |  |  |  | return undef; | 
| 2103 |  |  |  |  |  |  |  | 
| 2104 |  |  |  |  |  |  | } else { | 
| 2105 | 0 |  |  |  |  |  | return $self->SUPER::send_frame($frame, $wfcheck); | 
| 2106 |  |  |  |  |  |  |  | 
| 2107 |  |  |  |  |  |  | } | 
| 2108 |  |  |  |  |  |  | } | 
| 2109 |  |  |  |  |  |  |  | 
| 2110 |  |  |  |  |  |  | # Get details error description including code, message and reason | 
| 2111 |  |  |  |  |  |  | sub _get_error_message { | 
| 2112 | 0 |  |  | 0 |  |  | my ($self, $doc) = @_; | 
| 2113 |  |  |  |  |  |  |  | 
| 2114 | 0 |  |  |  |  |  | my $code    = $self->_get_response_code($doc); | 
| 2115 | 0 |  |  |  |  |  | my $error   = "Error $code"; | 
| 2116 |  |  |  |  |  |  |  | 
| 2117 | 0 |  |  |  |  |  | my $message = $self->_get_message($doc); | 
| 2118 | 0 | 0 |  |  |  |  | if ( $message ) { | 
| 2119 | 0 |  |  |  |  |  | $error .= ": $message"; | 
| 2120 |  |  |  |  |  |  | } | 
| 2121 |  |  |  |  |  |  |  | 
| 2122 | 0 |  |  |  |  |  | my $reason  = $self->_get_reason($doc); | 
| 2123 | 0 | 0 |  |  |  |  | if ( $reason ) { | 
| 2124 | 0 |  |  |  |  |  | $error .= " ($reason)"; | 
| 2125 |  |  |  |  |  |  | } | 
| 2126 |  |  |  |  |  |  |  | 
| 2127 | 0 |  |  |  |  |  | return $error; | 
| 2128 |  |  |  |  |  |  | } | 
| 2129 |  |  |  |  |  |  |  | 
| 2130 |  |  |  |  |  |  | sub _get_response_code { | 
| 2131 | 0 |  |  | 0 |  |  | my ($self, $doc) = @_; | 
| 2132 | 0 | 0 | 0 |  |  |  | if ($doc->isa('XML::DOM::Document') || $doc->isa('Net::EPP::Frame::Response')) { | 
| 2133 | 0 |  |  |  |  |  | my $els = $doc->getElementsByTagNameNS(EPP_XMLNS, 'result'); | 
| 2134 | 0 | 0 |  |  |  |  | if (defined($els)) { | 
| 2135 | 0 |  |  |  |  |  | my $el = $els->shift; | 
| 2136 | 0 | 0 |  |  |  |  | return $el->getAttribute('code') if (defined($el)); | 
| 2137 |  |  |  |  |  |  | } | 
| 2138 |  |  |  |  |  |  | } | 
| 2139 | 0 |  |  |  |  |  | return 2400; | 
| 2140 |  |  |  |  |  |  | } | 
| 2141 |  |  |  |  |  |  |  | 
| 2142 |  |  |  |  |  |  | sub _get_message { | 
| 2143 | 0 |  |  | 0 |  |  | my ($self, $doc) = @_; | 
| 2144 | 0 | 0 | 0 |  |  |  | if ($doc->isa('XML::DOM::Document') || $doc->isa('Net::EPP::Frame::Response')) { | 
| 2145 | 0 |  |  |  |  |  | my $msgs = $doc->getElementsByTagNameNS(EPP_XMLNS, 'msg'); | 
| 2146 | 0 | 0 |  |  |  |  | if (defined($msgs)) { | 
| 2147 | 0 |  |  |  |  |  | my $msg = $msgs->shift; | 
| 2148 | 0 | 0 |  |  |  |  | return $msg->textContent if (defined($msg)); | 
| 2149 |  |  |  |  |  |  | } | 
| 2150 |  |  |  |  |  |  | } | 
| 2151 | 0 |  |  |  |  |  | return ''; | 
| 2152 |  |  |  |  |  |  | } | 
| 2153 |  |  |  |  |  |  |  | 
| 2154 |  |  |  |  |  |  | sub _get_reason { | 
| 2155 | 0 |  |  | 0 |  |  | my ($self, $doc) = @_; | 
| 2156 | 0 | 0 | 0 |  |  |  | if ($doc->isa('XML::DOM::Document') || $doc->isa('Net::EPP::Frame::Response')) { | 
| 2157 | 0 |  |  |  |  |  | my $reasons = $doc->getElementsByTagNameNS(EPP_XMLNS, 'reason'); | 
| 2158 | 0 | 0 |  |  |  |  | if (defined($reasons)) { | 
| 2159 | 0 |  |  |  |  |  | my $reason = $reasons->shift; | 
| 2160 | 0 | 0 |  |  |  |  | if (defined($reason)) { | 
| 2161 | 0 |  |  |  |  |  | return $reason->textContent; | 
| 2162 |  |  |  |  |  |  | } | 
| 2163 |  |  |  |  |  |  | } | 
| 2164 |  |  |  |  |  |  | } | 
| 2165 | 0 |  |  |  |  |  | return ''; | 
| 2166 |  |  |  |  |  |  | } | 
| 2167 |  |  |  |  |  |  |  | 
| 2168 |  |  |  |  |  |  | sub logout { | 
| 2169 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 2170 | 0 | 0 |  |  |  |  | if ($self->authenticated) { | 
| 2171 | 0 |  |  |  |  |  | $self->debug('logging out'); | 
| 2172 | 0 |  |  |  |  |  | my $response = $self->request(Net::EPP::Frame::Command::Logout->new); | 
| 2173 | 0 |  |  |  |  |  | undef($self->{'authenticated'}); | 
| 2174 | 0 | 0 |  |  |  |  | if (!$response) { | 
| 2175 | 0 |  |  |  |  |  | $Code = COMMAND_FAILED; | 
| 2176 | 0 |  |  |  |  |  | $Message = $Error = 'unknown error'; | 
| 2177 |  |  |  |  |  |  | return undef | 
| 2178 |  |  |  |  |  |  |  | 
| 2179 | 0 |  |  |  |  |  | } else { | 
| 2180 | 0 |  |  |  |  |  | $Code = $self->_get_response_code($response); | 
| 2181 | 0 |  |  |  |  |  | $Message = $self->_get_message($response); | 
| 2182 |  |  |  |  |  |  |  | 
| 2183 |  |  |  |  |  |  | } | 
| 2184 |  |  |  |  |  |  | } | 
| 2185 | 0 |  |  |  |  |  | $self->debug('disconnecting from server'); | 
| 2186 | 0 |  |  |  |  |  | $self->disconnect; | 
| 2187 | 0 |  |  |  |  |  | undef($self->{'connected'}); | 
| 2188 | 0 |  |  |  |  |  | return 1; | 
| 2189 |  |  |  |  |  |  | } | 
| 2190 |  |  |  |  |  |  |  | 
| 2191 |  |  |  |  |  |  | sub DESTROY { | 
| 2192 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 2193 | 0 |  |  |  |  |  | $self->debug('DESTROY() method called'); | 
| 2194 | 0 | 0 |  |  |  |  | $self->logout if ($self->connected); | 
| 2195 |  |  |  |  |  |  | } | 
| 2196 |  |  |  |  |  |  |  | 
| 2197 |  |  |  |  |  |  | sub debug { | 
| 2198 | 0 |  |  | 0 | 1 |  | my ($self, $msg) = @_; | 
| 2199 | 0 |  |  |  |  |  | my $log = sprintf("%s (%d): %s", scalar(localtime()), $$, $msg); | 
| 2200 | 0 |  |  |  |  |  | push(@Log, $log); | 
| 2201 | 0 | 0 | 0 |  |  |  | print STDERR $log."\n" if (defined($self->{debug}) && $self->{debug} == 1); | 
| 2202 |  |  |  |  |  |  | } | 
| 2203 |  |  |  |  |  |  |  | 
| 2204 |  |  |  |  |  |  | =pod | 
| 2205 |  |  |  |  |  |  |  | 
| 2206 |  |  |  |  |  |  | $connected = $epp->connected; | 
| 2207 |  |  |  |  |  |  |  | 
| 2208 |  |  |  |  |  |  | Returns a boolean if C has a connection to the server. Note that this | 
| 2209 |  |  |  |  |  |  | connection might have dropped, use C to test it. | 
| 2210 |  |  |  |  |  |  |  | 
| 2211 |  |  |  |  |  |  | =cut | 
| 2212 |  |  |  |  |  |  |  | 
| 2213 |  |  |  |  |  |  | sub connected { | 
| 2214 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 2215 | 0 |  |  |  |  |  | return defined($self->{'connected'}); | 
| 2216 |  |  |  |  |  |  | } | 
| 2217 |  |  |  |  |  |  |  | 
| 2218 |  |  |  |  |  |  | =pod | 
| 2219 |  |  |  |  |  |  |  | 
| 2220 |  |  |  |  |  |  | $authenticated = $epp->authenticated; | 
| 2221 |  |  |  |  |  |  |  | 
| 2222 |  |  |  |  |  |  | Returns a boolean if C has successfully authenticated with the server. | 
| 2223 |  |  |  |  |  |  |  | 
| 2224 |  |  |  |  |  |  | =cut | 
| 2225 |  |  |  |  |  |  |  | 
| 2226 |  |  |  |  |  |  | sub authenticated { | 
| 2227 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 2228 | 0 |  |  |  |  |  | return defined($self->{'authenticated'}); | 
| 2229 |  |  |  |  |  |  | } | 
| 2230 |  |  |  |  |  |  |  | 
| 2231 |  |  |  |  |  |  | =pod | 
| 2232 |  |  |  |  |  |  |  | 
| 2233 |  |  |  |  |  |  | =head1 Package Variables | 
| 2234 |  |  |  |  |  |  |  | 
| 2235 |  |  |  |  |  |  | =head2 $Net::EPP::Simple::Error | 
| 2236 |  |  |  |  |  |  |  | 
| 2237 |  |  |  |  |  |  | This variable contains an english text message explaining the last error | 
| 2238 |  |  |  |  |  |  | to occur. This is may be due to invalid parameters being passed to a | 
| 2239 |  |  |  |  |  |  | method, a network error, or an error response being returned by the | 
| 2240 |  |  |  |  |  |  | server. | 
| 2241 |  |  |  |  |  |  |  | 
| 2242 |  |  |  |  |  |  | =head2 $Net::EPP::Simple::Message | 
| 2243 |  |  |  |  |  |  |  | 
| 2244 |  |  |  |  |  |  | This variable contains the contains the text content of the | 
| 2245 |  |  |  |  |  |  | CmsgE> element in the response frame for the last transaction. | 
| 2246 |  |  |  |  |  |  |  | 
| 2247 |  |  |  |  |  |  | =head2 $Net::EPP::Simple::Code | 
| 2248 |  |  |  |  |  |  |  | 
| 2249 |  |  |  |  |  |  | This variable contains the integer result code returned by the server | 
| 2250 |  |  |  |  |  |  | for the last transaction. A successful transaction will always return an | 
| 2251 |  |  |  |  |  |  | error code of 1999 or lower, for an unsuccessful transaction it will be | 
| 2252 |  |  |  |  |  |  | 2011 or more. If there is an internal client error (due to invalid | 
| 2253 |  |  |  |  |  |  | parameters being passed to a method, or a network error) then this will | 
| 2254 |  |  |  |  |  |  | be set to 2400 (C). See L for | 
| 2255 |  |  |  |  |  |  | more information about thes codes. | 
| 2256 |  |  |  |  |  |  |  | 
| 2257 |  |  |  |  |  |  | =cut | 
| 2258 |  |  |  |  |  |  |  | 
| 2259 |  |  |  |  |  |  | 1; |