File Coverage

blib/lib/IO/EPP/RIPN.pm
Criterion Covered Total %
statement 24 406 5.9
branch 0 230 0.0
condition 0 65 0.0
subroutine 8 26 30.7
pod 14 18 77.7
total 46 745 6.1


line stmt bran cond sub pod time code
1             package IO::EPP::RIPN;
2              
3             =encoding utf8
4              
5             =head1 NAME
6              
7             IO::EPP::RIPN
8              
9             =head1 SYNOPSIS
10              
11             use IO::EPP::RIPN;
12              
13             # Parameters for LWP
14             my %sock_params = (
15             PeerHost => 'uap.tcinet.ru',
16             PeerPort => 8028, # 8027 for .SU, 8028 for .RU, 8029 for .РФ
17             SSL_key_file => 'key_file.pem',
18             SSL_cert_file => 'cert_file.pem',
19             LocalAddr => '1.2.3.4',
20             Timeout => 30,
21             );
22              
23             # Create object, get greeting and call login()
24             my $conn = IO::EPP::RIPN->new( {
25             user => 'XXX-RU',
26             pass => 'XXXXXXXX',
27             sock_params => \%sock_params,
28             test_mode => 0, # real connect
29             } );
30              
31             # Check domain
32             my ( $answ, $code, $msg ) = $conn->check_domains( { domains => [ 'org.info' ] } );
33              
34             # Call logout() and destroy object
35             undef $conn;
36              
37             =head1 DESCRIPTION
38              
39             RIPN is the first organization the registry in the .ru tld
40             Then it transferred functions of the registry into TCI (L)
41             but all special headings in epp remained
42              
43             Examlpe:
44              
45             C
46             instead of
47             C
48              
49             Module overwrites IO::EPP::Base where there are differences from RFC
50             and work with tcinet epp using http api
51              
52             For details see:
53             L,
54             L,
55             L,
56             L
57              
58             All documents -- L
59              
60             IO::EPP::RIPN only works with .RU, .SU & .РФ cctlds.
61              
62             For work with the new gtlds .ДЕТИ, .TATAR need use IO::EPP::TCI
63              
64             Features:
65              
66             Working over https
67              
68             Completely other contacts
69              
70             Non-standard domain transfer in the .su zone
71              
72             The domain:check function has an error: when checking the availability of a blocked domain, it responds that it is available.
73             The list of blocked domains should be downloaded from the Registrar panel.
74              
75             =cut
76              
77 2     2   2284 use LWP::UserAgent;
  2         38801  
  2         56  
78 2     2   12 use HTTP::Request;
  2         3  
  2         37  
79 2     2   484 use HTTP::Cookies;
  2         5868  
  2         46  
80 2     2   431 use Time::HiRes qw( time );
  2         1187  
  2         8  
81              
82 2     2   907 use IO::EPP::Base;
  2         4  
  2         71  
83 2     2   10 use parent qw( IO::EPP::Base );
  2         2  
  2         14  
84              
85 2     2   145 use strict;
  2         4  
  2         34  
86 2     2   7 use warnings;
  2         3  
  2         9326  
