File Coverage

blib/lib/IO/EPP/Base.pm
Criterion Covered Total %
statement 765 1082 70.7
branch 327 682 47.9
condition 100 189 52.9
subroutine 55 62 88.7
pod 49 54 90.7
total 1296 2069 62.6


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   1161 use Digest::MD5 qw(md5_hex);
  14         27  
  14         807  
81 14     14   5180 use Time::HiRes qw(time);
  14         14321  
  14         62  
82 14     14   7856 use IO::Socket;
  14         152132  
  14         59  
83 14     14   15390 use IO::Socket::SSL;
  14         745413  
  14         136  
84              
85 14     14   2368 use strict;
  14         40  
  14         318  
86 14     14   73 use warnings;
  14         103  
  14         229412  
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             our $epp_dnssec =
98             'xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:ietf:params:xml:ns:secDNS-1.1 secDNS-1.1.xsd"';
99              
100              
101             our %id = ( crID => 'creater', clID => 'owner', upID => 'updater', reID => 'requestors_id', acID => 'senders_id' );
102             our %dt = ( crDate => 'cre_date', upDate => 'upd_date', trDate => 'trans_date', exDate => 'exp_date', reDate => 'request_date', acDate => 'send_date' );
103              
104              
105             =head1 FUNCTIONS
106              
107             =head2 make_request
108              
109             See L for description
110              
111             An example of working with functions is presented in the synopsis
112              
113             Work checked on CentralNic server
114              
115             INPUT:
116              
117             action name;
118              
119             parameters of query
120              
121             OUTPUT:
122              
123             io::epp object
124              
125             or, in list context:
126              
127             ( full answer with code and message, string with code and message, io::epp object )
128              
129             An Example:
130              
131             my ( $answer, $message, $conn_object ) = make_request( 'hello', \%login_params );
132              
133             =cut
134              
135             sub make_request {
136 92     92 1 84093 my ( $action, $params ) = @_;
137              
138 92         168 my ( $self, $code, $msg, $answ );
139              
140 92 100 66     530 if ( !$params->{tld} && $params->{dname} ) {
141 52         412 ( $params->{tld} ) = $params->{dname} =~ /^[0-9a-z\-]+\.(.+)$/;
142             }
143              
144 92 100       231 unless ( $params->{conn} ) {
145             # Need greate obj and login
146 46         126 ( $self, $code, $msg ) = IO::EPP::Base->new( $params );
147              
148 46 50 33     179 unless ( $code and $code == 1000 ) {
149 0         0 goto END_MR;
150             }
151             }
152             else {
153 46         79 $self = $params->{conn};
154             }
155              
156 92         187 $self->{critical_error} = '';
157              
158 92 50       381 if ( $self->can( $action ) ) {
159 92         286 ( $answ, $code, $msg ) = $self->$action( $params );
160             }
161             else {
162 0         0 $msg = "undefined command <$action>, request cancelled";
163 0         0 $code = 0;
164             }
165              
166             END_MR:
167              
168 92 50       277 $msg .= '; ' . $self->{critical_error} if $self->{critical_error};
169              
170 92         256 my $full_msg = "code: $code\nmsg: $msg";
171              
172 92 100 66     369 $answ = {} unless $answ && ref $answ;
173              
174 92         191 $answ->{code} = $code;
175 92         179 $answ->{msg} = $msg;
176              
177 92 50       435 return wantarray ? ( $answ, $full_msg, $self ) : $answ;
178             }
179              
180              
181             =head2 gen_id
182              
183             Gereration ID for contacts
184              
185             INPUT:
186              
187             no params
188              
189             OUTPUT:
190              
191             new id
192              
193             =cut
194              
195             sub gen_id {
196 0     0 1 0 my @chars = ( 'a'..'z', '0'..'9' );
197              
198 0         0 return join '', map( { $chars[ int rand( scalar @chars ) ] } 1..12 );
  0         0  
199             }
200              
201              
202             =head2 gen_pw
203              
204             Authinfo Generation
205              
206             INPUT:
207              
208             length of authInfo, default 16 symbols
209              
210             OUTPUT:
211              
212             new authInfo
213              
214             =cut
215              
216             sub gen_pw {
217 8     8 1 12 my ( $pw_length ) = @_;
218              
219 8 50       17 $pw_length = 16 unless $pw_length;
220              
221 8         13 my $pw;
222              
223 8         97 my @chars = ( '0'..'9', 'A'..'Z', 'a'..'z', '!', '@', '$', '%', '*', '_', '.', ':', '-', '=', '+', '?', '#', ',', '"', "'" );
224              
225 8         22 for ( 0..32 ) {
226 10         23 $pw = join '', map( { $chars[ int rand( scalar @chars ) ] } 1..$pw_length );
  160         304  
227              
228             # буквы, цифры и символы должны быть обязательно
229 10 50 66     105 last if ( $pw =~ /\d/ and $pw =~ /[A-Z]/ and $pw =~ /[a-z]/ and $pw =~ /[!\@\$\%\*_\.:-=\+\?#,"']/ );
      66        
      33        
230             }
231              
232 8         58 return $pw;
233             }
234              
235              
236             # Generation transaction id
237              
238             sub get_cltrid {
239 326     326 0 4413 return md5_hex( time() . $$ . rand(1000000) );
240             }
241              
242              
243             # recursive removal of utf8 flag
244              
245             sub recursive_utf8_unflaged {
246 0     0 0 0 my $root = shift;
247              
248 0 0 0     0 if ( ref $root eq 'HASH' ) {
    0          
    0          
249 0         0 foreach my $k ( keys %$root ) {
250 0         0 my $key = $k;
251 0         0 utf8::decode( $key );
252 0         0 utf8::decode( $key );
253 0         0 utf8::encode( $key );
254             # work if $root->{with_utf8_flag} ne $root->{without_utf8_flag}
255 0         0 $root->{$key} = recursive_utf8_unflaged( delete $root->{$k} ) ;
256             }
257             }
258             elsif ( ref $root eq 'ARRAY' ) {
259 0         0 $_ = recursive_utf8_unflaged($_) foreach @$root;
260             }
261             elsif ( $root && ref $root eq '' ) {
262 0         0 utf8::decode( $root );
263 0         0 utf8::decode( $root );
264 0         0 utf8::encode( $root );
265             }
266              
267 0         0 return $root;
268             }
269              
270             # clear date-time
271              
272             sub cldate {
273 51     51 0 176 my ( $dt ) = @_;
274              
275 51         194 $dt =~ s/T/ /;
276 51         187 $dt =~ s/\.\d+Z$//;
277 51         87 $dt =~ s/Z$//;
278              
279 51         200 return $dt;
280             }
281              
282              
283             =head1 METHODS
284              
285             =head2 new
286              
287             Create new IO::EPP object, аutomatically connects to the provider and logins.
288              
289             Example of a call
290              
291             # Parameters for L
292             my %sock_params = (
293             PeerHost => 'epp.example.com',
294             PeerPort => 700,
295             SSL_key_file => $path_to_ssl_key_file,
296             SSL_cert_file => $path_to_ssl_cert_file,
297             Timeout => 30,
298             );
299              
300             # initialization of an object, during which login is called
301             my $o = IO::EPP::Base->new( {
302             sock_params => \%sock_params,
303             user => $login_name,
304             pass => $login_password,
305             log_name => '/var/log/comm_epp_registry_name',
306             } );
307              
308             # call check of domains
309             my ( $answ, $code, $msg ) = $o->check_domains( { domains => [ 'kalinka.realty' ] } );
310              
311             undef $o; # call logout() и DESTROY() of object
312              
313             INPUT:
314              
315             package name, parameters.
316              
317             Connection parameters:
318              
319             C – login;
320              
321             C – password;
322              
323             C – zone for providers that have a binding in it, for example, verisign;
324              
325             C – server name if the registry has different servers with different extensions, for example, pir/afilias for afilias;
326              
327             C – hashref with L parameters;
328              
329             C – use a real connection or registry emulator.
330              
331             Parameters for logging:
332              
333             C – do not write anything to the log;
334              
335             C – write log in this file, not in STDOUT;
336              
337             C – ref on functions to write to the log.
338              
339             OUTPUT:
340              
341             io::epp object or array ( object, login code, login message )
342              
343             If the connection or authorization failed, the response will contain zero instead of an object
344              
345             =cut
346              
347             sub new {
348 77     77 1 2932 my ( $package, $params ) = @_;
349              
350 77         168 my ( $self, $code, $msg );
351              
352 77         0 my $sock;
353              
354 77         137 my $sock_params = delete $params->{sock_params};
355              
356 77         134 my $test = delete $params->{test_mode};
357              
358 77 50       142 if ( $test ) {
359 77         189 $sock = $sock_params->{PeerHost} . ':' . $sock_params->{PeerPort};
360             }
361             else {
362             $sock = IO::Socket::SSL->new(
363             PeerPort => 700,
364             Timeout => 30,
365 0         0 %{$sock_params},
  0         0  
366             );
367             }
368              
369 77 50       168 unless ( $sock ) {
370 0         0 $msg = "can not connect";
371 0         0 $code = 0;
372              
373 0         0 goto ERR;
374             }
375              
376             $self = bless {
377             sock => $sock,
378             user => delete $params->{user},
379             tld => $params->{tld} || '',
380             server => delete $params->{server} || '',
381             #launch => $params->{launch} || '',
382             log_name => delete $params->{log_name},
383             log_fn => delete $params->{log_fn},
384 77   100     1112 no_log => delete $params->{no_log} || 0,
      100        
      50        
385             test => $test,
386             critical_error => undef,
387             }, $package;
388              
389 77         231 $self->set_urn();
390              
391 77         196 $self->set_log_vars( $params );
392              
393 77         266 $self->epp_log( "Connect to $$sock_params{PeerHost}:$$sock_params{PeerPort}\n" );
394              
395 77         175 my $hello = $self->req();
396              
397 77 50 33     412 if ( !$hello || $self->{critical_error} ) {
398 0         0 $msg = "Can't get greeting";
399 0 0       0 $msg .= '; ' . $self->{critical_error} if $self->{critical_error};
400 0         0 $code = 0;
401              
402 0         0 goto ERR;
403             }
404              
405 77         159 my ( $svcs, $extension ) = ( '', '' );
406              
407 77 100       300 if ( ref( $self ) =~ /IO::EPP::Base/ ) {
408 47 50       220 if ( $hello =~ /urn:ietf:params:xml:ns:contact-1.0/ ) {
409 47         83 $svcs .= '
410             urn:ietf:params:xml:ns:contact-1.0';
411             }
412 47 50       155 if ( $hello =~ /urn:ietf:params:xml:ns:domain-1.0/ ) {
413 47         88 $svcs .= '
414             urn:ietf:params:xml:ns:domain-1.0';
415             }
416 47 50       143 if ( $hello =~ /urn:ietf:params:xml:ns:host-1.0/ ) {
417             # drs.ua not want host
418 47         65 $svcs .= '
419             urn:ietf:params:xml:ns:host-1.0';
420             }
421              
422 47 50       145 if ( $hello =~ /urn:ietf:params:xml:ns:secDNS-1.1/ ) {
423 47         81 $extension .= '
424             urn:ietf:params:xml:ns:secDNS-1.1';
425             }
426             }
427              
428             # have a connection, can log in
429 77         261 my ( undef, $c, $m ) = $self->login( delete $params->{pass}, $svcs, $extension ); # no save passwd in object
430              
431 77 50 33     414 if ( $c and $c == 1000 ) {
432 77 100       333 return wantarray ? ( $self, $c, $m ) : $self;
433             }
434              
435 0   0     0 $msg = ( $m || '' ) . $self->{critical_error};
436 0   0     0 $code = $c || 0;
437              
438 0 0       0 ERR:
439             return wantarray ? ( 0, $code, $msg ) : 0;
440             }
441              
442              
443             sub set_urn {
444             $_[0]->{urn} = {
445 77     77 0 292 head => $IO::EPP::Base::epp_head,
446             cont => $IO::EPP::Base::epp_cont_urn,
447             host => $IO::EPP::Base::epp_host_urn,
448             dom => $IO::EPP::Base::epp_dom_urn,
449             };
450             }
451              
452              
453             # Set name for log
454              
455             sub set_log_vars {
456 77     77 0 145 my ( $self, $params ) = @_;
457              
458 77 50       174 $self->{log_name} = delete $params->{log_name} if $params->{log_name};
459 77 50       181 $self->{log_fn} = delete $params->{log_fn} if $params->{log_fn};
460             }
461              
462              
463             =head2 epp_log
464              
465             Writes data to the log or calls the function specified when creating the object
466              
467             By default, the log is written: date and time, pid of the process, name and body of the request:
468              
469             Thu Jan 1 01:00:00 1111
470             pid: 12345
471             check_domains request:
472            
473            
474            
475            
476            
477             xyz.comcom.xyzreged.xyz
478            
479            
480             50df482a1e928a00fa0e7fce3fe68f0f
481            
482            
483              
484             Thu Feb 2 02:02:22 2222
485             pid: 12345
486             check_domains answer:
487            
488            
489            
490            
491             Command completed successfully.
492            
493            
494             xyz.com
495             com.xyz
496             reged.xyzin use
497            
498             50df482a1e928a00fa0e7fce3fe68f0fTEST-2979E52890117206AAA1639725F4E862
499            
500            
501            
502              
503             =cut
504              
505             sub epp_log {
506 814     814 1 1453 my ( $self, $string ) = @_;
507              
508 814 50       1936 return if $self->{no_log};
509              
510 0         0 $string = "pid: $$\n" . $string;
511              
512 0 0       0 if ( $self->{log_fn} ) {
    0          
513 0         0 &{$self->{log_fn}}( $string );
  0         0  
514             }
515             elsif ( $self->{log_name} ) {
516 0         0 my $fh;
517              
518 0 0       0 if ( $self->{log_fh} ) {
519 0         0 $fh = $self->{log_fh};
520             }
521             else{
522 0 0       0 open( $fh, '>>', $self->{log_name} ) or die "Can't open $self->{log_name}: $!\n";
523              
524 0         0 $self->{log_fh} = $fh;
525             }
526              
527 0         0 print $fh scalar(localtime) . "\n$string\n\n"; # if `print( $self->{log_fh} $string );` that get error `(Missing operator before $string?)`
528             }
529             else {
530 0         0 print scalar(localtime) . "\n$string\n\n";
531             }
532             }
533              
534              
535             =head2 req_test
536              
537             For replace L in test mode
538              
539             =cut
540              
541             sub req_test {
542 233     233 1 398 my ( $self, $out_data, $info ) = @_;
543              
544 233         2118 require IO::EPP::Test::Base;
545              
546 233 100       920 $self->epp_log( "$info request:\n$out_data" ) if $out_data;
547              
548 233         396 my $answ;
549             eval{
550 233         642 $answ = IO::EPP::Test::Base::req( @_ );
551 233         586 1;
552             }
553 233 50       333 or do {
554 0         0 $self->{critical_error} = "$info req error: $@";
555 0         0 return;
556             };
557              
558 233         917 $self->epp_log( "$info answer:\n$answ" );
559              
560 233         577 return $answ;
561             }
562              
563              
564             =head2 req
565              
566             Request to registry
567              
568             INPUT:
569              
570             C – body of request;
571              
572             C – name of request for log.
573              
574             OUTPUT:
575             answer from registry.
576              
577             =cut
578              
579             sub req {
580 407     407 1 796 my ( $self, $out_data, $info ) = @_;
581              
582 407         666 $self->{critical_error} = '';
583              
584 407   100     902 $info ||= '';
585              
586 407 50       1236 return $self->req_test( $out_data, $info ) if $self->{test};
587              
588 0         0 my $THRESHOLD = 100000000;
589              
590 0 0       0 if ( $out_data ) {
591 0         0 my $d = $out_data;
592             # Remove password, authinfo from log
593 0         0 $d =~ s/[^<>]+<\/pw>/xxxxx<\/pw>/;
594              
595 0         0 $self->epp_log( "$info request:\n$d" );
596             }
597              
598 0         0 my $in_data = '';
599 0         0 my $start_time = time;
600              
601             eval{
602 0     0   0 local $SIG{ALRM} = sub { die "connection timeout\n" };
  0         0  
603              
604 0         0 alarm 120;
605              
606 0 0       0 if ( $out_data ) {
607             # https://rt.cpan.org/Ticket/Display.html?id=98368
608             # https://rt.cpan.org/Ticket/Display.html?id=98372
609 0         0 utf8::downgrade( $out_data );
610              
611 0         0 my $len = length( $out_data ) + 4;
612 0         0 my $pk_data_size = pack( 'N', $len );
613              
614 0         0 my $a_out = $self->{sock}->print( $pk_data_size . $out_data );
615 0         0 $self->{sock}->flush();
616              
617 0 0       0 die "data write failed" unless $a_out;
618             };
619              
620             # header - 4 bytes Nxxx with size
621 0         0 my $hdr;
622 0 0       0 unless ( defined( $self->{sock}->read( $hdr, 4 ) ) ) {
623 0         0 die "closed connection\n";
624             }
625              
626 0   0     0 my $data_size = ( unpack( 'N', $hdr ) // 0 ) - 4;
627              
628 0 0       0 die "closed connection\n" if $data_size < 0;
629              
630 0 0       0 die "data length is zero\n" unless $data_size;
631              
632 0 0       0 die "data length is $data_size which exceeds $THRESHOLD\n" if $data_size > $THRESHOLD;
633              
634             # Read data block
635 0         0 my $buf;
636 0         0 my $wait_cnt = 0;
637              
638 0         0 while ( length( $in_data ) < $data_size ) {
639 0         0 $buf = '';
640 0         0 $self->{sock}->read( $buf, ( $data_size - length( $in_data ) ));
641              
642 0 0       0 if ( length( $buf ) == 0 ) {
643 0 0       0 if ( $wait_cnt < 3 ) {
644             # part of the data may come with a long delay when saving the connection
645             # this problem is observed in corenic and drs
646 0         0 $wait_cnt++;
647 0         0 sleep 1;
648 0         0 redo;
649             }
650             else {
651             # it is likely that the socket has closed
652 0         0 last;
653             }
654             }
655              
656 0         0 $in_data .= $buf;
657             }
658              
659             # recheck, because something could not reach or stop at \0
660 0         0 my $l = length( $in_data );
661 0 0       0 die "data read failed: readed $l, need $data_size\ndata: $in_data" if $l != $data_size;
662              
663 0         0 alarm 0;
664              
665 0         0 1;
666 0 0       0 } or do {
667 0         0 my $err = $@;
668              
669 0         0 alarm 0;
670              
671 0         0 my $req_time = sprintf( '%0.4f', time - $start_time );
672 0         0 $self->epp_log( "req_time: $req_time\n$info req error: $err" );
673              
674 0         0 $self->{critical_error} = "req error: $err";
675              
676 0         0 return;
677             };
678              
679 0         0 my $req_time = sprintf( '%0.4f', time - $start_time );
680 0         0 $self->epp_log( "req_time: $req_time\n$info answer:\n$in_data" );
681              
682 0         0 return $in_data;
683             }
684              
685              
686             =head2 simple_request
687              
688             Universal handler for simple answer
689              
690             INPUT:
691              
692             request body;
693              
694             request name;
695              
696             check or not epp poll, default is 0
697              
698             OUTPUT:
699              
700             answer, may contain the object's name, id, creation and/or expiration date, client-side transaction id, and registry id;
701              
702             answer code;
703              
704             answer message, if there is an error in the response, an additional reason for the error may be passed along with the message.
705              
706             An Example:
707              
708             # answer for create_contact:
709              
710             {
711             'msg' => 'Command completed successfully.',
712             'cont_id' => 'sxsup8ehs000',
713             'cre_date' => '2020-01-01 01:01:01',
714             'cltrid' => 'd0a528a4816ea4e16c3f56e25bf11111',
715             'code' => 1000,
716             'svtrid' => 'CNIC-22E5B2CBBD6C04169AEC9228FB0677FA173D76487AF8BA8734AF3C11111'
717             };
718              
719             # answer with error, "1.2.3.4 addr not found" is reason:
720              
721             {
722             'msg' => 'Parameter value policy error; 1.2.3.4 addr not found',
723             'cltrid' => 'd0e2a9c2af427264847b0a6e59b60000',
724             'code' => 2306,
725             'svtrid' => '4586654601-1579115463111'
726             };
727              
728             =cut
729              
730             sub simple_request {
731 211     211 1 453 my ( $self, $body, $info, $check_queue_msgs ) = @_;
732              
733 211 50       431 unless ( $body ) {
734 0 0       0 return wantarray ? ( 0, 0, 'no query' ) : 0 ;
735             }
736              
737 211         500 my $content = $self->req( $body, $info );
738              
739 211 50 33     1371 if ( $content && $content =~ // ) {
740 211         750 my $code = $1 + 0;
741              
742 211         399 my $msg = '';
743 211 50       1757 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
744 211         507 $msg = $1;
745             }
746              
747 211 100 66     803 if ( $code == 1001 or $code >= 2000 ) {
748             # 1001 -- pendingAction
749             # 2000+ - May be an addition to the error, It is the inventions of different providers
750 90         359 my $reason = join( ';', $content =~ /]*>([^<>]+)<\/reason>/g );
751              
752 90 100       208 $msg .= "; " . $reason if $reason;
753              
754 90         203 my ( $xcp ) = $content =~ /([^<>]+)<\/oxrs:xcp>/;
755              
756 90 50       180 $msg .= "; " . $xcp if $xcp;
757              
758 90         779 my ( $text ) = $content =~ /([^<>]+)<\/text>.+\/result>/s;
759              
760 90 50       218 $msg .= "; " . $text if $text;
761             }
762              
763             # And check epp poll
764 211         321 my $queue_msgs = '';
765              
766 211 50 33     471 if ( $check_queue_msgs and $content =~ // ) {
767 0         0 $queue_msgs = { count => $1 , id => $2 };
768             }
769              
770 211         337 my $info = {};
771              
772             # dates
773 211         752 foreach my $k ( keys %dt ) {
774 1266 100       21265 if ( $content =~ m|<[a-z]+:$k>([^<>]+)| ) {
775 21         70 $info->{$dt{$k}} = cldate( $1 );
776             }
777             }
778              
779 211 100       756 if ( $content =~ m{([^<>]+)} ) {
780 7         22 $info->{cont_id} = $1;
781             }
782              
783 211 100       568 if ( $content =~ m{<(host|domain):name>([^<>]+)} ) {
784 11         39 my %r = ( host => 'ns', domain => 'dname' );
785              
786 11         50 $info->{$r{$1}} = $2;
787             }
788              
789             # This is needed to monitor deferred actions at some providers
790 211         1206 ( $info->{cltrid} ) = $content =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
791 211         962 ( $info->{svtrid} ) = $content =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
792              
793 211 50       1360 return wantarray ? ( $info, $code, $msg, $queue_msgs ) : $info;
794             }
795              
796 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
797             }
798              
799             =head2 login
800              
801             Authorization on the server.
802             The function is automatically called from new.
803             A separate call is only needed to change the password.
804              
805             INPUT:
806              
807             password;
808              
809             addition standard parameters (xxxxx-1.0);
810              
811             extensions (yyyyyy-1.0);
812              
813             new password if need.
814              
815             OUTPUT: see L.
816              
817             =cut
818              
819             sub login {
820 77     77 1 220 my ( $self, $pw, $svcs, $ext, $new_pw ) = @_;
821              
822 77 50       191 return ( 0, 0, 'no user' ) unless $self->{user};
823 77 50       152 return ( 0, 0, 'no passwd' ) unless $pw;
824              
825 77   50     158 $svcs ||= ''; # addition standard parameters
826 77   50     139 $ext ||= ''; # extension
827              
828 77 50       144 if ( $ext ) {
829 77         161 $ext = "\n $ext\n ";
830             }
831              
832 77         108 my $npw = '';
833 77 50       170 if ( $new_pw ) {
834 0         0 $npw = "\n $new_pw";
835             }
836              
837 77         152 my $cltrid = get_cltrid();
838              
839 77         401 my $body = <
840             $$self{urn}{head}
841            
842            
843             $$self{user}
844             $pw$npw
845            
846             1.0
847             en
848            
849             $svcs$ext
850            
851            
852             $cltrid
853            
854            
855             LOGIN
856              
857 77         186 return $self->simple_request( $body, 'login' );
858             }
859              
860              
861             =head2 hello
862              
863             Get greeting, ping analog.
864              
865             No input parameters.
866              
867             Sample response:
868              
869             {
870             'msg' => '
871            
872             CentralNic EPP server EPP.CENTRALNIC.COM
873             2020-20-20T20:20:20.0Z
874            
875             1.0en
876             urn:ietf:params:xml:ns:domain-1.0
877             urn:ietf:params:xml:ns:contact-1.0
878             urn:ietf:params:xml:ns:host-1.0
879            
880             urn:ietf:params:xml:ns:rgp-1.0
881             urn:ietf:params:xml:ns:secDNS-1.1
882             urn:ietf:params:xml:ns:idn-1.0
883             urn:ietf:params:xml:ns:fee-0.4
884             urn:ietf:params:xml:ns:fee-0.5
885             urn:ietf:params:xml:ns:launch-1.0
886             urn:ietf:params:xml:ns:regtype-0.1
887             urn:ietf:params:xml:ns:auxcontact-0.1
888             urn:ietf:params:xml:ns:artRecord-0.2
889             http://www.nic.coop/contactCoopExt-1.0
890            
891            
892            
893            
894            
895            
896            
897            
898            
899             ',
900             'code' => 1000
901             };
902              
903             =cut
904              
905             sub hello {
906 4     4 1 3451 my ( $self ) = @_;
907              
908 4         31 my $body = <
909             $$self{urn}{head}
910            
911            
912             HELLO
913              
914 4         34 my $content = $self->req( $body, 'hello' );
915              
916 4 50 33     33 unless ( $content && $content =~ /greeting/ ) {
917 0 0       0 return wantarray ? ( 0, 0, 'no greeting' ) : 0;
918             }
919              
920 4         18 my $info = {
921             code => 1000,
922             msg => $content,
923             };
924              
925 4 50       33 return wantarray ? ( $info, 1000, $content ) : $info;
926             }
927              
928              
929             =head2 check_contacts
930              
931             Check whether there are contacts with such IDs
932              
933             INPUT:
934             params with key:
935             C -- arrayref on contact id list.
936              
937             Request:
938              
939             my ( $answ, $msg ) = make_request( 'check_contacts', { contacts => [ 'H1234567', 'nfjkrek-fre8fm' ] } );
940              
941             print Dumper $answ;
942              
943             Answer:
944              
945             $VAR1 = {
946             'msg' => 'Command completed successfully.',
947             'nfjkrek-fre8fm' => {
948             'avail' => '1'
949             },
950             'H1234567' => {
951             'avail' => '0'
952             },
953             'code' => '1000'
954             };
955              
956             =cut
957              
958             sub check_contacts {
959 1     1 1 3 my ( $self, $params ) = @_;
960              
961 1 50 50     9 return ( 0, 0, 'no contacts' ) unless $params->{contacts} && scalar( @{$params->{contacts}} );
  1         5  
962              
963 1         3 my $contacts = $params->{contacts};
964              
965 1         2 my $conts = '';
966              
967 1         4 foreach my $cont ( @$contacts ) {
968 2         6 $conts .= "$cont";
969             }
970              
971 1   50     7 my $ext = $$params{extension} || '';
972              
973 1 50       3 $ext = "\n \n$ext " if $ext;
974              
975 1         3 my $cltrid = get_cltrid();
976              
977 1         8 my $body = <
978             $$self{urn}{head}
979            
980            
981            
982             $conts
983            
984             $ext
985             $cltrid
986            
987            
988             CHECKCONT
989              
990 1         3 my $content = $self->req( $body, 'check_contacts' );
991              
992 1 50       28 if ( $content =~ // ) {
993 1         6 my $code = $1 + 0;
994              
995 1         2 my $msg = '';
996 1 50       25 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
997 1         5 $msg = $1;
998             }
999              
1000 1         10 my @aa = $content =~ /([^<>]+<\/contact:id>)/g;
1001              
1002 1         3 my %answ;
1003 1         2 foreach my $a ( @aa ) {
1004 2 50       11 if ( $a =~ /([^<>]+)<\/contact:id>/ ) {
1005 2         11 $answ{$2} = { avail => $1 };
1006             }
1007             }
1008              
1009 1 50       7 return wantarray ? ( \%answ, $code, $msg ) : \%answ;
1010             }
1011              
1012 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
1013             }
1014              
1015             =head2 cont_to_xml
1016              
1017             Covertor contact user date to epp xml
1018              
1019             for create/update_contact functions
1020              
1021             =cut
1022              
1023             sub cont_to_xml {
1024 13     13 1 23 my ( $self, $params ) = @_;
1025              
1026 13 100 100     59 unless ( $$params{'int'} && $$params{'loc'} ) {
1027             # Set default is 'int'
1028 8         21 foreach my $f ( 'name', 'first_name', 'last_name', 'patronymic', 'family_name', 'company', 'addr', 'city', 'state', 'postcode', 'country_code' ) {
1029 88 100       168 $$params{'int'}{$f} = delete $$params{$f} if defined $$params{$f};
1030             }
1031             }
1032              
1033 13         28 my $postalinfo = '';
1034 13         35 foreach my $type ( 'int', 'loc' ) { # legal - in children modules
1035 26 100       67 next unless $$params{$type};
1036              
1037             # need_name=1 for creation - always, for update:
1038             # According to the standard at change of id the name does not change ( https://tools.ietf.org/html/rfc5733#page-23 ),
1039             # but some providers of it do not know,
1040             # at the same time, they have no documentation, but send all to read RFC, example, drs.ua
1041 18         27 my $name = '';
1042 18 100       41 if ( $$params{need_name} ) {
1043 16 50       43 if ( $$params{$type}{name} ) {
1044 0         0 $name = $$params{$type}{name};
1045             }
1046             else {
1047 16 50       43 $name = $$params{$type}{first_name} if $$params{$type}{first_name};
1048              
1049 16 50 33     58 if ( $$params{$type}{last_name} && $$params{$type}{family_name} ) {
1050 0 0       0 if ( $$params{$type}{last_name} ) {
1051 0 0       0 $name .= ' ' if $name;
1052 0         0 $name .= $$params{$type}{last_name};
1053             }
1054              
1055 0 0       0 if ( $$params{$type}{patronymic} ) {
1056 0 0       0 $name .= ' ' if $name;
1057 0         0 $name .= $$params{$type}{patronymic};
1058             }
1059              
1060 0 0       0 if ( $$params{$type}{family_name} ) {
1061 0 0       0 $name .= ' ' if $name;
1062 0         0 $name .= $$params{$type}{family_name};
1063             }
1064             }
1065             else {
1066             # family_name eq last_name
1067 16 50       35 if ( $$params{$type}{patronymic} ) {
1068 0 0       0 $name .= ' ' if $name;
1069 0         0 $name .= $$params{$type}{patronymic};
1070             }
1071              
1072 16 50 33     40 if ( $$params{$type}{last_name} || $$params{$type}{family_name} ) {
1073 16 50       32 $name .= ' ' if $name;
1074 16   33     41 $name .= $$params{$type}{last_name} || $$params{$type}{family_name};
1075             }
1076             }
1077              
1078             }
1079              
1080 16 50       42 $name = "\n $name" if $name;
1081             }
1082              
1083 18         24 my $org;
1084 18 100       39 if ( $$params{$type}{org} ) {
1085 10         21 $$params{$type}{org} =~ s/&/&/g;
1086              
1087 10         20 $org = "$$params{$type}{org}";
1088             }
1089             else {
1090 8         13 $org = '';
1091             }
1092              
1093              
1094 18         27 my $street = '';
1095              
1096 18 100       51 $$params{$type}{addr} = [ $$params{$type}{addr} ] unless ref $$params{$type}{addr};
1097              
1098 18         27 foreach my $s ( @{$$params{$type}{addr}} ) {
  18         53  
1099 23 100       44 $street .= "\n " if $street;
1100 23         49 $street .= "$s";
1101             }
1102              
1103 18 50       56 my $sp = $$params{$type}{'state'} ? "$$params{$type}{state}" : '' ;
1104 18 50       51 my $pc = $$params{$type}{postcode} ? "$$params{$type}{postcode}" : '' ;
1105              
1106 18         96 $postalinfo .= qq|
1107             $name
1108             $org
1109            
1110             $street
1111             $$params{$type}{city}
1112             $sp
1113             $pc
1114             $$params{$type}{country_code}
1115            
1116             |;
1117             }
1118              
1119             # voice / fax Extension is disabled
1120 13         20 my $voice = '';
1121 13 100       41 $$params{phone} = [ $$params{phone} ] unless ref $$params{phone};
1122 13         22 foreach my $s ( @{$$params{phone}} ) {
  13         27  
1123 17 100       33 $voice .= "\n " if $voice;
1124 17         37 $voice .= "$s";
1125             }
1126              
1127 13 50       30 my $fax = $$params{fax} ? "$$params{fax}" : '';
1128              
1129 13         27 my $email = '';
1130 13 100       41 $$params{email} = [ $$params{email} ] unless ref $$params{email};
1131 13         19 foreach my $s ( @{$$params{email}} ) {
  13         28  
1132 13 50       27 $email .= "\n " if $email;
1133 13         31 $email .= "$s";
1134             }
1135              
1136 13 50       38 my $pw = $$params{authinfo} ? "$$params{authinfo}" : '' ;
1137              
1138 13   50     64 $$params{pp_ext} ||= '';
1139              
1140 13         57 my $textcont = qq|$postalinfo
1141             $voice
1142             $fax
1143             $email
1144            
1145             $pw
1146             $$params{pp_ext}|;
1147              
1148 13         34 return $textcont;
1149             }
1150              
1151             =head2 create_contact_ext
1152              
1153             Create contact extensions,
1154             for overwriting in child classes
1155              
1156             =cut
1157              
1158             sub create_contact_ext {
1159 11     11 1 20 return '';
1160             }
1161              
1162             =head2 create_contact
1163              
1164             Register a contact
1165              
1166             INPUT:
1167              
1168             Hash with parameters:
1169              
1170             C – some providers create contact ID automatically;
1171              
1172             C or C, C, C, C – full name in one field or first, last, patronymic, family names separately;
1173              
1174             C – organization if necessary, some registries require a zero-length string, while others require C;
1175              
1176             C – street, house, building, apartment;
1177              
1178             C – city, town;
1179              
1180             C – state, region, province, republic, optional field;
1181              
1182             C – zip code;
1183              
1184             C – two-character country code;
1185              
1186             C – the phone number in international format;
1187              
1188             C – usually only required for legal entities;
1189              
1190             C;
1191              
1192             C – the key is to transfer your contacts, but usually the contacts are transferred automatically together with a domain.
1193              
1194             If only the C type of contacts is passed, it can be omitted.
1195              
1196             OUTPUT: see L.
1197              
1198             An Example, one type (by default this is C):
1199              
1200             ( $answ, $code, $msg ) = $conn->create_contact(
1201             {
1202             cont_id => '123qwerty',
1203             first_name => 'Test',
1204             last_name => 'Testov',
1205             org => 'Private Person',
1206             addr => 'Vagnera 11-22',
1207             city => 'Donetsk',
1208             state => 'Donetskaya',
1209             postcode => '83061',
1210             country_code => 'DN',
1211             phone => '+380.501234567',
1212             fax => '',
1213             email => 'reg1010@yandex.com',
1214             authinfo => 'Q2+qqqqqqqqqqqqqqqqqqqqqqqqqq',
1215             },
1216             );
1217              
1218             Contact with two types
1219              
1220             ( $answ, $code, $msg ) = $conn->create_contact(
1221             {
1222             cont_id => '123qwerty',
1223             int => {
1224             first_name => 'Test',
1225             last_name => 'Testov',
1226             org => 'Private Person',
1227             addr => 'Vagnera 11-22',
1228             city => 'Donetsk',
1229             state => 'Donetskaya',
1230             postcode => '83061',
1231             country_code => 'DN',
1232             },
1233             loc => {
1234             first_name => 'Тест',
1235             last_name => 'Тестов',
1236             org => 'Частное лицо',
1237             addr => 'Вагнера 11/22',
1238             city => 'Донецк',
1239             state => 'Донецкая обл.',
1240             postcode => '83061',
1241             country_code => 'DN',
1242             },
1243             phone => '+380.501234567',
1244             fax => '',
1245             email => 'reg1010@yandex.com',
1246             authinfo => 'Q2+qqqqqqqqqqqqqqqqqqqqqqqqqq',
1247             }
1248             );
1249              
1250             =cut
1251              
1252             sub create_contact {
1253 11     11 1 57 my ( $self, $params ) = @_;
1254              
1255 11 50       30 return ( 0, 0, 'no params' ) unless ref $params;
1256              
1257 11 50       31 return ( 0, 0, 'no cont_id' ) unless $params->{cont_id};
1258              
1259 11         28 $params->{need_name} = 1;
1260              
1261 11         28 my $textcont = $self->cont_to_xml( $params );
1262              
1263 11   50     39 my $ext = $params->{extension} || '';
1264              
1265 11         32 $ext .= $self->create_contact_ext( $params );
1266              
1267 11 50       21 if ( $ext ) {
1268 0         0 $ext = "\n \n$ext ";
1269             }
1270              
1271 11         27 my $cltrid = get_cltrid();
1272              
1273 11         88 my $body = <
1274             $$self{urn}{head}
1275            
1276            
1277            
1278             $$params{cont_id}$textcont
1279            
1280             $ext
1281             $cltrid
1282            
1283            
1284             CRECONT
1285              
1286 11         29 return $self->simple_request( $body, 'create_contact' );
1287             }
1288              
1289              
1290             =head2 cont_from_xml
1291              
1292             Covertor contact epp xml data to hash
1293              
1294             for get_contact_info, overwritten in some child modules
1295              
1296             =cut
1297              
1298             sub cont_from_xml {
1299 3     3 1 13 my ( undef, $rdata ) = @_;
1300              
1301 3         4 my %cont;
1302              
1303 3         17 ( $cont{cont_id} ) = $rdata =~ /([^<>]+)<\/contact:id>/;
1304              
1305 3         13 ( $cont{roid} ) = $rdata =~ /([^<>]+)<\/contact:roid>/;
1306              
1307 3         10 my @atypes = ( 'int', 'loc' );
1308 3         15 foreach my $atype ( @atypes ) {
1309 6         170 my ( $postal ) = $rdata =~ /(.+?)<\/contact:postalInfo>/s;
1310              
1311 6 100       22 next unless $postal;
1312              
1313 5         26 ( $cont{$atype}{name} ) = $postal =~ /([^<>]+)<\/contact:name>/;
1314              
1315 5 100       24 if ( $rdata =~ /([^<>]*)<\/contact:org>/ ) {
1316 4         13 $cont{$atype}{org} = $1;
1317 4         9 $cont{$atype}{org} =~ s/&/&/g;
1318             }
1319              
1320              
1321 5         25 $cont{$atype}{addr} = join(', ', $postal =~ /([^<>]*)<\/contact:street>/ );
1322              
1323 5         24 ( $cont{$atype}{city} ) = $postal =~ /([^<>]*)<\/contact:city>/;
1324              
1325 5         22 ( $cont{$atype}{'state'} ) = $postal =~ /([^<>]*)<\/contact:sp>/;
1326              
1327 5         20 ( $cont{$atype}{postcode} ) = $postal =~ /([^<>]*)<\/contact:pc>/;
1328              
1329 5         35 ( $cont{$atype}{country_code} ) = $postal =~ /([A-Za-z]+)<\/contact:cc>/;
1330 5         16 $cont{$atype}{country_code} = uc $cont{$atype}{country_code};
1331             }
1332              
1333 3         25 $cont{phone} = [ $rdata =~ /]*>([0-9+.]*)<\/contact:voice>/g ];
1334              
1335 3         11 $cont{fax} = [ $rdata =~ /]*>([0-9+.]*)<\/contact:fax>/g ];
1336              
1337 3         20 $cont{email} = [ $rdata =~ /([^<>]+)<\/contact:email>/g ];
1338              
1339             #
1340 3         18 my @ss = $rdata =~ //g;
1341             # No changes pending
1342 3         9 my @aa = $rdata =~ /]+>[^<>]+<\/contact:status>/g;
1343 3 50       8 if ( scalar @aa ) {
1344 0         0 foreach my $row ( @aa ) {
1345 0 0       0 if ( $row =~ /([^<>]+)<\/contact:status>/ ) {
1346 0         0 $cont{statuses}{$1} = $2;
1347             }
1348             }
1349             }
1350             else {
1351 3         13 $cont{statuses}{$_} = '+' for @ss;
1352             }
1353              
1354 3 50       13 if ( $rdata =~ /\s*(.+?)<\/contact:pw>/s ) {
1355 0         0 $cont{authinfo} = $1;
1356             }
1357              
1358 3 50       22 if ( $rdata =~ // ) {
1359 3 50       13 $cont{pp_flag} = $1 ? 0 : 1;
1360             }
1361              
1362             # owner, ...
1363 3         13 foreach my $k ( keys %id ) {
1364 15 100       252 if ( $rdata =~ /([^<>]+)<\/contact:$k>/ ) {
1365 9         35 $cont{$id{$k}} = $1;
1366             }
1367             }
1368              
1369             # dates
1370 3         12 foreach my $k ( keys %dt ) {
1371 18 100       259 if ( $rdata =~ /([^<>]+)<\/contact:$k>/ ) {
1372 6         20 $cont{$dt{$k}} = $1;
1373              
1374 6         22 $cont{$dt{$k}} =~ s/T/ /;
1375 6         31 $cont{$dt{$k}} =~ s/\.\d+Z$//;
1376 6         19 $cont{$dt{$k}} =~ s/Z$//;
1377             }
1378             }
1379              
1380 3         12 return \%cont;
1381             }
1382              
1383              
1384             =head2 get_contact_ext
1385              
1386             Providers extension, replaced in provider modules
1387              
1388             Returns an empty hashref here
1389              
1390             =cut
1391              
1392             sub get_contact_ext {
1393 0     0 1 0 return {};
1394             }
1395              
1396              
1397             =head2 get_contact_info
1398              
1399             Get information on the specified contact
1400              
1401             INPUT:
1402              
1403             C – contact id
1404              
1405             C – epp extensions in xml
1406              
1407             An Example:
1408              
1409             my ( $answer, $code, $msg ) = $conn->get_contact_info( { cont_id => 'H12345' } );
1410              
1411             # $answer:
1412              
1413             {
1414             'owner' => 'H2220222',
1415             'int' => {
1416             'city' => 'Moscow',
1417             'org' => 'My Ltd',
1418             'country_code' => 'RU',
1419             'name' => 'Igor Igorev',
1420             'postcode' => '123456',
1421             'addr' => 'Igoreva str, 3',
1422             'state' => 'Igorevskya obl.'
1423             },
1424             'roid' => 'C2222888-CNIC',
1425             'cre_date' => '2012-12-12 12:12:12',
1426             'phone' => [
1427             '+7.4952334455'
1428             ],
1429             'pp_flag' => 1,
1430             'email' => [
1431             'mail@igor.ru'
1432             ],
1433             'upd_date' => '2012-12-12 12:12:12',
1434             'cont_id' => 'H12345',
1435             'fax' => [
1436             '+7.4952334455'
1437             ],
1438             'creater' => 'H2220222',
1439             'statuses' => {
1440             'serverDeleteProhibited' => '+',
1441             'serverTransferProhibited' => '+',
1442             'linked' => '+'
1443             }
1444             };
1445              
1446             =cut
1447              
1448             sub get_contact_info {
1449 4     4 1 12 my ( $self, $params ) = @_;
1450              
1451 4 50       12 return ( 0, 0, 'no cont_id' ) unless $$params{cont_id};
1452              
1453 4   50     18 my $ext = $$params{extension} || '';
1454              
1455 4 50       12 if ( $ext ) {
1456 0         0 $ext = "\n \n$ext ";
1457             }
1458              
1459 4         10 my $cltrid = get_cltrid();
1460              
1461 4         28 my $body = <
1462             $$self{urn}{head}
1463            
1464            
1465            
1466             $$params{cont_id}
1467            
1468             $ext
1469             $cltrid
1470            
1471            
1472             CONTINFO
1473              
1474 4         11 my $content = $self->req( $body, 'get_contact_info' );
1475              
1476 4 50       26 if ( $content =~ /result code=['"](\d+)['"]/ ) {
1477 4         14 my $rcode = $1 + 0;
1478              
1479 4         6 my $msg = '';
1480 4 50       108 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1481 4         11 $msg = $1;
1482             }
1483              
1484 4         7 my $cont;
1485              
1486             # take the main part and disassemble
1487 4 100       24 if ( $content =~ /(.+)<\/resData>/s ) {
1488 3         11 $cont = $self->cont_from_xml( $1 );
1489             }
1490             else {
1491 1 50       7 return wantarray ? ( 0, $rcode, $msg ) : 0 ;
1492             }
1493              
1494 3 50       15 if ( $content =~ /(.+)<\/extension>/s ) {
1495 0         0 my $ext = $1;
1496              
1497 0         0 my $spec_ext = $self->get_contact_ext( $ext );
1498              
1499 0         0 $cont->{$_} = $spec_ext->{$_} for keys %$spec_ext;
1500              
1501 0 0       0 if ( $content =~ /]*>(.+)<\/keysys:resData>/s ) {
1502             # key-system extension TODO: move
1503 0         0 my $krdata = $1;
1504              
1505 0 0       0 push @{$cont->{statuses}}, 'validated' if $krdata =~ /1<\/keysys:validated>/;
  0         0  
1506 0 0       0 push @{$cont->{statuses}}, 'verified' if $krdata =~ /1<\/keysys:verified>/;
  0         0  
1507 0 0       0 push @{$cont->{statuses}}, 'verification-requested'
  0         0  
1508             if $krdata =~ /1<\/keysys:verification-requested>/;
1509             }
1510             }
1511              
1512 3 50       16 return wantarray ? ( $cont, $rcode, $msg ) : $cont;
1513             }
1514              
1515 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
1516             }
1517              
1518             =head2 update_statuses_add
1519              
1520             Part of update_* functions
1521              
1522             =cut
1523              
1524             sub update_statuses_add {
1525 14     14 1 41 my ( undef, $type, $statuses ) = @_;
1526              
1527 14         23 my $add = '';
1528 14         18 my %sts;
1529              
1530 14 100       47 if ( ref $statuses eq 'HASH' ) {
    50          
1531 2         3 %sts = %{$statuses};
  2         6  
1532             }
1533             elsif ( ref $statuses eq 'ARRAY' ) {
1534 12         19 $sts{$_} = '+' for @{$statuses};
  12         40  
1535             }
1536              
1537 14         38 foreach my $st ( keys %sts ) {
1538 14 100 66     61 if ( !$sts{$st} or $sts{$st} eq '+' ) {
1539 12         43 $add .= qq| <$type:status s="$st"/>\n|;
1540             }
1541             else {
1542 2         9 $add .= qq| <$type:status s="$st">$sts{$st}\n|;
1543             }
1544             }
1545              
1546 14         43 return $add;
1547             }
1548              
1549              
1550             =head2 update_statuses_rem
1551              
1552             Part of update_* functions
1553              
1554             =cut
1555              
1556             sub update_statuses_rem {
1557 4     4 1 13 my ( undef, $type, $statuses ) = @_;
1558              
1559 4         7 my $rem = '';
1560 4         8 my @sts;
1561              
1562 4 50       22 if ( ref $statuses eq 'HASH' ) {
    50          
1563 0         0 @sts = keys %{$statuses};
  0         0  
1564             }
1565             elsif ( ref $statuses eq 'ARRAY' ) {
1566 4         6 @sts = @{$statuses};
  4         11  
1567             }
1568              
1569 4         34 $rem .= qq| <$type:status s="$_"/>\n| foreach @sts;
1570              
1571 4         12 return $rem;
1572             }
1573              
1574             =head2 update_contact
1575              
1576             To update contact information
1577              
1578             INPUT:
1579              
1580             params with keys:
1581              
1582             C – contact id
1583              
1584             C, C – only contact statuses can be added or deleted, , such as clientUpdateProhibited
1585              
1586             C – modify data, see fields in L
1587              
1588             OUTPUT: see L.
1589              
1590             An Example, change data, one type (by default this is C):
1591              
1592             ( $answ, $code, $msg ) = $conn->update_contact(
1593             {
1594             cont_id => '123qwerty',
1595             chg => {
1596             first_name => 'Test',
1597             last_name => 'Testov',
1598             org => 'Private Person',
1599             addr => 'Vagnera 11-22',
1600             city => 'Donetsk',
1601             state => 'Donetskaya',
1602             postcode => '83061',
1603             country_code => 'DN',
1604             phone => '+380.501234567',
1605             fax => '',
1606             email => 'reg1010@yandex.com',
1607             authinfo => 'Q2+qqqqqqqqqqqqqqqqqqqqqqqqqq',
1608             }
1609             },
1610             );
1611              
1612             =cut
1613              
1614             sub update_contact {
1615 2     2 1 6 my ( $self, $params ) = @_;
1616              
1617 2 50       6 return ( 0, 0, 'no params' ) unless ref $params;
1618              
1619 2 50       7 return ( 0, 0, 'no cont_id' ) unless $params->{cont_id};
1620              
1621 2         7 my ( $add, $rem, $chg ) = ( '', '', '' );
1622              
1623 2 50       7 if ( $$params{add} ) {
1624 0 0       0 if ( $$params{add}{statuses} ) {
1625 0         0 $add .= $self->update_statuses_add( 'contact', $$params{add}{statuses} );
1626             }
1627             }
1628              
1629 2 50       5 $add = "\n \n$add " if $add;
1630              
1631 2 50       5 if ( $$params{rem} ) {
1632 0 0       0 if ( $$params{rem}{statuses} ) {
1633 0         0 $rem .= $self->update_statuses_rem( 'contact', $$params{rem}{statuses} );
1634             }
1635             }
1636              
1637 2 50       6 $rem = "\n \n$rem " if $rem;
1638              
1639 2 50       5 if ( $$params{chg} ) {
1640 2         7 $chg .= $self->cont_to_xml( $$params{chg} );
1641              
1642 2         22 $chg =~ s/\n/\n /g;
1643             }
1644              
1645 2 50       9 $chg = "\n $chg " if $chg;
1646              
1647 2   50     10 my $ext = $$params{extension} || '';
1648              
1649 2 50       6 $ext = "\n \n$ext " if $ext;
1650              
1651 2         5 my $cltrid = get_cltrid();
1652              
1653 2         19 my $body = <
1654             $$self{urn}{head}
1655            
1656            
1657            
1658             $$params{cont_id}$add$rem$chg
1659            
1660             $ext
1661             $cltrid
1662            
1663            
1664             UPDCONT
1665              
1666 2         8 return $self->simple_request( $body, 'update_contact' );
1667             }
1668              
1669              
1670             =head2 delete_contact
1671              
1672             Delete the specified contact.
1673             Usually this function is not needed because the registry itself deletes unused contacts.
1674              
1675             INPUT:
1676              
1677             params with keys:
1678              
1679             C – contact id.
1680              
1681             C – extensions for some providers, empty by default
1682              
1683             OUTPUT: see L.
1684              
1685             An Example:
1686              
1687             my ( $answ, $msg ) = make_request( 'delete_contact', { cont_id => 'H12345', %conn_params } );
1688              
1689             =cut
1690              
1691             sub delete_contact {
1692 2     2 1 6 my ( $self, $params ) = @_;
1693              
1694 2 50       6 return ( 0, 0, 'no params' ) unless ref $params;
1695              
1696 2 50       6 return ( 0, 0, 'no cont_id' ) unless $$params{cont_id};
1697              
1698 2   50     9 my $ext = $$params{extension} || '';
1699              
1700 2 50       7 $ext = "\n \n$ext " if $ext;
1701              
1702 2         5 my $cltrid = get_cltrid();
1703              
1704 2         16 my $body = <
1705             $$self{urn}{head}
1706            
1707            
1708            
1709             $$params{cont_id}
1710            
1711             $ext
1712             $cltrid
1713            
1714            
1715             DELCONT
1716              
1717 2         6 return $self->simple_request( $body, 'delete_contact' );
1718             }
1719              
1720              
1721             =head2 check_nss
1722              
1723             Check that the nameserver is registered
1724              
1725             INPUT:
1726              
1727             params with keys:
1728              
1729             C – list with nameservers
1730              
1731             C – extensions for some providers, empty by default
1732              
1733             OUTPUT: see L.
1734              
1735             An Example:
1736              
1737             my ( $a, $m, $o ) = make_request( 'check_nss', { nss => [ 'ns99.cnic.com', 'ns1.godaddy.com' ] } );
1738              
1739             # answer:
1740              
1741             {
1742             'msg' => 'Command completed successfully.',
1743             'ns1.godaddy.com' => {
1744             'reason' => 'in use',
1745             'avail' => '0'
1746             },
1747             'ns99.cnic.com' => {
1748             'avail' => '1'
1749             },
1750             'code' => '1000'
1751             };
1752              
1753             =cut
1754              
1755             sub check_nss {
1756 2     2 1 6 my ( $self, $params ) = @_;
1757              
1758 2 50 50     11 return ( 0, 0, 'no nss' ) unless $params->{nss} && scalar( @{$params->{nss}} );
  2         10  
1759              
1760 2         6 my $hosts = '';
1761              
1762 2         4 foreach my $h ( @{$params->{nss}} ) {
  2         6  
1763 8         19 $hosts .= "$h";
1764             }
1765              
1766 2   100     9 my $ext = $$params{extension} || '';
1767              
1768 2 100       8 $ext = "\n \n$ext " if $ext;
1769              
1770 2         14 my $cltrid = get_cltrid();
1771              
1772 2         14 my $body = <
1773             $$self{urn}{head}
1774            
1775            
1776            
1777             $hosts
1778            
1779             $ext
1780             $cltrid
1781            
1782            
1783             CHECKNSS
1784              
1785 2         7 my $content = $self->req( $body, 'check_nss' );
1786              
1787 2 50       15 if ( $content =~ // ) {
1788 2         8 my $code = $1 + 0;
1789              
1790 2         4 my $msg = '';
1791 2 50       35 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
1792 2         5 $msg = $1;
1793             }
1794              
1795 2         23 my @aa = $content =~ /(.+?)<\/host:cd>/sg;
1796              
1797 2         4 my %answ;
1798 2         6 foreach my $a ( @aa ) {
1799 8 50       35 if ( $a =~ /([^<>]+)<\/host:name>/ ) {
1800 8         17 my $ns = $2;
1801 8         53 $answ{$ns} = { avail => $1 };
1802              
1803 8 100       45 if ( $a =~ /([^<>]+)<\/host:reason>/ ) {
1804 2         9 $answ{$ns}{reason} = $1;
1805             }
1806             }
1807             }
1808              
1809 2 50       24 return wantarray ? ( \%answ, $code, $msg ) : \%answ;
1810             }
1811              
1812 0 0       0 return wantarray ? ( 0, 0, 'no answer' ) : 0;
1813             }
1814              
1815              
1816             =head2 create_ns
1817              
1818             Registering a nameserver
1819              
1820             INPUT:
1821              
1822             params with keys:
1823              
1824             C – name server
1825              
1826             C – array with IP, IPv4 and IPv6,
1827             IP must be specified only we register nameserver based on the domain of the same registry
1828              
1829             C – extensions for some providers, empty by default
1830              
1831             OUTPUT:
1832             see L
1833              
1834             An Example:
1835              
1836             my ( $h, $m, $o ) = make_request( 'create_ns', { ns => 'ns111.sssllll.info', %conn_params } );
1837              
1838             # check answer
1839              
1840             ( $a, $m ) = make_request( 'create_ns', { ns => 'ns222.ssslll.com', ips => ['1.2.3.4', 'fe80::aa00:bb11' ], conn => $o } );
1841              
1842             # check answer
1843              
1844             =cut
1845              
1846             sub create_ns {
1847 17     17 1 36 my ( $self, $params ) = @_;
1848              
1849 17 50       43 return ( 0, 0, 'no ns' ) unless $params->{ns};
1850              
1851 17         29 my $addrs = '';
1852 17 100 66     76 if ( $params->{ips} and ref( $params->{ips} ) eq 'ARRAY' ) {
1853 8         14 foreach my $ip ( @{$params->{ips}} ) {
  8         19  
1854 10 100       50 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
1855 6         22 $addrs .= '' . $ip . '';
1856             }
1857             else {
1858 4         15 $addrs .= '' . $ip . '';
1859             }
1860             }
1861             }
1862              
1863 17   100     67 my $ext = $$params{extension} || '';
1864              
1865 17 100       45 $ext = "\n \n$ext " if $ext;
1866              
1867 17         37 my $cltrid = get_cltrid();
1868              
1869 17         100 my $body = <
1870             $$self{urn}{head}
1871            
1872            
1873            
1874             $$params{ns}$addrs
1875            
1876             $ext
1877             $cltrid
1878            
1879            
1880             CREATENS
1881              
1882 17         43 return $self->simple_request( $body, 'create_ns' );
1883             }
1884              
1885              
1886             =head2 get_ns_info_rdata
1887              
1888             Covertor NS xml resData data to hash.
1889              
1890             Can be overwritten in a child module.
1891              
1892             =cut
1893              
1894             sub get_ns_info_rdata {
1895 2     2 1 8 my ( undef, $rdata ) = @_;
1896              
1897 2         5 my %ns;
1898              
1899 2         12 ( $ns{name} ) = $rdata =~ /([^<>]+)<\/host:name>/;
1900 2         7 $ns{name} = lc $ns{name};
1901              
1902 2         10 ( $ns{roid} ) = $rdata =~ /([^<>]+)<\/host:roid>/;
1903              
1904             #
1905 2         15 my @ss = $rdata =~ //g;
1906             # No changes pending
1907 2         6 my @aa = $rdata =~ /]+>[^<>]+<\/host:status>/g;
1908 2 50       7 if ( scalar @aa ) {
1909 0         0 foreach my $row ( @aa ) {
1910 0 0       0 if ( $row =~ /([^<>]+)<\/host:status>/ ) {
1911 0         0 $ns{statuses}{$1} = $2;
1912             }
1913             }
1914             }
1915             else {
1916 2         13 $ns{statuses}{$_} = '+' for @ss;
1917             }
1918              
1919 2         15 $ns{ips} = [ $rdata =~ /([0-9A-Fa-f.:]+)<\/host:addr>/g ];
1920              
1921             # owner, ...
1922 2         12 foreach my $k ( keys %id ) {
1923 10 100       163 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
1924 5         24 $ns{$id{$k}} = $1;
1925             }
1926             }
1927              
1928             # dates
1929 2         9 foreach my $k ( keys %dt ) {
1930 12 100       193 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
1931 3         23 $ns{$dt{$k}} = $1;
1932              
1933 3         14 $ns{$dt{$k}} =~ s/T/ /;
1934 3         15 $ns{$dt{$k}} =~ s/\.\d+Z$//;
1935 3         10 $ns{$dt{$k}} =~ s/Z$//;
1936             }
1937             }
1938              
1939 2         11 return \%ns;
1940             }
1941              
1942              
1943             =head2 get_ns_info
1944              
1945             Get information about the specified nameserver
1946              
1947             INPUT:
1948              
1949             params with keys:
1950              
1951             C – name server;
1952              
1953             C – extensions for some providers, empty by default.
1954              
1955             OUTPUT:
1956              
1957             hash with ns data: statuses, dates, ips and other
1958              
1959             C – the account where the name server is located;
1960              
1961             C – the account where the name server was registered;
1962              
1963             C – name server registration date;
1964              
1965             C – name server id in the registry;
1966              
1967             C – list of IP addresses, IPv4 and IPv6;
1968              
1969             C – this status indicates that the name server is being used by some domain.
1970              
1971             An Example:
1972              
1973             my ( $answer, $msg, $conn ) = make_request( 'get_ns_info', { ns => 'ns1.sss.ru.com', %conn_params } );
1974              
1975             # answer:
1976              
1977             {
1978             'msg' => 'Command completed successfully.',
1979             'owner' => 'H2220222',
1980             'roid' => 'H370000-CNIC',
1981             'cre_date' => '2013-09-05 18:42:49',
1982             'name' => 'ns1.sss.ru.com',
1983             'ips' => [
1984             '2A00:3B00:0:0:0:0:0:25'
1985             ],
1986             'creater' => 'H2220222',
1987             'statuses' => {
1988             'ok' => '+',
1989             'linked' => '+'
1990             },
1991             'code' => '1000'
1992             };
1993              
1994             =cut
1995              
1996             sub get_ns_info {
1997 6     6 1 12 my ( $self, $params ) = @_;
1998              
1999 6 50       19 return ( 0, 0, 'no ns' ) unless $params->{ns};
2000              
2001 6   100     21 my $ext = $$params{extension} || '';
2002              
2003 6 100       17 $ext = "\n \n$ext " if $ext;
2004              
2005 6         14 my $cltrid = get_cltrid();
2006              
2007 6         44 my $body = <
2008             $$self{urn}{head}
2009            
2010            
2011            
2012             $$params{ns}
2013            
2014             $ext
2015             $cltrid
2016            
2017            
2018             NSINFO
2019              
2020 6         16 my $content = $self->req( $body, 'get_ns_info' );
2021              
2022 6 50       32 if ( $content =~ /result code=['"](\d+)['"]/ ) {
2023 6         19 my $rcode = $1 + 0;
2024              
2025 6         12 my $msg = '';
2026 6 50       78 if ( $content =~ /]*>(.+)<\/msg>.+\/result>/s ) {
2027 6         18 $msg = $1;
2028             }
2029              
2030 6         10 my $ns = {};
2031              
2032             # вытягиваем смысловую часть и парсим
2033 6 100       41 if ( $content =~ /(.+)<\/resData>/s ) {
2034 2         7 my $rdata = $1;
2035              
2036 2         22 $ns = $self->get_ns_info_rdata( $rdata );
2037             }
2038              
2039 6 50       36 return wantarray ? ( $ns, $rcode, $msg ) : $ns;
2040             }
2041              
2042 0         0 return 0;
2043             }
2044              
2045             =head2 update_ns
2046              
2047             Change the data of the specified name server
2048              
2049             INPUT
2050              
2051             params with keys:
2052              
2053             C – name server
2054              
2055             C, C – adding or removing the name server parameters listed below:
2056              
2057             C – IPv4 and IPv6 addresses;
2058              
2059             C – clientUpdateProhibited and other client*;
2060              
2061             C – change the server name, this is used to move the name server to a different domain.
2062              
2063             C – some registries prohibit passing an empty chg parameter – C<< >>
2064              
2065             C – extensions for some providers, empty by default
2066              
2067             OUTPUT:
2068             see L.
2069              
2070             An Example
2071              
2072             my ( $answ, $msg, $conn ) = make_request( 'update_ns', {
2073             ns => 'ns1.sss.ru.com',
2074             rem => { ips => [ '2A00:3B00:0:0:0:0:0:25' ] },
2075             add => { ips => [ '176.99.13.11' ] },
2076             %conn_params,
2077             } );
2078              
2079             ( $answ, $msg ) = make_request( 'update_ns', {
2080             ns => 'ns1.sss.ru.com',
2081             chg => { new_name => 'ns1.sss.xyz' },
2082             conn => $conn,
2083             } );
2084              
2085             =cut
2086              
2087             sub update_ns {
2088 16     16 1 30 my ( $self, $params ) = @_;
2089              
2090 16 50       35 return ( 0, 0, 'no ns' ) unless $$params{ns};
2091              
2092 16         24 my $add = '';
2093              
2094 16 100       37 if ( $params->{add} ) {
2095 11 100 66     49 if ( $params->{add}{ips} and ref $params->{add}{ips} ) {
2096 7         11 foreach my $ip ( @{$params->{add}{ips}} ) {
  7         18  
2097 7 100       44 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
2098 5         15 $add .= ' '.$ip."\n";
2099             }
2100             else {
2101 2         10 $add .= ' '.$ip."\n";
2102             }
2103             }
2104             }
2105              
2106 11 100       28 if ( $params->{add}{statuses} ) {
2107 4         13 $add .= $self->update_statuses_add( 'host', $params->{add}{statuses} );
2108             }
2109             }
2110              
2111 16 100       35 if ( $add ) {
2112 11         22 $add = "\n$add ";
2113             }
2114             else {
2115 5         16 $add = '';
2116             }
2117              
2118 16         24 my $rem = '';
2119              
2120 16 100       33 if ( $params->{rem} ) {
2121 5 100 66     22 if ( defined $params->{rem}{ips} and ref $params->{rem}{ips} ) {
2122 3         4 foreach my $ip ( @{$params->{rem}{ips}} ) {
  3         7  
2123 4 100       19 if ( $ip =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
2124 3         9 $rem .= ' '.$ip."\n";
2125             }
2126             else {
2127 1         3 $rem .= ' '.$ip."\n";
2128             }
2129             }
2130             }
2131              
2132 5 100       12 if ( $params->{rem}{statuses} ) {
2133 2         15 $rem .= $self->update_statuses_rem( 'host', $params->{rem}{statuses} );
2134             }
2135             }
2136              
2137 16 100       27 if ( $rem ) {
2138 5         11 $rem = "\n$rem ";
2139             }
2140             else {
2141 11         19 $rem = "";
2142             }
2143              
2144 16         21 my $chg = '';
2145              
2146 16 100       31 if ( $params->{chg} ) {
2147 1 50       4 if ( $params->{chg}{new_name} ) {
2148 0         0 $chg .= " " . $$params{chg}{new_name} . "\n";
2149             }
2150             }
2151              
2152 16 50       42 if ( $chg ) {
    100          
2153 0         0 $chg = "\n$chg \n";
2154             }
2155             elsif ( !$params->{no_empty_chg} ) {
2156 3         5 $chg = "";
2157             }
2158              
2159 16   100     39 my $ext = $$params{extension} || '';
2160              
2161 16 100       36 $ext = "\n \n$ext " if $ext;
2162              
2163 16         32 my $cltrid = get_cltrid();
2164              
2165 16         113 my $body = <
2166             $$self{urn}{head}
2167            
2168            
2169            
2170             $$params{ns}
2171             $add
2172             $rem
2173             $chg
2174            
2175             $ext
2176             $cltrid
2177            
2178            
2179             UPDATENS
2180              
2181 16         49 return $self->simple_request( $body, 'update_ns' );
2182             }
2183              
2184              
2185             =head2 delete_ns
2186              
2187             Remove nameserver from the registry.
2188              
2189             It is usually forbidden to delete a name server that has the C status.
2190              
2191             INPUT:
2192              
2193             params with keys:
2194              
2195             C – name server;
2196              
2197             C – extensions for some providers, empty by default.
2198              
2199             OUTPUT:
2200             see L.
2201              
2202             An Example:
2203              
2204             my ( $answ, $msg ) = make_request( 'delete_ns', { ns => 'ns1.sss.ru.com', %conn_params } );
2205              
2206             =cut
2207              
2208             sub delete_ns {
2209 5     5 1 10 my ( $self, $params ) = @_;
2210              
2211 5 50       17 return ( 0, 0, 'no ns' ) unless $$params{ns};
2212              
2213 5   100     18 my $ext = $$params{extension} || '';
2214              
2215 5 100       18 $ext = "\n \n$ext " if $ext;
2216              
2217 5         9 my $cltrid = get_cltrid();
2218              
2219 5         30 my $body = <
2220             $$self{urn}{head}
2221            
2222            
2223            
2224             $$params{ns}
2225            
2226             $ext
2227             $cltrid
2228            
2229            
2230             DELNS
2231              
2232 5         14 return $self->simple_request( $body, 'delete_ns' );
2233             }
2234              
2235              
2236             =head2 check_domains_rdata
2237              
2238             Parses resData in the registry response
2239              
2240             Can be overwritten in a child module.
2241              
2242             =cut
2243              
2244             sub check_domains_rdata {
2245 3     3 1 11 my ( undef, $rdata ) = @_;
2246              
2247 3         6 my %domlist;
2248              
2249 3         66 my @aa = $rdata =~ /\s*([^<>]+<\/domain:name>(?:\s*[^<>]+<\/domain:reason>)?)\s*<\/domain:cd>/sg;
2250              
2251 3         9 foreach my $a ( @aa ) {
2252 26 50       110 if ( $a =~ /([^<>]+)<\/domain:name>/ ) {
2253 26         63 my $dm = lc($2);
2254              
2255 26         89 $domlist{$dm} = { avail => $1 }; # no utf8, puny only
2256              
2257 26 100       90 if ( $a =~ /([^<>]+)<\/domain:reason>/ ) {
2258 8         25 $domlist{$dm}{reason} = $1;
2259             }
2260             }
2261             }
2262              
2263 3 50       13 if ( $rdata =~ /claims<\/launch:phase>/ ) {
2264             # this is a call with an extension to get the key, if there is one
2265 0 0       0 if ( $rdata =~ /([0-9a-z.\-]+)<\/launch:name>\n?\s*([^<>]+)<\/launch:claimKey>/ ) {
2266 0         0 $domlist{ lc($2) }{claim} = { avail => $1, claimkey => $3 };
2267             }
2268             }
2269              
2270 3 50       23 if ( $rdata =~ /]+>(.+)<\/fee:chkData>/ ) {
2271             # this is a call with the extension draft-brown-epp-fees-02
2272 0         0 my $fee = $1;
2273              
2274 0         0 my @ff = $fee =~ /(.+)<\/fee:cd>/g;
2275              
2276 0         0 foreach my $f ( @ff ) {
2277 0         0 $f =~ /([0-9a-z\-\.])<\/fee:name>.*([0-9\.])<\/fee:fee>/;
2278 0         0 $domlist{ lc($1) }{fee} = { new => $2 }
2279             }
2280             }
2281              
2282 3         12 return \%domlist;
2283             }
2284              
2285             =head2 check_domains
2286              
2287             Check that the domain is available for registration
2288              
2289             INPUT:
2290              
2291             params with keys:
2292              
2293             C – list of domains to check;
2294              
2295             C – extensions for some providers, empty by default.
2296              
2297             OUTPUT:
2298              
2299             hash with domains, each domain has an C parameter, if avail == 0, the C parameter is usually added
2300              
2301             An Example
2302              
2303             my ( $answer, $code, $msg ) = $conn->check_domains( {
2304             tld => 'com',
2305             domains => [ 'qwerty.com', 'bjdwferbkre3jd0hf.net', 'xn--xx.com', 'hiebw.info' ],
2306             } );
2307              
2308             # answer:
2309              
2310             {
2311             'qwerty.com' => {
2312             'reason' => 'Domain exists',
2313             'avail' => '0'
2314             },
2315             'bjdwferbkre3jd0hf.net' => {
2316             'avail' => '1'
2317             },
2318             'hiebw.info' => {
2319             'reason' => 'Not an authoritative TLD',
2320             'avail' => '0'
2321             },
2322             'xn--xx.com' => {
2323             'reason' => 'Invalid punycode encoding',
2324             'avail' => '0'
2325             }
2326             };
2327              
2328             =cut
2329              
2330             sub check_domains {
2331 3     3 1 12 my ( $self, $params ) = @_;
2332              
2333 3 50 50     14 return ( 0, 0, 'no domains' ) unless $params->{domains} && scalar( @{$params->{domains}} );
  3         16  
2334              
2335 3         7 my $dms = '';
2336              
2337 3         7 foreach my $dm ( @{$params->{domains}} ) {
  3         11  
2338 26         51 $dms .= "$dm";
2339             }
2340              
2341 3   100     18 my $ext = $$params{extension} || '';
2342              
2343 3 100       12 $ext = "\n \n$ext " if $ext;
2344              
2345 3         11 my $cltrid = get_cltrid();
2346              
2347 3         26 my $body = <
2348             $$self{urn}{head}
2349            
2350            
2351            
2352             $dms
2353            
2354             $ext
2355             $cltrid
2356            
2357            
2358             CHECKDOMS
2359              
2360 3   50     11 my $answ = $self->req( $body, 'check_domains' ) // '';
2361              
2362 3         9 my $rcode = '';
2363 3         6 my $msg = '';
2364              
2365 3 50       24 if ( $answ =~ /
2366 3         14 $rcode = $1 + 0;
2367             }
2368              
2369 3 50       20 if ( $answ =~ /]*>([^<>]+)<\/msg>/ ) {
2370 3         9 $msg = $1;
2371             }
2372              
2373 3 50       35 if ( $answ =~ /(.+)<\/resData>/s ) {
2374 3   50     18 my $rdata = $1 // '';
2375              
2376 3         28 my $domlist = $self->check_domains_rdata( $rdata );
2377              
2378 3 50       24 return wantarray ? ( $domlist, $rcode, $msg ) : $domlist;
2379             }
2380              
2381 0 0       0 return wantarray ? ( 0, $rcode, $msg ) : 0;
2382             }
2383              
2384              
2385             =head2 create_domain_nss
2386              
2387             Generating a list of ns-s for domain registration.
2388              
2389             Can be overwritten in a child module.
2390              
2391             =cut
2392              
2393             sub create_domain_nss {
2394 14     14 1 26 my ( $self, $params ) = @_;
2395              
2396 14         22 my $nss = '';
2397              
2398 14         22 foreach my $ns ( @{$params->{nss}} ) {
  14         31  
2399 27         69 $nss .= " $ns\n";
2400             }
2401              
2402 14 50       60 $nss = "\n \n$nss " if $nss;
2403              
2404 14         33 return $nss;
2405             }
2406              
2407              
2408             =head2 create_domain_authinfo
2409              
2410             authinfo block for domain registration.
2411              
2412             Can be overwritten in a child module.
2413              
2414             =cut
2415              
2416             sub create_domain_authinfo {
2417 21     21 1 33 my ( $self, $params ) = @_;
2418              
2419             # Some providers require an empty authinfo, but no
2420 21 50       44 if ( exists $params->{authinfo} ) {
2421 21         61 return "\n \n $$params{authinfo}\n ";
2422             }
2423              
2424 0         0 return "\n \n \n ";
2425             }
2426              
2427              
2428             =head2 create_domain_ext
2429              
2430             Block with the DNSSEC extension for domain registration
2431              
2432             Can be overwritten in a child module.
2433              
2434             =cut
2435              
2436             sub create_domain_ext {
2437 21     21 1 34 my ( $self, $params ) = @_;
2438              
2439 21         32 my $ext = '';
2440              
2441 21 50       41 if ( $params->{dnssec} ) {
2442 0         0 my $dsdata = '';
2443 0         0 foreach my $raw ( @{$params->{dnssec}} ) {
  0         0  
2444 0         0 my $ds = '';
2445 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
2446 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
2447 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
2448 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
2449              
2450 0 0       0 $dsdata .= " \n$ds \n" if $ds;
2451             }
2452              
2453 0 0       0 $ext = " \n$dsdata \n"
2454             if $dsdata;
2455             }
2456              
2457 21         46 return $ext;
2458             }
2459              
2460              
2461             =head2 create_domain
2462              
2463             Domain registration.
2464              
2465             INPUT:
2466              
2467             params with keys:
2468              
2469             C – domain name;
2470              
2471             C – domain registration period, usually the default value is 1 year,
2472             registration for several months is not implemented – this is a very rare case;
2473              
2474             C, C, C, C – id of registrant, administrator, technical and billing contacts,
2475             at least one contact is required, usually the registrant;
2476              
2477             C -- hash for DNSSEC params: C, C, C, C,
2478             for details see L;
2479              
2480             C – array with nameservers;
2481              
2482             C – extensions for some providers, empty by default.
2483              
2484             OUTPUT:
2485             see L.
2486              
2487             An Example:
2488              
2489             my ( $answ, $msg ) = make_request(
2490             'create_domain',
2491             {
2492             dname => "sss.ru.com",
2493             reg_id => 'jp1g8fcv30fq',
2494             admin_id => 'jp1g8fcv31fq',
2495             tech_id => 'jp1g8fcv32fq',
2496             billing_id => 'jp1g8fcv33fq',
2497             authinfo => 'jp1g8fcv30fq+jp1g8fcv31fq',
2498             nss => [ 'dns1.yandex.net','dns2.yandex.net' ],
2499             period => 1,
2500             },
2501             );
2502              
2503             =cut
2504              
2505             sub create_domain {
2506 21     21 1 43 my ( $self, $params ) = @_;
2507              
2508 21 50       48 return ( 0, 0, 'no dname' ) unless $params->{dname};
2509              
2510 21         35 my $nss = '';
2511 21 100 100     46 if ( $params->{nss} && scalar @{$params->{nss}} ) {
  15         45  
2512 14         38 $nss = $self->create_domain_nss( $params );
2513             }
2514              
2515 21         37 my $cont = '';
2516             # 1. There is a zone without an owner, but with admin :)
2517             # 2. Verisign Core server -- without all contacts
2518 21 100       52 $cont .= qq|\n $$params{reg_id}| if $$params{reg_id};
2519              
2520 21         37 foreach my $t ( 'tech', 'admin', 'billing' ) {
2521 63 100       137 if ( $$params{$t.'_id'} ) {
2522 36 50       106 $$params{$t.'_id'} = [ $$params{$t.'_id'} ] unless ref $$params{$t.'_id'};
2523              
2524 36         49 foreach my $c ( @{$$params{$t.'_id'}} ) {
  36         67  
2525 36         77 $cont .= qq|\n $c|;
2526             }
2527             }
2528             }
2529              
2530 21         31 my $descr = ''; # tcinet registry
2531 21 50       44 if ( $params->{descr} ) {
2532 0 0       0 $params->{descr} = [ $params->{descr} ] unless ref $params->{descr};
2533              
2534 0         0 $descr .= "\n $_" for @{$params->{descr}};
  0         0  
2535             }
2536              
2537 21         49 my $authinfo = $self->create_domain_authinfo( $params );
2538              
2539 21   100     72 my $ext = $params->{extension} || '';
2540              
2541 21         53 $ext .= $self->create_domain_ext( $params );
2542              
2543 21 100       52 $ext = "\n \n$ext " if $ext;
2544              
2545 21         39 my $cltrid = get_cltrid();
2546              
2547 21         174 my $body = <
2548             $$self{urn}{head}
2549            
2550            
2551            
2552             $$params{dname}
2553             $$params{period}$nss$cont$authinfo$descr
2554            
2555             $ext
2556             $cltrid
2557            
2558            
2559             CREATEDOM
2560              
2561 21         54 return $self->simple_request( $body, 'create_domain' );
2562             }
2563              
2564              
2565             =head2 get_domain_info_rdata
2566              
2567             Covertor domains xml resData data to hash.
2568              
2569             Can be overwritten in a child module.
2570              
2571             =cut
2572              
2573             sub get_domain_info_rdata {
2574 10     10 1 22 my ( $self, $rdata ) = @_;
2575              
2576 10         14 my $info = {};
2577              
2578 10         53 ( $info->{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
2579 10         24 $info->{dname} = lc $info->{dname};
2580              
2581             #
2582 10         56 my @ss = $rdata =~ //g;
2583             # No reason supplied
2584 10         33 my @aa = $rdata =~ /]+>[^<>]+<\/domain:status>/g;
2585 10 50       25 if ( scalar @aa ) {
2586 0         0 foreach my $row ( @aa ) {
2587 0 0       0 if ( $row =~ /([^<>]+)<\/domain:status>/ ) {
2588 0         0 $info->{statuses}{$1} = $2;
2589             }
2590             }
2591             }
2592             else {
2593 10         46 $info->{statuses}{$_} = '+' for @ss;
2594             }
2595              
2596 10 100       41 if ( $rdata =~ /([^<>]+)<\/domain:registrant>/ ) {
2597             # One of the .ua zones uses admin instead of owner
2598 3         9 $info->{reg_id} = $1;
2599             }
2600              
2601 10         41 my @cc = $rdata =~ /[^<>]+<\/domain:contact>/g;
2602 10         22 foreach my $row ( @cc ) {
2603 9 50       38 if ( $row =~ /([^<>]+)<\/domain:contact>/ ) {
2604 9         34 $info->{ lc($1) . '_id' } = $2;
2605             }
2606             }
2607              
2608 10 50       29 if ( $rdata =~ // ) {
2609 0         0 $info->{descr} = [ $rdata =~ /([^<>]+)<\/domain:description>/g ];
2610             }
2611              
2612 10 50       29 if ( $rdata =~ // ) {
2613 10         88 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:hostObj>/g ];
2614             }
2615              
2616 10 50 33     48 unless ( $info->{nss} or $rdata !~ // ) {
2617             # some providers use the old variant for some zones, example: irrp for ph
2618             # this is a rare option, so it is made separately and not built into the previous regexp
2619 0         0 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:hostName>/g ];
2620             }
2621              
2622 10 0 33     29 unless ( $info->{nss} or $rdata !~ // or scalar @{$info->{nss}} ) {
  0   50     0  
2623             # 1 more ancient artifact, probably the oldest
2624 0         0 $info->{nss} = [ $rdata =~ /([^<>]+)<\/domain:ns>/g ];
2625             }
2626              
2627 10 50       25 if ( $info->{nss} ) {
2628 10         14 $info->{nss} = [ map{ lc $_ } @{$info->{nss}} ];
  20         61  
  10         28  
2629             }
2630              
2631             # Domain-based nss
2632 10 100       54 if ( $rdata =~ // ) {
2633 4         32 $info->{hosts} = [ $rdata =~ /([^<>]+)<\/domain:host>/g ];
2634 4         7 $info->{hosts} = [ map{ lc $_ } @{$info->{hosts}} ];
  5         15  
  4         13  
2635             }
2636              
2637             # owner, ...
2638 10         42 foreach my $k ( keys %id ) {
2639 50 100       824 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2640 30         129 $info->{$id{$k}} = $1;
2641             }
2642             }
2643              
2644             # dates
2645 10         35 foreach my $k ( keys %dt ) {
2646 60 100       903 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
2647 30         70 $info->{$dt{$k}} = cldate( $1 );
2648             }
2649             }
2650              
2651 10 100       78 if ( $rdata =~ /authInfo.+([^<>]+)<\/domain:pw>.+authInfo/s ) {
2652 7         20 ( $info->{authinfo} ) = $1;
2653              
2654 7         13 $info->{authinfo} =~ s/>/>/g;
2655 7         11 $info->{authinfo} =~ s/</
2656 7         14 $info->{authinfo} =~ s/&/&/g;
2657             }
2658              
2659 10         65 ( $info->{roid} ) = $rdata =~ /([^<>]+)<\/domain:roid>/;
2660              
2661 10         36 my $spec = $self->get_domain_spec_rdata( $rdata );
2662 10         65 $info->{$_} = $spec->{$_} for keys %$spec;
2663              
2664 10         41 return $info;
2665             }
2666              
2667              
2668             =head2 get_domain_spec_rdata
2669              
2670             Parse special data in resData from provider.
2671              
2672             For overwritten in a child module.
2673              
2674             In this module, the function does nothing
2675              
2676             =cut
2677              
2678             sub get_domain_spec_rdata {
2679 10     10 1 19 return {};
2680             }
2681              
2682              
2683             =head2 get_domain_spec_ext
2684              
2685             Parse special data in extension from provider.
2686              
2687             For overwritten in a child module.
2688              
2689             In this module, the function does nothing
2690              
2691             =cut
2692              
2693             sub get_domain_spec_ext {
2694 7     7 1 21 return {};
2695             }
2696              
2697              
2698             =head2 get_domain_info
2699              
2700             The main information on the domain
2701              
2702             INPUT:
2703              
2704             params with keys:
2705              
2706             C – domain name;
2707              
2708             C – extensions for some providers, empty by default.
2709              
2710             OUTPUT:
2711              
2712             C;
2713              
2714             C – domain id if registry;
2715              
2716             C – the account where the domain is located now;
2717              
2718             C – the account where the domain was registered;
2719              
2720             C – domain registration date;
2721              
2722             C – domain last transfer date;
2723              
2724             C – domain last update date;
2725              
2726             C – domain expiration date;
2727              
2728             C, C, C, C – domain contact IDs;
2729              
2730             C – list of domain name servers;
2731              
2732             C – hash where keys is status flags, values is status expiration date, if any, or other information;
2733              
2734             C – list with name servers based on this domain.
2735              
2736             There can also be extension parameters.
2737              
2738             An Example:
2739              
2740             my ( $answer, $msg, $conn ) = make_request( 'get_domain_info', { dname => 'sss.ru.com', %conn_params } );
2741              
2742             # answer:
2743              
2744             {
2745             'hosts' => [
2746             'ns1.sss.ru.com',
2747             'ns2.sss.ru.com'
2748             ],
2749             'roid' => 'D888888-CNIC',
2750             'cre_date' => '1212-12-12 12:12:12',
2751             'upd_date' => '2020-02-02 20:02:20',
2752             'trans_date' => '2012-12-12 12:12:12',
2753             'creater' => 'H12345',
2754             'tech_id' => '1iuhajppwsjp',
2755             'reg_id' => 'H12346',
2756             'owner' => 'H2220222',
2757             'exp_date' => '2022-12-12 23:59:59',
2758             'billing_id' => 'H12347',
2759             'nss' => [
2760             'ns1.sss.ru.com'
2761             'ns1.mmm.ru.com'
2762             ],
2763             'dname' => 'sss.ru.com',
2764             'admin_id' => 'H12348',
2765             'statuses' => {
2766             'renewPeriod' => '+',
2767             'clientTransferProhibited' => '+'
2768             }
2769             };
2770              
2771             =cut
2772              
2773             sub get_domain_info {
2774 14     14 1 30 my ( $self, $params ) = @_;
2775              
2776 14 50       40 unless ( $$params{dname} ) {
2777 0 0       0 return wantarray ? ( 0, 0, 'no dname') : 0;
2778             }
2779              
2780 14 50       40 my $pw = $$params{authinfo} ? "\n \n $$params{authinfo}\n " : '';
2781              
2782 14 50       32 my $hosts_type = $$params{hosts} ? ' hosts="'.$$params{hosts}.'"' : '';
2783              
2784 14   100     41 my $ext = $$params{extension} || '';
2785              
2786 14 100       38 $ext = "\n \n$ext " if $ext;
2787              
2788 14         30 my $cltrid = get_cltrid();
2789              
2790 14         88 my $body = <
2791             $$self{urn}{head}
2792            
2793            
2794            
2795             $$params{dname}$pw
2796            
2797             $ext
2798             $cltrid
2799            
2800            
2801             DOMINFO
2802              
2803 14         38 my $answ = $self->req( $body, 'domain_info' );
2804              
2805 14 50 33     127 if ( $answ && $answ =~ // ) {
2806 14         54 my $rcode = $1 + 0;
2807              
2808 14         22 my $msg = '';
2809 14 50       303 if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
2810 14         36 $msg = $1;
2811             }
2812              
2813 14 100       36 if ( $rcode != 1000 ) {
2814 4 100       20 if ( $answ =~ /(.+)<\/reason>/s ) {
2815             # for details
2816 2         8 $msg .= '; ' . $1;
2817             }
2818              
2819 4 50       14 if ( $answ =~ /(.+)<\/oxrs:xcp>/s ) {
2820             # for oxrs details
2821 0         0 $msg .= '; ' . $1;
2822             }
2823              
2824 4 50       34 return wantarray ? ( 0, $rcode, $msg ) : 0;
2825             }
2826              
2827 10         20 my $info = {};
2828              
2829             # pull out the main part and parse
2830 10 50       74 if ( $answ =~ /(.+)<\/resData>/s ) {
2831 10   50     44 my $rdata = $1 // '';
2832              
2833 10         36 $info = $self->get_domain_info_rdata( $rdata );
2834             }
2835              
2836 10 100       70 if ( $answ =~ /(.+)<\/extension>/s ) {
2837 7         18 my $rdata = $1;
2838              
2839 7         27 my @st = $rdata =~ //g;
2840 7         15 $info->{statuses}{$_} = '+' for @st;
2841              
2842 7         44 my @est = $rdata =~ /([^<>]+<\/rgp:rgpStatus>)/g;
2843              
2844 7         15 foreach my $e ( @est ) {
2845 14         56 my ( $st, $descr ) = $e =~ /([^<>]+)<\/rgp:rgpStatus>/;
2846              
2847 14 50       42 if ( $descr =~ /^endDate=/ ) {
2848 14         38 $descr =~ s/T/ /;
2849 14         42 $descr =~ s/\.\d+Z$//;
2850 14         21 $descr =~ s/Z$//;
2851             }
2852 14         39 $info->{statuses}{$st} = $descr;
2853             }
2854              
2855 7 50       21 if ( $rdata =~ /secDNS:infData/ ) {
2856 0         0 $info->{dnssec} = [];
2857              
2858 0         0 my @dsdata = $rdata =~ /(.+?)<\/secDNS:dsData>/g;
2859 0         0 foreach my $sdata ( @dsdata ) {
2860 0         0 my %one_raw;
2861 0         0 ( $one_raw{keytag} ) = $sdata =~ /(\d+)<\/secDNS:keyTag>/;
2862 0         0 ( $one_raw{alg} ) = $sdata =~ /(\d+)<\/secDNS:alg>/;
2863 0         0 ( $one_raw{digtype} ) = $sdata =~ /(\d+)<\/secDNS:digestType>/;
2864 0         0 ( $one_raw{digest} ) = $sdata =~ /([A-Za-z0-9]+)<\/secDNS:digest>/;
2865              
2866 0 0       0 if ( $sdata =~ /(.+)<\/secDNS:keyData>/s ) {
2867 0         0 my $kdata = $1;
2868              
2869 0         0 $one_raw{keydata} = {};
2870 0         0 ( $one_raw{keydata}{flags} ) = $kdata =~ /(\d+)<\/secDNS:flags>/;
2871 0         0 ( $one_raw{keydata}{protocol} ) = $kdata =~ /(\d+)<\/secDNS:protocol>/;
2872 0         0 ( $one_raw{keydata}{alg} ) = $kdata =~ /(\d+)<\/secDNS:alg>/;
2873 0         0 ( $one_raw{keydata}{pubkey} ) = $kdata =~ /([^<>]+)<\/secDNS:pubKey>/;
2874             }
2875              
2876 0         0 push @{$$info{dnssec}}, \%one_raw;
  0         0  
2877             }
2878             }
2879              
2880 7         26 my $spec = $self->get_domain_spec_ext( $rdata );
2881 7         26 $info->{$_} = $spec->{$_} for keys %$spec;
2882             }
2883              
2884 10 50       72 return wantarray ? ( $info, $rcode, $msg ) : $info;
2885             }
2886              
2887 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0;
2888             }
2889              
2890              
2891             =head2 renew_domain
2892              
2893             Domain registration renewal for N years.
2894              
2895             INPUT:
2896              
2897             params with keys:
2898              
2899             C – domain name;
2900              
2901             C – the domain renewal period in years, by default, will be prologed for 1 year;
2902              
2903             C – current expiration date, without specifying the time;
2904              
2905             C – extensions for some providers, empty by default.
2906              
2907             OUTPUT:
2908             see L.
2909              
2910             An Example:
2911              
2912             my ( $a, $m ) = make_request( 'renew_domain', { dname => 'datada.net', period => 1, exp_date => '2022-22-22' } );
2913              
2914             =cut
2915              
2916             sub renew_domain {
2917 11     11 1 20 my ( $self, $params ) = @_;
2918              
2919 11 50 33     61 return ( 0, 0, 'no params' ) unless $$params{dname} && $$params{exp_date};
2920              
2921 11   50     27 $params->{period} ||= 1;
2922              
2923 11   100     32 my $ext = $params->{extension} || '';
2924              
2925 11 100       31 $ext = "\n \n$ext " if $ext;
2926              
2927 11         27 my $cltrid = get_cltrid();
2928              
2929 11         75 my $body = <
2930             $$self{urn}{head}
2931            
2932            
2933            
2934             $$params{dname}
2935             $$params{exp_date}
2936             $$params{period}
2937            
2938             $ext
2939             $cltrid
2940            
2941            
2942             RENEWDOM
2943              
2944 11         27 return $self->simple_request( $body, 'renew_domain' );
2945             }
2946              
2947              
2948             =head2 update_domain_add_nss
2949              
2950             Part of the update_domain function.
2951              
2952             Can be overwritten in a child module, example, in L
2953              
2954             =cut
2955              
2956             sub update_domain_add_nss {
2957 9     9 1 18 my ( undef, $params ) = @_;
2958              
2959 9         15 my $add = " \n";
2960              
2961 9         12 foreach my $ns ( @{$$params{add}{nss}} ) {
  9         23  
2962 9         31 $add .= " $ns\n";
2963             }
2964              
2965 9         21 $add .= " \n";
2966              
2967 9         20 return $add;
2968             }
2969              
2970              
2971             =head2 update_domain_rem_nss
2972              
2973             Part of the update_domain function.
2974              
2975             Can be overwritten in a child module.
2976              
2977             =cut
2978              
2979             sub update_domain_rem_nss {
2980 6     6 1 13 my ( undef, $params ) = @_;
2981              
2982 6         11 my $rem = " \n";
2983              
2984 6         12 foreach my $ns ( @{$$params{rem}{nss}} ) {
  6         14  
2985 6         20 $rem .= " $ns\n";
2986             }
2987              
2988 6         13 $rem .= " \n";
2989              
2990 6         13 return $rem;
2991             }
2992              
2993              
2994             =head2 update_domain_ext
2995              
2996             Part of the update_domain function.
2997              
2998             Can be overwritten in a child module.
2999              
3000             In this function this module contains the DNSSEC extension
3001              
3002             =cut
3003              
3004             sub update_domain_ext {
3005 39     39 1 73 my ( undef, $params ) = @_;
3006              
3007 39         54 my $ext = '';
3008              
3009 39         53 my $rem_ds = '';
3010 39 50 66     97 if ( $params->{rem} && $params->{rem}{dnssec} ) {
3011 0         0 foreach my $raw ( @{$params->{rem}{dnssec}} ) {
  0         0  
3012 0         0 my $ds = '';
3013 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
3014 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
3015 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
3016 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
3017              
3018 0 0       0 $rem_ds .= " \n$ds \n" if $ds;
3019             }
3020              
3021 0 0       0 $rem_ds = " \n$rem_ds \n" if $rem_ds;
3022             }
3023              
3024 39         71 my $add_ds = '';
3025 39 50 66     113 if ( $params->{add} && $params->{add}{dnssec} ) {
3026 0         0 foreach my $raw ( @{$params->{add}{dnssec}} ) {
  0         0  
3027 0         0 my $ds = '';
3028 0 0       0 $ds .= " $$raw{keytag}\n" if $raw->{keytag};
3029 0 0       0 $ds .= " $$raw{alg}\n" if $raw->{alg};
3030 0 0       0 $ds .= " $$raw{digtype}\n" if $raw->{digtype};
3031 0 0       0 $ds .= " $$raw{digest}\n" if $raw->{digest};
3032              
3033 0 0       0 $add_ds .= " \n$ds \n" if $ds;
3034             }
3035              
3036 0 0       0 $add_ds = " \n$add_ds \n" if $add_ds;
3037             }
3038              
3039 39 50 33     129 if ( $rem_ds || $add_ds ) {
3040 0         0 $ext .= "\n \n";
3041 0         0 $ext .= $rem_ds;
3042 0         0 $ext .= $add_ds;
3043 0         0 $ext .= " \n";
3044             }
3045              
3046 39         77 return $ext;
3047             }
3048              
3049             =head2 update_domain
3050              
3051             To update domain data: contact ids, authinfo, nss, statuses.
3052              
3053             INPUT:
3054              
3055             params with keys:
3056              
3057             C – domain name
3058              
3059             C, C – hashes for adding and deleting data:
3060              
3061             C, C, C – contact IDs;
3062              
3063             C – list with name servers;
3064              
3065             C – various client* statuses;
3066              
3067             C – DNSSEC extension parameters.
3068              
3069             C – hash for changeable data:
3070              
3071             C – registrant contact id;
3072              
3073             C – new key for domain;
3074              
3075             OUTPUT:
3076             see L.
3077              
3078             Examples:
3079              
3080             my ( $a, $m, $c ) = make_request( 'update_domain', {
3081             dname => 'example.com',
3082             chg => { authinfo => 'fnjkfrekrejkfrenkfrenjkfren' },
3083             rem => { nss => [ 'ns1.qqfklnqq.com', 'ns2.qqfklnqq.com' ] },
3084             add => { nss => [ 'ns1.web.name', 'ns2.web.name' ] },
3085             %conn_params,
3086             } );
3087              
3088             ( $a, $m ) = make_request( 'update_domain', {
3089             dname => 'example.com',
3090             rem => { statuses => [ 'clientUpdateProhibited','clientDeleteProhibited' ] },
3091             add => { statuses => [ 'clientHold' ] },
3092             conn => $c,
3093             } );
3094              
3095             =cut
3096              
3097             sub update_domain {
3098 39     39 1 73 my ( $self, $params ) = @_;
3099              
3100 39 50       98 return ( 0, 0, 'no params' ) unless ref $params;
3101              
3102 39 50       86 return ( 0, 0, 'no dname' ) unless $params->{dname};
3103              
3104 39         61 my $nm = 'update_domain';
3105              
3106 39         50 my $add = '';
3107 39 100       90 if ( ref $$params{add} ) {
3108 21 50 66     75 if ( $$params{add}{nss} && ref $$params{add}{nss} && scalar( @{$$params{add}{nss}} ) ) {
  9   100     29  
3109 9         28 $add .= $self->update_domain_add_nss( $params );
3110              
3111 9         14 $nm .= '_add_ns';
3112             }
3113              
3114 21         43 foreach my $t ( 'admin', 'billing', 'tech' ) {
3115 63 100       150 if ( $$params{add}{$t.'_id'} ) {
3116 2 50       9 $$params{add}{$t.'_id'} = [ $$params{add}{$t.'_id'} ] unless ref $$params{add}{$t.'_id'};
3117              
3118 2         5 foreach my $c ( @{$$params{add}{$t.'_id'}} ) {
  2         7  
3119 2         7 $add .= qq| $c\n|;
3120             }
3121             }
3122             }
3123              
3124 21 100       48 if ( $params->{add}{statuses} ) {
3125 10         29 $add .= $self->update_statuses_add( 'domain', $params->{add}{statuses} );
3126              
3127 10         17 $nm .= '_add_status';
3128             }
3129             }
3130              
3131 39 100       76 if ( $add ) {
3132 21         47 $add = "\n$add ";
3133             }
3134             else {
3135 18         27 $add = '';
3136             }
3137              
3138 39         58 my $chg = '';
3139 39 100       84 if ( ref $$params{chg} ) {
3140 9 100       71 if ( $$params{chg}{reg_id} ) {
3141 3         12 $chg .= ' ' . $$params{chg}{reg_id} . "\n";
3142              
3143 3         7 $nm .= '_chg_cont';
3144             }
3145              
3146 9 100       22 if ( $$params{chg}{authinfo} ) {
3147 6         21 $chg .= " \n ".$$params{chg}{authinfo}."\n \n";
3148              
3149 6         10 $nm .= '_chg_key';
3150             }
3151              
3152 9 50       18 if ( $params->{chg}{descr} ) {
3153 0 0       0 $params->{chg}{descr} = [ $params->{chg}{descr} ] unless ref $params->{chg}{descr};
3154              
3155 0         0 $chg .= " $_\n" foreach @{$params->{chg}{descr}};
  0         0  
3156              
3157 0         0 $nm .= '_chg_descr';
3158             }
3159             }
3160              
3161 39 100       68 if ( $chg ) {
3162 9         23 $chg = "\n$chg ";
3163             }
3164             else {
3165 30         47 $chg = '';
3166             }
3167              
3168 39         54 my $rem = '';
3169 39 100       72 if ( $$params{rem} ) {
3170 10 50 66     46 if ( $$params{rem}{nss} && ref $$params{rem}{nss} && scalar( @{$$params{rem}{nss}} ) ) {
  6   100     21  
3171 6         20 $rem .= $self->update_domain_rem_nss( $params );
3172              
3173 6         11 $nm .= '_del_ns';
3174             }
3175              
3176 10         21 foreach my $t ( 'admin', 'billing', 'tech' ) {
3177 30 100       71 if ( $$params{rem}{$t.'_id'} ) {
3178 2 50       8 $$params{rem}{$t.'_id'} = [ $$params{rem}{$t.'_id'} ] unless ref $$params{rem}{$t.'_id'};
3179              
3180 2         5 foreach my $c ( @{$$params{rem}{$t.'_id'}} ) {
  2         5  
3181 2         7 $rem .= qq| $c\n|;
3182             }
3183             }
3184             }
3185              
3186 10 100       25 if ( $$params{rem}{statuses} ) {
3187 2         10 $rem .= $self->update_statuses_rem( 'domain', $$params{rem}{statuses} );
3188              
3189 2         4 $nm .= '_del_status';
3190             }
3191             }
3192              
3193 39 100       64 if ( $rem ) {
3194 10         25 $rem = "\n$rem ";
3195             }
3196             else {
3197 29         40 $rem = '';
3198             }
3199              
3200 39   100     130 my $ext = $$params{extension} || '';
3201              
3202 39         98 $ext .= $self->update_domain_ext( $params );
3203              
3204 39 100       92 $ext = "\n \n$ext " if $ext;
3205              
3206 39         70 my $cltrid = get_cltrid();
3207              
3208 39         277 my $body = <
3209             $$self{urn}{head}
3210            
3211            
3212            
3213             $$params{dname}
3214             $add
3215             $rem
3216             $chg
3217            
3218             $ext
3219             $cltrid
3220            
3221            
3222             UPDDOM
3223              
3224 39         94 return $self->simple_request( $body, $nm );
3225             }
3226              
3227              
3228             =head2 transfer
3229              
3230             Domain transfers: to us, from us, reject transfers.
3231              
3232             INPUT:
3233              
3234             params with keys:
3235              
3236             C – operation, possible variants: C, C, C, C, C;
3237              
3238             C – key for alien domain;
3239              
3240             C – if the transfer with renew, you can specify the extension period for some registries, undef and zero have different values;
3241              
3242             C – extensions for some registries in xml format;
3243              
3244             C – special parameters for very original providers.
3245              
3246             OUTPUT:
3247              
3248             It depends very much on the operation and on the registry
3249              
3250             Examples:
3251              
3252             my ( $answ, $code, $msg ) = $conn->transfer( { op => 'request', dname => 'reclick.realty', authinfo => '123qweRTY{*}', period => 1 } );
3253              
3254             ( $answ, $code, $msg ) = $conn->transfer( { op => 'query', dname => 'reclick.realty', authinfo => '123qweRTY{*}' } );
3255              
3256             # answer from the CentralNic
3257              
3258             {
3259             'exp_date' => '2021-01-18 23:59:59',
3260             'cltrid' => '9d7e6ec767ec7d9d9d40fc518a5',
3261             'trstatus' => 'pending', # transfer status
3262             'requestors_id' => 'H2220222', # this we
3263             'dname' => 'reclick.realty',
3264             'senders_id' => 'H3105376', # godaddy
3265             'send_date' => '2020-01-15 21:14:26',
3266             'svtrid' => 'CNIC-82A2E9B355020697D1B3EF6FDE9D822D4CCE1D1616412EF53',
3267             'request_date' => '2020-01-10 21:14:26'
3268             };
3269              
3270             ( $answ, $code, $msg ) = $conn->transfer( { op => 'approve', dname => 'reclick.realty' } );
3271              
3272             ( $answ, $code, $msg ) = $conn->transfer( { op => 'reject', dname => 'reclick.realty', authinfo => '123qweRTY{*}' } );
3273              
3274             ( $answ, $code, $msg ) = $conn->transfer( { op => 'cancel', dname => 'reclick.realty', authinfo => '123qweRTY{*}' } );
3275              
3276             =cut
3277              
3278             sub transfer {
3279 7     7 1 15 my ( $self, $params ) = @_;
3280              
3281 7 50       20 return ( 0, 0, 'no dname' ) unless $params->{dname};
3282              
3283 7 50 33     52 return ( 0, 0, 'no op[eration]' ) unless $params->{op} && $params->{op} =~ /query|request|cancel|approve|reject|usertransfer/;
3284              
3285 7         15 my $pw = '';
3286 7 100       17 if ( defined $params->{authinfo} ) {
3287             # 0 & undef are differents
3288 2         7 $pw = "\n \n $$params{authinfo}\n ";
3289             }
3290              
3291 7         12 my $per = '';
3292 7 100       16 if ( defined $params->{period} ) {
3293             # 0 & undef is different
3294 4         12 $per = qq|\n $$params{period}|;
3295             }
3296              
3297             # special parameters for very original registries
3298 7   50     22 my $spec = $$params{addition} // '';
3299              
3300 7   50     73 my $ext = $$params{extension} || '';
3301              
3302 7 50       16 $ext = "\n \n$ext " if $ext;
3303              
3304 7         16 my $cltrid = get_cltrid();
3305              
3306 7         45 my $body = <
3307             $$self{urn}{head}
3308            
3309            
3310            
3311             $$params{dname}$per$pw$spec
3312            
3313             $ext
3314             $cltrid
3315            
3316            
3317             TRANS
3318              
3319 7         24 my $answ = $self->req( $body, $$params{op}.'_transfer' );
3320              
3321 7 50       40 if ( $answ =~ // ) {
3322 7         21 my $rcode = $1 + 0;
3323              
3324 7         13 my $msg = '';
3325 7 50       74 if ( $answ =~ /]*>(.+)<\/msg>.+\/result>/s ) {
3326 7         16 $msg = $1;
3327              
3328 7 50       53 if ( $answ =~ /]*>(.+)<\/text>.+\/result>/s ) {
3329 0         0 $msg .= '; ' . $1;
3330             }
3331              
3332 7 50       23 if ( $answ =~ /([^<>]+)<\/reason>/ ) {
3333 0         0 $msg .= '; ' . $1;
3334             }
3335             }
3336              
3337 7         13 my $info = {}; # for data
3338              
3339             # pull out the main part and parse
3340 7 100       27 if ( $answ =~ /(.+)<\/resData>/s ) {
3341 2         6 my $rdata = $1;
3342              
3343 2         11 ( $info->{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
3344              
3345 2         12 ( $info->{trstatus} ) = $rdata =~ /([^<>]+)<\/domain:trStatus>/;
3346              
3347             # owner, ...
3348 2         8 foreach my $k ( keys %id ) {
3349 10 100       166 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
3350 4         31 $info->{$id{$k}} = $1;
3351             }
3352             }
3353              
3354             # dates
3355 2         10 foreach my $k ( keys %dt ) {
3356 12 100       181 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
3357 6         22 $info->{$dt{$k}} = $1;
3358              
3359 6         22 $info->{$dt{$k}} =~ s/T/ /;
3360 6         24 $info->{$dt{$k}} =~ s/\.\d+Z$//;
3361 6         16 $info->{$dt{$k}} =~ s/Z$//;
3362             }
3363             }
3364             }
3365              
3366 7         39 ( $info->{cltrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
3367 7         34 ( $info->{svtrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
3368              
3369 7 50       36 return wantarray ? ( $info, $rcode, $msg ) : $info;
3370             }
3371              
3372 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0;
3373             }
3374              
3375              
3376             =head2 delete_domain
3377              
3378             Deleting a domain.
3379              
3380             params with keys:
3381              
3382             C – domain name
3383              
3384             C – extensions for some registries in xml format
3385              
3386             OUTPUT:
3387             see L.
3388              
3389             An Example:
3390              
3391             my ( $a, $m ) = make_request( 'delete_domain', { dname => 'ssslll.ru.com', %conn_params } );
3392              
3393             =cut
3394              
3395             sub delete_domain {
3396 10     10 1 22 my ( $self, $params ) = @_;
3397              
3398 10 50       30 return ( 0, 0, 'no dname' ) unless $params->{dname};
3399              
3400 10   100     32 my $ext = $$params{extension} || '';
3401              
3402 10 100       27 $ext = "\n \n$ext " if $ext;
3403              
3404 10         22 my $cltrid = get_cltrid();
3405              
3406 10         59 my $body = <
3407             $$self{urn}{head}
3408            
3409            
3410            
3411             $$params{dname}
3412            
3413             $ext
3414             $cltrid
3415            
3416            
3417             DELDOM
3418              
3419 10         26 return $self->simple_request( $body, 'delete_domain' );
3420             }
3421              
3422              
3423             =head2 req_poll_rdata
3424              
3425             Parse resData from req poll
3426              
3427             Can be overwritten in a child module
3428              
3429             =cut
3430              
3431             sub req_poll_rdata {
3432 0     0 1 0 my ( $self, $rdata ) = @_;
3433              
3434 0         0 my %info;
3435              
3436 0 0       0 if ( $rdata =~ /^\s*
    0          
    0          
    0          
    0          
    0          
3437 0         0 $info{upd_del} = {};
3438 0         0 ( $info{upd_del}{result}, $info{upd_del}{contact} ) =
3439             $rdata =~ /([^<>]+)<\/contact:id>/;
3440             }
3441              
3442             elsif ( $rdata =~ /\s*
3443 0         0 ( $info{ns} ) = $rdata =~ m|([^<>]+)|s;
3444 0         0 $info{ns} = lc $info{ns};
3445              
3446 0         0 ( $info{roid} ) = $rdata =~ m|([^<>]+)|s;
3447              
3448 0         0 my @sts = $rdata =~ m|()|gs;
3449 0         0 for my $row ( @sts ) {
3450 0 0       0 if ( $row =~ /host:status s="([^"]+)"/ ) {
3451 0         0 $info{statuses}{$1} = '+';
3452             }
3453             }
3454              
3455 0         0 my @ips = $rdata =~ m|([^<>]+)|gs;
3456 0         0 $info{ips} = [];
3457 0         0 for my $row ( @ips ) {
3458 0 0       0 if ( $row =~ m|host:addr ip="v\d">([^<>]+)
3459 0         0 push @{$info{ips}}, $1;
  0         0  
3460             }
3461             }
3462             # owner, ...
3463 0         0 foreach my $k ( keys %id ) {
3464 0 0       0 if ( $rdata =~ /([^<>]+)<\/host:$k>/ ) {
3465 0         0 $info{$id{$k}} = $1;
3466             }
3467             }
3468             # dates
3469 0         0 foreach my $k ( keys %dt ) {
3470 0 0       0 if ( $rdata =~ m|([^<>]+)| ) {
3471 0         0 $info{$dt{$k}} = cldate( $1 );
3472             }
3473             }
3474             }
3475              
3476             elsif ( $rdata =~ /^\s*
3477 0         0 $info{create} = {};
3478 0         0 ( $info{create}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
3479              
3480 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:crDate>/ ) {
3481 0         0 $info{create}{date} = IO::EPP::Base::cldate( $1 );
3482             }
3483             }
3484              
3485             elsif ( $rdata =~ /^\s*
3486 0         0 $info{renew} = {};
3487 0         0 ( $info{renew}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
3488             }
3489              
3490             elsif ( $rdata =~ /^\s*
3491 0         0 $info{transfer} = {};
3492 0         0 ( $info{transfer}{dname} ) = $rdata =~ /([^<>]+)<\/domain:name>/;
3493 0         0 ( $info{transfer}{status} ) = $rdata =~ /([^<>]+)<\/domain:trStatus>/;
3494              
3495             # sender, requestor
3496 0         0 foreach my $k ( keys %id ) {
3497 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
3498 0         0 $info{transfer}{$id{$k}} = $1;
3499             }
3500             }
3501             # dates
3502 0         0 foreach my $k ( keys %dt ) {
3503 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:$k>/ ) {
3504 0         0 $info{transfer}{$dt{$k}} = IO::EPP::Base::cldate( $1 );
3505             }
3506             }
3507             }
3508              
3509             elsif ( $rdata =~ /^\s*
3510 0         0 $info{upd_del} = {};
3511 0         0 ( $info{upd_del}{result}, $info{upd_del}{dname} ) =
3512             $rdata =~ /([^<>]+)<\/domain:name>/;
3513              
3514 0 0       0 if ( $rdata =~ /(.+?)<\/domain:paTRID>/s ) {
3515 0         0 my $trids = $1;
3516 0 0       0 if ( $trids =~ /([0-9A-Za-z]+)<\/clTRID>/ ) {
3517 0         0 $info{upd_del}{cltrid} = $1;
3518             }
3519 0 0       0 if ( $trids =~ /([0-9A-Za-z\-]+)<\/svTRID>/ ) {
3520 0         0 $info{upd_del}{svtrid} = $1;
3521             }
3522             }
3523              
3524 0 0       0 if ( $rdata =~ /([^<>]+)<\/domain:paDate>/ ) {
3525 0         0 $info{upd_del}{date} = IO::EPP::Base::cldate( $1 );
3526             }
3527             }
3528              
3529             else {
3530 0         0 return ( 0, 'New poll message type!' );
3531             }
3532              
3533 0         0 return ( \%info, '' );
3534              
3535             }
3536              
3537              
3538             =head2 req_poll_ext
3539              
3540             Parse req poll extension
3541              
3542             Empty, for overwriting in children modules
3543              
3544             =cut
3545              
3546             sub req_poll_ext {
3547 0     0 1 0 return {};
3548             }
3549              
3550              
3551             =head2 req_poll
3552              
3553             Get and parse top message from poll
3554              
3555             No input params.
3556              
3557             OUTPUT:
3558              
3559             Еach provider has a lot of different types of messages.
3560             Only domain transfer messages are similar.
3561             They have something like this format:
3562              
3563             {
3564             'code' => '1301',
3565             'msg' => 'Command completed successfully; ack to dequeue',
3566             'count' => '1',
3567             'id' => '456789',
3568             'date' => '2020-02-02 20:02:02',
3569             'qmsg' => 'Transfer Requested.',
3570             'transfer' => {
3571             'dname' => 'example.com',
3572             'status' => 'pending',
3573             'senders_id' => '1111'.
3574             'requestors_id' => '999',
3575             'request_date' => '2001-01-01 01:01:01',
3576             'send_date' => '2001-01-06 01:01:01'
3577             },
3578             'svtrid' => '4569552848-1578703988000',
3579             'cltrid' => '1f80c34195a936dfb0d2bd0c414141414'
3580             };
3581              
3582             C – the registrar who made the transfer request;
3583              
3584             C – the registrar from which the domain is transferred;
3585              
3586             C – the start date of the transfer
3587              
3588             C – date when the transfer is completed, unless it is canceled or the domain is released;
3589              
3590             C – the status of the transfer, the most common meaning: C, C, C, C.
3591              
3592             =cut
3593              
3594             sub req_poll {
3595 1     1 1 3 my ( $self, undef ) = @_;
3596              
3597 1         4 my $cltrid = get_cltrid();
3598              
3599 1         6 my $body = <
3600             $$self{urn}{head}
3601            
3602            
3603             $cltrid
3604            
3605            
3606             RPOLL
3607              
3608 1         4 my $answ = $self->req( $body, 'req_poll' );
3609              
3610 1 50 33     10 if ( $answ and $answ =~ // ) {
3611 1         4 my $rcode = $1 + 0;
3612              
3613 1         2 my $msg = '';
3614 1 50       11 if ( $answ =~ /]*>(.+?)<\/msg>.+\/result>/s ) {
3615 1         3 $msg = $1;
3616             }
3617              
3618 1         4 my %info;
3619              
3620 1 50       4 if ( $rcode == 1301 ) {
3621 0 0       0 if ( $answ =~ /(.*)<\/msgQ>/s ) {
3622 0         0 $info{$1} = $2;
3623 0         0 $info{$3} = $4;
3624 0         0 my $q = $5;
3625              
3626 0 0 0     0 if ( $q and $q =~ /(.+)<\/qDate>.*(.+?)<\/msg>/s ) {
    0 0        
    0          
3627 0         0 $info{date} = IO::EPP::Base::cldate( $1 );
3628 0         0 $info{qmsg} = $3;
3629 0         0 $info{qmsg} =~ s/"/"/g;
3630 0 0       0 if ( $info{qmsg} =~ /\[CDATA\[/ ) {
3631 0         0 $info{qmsg} =~ s/
3632 0         0 $info{qmsg} =~ s/\]\]>//;
3633             }
3634             }
3635             # wihout special message
3636             elsif ( $q and $q =~ /(.+)<\/qDate>/s ) {
3637 0         0 $info{date} = IO::EPP::Base::cldate( $1 );
3638 0         0 $info{qmsg} = $q; #
3639             }
3640             elsif ( $q ) {
3641             # not standard
3642 0         0 $info{qmsg} = $q;
3643             }
3644             }
3645              
3646 0 0       0 if ( $answ =~ /(.+?)<\/resData>/s ) {
3647 0         0 my ( $rdata, $err ) = $self->req_poll_rdata( $1 );
3648              
3649 0 0 0     0 if ( !$rdata and $err ) {
3650 0 0       0 return wantarray ? ( 0, 0, $err ) : 0 ;
3651             }
3652              
3653 0         0 $info{$_} = $rdata->{$_} for keys %$rdata;
3654             }
3655              
3656 0 0       0 if ( $answ =~ /(.+?<\/extension>)/s ) {
3657 0         0 $info{ext} = $self->req_poll_ext( $1 );
3658             }
3659              
3660 0         0 ( $info{cltrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/clTRID>/;
3661 0         0 ( $info{svtrid} ) = $answ =~ /([0-9A-Za-z\-]+)<\/svTRID>/;
3662             }
3663              
3664 1 50       7 return wantarray ? ( \%info, $rcode, $msg ) : \%info;
3665             }
3666              
3667 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
3668             }
3669              
3670             =head2 ask_poll
3671              
3672             Delete message from poll by id
3673              
3674             INPUT:
3675              
3676             C – id of the message to be removed from the queue.
3677              
3678             =cut
3679              
3680             sub ask_poll {
3681 0     0 1 0 my ( $self, $params ) = @_;
3682              
3683 0 0       0 return ( 0, 0, 'no msg_id' ) unless $params->{msg_id};
3684              
3685 0         0 my $cltrid = get_cltrid();
3686              
3687 0         0 my $body = <
3688             $$self{urn}{head}
3689            
3690            
3691             $cltrid
3692            
3693            
3694             APOLL
3695              
3696 0         0 my $answ = $self->req( $body, 'ask_poll' );
3697              
3698 0 0 0     0 if ( $answ && $answ =~ // ) {
3699 0         0 my $rcode = $1 + 0;
3700              
3701 0         0 my ( $msg ) = $answ =~ /]*>(.+)<\/msg>.+\/result>/s;
3702              
3703 0         0 my %info;
3704              
3705 0 0       0 if ( $answ =~ // ) {
3706 0         0 $info{msg_cnt} = $1;
3707 0         0 $info{msg_id} = $2;
3708             }
3709              
3710 0 0       0 return wantarray ? ( \%info, $rcode, $msg ) : \%info;
3711             }
3712              
3713             # Неконнект или ошибка запроса
3714             # По хорошему надо отделять неконнект от ошибки
3715 0 0       0 return wantarray ? ( 0, 0, 'empty answer' ) : 0 ;
3716             }
3717              
3718              
3719             =head2 logout
3720              
3721             Close session, disconnect
3722              
3723             No input parameters.
3724              
3725             =cut
3726              
3727             sub logout {
3728 77     77 1 4094 my ( $self ) = @_;
3729              
3730 77 50       170 return unless $self->{sock};
3731              
3732 77 50 33     192 unless ( $self->{test} || $self->{sock}->opened() ) {
3733 0         0 delete $self->{sock};
3734              
3735 0         0 return;
3736             }
3737              
3738 77         136 my $cltrid = get_cltrid();
3739              
3740 77         322 my $logout = <
3741             $$self{urn}{head}
3742            
3743            
3744             $cltrid
3745            
3746            
3747             LOGOUT
3748              
3749 77         267 $self->req( $logout, 'logout' );
3750              
3751 77 50       163 unless ( $self->{test} ) {
3752 0         0 close( $self->{sock} );
3753             }
3754              
3755 77         137 delete $self->{sock};
3756              
3757 77         147 return ( undef, '1500', 'ok' );
3758             }
3759              
3760              
3761             sub DESTROY {
3762 77     77   29464 my ( $self ) = @_;
3763              
3764 77         587 local ($!, $@, $^E, $?); # Protection against action-at-distance
3765              
3766 77 100       214 if ( $self->{sock} ) {
3767 73         167 $self->logout();
3768             }
3769              
3770 77 50       922 if ( $self->{log_fh} ) {
3771 0           close $self->{log_fh};
3772              
3773 0           delete $self->{log_fh};
3774             }
3775             }
3776              
3777              
3778             1;
3779              
3780             __END__