1## Domain Registry Interface, Registry object 2## 3## Copyright (c) 2005-2010 Patrick Mevzek <netdri@dotandco.com>. 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# 16#################################################################################################### 17 18package Net::DRI::Registry; 19 20use strict; 21use warnings; 22 23use base qw(Class::Accessor::Chained::Fast Net::DRI::BaseClass); 24__PACKAGE__->mk_ro_accessors(qw(name driver profile trid_factory logging)); ## READ-ONLY !! 25 26use Time::HiRes (); 27 28use Net::DRI::Exception; 29use Net::DRI::Util; 30use Net::DRI::Protocol::ResultStatus; 31use Net::DRI::Data::RegistryObject; 32 33our $AUTOLOAD; 34 35our $VERSION=do { my @r=(q$Revision: 1.32 $=~/\d+/g); sprintf("%d".".%02d" x $#r, @r); }; 36 37=pod 38 39=head1 NAME 40 41Net::DRI::Registry - Specific Registry Driver Instance inside Net::DRI 42 43=head1 DESCRIPTION 44 45Please see the README file for details. 46 47=head1 SUPPORT 48 49For now, support questions should be sent to: 50 51E<lt>netdri@dotandco.comE<gt> 52 53Please also see the SUPPORT file in the distribution. 54 55=head1 SEE ALSO 56 57E<lt>http://www.dotandco.com/services/software/Net-DRI/E<gt> 58 59=head1 AUTHOR 60 61Patrick Mevzek, E<lt>netdri@dotandco.comE<gt> 62 63=head1 COPYRIGHT 64 65Copyright (c) 2005-2010 Patrick Mevzek <netdri@dotandco.com>. 66All rights reserved. 67 68This program is free software; you can redistribute it and/or modify 69it under the terms of the GNU General Public License as published by 70the Free Software Foundation; either version 2 of the License, or 71(at your option) any later version. 72 73See the LICENSE file that comes with this distribution for more details. 74 75=cut 76 77#################################################################################################### 78 79sub new 80{ 81 my ($class,$name,$drd,$cache,$trid,$logging)=@_; 82 83 my $self={name => $name, 84 driver => $drd, 85 cache => $cache, 86 profiles => {}, ## { profile name => { protocol => X 87 ## transport => X 88 ## status => Net::DRI::Protocol::ResultStatus 89 ## %extra 90 ## } 91 ## } 92 profile => undef, ## current profile 93 auto_target => {}, 94 last_data => {}, 95 last_process => {}, 96 trid_factory => $trid, 97 logging => $logging, 98 }; 99 100 bless($self,$class); 101 return $self; 102} 103 104sub available_profile 105{ 106 my $self=shift; 107 return (defined($self->{profile}))? 1 : 0; 108} 109 110sub available_profiles 111{ 112 my ($self,$full)=@_; 113 $full||=0; 114 return sort($full ? map { $_->{fullname} } values(%{$self->{profiles}}) : keys(%{$self->{profiles}})); 115} 116 117sub exist_profile 118{ 119 my ($self,$name)=@_; 120 return (defined($name) && exists($self->{profiles}->{$name})); 121} 122 123sub err_no_current_profile { Net::DRI::Exception->die(0,'DRI',3,'No current profile available'); } 124sub err_profile_name_does_not_exist { Net::DRI::Exception->die(0,'DRI',4,'Profile name '.$_[0].' does not exist'); } 125 126sub remote_object 127{ 128 my $self=shift; 129 return Net::DRI::Data::RegistryObject->new($self,@_); 130} 131 132sub _current 133{ 134 my ($self,$what,$tostore)=@_; 135 err_no_current_profile() unless (defined($self->{profile})); 136 err_profile_name_does_not_exist($self->{profile}) unless (exists($self->{profiles}->{$self->{profile}})); 137 Net::DRI::Exception::err_method_not_implemented($what) unless (exists($self->{profiles}->{$self->{profile}}->{$what})); 138 139 if (($what eq 'status') && $tostore) 140 { 141 $self->{profiles}->{$self->{profile}}->{$what}=$tostore; 142 } 143 144 return $self->{profiles}->{$self->{profile}}->{$what}; 145} 146 147sub transport { return shift->_current('transport'); } 148sub protocol { return shift->_current('protocol'); } 149sub status { return shift->_current('status',@_); } 150sub protocol_transport { my $self=shift; return ($self->protocol(),$self->transport()); } 151 152sub local_object 153{ 154 my $self=shift; 155 my $f=shift; 156 return unless $self && $f; 157 return $self->_current('protocol')->create_local_object($f,@_); 158} 159 160sub _result 161{ 162 my ($self,$f)=@_; 163 my $p=$self->profile(); 164 err_no_current_profile() unless (defined($p)); 165 Net::DRI::Exception->die(0,'DRI',6,'No last status code available for current registry and profile') unless (exists($self->{profiles}->{$p}->{status})); 166 my $rc=$self->{profiles}->{$p}->{status}; ## a Net::DRI::Protocol::ResultStatus object ! 167 Net::DRI::Exception->die(1,'DRI',5,'Status key is not a Net::DRI::Protocol::ResultStatus object') unless UNIVERSAL::isa($rc,'Net::DRI::Protocol::ResultStatus'); 168 return $rc if ($f eq 'self'); 169 Net::DRI::Exception->die(1,'DRI',5,'Method '.$f.' not implemented in Net::DRI::Protocol::ResultStatus') unless ($f && $rc->can($f)); 170 return $rc->$f(); 171} 172 173sub result_is_success { return shift->_result('is_success'); } 174sub is_success { return shift->_result('is_success'); } ## Alias 175sub result_code { return shift->_result('code'); } 176sub result_native_code { return shift->_result('native_code'); } 177sub result_message { return shift->_result('message'); } 178sub result_lang { return shift->_result('lang'); } 179sub result_status { return shift->_result('self'); } 180sub result_extra_info { return shift->_result('info'); } 181 182sub cache_expire { return shift->{cache}->delete_expired(); } 183sub cache_clear { return shift->{cache}->delete(); } 184 185sub set_info 186{ 187 my ($self,$type,$key,$data,$ttl)=@_; 188 my $p=$self->profile(); 189 err_no_current_profile() unless defined($p); 190 my $regname=$self->name(); 191 192 my $c=$self->{cache}->set($regname.'.'.$p,$type,$key,$data,$ttl); 193 $self->{last_data}=$c; ## the hash exists, since we called clear_info somewhere before 194 195 return $c; 196} 197 198## Returns a $rc object or undef if nothing found in cache for the specific object ($type/$key) and action ($action) 199sub try_restore_from_cache 200{ 201 my ($self,$type,$key,$action)=@_; 202 if (! Net::DRI::Util::all_valid($type,$key,$action)) { Net::DRI::Exception::err_assert('try_restore_from_cache improperly called'); } 203 204 my $a=$self->get_info('action',$type,$key); 205 ## not in cache or in cache but for some other action 206 if (! defined $a || ($a ne $action)) { $self->log_output('debug','core',sprintf('Cache MISS (empty cache or other action) for type=%s key=%s',$type,$key)); return; } 207 208 ## retrieve from cache, copy, and do some cleanup 209 $self->{last_data}=$self->get_info_all($type,$key); 210 ## since we passed the above test on get_info('action'), we know here we received something defined by get_info_all, 211 ## but we test explicitely again (get_info_all returns an empty ref hash on problem, not undef), to avoid race conditions and such 212 if (! keys(%{$self->{last_data}})) { $self->log_output('debug','core',sprintf('Cache MISS (no last_data content) for type=%s key=%s',$type,$key)); return; } 213 214 ## get_info_all makes a copy, but only at first level ! so this high level change is ok (no pollution), but be warned for below ! 215 $self->{last_data}->{result_from_cache}=1; 216 217 ## however we must take care of what we do in levels further below, as the same data is probably in the original $rc object (if not thrown away by application) 218 my $rd=$self->{last_data}->{result_status}->get_data_collection(); 219 220 ## we first make a copy (here it is a plain ref hash, no objects inside, otherwise a proper clone() would be needed, see Clone::* modules), then we can update it. 221 ## If something more complex is needed, a proper clone() should be implemented 222 $rd->{session}={ %{$rd->{session}} }; 223 ## (if there are other keys than exchange, we do not need to copy them, since we do not change their content) 224 $rd->{session}->{exchange}={ %{$rd->{session}->{exchange}} }; 225 $rd->{session}->{exchange}->{result_from_cache}=1; 226 ## Should we delete the raw exchange (session/exchange/command,duration,reply) data too ? 227 228 $self->log_output('debug','core',sprintf('Cache HIT for type=%s key=%s',$type,$key)); 229 return $self->get_info('result_status'); 230} 231 232sub clear_info { shift->{last_data}={}; } 233 234sub get_info 235{ 236 my ($self,$what,$type,$key)=@_; 237 return unless (defined($what) && $what); 238 239 if (Net::DRI::Util::all_valid($type,$key)) ## search the cache, by default same registry & profile ! 240 { 241 my $p=$self->profile(); 242 err_no_current_profile() unless defined($p); 243 my $regname=$self->name(); 244 return $self->{cache}->get($type,$key,$what,$regname.'.'.$p); 245 } else 246 { 247 return unless exists($self->{last_data}->{$what}); 248 return $self->{last_data}->{$what}; 249 } 250} 251 252sub get_info_all 253{ 254 my ($self,$type,$key)=@_; 255 my $rh; 256 257 if (Net::DRI::Util::all_valid($type,$key)) 258 { 259 my $p=$self->profile(); 260 err_no_current_profile() unless defined($p); 261 my $regname=$self->name(); 262 $rh=$self->{cache}->get($type,$key,undef,$regname.'.'.$p); 263 } else 264 { 265 $rh=$self->{last_data}; 266 } 267 268 return {} unless (defined($rh) && ref($rh) && keys(%$rh)); 269 270 my %h=%{ $rh }; ## create a copy, as we will delete content... ## BUGFIX !! 271 foreach my $k (grep { /^_/ } keys(%h)) { delete($h{$k}); } 272 return \%h; 273} 274 275sub get_info_keys 276{ 277 my ($self,$type,$key)=@_; 278 my $rh=$self->get_info_all($type,$key); 279 return sort { $a cmp $b } keys(%$rh); 280} 281 282#################################################################################################### 283## Change profile 284sub target 285{ 286 my ($self,$profile)=@_; 287 err_profile_name_does_not_exist($profile) unless ($profile && exists($self->{profiles}->{$profile})); 288 $self->{profile}=$profile; 289} 290 291sub profile_auto_switch 292{ 293 my ($self,$otype,$oaction)=@_; 294 my $p=$self->get_auto_target($otype,$oaction); 295 return unless defined($p); 296 $self->target($p); 297 return; 298} 299 300sub set_auto_target 301{ 302 my ($self,$profile,$otype,$oaction)=@_; ## $otype/$oaction may be undef 303 err_profile_name_does_not_exist($profile) unless ($profile && exists($self->{profiles}->{$profile})); 304 305 my $rh=$self->{auto_target}; 306 $otype||='_default'; 307 $oaction||='_default'; 308 $rh->{$otype}={} unless (exists($rh->{$otype})); 309 $rh->{$otype}->{$oaction}=$profile; 310} 311 312sub get_auto_target 313{ 314 my ($self,$otype,$oaction)=@_; 315 my $at=$self->{auto_target}; 316 $otype='_default' unless (exists($at->{$otype})); 317 return unless (exists($at->{$otype})); 318 my $ac=$at->{$otype}; 319 return unless (defined($ac) && ref($ac)); 320 $oaction='_default' unless (exists($ac->{$oaction})); 321 return unless (exists($ac->{$oaction})); 322 return $ac->{$oaction}; 323} 324 325sub add_current_profile 326{ 327 my ($self,@p)=@_; 328 my $rc=$self->add_profile(@p); 329 if ($rc->is_success()) { $self->target($p[0]); } 330 return $rc; 331} 332 333## Transport and Protocol parameters are merged (semantically but not chronologically, parameters coming later erase previous ones) in this order; 334## - TransportConnectionClass->transport_default() [only for transport parameters] 335## - Protocol->transport_default() [only for transport parameters] 336## - DRD->transport_protocol_default() 337## - user specified parameters to add_profile (they always have precedence over defaults stored in the 3 previous cases) 338 339## API: profile name, profile type (types starting with "test=" are only for internal tests, and should not be used in production), transport params {}, protocol params {} 340sub add_profile 341{ 342 my ($self,$name,$type,$trans_p,$prot_p)=@_; 343 344 if (! Net::DRI::Util::all_valid($name,$type)) { Net::DRI::Exception::usererr_insufficient_parameters('add_profile needs at least 2 parameters: new profile name and type'); } 345 if (defined $trans_p && ref $trans_p ne 'HASH') { Net::DRI::Exception::usererr_invalid_parameters('If provided, 3rd parameter of add_profile (transport data) must be a ref hash'); } 346 if (defined $prot_p && ref $prot_p ne 'HASH') { Net::DRI::Exception::usererr_invalid_parameters('If provided, 4th parameter of add_profile (protocol data) must be a ref hash'); } 347 if ($self->exist_profile($name)) { Net::DRI::Exception::usererr_invalid_parameters('New profile name "'.$name.'" already in use'); } 348 349 my $drd=$self->driver(); 350 my ($test)=($type=~s/^test=//)? 1 : 0; 351 my ($tc,$tp,$pc,$pp); ## Transport Class, Transport Params, Protocol Class, Protocol Params 352 ($tc,$tp,$pc,$pp)=$drd->transport_protocol_default($type) if (!$test || $type!~m/[A-Z]/); 353 if ($test) 354 { 355 $self->log_output('emergency','core','For profile "'.$name.'", using INTERNAL TESTING configuration! This should not happen in production, but only during "make test"!'); 356 Net::DRI::Exception::err_assert('test profile types are to be used only during internal tests') unless exists $INC{'Test/More.pm'}; 357 $tc='Dummy'; 358 $tp=$trans_p; 359 $trans_p=undef; 360 if ($type=~m/[A-Z]/) 361 { 362 $pc=$type; 363 $pp=defined $prot_p ? $prot_p : {}; 364 $prot_p=undef; 365 } 366 } 367 if (!Net::DRI::Util::all_valid($tc,$tp,$pc,$pp) || ref $tp ne 'HASH' || ref $pp ne 'HASH') { Net::DRI::Exception::usererr_invalid_parameters(sprintf('Registry "%s" does not provide profile type "%s")',$self->name(),$type)); } 368 369 $tp={ %$tp, %$trans_p } if defined $trans_p; 370 $pp={ %$pp, %$prot_p } if defined $prot_p; 371 $tc='Net::DRI::Transport::'.$tc unless ($tc=~m/::/); 372 $pc='Net::DRI::Protocol::'.$pc unless ($pc=~m/::/); 373 374 $drd->transport_protocol_init($type,$tc,$tp,$pc,$pp,$test) if $drd->can('transport_protocol_init'); 375 376 $tc->require() or Net::DRI::Exception::err_failed_load_module('DRI',$tc,$@); 377 $pc->require() or Net::DRI::Exception::err_failed_load_module('DRI',$pc,$@); 378 $self->log_output('debug','core',sprintf('For profile "%s" attempting to initialize transport "%s" and protocol "%s"',$name,$tc,$pc)); 379 380 my $po=$pc->new($drd,$pp); ## Protocol must come first, as it may be needed during transport setup; it should not die 381 $tp={ $po->transport_default(), %$tp } if ($po->can('transport_default')); 382 383 my $to; 384 eval 385 { 386 $to=$tc->new({registry=>$self,profile=>$name,protocol=>$po},$tp); ## this may die ! 387 }; 388 if ($@) ## some kind of error happened 389 { 390 return $@ if (ref($@) eq 'Net::DRI::Protocol::ResultStatus'); 391 $@=Net::DRI::Exception->new(1,'internal',0,'Error not handled: '.$@) unless ref($@); 392 die($@); 393 } 394 395 my $fullname=sprintf('%s (%s/%s + %s/%s)',$name,$po->name(),$po->version(),$to->name(),$to->version()); 396 $self->{profiles}->{$name}={ fullname => $fullname, transport => $to, protocol => $po, status => undef }; 397 $self->log_output('notice','core','Successfully added profile "'.$fullname.'"'); 398 return Net::DRI::Protocol::ResultStatus->new_success('COMMAND_SUCCESSFUL','Profile "'.$name.'" added successfully'); 399} 400 401sub del_profile 402{ 403 my ($self,$name)=@_; 404 if (defined($name)) 405 { 406 err_profile_name_does_not_exist($name) unless ($self->exist_profile($name)); 407 } else 408 { 409 err_no_current_profile() unless (defined($self->{profile})); 410 $name=$self->{profile}; 411 } 412 413 my $p=$self->{profiles}->{$name}; 414 $p->{protocol}->end() if (ref($p->{protocol}) && $p->{protocol}->can('end')); 415 $p->{transport}->end({registry => $self, profile => $name}) if (ref($p->{transport}) && $p->{transport}->can('end')); 416 delete($self->{profiles}->{$name}); 417 $self->{profile}=undef if $self->{profile} eq $name; ## current profile is not defined anymore 418 return Net::DRI::Protocol::ResultStatus->new_success('COMMAND_SUCCESSFUL','Profile '.$name.' deleted successfully'); 419} 420 421sub end 422{ 423 my $self=shift; 424 foreach my $name (keys(%{$self->{profiles}})) 425 { 426 my $p=$self->{profiles}->{$name}; 427 $p->{protocol}->end() if (ref($p->{protocol}) && $p->{protocol}->can('end')); 428 $p->{transport}->end() if (ref($p->{transport}) && $p->{transport}->can('end')); 429 delete $self->{profiles}->{$name} 430 } 431 432 $self->{driver}->end() if $self->{driver}->can('end'); 433} 434 435sub can 436{ 437 my ($self,$what)=@_; 438 return $self->UNIVERSAL::can($what) || $self->driver->can($what); 439} 440 441#################################################################################################### 442#################################################################################################### 443 444sub has_action 445{ 446 my ($self,$otype,$oaction)=@_; 447 my ($po,$to)=$self->protocol_transport(); 448 return $po->has_action($otype,$oaction); 449} 450 451sub process 452{ 453 my ($self,$otype,$oaction)=@_[0,1,2]; 454 my $pa=$_[3] || []; ## store them ? 455 my $ta=$_[4] || []; 456 $self->{last_process}=[$otype,$oaction,$pa,$ta]; ## should be handled more generally by LocalStorage/Exchange 457 458 ## Automated switch, if enabled 459 $self->profile_auto_switch($otype,$oaction); 460 461 ## Current protocol/transport objects for current profile 462 my ($po,$to)=$self->protocol_transport(); 463 my $trid=$self->generate_trid(); 464 my $ctx={trid => $trid, otype => $otype, oaction => $oaction, phase => 'active' }; 465 my $tosend; 466 467 eval { $tosend=$po->action($otype,$oaction,$trid,@$pa); }; ## TODO : this may need to be pushed in loop below if we need to change message to send when failure 468 return $self->format_error($@) if $@; 469 470 $self->{ops}->{$trid}=[0,$tosend,undef]; ## 0 = todo, not sent ## This will be done in/with LocalStorage 471 my $timeout=$to->timeout(); 472 my $prevalarm=alarm(0); ## removes current alarm 473 my $pause=$to->pause(); 474 my $start=Time::HiRes::time(); 475 $self->{ops}->{$trid}->[2]=$start; 476 477 my $count=0; 478 my $r; 479 while (++$count <= $to->retry()) 480 { 481 $self->log_output('debug','core',sprintf('New process loop iteration for TRID=%s with count=%d pause=%f timeout=%f',$trid,$count,$pause,$timeout)); 482 Time::HiRes::sleep($pause) if (defined($pause) && $pause && ($count > 1)); 483 $self->log_output('warning','core',sprintf('Starting try #%d for TRID=%s',$count,$trid)) if $count>1; 484 $r=eval 485 { 486 local $SIG{ALRM}=sub { die 'timeout' }; 487 alarm($timeout) if ($timeout); 488 $self->log_output('debug','core',sprintf('Attempting to send data for TRID=%s',$trid)); 489 ## Should we also pass the current registry driver (or at least its name), and the current profile name ? This may be useful in logging 490 $to->send($ctx,$tosend,$count,$ta); ## either success or exception, no result code 491 $self->log_output('debug','core','Successfully sent data to registry for TRID='.$trid); 492 $self->{ops}->{$trid}->[0]=1; ## now it is sent 493 return $self->process_back($trid,$po,$to,$otype,$oaction,$count) if $to->is_sync(); 494 my $rc=Net::DRI::Protocol::ResultStatus->new_success('COMMAND_SUCCESSFUL_PENDING'); 495 $rc->_set_trid([ $trid ]); 496 $self->status($rc); 497 return $rc; 498 }; 499 alarm(0) if ($timeout); ## removes our alarm 500 if ($@) ## some die happened inside the eval 501 { 502 return $self->format_error($@) if (ref($@) eq 'Net::DRI::Protocol::ResultStatus'); ## should probably be a return here see below TODOXXX 503 my $is_timeout=(!ref($@) && ($@=~m/timeout/))? 1 : 0; 504 $@=$is_timeout? Net::DRI::Exception->new(1,'transport',1,'timeout') : Net::DRI::Exception->new(1,'internal',0,'Error not handled: '.$@) unless ref($@); 505 $self->log_output('debug','core',$is_timeout? 'Got timeout for TRID='.$trid : 'Got error for TRID='.$trid.' : '.$@->as_string()); 506 next if $to->try_again($ctx,$po,$@,$count,$is_timeout,$self->{ops}->{$trid}->[0],\$pause,\$timeout); ## will determine if 1) we break now the loop/we propagate the error (fatal error) 2) we retry 507 die($@); 508 } 509 last if defined($r); 510 } ## end of while 511 alarm($prevalarm) if $prevalarm; ## re-enable previous alarm (warning, time is off !!) 512 Net::DRI::Exception->die(0,'transport',1,sprintf('Unable to communicate with registry after %d tries for a total delay of %.03f seconds',$to->retry(),Time::HiRes::time()-$start)) unless defined $r; 513 return $r; 514} 515 516sub format_error 517{ 518 my ($self,$err)=@_; 519 if (ref($err) eq 'Net::DRI::Protocol::ResultStatus') 520 { 521 $self->status($err); ## should that be done above also ? TODOXXX 522 return $err; 523 } 524 $err=Net::DRI::Exception->new(1,'internal',0,'Error not handled: '.$err) unless ref($err); 525 die($err); 526} 527 528## also called directly , when we found something to do for asynchronous case, through TRID (TODO) 529## We are already in an eval here, and a while loop for retries 530sub process_back 531{ 532 my ($self,$trid,$po,$to,$otype,$oaction,$count)=@_; 533 my $ctx={trid => $trid, otype => $otype, oaction => $oaction }; ## How will we fill that in case of async operation (direct call here) ? 534 my ($rc,$ri,$oname); 535 536 $self->log_output('debug','core','Attempting to receive data from registry for TRID='.$trid); 537 my $res=$to->receive($ctx,$count); ## a Net::DRI::Data::Raw or die inside 538 my $stop=Time::HiRes::time(); 539 $self->log_output('debug','core','Successfully received data from registry for TRID='.$trid); 540 Net::DRI::Exception->die(0,'transport',5,'Unable to receive message from registry') unless defined($res); 541 $self->{ops}->{$trid}->[0]=2; ## now it is received 542 $self->clear_info(); ## make sure we will overwrite current latest info 543 $oname=_extract_oname($otype,$oaction,$self->{last_process}->[2]); ## lc() would be good here but this breaks a lot of things ! 544 ($rc,$ri)=$po->reaction($otype,$oaction,$res,$self->{ops}->{$trid}->[1],$oname); ## $tosend needed to propagate EPP version, for example 545 $rc->_set_trid([ $trid ]) unless $rc->trid(); ## if not done inside Protocol::*::Message::result_status, make sure we save at least our transaction id 546 547 if ($rc->is_closing() || (exists($ri->{_internal}) && exists($ri->{_internal}->{must_reconnect}) && $ri->{_internal}->{must_reconnect})) 548 { 549 $self->log_output('notice','core','Registry closed connection, we will automatically reconnect during next exchange'); 550 $to->current_state(0); 551 } 552 delete($ri->{_internal}); 553 554 ## Set latest status from what we got 555 $self->status($rc); 556 557 $ri->{session}->{exchange}->{result_from_cache}=0; 558 $ri->{session}->{exchange}->{protocol}=$po->name().'/'.$po->version(); 559 $ri->{session}->{exchange}->{transport}=$to->name().'/'.$to->version(); 560 $ri->{session}->{exchange}->{registry}=$self->name(); 561 $ri->{session}->{exchange}->{profile}=$self->profile(); 562 $ri->{session}->{exchange}->{trid}=$trid; 563 564 ## set_info stores also data in last_data, so we make sure to call last for current object 565 foreach my $type (keys(%$ri)) 566 { 567 foreach my $key (keys(%{$ri->{$type}})) 568 { 569 next if ($oname && ($type eq $otype) && ($key eq $oname)); 570 $self->set_info($type,$key,$ri->{$type}->{$key}); 571 } 572 } 573 574 ## Now set the last info, the one regarding directly the object 575 if ($oname && $otype) 576 { 577 my $rli={ result_status => $rc }; 578 $rli=$ri->{$otype}->{$oname} if (exists($ri->{$otype}) && exists($ri->{$otype}->{$oname})); ## result_status already done in Protocol 579 $self->set_info($otype,$oname,$rli); 580 } 581 582 ## Not before ! 583 ## Remove all ResultStatus object, to avoid all circular references 584 foreach my $v1 (values(%$ri)) 585 { 586 foreach my $v2 (values(%{$v1})) 587 { 588 delete($v2->{result_status}) if exists($v2->{result_status}); 589 } 590 } 591 592 $ri->{session}->{exchange}={ %{$ri->{session}->{exchange}}, duration_seconds => $stop-$self->{ops}->{$trid}->[2], raw_command => $self->{ops}->{$trid}->[1]->as_string(), raw_reply => $res->as_string(), object_type => $otype, object_action => $oaction }; 593 $ri->{session}->{exchange}->{object_name}=$oname if $oname; 594 $rc->_set_data($ri); 595 delete($self->{ops}->{$trid}); 596 return $rc; 597} 598 599sub _extract_oname 600{ 601 my ($otype,$oaction,$pa)=@_; 602 603 return 'domains' if ($otype eq 'account' && $oaction eq 'list_domains'); 604 my $o=$pa->[0]; 605 return 'session' unless defined($o); 606 $o=$o->[1] if (ref($o) eq 'ARRAY'); ## should be enough for _multi but still a little strange 607 return (Net::DRI::Util::normalize_name($otype,$o))[1] unless ref($o); ## ?? ## TODO ## this fails t/626nominet line 306 608 return (Net::DRI::Util::normalize_name('nsgroup',$otype eq 'nsgroup'? $o->name() : $o->get_details(1)))[1] if Net::DRI::Util::isa_hosts($o); 609 return $o->srid() if Net::DRI::Util::isa_contact($o); 610 return 'session'; 611} 612 613#################################################################################################### 614 615sub protocol_capable 616{ 617 my ($ndr,$op,$subop,$action)=@_; 618 return 0 unless ($op && $subop); ## $action may be undefined 619 my $po=$ndr->protocol(); 620 my $cap=$po->capabilities(); ## hashref 621 622 return 0 unless ($cap && (ref($cap) eq 'HASH') && exists($cap->{$op}) 623 && (ref($cap->{$op}) eq 'HASH') && exists($cap->{$op}->{$subop}) 624 && (ref($cap->{$op}->{$subop}) eq 'ARRAY')); 625 626 return 1 unless (defined($action) && $action); 627 628 foreach my $a (@{$cap->{$op}->{$subop}}) 629 { 630 return 1 if ($a eq $action); 631 } 632 return 0; 633} 634 635sub log_output 636{ 637 my ($self,$level,$where,$msg)=@_; 638 my $r=$self->name(); 639 $r.='.'.$self->{profile} if (defined $self->{profile}); 640 $msg='('.$r.') '.$msg; 641 return $self->SUPER::log_output($level,$where,$msg); 642} 643 644#################################################################################################### 645 646sub AUTOLOAD 647{ 648 my $self=shift; 649 my $attr=$AUTOLOAD; 650 $attr=~s/.*:://; 651 return unless $attr=~m/[^A-Z]/; ## skip DESTROY and all-cap methods 652 653 my $drd=$self->driver(); ## This is a DRD object 654 Net::DRI::Exception::err_method_not_implemented($attr.' in '.$drd) unless (ref($drd) && $drd->can($attr)); 655 $self->log_output('debug','core',sprintf('Calling %s from Net::DRI::Registry',$attr)); 656 return $drd->$attr($self,@_); 657} 658 659#################################################################################################### 6601; 661