87              
88             # Old TCI uses special headings
89             our $epp_head = '
90             ';
91             our $epp_cont_urn =
92             'xmlns:contact="http://www.ripn.net/epp/ripn-contact-1.0" xsi:schemaLocation="http://www.ripn.net/epp/ripn-contact-1.0 ripn-contact-1.0.xsd"';
93             our $epp_host_urn =
94             'xmlns:host="http://www.ripn.net/epp/ripn-host-1.0" xsi:schemaLocation="http://www.ripn.net/epp/ripn-host-1.0 ripn-host-1.0.xsd"';
95             our $epp_dom_urn =
96             'xmlns:domain="http://www.ripn.net/epp/ripn-domain-1.0" xsi:schemaLocation="http://www.ripn.net/epp/ripn-domain-1.0 ripn-domain-1.0.xsd"';
97             our $epp_dom_urn_ru =
98             'xmlns:domain="http://www.ripn.net/epp/ripn-domain-1.1" xsi:schemaLocation="http://www.ripn.net/epp/ripn-domain-1.1 ripn-domain-1.1.xsd"';
99             our $epp_reg_urn =
100             'xmlns:registrar="http://www.ripn.net/epp/ripn-registrar-1.0" xsi:schemaLocation="http://www.ripn.net/epp/ripn-registrar-1.0 ripn-registrar-1.0.xsd"';
101              
102              
103             sub make_request {
104 0     0 1   my ( $action, $params ) = @_;
105              
106             #$params = IO::EPP::Base::recursive_utf8_unflaged( $params );
107              
108 0           my ( $code, $msg, $answ, $self );
109              
110 0 0         unless ( $params->{conn} ) {
111             # Default:
112 0   0       $params->{sock_params}{PeerHost} ||= 'uap.tcinet.ru';
113 0   0       $params->{sock_params}{PeerPort} ||= 8028; # .RU
114              
115 0           ( $self, $code, $msg ) = IO::EPP::RIPN->new( $params );
116              
117 0 0 0       unless ( $code and $code == 1000 ) {
118 0           goto END_MR;
119             }
120             }
121             else {
122 0           $self = $params->{conn};
123             }
124              
125 0           $self->{critical_error} = '';
126              
127 0 0         if ( $self->can( $action ) ) {
128 0           ( $answ, $code, $msg ) = $self->$action( $params );
129             }
130             else {
131 0           $msg = "undefined command <$action>, request cancelled";
132 0           $code = 0;
133             }
134              
135             END_MR:
136              
137 0 0         $msg .= ', ' . $self->{critical_error} if $self->{critical_error};
138              
139 0           my $full_answ = "code: $code\nmsg: $msg";
140              
141 0 0 0       $answ = {} unless $answ && ref $answ;
142              
143 0           $answ->{code} = $code;
144 0           $answ->{msg} = $msg;
145              
146 0 0         return wantarray ? ( $answ, $full_answ, $self ) : $answ;
147             }
148              
149              
150             sub gen_pw {
151 0     0 1   my @chars = ( 'A'..'Z', 'a'..'z', '0'..'9', '!', '@', '$', '%', '*', '_', '.', ':', '-', '=', '+', '?', '#', ',' );
152              
153 0           return join '', map( { $chars[ int rand( scalar @chars ) ] } 1..16 );
  0            
154             }
155              
156              
157             =head1 METHODS
158              
159             =head2 new
160              
161             If the C parameter is received, it loads cookies from the file specified by C
162              
163             =cut
164              
165             sub new {
166 0     0 1   my ( $package, $params ) = @_;
167              
168 0           my ( $self, $code, $msg );
169              
170 0           my $sock_params = delete $params->{sock_params};
171              
172 0           my $host = $sock_params->{PeerHost};
173 0           my $port = $sock_params->{PeerPort};
174 0           my $url = "https://$host:$port";
175 0           my $local_address = $sock_params->{LocalAddr};
176 0   0       my $timeout = $sock_params->{Timeout} || 5;
177              
178 0           my %ua_params = ( ssl_opts => $sock_params );
179 0 0         $ua_params{local_address} = $local_address if $local_address;
180              
181 0 0         if ( $timeout ) {
182             # LWP feature: first param for LWP, second - for IO::Socket
183 0           $ua_params{timeout} = $timeout;
184 0           $ua_params{Timeout} = $timeout;
185             }
186              
187 0           my $cookie;
188 0 0         if ( $params->{alien_conn} ) {
189 0           $cookie = HTTP::Cookies->new( autosave => 0 );
190              
191 0 0         unless ( $cookie->load( $params->{load_cook_from} ) ) {
192 0           $msg = "load cooker is fail";
193 0           $code = 0;
194              
195 0           goto ERR;
196             }
197             }
198             else {
199 0           $cookie = HTTP::Cookies->new;
200             }
201              
202 0           my $ua = LWP::UserAgent->new(
203             agent => 'EppBot/7.02 (Perl; Linux i686; ru, en_US)',
204             parse_head => 0,
205             keep_alive => 30,
206             cookie_jar => $cookie,
207             %ua_params,
208             );
209              
210 0 0         unless ( $ua ) {
211 0           $msg = "can not connect";
212 0           $code = 0;
213              
214 0           goto ERR;
215             }
216              
217             $self = bless {
218             sock => $ua,
219             user => $params->{user},
220             url => $url,
221             cookies => $cookie,
222             no_log => delete $params->{no_log},
223 0 0         alien => $params->{alien_conn} ? 1 : 0,
224             };
225              
226 0           $self->set_urn();
227              
228 0           $self->set_log_vars( $params );
229              
230 0           $self->epp_log( "Connect to $url\n" );
231              
232 0 0         if ( $self->{alien} ) {
233 0 0         return wantarray ? ( $self, 1000, 'ok' ) : $self;
234             }
235              
236             # Get HEADER only
237 0           $self->epp_log( "HEAD connect to $url from $local_address" );
238              
239 0           my $request = HTTP::Request->new( HEAD => $url ); # не POST
240 0           my $response = $ua->request( $request );
241              
242 0           my $rcode = $response->code;
243 0           $self->epp_log( "header answ code: $rcode" );
244              
245 0 0         unless ( $rcode == 200 ) {
246 0           $code = 0;
247 0           $msg = "Can't open socket";
248              
249 0           goto ERR;
250             }
251              
252 0           my $headers = $response->headers;
253              
254 0           my $length = $headers->content_length;
255 0           $self->epp_log( "header content-length == $length" );
256              
257 0 0         if ( $length == 0 ) {
258 0           $code = 0;
259 0           $msg = "Can't open socket";
260              
261 0           goto ERR;
262             }
263              
264 0           my ( undef, $c0, $m0 ) = $self->hello();
265              
266 0 0 0       unless ( $c0 && $c0 == 1000 ) {
267 0           $code = 0;
268 0           $msg = "Can't get greeting";
269 0 0         $msg .= '; ' . $self->{critical_error} if $self->{critical_error};
270              
271 0           goto ERR;
272             }
273              
274              
275 0           my ( undef, $c1, $m1 ) = $self->login( delete $params->{pass} ); # no password in object
276              
277 0 0 0       if ( $c1 && $c1 == 1000 ) {
278 0 0         return wantarray ? ( $self, $c1, $m1 ) : $self;
279             }
280              
281 0   0       $msg = ( $m1 || '' ) . $self->{critical_error};
282 0   0       $code = $c1 || 0;
283              
284 0 0         ERR:
285             return wantarray ? ( 0, $code, $msg ) : 0;
286             }
287              
288              
289             sub set_urn {
290             $_[0]->{urn} = {
291 0     0 0   head => $IO::EPP::RIPN::epp_head,
292             cont => $IO::EPP::RIPN::epp_cont_urn,
293             host => $IO::EPP::RIPN::epp_host_urn,
294             dom => $IO::EPP::RIPN::epp_dom_urn,
295             reg => $IO::EPP::RIPN::epp_reg_urn,
296             };
297             }
298              
299              
300             sub req {
301 0     0 1   my ( $self, $out_data, $info ) = @_;
302              
303 0 0 0       return 0 unless $out_data && $self->{sock};
304              
305 0   0       $info ||= '';
306              
307 0 0         if ( $out_data ) {
308 0           my $d = $out_data;
309             # remove password, authinfo from log
310 0           $d =~ s/[^<>]+<\/pw>/xxxxx<\/pw>/;
311              
312 0           $self->epp_log( "$info request:\n$d" );
313             }
314              
315 0           my $request = HTTP::Request->new( POST => $self->{url} );
316 0           $request->content_type('text/xml');
317 0           $request->content_type_charset('UTF-8');
318 0           $request->content( $out_data );
319              
320 0           my $start_time = time;
321              
322 0           my $response = $self->{sock}->request( $request );
323              
324 0           my $req_time = sprintf( '%0.4f', time - $start_time );
325              
326             # print Dumper $response;
327              
328 0           my $rcode = $response->code;
329              
330 0 0         unless ( $rcode == 200 ) {
331 0           $self->{critical_error} = "Get answer code = $rcode";
332              
333 0           return 0;
334             }
335              
336             # feature of connection on epp over https
337 0 0         if ( $info eq 'login' ) {
338 0           $self->{cook} = $self->{sock}->cookie_jar->as_string;
339 0           $self->epp_log( "cookies: $$self{cook}" );
340              
341 0   0       $self->{sessionid} = $response->header('set-cookie') || '';
342 0           $self->epp_log( "sessionid: $$self{sessionid}" );
343             }
344              
345 0           my $in_data = $response->content;
346              
347 0           $self->epp_log( "req_time: $req_time\n$info answer:\n$in_data\n" );
348              
349 0           return $in_data;
350             }
351              
352              
353             =head2 login
354              
355             Ext params for login,
356              
357             INPUT: new password for change
358              
359             =cut
360              
361             sub login {
362 0     0 1   my ( $self, $pw, undef, undef, $new_pw ) = @_;
363              
364 0 0         return 0 unless $pw;
365              
366 0 0         my $npw = $new_pw ? "\n $new_pw" : '';
367              
368 0           my ( $svcs, $ext ) = ( '', '' );
369              
370 0 0         if ( $self->{user} =~ /-(RU|RF)$/ ) {
371 0           $svcs = "\n http://www.ripn.net/epp/ripn-domain-1.1";
372             # Does not work $ext = "\n http://www.tcinet.ru/epp/tci-billing-1.0";
373             }
374              
375 0           my $cltrid = $self->get_cltrid();
376              
377 0           my $body = <
378             $$self{urn}{head}
379            
380            
381             $$self{user}
382             $pw$npw
383            
384             1.0
385             en
386            
387            
388             http://www.ripn.net/epp/ripn-contact-1.0
389             http://www.ripn.net/epp/ripn-domain-1.0$svcs
390             http://www.ripn.net/epp/ripn-epp-1.0
391             http://www.ripn.net/epp/ripn-eppcom-1.0
392             http://www.ripn.net/epp/ripn-host-1.0
393             http://www.ripn.net/epp/ripn-registrar-1.0
394            
395             urn:ietf:params:xml:ns:secDNS-1.1$ext
396            
397            
398            
399             $cltrid
400            
401            
402             LOGIN
403              
404 0           return $self->simple_request( $body, 'login' );
405             }
406              
407              
408             =head2 save_cookies
409              
410             Save http connection cookies,
411             they can be used to create another connection on this IP address without opening a new session, that is, without a login
412              
413             =cut
414              
415             sub save_cookies {
416 0     0 1   my ( $self, $params ) = @_;
417              
418 0 0 0       unless ( ref $params and $params->{save_cook_to} ) {
419 0 0         return wantarray ? ( 0, 0, 'no params' ) : 0;
420             }
421              
422 0           my $cook = $self->{sock}->cookie_jar->as_string;
423              
424 0 0         open( COOKFILE, '>', $params->{save_cook_to} ) or return ( 0, 0, "Can't open $$params{save_cook_to} file: $!" );
425 0           print COOKFILE "#LWP-Cookies-1.0\n";
426 0           print COOKFILE "$cook\n";
427 0           close COOKFILE;
428              
429 0           my %info = ( cook => $cook );
430 0           $self->{cook} = $cook;
431              
432 0 0         return wantarray ? ( \%info, 1000, 'ok' ) : \%info;
433             }
434              
435              
436             sub hello {
437 0     0 1   my ( $self ) = @_;
438              
439 0           my $body = <
440             $$self{urn}{head}
441            
442            
443             HELLO
444              
445 0           my $content = $self->req( $body, 'hello' );
446              
447 0 0 0       return 0 unless $content && $content =~ /greeting/;
448              
449 0           my $info = { code => 1000, msg => $content };
450              
451 0 0         return wantarray ? ( $info, 1000, $content ) : $info;
452             }
453              
454             sub cont_to_xml {
455 0     0 0   my ( undef, $cont ) = @_;
456              
457 0 0         my $is_person = $cont->{passport} ? 1 : 0;
458              
459 0 0         my $txtcont .= $is_person ? "\n" : "\n";
460              
461 0           foreach my $type ( 'int', 'loc' ) {
462 0           $txtcont .= " \n";
463              
464 0 0         if ( $is_person ) {
465 0           $txtcont .= " ".$$cont{$type}{name}."\n";
466             }
467             else {
468 0           $txtcont .= " ".$$cont{$type}{org}."\n";
469             }
470              
471 0 0         $$cont{$type}{addr} = [ $$cont{$type}{addr} ] unless ref $$cont{$type}{addr};
472              
473 0           $txtcont .= " $_\n" foreach @{$$cont{$type}{addr}};
  0            
474              
475 0           $txtcont .= " \n";
476             }
477              
478 0 0         unless ( $is_person ) {
479 0           $txtcont .= " \n";
480              
481 0 0         $$cont{legal}{addr} = [ $$cont{legal}{addr} ] unless ref $$cont{legal}{addr};
482              
483 0           $txtcont .= " $_\n" foreach @{$$cont{legal}{addr}};
  0            
484              
485 0           $txtcont .= " \n";
486             }
487              
488 0 0         if ( $$cont{taxpayerNumbers} ) {
489 0           $txtcont .= " $$cont{taxpayerNumbers}\n";
490             }
491             else {
492 0           $txtcont .= " \n";
493             }
494              
495 0 0         if ( $is_person ) {
496 0           $txtcont .= " $$cont{birthday}\n";
497              
498 0 0         $$cont{passport} = [ $$cont{passport} ] unless ref $$cont{passport};
499              
500 0           $txtcont .= " $_\n" foreach @{$$cont{passport}};
  0            
501             }
502              
503 0 0         $$cont{voice} = [ $$cont{voice} ] unless ref $$cont{voice};
504              
505 0           $txtcont .= " $_\n" foreach @{$$cont{voice}};
  0            
506              
507 0 0         if ( $$cont{fax} ) {
508 0 0         $$cont{fax} = [ $$cont{fax} ] unless ref $$cont{fax};
509              
510 0           $txtcont .= " $_\n" foreach @{$$cont{fax}};
  0            
511             }
512             else {
513 0           $txtcont .= " \n";
514             }
515              
516 0 0         $$cont{email} = [ $$cont{email} ] unless ref $$cont{email};
517              
518 0           $txtcont .= " $_\n" foreach @{$$cont{email}};
  0            
519              
520 0 0         if ( $is_person ) {
521 0           $txtcont .= " \n";
522             }
523             else {
524 0           $txtcont .= " \n";
525             }
526              
527 0 0         if ( $$cont{verified} ) {
528 0           $txtcont .= " ";
529             }
530             else {
531 0           $txtcont .= " ";
532             }
533              
534 0           return $txtcont;
535             }
536              
537              
538             sub cont_from_xml {
539 0     0 0   my ( undef, $txtcont ) = @_;
540              
541 0           my %cont;
542              
543 0 0         my $is_person = ($txtcont =~ /contact:person/) ? 1 : 0;
544              
545 0           my @ss = $txtcont =~ //g;
546 0           $cont{statuses}{$_} = '+' for @ss;
547              
548 0           my %types = ( intPostalInfo => 'int', locPostalInfo => 'loc', legalInfo => 'legal' );
549 0           foreach my $type ( keys %types ) {
550 0 0         if ( $txtcont =~ /(.+)<\/contact:$type>/s ) {
551 0           my $pi = $1;
552              
553 0 0         if ( $pi =~ /([^<>]+)<\/contact:name>/ ) {
554 0           $cont{$types{$type}}{name} = $1;
555             }
556 0 0         if ( $pi =~ /([^<>]+)<\/contact:org>/ ) {
557 0           $cont{$types{$type}}{org} = $1;
558             }
559              
560 0           $cont{$types{$type}}{addr} = [ $pi =~ /([^<>]+)<\/contact:address>/g ];
561             }
562             }
563              
564 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:taxpayerNumbers>/ ) {
565 0           $cont{taxpayerNumbers} = $1;
566             }
567              
568 0 0         if ( $is_person ) {
569 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:birthday>/ ) {
570 0           $cont{birthday} = $1;
571             }
572              
573 0           $cont{passport} = [ $txtcont =~ /([^<>]+)<\/contact:passport>/g ];
574             }
575              
576 0           $cont{voice} = [ $txtcont =~ /([^<>]+)<\/contact:voice>/g ];
577              
578 0           $cont{fax} = [ $txtcont =~ /([^<>]+)<\/contact:fax>/g ];
579              
580 0           $cont{email} = [ $txtcont =~ /([^<>]+)<\/contact:email>/g ];
581              
582 0 0         if ( $txtcont =~ // ) {
    0          
583 0           $cont{verified} = 1;
584             }
585             elsif ( $txtcont =~ // ) {
586 0           $cont{verified} = 0;
587             }
588              
589 0           my %id = %IO::EPP::Base::id;
590 0           foreach my $k ( keys %id ) {
591 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:$k>/ ) {
592 0           $cont{$id{$k}} = $1;
593             }
594             }
595              
596 0           my %dt = %IO::EPP::Base::dt;
597 0           foreach my $k ( keys %dt ) {
598 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:$k>/ ) {
599 0           $cont{$dt{$k}} = $1;
600              
601 0           $cont{$dt{$k}} =~ s/T/ /;
602 0           $cont{$dt{$k}} =~ s/\.\d+Z$//;
603             }
604             }
605              
606 0           return \%cont;
607             }
608              
609              
610             sub create_domain_authinfo {
611 0     0 0   my ( $self, $params ) = @_;
612              
613 0 0         return '' unless $params->{authinfo};
614              
615 0           $params->{authinfo} =~ s/&/&/g;
616              
617 0           return "\n \n $$params{authinfo}\n ";
618             }
619              
620             =head2 transfer
621              
622             Addition parameter for .SU, .NET.RU, .ORG.RU, .PP.RU:
623             C - registrar name which will receive the domain (here all on the contrary)
624              
625             =cut
626              
627             sub transfer {
628 0     0 1   my ( $self, $params ) = @_;
629              
630 0 0         if ( $params->{to} ) {
631 0           $params->{addition} = "\n $$params{sent_to}";
632             }
633              
634 0 0         if ( $params->{user} =~ /-(RU|RF)$/ ) {
635 0           $self->{urn}{dom} = $epp_dom_urn_ru;
636             }
637              
638 0           my @res = $self->SUPER::transfer( $params );
639              
640 0           $self->{urn}{dom} = $IO::EPP::RIPN::epp_dom_urn;
641              
642 0           return @res;
643             }
644              
645              
646             =head2 get_registrar_info
647              
648             Get Registrar data: white IP, email, whois data
649              
650             =cut
651              
652             sub get_registrar_info {
653 0     0 1   my ( $self ) = @_;
654              
655 0           my $cltrid = $self->get_cltrid();
656              
657 0           my $body = <
658             $$self{urn}{head}
659            
660            
661            
662             $$self{user}
663            
664            
665             $cltrid
666            
667            
668             REGINFO
669              
670 0           my $answ = $self->req( $body, 'registrar_info' );
671              
672 0 0 0       if ( $answ && $answ =~ // ) {
673 0           my $rcode = $1 + 0;
674              
675 0           my $msg = '';
676 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
677 0           $msg = $1;
678             }
679              
680 0 0         if ( $rcode != 1000 ) {
681 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
682 0           $msg .= '; ' . $1;
683             }
684              
685 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
686             }
687              
688 0           my $info = {};
689              
690 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
691 0   0       my $rdata = $1 // '';
692              
693 0           my %types = ( intPostalInfo => 'int', locPostalInfo => 'loc', legalInfo => 'legal' );
694 0           foreach my $type ( keys %types ) {
695 0 0         if ( $rdata =~ /(.+)<\/registrar:$type>/s ) {
696 0           my $pi = $1;
697 0 0         if ( $pi =~ /([^<>]+)<\/registrar:org>/ ) {
698 0           $info->{$types{$type}}{org} = $1;
699             }
700              
701 0           $info->{$types{$type}}{address} = join(', ', $pi =~ /([^<>]+)<\/registrar:address>/g );
702             }
703             }
704              
705 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:taxpayerNumbers>/ ) {
706 0           $info->{taxpayerNumbers} = $1;
707             }
708              
709 0           $info->{voice} = [ $rdata =~ /([^<>]+)<\/registrar:voice>/g ];
710              
711 0           $info->{fax} = [ $rdata =~ /([^<>]+)<\/registrar:fax>/g ];
712              
713 0           my @emails = $rdata =~ /([^<>]+<\/registrar:email>)/g;
714              
715 0           foreach my $e ( @emails ) {
716 0 0         if ( $e =~ /registrar:email type="([^"]+)">([^<>]+)<\/registrar:email/ ) {
717 0           $info->{emails}{$1} = $2;
718             }
719             }
720              
721 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:www>/ ) {
722 0           $info->{www} = $1;
723             }
724              
725 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:whois>/ ) {
726 0           $info->{whois} = $1;
727             }
728              
729 0           $info->{addrs} = [ $rdata =~ /([0-9A-Fa-f.:]+)<\/registrar:addr>/g ];
730              
731 0           my %dt = %IO::EPP::Base::dt;
732 0           foreach my $k ( keys %dt ) {
733 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:$k>/ ) {
734 0           $info->{$dt{$k}} = $1;
735              
736 0           $info->{$dt{$k}} =~ s/T/ /;
737 0           $info->{$dt{$k}} =~ s/\.\d+Z$//;
738             }
739             }
740             }
741              
742 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
743             }
744              
745 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
746             }
747              
748              
749             =head2 update_registrar
750              
751             Changing Registrar data: white IP, email, whois data
752              
753             INPUT:
754              
755             key of params:
756              
757             C or C:
758              
759             C -- arrayref of ipv4 or ipv6 address,
760              
761             C - hashref where keys - email type, values - email
762              
763             C:
764              
765             C - new web url
766             C - new whois url
767              
768             =cut
769              
770             sub update_registrar {
771 0     0 1   my ( $self, $params ) = @_;
772              
773 0 0         return ( 0, 0, 'no params' ) unless ref $params;
774              
775 0           my $cltrid = $self->get_cltrid();
776              
777 0           my $add = '';
778 0 0         if ( $params->{add} ) {
779 0 0 0       if ( defined $params->{add}{ips} and ref $params->{add}{ips} ) {
780 0           foreach my $ip ( @{$params->{add}{ips}} ) {
  0            
781 0 0         if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
782 0           $add .= ' ' . $ip . "\n";
783             }
784             else {
785 0           $add .= ' ' . $ip . "\n";
786             }
787             }
788             }
789              
790 0 0 0       if ( defined $params->{add}{emails} and ref $params->{add}{emails} ) {
791 0           foreach my $type ( @{$params->{add}{emails}} ) {
  0            
792 0           $add .= qq| | . $$params{add}{emails}{$type} . "\n";
793             }
794             }
795             }
796              
797 0 0         if ( $add ) {
798 0           $add = "\n$add ";
799             }
800             else {
801 0           $add = ''
802             }
803              
804 0           my $rem = '';
805 0 0         if ( $params->{rem} ) {
806 0 0 0       if ( defined $params->{rem}{ips} && ref $params->{rem}{ips} ) {
807 0           foreach my $ip ( @{$params->{rem}{ips}} ) {
  0            
808 0 0         if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
809 0           $rem .= ' ' . $ip . "\n";
810             }
811             else {
812 0           $rem .= ' ' . $ip . "\n";
813             }
814             }
815             }
816              
817 0 0 0       if ( defined $params->{rem}{emails} and ref $params->{rem}{emails} ) {
818 0           foreach my $type ( @{$params->{rem}{emails}} ) {
  0            
819 0           $rem .= qq| | . $$params{rem}{emails}{$type} . "\n";
820             }
821             }
822             }
823              
824 0 0         if ( $rem ) {
825 0           $rem = "\n$rem ";
826             }
827             else {
828 0           $rem = ''
829             }
830              
831 0           my $chg = '';
832 0 0         if ( $params->{chg} ) {
833 0 0         if ( $params->{chg}{www} ) {
834 0           $chg .= ' ' . $$params{chg}{www} . "\n";
835             }
836              
837 0 0         if ( $params->{chg}{whois} ) {
838 0           $chg .= ' ' . $$params{chg}{www} . "\n";
839             }
840             }
841              
842 0 0         if ( $chg ) {
843 0           $chg = "\n$chg ";
844             }
845             else {
846 0           $chg = "";
847             }
848              
849              
850 0           my $body = <
851             $$self{urn}{head}
852            
853            
854            
855             $$self{user}
856             $add
857             $rem
858             $chg
859            
860            
861             $cltrid
862            
863            
864             UPDREG
865              
866 0           return $self->simple_request( $body, 'update_registrar' );
867             }
868              
869              
870             =head2 get_billing_info
871              
872             INPUT:
873              
874             keys of params:
875              
876             C,
877             C: in days,
878             C: RUB.
879              
880             =cut
881              
882             sub get_billing_info {
883 0     0 1   my ( $self, $params ) = @_;
884              
885 0 0         return ( 0, 0, 'no params' ) unless ref $params;
886              
887 0           my $cltrid = $self->get_cltrid();
888              
889 0           my $body = <
890             $$self{urn}{head}
891            
892            
893            
894             balance
895            
896             $$params{date}
897             $$params{period}
898             $$params{currency}
899            
900            
901            
902             $cltrid
903            
904            
905             BILINFO
906              
907 0           my $answ = $self->req( $body, 'billing_info' );
908              
909 0 0 0       if ( $answ && $answ =~ // ) {
910 0           my $rcode = $1 + 0;
911              
912 0           my $msg = '';
913 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
914 0           $msg = $1;
915             }
916              
917 0 0         if ( $rcode != 1000 ) {
918 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
919 0           $msg .= '; ' . $1;
920             }
921              
922 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
923             }
924              
925 0           my $info = {};
926              
927 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
928 0   0       my $rdata = $1 // '';
929              
930 0           my @billing = $rdata =~ /(]+>[^<>]+<\/billing:[^<>]+>)/g;
931              
932 0           foreach my $row ( @billing ) {
933 0 0         if ( $row =~ /]*>([^<>]+)<\/billing:[^<>]+>/ ) {
934 0           $info->{$1} = $2;
935             }
936             }
937              
938 0           $info->{calc_date} = delete $info->{calcDate};
939 0           $info->{calc_date} =~ s/T/ /;
940 0           $info->{calc_date} =~ s/\.\d+Z$//;
941             }
942              
943 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
944             }
945              
946 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
947             }
948              
949              
950             =head2 get_limits_info
951              
952             How many requests are left in this hour
953              
954             =cut
955              
956             sub get_limits_info {
957 0     0 1   my ( $self ) = @_;
958              
959 0           my $cltrid = $self->get_cltrid();
960              
961 0           my $body = <
962             $$self{urn}{head}
963            
964            
965            
966            
967             $cltrid
968            
969            
970             LIMINFO
971              
972 0           my $answ = $self->req( $body, 'limits_info' );
973              
974 0 0 0       if ( $answ && $answ =~ // ) {
975 0           my $rcode = $1 + 0;
976              
977 0           my $msg = '';
978 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
979 0           $msg = $1;
980             }
981              
982 0 0         if ( $rcode != 1000 ) {
983 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
984 0           $msg .= '; ' . $1;
985             }
986              
987 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
988             }
989              
990 0           my $info = {};
991              
992 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
993 0   0       my $rdata = $1 // '';
994              
995 0           my @limits = $rdata =~ /(]+>[^<>]+<\/limits:[^<>]+>)/g;
996              
997 0           foreach my $row ( @limits ) {
998 0 0         if ( $row =~ /]+)>([^<>]+)<\/limits:[^<>]+>/ ) {
999 0           $info->{$1} = $2;
1000             }
1001             }
1002             }
1003              
1004 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
1005             }
1006              
1007 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
1008             }
1009              
1010              
1011             =head2 get_stat_info
1012              
1013             Show domain statistics by metric
1014              
1015             key of params:
1016             C -- varians: C, C, C, C, C, C
1017              
1018             Now not work:
1019              
1020             code="2400", msg="Command failed", reason="Internal server error"
1021              
1022             =cut
1023              
1024             sub get_stat_info {
1025 0     0 1   my ( $self, $params ) = @_;
1026              
1027 0 0         return ( 0, 0, 'no params' ) unless ref $params;
1028              
1029 0           my $cltrid = $self->get_cltrid();
1030              
1031 0           my $body = <
1032             $$self{urn}{head}
1033            
1034            
1035            
1036            
1037            
1038            
1039             $cltrid
1040            
1041            
1042             STATINFO
1043              
1044 0           return $self->simple_request( $body, 'info' );
1045             }
1046              
1047              
1048             =head2 logout
1049              
1050             Close session, disconnect
1051              
1052             =cut
1053              
1054             sub logout {
1055 0     0 1   my ( $self ) = @_;
1056              
1057 0 0 0       return 0 unless $self && $self->{sock};
1058              
1059 0 0         return 0 if $self->{alien};
1060              
1061 0           my $cltrid = $self->get_cltrid();
1062              
1063 0           my $body = <
1064             $$self{urn}{head}
1065            
1066            
1067             $cltrid
1068            
1069            
1070             LOGOUT
1071              
1072             # The answer doesn't matter
1073 0           $self->req( $body, 'logout' );
1074              
1075 0           delete $$self{sock};
1076 0           delete $$self{cook};
1077 0           delete $$self{cookies};
1078 0           delete $$self{sessionid};
1079 0           delete $$self{user};
1080 0           delete $$self{url};
1081             }
1082              
1083             1;
1084              
1085              
1086             __END__