File Coverage

blib/lib/IO/EPP/RIPN.pm
Criterion Covered Total %
statement 24 405 5.9
branch 0 228 0.0
condition 0 62 0.0
subroutine 8 26 30.7
pod 17 18 94.4
total 49 739 6.6


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 => [ 'my.ru', 'out.ru' ] } );
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 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 L.
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   2215 use LWP::UserAgent;
  2         44827  
  2         62  
78 2     2   14 use HTTP::Request;
  2         4  
  2         43  
79 2     2   512 use HTTP::Cookies;
  2         6976  
  2         57  
80 2     2   497 use Time::HiRes qw( time );
  2         1462  
  2         15  
81              
82 2     2   1143 use IO::EPP::Base;
  2         6  
  2         80  
83 2     2   13 use parent qw( IO::EPP::Base );
  2         4  
  2         17  
84              
85 2     2   126 use strict;
  2         3  
  2         54  
86 2     2   10 use warnings;
  2         4  
  2         10514  
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             =head2 hello
437              
438             For details, see L
439              
440             =cut
441              
442             sub hello {
443 0     0 1   my ( $self ) = @_;
444              
445 0           my $body = <
446             $$self{urn}{head}
447            
448            
449             HELLO
450              
451 0           my $content = $self->req( $body, 'hello' );
452              
453 0 0 0       return 0 unless $content && $content =~ /greeting/;
454              
455 0           my $info = { code => 1000, msg => $content };
456              
457 0 0         return wantarray ? ( $info, 1000, $content ) : $info;
458             }
459              
460             =head2 cont_to_xml
461              
462             Overrides the base class converter, since the contacts are very different here.
463              
464             =cut
465              
466             sub cont_to_xml {
467 0     0 1   my ( undef, $cont ) = @_;
468              
469 0 0         my $is_person = $cont->{passport} ? 1 : 0;
470              
471 0 0         my $txtcont .= $is_person ? "\n" : "\n";
472              
473 0           foreach my $type ( 'int', 'loc' ) {
474 0           $txtcont .= " \n";
475              
476 0 0         if ( $is_person ) {
477 0           $txtcont .= " ".$$cont{$type}{name}."\n";
478             }
479             else {
480 0           $txtcont .= " ".$$cont{$type}{org}."\n";
481             }
482              
483 0 0         $$cont{$type}{addr} = [ $$cont{$type}{addr} ] unless ref $$cont{$type}{addr};
484              
485 0           $txtcont .= " $_\n" foreach @{$$cont{$type}{addr}};
  0            
486              
487 0           $txtcont .= " \n";
488             }
489              
490 0 0         unless ( $is_person ) {
491 0           $txtcont .= " \n";
492              
493 0 0         $$cont{legal}{addr} = [ $$cont{legal}{addr} ] unless ref $$cont{legal}{addr};
494              
495 0           $txtcont .= " $_\n" foreach @{$$cont{legal}{addr}};
  0            
496              
497 0           $txtcont .= " \n";
498             }
499              
500 0 0         if ( $$cont{taxpayerNumbers} ) {
501 0           $txtcont .= " $$cont{TIN}\n";
502             }
503             else {
504 0           $txtcont .= " \n";
505             }
506              
507 0 0         if ( $is_person ) {
508 0           $txtcont .= " $$cont{birthday}\n";
509              
510 0 0         $$cont{passport} = [ $$cont{passport} ] unless ref $$cont{passport};
511              
512 0           $txtcont .= " $_\n" foreach @{$$cont{passport}};
  0            
513             }
514              
515 0 0         $$cont{phone} = [ $$cont{phone} ] unless ref $$cont{phone};
516              
517 0           $txtcont .= " $_\n" foreach @{$$cont{phone}};
  0            
518              
519 0 0         if ( $$cont{fax} ) {
520 0 0         $$cont{fax} = [ $$cont{fax} ] unless ref $$cont{fax};
521              
522 0           $txtcont .= " $_\n" foreach @{$$cont{fax}};
  0            
523             }
524             else {
525 0           $txtcont .= " \n";
526             }
527              
528 0 0         $$cont{email} = [ $$cont{email} ] unless ref $$cont{email};
529              
530 0           $txtcont .= " $_\n" foreach @{$$cont{email}};
  0            
531              
532 0 0         if ( $is_person ) {
533 0           $txtcont .= " \n";
534             }
535             else {
536 0           $txtcont .= " \n";
537             }
538              
539 0 0         if ( $$cont{verified} ) {
540 0           $txtcont .= " ";
541             }
542             else {
543 0           $txtcont .= " ";
544             }
545              
546 0           return $txtcont;
547             }
548              
549              
550             =head2 create_contact
551              
552             Parameter names are maximally unified with other providers.
553              
554             INPUT:
555              
556             for individual:
557              
558             C — full name, need for C and C types;
559              
560             C — date of birth;
561              
562             C — identification card number, place and date of issue;
563              
564             for legal entity:
565              
566             C — organization name
567              
568             C — string or array with full legal address of the organization, need for C type data
569              
570             common fields:
571              
572             C — string or array with full address;
573              
574             C - taxpayer numbers;
575              
576             C – string or array with phone numbers in international format,
577             you can specify a list of multiple phones,
578             the suffixes C<(sms)> and C<(transfer)> are used to mark phones for confirming transfers;
579              
580             C – string or array with faxes, usually only required for legal entities;
581              
582             C;
583              
584             C – the full name or name of the organization was confirmed by documents.
585              
586             Examples:
587              
588             Create person contact
589              
590             my %pers = (
591             cont_id => 'MY-123456',
592             'int' => {
593             name => 'Igor I Igover',
594             addr => 'UA, 12345, Igorevsk, Igoreva str, 13',
595             },
596             loc => {.
597             name => 'Игорь Игоревич Игорев',.
598             addr => [ 'UA', '85012', 'Игоревск', 'ул. Игорева, д.12, Игореву И.И.' ],
599             },
600             TIN => '',
601             birthday => '2001-01-01',
602             passport => [ 'II662244', 'выдан Игоревским МВД УДМС', '1.1.2017' ],
603             phone => '+380.501234567',
604             fax => '',
605             email => 'mail@igor.name',
606             );
607              
608             my ( $answ, $code, $msg ) = $conn->create_contact( \%pers );
609              
610             # answer
611              
612             {
613             'cont_id' => 'my-123456',
614             'cre_date' => '2020-01-11 10:10:10',
615             'cltrid' => '1710de82a0e9249277ffd713f51c8888',
616             'svtrid' => '4997598888'
617             };
618              
619             Create legal entity contact
620              
621             my %org = (
622             # cont_id - auto
623             'int' => {.
624             org => 'Igor Limited Liability Company',
625             addr => [ 'RU', '123456', 'Moscow', 'Igoreva str, 3', 'Igor LLC' ]
626             },
627             loc => {
628             org => 'ООО «Игорь»',
629             addr => [ 'RU, 123456, г. Москва, ул. Игорева, дом 3, ООО «Игорь»', 'охраннику' ],
630             },
631             legal => {.
632             addr => [ '125476, г.Москва, ул. Игорева, д.3' ],
633             },
634             TIN => '7777777777',
635             phone => [ '+7.4951111111', '+7.4951111111(transfer)' ],
636             fax => '+7.4951111111',
637             email => [ 'mail@igor.ru' ],
638             );
639              
640             my ( $answ, $code, $msg ) = $conn->create_contact( \%org );
641              
642             # answer
643              
644             {
645             'cont_id' => 'e88c1fngsz1e',
646             'cre_date' => '2020-01-01 10:10:10',
647             'cltrid' => '6194b816dd3f5d3f417fd2cfe0c88888',
648             'svtrid' => '4997633333'
649             };
650              
651             =cut
652              
653             sub create_contact {
654 0     0 1   my ( $self, $params ) = @_;
655              
656 0   0       $params->{cont_id} ||= IO::EPP::Base::gen_id( 16 );
657              
658 0           return $self->SUPER::create_contact( $params );
659             }
660              
661              
662             =head2 cont_from_xml
663              
664             Overrides the base class contact parser.
665              
666             As a result, the get_contact_info function displays the request response in the registry as follows:
667              
668             Individual
669              
670             my ( $a, $m, $o ) = make_request( 'get_contact_info', { cont_id => 'my-123456' } );
671              
672             # answer
673              
674             {
675             'msg' => 'Command completed successfully',
676             'owner' => 'XXX-RU',
677             'int' => {
678             'name' => 'Igor I Igover',
679             'addr' => [
680             'UA, 12345, Igorevsk, Igoreva str, 13'
681             ]
682             },
683             'cre_date' => '2020-01-10 10:10:10',
684             'phone' => [
685             '+380.501234567'
686             ],
687             'email' => [
688             'mail@igor.name'
689             ],
690             'loc' => {
691             'name' => 'Игорь Игоревич Игорев',
692             'addr' => [
693             'UA',
694             '85012',
695             'Игоревск',
696             'ул. Игорева, д.12, Игореву И.И.'
697             ]
698             },
699             'fax' => [],
700             'creater' => 'XXX-RU',
701             'verified' => 0,
702             'statuses' => {
703             'ok' => '+'
704             },
705             'birthday' => '2001-01-01',
706             'passport' => [
707             'II662244',
708             'выдан Игоревским МВД УДМС',
709             '1.1.2017'
710             ],
711             'code' => '1000'
712             };
713              
714             Legal entity
715              
716             my ( $a, $m, $o ) = make_request( 'get_contact_info', { cont_id => 'e88c1fngsz1e' } );
717              
718             # answer
719              
720             {
721             'msg' => 'Command completed successfully',
722             'owner' => 'XXX-RU',
723             'int' => {
724             'org' => 'Igor Limited Liability Company',
725             'addr' => [
726             'RU',
727             '123456',
728             'Moscow',
729             'Igoreva str, 3',
730             'Igor LLC'
731             ]
732             },
733             'cre_date' => '2020-01-10 10:10:10',
734             'phone' => [
735             '+7.4951111111',
736             '+7.4951111111(transfer)'
737             ],
738             'email' => [
739             'mail@igor.ru'
740             ],
741             'loc' => {
742             'org' => 'ООО «Игорь»',
743             'addr' => [
744             'RU, 123456, г. Москва, ул. Игорева, дом 3, ООО «Игорь»',
745             'охраннику'
746             ]
747             },
748             'fax' => [
749             '+7.4951111111'
750             ],
751             'legal' => {
752             'addr' => [
753             '125476, г.Москва, ул. Игорева, д.3'
754             ]
755             },
756             'creater' => 'XXX-RU',
757             'verified' => 0,
758             'statuses' => {
759             'ok' => '+'
760             },
761             'code' => '1000'
762             };
763              
764             =cut
765              
766             sub cont_from_xml {
767 0     0 1   my ( undef, $txtcont ) = @_;
768              
769 0           my %cont;
770              
771 0 0         my $is_person = ($txtcont =~ /contact:person/) ? 1 : 0;
772              
773 0           my @ss = $txtcont =~ //g;
774 0           $cont{statuses}{$_} = '+' for @ss;
775              
776 0           my %types = ( intPostalInfo => 'int', locPostalInfo => 'loc', legalInfo => 'legal' );
777 0           foreach my $type ( keys %types ) {
778 0 0         if ( $txtcont =~ /(.+)<\/contact:$type>/s ) {
779 0           my $pi = $1;
780              
781 0 0         if ( $pi =~ /([^<>]+)<\/contact:name>/ ) {
782 0           $cont{$types{$type}}{name} = $1;
783             }
784 0 0         if ( $pi =~ /([^<>]+)<\/contact:org>/ ) {
785 0           $cont{$types{$type}}{org} = $1;
786             }
787              
788 0           $cont{$types{$type}}{addr} = [ $pi =~ /([^<>]+)<\/contact:address>/g ];
789             }
790             }
791              
792 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:taxpayerNumbers>/ ) {
793 0           $cont{TIN} = $1;
794             }
795              
796 0 0         if ( $is_person ) {
797 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:birthday>/ ) {
798 0           $cont{birthday} = $1;
799             }
800              
801 0           $cont{passport} = [ $txtcont =~ /([^<>]+)<\/contact:passport>/g ];
802             }
803              
804 0           $cont{phone} = [ $txtcont =~ /([^<>]+)<\/contact:voice>/g ];
805              
806 0           $cont{fax} = [ $txtcont =~ /([^<>]+)<\/contact:fax>/g ];
807              
808 0           $cont{email} = [ $txtcont =~ /([^<>]+)<\/contact:email>/g ];
809              
810 0 0         if ( $txtcont =~ // ) {
    0          
811 0           $cont{verified} = 1;
812             }
813             elsif ( $txtcont =~ // ) {
814 0           $cont{verified} = 0;
815             }
816              
817 0           my %id = %IO::EPP::Base::id;
818 0           foreach my $k ( keys %id ) {
819 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:$k>/ ) {
820 0           $cont{$id{$k}} = $1;
821             }
822             }
823              
824 0           my %dt = %IO::EPP::Base::dt;
825 0           foreach my $k ( keys %dt ) {
826 0 0         if ( $txtcont =~ /([^<>]+)<\/contact:$k>/ ) {
827 0           $cont{$dt{$k}} = $1;
828              
829 0           $cont{$dt{$k}} =~ s/T/ /;
830 0           $cont{$dt{$k}} =~ s/\.\d+Z$//;
831             }
832             }
833              
834 0           return \%cont;
835             }
836              
837              
838             =head2 transfer
839              
840             Addition parameter for .SU, .NET.RU, .ORG.RU, .PP.RU:
841             C - registrar name which will receive the domain (here all on the contrary)
842              
843             =cut
844              
845             sub transfer {
846 0     0 1   my ( $self, $params ) = @_;
847              
848 0 0         if ( $params->{to} ) {
849 0           $params->{addition} = "\n $$params{sent_to}";
850             }
851              
852 0 0         if ( $params->{user} =~ /-(RU|RF)$/ ) {
853 0           $self->{urn}{dom} = $epp_dom_urn_ru;
854             }
855              
856 0           my @res = $self->SUPER::transfer( $params );
857              
858 0           $self->{urn}{dom} = $IO::EPP::RIPN::epp_dom_urn;
859              
860 0           return @res;
861             }
862              
863              
864             =head2 get_registrar_info
865              
866             Get Registrar data: white IP, email, whois data
867              
868             =cut
869              
870             sub get_registrar_info {
871 0     0 1   my ( $self ) = @_;
872              
873 0           my $cltrid = $self->get_cltrid();
874              
875 0           my $body = <
876             $$self{urn}{head}
877            
878            
879            
880             $$self{user}
881            
882            
883             $cltrid
884            
885            
886             REGINFO
887              
888 0           my $answ = $self->req( $body, 'registrar_info' );
889              
890 0 0 0       if ( $answ && $answ =~ // ) {
891 0           my $rcode = $1 + 0;
892              
893 0           my $msg = '';
894 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
895 0           $msg = $1;
896             }
897              
898 0 0         if ( $rcode != 1000 ) {
899 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
900 0           $msg .= '; ' . $1;
901             }
902              
903 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
904             }
905              
906 0           my $info = {};
907              
908 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
909 0           my $rdata = $1;
910              
911 0           my %types = ( intPostalInfo => 'int', locPostalInfo => 'loc', legalInfo => 'legal' );
912 0           foreach my $type ( keys %types ) {
913 0 0         if ( $rdata =~ /(.+)<\/registrar:$type>/s ) {
914 0           my $pi = $1;
915 0 0         if ( $pi =~ /([^<>]+)<\/registrar:org>/ ) {
916 0           $info->{$types{$type}}{org} = $1;
917             }
918              
919 0           $info->{$types{$type}}{addr} = join(', ', $pi =~ /([^<>]+)<\/registrar:address>/g );
920             }
921             }
922              
923 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:taxpayerNumbers>/ ) {
924 0           $info->{TIN} = $1;
925             }
926              
927 0           $info->{phone} = [ $rdata =~ /([^<>]+)<\/registrar:voice>/g ];
928              
929 0           $info->{fax} = [ $rdata =~ /([^<>]+)<\/registrar:fax>/g ];
930              
931 0           my @emails = $rdata =~ /([^<>]+<\/registrar:email>)/g;
932              
933 0           foreach my $e ( @emails ) {
934 0 0         if ( $e =~ /registrar:email type="([^"]+)">([^<>]+)<\/registrar:email/ ) {
935 0           $info->{emails}{$1} = $2;
936             }
937             }
938              
939 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:www>/ ) {
940 0           $info->{www} = $1;
941             }
942              
943 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:whois>/ ) {
944 0           $info->{whois} = $1;
945             }
946              
947 0           $info->{ips} = [ $rdata =~ /([0-9A-Fa-f.:]+)<\/registrar:addr>/g ];
948              
949 0           my %dt = %IO::EPP::Base::dt;
950 0           foreach my $k ( keys %dt ) {
951 0 0         if ( $rdata =~ /([^<>]+)<\/registrar:$k>/ ) {
952 0           $info->{$dt{$k}} = $1;
953              
954 0           $info->{$dt{$k}} =~ s/T/ /;
955 0           $info->{$dt{$k}} =~ s/\.\d+Z$//;
956             }
957             }
958             }
959              
960 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
961             }
962              
963 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
964             }
965              
966              
967             =head2 update_registrar
968              
969             Changing Registrar data: white IP, email, whois data
970              
971             INPUT:
972              
973             key of params:
974              
975             C or C:
976              
977             C -- arrayref of ipv4 or ipv6 address,
978              
979             C - hashref where keys - email type, values - email
980              
981             C:
982              
983             C - new web url
984              
985             C - new whois url
986              
987             =cut
988              
989             sub update_registrar {
990 0     0 1   my ( $self, $params ) = @_;
991              
992 0 0         return ( 0, 0, 'no params' ) unless ref $params;
993              
994 0           my $cltrid = $self->get_cltrid();
995              
996 0           my $add = '';
997 0 0         if ( $params->{add} ) {
998 0 0 0       if ( defined $params->{add}{ips} and ref $params->{add}{ips} ) {
999 0           foreach my $ip ( @{$params->{add}{ips}} ) {
  0            
1000 0 0         if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1001 0           $add .= ' ' . $ip . "\n";
1002             }
1003             else {
1004 0           $add .= ' ' . $ip . "\n";
1005             }
1006             }
1007             }
1008              
1009 0 0 0       if ( defined $params->{add}{emails} and ref $params->{add}{emails} ) {
1010 0           foreach my $type ( @{$params->{add}{emails}} ) {
  0            
1011 0           $add .= qq| | . $$params{add}{emails}{$type} . "\n";
1012             }
1013             }
1014             }
1015              
1016 0 0         if ( $add ) {
1017 0           $add = "\n$add ";
1018             }
1019             else {
1020 0           $add = ''
1021             }
1022              
1023 0           my $rem = '';
1024 0 0         if ( $params->{rem} ) {
1025 0 0 0       if ( defined $params->{rem}{ips} && ref $params->{rem}{ips} ) {
1026 0           foreach my $ip ( @{$params->{rem}{ips}} ) {
  0            
1027 0 0         if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1028 0           $rem .= ' ' . $ip . "\n";
1029             }
1030             else {
1031 0           $rem .= ' ' . $ip . "\n";
1032             }
1033             }
1034             }
1035              
1036 0 0 0       if ( defined $params->{rem}{emails} and ref $params->{rem}{emails} ) {
1037 0           foreach my $type ( @{$params->{rem}{emails}} ) {
  0            
1038 0           $rem .= qq| | . $$params{rem}{emails}{$type} . "\n";
1039             }
1040             }
1041             }
1042              
1043 0 0         if ( $rem ) {
1044 0           $rem = "\n$rem ";
1045             }
1046             else {
1047 0           $rem = ''
1048             }
1049              
1050 0           my $chg = '';
1051 0 0         if ( $params->{chg} ) {
1052 0 0         if ( $params->{chg}{www} ) {
1053 0           $chg .= ' ' . $$params{chg}{www} . "\n";
1054             }
1055              
1056 0 0         if ( $params->{chg}{whois} ) {
1057 0           $chg .= ' ' . $$params{chg}{www} . "\n";
1058             }
1059             }
1060              
1061 0 0         if ( $chg ) {
1062 0           $chg = "\n$chg ";
1063             }
1064             else {
1065 0           $chg = "";
1066             }
1067              
1068              
1069 0           my $body = <
1070             $$self{urn}{head}
1071            
1072            
1073            
1074             $$self{user}
1075             $add
1076             $rem
1077             $chg
1078            
1079            
1080             $cltrid
1081            
1082            
1083             UPDREG
1084              
1085 0           return $self->simple_request( $body, 'update_registrar' );
1086             }
1087              
1088              
1089             =head2 get_billing_info
1090              
1091             INPUT:
1092              
1093             keys of params:
1094              
1095             C,
1096              
1097             C: in days,
1098              
1099             C: RUB.
1100              
1101             =cut
1102              
1103             sub get_billing_info {
1104 0     0 1   my ( $self, $params ) = @_;
1105              
1106 0 0         return ( 0, 0, 'no params' ) unless ref $params;
1107              
1108 0           my $cltrid = $self->get_cltrid();
1109              
1110 0           my $body = <
1111             $$self{urn}{head}
1112            
1113            
1114            
1115             balance
1116            
1117             $$params{date}
1118             $$params{period}
1119             $$params{currency}
1120            
1121            
1122            
1123             $cltrid
1124            
1125            
1126             BILINFO
1127              
1128 0           my $answ = $self->req( $body, 'billing_info' );
1129              
1130 0 0 0       if ( $answ && $answ =~ // ) {
1131 0           my $rcode = $1 + 0;
1132              
1133 0           my $msg = '';
1134 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1135 0           $msg = $1;
1136             }
1137              
1138 0 0         if ( $rcode != 1000 ) {
1139 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
1140 0           $msg .= '; ' . $1;
1141             }
1142              
1143 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
1144             }
1145              
1146 0           my $info = {};
1147              
1148 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
1149 0           my $rdata = $1;
1150              
1151 0           my @billing = $rdata =~ /(]+>[^<>]+<\/billing:[^<>]+>)/g;
1152              
1153 0           foreach my $row ( @billing ) {
1154 0 0         if ( $row =~ /]*>([^<>]+)<\/billing:[^<>]+>/ ) {
1155 0           $info->{$1} = $2;
1156             }
1157             }
1158              
1159 0           $info->{calc_date} = delete $info->{calcDate};
1160 0           $info->{calc_date} =~ s/T/ /;
1161 0           $info->{calc_date} =~ s/\.\d+Z$//;
1162             }
1163              
1164 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
1165             }
1166              
1167 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
1168             }
1169              
1170              
1171             =head2 get_limits_info
1172              
1173             How many requests are left in this hour
1174              
1175             =cut
1176              
1177             sub get_limits_info {
1178 0     0 1   my ( $self ) = @_;
1179              
1180 0           my $cltrid = $self->get_cltrid();
1181              
1182 0           my $body = <
1183             $$self{urn}{head}
1184            
1185            
1186            
1187            
1188             $cltrid
1189            
1190            
1191             LIMINFO
1192              
1193 0           my $answ = $self->req( $body, 'limits_info' );
1194              
1195 0 0 0       if ( $answ && $answ =~ // ) {
1196 0           my $rcode = $1 + 0;
1197              
1198 0           my $msg = '';
1199 0 0         if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1200 0           $msg = $1;
1201             }
1202              
1203 0 0         if ( $rcode != 1000 ) {
1204 0 0         if ( $answ =~ /(.+)<\/reason>/s ) {
1205 0           $msg .= '; ' . $1;
1206             }
1207              
1208 0 0         return wantarray ? ( 0, $rcode, $msg ) : 0;
1209             }
1210              
1211 0           my $info = {};
1212              
1213 0 0         if ( $answ =~ /(.+)<\/resData>/s ) {
1214 0           my $rdata = $1;
1215              
1216 0           my @limits = $rdata =~ /(]+>[^<>]+<\/limits:[^<>]+>)/g;
1217              
1218 0           foreach my $row ( @limits ) {
1219 0 0         if ( $row =~ /]+)>([^<>]+)<\/limits:[^<>]+>/ ) {
1220 0           $info->{$1} = $2;
1221             }
1222             }
1223             }
1224              
1225 0 0         return wantarray ? ( $info, $rcode, $msg ) : $info;
1226             }
1227              
1228 0 0         return wantarray ? ( 0, 0, 'empty answer' ) : 0;
1229             }
1230              
1231              
1232             =head2 get_stat_info
1233              
1234             Show domain statistics by metric
1235              
1236             key of params:
1237             C -- varians: C, C, C, C, C, C
1238              
1239             Now not work:
1240              
1241             code="2400", msg="Command failed", reason="Internal server error"
1242              
1243             =cut
1244              
1245             sub get_stat_info {
1246 0     0 1   my ( $self, $params ) = @_;
1247              
1248 0 0         return ( 0, 0, 'no params' ) unless ref $params;
1249              
1250 0           my $cltrid = $self->get_cltrid();
1251              
1252 0           my $body = <
1253             $$self{urn}{head}
1254            
1255            
1256            
1257            
1258            
1259            
1260             $cltrid
1261            
1262            
1263             STATINFO
1264              
1265 0           return $self->simple_request( $body, 'info' );
1266             }
1267              
1268              
1269             =head2 logout
1270              
1271             Close session, disconnect
1272              
1273             =cut
1274              
1275             sub logout {
1276 0     0 1   my ( $self ) = @_;
1277              
1278 0 0 0       return 0 unless $self && $self->{sock};
1279              
1280 0 0         return 0 if $self->{alien};
1281              
1282 0           my $cltrid = $self->get_cltrid();
1283              
1284 0           my $body = <
1285             $$self{urn}{head}
1286            
1287            
1288             $cltrid
1289            
1290            
1291             LOGOUT
1292              
1293             # The answer doesn't matter
1294 0           $self->req( $body, 'logout' );
1295              
1296 0           delete $$self{sock};
1297 0           delete $$self{cook};
1298 0           delete $$self{cookies};
1299 0           delete $$self{sessionid};
1300 0           delete $$self{user};
1301 0           delete $$self{url};
1302             }
1303              
1304             1;
1305              
1306              
1307             __END__