File Coverage

blib/lib/Net/DRI/Protocol.pm
Criterion Covered Total %
statement 142 180 78.8
branch 30 70 42.8
condition 30 90 33.3
subroutine 26 36 72.2
pod 1 13 7.6
total 229 389 58.8


line stmt bran cond sub pod time code
1             ## Domain Registry Interface, Protocol superclass
2             ##
3             ## Copyright (c) 2005-2011,2013-2014 Patrick Mevzek . All rights reserved.
4             ##
5             ## This file is part of Net::DRI
6             ##
7             ## Net::DRI is free software; you can redistribute it and/or modify
8             ## it under the terms of the GNU General Public License as published by
9             ## the Free Software Foundation; either version 2 of the License, or
10             ## (at your option) any later version.
11             ##
12             ## See the LICENSE file that comes with this distribution for more details.
13             ####################################################################################################
14              
15             package Net::DRI::Protocol;
16              
17 59     59   2094 use strict;
  59         90  
  59         2027  
18 59     59   250 use warnings;
  59         74  
  59         1573  
19              
20 59     59   300 use base qw(Class::Accessor::Chained::Fast Net::DRI::BaseClass);
  59         90  
  59         7454  
21             __PACKAGE__->mk_accessors(qw(name version commands message default_parameters logging));
22              
23 59     59   1886 use DateTime;
  59         224572  
  59         1330  
24 59     59   270 use DateTime::Duration;
  59         81  
  59         1310  
25 59     59   37630 use DateTime::Format::ISO8601;
  59         2270008  
  59         4705  
26 59     59   743 use DateTime::Format::Strptime;
  59         100  
  59         3177  
27              
28 59     59   313 use Net::DRI::Exception;
  59         101  
  59         1310  
29 59     59   949 use Net::DRI::Util;
  59         86  
  59         1328  
30 59     59   46992 use Net::DRI::Data::Changes;
  59         174  
  59         1857  
31 59     59   28395 use Net::DRI::Data::Contact;
  59         176  
  59         632  
32 59     59   42643 use Net::DRI::Data::ContactSet;
  59         224  
  59         1914  
33 59     59   34017 use Net::DRI::Data::Hosts;
  59         158  
  59         441  
34 59     59   34492 use Net::DRI::Data::StatusList;
  59         145  
  59         106192  
