File Coverage

blib/lib/IO/EPP/Base.pm
Criterion Covered Total %
statement 764 1081 70.6
branch 326 680 47.9
condition 98 190 51.5
subroutine 55 62 88.7
pod 29 54 53.7
total 1272 2067 61.5


line stmt bran cond sub pod time code
1             package IO::EPP::Base;
2              
3             =encoding utf8
4              
5             =head1 NAME
6              
7             IO::EPP::Base
8              
9             =head1 SYNOPSIS
10              
11             use Data::Dumper;
12             use IO::EPP::Base;
13              
14             sub make_request {
15             my ( $action, $params ) = @_;
16              
17             unless ( $params->{conn} ) {
18             # need to connect
19              
20             my %sock_params = (
21             PeerHost => 'epp.example.com',
22             PeerPort => 700,
23             SSL_key_file => 'key.pem',
24             SSL_cert_file => 'cert.pem',
25             Timeout => 30,
26             );
27              
28             $params->{user} = 'login';
29             $params->{pass} = 'xxxxx';
30              
31             $params->{sock_params} = \%sock_params;
32              
33             $params->{test_mode} = 1; # use emulator
34              
35             # $params->{no_log} = 1; # 1 if no logging
36              
37             # enter a name if you need to specify a file for the log
38             # $params->{log_name} = '/var/log/comm_epp_example.log';
39              
40             # use our function for logging
41             $params->{log_fn} = sub { print "epp.example.com logger:\n$_[0]\n" };
42             }
43              
44             return IO::EPP::Base::make_request( $action, $params );
45             }
46              
47             my ( $answ, $msg, $conn_obj ) = make_request( 'check_domains', { domains => [ 'xyz.com', 'com.xyz', 'reged.xyz' ] } );
48              
49             print Dumper $answ;
50              
51             Result:
52              
53             $VAR1 = {
54             'msg' => 'Command completed successfully.',
55             'xyz.com' => {
56             'avail' => '1'
57             },
58             'reged.xyz' => {
59             'reason' => 'in use',
60             'avail' => '0'
61             },
62             'code' => '1000',
63             'com.xyz' => {
64             'avail' => '1'
65             }
66             };
67             }
68              
69             =head1 DESCRIPTION
70              
71             Module for common EPP-functions, without extension (dnssec only).
72              
73             The module can be used to work with any provider,
74             if the requests do not use extensions and the provider does not have its own features
75              
76             It has two options: using a separate function call or working as an object
77              
78             =cut
79              
80 14     14   867 use Digest::MD5 qw(md5_hex);
  14         24  
  14         635  
81 14     14   4649 use Time::HiRes qw(time);
  14         11994  
  14         52  
82 14     14   6843 use IO::Socket;
  14         131815  
  14         51  
83 14     14   13299 use IO::Socket::SSL;
  14         645430  
  14         94  
84              
85 14     14   1892 use strict;
  14         34  
  14         281  
86 14     14   60 use warnings;
  14         91  
  14         199622  
87              
88             # common chunks for all standard queries
89             our $epp_head = '
90             ';
91             our $epp_cont_urn =
92             'xmlns:contact="urn:ietf:params:xml:ns:contact-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd"';
93             our $epp_host_urn =
94             'xmlns:host="urn:ietf:params:xml:ns:host-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:host-1.0 host-1.0.xsd"';
95             our $epp_dom_urn =
96             'xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd"';
97              
98             our %id = ( crID => 'creater', clID => 'owner', upID => 'updater', reID => 'requestors_id', acID => 'senders_id' );
99             our %dt = ( crDate => 'cre_date', upDate => 'upd_date', trDate => 'trans_date', exDate => 'exp_date', reDate => 'request_date', acDate => 'send_date' );
100              
101              
102             =head1 FUNCTIONS
103              
104             =head2 make_request
105              
106             See IO:EPP for description
107              
108             An example of working with functions is presented in the synopsis
109              
110             Work checked on CentralNic server
111              
112             INPUT:
113             action name;
114             parameters of query
115              
116             OUTPUT:
117             full answer with code and message;
118             string with code and message;
119             io::epp object
120              
121             =cut
122              
123             sub make_request {
124 92     92 1 66808 my ( $action, $params ) = @_;
125              
126 92         144 my ( $self, $code, $msg, $answ );
127              
128 92 100 66     370 if ( !$params->{tld} && $params->{dname} ) {
129 52         318 ( $params->{tld} ) = $params->{dname} =~ /^[0-9a-z\-]+\.(.+)$/;
130             }
131              
132 92 100       190 unless ( $params->{conn} ) {
133             # Need greate obj and login
134 46         100 ( $self, $code, $msg ) = IO::EPP::Base->new( $params );
135              
136 46 50 33     139 unless ( $code and $code == 1000 ) {
137 0         0 goto END_MR;
138             }
139             }
140             else {
141 46         59 $self = $params->{conn};
142             }
143              
144 92         136 $self->{critical_error} = '';
145              
146 92 50       319 if ( $self->can( $action ) ) {
147 92         223 ( $answ, $code, $msg ) = $self->$action( $params );
148             }
149             else {
150 0         0 $msg = "undefined command <$action>, request cancelled";
151 0         0 $code = 0;
152             }
153              
154             END_MR:
155              
156 92 50       216 $msg .= '; ' . $self->{critical_error} if $self->{critical_error};
157              
158 92         227 my $full_msg = "code: $code\nmsg: $msg";
159              
160 92 100 66     365 $answ = {} unless $answ && ref $answ;
161              
162 92         158 $answ->{code} = $code;
163 92         120 $answ->{msg} = $msg;
164              
165 92 50       366 return wantarray ? ( $answ, $full_msg, $self ) : $answ;
166             }
167              
168              
169             sub gen_id {
170 0     0 0 0 my @chars = ( 'a'..'z', '0'..'9' );
171              
172 0         0 return join '', map( { $chars[ int rand( scalar @chars ) ] } 1..12 );
  0         0  
173             }
174              
175              
176             =head2 gen_pw
177              
178             Authinfo Generation
179              
180             INPUT:
181             length of authInfo, default 16 symbols
182              
183             OUTPUT:
184             new authInfo
185              
186             =cut
187              
188             sub gen_pw {
189 8     8 1 12 my ( $pw_length ) = @_;
190              
191 8 50       16 $pw_length = 16 unless $pw_length;
192              
193 8         9 my $pw;
194              
195 8         82 my @chars = ( '0'..'9', 'A'..'Z', 'a'..'z', '!', '@', '$', '%', '*', '_', '.', ':', '-', '=', '+', '?', '#', ',', '"', "'" );
196              
197 8         15 for ( 0..32 ) {
198 8         20 $pw = join '', map( { $chars[ int rand( scalar @chars ) ] } 1..$pw_length );
  128         198  
199              
200             # буквы, цифры и символы должны быть обязательно
201 8 50 33     112 last if ( $pw =~ /\d/ and $pw =~ /[A-Z]/ and $pw =~ /[a-z]/ and $pw =~ /[!\@\$\%\*_\.:-=\+\?#,"']/ );
      33        
      33        
202             }
203              
204 8         48 return $pw;
205             }
206              
207              
208             # Generation transaction id
209              
210             sub get_cltrid {
211 326     326 0 3554 return md5_hex( time() . $$ . rand(1000000) );
212             }
213              
214              
215             # recursive removal of utf8 flag
216              
217             sub recursive_utf8_unflaged {
218 0     0 0 0 my $root = shift;
219              
220 0 0 0     0 if ( ref $root eq 'HASH' ) {
    0          
    0          
221 0         0 foreach my $k ( keys %$root ) {
222 0         0 my $key = $k;
223 0         0 utf8::decode( $key );
224 0         0 utf8::decode( $key );
225 0         0 utf8::encode( $key );
226             # work if $root->{with_utf8_flag} ne $root->{without_utf8_flag}
227 0         0 $root->{$key} = recursive_utf8_unflaged( delete $root->{$k} ) ;
228             }
229             }
230             elsif ( ref $root eq 'ARRAY' ) {
231 0         0 $_ = recursive_utf8_unflaged($_) foreach @$root;
232             }
233             elsif ( $root && ref $root eq '' ) {
234 0         0 utf8::decode( $root );
235 0         0 utf8::decode( $root );
236 0         0 utf8::encode( $root );
237             }
238              
239 0         0 return $root;
240             }
241              
242             # clear date-time
243              
244             sub cldate {
245 51     51 0 109 my ( $dt ) = @_;
246              
247 51         155 $dt =~ s/T/ /;
248 51         151 $dt =~ s/\.\d+Z$//;
249 51         104 $dt =~ s/Z$//;
250              
251 51         164 return $dt;
252             }
253              
254              
255             =head1 METHODS
256              
257             =head2 new
258              
259             Create new IO::EPP object, аutomatically connects to the provider and logins.
260              
261             Example of a call
262              
263             # Parameters for L
264             my %sock_params = (
265             PeerHost => 'epp.example.com',
266             PeerPort => 700,
267             SSL_key_file => $path_to_ssl_key_file,
268             SSL_cert_file => $path_to_ssl_cert_file,
269             Timeout => 30,
270             );
271              
272             # initialization of an object, during which login is called
273             my $o = IO::EPP::Base->new( {
274             sock_params => \%sock_params,
275             user => $login_name,
276             pass => $login_password,
277             log_name => '/var/log/comm_epp_registry_name',
278             } );
279              
280             # call check of domains
281             my ( $answ, $code, $msg ) = $o->check_domains( { domains => [ 'kalinka.realty' ] } );
282              
283             undef $o; # call logout() и DESTROY() of object
284              
285             Connection parameters:
286             C -- login;
287             C -- password;
288             C -- zone for providers that have a binding in it, for example, verisign;
289             C -- server name if the registry has different servers with different extensions, for example, pir/afilias for afilias;
290             C -- hashref with L parameters;
291             C -- use a real connection or registry emulator.
292              
293             Parameters for logging:
294             C -- do not write anything to the log;
295             C -- write log in this file, not in STDOUT;
296             C -- ref on functions to write to the log.
297              
298             =cut
299              
300             sub new {
301 77     77 1 2166 my ( $package, $params ) = @_;
302              
303 77         132 my ( $self, $code, $msg );
304              
305 77         0 my $sock;
306              
307 77         122 my $sock_params = delete $params->{sock_params};
308              
309 77         102 my $test = delete $params->{test_mode};
310              
311 77 50       127 if ( $test ) {
312 77         162 $sock = $sock_params->{PeerHost} . ':' . $sock_params->{PeerPort};
313             }
314             else {
315             $sock = IO::Socket::SSL->new(
316             PeerPort => 700,
317             Timeout => 30,
318 0         0 %{$sock_params},
  0         0  
319             );
320             }
321              
322 77 50       129 unless ( $sock ) {
323 0         0 $msg = "can not connect";
324 0         0 $code = 0;
325              
326 0         0 goto ERR;
327             }
328              
329             $self = bless {
330             sock => $sock,
331             user => delete $params->{user},
332             tld => $params->{tld} || '',
333             server => delete $params->{server} || '',
334             #launch => $params->{launch} || '',
335             log_name => delete $params->{log_name},
336             log_fn => delete $params->{log_fn},
337 77   100     583 no_log => delete $params->{no_log} || 0,
      100        
      50        
338             test => $test,
339             critical_error => undef,
340             }, $package;
341              
342 77         187 $self->set_urn();
343              
344 77         162 $self->set_log_vars( $params );
345              
346 77         219 $self->epp_log( "Connect to $$sock_params{PeerHost}:$$sock_params{PeerPort}\n" );
347              
348 77         122 my $hello = $self->req();
349              
350 77 50 33     279 if ( !$hello || $self->{critical_error} ) {
351 0         0 $msg = "Can't get greeting";
352 0 0       0 $msg .= '; ' . $self->{critical_error} if $self->{critical_error};
353 0         0 $code = 0;
354              
355 0         0 goto ERR;
356             }
357              
358 77         132 my ( $svcs, $extension ) = ( '', '' );
359              
360 77 100       255 if ( ref( $self ) =~ /IO::EPP::Base/ ) {
361 47 50       183 if ( $hello =~ /urn:ietf:params:xml:ns:contact-1.0/ ) {
362 47         68 $svcs .= '
363             urn:ietf:params:xml:ns:contact-1.0';
364             }
365 47 50       118 if ( $hello =~ /urn:ietf:params:xml:ns:domain-1.0/ ) {
366 47         62 $svcs .= '
367             urn:ietf:params:xml:ns:domain-1.0';
368             }
369 47 50       113 if ( $hello =~ /urn:ietf:params:xml:ns:host-1.0/ ) {
370             # drs.ua not want host
371 47         58 $svcs .= '
372             urn:ietf:params:xml:ns:host-1.0';
373             }
374              
375 47 50       128 if ( $hello =~ /urn:ietf:params:xml:ns:secDNS-1.1/ ) {
376 47         65 $extension .= '
377             urn:ietf:params:xml:ns:secDNS-1.1';
378             }
379             }
380              
381             # have a connection, can log in
382 77         201 my ( undef, $c, $m ) = $self->login( delete $params->{pass}, $svcs, $extension ); # no save passwd in object
383              
384 77 50 33     367 if ( $c and $c == 1000 ) {
385 77 100       273 return wantarray ? ( $self, $c, $m ) : $self;
386             }
387              
388 0   0     0 $msg = ( $m || '' ) . $self->{critical_error};
389 0   0     0 $code = $c || 0;
390              
391 0 0       0 ERR:
392             return wantarray ? ( 0, $code, $msg ) : 0;
393             }
394              
395              
396             sub set_urn {
397             $_[0]->{urn} = {
398 77     77 0 240 head => $IO::EPP::Base::epp_head,
399             cont => $IO::EPP::Base::epp_cont_urn,
400             host => $IO::EPP::Base::epp_host_urn,
401             dom => $IO::EPP::Base::epp_dom_urn,
402             };
403             }
404              
405              
406             # Set name for log
407              
408             sub set_log_vars {
409 77     77 0 100 my ( $self, $params ) = @_;
410              
411 77 50       152 $self->{log_name} = delete $params->{log_name} if $params->{log_name};
412 77 50       150 $self->{log_fn} = delete $params->{log_fn} if $params->{log_fn};
413             }
414              
415              
416             =head2 epp_log
417              
418             Writes data to the log or calls the function specified when creating the object
419              
420             By default, the log is written: date and time, pid of the process, name and body of the request:
421              
422             Thu Jan 1 01:00:00 1111
423             pid: 12345
424             check_domains request:
425            
426            
427            
428            
429            
430             xyz.comcom.xyzreged.xyz
431            
432            
433             50df482a1e928a00fa0e7fce3fe68f0f
434            
435            
436              
437             Thu Feb 2 02:02:22 2222
438             pid: 12345
439             check_domains answer:
440            
441            
442            
443            
444             Command completed successfully.
445            
446            
447             xyz.com
448             com.xyz
449             reged.xyzin use
450            
451             50df482a1e928a00fa0e7fce3fe68f0fTEST-2979E52890117206AAA1639725F4E862
452            
453            
454            
455              
456             =cut
457              
458             sub epp_log {
459 814     814 1 1211 my ( $self, $string ) = @_;
460              
461 814 50       1557 return if $self->{no_log};
462              
463 0         0 $string = "pid: $$\n" . $string;
464              
465 0 0       0 if ( $self->{log_fn} ) {
    0          
466 0         0 &{$self->{log_fn}}( $string );
  0         0  
467             }
468             elsif ( $self->{log_name} ) {
469 0         0 my $fh;
470              
471 0 0       0 if ( $self->{log_fh} ) {
472 0         0 $fh = $self->{log_fh};
473             }
474             else{
475 0 0       0 open( $fh, '>>', $self->{log_name} ) or die "Can't open $self->{log_name}: $!\n";
476              
477 0         0 $self->{log_fh} = $fh;
478             }
479              
480 0         0 print $fh scalar(localtime) . "\n$string\n\n"; # if `print( $self->{log_fh} $string );` that get error `(Missing operator before $string?)`
481             }
482             else {
483 0         0 print scalar(localtime) . "\n$string\n\n";
484             }
485             }
486              
487              
488             =head2 req_test
489              
490             For replace req() in test mode
491              
492             =cut
493              
494             sub req_test {
495 233     233 1 298 my ( $self, $out_data, $info ) = @_;
496              
497 233         1695 require IO::EPP::Test::Base;
498              
499 233 100       738 $self->epp_log( "$info request:\n$out_data" ) if $out_data;
500              
501 233         338 my $answ;
502             eval{
503 233         495 $answ = IO::EPP::Test::Base::req( @_ );
504 233         467 1;
505             }
506 233 50       276 or do {
507 0         0 $self->{critical_error} = "$info req error: $@";
508 0         0 return;
509             };
510              
511 233         769 $self->epp_log( "$info answer:\n$answ" );
512              
513 233         463 return $answ;
514             }
515              
516              
517             =head2 req
518              
519             Request to registry
520              
521             INPUT:
522             out_data -- body of request;
523             info -- name of request for log.
524              
525             OUTPUT:
526             answer from registry.
527              
528             =cut
529              
530             sub req {
531 407     407 1 615 my ( $self, $out_data, $info ) = @_;
532              
533 407         537 $self->{critical_error} = '';
534              
535 407   100     700 $info ||= '';
536              
537 407 50       1019 return $self->req_test( $out_data, $info ) if $self->{test};
538              
539 0         0 my $THRESHOLD = 100000000;
540              
541 0 0       0 if ( $out_data ) {
542 0         0 my $d = $out_data;
543             # Remove password, authinfo from log
544 0         0 $d =~ s/[^<>]+<\/pw>/xxxxx<\/pw>/;
545              
546 0         0 $self->epp_log( "$info request:\n$d" );
547             }
548              
549 0         0 my $in_data = '';
550 0         0 my $start_time = time;
551              
552             eval{
553 0     0   0 local $SIG{ALRM} = sub { die "connection timeout\n" };
  0         0  
554              
555 0         0 alarm 120;
556              
557 0 0       0 if ( $out_data ) {
558             # https://rt.cpan.org/Ticket/Display.html?id=98368
559             # https://rt.cpan.org/Ticket/Display.html?id=98372
560 0         0 utf8::downgrade( $out_data );
561              
562 0         0 my $len = length( $out_data ) + 4;
563 0         0 my $pk_data_size = pack( 'N', $len );
564              
565 0         0 my $a_out = $self->{sock}->print( $pk_data_size . $out_data );
566 0         0 $self->{sock}->flush();
567              
568 0 0       0 die "data write failed" unless $a_out;
569             };
570              
571             # header - 4 bytes Nxxx with size
572 0         0 my $hdr;
573 0 0       0 unless ( defined( $self->{sock}->read( $hdr, 4 ) ) ) {
574 0         0 die "closed connection\n";
575             }
576              
577 0   0     0 my $data_size = ( unpack( 'N', $hdr ) // 0 ) - 4;
578              
579 0 0       0 die "closed connection\n" if $data_size < 0;
580              
581 0 0       0 die "data length is zero\n" unless $data_size;
582              
583 0 0       0 die "data length is $data_size which exceeds $THRESHOLD\n" if $data_size > $THRESHOLD;
584              
585             # Read data block
586 0         0 my $buf;
587 0         0 my $wait_cnt = 0;
588              
589 0         0 while ( length( $in_data ) < $data_size ) {
590 0         0 $buf = '';
591 0         0 $self->{sock}->read( $buf, ( $data_size - length( $in_data ) ));
592              
593 0 0       0 if ( length( $buf ) == 0 ) {
594 0 0       0 if ( $wait_cnt < 3 ) {
595             # part of the data may come with a long delay when saving the connection
596             # this problem is observed in corenic and drs
597 0         0 $wait_cnt++;
598 0         0 sleep 1;
599 0         0 redo;
600             }
601             else {
602             # it is likely that the socket has closed
603 0         0 last;
604             }
605             }
606              
607 0         0 $in_data .= $buf;
608             }
609              
610             # recheck, because something could not reach or stop at \0
611 0         0 my $l = length( $in_data );
612 0 0       0 die "data read failed: readed $l, need $data_size\ndata: $in_data" if $l != $data_size;
613              
614 0         0 alarm 0;
615              
616 0         0 1;
617 0 0       0 } or do {
618 0         0 my $err = $@;
619              
620 0         0 alarm 0;
621              
622 0         0 my $req_time = sprintf( '%0.4f', time - $start_time );
623 0         0 $self->epp_log( "req_time: $req_time\n$info req error: $err" );
624              
625 0         0 $self->{critical_error} = "req error: $err";
626              
627 0         0 return;
628             };
629              
630 0         0 my $req_time = sprintf( '%0.4f', time - $start_time );
631 0         0 $self->epp_log( "req_time: $req_time\n$info answer:\n$in_data" );
632              
633 0         0 return $in_data;
634             }
635              
636              
637             =head2 simple_request
638              
639             Universal handler for simple answer
640              
641             INPUT:
642             request body;
643             request name;
644             check or not epp poll, default is 0
645              
646             OUTPUT:
647             answer;
648             answer code;
649             answer message
650              
651             =cut
652              
653             sub simple_request {
654 211     211 1 376 my ( $self, $body, $info, $check_queue_msgs ) = @_;
655              
656 211 50       380 unless ( $body ) {
657 0 0       0 return wantarray ? ( 0, 0, 'no query' ) : 0 ;
658             }
659              
660 211         393 my $content = $self->req( $body, $info );
661              
662 211 50 33     1123 if ( $content && $content =~ // ) {
663 211         560 my $code = $1 + 0;
664              
665 211         292 my $msg = '';
666 211 50       1448 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
667 211         408 $msg = $1;
668             }
669              
670 211 100 66     599 if ( $code == 1001 or $code >= 2000 ) {
671             # 1001 -- pendingAction
672             # 2000+ - May be an addition to the error, It is the inventions of different providers
673 90         298 my $reason = join( ';', $content =~ /]*>([^<>]+)<\/reason>/g );
674              
675 90 100       196 $msg .= "; " . $reason if $reason;
676              
677 90         157 my ( $xcp ) = $content =~ /([^<>]+)<\/oxrs:xcp>/;
678              
679 90 50       143 $msg .= "; " . $xcp if $xcp;
680              
681 90         370 my ( $text ) = $content =~ /([^<>]+)<\/text>.+\/result>/s;
682              
683 90 50       163 $msg .= "; " . $text if $text;
684             }
685              
686             # And check epp poll
687 211         240 my $queue_msgs = '';
688              
689 211 50 33     394 if ( $check_queue_msgs and $content =~ // ) {
690 0         0 $queue_msgs = { count => $1 , id => $2 };
691             }
692              
693 211         302 my $info = {};
694              
695             # dates
696 211         601 foreach my $k ( keys %dt ) {
697 1266 100       17232 if ( $content =~ m|<[a-z]+:$k>([^<>]+)| ) {
698 21         53 $info->{$dt{$k}} = cldate( $1 );
699             }
700             }
701              
702 211 100       609 if ( $content =~ m{([^<>]+)} ) {
703 7         18 $info->{cont_id} = $1;
704             }
705              
706 211 100       476 if ( $content =~ m{<(host|domain):name>([^<>]+)} ) {
707 11         35 my %r = ( host => 'ns', domain => 'dname' );
708              
709 11         53 $info->{$r{$1}} = $2;
710             }
711              
712             # This is needed to monitor deferred actions at some providers
713 211         978 ( $info->{cltrid} ) = $content =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
714 211         751 ( $info->{svtrid} ) = $content =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
715              
716 211 50       1099 return wantarray ? ( $info, $code, $msg, $queue_msgs ) : $info;
717             }
718              
719 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
720             }
721              
722             =head2 login
723              
724             Authorization on the server.
725             The function is automatically called from new.
726             A separate call is only needed to change the password.
727              
728             INPUT:
729             password;
730             addition standard parameters (xxxxx-1.0);
731             extensions (yyyyyy-1.0);
732             new password if need.
733              
734             OUTPUT: see L.
735              
736             =cut
737              
738             sub login {
739 77     77 1 157 my ( $self, $pw, $svcs, $ext, $new_pw ) = @_;
740              
741 77 50       139 return ( 0, 0, 'no user' ) unless $self->{user};
742 77 50       123 return ( 0, 0, 'no passwd' ) unless $pw;
743              
744 77   50     114 $svcs ||= ''; # addition standard parameters
745 77   50     108 $ext ||= ''; # extension
746              
747 77 50       113 if ( $ext ) {
748 77         127 $ext = "\n $ext\n ";
749             }
750              
751 77         97 my $npw = '';
752 77 50       113 if ( $new_pw ) {
753 0         0 $npw = "\n $new_pw";
754             }
755              
756 77         406 my $cltrid = get_cltrid();
757              
758 77         310 my $body = <
759             $$self{urn}{head}
760            
761            
762             $$self{user}
763             $pw$npw
764            
765             1.0
766             en
767            
768             $svcs$ext
769            
770            
771             $cltrid
772            
773            
774             LOGIN
775              
776 77         148 return $self->simple_request( $body, 'login' );
777             }
778              
779              
780             =head2 hello
781              
782             Get greeting, ping analog.
783              
784             No parameters.
785              
786             =cut
787              
788             sub hello {
789 4     4 1 2784 my ( $self ) = @_;
790              
791 4         14 my $body = <
792             $$self{urn}{head}
793            
794            
795             HELLO
796              
797 4         15 my $content = $self->req( $body, 'hello' );
798              
799 4 50 33     29 unless ( $content && $content =~ /greeting/ ) {
800 0 0       0 return wantarray ? ( 0, 0, 'no greeting' ) : 0;
801             }
802              
803 4         12 my $info = {
804             code => 1000,
805             msg => $content,
806             };
807              
808 4 50       20 return wantarray ? ( $info, 1000, $content ) : $info;
809             }
810              
811              
812             =head2 check_contacts
813              
814             Check whether there are contacts with such IDs
815              
816             INPUT:
817             params with key:
818             C -- arrayref on contact id list.
819              
820             Request:
821              
822             my ( $answ, $msg ) = make_request( 'check_contacts', { contacts => [ 'H1234567', 'nfjkrek-fre8fm' ] } );
823              
824             print Dumper $answ;
825              
826             Answer:
827              
828             $VAR1 = {
829             'msg' => 'Command completed successfully.',
830             'nfjkrek-fre8fm' => {
831             'avail' => '1'
832             },
833             'H1234567' => {
834             'avail' => '0'
835             },
836             'code' => '1000'
837             };
838              
839             =cut
840              
841             sub check_contacts {
842 1     1 1 3 my ( $self, $params ) = @_;
843              
844 1 50 50     5 return ( 0, 0, 'no contacts' ) unless $params->{contacts} && scalar( @{$params->{contacts}} );
  1         4  
845              
846 1         2 my $contacts = $params->{contacts};
847              
848 1         2 my $conts = '';
849              
850 1         3 foreach my $cont ( @$contacts ) {
851 2         5 $conts .= "$cont";
852             }
853              
854 1   50     4 my $ext = $$params{extension} || '';
855              
856 1 50       2 $ext = "\n \n$ext " if $ext;
857              
858 1         3 my $cltrid = get_cltrid();
859              
860 1         6 my $body = <
861             $$self{urn}{head}
862            
863            
864            
865             $conts
866            
867             $ext
868             $cltrid
869            
870            
871             CHECKCONT
872              
873 1         5 my $content = $self->req( $body, 'check_contacts' );
874              
875 1 50       6 if ( $content =~ // ) {
876 1         3 my $code = $1 + 0;
877              
878 1         2 my $msg = '';
879 1 50       12 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
880 1         2 $msg = $1;
881             }
882              
883 1         15 my @aa = $content =~ /([^<>]+<\/contact:id>)/g;
884              
885 1         7 my %answ;
886 1         3 foreach my $a ( @aa ) {
887 2 50       10 if ( $a =~ /([^<>]+)<\/contact:id>/ ) {
888 2         8 $answ{$2} = { avail => $1 };
889             }
890             }
891              
892 1 50       6 return wantarray ? ( \%answ, $code, $msg ) : \%answ;
893             }
894              
895 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
896             }
897              
898             # Convert contact params to xml text
899              
900             sub cont_to_xml {
901 13     13 0 20 my ( $self, $params ) = @_;
902              
903 13 100 100     48 unless ( $$params{'int'} && $$params{'loc'} ) {
904             # Set default is 'int'
905 8         19 foreach my $f ( 'name', 'first_name', 'last_name', 'patronymic', 'family_name', 'company', 'addr', 'city', 'state', 'postcode', 'country_code' ) {
906 88 100       136 $$params{'int'}{$f} = delete $$params{$f} if defined $$params{$f};
907             }
908             }
909              
910 13         17 my $postalinfo = '';
911 13         21 foreach my $type ( 'int', 'loc' ) { # legal - in children modules
912 26 100       50 next unless $$params{$type};
913              
914             # need_name=1 for creation - always, for update:
915             # According to the standard at change of id the name does not change ( https://tools.ietf.org/html/rfc5733#page-23 ),
916             # but some providers of it do not know,
917             # at the same time, they have no documentation, but send all to read RFC, example, drs.ua
918 18         18 my $name = '';
919 18 100       44 if ( $$params{need_name} ) {
920 16 50       31 if ( $$params{$type}{name} ) {
921 0         0 $name = $$params{$type}{name};
922             }
923             else {
924 16 50       36 $name = $$params{$type}{first_name} if $$params{$type}{first_name};
925              
926 16 50 33     48 if ( $$params{$type}{last_name} && $$params{$type}{family_name} ) {
927 0 0       0 if ( $$params{$type}{last_name} ) {
928 0 0       0 $name .= ' ' if $name;
929 0         0 $name .= $$params{$type}{last_name};
930             }
931              
932 0 0       0 if ( $$params{$type}{patronymic} ) {
933 0 0       0 $name .= ' ' if $name;
934 0         0 $name .= $$params{$type}{patronymic};
935             }
936              
937 0 0       0 if ( $$params{$type}{family_name} ) {
938 0 0       0 $name .= ' ' if $name;
939 0         0 $name .= $$params{$type}{family_name};
940             }
941             }
942             else {
943             # family_name eq last_name
944 16 50       26 if ( $$params{$type}{patronymic} ) {
945 0 0       0 $name .= ' ' if $name;
946 0         0 $name .= $$params{$type}{patronymic};
947             }
948              
949 16 50 33     31 if ( $$params{$type}{last_name} || $$params{$type}{family_name} ) {
950 16 50       26 $name .= ' ' if $name;
951 16   33     28 $name .= $$params{$type}{last_name} || $$params{$type}{family_name};
952             }
953             }
954              
955             }
956              
957 16 50       35 $name = "\n $name" if $name;
958             }
959              
960 18         22 my $org;
961 18 100       28 if ( $$params{$type}{org} ) {
962 10         17 $$params{$type}{org} =~ s/&/&/g;
963              
964 10         18 $org = "$$params{$type}{org}";
965             }
966             else {
967 8         12 $org = '';
968             }
969              
970              
971 18         20 my $street = '';
972              
973 18 100       53 $$params{$type}{addr} = [ $$params{$type}{addr} ] unless ref $$params{$type}{addr};
974              
975 18         21 foreach my $s ( @{$$params{$type}{addr}} ) {
  18         37  
976 23 100       32 $street .= "\n " if $street;
977 23         41 $street .= "$s";
978             }
979              
980 18 50       44 my $sp = $$params{$type}{'state'} ? "$$params{$type}{state}" : '' ;
981 18 50       41 my $pc = $$params{$type}{postcode} ? "$$params{$type}{postcode}" : '' ;
982              
983 18         72 $postalinfo .= qq|
984             $name
985             $org
986            
987             $street
988             $$params{$type}{city}
989             $sp
990             $pc
991             $$params{$type}{country_code}
992            
993             |;
994             }
995              
996             # voice / fax Extension is disabled
997 13         17 my $voice = '';
998 13 100       35 $$params{phone} = [ $$params{phone} ] unless ref $$params{phone};
999 13         18 foreach my $s ( @{$$params{phone}} ) {
  13         23  
1000 17 100       27 $voice .= "\n " if $voice;
1001 17         28 $voice .= "$s";
1002             }
1003              
1004 13 50       29 my $fax = $$params{fax} ? "$$params{fax}" : '';
1005              
1006 13         17 my $email = '';
1007 13 100       31 $$params{email} = [ $$params{email} ] unless ref $$params{email};
1008 13         16 foreach my $s ( @{$$params{email}} ) {
  13         26  
1009 13 50       22 $email .= "\n " if $email;
1010 13         23 $email .= "$s";
1011             }
1012              
1013 13 50       30 my $pw = $$params{authinfo} ? "$$params{authinfo}" : '' ;
1014              
1015 13   50     44 $$params{pp_ext} ||= '';
1016              
1017 13         51 my $textcont = qq|$postalinfo
1018             $voice
1019             $fax
1020             $email
1021            
1022             $pw
1023             $$params{pp_ext}|;
1024              
1025 13         26 return $textcont;
1026             }
1027              
1028              
1029             # create contact extensions,
1030             # for overwriting in child classes
1031              
1032             sub create_contact_ext {
1033 11     11 0 18 return '';
1034             }
1035              
1036             =head2 create_contact
1037              
1038             Register a contact
1039              
1040             OUTPUT: see L.
1041              
1042             =cut
1043              
1044             sub create_contact {
1045 11     11 1 20 my ( $self, $params ) = @_;
1046              
1047 11 50       26 return ( 0, 0, 'no params' ) unless ref $params;
1048              
1049 11 50       25 return ( 0, 0, 'no cont_id' ) unless $params->{cont_id};
1050              
1051 11         21 $params->{need_name} = 1;
1052              
1053 11         25 my $textcont = $self->cont_to_xml( $params );
1054              
1055 11   50     33 my $ext = $params->{extension} || '';
1056              
1057 11         20 $ext .= $self->create_contact_ext( $params );
1058              
1059 11 50       21 if ( $ext ) {
1060 0         0 $ext = "\n \n$ext ";
1061             }
1062              
1063 11         17 my $cltrid = get_cltrid();
1064              
1065 11         74 my $body = <
1066             $$self{urn}{head}
1067            
1068            
1069            
1070             $$params{cont_id}$textcont
1071            
1072             $ext
1073             $cltrid
1074            
1075            
1076             CRECONT
1077              
1078 11         20 return $self->simple_request( $body, 'create_contact' );
1079             }
1080              
1081              
1082             sub cont_from_xml {
1083 3     3 0 11 my ( undef, $rdata ) = @_;
1084              
1085 3         4 my %cont;
1086              
1087 3         13 ( $cont{cont_id} ) = $rdata =~ /([^<>]+)<\/contact:id>/;
1088              
1089 3         12 ( $cont{roid} ) = $rdata =~ /([^<>]+)<\/contact:roid>/;
1090              
1091 3         8 my @atypes = ( 'int', 'loc' );
1092 3         5 foreach my $atype ( @atypes ) {
1093 6         123 my ( $postal ) = $rdata =~ /(.+?)<\/contact:postalInfo>/s;
1094              
1095 6 100       19 next unless $postal;
1096              
1097 5         22 ( $cont{$atype}{name} ) = $postal =~ /([^<>]+)<\/contact:name>/;
1098              
1099 5 100       18 if ( $rdata =~ /([^<>]*)<\/contact:org>/ ) {
1100 4         10 $cont{$atype}{org} = $1;
1101 4         8 $cont{$atype}{org} =~ s/&/&/g;
1102             }
1103              
1104              
1105 5         28 $cont{$atype}{addr} = join(', ', $postal =~ /([^<>]*)<\/contact:street>/ );
1106              
1107 5         25 ( $cont{$atype}{city} ) = $postal =~ /([^<>]*)<\/contact:city>/;
1108              
1109 5         17 ( $cont{$atype}{'state'} ) = $postal =~ /([^<>]*)<\/contact:sp>/;
1110              
1111 5         17 ( $cont{$atype}{postcode} ) = $postal =~ /([^<>]*)<\/contact:pc>/;
1112              
1113 5         17 ( $cont{$atype}{country_code} ) = $postal =~ /([A-Za-z]+)<\/contact:cc>/;
1114 5         13 $cont{$atype}{country_code} = uc $cont{$atype}{country_code};
1115             }
1116              
1117 3         19 $cont{phone} = [ $rdata =~ /]*>([0-9+.]*)<\/contact:voice>/g ];
1118              
1119 3         12 $cont{fax} = [ $rdata =~ /]*>([0-9+.]*)<\/contact:fax>/g ];
1120              
1121 3         14 $cont{email} = [ $rdata =~ /([^<>]+)<\/contact:email>/g ];
1122              
1123             #
1124 3         13 my @ss = $rdata =~ //g;
1125             # No changes pending
1126 3         7 my @aa = $rdata =~ /]+>[^<>]+<\/contact:status>/g;
1127 3 50       6 if ( scalar @aa ) {
1128 0         0 foreach my $row ( @aa ) {
1129 0 0       0 if ( $row =~ /([^<>]+)<\/contact:status>/ ) {
1130 0         0 $cont{statuses}{$1} = $2;
1131             }
1132             }
1133             }
1134             else {
1135 3         11 $cont{statuses}{$_} = '+' for @ss;
1136             }
1137              
1138 3 50       10 if ( $rdata =~ /\s*(.+?)<\/contact:pw>/s ) {
1139 0         0 $cont{authinfo} = $1;
1140             }
1141              
1142 3         10 my ( $visible ) = $rdata =~ //;
1143 3 50       10 $cont{pp_flag} = $visible ? 0 : 1;
1144              
1145             # owner, ...
1146 3         9 foreach my $k ( keys %id ) {
1147 15 100       216 if ( $rdata =~ /([^<>]+)<\/contact:$k>/ ) {
1148 9         31 $cont{$id{$k}} = $1;
1149             }
1150             }
1151              
1152             # dates
1153 3         9 foreach my $k ( keys %dt ) {
1154 18 100       255 if ( $rdata =~ /([^<>]+)<\/contact:$k>/ ) {
1155 6         18 $cont{$dt{$k}} = $1;
1156              
1157 6         16 $cont{$dt{$k}} =~ s/T/ /;
1158 6         20 $cont{$dt{$k}} =~ s/\.\d+Z$//;
1159 6         15 $cont{$dt{$k}} =~ s/Z$//;
1160             }
1161             }
1162              
1163 3         12 return \%cont;
1164             }
1165              
1166              
1167             # Providers extension, replaced in provider modules
1168              
1169             sub get_contact_ext {
1170 0     0 0 0 return {};
1171             }
1172              
1173              
1174             =head2 get_contact_info
1175              
1176             Get information on the specified contact
1177              
1178             =cut
1179              
1180             sub get_contact_info {
1181 4     4 1 8 my ( $self, $params ) = @_;
1182              
1183 4 50       8 return ( 0, 0, 'no cont_id' ) unless $$params{cont_id};
1184              
1185 4   50     13 my $ext = $$params{extension} || '';
1186              
1187 4 50       9 if ( $ext ) {
1188 0         0 $ext = "\n \n$ext ";
1189             }
1190              
1191 4         6 my $cltrid = get_cltrid();
1192              
1193 4         18 my $body = <
1194             $$self{urn}{head}
1195            
1196            
1197            
1198             $$params{cont_id}
1199            
1200             $ext
1201             $cltrid
1202            
1203            
1204             CONTINFO
1205              
1206 4         9 my $content = $self->req( $body, 'get_contact_info' );
1207              
1208 4 50       21 if ( $content =~ /result code=['"](\d+)['"]/ ) {
1209 4         9 my $rcode = $1 + 0;
1210              
1211 4         5 my $msg = '';
1212 4 50       87 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1213 4         8 $msg = $1;
1214             }
1215              
1216 4         5 my $cont;
1217              
1218             # take the main part and disassemble
1219 4 100       20 if ( $content =~ /(.+)<\/resData>/s ) {
1220 3         14 $cont = $self->cont_from_xml( $1 );
1221             }
1222             else {
1223 1 50       5 return wantarray ? ( 0, $rcode, $msg ) : 0 ;
1224             }
1225              
1226 3 50       12 if ( $content =~ /(.+)<\/extension>/s ) {
1227 0         0 my $ext = $1;
1228              
1229 0         0 my $spec_ext = $self->get_contact_ext( $ext );
1230              
1231 0         0 $cont->{$_} = $spec_ext->{$_} for keys %$spec_ext;
1232              
1233 0 0       0 if ( $content =~ /]*>(.+)<\/keysys:resData>/s ) {
1234             # key-system extension TODO: move
1235 0         0 my $krdata = $1;
1236              
1237 0 0       0 push @{$cont->{statuses}}, 'validated' if $krdata =~ /1<\/keysys:validated>/;
  0         0  
1238 0 0       0 push @{$cont->{statuses}}, 'verified' if $krdata =~ /1<\/keysys:verified>/;
  0         0  
1239 0 0       0 push @{$cont->{statuses}}, 'verification-requested'
  0         0  
1240             if $krdata =~ /1<\/keysys:verification-requested>/;
1241             }
1242             }
1243              
1244 3 50       14 return wantarray ? ( $cont, $rcode, $msg ) : $cont;
1245             }
1246              
1247 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
1248             }
1249              
1250             sub update_statuses_add {
1251 14     14 0 22 my ( undef, $type, $statuses ) = @_;
1252              
1253 14         20 my $add = '';
1254 14         18 my %sts;
1255              
1256 14 100       41 if ( ref $statuses eq 'HASH' ) {
    50          
1257 2         2 %sts = %{$statuses};
  2         7  
1258             }
1259             elsif ( ref $statuses eq 'ARRAY' ) {
1260 12         14 $sts{$_} = '+' for @{$statuses};
  12         30  
1261             }
1262              
1263 14         34 foreach my $st ( keys %sts ) {
1264 14 100 66     54 if ( !$sts{$st} or $sts{$st} eq '+' ) {
1265 12         35 $add .= qq| <$type:status s="$st"/>\n|;
1266             }
1267             else {
1268 2         9 $add .= qq| <$type:status s="$st">$sts{$st}\n|;
1269             }
1270             }
1271              
1272 14         32 return $add;
1273             }
1274              
1275              
1276             sub update_statuses_rem {
1277 4     4 0 10 my ( undef, $type, $statuses ) = @_;
1278              
1279 4         9 my $rem = '';
1280 4         7 my @sts;
1281              
1282 4 50       17 if ( ref $statuses eq 'HASH' ) {
    50          
1283 0         0 @sts = keys %{$statuses};
  0         0  
1284             }
1285             elsif ( ref $statuses eq 'ARRAY' ) {
1286 4         6 @sts = @{$statuses};
  4         8  
1287             }
1288              
1289 4         19 $rem .= qq| <$type:status s="$_"/>\n| foreach @sts;
1290              
1291 4         8 return $rem;
1292             }
1293              
1294             =head2 update_contact
1295              
1296             To update contact information
1297              
1298             OUTPUT: see L.
1299              
1300             =cut
1301              
1302             sub update_contact {
1303 2     2 1 5 my ( $self, $params ) = @_;
1304              
1305 2 50       5 return ( 0, 0, 'no params' ) unless ref $params;
1306              
1307 2 50       5 return ( 0, 0, 'no cont_id' ) unless $params->{cont_id};
1308              
1309 2         4 my ( $add, $rem, $chg ) = ( '', '', '' );
1310              
1311 2 50       5 if ( $$params{add} ) {
1312 0 0       0 if ( $$params{add}{statuses} ) {
1313 0         0 $add .= $self->update_statuses_add( 'contact', $$params{add}{statuses} );
1314             }
1315             }
1316              
1317 2 50       4 $add = "\n \n$add " if $add;
1318              
1319 2 50       4 if ( $$params{rem} ) {
1320 0 0       0 if ( $$params{rem}{statuses} ) {
1321 0         0 $rem .= $self->update_statuses_rem( 'contact', $$params{rem}{statuses} );
1322             }
1323             }
1324              
1325 2 50       4 $rem = "\n \n$rem " if $rem;
1326              
1327 2 50       5 if ( $$params{chg} ) {
1328 2         4 $chg .= $self->cont_to_xml( $$params{chg} );
1329              
1330 2         18 $chg =~ s/\n/\n /g;
1331             }
1332              
1333 2 50       8 $chg = "\n $chg " if $chg;
1334              
1335 2   50     7 my $ext = $$params{extension} || '';
1336              
1337 2 50       5 $ext = "\n \n$ext " if $ext;
1338              
1339 2         4 my $cltrid = get_cltrid();
1340              
1341 2         13 my $body = <
1342             $$self{urn}{head}
1343            
1344            
1345            
1346             $$params{cont_id}$add$rem$chg
1347            
1348             $ext
1349             $cltrid
1350            
1351            
1352             UPDCONT
1353              
1354 2         5 return $self->simple_request( $body, 'update_contact' );
1355             }
1356              
1357              
1358             =head2 delete_contact
1359              
1360             Delete the specified contact
1361              
1362             OUTPUT:
1363             see L.
1364              
1365             =cut
1366              
1367             sub delete_contact {
1368 2     2 1 3 my ( $self, $params ) = @_;
1369              
1370 2 50       7 return ( 0, 0, 'no params' ) unless ref $params;
1371              
1372 2 50       4 return ( 0, 0, 'no cont_id' ) unless $$params{cont_id};
1373              
1374 2   50     9 my $ext = $$params{extension} || '';
1375              
1376 2 50       4 $ext = "\n \n$ext " if $ext;
1377              
1378 2         4 my $cltrid = get_cltrid();
1379              
1380 2         13 my $body = <
1381             $$self{urn}{head}
1382            
1383            
1384            
1385             $$params{cont_id}
1386            
1387             $ext
1388             $cltrid
1389            
1390            
1391             DELCONT
1392              
1393 2         4 return $self->simple_request( $body, 'delete_contact' );
1394             }
1395              
1396              
1397             =head2 check_nss
1398              
1399             Check that the nameserver is registered
1400              
1401             OUTPUT:
1402             see L.
1403              
1404             =cut
1405              
1406             sub check_nss {
1407 2     2 1 5 my ( $self, $params ) = @_;
1408              
1409 2 50 50     8 return ( 0, 0, 'no nss' ) unless $params->{nss} && scalar( @{$params->{nss}} );
  2         7  
1410              
1411 2         5 my $hosts = '';
1412              
1413 2         3 foreach my $h ( @{$params->{nss}} ) {
  2         6  
1414 8         14 $hosts .= "$h";
1415             }
1416              
1417 2   100     17 my $ext = $$params{extension} || '';
1418              
1419 2 100       7 $ext = "\n \n$ext " if $ext;
1420              
1421 2         6 my $cltrid = get_cltrid();
1422              
1423 2         11 my $body = <
1424             $$self{urn}{head}
1425            
1426            
1427            
1428             $hosts
1429            
1430             $ext
1431             $cltrid
1432            
1433            
1434             CHECKNSS
1435              
1436 2         6 my $content = $self->req( $body, 'check_nss' );
1437              
1438 2 50       13 if ( $content =~ // ) {
1439 2         7 my $code = $1 + 0;
1440              
1441 2         5 my $msg = '';
1442 2 50       29 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1443 2         5 $msg = $1;
1444             }
1445              
1446 2         25 my @aa = $content =~ /(.+?)<\/host:cd>/sg;
1447              
1448 2         4 my %answ;
1449 2         5 foreach my $a ( @aa ) {
1450 8 50       30 if ( $a =~ /([^<>]+)<\/host:name>/ ) {
1451 8         12 my $ns = $2;
1452 8         19 $answ{$ns} = { avail => $1 };
1453              
1454 8 100       35 if ( $a =~ /([^<>]+)<\/host:reason>/ ) {
1455 2         6 $answ{$ns}{reason} = $1;
1456             }
1457             }
1458             }
1459              
1460 2 50       20 return wantarray ? ( \%answ, $code, $msg ) : \%answ;
1461             }
1462              
1463 0 0       0 return wantarray ? ( 0, 0, 'no answer' ) : 0;
1464             }
1465              
1466              
1467             =head2 create_ns
1468              
1469             Registering a nameserver
1470              
1471             OUTPUT:
1472             see L.
1473              
1474             =cut
1475              
1476             sub create_ns {
1477 17     17 1 31 my ( $self, $params ) = @_;
1478              
1479 17 50       34 return ( 0, 0, 'no ns' ) unless $params->{ns};
1480              
1481 17         23 my $addrs = '';
1482 17 100 66     54 if ( $params->{ips} and ref( $params->{ips} ) eq 'ARRAY' ) {
1483 8         12 foreach my $ip ( @{$params->{ips}} ) {
  8         19  
1484 10 100       46 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1485 6         17 $addrs .= '' . $ip . '';
1486             }
1487             else {
1488 4         10 $addrs .= '' . $ip . '';
1489             }
1490             }
1491             }
1492              
1493 17   100     44 my $ext = $$params{extension} || '';
1494              
1495 17 100       35 $ext = "\n \n$ext " if $ext;
1496              
1497 17         43 my $cltrid = get_cltrid();
1498              
1499 17         84 my $body = <
1500             $$self{urn}{head}
1501            
1502            
1503            
1504             $$params{ns}$addrs
1505            
1506             $ext
1507             $cltrid
1508            
1509            
1510             CREATENS
1511              
1512 17         33 return $self->simple_request( $body, 'create_ns' );
1513             }
1514              
1515              
1516             sub get_ns_info_rdata {
1517 2     2 0 7 my ( undef, $rdata ) = @_;
1518              
1519 2         12 my %ns;
1520              
1521 2         10 ( $ns{name} ) = $rdata =~ /([^<>]+)<\/host:name>/;
1522 2         8 $ns{name} = lc $ns{name};
1523              
1524 2         9 ( $ns{roid} ) = $rdata =~ /([^<>]+)<\/host:roid>/;
1525              
1526             #
1527 2         11 my @ss = $rdata =~ //g;
1528             # No changes pending
1529 2         5 my @aa = $rdata =~ /]+>[^<>]+<\/host:status>/g;
1530 2 50       7 if ( scalar @aa ) {
1531 0         0 foreach my $row ( @aa ) {
1532 0 0       0 if ( $row =~ /([^<>]+)<\/host:status>/ ) {
1533 0         0 $ns{statuses}{$1} = $2;
1534             }
1535             }
1536             }
1537             else {
1538 2         8 $ns{statuses}{$_} = '+' for @ss;
1539             }
1540              
1541 2         13 $ns{addrs} = [ $rdata =~ /([0-9A-Fa-f.:]+)<\/host:addr>/g ];
1542              
1543             # owner, ...
1544 2         8 foreach my $k ( keys %id ) {
1545 10 100       144 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
1546 5         18 $ns{$id{$k}} = $1;
1547             }
1548             }
1549              
1550             # dates
1551 2         9 foreach my $k ( keys %dt ) {
1552 12 100       156 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
1553 3         12 $ns{$dt{$k}} = $1;
1554              
1555 3         11 $ns{$dt{$k}} =~ s/T/ /;
1556 3         11 $ns{$dt{$k}} =~ s/\.\d+Z$//;
1557 3         8 $ns{$dt{$k}} =~ s/Z$//;
1558             }
1559             }
1560              
1561 2         8 return \%ns;
1562             }
1563              
1564              
1565             =head2 get_ns_info
1566              
1567             Get information about the specified nameserver
1568              
1569             =cut
1570              
1571             sub get_ns_info {
1572 6     6 1 38 my ( $self, $params ) = @_;
1573              
1574 6 50       16 return ( 0, 0, 'no ns' ) unless $params->{ns};
1575              
1576 6   100     17 my $ext = $$params{extension} || '';
1577              
1578 6 100       15 $ext = "\n \n$ext " if $ext;
1579              
1580 6         11 my $cltrid = get_cltrid();
1581              
1582 6         29 my $body = <
1583             $$self{urn}{head}
1584            
1585            
1586            
1587             $$params{ns}
1588            
1589             $ext
1590             $cltrid
1591            
1592            
1593             NSINFO
1594              
1595 6         12 my $content = $self->req( $body, 'get_ns_info' );
1596              
1597 6 50       35 if ( $content =~ /result code=['"](\d+)['"]/ ) {
1598 6         20 my $rcode = $1 + 0;
1599              
1600 6         9 my $msg = '';
1601 6 50       54 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1602 6         13 $msg = $1;
1603             }
1604              
1605 6         10 my $ns = {};
1606              
1607             # вытягиваем смысловую часть и парсим
1608 6 100       20 if ( $content =~ /(.+)<\/resData>/s ) {
1609 2         6 my $rdata = $1;
1610              
1611 2         9 $ns = $self->get_ns_info_rdata( $rdata );
1612             }
1613              
1614 6 50       30 return wantarray ? ( $ns, $rcode, $msg ) : $ns;
1615             }
1616              
1617 0         0 return 0;
1618             }
1619              
1620             =head2 update_ns
1621              
1622             Change the data of the specified nameserver
1623              
1624             OUTPUT:
1625             see L.
1626              
1627             =cut
1628              
1629             sub update_ns {
1630 16     16 1 28 my ( $self, $params ) = @_;
1631              
1632 16 50       28 return ( 0, 0, 'no ns' ) unless $$params{ns};
1633              
1634 16         20 my $add = '';
1635              
1636 16 100       31 if ( $params->{add} ) {
1637 11 100 66     39 if ( $params->{add}{ips} and ref $params->{add}{ips} ) {
1638 7         9 foreach my $ip ( @{$params->{add}{ips}} ) {
  7         16  
1639 7 100       33 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1640 5         14 $add .= ' '.$ip."\n";
1641             }
1642             else {
1643 2         7 $add .= ' '.$ip."\n";
1644             }
1645             }
1646             }
1647              
1648 11 100       20 if ( $params->{add}{statuses} ) {
1649 4         12 $add .= $self->update_statuses_add( 'host', $params->{add}{statuses} );
1650             }
1651             }
1652              
1653 16 100       25 if ( $add ) {
1654 11         19 $add = "\n$add ";
1655             }
1656             else {
1657 5         10 $add = '';
1658             }
1659              
1660 16         18 my $rem = '';
1661              
1662 16 100       26 if ( $params->{rem} ) {
1663 5 100 66     18 if ( defined $params->{rem}{ips} and ref $params->{rem}{ips} ) {
1664 3         4 foreach my $ip ( @{$params->{rem}{ips}} ) {
  3         4  
1665 4 100       16 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1666 3         6 $rem .= ' '.$ip."\n";
1667             }
1668             else {
1669 1         3 $rem .= ' '.$ip."\n";
1670             }
1671             }
1672             }
1673              
1674 5 100       10 if ( $params->{rem}{statuses} ) {
1675 2         9 $rem .= $self->update_statuses_rem( 'host', $params->{rem}{statuses} );
1676             }
1677             }
1678              
1679 16 100       26 if ( $rem ) {
1680 5         8 $rem = "\n$rem ";
1681             }
1682             else {
1683 11         13 $rem = "";
1684             }
1685              
1686 16         19 my $chg = '';
1687              
1688 16 100       25 if ( $params->{chg} ) {
1689 1 50       3 if ( $params->{chg}{new_name} ) {
1690 0         0 $chg .= " " . $$params{chg}{new_name} . "\n";
1691             }
1692             }
1693              
1694 16 50       33 if ( $chg ) {
    100          
1695 0         0 $chg = "\n$chg \n";
1696             }
1697             elsif ( !$params->{no_empty_chg} ) {
1698 3         3 $chg = "";
1699             }
1700              
1701 16   100     31 my $ext = $$params{extension} || '';
1702              
1703 16 100       31 $ext = "\n \n$ext " if $ext;
1704              
1705 16         26 my $cltrid = get_cltrid();
1706              
1707 16         85 my $body = <
1708             $$self{urn}{head}
1709            
1710            
1711            
1712             $$params{ns}
1713             $add
1714             $rem
1715             $chg
1716            
1717             $ext
1718             $cltrid
1719            
1720            
1721             UPDATENS
1722              
1723 16         35 return $self->simple_request( $body, 'update_ns' );
1724             }
1725              
1726              
1727             =head2 delete_ns
1728              
1729             Remove nameserver from the registry.
1730              
1731             OUTPUT:
1732             see L.
1733              
1734             =cut
1735              
1736             sub delete_ns {
1737 5     5 1 10 my ( $self, $params ) = @_;
1738              
1739 5 50       14 return ( 0, 0, 'no ns' ) unless $$params{ns};
1740              
1741 5   100     14 my $ext = $$params{extension} || '';
1742              
1743 5 100       12 $ext = "\n \n$ext " if $ext;
1744              
1745 5         9 my $cltrid = get_cltrid();
1746              
1747 5         26 my $body = <
1748             $$self{urn}{head}
1749            
1750            
1751            
1752             $$params{ns}
1753            
1754             $ext
1755             $cltrid
1756            
1757            
1758             DELNS
1759              
1760 5         10 return $self->simple_request( $body, 'delete_ns' );
1761             }
1762              
1763             # TODO: move
1764             sub check_domains_rdata {
1765 3     3 0 7 my ( undef, $rdata ) = @_;
1766              
1767 3         4 my %domlist;
1768              
1769 3         59 my @aa = $rdata =~ /\s*([^<>]+<\/domain:name>(?:\s*[^<>]+<\/domain:reason>)?)\s*<\/domain:cd>/sg;
1770              
1771 3         10 foreach my $a ( @aa ) {
1772 26 50       81 if ( $a =~ /([^<>]+)<\/domain:name>/ ) {
1773 26         41 my $dm = lc($2);
1774              
1775 26         70 $domlist{$dm} = { avail => $1 }; # no utf8, puny only
1776              
1777 26 100       73 if ( $a =~ /([^<>]+)<\/domain:reason>/ ) {
1778 10         24 $domlist{$dm}{reason} = $1;
1779             }
1780             }
1781             }
1782              
1783 3 50       28 if ( $rdata =~ /claims<\/launch:phase>/ ) {
1784             # this is a call with an extension to get the key, if there is one
1785 0 0       0 if ( $rdata =~ /([0-9a-z.\-]+)<\/launch:name>\n?\s*([^<>]+)<\/launch:claimKey>/ ) {
1786 0         0 $domlist{ lc($2) }{claim} = { avail => $1, claimkey => $3 };
1787             }
1788             }
1789              
1790 3 50       15 if ( $rdata =~ /]+>(.+)<\/fee:chkData>/ ) {
1791             # this is a call with the extension draft-brown-epp-fees-02
1792 0         0 my $fee = $1;
1793              
1794 0         0 my @ff = $fee =~ /(.+)<\/fee:cd>/g;
1795              
1796 0         0 foreach my $f ( @ff ) {
1797 0         0 $f =~ /([0-9a-z\-\.])<\/fee:name>.*([0-9\.])<\/fee:fee>/;
1798 0         0 $domlist{ lc($1) }{fee} = { new => $2 }
1799             }
1800             }
1801              
1802 3         10 return \%domlist;
1803             }
1804              
1805             =head2 check_domains
1806              
1807             Check that the domain is available for registration
1808              
1809             =cut
1810              
1811             sub check_domains {
1812 3     3 1 8 my ( $self, $params ) = @_;
1813              
1814 3 50 50     12 return ( 0, 0, 'no domains' ) unless $params->{domains} && scalar( @{$params->{domains}} );
  3         13  
1815              
1816 3         6 my $dms = '';
1817              
1818 3         5 foreach my $dm ( @{$params->{domains}} ) {
  3         9  
1819 26         43 $dms .= "$dm";
1820             }
1821              
1822 3   100     14 my $ext = $$params{extension} || '';
1823              
1824 3 100       9 $ext = "\n \n$ext " if $ext;
1825              
1826 3         7 my $cltrid = get_cltrid();
1827              
1828 3         29 my $body = <
1829             $$self{urn}{head}
1830            
1831            
1832            
1833             $dms
1834            
1835             $ext
1836             $cltrid
1837            
1838            
1839             CHECKDOMS
1840              
1841 3   50     11 my $answ = $self->req( $body, 'check_domains' ) // '';
1842              
1843 3         13 my $rcode = '';
1844 3         5 my $msg = '';
1845              
1846 3 50       20 if ( $answ =~ /
1847 3         11 $rcode = $1 + 0;
1848             }
1849              
1850 3 50       17 if ( $answ =~ /]*>([^<>]+)<\/msg>/ ) {
1851 3         13 $msg = $1;
1852             }
1853              
1854 3 50       23 if ( $answ =~ /(.+)<\/resData>/s ) {
1855 3   50     17 my $rdata = $1 // '';
1856              
1857 3         17 my $domlist = $self->check_domains_rdata( $rdata );
1858              
1859 3 50       20 return wantarray ? ( $domlist, $rcode, $msg ) : $domlist;
1860             }
1861              
1862 0 0       0 return wantarray ? ( 0, $rcode, $msg ) : 0;
1863             }
1864              
1865              
1866             sub create_domain_nss {
1867 14     14 0 22 my ( $self, $params ) = @_;
1868              
1869 14         17 my $nss = '';
1870              
1871 14         15 foreach my $ns ( @{$params->{nss}} ) {
  14         27  
1872 27         57 $nss .= " $ns\n";
1873             }
1874              
1875 14 50       37 $nss = "\n \n$nss " if $nss;
1876              
1877 14         25 return $nss;
1878             }
1879              
1880              
1881             sub create_domain_authinfo {
1882 21     21 0 33 my ( $self, $params ) = @_;
1883              
1884             # Some providers require an empty authinfo, but no
1885 21 50       33 if ( exists $params->{authinfo} ) {
1886 21         49 return "\n \n $$params{authinfo}\n ";
1887             }
1888              
1889 0         0 return "\n \n \n ";
1890             }
1891              
1892             # DNSSEC extension
1893              
1894             sub create_domain_ext {
1895 21     21 0 28 my ( $self, $params ) = @_;
1896              
1897 21         24 my $ext = '';
1898              
1899 21 50       39 if ( $params->{dnssec} ) {
1900 0         0 my $dsdata = '';
1901 0         0 foreach my $raw ( @{$params->{dnssec}} ) {
  0         0  
1902 0         0 my $ds = '';
1903 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
1904 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
1905 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
1906 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
1907              
1908 0 0       0 $dsdata .= " \n$ds \n" if $ds;
1909             }
1910              
1911 0 0       0 $ext = qq| \n$dsdata \n|
1912             if $dsdata;
1913             }
1914              
1915 21         35 return $ext;
1916             }
1917              
1918              
1919             =head2 create_domain
1920              
1921             Domain registration.
1922              
1923             OUTPUT:
1924             see L.
1925              
1926             =cut
1927              
1928             sub create_domain {
1929 21     21 1 34 my ( $self, $params ) = @_;
1930              
1931 21 50       43 return ( 0, 0, 'no dname' ) unless $params->{dname};
1932              
1933 21         25 my $nss = '';
1934 21 100 100     43 if ( $params->{nss} && scalar @{$params->{nss}} ) {
  15         37  
1935 14         31 $nss = $self->create_domain_nss( $params );
1936             }
1937              
1938 21         31 my $cont = '';
1939             # 1. There is a zone without an owner, but with admin :)
1940             # 2. Verisign Core server -- without all contacts
1941 21 100       41 $cont .= qq|\n $$params{reg_id}| if $$params{reg_id};
1942              
1943 21         32 foreach my $t ( 'tech', 'admin', 'billing' ) {
1944 63 100       110 if ( $$params{$t.'_id'} ) {
1945 36 50       89 $$params{$t.'_id'} = [ $$params{$t.'_id'} ] unless ref $$params{$t.'_id'};
1946              
1947 36         33 foreach my $c ( @{$$params{$t.'_id'}} ) {
  36         57  
1948 36         64 $cont .= qq|\n $c|;
1949             }
1950             }
1951             }
1952              
1953 21         29 my $descr = ''; # tcinet registry
1954 21 50       35 if ( $params->{descr} ) {
1955 0 0       0 $params->{descr} = [ $params->{descr} ] unless ref $params->{descr};
1956              
1957 0         0 $descr .= "\n $_" for @{$params->{descr}};
  0         0  
1958             }
1959              
1960 21         39 my $authinfo = $self->create_domain_authinfo( $params );
1961              
1962 21   100     53 my $ext = $params->{extension} || '';
1963              
1964 21         39 $ext .= $self->create_domain_ext( $params );
1965              
1966 21 100       38 $ext = "\n \n$ext " if $ext;
1967              
1968 21         36 my $cltrid = get_cltrid();
1969              
1970 21         145 my $body = <
1971             $$self{urn}{head}
1972            
1973            
1974            
1975             $$params{dname}
1976             $$params{period}$nss$cont$authinfo$descr
1977            
1978             $ext
1979             $cltrid
1980            
1981            
1982             CREATEDOM
1983              
1984 21         43 return $self->simple_request( $body, 'create_domain' );
1985             }
1986              
1987             # For replace in children class
1988             sub get_domain_spec_rdata {
1989 10     10 0 19 return {};
1990             }
1991              
1992              
1993             sub get_domain_info_rdata {
1994 10     10 0 19 my ( $self, $rdata ) = @_;
1995              
1996 10         14 my $info = {};
1997              
1998 10         43 ( $info->{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
1999 10         20 $info->{dname} = lc $info->{dname};
2000              
2001             #
2002 10         47 my @ss = $rdata =~ //g;
2003             # No reason supplied
2004 10         24 my @aa = $rdata =~ /]+>[^<>]+<\/domain:status>/g;
2005 10 50       20 if ( scalar @aa ) {
2006 0         0 foreach my $row ( @aa ) {
2007 0 0       0 if ( $row =~ /([^<>]+)<\/domain:status>/ ) {
2008 0         0 $info->{statuses}{$1} = $2;
2009             }
2010             }
2011             }
2012             else {
2013 10         35 $info->{statuses}{$_} = '+' for @ss;
2014             }
2015              
2016 10 100       31 if ( $rdata =~ /([^<>]+)<\/domain:registrant>/ ) {
2017             # One of the .ua zones uses admin instead of owner
2018 3         8 $info->{reg_id} = $1;
2019             }
2020              
2021 10         31 my @cc = $rdata =~ /[^<>]+<\/domain:contact>/g;
2022 10         16 foreach my $row ( @cc ) {
2023 9 50       29 if ( $row =~ /([^<>]+)<\/domain:contact>/ ) {
2024 9         25 $info->{ lc($1) . '_id' } = $2;
2025             }
2026             }
2027              
2028 10 50       25 if ( $rdata =~ // ) {
2029 0         0 $info->{descr} = [ $rdata =~ /([^<>]+)<\/domain:description>/g ];
2030             }
2031              
2032 10 50       27 if ( $rdata =~ // ) {
2033 10         54 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:hostObj>/g ];
2034             }
2035              
2036 10 50 33     36 unless ( $info->{nss} or $rdata !~ // ) {
2037             # some providers use the old variant for some zones, example: irrp for ph
2038             # this is a rare option, so it is made separately and not built into the previous regexp
2039 0         0 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:hostName>/g ];
2040             }
2041              
2042 10 0 33     33 unless ( $info->{nss} or $rdata !~ // or scalar @{$info->{nss}} ) {
  0   50     0  
2043             # ещё 1 древний вариант, наверное самый старый
2044 0         0 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:ns>/g ];
2045             }
2046              
2047 10 50       25 if ( $info->{nss} ) {
2048 10         14 $info->{nss} = [ map{ lc $_ } @{$info->{nss}} ];
  20         46  
  10         23  
2049             }
2050              
2051             # Domain-based nss
2052 10 100       38 if ( $rdata =~ // ) {
2053 4         20 $info->{hosts} = [ $rdata =~ /([^<>]+)<\/domain:host>/g ];
2054 4         6 $info->{hosts} = [ map{ lc $_ } @{$info->{hosts}} ];
  5         10  
  4         10  
2055             }
2056              
2057             # owner, ...
2058 10         45 foreach my $k ( keys %id ) {
2059 50 100       681 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2060 30         105 $info->{$id{$k}} = $1;
2061             }
2062             }
2063              
2064             # dates
2065 10         32 foreach my $k ( keys %dt ) {
2066 60 100       758 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2067 30         59 $info->{$dt{$k}} = cldate( $1 );
2068             }
2069             }
2070              
2071 10 100       59 if ( $rdata =~ /authInfo.+([^<>]+)<\/domain:pw>.+authInfo/s ) {
2072 7         18 ( $info->{authinfo} ) = $1;
2073              
2074 7         13 $info->{authinfo} =~ s/>/>/g;
2075 7         10 $info->{authinfo} =~ s/</
2076 7         9 $info->{authinfo} =~ s/&/&/g;
2077             }
2078              
2079 10         41 ( $info->{roid} ) = $rdata =~ /([^<>]+)<\/domain:roid>/;
2080              
2081 10         31 my $spec = $self->get_domain_spec_rdata( $rdata );
2082 10         25 $info->{$_} = $spec->{$_} for keys %$spec;
2083              
2084 10         30 return $info;
2085             }
2086              
2087             # For replace in children
2088             sub get_domain_spec_ext {
2089 7     7 0 12 return {};
2090             }
2091              
2092             =head2 get_domain_info
2093              
2094             The main information on the domain
2095              
2096             =cut
2097              
2098             sub get_domain_info {
2099 14     14 1 20 my ( $self, $params ) = @_;
2100              
2101 14 50       33 unless ( $$params{dname} ) {
2102 0 0       0 return wantarray ? ( 0, 0, 'no dname') : 0;
2103             }
2104              
2105 14 50       32 my $pw = $$params{authinfo} ? "\n \n $$params{authinfo}\n " : '';
2106              
2107 14 50       28 my $hosts_type = $$params{hosts} ? ' hosts="'.$$params{hosts}.'"' : '';
2108              
2109 14   100     31 my $ext = $$params{extension} || '';
2110              
2111 14 100       36 $ext = "\n \n$ext " if $ext;
2112              
2113 14         24 my $cltrid = get_cltrid();
2114              
2115 14         73 my $body = <
2116             $$self{urn}{head}
2117            
2118            
2119            
2120             $$params{dname}$pw
2121            
2122             $ext
2123             $cltrid
2124            
2125            
2126             DOMINFO
2127              
2128 14         32 my $answ = $self->req( $body, 'domain_info' );
2129              
2130 14 50 33     89 if ( $answ && $answ =~ // ) {
2131 14         39 my $rcode = $1 + 0;
2132              
2133 14         21 my $msg = '';
2134 14 50       241 if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
2135 14         28 $msg = $1;
2136             }
2137              
2138 14 100       41 if ( $rcode != 1000 ) {
2139 4 100       18 if ( $answ =~ /(.+)<\/reason>/s ) {
2140             # for details
2141 2         6 $msg .= '; ' . $1;
2142             }
2143              
2144 4 50       12 if ( $answ =~ /(.+)<\/oxrs:xcp>/s ) {
2145             # for oxrs details
2146 0         0 $msg .= '; ' . $1;
2147             }
2148              
2149 4 50       23 return wantarray ? ( 0, $rcode, $msg ) : 0;
2150             }
2151              
2152 10         17 my $info = {};
2153              
2154             # pull out the main part and parse
2155 10 50       61 if ( $answ =~ /(.+)<\/resData>/s ) {
2156 10   50     37 my $rdata = $1 // '';
2157              
2158 10         25 $info = $self->get_domain_info_rdata( $rdata );
2159             }
2160              
2161 10 100       55 if ( $answ =~ /(.+)<\/extension>/s ) {
2162 7         22 my $rdata = $1;
2163              
2164 7         17 my @st = $rdata =~ //g;
2165 7         11 $info->{statuses}{$_} = '+' for @st;
2166              
2167 7         44 my @est = $rdata =~ /([^<>]+<\/rgp:rgpStatus>)/g;
2168              
2169 7         11 foreach my $e ( @est ) {
2170 14         51 my ( $st, $descr ) = $e =~ /([^<>]+)<\/rgp:rgpStatus>/;
2171              
2172 14 50       42 if ( $descr =~ /^endDate=/ ) {
2173 14         32 $descr =~ s/T/ /;
2174 14         34 $descr =~ s/\.\d+Z$//;
2175 14         24 $descr =~ s/Z$//;
2176             }
2177 14         38 $info->{statuses}{$st} = $descr;
2178             }
2179              
2180 7 50       19 if ( $rdata =~ /secDNS:infData/ ) {
2181 0         0 $info->{dnssec} = [];
2182              
2183 0         0 my @dsdata = $rdata =~ /(.+?)<\/secDNS:dsData>/g;
2184 0         0 foreach my $sdata ( @dsdata ) {
2185 0         0 my %one_raw;
2186 0         0 ( $one_raw{keytag} ) = $sdata =~ /(\d+)<\/secDNS:keyTag>/;
2187 0         0 ( $one_raw{alg} ) = $sdata =~ /(\d+)<\/secDNS:alg>/;
2188 0         0 ( $one_raw{digtype} ) = $sdata =~ /(\d+)<\/secDNS:digestType>/;
2189 0         0 ( $one_raw{digest} ) = $sdata =~ /([A-Za-z0-9]+)<\/secDNS:digest>/;
2190              
2191 0 0       0 if ( $sdata =~ /(.+)<\/secDNS:keyData>/s ) {
2192 0         0 my $kdata = $1;
2193              
2194 0         0 $one_raw{keydata} = {};
2195 0         0 ( $one_raw{keydata}{flags} ) = $kdata =~ /(\d+)<\/secDNS:flags>/;
2196 0         0 ( $one_raw{keydata}{protocol} ) = $kdata =~ /(\d+)<\/secDNS:protocol>/;
2197 0         0 ( $one_raw{keydata}{alg} ) = $kdata =~ /(\d+)<\/secDNS:alg>/;
2198 0         0 ( $one_raw{keydata}{pubkey} ) = $kdata =~ /([^<>]+)<\/secDNS:pubKey>/;
2199             }
2200              
2201 0         0 push @{$$info{dnssec}}, \%one_raw;
  0         0  
2202             }
2203             }
2204              
2205 7         14 my $spec = $self->get_domain_spec_ext( $rdata );
2206 7         20 $info->{$_} = $spec->{$_} for keys %$spec;
2207             }
2208              
2209 10 50       54 return wantarray ? ( $info, $rcode, $msg ) : $info;
2210             }
2211              
2212 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0;
2213             }
2214              
2215              
2216             =head2 renew_domain
2217              
2218             Domain registration renewal for N years.
2219              
2220             OUTPUT:
2221             see L.
2222              
2223             =cut
2224              
2225             sub renew_domain {
2226 11     11 1 20 my ( $self, $params ) = @_;
2227              
2228 11 50 33     57 return ( 0, 0, 'no params' ) unless $$params{dname} && $$params{period} && $$params{exp_date};
      33        
2229              
2230 11   100     29 my $ext = $params->{extension} || '';
2231              
2232 11 100       25 $ext = "\n \n$ext " if $ext;
2233              
2234 11         20 my $cltrid = get_cltrid();
2235              
2236 11         57 my $body = <
2237             $$self{urn}{head}
2238            
2239            
2240            
2241             $$params{dname}
2242             $$params{exp_date}
2243             $$params{period}
2244            
2245             $ext
2246             $cltrid
2247            
2248            
2249             RENEWDOM
2250              
2251 11         23 return $self->simple_request( $body, 'renew_domain' );
2252             }
2253              
2254             # replaced in DrsUa
2255             sub update_domain_add_nss {
2256 9     9 0 13 my ( undef, $params ) = @_;
2257              
2258 9         12 my $add = " \n";
2259              
2260 9         11 foreach my $ns ( @{$$params{add}{nss}} ) {
  9         19  
2261 9         28 $add .= " $ns\n";
2262             }
2263              
2264 9         14 $add .= " \n";
2265              
2266 9         16 return $add;
2267             }
2268              
2269              
2270             sub update_domain_rem_nss {
2271 6     6 0 11 my ( undef, $params ) = @_;
2272              
2273 6         11 my $rem = " \n";
2274              
2275 6         6 foreach my $ns ( @{$$params{rem}{nss}} ) {
  6         12  
2276 6         17 $rem .= " $ns\n";
2277             }
2278              
2279 6         11 $rem .= " \n";
2280              
2281 6         10 return $rem;
2282             }
2283              
2284              
2285             sub update_domain_ext {
2286 39     39 0 51 my ( undef, $params ) = @_;
2287              
2288 39         42 my $ext = '';
2289              
2290 39         45 my $rem_ds = '';
2291 39 50 66     85 if ( $params->{rem} && $params->{rem}{dnssec} ) {
2292 0         0 foreach my $raw ( @{$params->{rem}{dnssec}} ) {
  0         0  
2293 0         0 my $ds = '';
2294 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
2295 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
2296 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
2297 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
2298              
2299 0 0       0 $rem_ds .= " \n$ds \n" if $ds;
2300             }
2301              
2302 0 0       0 $rem_ds = " \n$rem_ds \n" if $rem_ds;
2303             }
2304              
2305 39         56 my $add_ds = '';
2306 39 50 66     93 if ( $params->{add} && $params->{add}{dnssec} ) {
2307 0         0 foreach my $raw ( @{$params->{add}{dnssec}} ) {
  0         0  
2308 0         0 my $ds = '';
2309 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
2310 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
2311 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
2312 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
2313              
2314 0 0       0 $add_ds .= " \n$ds \n" if $ds;
2315             }
2316              
2317 0 0       0 $add_ds = " \n$add_ds \n" if $add_ds;
2318             }
2319              
2320 39 50 33     128 if ( $rem_ds || $add_ds ) {
2321 0         0 $ext .= qq|
2322             \n|;
2323 0         0 $ext .= $rem_ds;
2324 0         0 $ext .= $add_ds;
2325 0         0 $ext .= " \n";
2326             }
2327              
2328 39         65 return $ext;
2329             }
2330              
2331             =head2 update_domain
2332              
2333             To update domain data: contact ids, nss, hosts, statuses.
2334              
2335             OUTPUT:
2336             see L.
2337              
2338             =cut
2339              
2340             sub update_domain {
2341 39     39 1 70 my ( $self, $params ) = @_;
2342              
2343 39 50       78 return ( 0, 0, 'no params' ) unless ref $params;
2344              
2345 39 50       71 return ( 0, 0, 'no dname' ) unless $params->{dname};
2346              
2347 39         53 my $nm = 'update_domain';
2348              
2349 39         39 my $add = '';
2350 39 100       70 if ( ref $$params{add} ) {
2351 21 50 66     64 if ( $$params{add}{nss} && ref $$params{add}{nss} && scalar( @{$$params{add}{nss}} ) ) {
  9   100     22  
2352 9         25 $add .= $self->update_domain_add_nss( $params );
2353              
2354 9         12 $nm .= '_add_ns';
2355             }
2356              
2357 21         34 foreach my $t ( 'admin', 'billing', 'tech' ) {
2358 63 100       120 if ( $$params{add}{$t.'_id'} ) {
2359 2 50       8 $$params{add}{$t.'_id'} = [ $$params{add}{$t.'_id'} ] unless ref $$params{add}{$t.'_id'};
2360              
2361 2         3 foreach my $c ( @{$$params{add}{$t.'_id'}} ) {
  2         5  
2362 2         6 $add .= qq| $c\n|;
2363             }
2364             }
2365             }
2366              
2367 21 100       41 if ( $params->{add}{statuses} ) {
2368 10         25 $add .= $self->update_statuses_add( 'domain', $params->{add}{statuses} );
2369              
2370 10         20 $nm .= '_add_status';
2371             }
2372             }
2373              
2374 39 100       52 if ( $add ) {
2375 21         37 $add = "\n$add ";
2376             }
2377             else {
2378 18         29 $add = '';
2379             }
2380              
2381 39         57 my $chg = '';
2382 39 100       61 if ( ref $$params{chg} ) {
2383 9 100       21 if ( $$params{chg}{reg_id} ) {
2384 3         11 $chg .= ' ' . $$params{chg}{reg_id} . "\n";
2385              
2386 3         5 $nm .= '_chg_cont';
2387             }
2388              
2389 9 100       16 if ( $$params{chg}{authinfo} ) {
2390 6         20 $chg .= " \n ".$$params{chg}{authinfo}."\n \n";
2391              
2392 6         8 $nm .= '_chg_key';
2393             }
2394              
2395 9 50       15 if ( $params->{chg}{descr} ) {
2396 0 0       0 $params->{chg}{descr} = [ $params->{chg}{descr} ] unless ref $params->{chg}{descr};
2397              
2398 0         0 $chg .= " $_\n" foreach @{$params->{chg}{descr}};
  0         0  
2399              
2400 0         0 $nm .= '_chg_descr';
2401             }
2402             }
2403              
2404 39 100       59 if ( $chg ) {
2405 9         18 $chg = "\n$chg ";
2406             }
2407             else {
2408 30         39 $chg = '';
2409             }
2410              
2411 39         41 my $rem = '';
2412 39 100       63 if ( $$params{rem} ) {
2413 10 50 66     37 if ( $$params{rem}{nss} && ref $$params{rem}{nss} && scalar( @{$$params{rem}{nss}} ) ) {
  6   100     17  
2414 6         19 $rem .= $self->update_domain_rem_nss( $params );
2415              
2416 6         9 $nm .= '_del_ns';
2417             }
2418              
2419 10         17 foreach my $t ( 'admin', 'billing', 'tech' ) {
2420 30 100       57 if ( $$params{rem}{$t.'_id'} ) {
2421 2 50       7 $$params{rem}{$t.'_id'} = [ $$params{rem}{$t.'_id'} ] unless ref $$params{rem}{$t.'_id'};
2422              
2423 2         3 foreach my $c ( @{$$params{rem}{$t.'_id'}} ) {
  2         5  
2424 2         5 $rem .= qq| $c\n|;
2425             }
2426             }
2427             }
2428              
2429 10 100       19 if ( $$params{rem}{statuses} ) {
2430 2         9 $rem .= $self->update_statuses_rem( 'domain', $$params{rem}{statuses} );
2431              
2432 2         4 $nm .= '_del_status';
2433             }
2434             }
2435              
2436 39 100       54 if ( $rem ) {
2437 10         19 $rem = "\n$rem ";
2438             }
2439             else {
2440 29         36 $rem = '';
2441             }
2442              
2443 39   100     89 my $ext = $$params{extension} || '';
2444              
2445 39         76 $ext .= $self->update_domain_ext( $params );
2446              
2447 39 100       82 $ext = "\n \n$ext " if $ext;
2448              
2449 39         70 my $cltrid = get_cltrid();
2450              
2451 39         219 my $body = <
2452             $$self{urn}{head}
2453            
2454            
2455            
2456             $$params{dname}
2457             $add
2458             $rem
2459             $chg
2460            
2461             $ext
2462             $cltrid
2463            
2464            
2465             UPDDOM
2466              
2467 39         82 return $self->simple_request( $body, $nm );
2468             }
2469              
2470              
2471             =head2 transfer
2472              
2473             Domain transfers: to us, from us, reject transfers.
2474              
2475             =cut
2476              
2477             sub transfer {
2478 7     7 1 12 my ( $self, $params ) = @_;
2479              
2480 7 50       15 return ( 0, 0, 'no dname' ) unless $params->{dname};
2481              
2482 7 50 33     43 return ( 0, 0, 'no op[eration]' ) unless $params->{op} && $params->{op} =~ /query|request|cancel|approve|reject|usertransfer/;
2483              
2484 7         11 my $pw = '';
2485 7 100       14 if ( defined $params->{authinfo} ) {
2486             # 0 & undef are differents
2487 2         5 $pw = "\n \n $$params{authinfo}\n ";
2488             }
2489              
2490 7         8 my $per = '';
2491 7 100       14 if ( defined $params->{period} ) {
2492             # 0 & undef is different
2493 4         9 $per = qq|\n $$params{period}|;
2494             }
2495              
2496             # special parameters for very original registries
2497 7   50     20 my $spec = $$params{addition} // '';
2498              
2499 7   50     17 my $ext = $$params{extension} || '';
2500              
2501 7 50       45 $ext = "\n \n$ext " if $ext;
2502              
2503 7         12 my $cltrid = get_cltrid();
2504              
2505 7         40 my $body = <
2506             $$self{urn}{head}
2507            
2508            
2509            
2510             $$params{dname}$per$pw$spec
2511            
2512             $ext
2513             $cltrid
2514            
2515            
2516             TRANS
2517              
2518 7         17 my $answ = $self->req( $body, $$params{op}.'_transfer' );
2519              
2520 7 50       33 if ( $answ =~ // ) {
2521 7         19 my $rcode = $1 + 0;
2522              
2523 7         8 my $msg = '';
2524 7 50       58 if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
2525 7         15 $msg = $1;
2526              
2527 7 50       36 if ( $answ =~ /]*>(.+)<\/text>.+\/result>/s ) {
2528 0         0 $msg .= '; ' . $1;
2529             }
2530              
2531 7 50       17 if ( $answ =~ /([^<>]+)<\/reason>/ ) {
2532 0         0 $msg .= '; ' . $1;
2533             }
2534             }
2535              
2536 7         11 my $info = {}; # for data
2537              
2538             # pull out the main part and parse
2539 7 100       21 if ( $answ =~ /(.+)<\/resData>/s ) {
2540 2         5 my $rdata = $1;
2541              
2542 2         7 ( $info->{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
2543              
2544 2         10 ( $info->{trstatus} ) = $rdata =~ /([^<>]+)<\/domain:trStatus>/;
2545              
2546             # owner, ...
2547 2         8 foreach my $k ( keys %id ) {
2548 10 100       135 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2549 4         17 $info->{$id{$k}} = $1;
2550             }
2551             }
2552              
2553             # dates
2554 2         8 foreach my $k ( keys %dt ) {
2555 12 100       155 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2556 6         19 $info->{$dt{$k}} = $1;
2557              
2558 6         18 $info->{$dt{$k}} =~ s/T/ /;
2559 6         18 $info->{$dt{$k}} =~ s/\.\d+Z$//;
2560 6         14 $info->{$dt{$k}} =~ s/Z$//;
2561             }
2562             }
2563             }
2564              
2565 7         30 ( $info->{cltrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
2566 7         36 ( $info->{svtrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
2567              
2568 7 50       30 return wantarray ? ( $info, $rcode, $msg ) : $info;
2569             }
2570              
2571 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0;
2572             }
2573              
2574              
2575             =head2 delete_domain
2576              
2577             Deleting a domain.
2578              
2579             OUTPUT:
2580             see L.
2581              
2582             =cut
2583              
2584             sub delete_domain {
2585 10     10 1 14 my ( $self, $params ) = @_;
2586              
2587 10 50       25 return ( 0, 0, 'no dname' ) unless $params->{dname};
2588              
2589 10   100     25 my $ext = $$params{extension} || '';
2590              
2591 10 100       26 $ext = "\n \n$ext " if $ext;
2592              
2593 10         15 my $cltrid = get_cltrid();
2594              
2595 10         46 my $body = <
2596             $$self{urn}{head}
2597            
2598            
2599            
2600             $$params{dname}
2601            
2602             $ext
2603             $cltrid
2604            
2605            
2606             DELDOM
2607              
2608 10         23 return $self->simple_request( $body, 'delete_domain' );
2609             }
2610              
2611              
2612             # Parse resData from req poll
2613              
2614             sub req_poll_rdata {
2615 0     0 0 0 my ( $self, $rdata ) = @_;
2616              
2617 0         0 my %info;
2618              
2619 0 0       0 if ( $rdata =~ /^\s*
    0          
    0          
    0          
    0          
    0          
2620 0         0 $info{upd_del} = {};
2621 0         0 ( $info{upd_del}{result}, $info{upd_del}{contact} ) =
2622             $rdata =~ /([^<>]+)<\/contact:id>/;
2623             }
2624              
2625             elsif ( $rdata =~ /\s*
2626 0         0 ( $info{ns} ) = $rdata =~ m|([^<>]+)|s;
2627 0         0 $info{ns} = lc $info{ns};
2628              
2629 0         0 ( $info{roid} ) = $rdata =~ m|([^<>]+)|s;
2630              
2631 0         0 my @sts = $rdata =~ m|()|gs;
2632 0         0 for my $row ( @sts ) {
2633 0 0       0 if ( $row =~ /host:status s="([^"]+)"/ ) {
2634 0         0 $info{statuses}{$1} = '+';
2635             }
2636             }
2637              
2638 0         0 my @ips = $rdata =~ m|([^<>]+)|gs;
2639 0         0 $info{ips} = [];
2640 0         0 for my $row ( @ips ) {
2641 0 0       0 if ( $row =~ m|host:addr ip="v\d">([^<>]+)
2642 0         0 push @{$info{ips}}, $1;
  0         0  
2643             }
2644             }
2645             # owner, ...
2646 0         0 foreach my $k ( keys %id ) {
2647 0 0       0 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
2648 0         0 $info{$id{$k}} = $1;
2649             }
2650             }
2651             # dates
2652 0         0 foreach my $k ( keys %dt ) {
2653 0 0       0 if ( $rdata =~ m|([^<>]+)| ) {
2654 0         0 $info{$dt{$k}} = cldate( $1 );
2655             }
2656             }
2657             }
2658              
2659             elsif ( $rdata =~ /^\s*
2660 0         0 $info{create} = {};
2661 0         0 ( $info{create}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
2662              
2663 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:crDate>/ ) {
2664 0         0 $info{create}{date} = IO::EPP::Base::cldate( $1 );
2665             }
2666             }
2667              
2668             elsif ( $rdata =~ /^\s*
2669 0         0 $info{renew} = {};
2670 0         0 ( $info{renew}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
2671             }
2672              
2673             elsif ( $rdata =~ /^\s*
2674 0         0 $info{transfer} = {};
2675 0         0 ( $info{transfer}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
2676 0         0 ( $info{transfer}{status} ) = $rdata =~ /([^<>]+)<\/domain:trStatus>/;
2677              
2678             # sender, requestor
2679 0         0 foreach my $k ( keys %id ) {
2680 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2681 0         0 $info{transfer}{$id{$k}} = $1;
2682             }
2683             }
2684             # dates
2685 0         0 foreach my $k ( keys %dt ) {
2686 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2687 0         0 $info{transfer}{$dt{$k}} = IO::EPP::Base::cldate( $1 );
2688             }
2689             }
2690             }
2691              
2692             elsif ( $rdata =~ /^\s*
2693 0         0 $info{upd_del} = {};
2694 0         0 ( $info{upd_del}{result}, $info{upd_del}{dname} ) =
2695             $rdata =~ /([^<>]+)<\/domain:name>/;
2696              
2697 0 0       0 if ( $rdata =~ /(.+?)<\/domain:paTRID>/s ) {
2698 0         0 my $trids = $1;
2699 0 0       0 if ( $trids =~ /([0-9A-Za-z]+)<\/clTRID>/ ) {
2700 0         0 $info{upd_del}{cltrid} = $1;
2701             }
2702 0 0       0 if ( $trids =~ /([0-9A-Za-z\-]+)<\/svTRID>/ ) {
2703 0         0 $info{upd_del}{svtrid} = $1;
2704             }
2705             }
2706              
2707 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:paDate>/ ) {
2708 0         0 $info{upd_del}{date} = IO::EPP::Base::cldate( $1 );
2709             }
2710             }
2711              
2712             else {
2713 0         0 return ( 0, 'New poll message type!' );
2714             }
2715              
2716 0         0 return ( \%info, '' );
2717              
2718             }
2719              
2720              
2721             # Parse req poll extension
2722              
2723             # Empty, for replace in children modules
2724              
2725             sub req_poll_ext {
2726 0     0 0 0 return {};
2727             }
2728              
2729             =head2 req_poll
2730              
2731             Get and parse message from poll
2732              
2733             =cut
2734              
2735             sub req_poll {
2736 1     1 1 3 my ( $self, undef ) = @_;
2737              
2738 1         3 my $cltrid = get_cltrid();
2739              
2740 1         4 my $body = <
2741             $$self{urn}{head}
2742            
2743            
2744             $cltrid
2745            
2746            
2747             RPOLL
2748              
2749 1         3 my $answ = $self->req( $body, 'req_poll' );
2750              
2751 1 50 33     10 if ( $answ and $answ =~ // ) {
2752 1         4 my $rcode = $1 + 0;
2753              
2754 1         2 my $msg = '';
2755 1 50       7 if ( $answ =~ /]*>(.+?)<\/msg>.+\/result>/s ) {
2756 1         3 $msg = $1;
2757             }
2758              
2759 1         2 my %info;
2760              
2761 1 50       4 if ( $rcode == 1301 ) {
2762 0 0       0 if ( $answ =~ /(.*)<\/msgQ>/s ) {
2763 0         0 $info{$1} = $2;
2764 0         0 $info{$3} = $4;
2765 0         0 my $q = $5;
2766              
2767 0 0 0     0 if ( $q and $q =~ /(.+)<\/qDate>.*(.+?)<\/msg>/s ) {
    0 0        
    0          
2768 0         0 $info{date} = IO::EPP::Base::cldate( $1 );
2769 0         0 $info{qmsg} = $3;
2770 0         0 $info{qmsg} =~ s/"/"/g;
2771 0 0       0 if ( $info{qmsg} =~ /\[CDATA\[/ ) {
2772 0         0 $info{qmsg} =~ s/
2773 0         0 $info{qmsg} =~ s/\]\]>//;
2774             }
2775             }
2776             # wihout special message
2777             elsif ( $q and $q =~ /(.+)<\/qDate>/s ) {
2778 0         0 $info{date} = IO::EPP::Base::cldate( $1 );
2779 0         0 $info{qmsg} = $q; #
2780             }
2781             elsif ( $q ) {
2782             # not standard
2783 0         0 $info{qmsg} = $q;
2784             }
2785             }
2786              
2787 0 0       0 if ( $answ =~ /(.+?)<\/resData>/s ) {
2788 0         0 my ( $rdata, $err ) = $self->req_poll_rdata( $1 );
2789              
2790 0 0 0     0 if ( !$rdata and $err ) {
2791 0 0       0 return wantarray ? ( 0, 0, $err ) : 0 ;
2792             }
2793              
2794 0         0 $info{$_} = $rdata->{$_} for keys %$rdata;
2795             }
2796              
2797 0 0       0 if ( $answ =~ /(.+?<\/extension>)/s ) {
2798 0         0 $info{ext} = $self->req_poll_ext( $1 );
2799             }
2800              
2801 0         0 ( $info{cltrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
2802 0         0 ( $info{svtrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
2803             }
2804              
2805 1 50       7 return wantarray ? ( \%info, $rcode, $msg ) : \%info;
2806             }
2807              
2808 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
2809             }
2810              
2811             =head2 ask_poll
2812              
2813             Delete message from poll
2814              
2815             =cut
2816              
2817             sub ask_poll {
2818 0     0 1 0 my ( $self, $params ) = @_;
2819              
2820 0 0       0 return ( 0, 0, 'no msg_id' ) unless $params->{msg_id};
2821              
2822 0         0 my $cltrid = get_cltrid();
2823              
2824 0         0 my $body = <
2825             $$self{urn}{head}
2826            
2827            
2828             $cltrid
2829            
2830            
2831             APOLL
2832              
2833 0         0 my $answ = $self->req( $body, 'ask_poll' );
2834              
2835 0 0 0     0 if ( $answ && $answ =~ // ) {
2836 0         0 my $rcode = $1 + 0;
2837              
2838 0         0 my ( $msg ) = $answ =~ /]*>(.+)<\/msg>.+\/result>/s;
2839              
2840 0         0 my %info;
2841              
2842 0 0       0 if ( $answ =~ // ) {
2843 0         0 $info{msg_cnt} = $1;
2844 0         0 $info{msg_id} = $2;
2845             }
2846              
2847 0 0       0 return wantarray ? ( \%info, $rcode, $msg ) : \%info;
2848             }
2849              
2850             # Неконнект или ошибка запроса
2851             # По хорошему надо отделять неконнект от ошибки
2852 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
2853             }
2854              
2855              
2856             =head2 logout
2857              
2858             Close session, disconnect
2859              
2860             No parameters.
2861              
2862             =cut
2863              
2864             sub logout {
2865 77     77 1 3177 my ( $self ) = @_;
2866              
2867 77 50       150 return unless $self->{sock};
2868              
2869 77 50 33     160 unless ( $self->{test} || $self->{sock}->opened() ) {
2870 0         0 delete $self->{sock};
2871              
2872 0         0 return;
2873             }
2874              
2875 77         119 my $cltrid = get_cltrid();
2876              
2877 77         266 my $logout = <
2878             $$self{urn}{head}
2879            
2880            
2881             $cltrid
2882            
2883            
2884             LOGOUT
2885              
2886 77         183 $self->req( $logout, 'logout' );
2887              
2888 77 50       136 unless ( $self->{test} ) {
2889 0         0 close( $self->{sock} );
2890             }
2891              
2892 77         120 delete $self->{sock};
2893              
2894 77         119 return ( undef, '1500', 'ok' );
2895             }
2896              
2897              
2898             sub DESTROY {
2899 77     77   23317 my ( $self ) = @_;
2900              
2901 77         483 local ($!, $@, $^E, $?); # Protection against action-at-distance
2902              
2903 77 100       201 if ( $self->{sock} ) {
2904 73         175 $self->logout();
2905             }
2906              
2907 77 50       736 if ( $self->{log_fh} ) {
2908 0           close $self->{log_fh};
2909              
2910 0           delete $self->{log_fh};
2911             }
2912             }
2913              
2914              
2915             1;
2916              
2917             __END__