35              
36             =pod
37              
38             =head1 NAME
39              
40             Net::DRI::Protocol - Superclass of all Net::DRI Protocols
41              
42             =head1 DESCRIPTION
43              
44             Please see the README file for details.
45              
46             =head1 SUPPORT
47              
48             For now, support questions should be sent to:
49              
50             Enetdri@dotandco.comE
51              
52             Please also see the SUPPORT file in the distribution.
53              
54             =head1 SEE ALSO
55              
56             Ehttp://www.dotandco.com/services/software/Net-DRI/E
57              
58             =head1 AUTHOR
59              
60             Patrick Mevzek, Enetdri@dotandco.comE
61              
62             =head1 COPYRIGHT
63              
64             Copyright (c) 2005-2011,2013-2014 Patrick Mevzek .
65             All rights reserved.
66              
67             This program is free software; you can redistribute it and/or modify
68             it under the terms of the GNU General Public License as published by
69             the Free Software Foundation; either version 2 of the License, or
70             (at your option) any later version.
71              
72             See the LICENSE file that comes with this distribution for more details.
73              
74             =cut
75              
76             ####################################################################################################
77              
78             sub new
79             {
80 2     2 1 27 my ($class,$ctx)=@_;
81              
82             my $self={ capabilities => {},
83 0     0   0 factories => { datetime => sub { return DateTime->new(@_); },
84 0     0   0 duration => sub { return DateTime::Duration->new(@_); },
85 3     3   22 changes => sub { return Net::DRI::Data::Changes->new(@_); },
86 0     0   0 contact => sub { return Net::DRI::Data::Contact->new(); },
87 0     0   0 contactset => sub { return Net::DRI::Data::ContactSet->new(@_); },
88 8     8   31 hosts => sub { return Net::DRI::Data::Hosts->new(@_); },
89 0     0   0 status => sub { return Net::DRI::Data::StatusList->new(@_); },
90             },
91 2         63 logging => $ctx->{registry}->logging(),
92             logging_ctx => { registry => $ctx->{registry}->name(), profile => $ctx->{profile}, transport_class => $ctx->{transport_class} },
93             };
94              
95 2         27 bless($self,$class);
96 2         18 $self->message(undef);
97 2         49 $self->default_parameters({});
98              
99 2         26 $self->log_setup_channel($class,'protocol',$self->{logging_ctx});
100 2         10 $self->log_output('debug','core',sprintf('Added profile %s for registry %s',$class,$ctx->{registry}->name()));
101 2         7 return $self;
102             }
103              
104             sub log_output
105             {
106 5     5 0 29 my ($self,$level,$type,$data1)=@_;
107 5 100 100     54 $self->{logging_ctx}->{protocol}=$self->name().'/'.$self->version() if (! exists $self->{logging_ctx}->{protocol} && defined $self->name());
108 5 50       57 return $self->logging()->output($level,$type,ref $data1 ? +{ %{$self->{logging_ctx}}, %$data1 } : $data1);
  0         0  
109             }
110              
111              
112             sub parse_iso8601
113             {
114 0     0 0 0 my ($self,$d)=@_;
115 0 0       0 $self->{iso8601_parser}=DateTime::Format::ISO8601->new() unless exists $self->{iso8601_parser};
116 0         0 return $self->{iso8601_parser}->parse_datetime($d);
117             }
118              
119             sub build_strptime_parser
120             {
121 0     0 0 0 my ($self,@args)=@_;
122 0         0 my $key=join("\x{001E}",@args);
123 0 0       0 $self->{strptime_parser}->{$key}=DateTime::Format::Strptime->new(@args) unless exists $self->{strptime_parser}->{$key};
124 0         0 return $self->{strptime_parser}->{$key};
125             }
126              
127             sub create_local_object
128             {
129 31     31 0 47 my ($self,$what,@args)=@_;
130 31 50 33     115 return unless defined $self && defined $what;
131 31         58 my $fn=$self->factories();
132 31 50 33     265 return unless (defined($fn) && ref($fn) && exists($fn->{$what}) && (ref($fn->{$what}) eq 'CODE'));
      33        
      33        
133 31         83 return $fn->{$what}->(@args);
134             }
135              
136             ## This should not be called multiple times for a given Protocol class (as it will erase the loaded_modules slot)
137             sub _load
138             {
139 1     1   3 my ($self,@classes)=@_;
140 1         7 my $etype='protocol/'.$self->name();
141 1         15 my $version=$self->version();
142              
143 1         9 my %skip = map { substr($_,1) => 1 } grep { /^-/ } @classes;
  0         0  
  3         10  
144              
145 1         3 my (%c,%done,@done);
146 1         3 foreach my $class (@classes)
147             {
148 3 50 33     32 next if exists $done{$class} || $class=~m/^-/ || exists $skip{$class};
      33        
149 3         24 $self->log_output('debug','core',sprintf('Loading class "%s"',$class));
150 3         13 Net::DRI::Util::load_module($class,$etype);
151 3 50       56 Net::DRI::Exception::method_not_implemented('register_commands',$class) unless $class->can('register_commands');
152 3         14 my $rh=$class->register_commands($version);
153 3         12 $self->{commands_by_class}->{$class}=$rh;
154 3         14 Net::DRI::Util::hash_merge(\%c,$rh); ## { object type => { action type => [ build action, parse action ]+ } }
155 3 50       35 if ($class->can('capabilities_add'))
156             {
157 0         0 my @a=$class->capabilities_add();
158 0 0       0 if (ref($a[0]))
159             {
160 0         0 foreach my $a (@a) { $self->capabilities(@$a); }
  0         0  
161             } else
162             {
163 0         0 $self->capabilities(@a);
164             }
165             }
166 3 50       23 $class->setup($self,$version) if $class->can('setup');
167 3         8 $done{$class}=1;
168 3         8 push @done,$class;
169             }
170              
171 1         5 $self->{loaded_modules}=\@done;
172 1         14 $self->commands(\%c);
173 1         15 return;
174             }
175              
176             ## has_module + find_action_in_class should instead better be based on some ID, like the XML namespace in EPP,
177             ## instead of the Perl module names
178             sub has_module
179             {
180 0     0 0 0 my ($self,$mod)=@_;
181 0 0 0     0 return 0 unless defined $mod && length $mod;
182 0 0       0 return (grep { $_ eq $mod } @{$self->{loaded_modules}})? 1 : 0;
  0         0  
  0         0  
183             }
184              
185             sub find_action_in_class
186             {
187 0     0 0 0 my ($self,$class,$otype,$oaction)=@_;
188 0 0 0     0 return unless defined $class && length $class && exists $self->{commands_by_class}->{$class} && exists $self->{commands_by_class}->{$class}->{$otype} && exists $self->{commands_by_class}->{$class}->{$otype}->{$oaction};
      0        
      0        
      0        
189 0 0       0 return wantarray ? @{$self->{commands_by_class}->{$class}->{$otype}->{$oaction}} : $self->{commands_by_class}->{$class}->{$otype}->{$oaction}->[0];
  0         0  
190             }
191              
192             sub _load_commands
193             {
194 20     20   23 my ($self,$otype,$oaction)=@_;
195              
196 20         57 my $etype='protocol/'.$self->name();
197 20 50 33     227 Net::DRI::Exception->die(1,$etype,7,'Object type and/or action not defined') unless (defined $otype && length $otype && defined $oaction && length $oaction);
      33        
      33        
198 20         48 my $h=$self->commands();
199 20 50       154 Net::DRI::Exception->die(1,$etype,8,'No actions defined for object of type <'.$otype.'>') unless exists($h->{$otype});
200 20 50       53 Net::DRI::Exception->die(1,$etype,9,'No action name <'.$oaction.'> defined for object of type <'.$otype.'> in '.ref($self)) unless exists($h->{$otype}->{$oaction});
201 20         30 return $h;
202             }
203              
204             sub has_action
205             {
206 0     0 0 0 my ($self,$otype,$oaction)=@_;
207 0 0       0 return eval { my $h=$self->_load_commands($otype,$oaction); 1; } ? 1 : 0;
  0         0  
  0         0  
208             }
209              
210             sub action
211             {
212 10     10 0 22 my ($self,$otype,$oaction,$trid,@params)=@_;
213 10         36 my $h=$self->_load_commands($otype,$oaction);
214              
215             ## Create a new message from scratch and loop through all functions registered for given action & type
216 10         29 my $msg=$self->create_local_object('message',$trid,$otype,$oaction);
217 10 50 33     114 Net::DRI::Exception->die(0,'protocol',1,'Unsuccessfull message creation') unless ($msg && ref $msg && $msg->isa('Net::DRI::Protocol::Message'));
      33        
218 10         35 $self->message($msg); ## store it for later use (in loop below)
219              
220 10         49 foreach my $t (@{$h->{$otype}->{$oaction}})
  10         39  
221             {
222 10         19 my $pf=$t->[0];
223 10 50 33     58 next unless (defined($pf) && (ref($pf) eq 'CODE'));
224 10         43 $pf->($self,@params);
225             }
226              
227 10         35 $self->message(undef); ## needed ? useful ?
228 10         60 return $msg;
229             }
230              
231             sub reaction
232             {
233 10     10 0 92 my ($self,$otype,$oaction,$dr,$sent,$oname,$trid)=@_;
234 10         28 my $h=$self->_load_commands($otype,$oaction);
235 10         27 my $msg=$self->create_local_object('message');
236 10 50 33     97 Net::DRI::Exception->die(0,'protocol',1,'Unsuccessfull message creation') unless ($msg && ref($msg) && $msg->isa('Net::DRI::Protocol::Message'));
      33        
237              
238 10         14 my %info;
239             ## TODO is $sent needed here really ? if not remove from API above also !
240 10         36 $msg->parse($dr,\%info,$otype,$oaction,$sent); ## will trigger an Exception by itself if problem ## TODO : add later the whole LocalStorage stuff done when sending ? (instead of otype/oaction/message sent)
241 10         24 $self->message($msg); ## store it for later use (in loop below)
242 10 0 66     92 $info{$otype}->{$oname}->{name}=$oname if ($otype eq 'domain' || $otype eq 'host' || $otype eq 'nsgroup' || $otype eq 'keygroup'); ## TODO : abstract this ?
      33        
      33        
243              
244 10 50 33     29 if (exists $h->{message} && exists $h->{message}->{result})
245             {
246 0         0 foreach my $t (@{$h->{message}->{result}})
  0         0  
247             {
248 0         0 my $pf=$t->[1];
249 0 0 0     0 next unless (defined $pf && ref $pf eq 'CODE');
250 0         0 $pf->($self,$otype,$oaction,$oname,\%info);
251             }
252             }
253              
254 10         11 foreach my $t (@{$h->{$otype}->{$oaction}})
  10         30  
255             {
256 10         17 my $pf=$t->[1];
257 10 100 66     46 next unless (defined $pf && ref $pf eq 'CODE');
258 6         25 $pf->($self,$otype,$oaction,$oname,\%info);
259             }
260              
261 10         35 my $rc=$msg->result_status();
262 10 50       32 if (defined $rc)
263             {
264 10 50       37 $rc->_set_trid([ $trid ]) unless $rc->trid(); ## if not done inside Protocol::*::Message::result_status, make sure we save at least our transaction id
265 10         30 foreach my $v1 (values %info)
266             {
267 10 50 33     64 next unless ref $v1 eq 'HASH' && keys %$v1;
268 10         15 foreach my $v2 (values %{$v1})
  10         30  
269             {
270 10 50 33     49 next unless ref $v2 eq 'HASH' && keys %$v2; ## yes, this can happen, with must_reconnect for example
271 10 50       25 next if exists $v2->{result_status};
272 10         38 $v2->{result_status}=$rc;
273             }
274             }
275             }
276 10         32 $self->message(undef); ## needed ? useful ?
277              
278 10         69 $info{session}->{exchange}->{result_from_cache}=0;
279 10         34 $info{session}->{exchange}->{protocol}=$self->nameversion();
280 10         112 $info{session}->{exchange}->{trid}=$trid;
281 10         81 return ($rc,\%info);
282             }
283              
284             sub nameversion
285             {
286 14     14 0 958 my $self=shift;
287 14         42 return $self->name().'/'.$self->version();
288             }
289              
290             sub factories
291             {
292 33     33 0 39 my ($self,$object,$code)=@_;
293 33 100 66     73 if (defined $object && defined $code)
294             {
295 2         4 $self->{factories}->{$object}=$code;
296 2         8 return $self;
297             }
298 31         45 return $self->{factories};
299             }
300              
301             sub capabilities
302             {
303 12     12 0 1245 my ($self,$action,$object,$cap)=@_;
304 12 100 66     34 if (defined($action) && defined($object))
305             {
306 4 100       14 $self->{capabilities}->{$action}={} unless exists($self->{capabilities}->{$action});
307 4 50       7 if (defined($cap))
308             {
309 4         7 $self->{capabilities}->{$action}->{$object}=$cap;
310             } else
311             {
312 0         0 delete($self->{capabilities}->{$action}->{$object});
313             }
314             }
315 12         28 return $self->{capabilities};
316             }
317              
318             ####################################################################################################
319             1;