File Coverage

blib/lib/Net/DirectConnect.pm
Criterion Covered Total %
statement 51 907 5.6
branch 2 582 0.3
condition 1 491 0.2
subroutine 16 101 15.8
pod 0 73 0.0
total 70 2154 3.2


line stmt bran cond sub pod time code
1             #$Id: DirectConnect.pm 1002 2014-08-27 14:35:23Z pro $ $URL: svn://svn.setun.net/dcppp/trunk/lib/Net/DirectConnect.pm $
2             package Net::DirectConnect;
3 1     1   44260 use strict;
  1         2  
  1         44  
4 1     1   5 no strict qw(refs);
  1         1  
  1         26  
5 1     1   5 use warnings "NONFATAL" => "all";
  1         6  
  1         44  
6 1     1   3 no warnings qw(uninitialized);
  1         499  
  1         4103  
7 1     1   1408 no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  1         10  
  1         7  
8             our $VERSION = '0.14'; # . '_' . ( split ' ', '$Revision: 1002 $' )[1];
9 1     1   1266 use utf8;
  1         11  
  1         5  
10 1     1   980 use Encode;
  1         24313  
  1         109  
11 1     1   1070 use Socket;
  1         4745  
  1         657  
12 1     1   1226 use IO::Socket;
  1         24420  
  1         5  
13 1     1   25437 use IO::Select;
  1         3707  
  1         81  
14 1     1   1345 use POSIX;
  1         8830  
  1         8  
15             #use Fcntl;
16 1     1   19241 use Time::HiRes qw(time sleep);
  1         3107  
  1         8  
17 1     1   1700 use Data::Dumper;
  1         13213  
  1         21436  
18             $Data::Dumper::Sortkeys = $Data::Dumper::Useqq = $Data::Dumper::Indent = $Data::Dumper::Terse = 1;
19             our $AUTOLOAD;
20             our %global;
21 0     0 0 0 sub is_code ($) { UNIVERSAL::isa( $_[0], 'CODE' ) }
22 0 0   0 0 0 sub code_run ($;@) { my $f = shift; return $f->(@_) if is_code $f }
  0         0  
23 0 0   0 0 0 sub is_object ($) { ref $_[0] and ref $_[0] ne 'HASH' }
24 0 0 0 0 0 0 sub can_run ($$;@) { my $c = shift || return; return unless is_object $c; my $f = shift || return; my $r = $c->can($f); return $r->( $c, @_ ) if $r; }
  0 0 0     0  
  0         0  
  0         0  
  0         0  
25              
26             sub float { #v1
27 0 0   0 0 0 my $self = shift if ref $_[0];
28 0 0 0     0 return ( $_[0] < 8 and $_[0] - int( $_[0] ) )
    0          
    0          
29             ? sprintf( '%.' . ( $_[0] < 1 ? 3 : ( $_[0] < 3 ? 2 : 1 ) ) . 'f', $_[0] )
30             : int( $_[0] );
31             }
32              
33             sub mkdir_rec(;$$) {
34 0   0 0 0 0 local $_ = shift // $_;
35 0 0       0 $_ .= '/' unless m{/$};
36 0 0       0 while (m{/}g) { @_ ? mkdir $`, $_[0] : mkdir $` if length $` }
  0 0       0  
37             }
38              
39             sub send_udp ($$;@) {
40 0 0   0 0 0 my $self = shift if ref $_[0];
41 0         0 my $host = shift;
42             #$host =~ s/:(\d+)$//;
43 0         0 my $port = shift;
44             #$port ||= $1;
45 0         0 $self->log( 'dcdev', "sending UDP to [$host]:[$port] = [$_[0]]" );
46             #$self->log( 'dcdev', "sending UDP to [$host] = [$_[0]]" );
47 0   0     0 my $opt = $_[1] || {};
48 0 0       0 if (
    0          
49 0 0       0 my $s = $self->{'socket_class'}->new(
50             'PeerAddr' => $host,
51             ( $port ? ( 'PeerPort' => $port ) : () ),
52             'Proto' => 'udp',
53             'Timeout' => $opt->{'Timeout'}, (
54             #$opt->{'nonblocking'} ? (
55             'Blocking' => 0,
56             #'MultiHomed' => 1, #del
57             #) : ()
58             ),
59             %{ $opt->{'socket_options'} || {} },
60             )
61             )
62             {
63 0         0 $s->send( Encode::encode $self->{charset_protocol}, $_[0], Encode::FB_WARN );
64 0         0 $self->{bytes_send} += length $_[0];
65             #$s->shutdown(2);
66 0         0 $s->close();
67             #close($s);
68             #$self->log( 'dcdev', "sent ",length $_[0]," closed [$s],");
69             } else {
70             #$self->log( 'dcerr', "FAILED sending UDP to $host :$port = [$_[0]]" );
71 0         0 $self->log( 'dcerr', "FAILED sending UDP to $host = [$_[0]]" );
72             }
73             }
74              
75             sub socket_addr ($) {
76 0     0 0 0 my ($socket) = @_;
77 0 0       0 return wantarray ? ( $socket->peerhost, $socket->peerport ) : $socket->peerhost;
78              
79             =old
80             local @_;
81             #eval { @_ = unpack_sockaddr_in( getpeername($socket) || return ) };
82             eval { @_ = unpack_sockaddr_in( $socket->peername || return ) };
83             return unless $_[1];
84             return unless $_[1] = inet_ntoa( $_[1] );
85             return @_;
86             =cut
87              
88             =todo
89             my ($err, $hostname, $servicename) = Socket::getnameinfo($socket->peername);
90             if ($err) {
91             if (use_try 'Socket6') {
92             #warn "Cannot getnameinfo - $err; $hostname, $servicename" ;
93             $hostname = Socket6::getnameinfo($socket->peername);
94             }
95             }
96             warn 'getn', $hostname,$servicename;
97             return wantarray ? ($hostname,$servicename) : $hostname;
98             =cut
99              
100             }
101              
102             sub schedule($$;@)
103             { #$Id: DirectConnect.pm 1002 2014-08-27 14:35:23Z pro $ $URL: svn://svn.setun.net/dcppp/trunk/lib/Net/DirectConnect.pm $
104 0     0 0 0 our %schedule;
105 0         0 my ( $every, $func ) = ( shift, shift );
106 0         0 my $p;
107 0 0       0 ( $p->{'wait'}, $p->{'every'}, $p->{'runs'}, $p->{'cond'}, $p->{'id'} ) = @$every if ref $every eq 'ARRAY';
108 0 0       0 $p = $every if ref $every eq 'HASH';
109 0 0 0     0 $p->{'every'} ||= $every if !ref $every;
110 0   0     0 $p->{'id'} ||= join ';', caller;
111             #dmp $p, \%schedule;
112             #dmp $schedule{ $p->{'id'} }{'runs'}, $p->{'runs'}, $p, $schedule{ $p->{'id'} } if $p->{'runs'};
113 0 0 0     0 $schedule{ $p->{'id'} }{'func'} = $func if !$schedule{ $p->{'id'} }{'func'} or $p->{'update'};
114 0 0 0     0 $schedule{ $p->{'id'} }{'last'} = time - $p->{'every'} + $p->{'wait'} if $p->{'wait'} and !$schedule{ $p->{'id'} }{'last'};
115             #dmp("RUN", $p->{'id'}),
116 0 0 0     0 ++$schedule{ $p->{'id'} }{'runs'}, $schedule{ $p->{'id'} }{'last'} = time, $schedule{ $p->{'id'} }{'func'}->(@_),
      0        
      0        
      0        
      0        
117             if ( $schedule{ $p->{'id'} }{'last'} + $p->{'every'} < time )
118             and ( !$p->{'runs'} or $schedule{ $p->{'id'} }{'runs'} < $p->{'runs'} )
119             and ( !( ref $p->{'cond'} eq 'CODE' ) or $p->{'cond'}->( $p, $schedule{ $p->{'id'} }, @_ ) )
120             and ref $schedule{ $p->{'id'} }{'func'} eq 'CODE';
121             }
122              
123             sub notone (@) {
124 0 0   0 0 0 @_ = grep { $_ and $_ != 1 } @_;
  0         0  
125 0 0       0 wantarray ? @_ : $_[0];
126             }
127              
128             sub use_try ($;@) {
129 1 50   1 0 4 my $self = shift if ref $_[0];
130 1         2 our %tried;
131 1         20 ( my $path = ( my $module = shift ) . '.pm' ) =~ s{::}{/}g;
132 1 50       4 return $tried{$module} if exists $tried{$module};
133 1         9 local $SIG{__DIE__} = undef;
134 1   33 1   974 $tried{$module} = ( $INC{$path} or eval 'use ' . $module . ' qw(' . ( join ' ', @_ ) . ');1;' and $INC{$path} );
  1         691  
  1         11  
  1         115  
135             }
136              
137             sub module_load {
138 0 0   0 0   my $self = shift if ref $_[0];
139 0           local $_ = shift;
140 0 0         return unless length $_;
141             #$self->log( 'dev', "loading", $_, $self->{'module_loaded'}{$_});
142 0 0         return if $self->{'module_loaded'}{$_}++;
143 0           my $module = __PACKAGE__ . '::' . $_;
144             #eval "use $module;";
145 0 0         $self->log( 'err', 'cant load', $module, $@ ), return unless use_try $module;
146             #$self->log( 'err', 'cant load', $module, $@ ), return if $@;
147             #${module}::new($self, @_) if $module->can('new');
148             #${module}::init($self, @_) if $module->can('init');
149             #$self->log( 'dev', 'can', $module->can('new'));
150             #$self->log( 'dev', 'can', $module->can('init'));
151             #eval "$module\::new(\$self, \@_);"; #, \@param
152 0 0 0       $_->( $self, @_ ) if $_ = $module->can('new') and $_ ne __PACKAGE__->can('new');
153 0 0         $_->( $self, @_ ) if $_ = $module->can('init');
154             #$self->log( 'err', 'cant new', $module, $@ ), return if $@;
155             #eval "$module\::init(\$self, \@_);"; #, \@param
156             #$self->log( 'err', 'cant init', $module, $@ ), return if $@;
157             # $self->log( 'ddev', 'loaded module', $module, );
158 0           1;
159             }
160              
161             sub new {
162             #print 'NEW:',Dumper \@_;
163 0     0 0   my $class = shift;
164 0           my $self = {};
165 0 0         if ( ref $class eq __PACKAGE__ ) { $self = $class; }
  0            
166 0 0         else { bless( $self, $class ) unless ref $class; }
167             #print ref $self;
168             #$self-
169             #psmisc::printlog('dev', 'new', @_);
170             #psmisc::printlog('dev', 'func', Dumper @_);
171 0   0       $self->{'number'} ||= ++$global{'total'};
172 0           ++$global{'count'};
173             #$self->log('dev', 'new', @_);
174 0           $self->func(@_); #@param
175 0 0         eval { $self->{'recv_flags'} = MSG_DONTWAIT; } unless $^O =~ /win/i;
  0            
176 0   0       $self->{'recv_flags'} ||= 0;
177             #psmisc::printlog('dev', 'init');
178 0           $self->init_main(@_); #@param
179 0           $self->init(@_); #@param
180             #}
181 0           $self->{activity} = time;
182             #$self->{$_} ||= $self->{'parent'}{$_} for grep { exists $self->{'parent'}{$_} } qw(log sockets select select_send);
183             #(!$self->{'parent'}{$_} ? () : $self->{$_} = $self->{'parent'}{$_} ) for qw(log );
184             #$self->{'log'} = $self->{'parent'}{'log'} if $self->{'parent'}{'log'};
185             #$self->{$_} ||= $self->{'parent'}{$_} ||= {}
186             #$self->log( 'dev', '1uphandler my=',$self->{handler},Dumper($self->{handler}) , 'p=',Dumper($self->{'parent'}{handler}),$self->{'parent'}{handler},);
187             #$self->{'parent'}{$_} ||= {} , $self->{$_} ||= $self->{'parent'}{$_},
188             #$self->log( 'dev', '2uphandler my=',$self->{handler},Dumper($self->{handler}) , 'p=',Dumper($self->{'parent'}{handler}),$self->{'parent'}{handler},);
189             #$self->log( 'dev', "my number=$self->{'number'} total=$global{'total'} count=$global{'count'}" );
190             #$self->log( 'dev', $class ,' eq ', __PACKAGE__);
191 0 0         if ( $class eq __PACKAGE__ ) {
192             #local %_ = (@_);
193             #$self->log( 'dev', $class ,' eq ', __PACKAGE__, Dumper @_);
194              
195             #for keys
196             #$self->{$_} = $_{$_} for keys %_;
197             #$self->log( 'init00', $self, "h=$self->{'host'}", 'p=', $self->{'protocol'}, 'm=', $self->{'module'} );
198 0 0 0       if ( $self->{'host'} ~~ m{^(?:\w+://)?broadcast} or $self->{'host'} =~ /^(?:255\.|\[?ff)/i ) {
199             #if (use_try 'Socket::Multicast6', 'ipv6') {
200             #IPV6_JOIN_GROUP
201             #}
202 0   0       $self->{'protocol'} ||= 'adc';
203 0           $self->{'auto_listen'} = 1;
204 0           delete $self->{'auto_connect'};
205 0           $self->{'Proto'} = 'udp';
206 0           $self->{'socket_options'}{'Broadcast'} = 1;
207 0           $self->{'socket_options'}{'ReuseAddr'} = 1;
208             #$self->{'host'} = $self->{dev_ipv6} ? 'ff02::1' : inet_ntoa(INADDR_BROADCAST) if $self->{'host'} !~ /^(?:255\.|\[?ff)/i;
209 0 0         $self->{'host'} = inet_ntoa(INADDR_BROADCAST) if $self->{'host'} !~ /^(?:255\.|\[?ff)/i;
210             #$self->{socket_options_listen}{'LocalHost'} = 'ff02::1';
211             #$self->{'port'},
212             #$self->log( 'dev', "send to", );
213 0           $self->{'broadcast'} = 1;
214             #$self->{'lis'} = 1;
215             #$self->log('dev', "broadcast=$self->{'host'}, auto_listen=$self->{'auto_listen'} auto_connect=$self->{'auto_connect'}");
216             }
217 0 0 0       if (
218             #!$self->{'module'} and
219             !$self->{'protocol'} and $self->{'host'}
220             )
221             {
222             #$self->log( 'proto0 ', $1);
223 0 0         my $p = lc $1 if $self->{'host'} =~ m{^(.+?)://};
224             #$self->protocol_init($p);
225 0           $self->{'protocol'} = $p;
226 0 0 0       $self->{'protocol'} = 'nmdc'
227             if !$self->{'protocol'}
228             or $self->{'protocol'} eq 'dchub';
229             #$self->{'protocol'}
230             #$self->log( 'proto ', $self->{'protocol'} );
231             }
232             #$self->{'module'} ||= $self->{'protocol'};
233             #if ( $self->{'module'} eq 'nmdc' ) {
234             # $self->{'module'} = [ 'nmdc', ( $self->{'hub'} ? 'hubcli' : 'clihub' ) ];
235             #}
236 0           ++$self->{'module'}{ $self->{'protocol'} };
237 0 0         if ( $self->{'protocol'} eq 'nmdc' ) {
238 0 0         ++$self->{'module'}{ $self->{'hub'} ? 'hubcli' : 'clihub' };
239             }
240             #++$self->{'module'}{$_} for grep { $self->{ 'dev_' . $_ } } qw(ipv6);
241             #, ($] < 5.014 ? 'ipv6' : ()); #sctp
242             #if ( $self->{'module'} ) {
243             }
244 0 0         if ( $self->{'dev_sctp'} ) {
245 0 0         unless ( $self->{'no_sctp_fallback'} ) {
246 0           $self->log( 'dev', 'make sctp clone', $class );
247 0           $self->{'clients'}{ 'sctp_' . $self->{'number'} } = $class->new(
248             @_,
249             'dev_sctp' => undef,
250             'parent' => $self,
251             auto_work => 0,
252             no_wait_connect => 1,
253             #reconnect_tries=>1,
254             #myport_tries=>1,
255             #'Proto' => 'sctp',
256             modules => [qw(sctp)],
257             );
258             } else {
259 0           ++$self->{'module'}{'sctp'};
260             }
261 0           $self->info;
262             }
263 0           ++$self->{'module'}{$_} for grep { $self->{'protocol'} eq $_ } qw(adcs http);
  0            
264             #$self->log( 'dev', 'module load', $self->{'module'}, 'p', $self->{'protocol'} );
265 0           my @modules; #= ($self->{'module'});
266 0           for (qw(module modules)) {
267 0 0         push @modules, @{ $self->{$_} } if ref $self->{$_} eq 'ARRAY';
  0            
268 0 0         push @modules, keys %{ $self->{$_} } if ref $self->{$_} eq 'HASH';
  0            
269 0 0         push @modules, split /[;,\s]/, $self->{$_} unless ref $self->{$_};
270             }
271             #$self->log( 'dev', 'modules load', @modules );
272             #$self->log( 'modules load', @modules, @_);
273 0           $self->module_load( $_, @_ ) for @modules;
274             #$self->log( 'now proto', $self->{'Proto'});
275 0   0       $self->{charset_chat} ||= $self->{charset_protocol};
276             #$self->protocol_init();
277             #$self->log( 'dev', $self, 'new inited', "MT:$self->{'message_type'}", 'autolisten=', $self->{'auto_listen'} );
278             #$self->{charset_console} = 'utf8';
279             # $self->log( 'dev', "set console encoding [$self->{charset_console}]");
280             #eval qq{use encoding $self->{charset_console}};
281 0 0 0       eval qq{use encoding '$self->{charset_internal}', STDOUT=> '$self->{charset_console}', STDIN => '$self->{charset_console}'}
      0        
      0        
282             if !$self->{parent}
283             and !$self->{no_charset_console}
284             and $self->{charset_internal}
285             and $self->{charset_console};
286             #$self->log( 'dev', 'utf8: УТф восемь');
287             #$self->log( 'dev', Dumper $self);
288 0 0         $self->log( 'dcdbg',
289             "using encodings console:[$self->{charset_console}] protocol:[$self->{charset_protocol}] fs:[$self->{charset_fs}]" )
290             unless $self->{parent}{parent};
291 0 0         if ( $self->{'auto_say'} ) {
292 0 0         for my $cmd ( @{ $self->{'auto_say_cmd'} || [] } ) {
  0            
293             #$self->log('AS', $cmd);
294             $self->{'handler_int'}{$cmd} = sub {
295 0     0     my $dc = shift;
296             #$self->log('ASH', $cmd);
297 0           $self->say( $cmd, @_ ); #print with console encoding
298 0           };
299             }
300             }
301             #$self->log('dev', 'sctp', $self->{'dev_sctp'});
302             #$self->log('dev', "auto_listen=$self->{'auto_listen'} auto_connect=$self->{'auto_connect'}");
303 0 0         if ( $self->{'auto_listen'} ) {
    0          
304             #$self->{'disconnect_recursive'} = $self->{'parent'}{'disconnect_recursive'};
305 0   0       $self->{'incomingclass'} ||= $self->{'parent'}{'incomingclass'}; # if $self->{'parent'};
306 0           $self->listen();
307             #$self->log('go conaft');
308 0 0         $self->connect_aft() if $self->{'broadcast'};
309             } elsif ( $self->{'auto_connect'} ) {
310             #$self->log( $self, 'new inited', "auto_connect MT:$self->{'message_type'}", ' with' );
311 0           $self->connect();
312             #$self->work();
313 0 0         $self->wait_connect() unless $self->{'no_wait_connect'};
314             } else {
315 0           $self->get_my_addr();
316 0           $self->get_peer_addr();
317             }
318 0 0         if ( $self->{'auto_work'} ) {
319             #$self->log( $self, '', "auto_work ", $self->active() );
320 0           while ( $self->active() ) {
321 0           $self->work(); #forever
322             #$self->{'auto_work'}->($self) if ref $self->{'auto_work'} eq 'CODE';
323             #Time::HiRes::sleep 0.01;
324             }
325 0           $self->disconnect();
326             }
327             #@ psmisc::file_rewrite( 'dump.new', Dumper $self);
328 0           return $self;
329             }
330              
331             sub log(@) {
332 0     0 0   my $self = shift;
333 0 0         return $self->{'log'}->( $self, @_ ) if ref $self->{'log'} eq 'CODE';
334 0           print( join( ' ', "[$self->{'number'}]", @_ ), "\n" );
335             }
336              
337             sub cmd {
338 0     0 0   my $self = shift;
339             #return unless $self->{'cmd'};
340 0           my $dst;
341 0 0 0       $dst = #$_[0]
342             shift if $self->{'adc'} and length $_[0] == 1;
343 0           my $cmd = shift;
344 0           my ( @ret, $ret );
345             #$self->{'log'}->($self,'dev', 'cmd', $cmd, @_) if $cmd ne 'log';
346             #$self->{'log'}->($self,'dev', $self->{number},'cmd', $cmd, @_) if $cmd ne 'log';
347 0           my ( $func, $handler );
348 0 0 0       if ( $self->{'cmd'} and ref $self->{'cmd'}{$cmd} eq 'CODE' ) {
    0 0        
    0          
    0          
349 0           $func = $self->{'cmd'}{$cmd};
350 0           $handler = '_cmd';
351 0 0         unshift @_, $dst if $dst;
352             } elsif ( ref $self->{$cmd} eq 'CODE' ) {
353 0           $func = $self->{$cmd};
354             } elsif ( $self->{'cmd'} and ref $self->{'cmd'}{ $dst . $cmd } eq 'CODE' ) {
355 0           $func = $self->{'cmd'}{ $dst . $cmd };
356 0           $handler = '_cmd';
357             #unshift @_, $dst if $dst;
358             } elsif ( $func = $self->can($cmd) ) {
359             }
360 0           $self->handler( $cmd . $handler . '_bef_bef', \@_ );
361 0 0 0       if ( $self->{'min_cmd_delay'}
362             and ( time - $self->{'last_cmd_time'} < $self->{'min_cmd_delay'} ) )
363             {
364 0           $self->log( 'dbg', 'sleepcmd', $self->{'min_cmd_delay'} - time + $self->{'last_cmd_time'} );
365 0           sleep( $self->{'min_cmd_delay'} - time + $self->{'last_cmd_time'} );
366             }
367 0           $self->{'last_cmd_time'} = time;
368 0           $self->handler( $cmd . $handler . '_bef', \@_ );
369             #$self->{'log'}->($self,'dev', $self->{number},'cmdrun', $dst, $cmd, @_, $func) if $cmd ne 'log';
370 0 0 0       if ($func) {
    0 0        
    0          
371 0           @ret = $func->( $self, @_ ); #$self->{'cmd'}{$cmd}->(@_);
372             } elsif ( $self->{'adc'} and length $dst == 1 and length $cmd == 3 ) {
373 0           @ret = $self->cmd_adc( $dst, $cmd, @_ );
374             } elsif ( exists $self->{$cmd} ) {
375 0           $self->log( 'dev', "cmd call by var name $cmd=$self->{$cmd}" );
376 0           @ret = ( $self->{$cmd} );
377             } else {
378 0           $self->log(
379             'dev',
380             "UNKNOWN CMD(st[$self->{'status'}]):[$cmd]{@_}: please add \$dc->{'cmd'}{'$cmd'} = sub { ... };",
381             "self=", ref $self,
382             #Dumper $self->{'cmd'},
383             $self->{'parse'}
384 0 0         ) if !grep { $cmd eq $_ } qw(new init);
385 0     0     $self->{'cmd'}{$cmd} = sub { }
386 0 0         if $self->{'cmd'};
387             }
388 0 0         $ret = scalar @ret > 1 ? \@ret : $ret[0];
389 0           $self->handler( $cmd . $handler . '_aft', \@_, $ret );
390 0 0 0       if ( $self->{'cmd'} and $self->{'cmd'}{$cmd} ) {
391 0 0         if ( $self->{'auto_wait'} ) { $self->wait(); }
  0 0          
392 0           elsif ( $self->{'auto_recv'} ) { $self->select(); }
393             }
394 0           $self->handler( $cmd . $handler . '_aft_aft', \@_, $ret );
395 0 0         return wantarray ? @ret : $ret[0];
396             }
397              
398             sub AUTOLOAD {
399             #psmisc::printlog('autoload', $AUTOLOAD,@_);
400 0   0 0     my $self = shift || return;
401 0   0       my $type = ref($self) || return;
402             #my @p = @_;
403 0           my $name = $AUTOLOAD;
404 0           $name =~ s/.*\://;
405             #return $self->cmd( $name, @p );
406 0           return $self->cmd( $name, @_ );
407             }
408              
409             sub DESTROY {
410 0     0     my $self = shift;
411             #warn "DESTROY [$self->{number}]";
412             #$self->log( 'dev', 'DESTROYing' );
413 0           $self->destroy();
414 0           --$global{'count'};
415             }
416              
417             sub handler {
418 0     0 0   my $self = shift;
419 0 0         shift if ref $_[0];
420 0           my $cmd = shift;
421             #$self->log('dev', 'handler select', $cmd, $self->{'handler_int'}{$cmd}, $self->{'handler'}{$cmd});
422 0 0 0       $self->{'handler_int'}{$cmd}->( $self, @_ )
423             if $self->{'handler_int'}
424             and ref $self->{'handler_int'}{$cmd} eq 'CODE'; #internal lib
425 0 0 0       $self->{'handler'}{$cmd}->( $self, @_ )
426             if $self->{'handler'} and ref $self->{'handler'}{$cmd} eq 'CODE';
427             }
428             #sub baseinit {
429             #my $self = shift;
430             #$self->{'number'} = ++$global{'total'};
431             #$self->myport_generate();
432             #$self->{'port'} = $1 if $self->{'host'} =~ s/:(\d+)//;
433             #$self->{'want'} ||= {};
434             #$self->{'NickList'} ||= {};
435             #$self->{'IpList'} ||= {};
436             #$self->{'PortList'} ||= {};
437             #++$global{'count'};
438             #$self->{'status'} = 'disconnected';
439             #$self->protocol_init( $self->{'protocol'} );
440             #}
441             sub func {
442 0     0 0   my $self = shift;
443             #$self->log( 'dev', 'func', __PACKAGE__, 'func', __FILE__, __LINE__ );
444             }
445              
446             sub init_main { #$self->{'init_main'} ||= sub {
447 0     0 0   my $self = shift;
448             #$self->log( 'dev', 'init', __PACKAGE__, 'func', __FILE__, __LINE__ , Dumper \@_);
449 0           local %_ = @_;
450             #warn Dumper \%_;
451             #$self->log('dev', __LINE__, "Proto=$self->{Proto}");
452 0           $self->{$_} = $_{$_} for keys %_;
453 0           local %_ = (
454             'recv' => 'recv',
455             'send' => 'send',
456             'Listen' => 10,
457             'Timeout' => 10, #connect
458             'Timeout_connected' => 300,
459             'myport' => 412, #first try
460             'myport_base' => 40000,
461             'myport_random' => 1000,
462             'myport_tries' => 5,
463             'cmd_sep' => ' ',
464             'no_print' => { map { $_ => 1 } qw(Search Quit MyINFO Hello SR UserCommand) },
465             'log' => sub (@) {
466 0 0   0     my $self = ref $_[0] ? shift() : {};
467 0 0         if ( ref $self->{'parent'}{'log'} eq 'CODE' ) {
468 0           return $self->{'parent'}->log( "[$self->{'number'}]", @_ );
469             }
470             #utf8::valid(join '', @_) and
471 0           print( join( ' ', "[$self->{'number'}]", @_ ), "\n" );
472             #Dumper \
473             },
474             #'auto_recv' => 1,
475             #'max_reads' => 20,
476             #'wait_once' => 0.1,
477             #'waits' => 100,
478             #'wait_finish_tries' => 600,
479             #'wait_finish_by' => 1,
480             #'wait_connect_tries' => 600,
481 0 0         'clients_max' => 50,
    0          
    0          
482             #'wait_clients_tries' => 200,
483             'wait_finish_tries' => 300, #5 min
484             'wait_clients' => 300, #5 min
485             #del 'wait_clients_by' => 0.01,
486             #'work_sleep' => 0.01,
487             'work_sleep' => 0.005,
488             'select_timeout' => 1,
489             'cmd_recurse_sleep' => 0,
490             #( $^O eq 'MSWin32' ? () : ( 'nonblocking' => 1 ) ),
491             #'nonblocking' => 1,
492             'informative' => [qw(number peernick status host port filebytes filetotal proxy bytes_send bytes_recv)], # sharesize
493             #'informative_hash' => [qw(clients)], #NickList IpList PortList
494             #'disconnect_recursive' => 1,
495             'reconnect_sleep' => 5,
496             'partial_ext' => '.partial',
497             'file_send_by' => 1024 * 1024, #1024 * 64,
498             'local_mask_rfc' => [qw(10 172.[123]\d 192\.168)],
499             'status' => 'disconnected',
500             'time_start' => time,
501             #'peers' => {},
502             'download_to' => './downloads/',
503             #'partial_prefix' => './downloads/Incomplete/',
504             #ADC
505             #number => ++$global{'total'},
506             #};
507             'charset_fs' => (
508             $^O eq 'MSWin32'
509             ? 'cp1251'
510             #: $^O eq 'freebsd' ? 'koi8r'
511             : 'utf8'
512             ),
513             'charset_console' => ( $^O eq 'MSWin32' ? 'cp866' :
514             #$^O eq 'freebsd' ? 'koi8r' :
515             'utf8' ),
516             'charset_protocol' => 'utf8',
517             'charset_internal' => 'utf8',
518             #charset_nick => 'utf8',
519             'socket_class' => ( use_try('IO::Socket::IP') ? 'IO::Socket::IP' : 'IO::Socket::INET' ),
520             );
521             #$self->log(__LINE__, "Proto=$self->{Proto}, protocol=$self->{protocol} class $self->{'socket_class'}");
522 0   0       $self->{'wait_connect_tries'} //= $self->{'Timeout'};
523 0   0       $self->{$_} //= $self->{'parent'}{$_} ||= {} for qw(peers peers_sid peers_cid handler clients); #want share_full share_tth
      0        
524             #$self->{$_} ||= $self->{'parent'}{$_} ||= {}, for qw( );
525 0   0       $self->{$_} //= $self->{'parent'}{$_} ||= [] for qw(queue_download);
      0        
526             $self->{$_} //= $self->{'parent'}{$_} ||= $global{$_} ||= {},
527 0   0       for qw(sockets share_full share_tth want want_download want_download_filename downloading);
      0        
      0        
528 0   0       $self->{$_} //= $self->{'parent'}{$_} ||= $global{$_}, for qw(db);
      0        
529             $self->{'parent'}{$_} ? $self->{$_} //= $self->{'parent'}{$_} : ()
530 0 0 0       for
531             qw(log disconnect_recursive partial_prefix partial_ext download_to Proto dev_ipv6 protocol myport_inc no_sctp_fallback)
532             ; #dev_adcs socket_class
533             #$self->log( 'dev', "Proto=$self->{Proto}, Listen=$self->{Listen} protocol=$self->{protocol} inc=$self->{myport_inc}" );
534 0   0       $self->{$_} //= { %{ $self->{'parent'}{$_} } } for qw(socket_options); # clone, childs can change
  0            
535 0   0       $self->{$_} //= $_{$_} for keys %_;
536 0   0       $self->{'partial_prefix'} //= $self->{'download_to'} . 'Incomplete/';
537             #$self->log(__LINE__, "Proto=$self->{Proto}, protocol=$self->{protocol} class $self->{'socket_class'} ");
538             #$self->log("charset_console=$self->{charset_console} charset_fs=$self->{charset_fs}");
539             #psmisc::printlog('dev', 'init0', Dumper $self);
540             }
541              
542             sub myport_generate { #$self->{'myport_generate'} ||= sub {
543 0     0 0   my $self = shift;
544             #$self->log( 'myport', "$self->{'myport'}: $self->{'myport_base'} or $self->{'myport_random'}" );
545 0 0 0       return $self->{'myport'}
546             unless $self->{'myport_base'}
547             or $self->{'myport_random'};
548 0 0         $self->{'myport'} = undef if $_[0];
549 0 0 0       return $self->{'myport'} ||= $self->{'myport_base'} + $self->{'myport_inc'}++ if $self->{'myport_inc'};
550 0   0       return $self->{'myport'} ||= $self->{'myport_base'} + int( rand( $self->{'myport_random'} ) );
551             }
552              
553             sub select_add { #$self->{'select_add'} ||= sub {
554 0     0 0   my $self = shift;
555             #$self->{'select'} ||= $self->{parent}{'select'} $self->{'select_send'} ||= $self->{parent}{'select_send'} if $self->{parent};
556             #$self->{'sockets'} ||= $self->{parent}{'sockets'} if $self->{parent};
557 0   0       $self->{$_} ||= $self->{parent}{$_} ||= $global{$_} ||= IO::Select->new() for qw (select select_send);
      0        
      0        
558             #$self->{'select'} ||= IO::Select->new(); #$self->{'socket'}
559             #$self->{'select_send'} ||= IO::Select->new(); #$self->{'socket'}
560 0 0         return unless $self->{'socket'};
561 0           $self->{'select'}->add( $self->{'socket'} );
562 0           $self->{'select_send'}->add( $self->{'socket'} );
563 0           $self->{'sockets'}{ $self->{'socket'} } = $self;
564             #$self->log( 'dev', 'add:', $self->{'socket'},' current select', $self->{'select'}->handles );
565             }
566             #$self->{'connect_check'} ||= sub {
567             sub connect_check {
568 0     0 0   my $self = shift;
569             #$self->log('dev', 'connect_check', " s=$self->{'status'}, i=$self->{'incoming'}");
570 0 0 0       return 0
      0        
      0        
      0        
571             if $self->{'Proto'} eq 'udp'
572             or $self->{'incoming'}
573             #or $self->{'status'} eq 'listening' or
574             or $self->{'listener'}
575             or (
576             $self->{'socket'}
577             #and $self->{'socket'}->connected()
578             ) or !$self->active();
579             #$self->log( 'warn', 'connect_check: must reconnect', Dumper $self->{'socket'}->connected(), $self->{'socket'}, $self->{'status'});
580 0           $self->{'status'} = 'reconnecting';
581             $self->every(
582             $self->{'reconnect_sleep'},
583             $self->{'reconnect_func'} ||= sub {
584 0 0   0     if ( $self->{'reconnect_tries'}++ < $self->{'reconnects'} ) {
585 0           $self->log(
586             'warn',
587             "reconnecting ($self->{'host'}) [$self->{'reconnect_tries'}/$self->{'reconnects'}] every",
588             $self->{'reconnect_sleep'}
589             );
590 0           $self->connect();
591             } else {
592 0           $self->{'status'} = 'disconnected';
593             }
594             }
595 0   0       );
596 0           return 1;
597             }
598              
599             sub connect { #$self->{'connect'} ||= sub {
600 0     0 0   my $self = shift;
601             #$self->log( 'c', 'connect0 inited', "MT:$self->{'message_type'}", ' with', $self->{'host'} );
602 0 0 0       if ( $_[0] or $self->{'host'} =~ /:/ ) {
603 0 0         $self->{'host'} = $_[0] if $_[0];
604 0           $self->{'host'} =~ s{^(.*?)://}{};
605 0           my $p = lc $1;
606 0 0         $self->module_load('adcs') if $p eq 'adcs';
607             #$self->protocol_init($p) if $p =~ /^adc/;
608 0           $self->{'host'} =~ s{/.*}{}g;
609 0 0         ( $self->{'host'}, $self->{'port'} ) = ( $1, $2 ) if $self->{'host'} =~ m{^\[(\S+)\]:(\d+)}; # [::1]:411
610 0 0         ( $self->{'host'}, $self->{'port'} ) = ( $1, $2 ) if $self->{'host'} =~ s{^([^:]+):(\d+)$}{}; # 1.2.3.4:411
611             }
612             #$self->log('dev', 'host, port =', $self->{'host'}, $self->{'port'} );
613             #$self->log( 'H:', ((),$self->{'host'} =~ /(:)/g)>1 );
614             #$self->module_load('ipv6') if @{ [ $self->{'host'} =~ /(:)/g ] } > 1;
615             #$self->{'port'} = $_[1] if $_[1];
616             #print "Hhohohhhh" ,$self->{'protocol'},$self->{'host'};
617 0           return 0
618             if ( $self->{'socket'} and $self->{'socket'}->connected() )
619 0 0 0       or grep { $self->{'status'} eq $_ } qw(destroy); #connected
      0        
620 0 0         $self->log(
621             'info',
622             "connecting to $self->{'protocol'}://[$self->{'host'}]:$self->{'port'} via $self->{'Proto'} class $self->{'socket_class'}",
623 0           %{ $self->{'socket_options'} || {} }
624             );
625             #$self->{'status'} = 'connecting';
626 0           $self->{'status'} = 'connecting_tcp';
627 0           $self->{'outgoing'} = 1;
628             #$self->{'port'} = $1 if $self->{'host'} =~ s/:(\d+)//;
629 0           delete $self->{'recv_buf'};
630             #$self->log('dev', 'conn strt', $self->{'Timeout'}, $self->{'Proto'}, Socket::SOCK_STREAM);
631 0           eval {
632 0           $self->{'socket'} ||= $self->{'socket_class'}->new(
633             'PeerAddr' => $self->{'host'},
634             ( $self->{'port'} ? ( 'PeerPort' => $self->{'port'} ) : () ),
635             ( $self->{'Proto'} ? ( 'Proto' => $self->{'Proto'} ) : () ),
636             #( $self->{'Proto'} eq 'sctp' ? ( 'Type' => Socket::SOCK_STREAM ) : () ),
637             #'Timeout' => $self->{'Timeout'},
638             #(
639             #$self->{'nonblocking'} ? (
640             'Blocking' => 0,
641             #'MultiHomed' => 1, #del
642             #) : ()
643             #),
644 0           %{ $self->{'socket_options'} },
645 0 0 0       %{ $self->{'socket_options_connect'} },
    0          
646             );
647             };
648             #$self->log('dev', 'connect end');
649 0 0         $self->log(
650             'err',
651             "connect socket error: $@,",
652             Encode::decode( $self->{charset_fs}, $!, Encode::FB_WARN ),
653             "[$self->{'socket'}]"
654             ),
655             return 1
656             if !$self->{'socket'};
657             #$self->log( 'dev', 'timeout to', $self->{'Timeout'});
658 0 0         $self->{'socket'}->timeout( $self->{'Timeout'} ) if $self->{'Timeout'}; #timeout must be after new, ifyou want nonblocking
659             #$self->log( 'dev', 'ssltry'), IO::Socket::SSL->start_SSL($self->{'socket'}) if $self->{'protocol'} eq 'adcs';
660             #$self->log( 'err', "connect socket error: $@, $! [$self->{'socket'}]" ), return 1 if !$self->{'socket'};
661             #$self->{'socket'}->binmode(":encoding($self->{charset_protocol})");
662             #$self->{charset_protocol} = 'utf8';
663             #$self->log( 'dev', "set encoding of socket to [$self->{charset_protocol}]");
664             # binmode($self->{'socket'}, ":encoding($self->{charset_protocol})");
665             # binmode($self->{'socket'}, ":raw:encoding($self->{charset_protocol})");
666             # binmode($self->{'socket'}, ":encoding($self->{charset_protocol}):bytes");
667             # binmode($self->{'socket'}, ":$self->{charset_protocol}");
668             #eval {$self->{'socket'}->fcntl( Fcntl::O_ASYNC,1);}; $self->log('warn', "cant Fcntl::O_ASYNC : $@") if $@;
669             #eval {$self->{'socket'}->fcntl( Fcntl::O_NONBLOCK,1);}; $self->log('warn', "cant Fcntl::O_NONBLOCK : $@") if $@;
670 0           $self->select_add();
671 0           $self->{time_start} = time;
672             #$self->log($self, 'connected2 inited',"MT:$self->{'message_type'}", ' with');
673             #$self->log( 'dev', "connect_aft after", );
674             #!!$self->select();
675             #$self->log( 'dev', "connect after", );
676 0           return 0;
677             }
678              
679             sub connected { #$self->{'connected'} ||= sub {
680 0     0 0   my $self = shift;
681 0           $self->get_my_addr();
682             #$self->log( 'info', 'broken socket, cant get my ip'),
683             #$self->destroy(),
684 0 0         return unless $self->{'myip'};
685 0           $self->{'status'} = 'connecting';
686             #$self->log( 'dev', "connected0", "[$self->{'socket'}] c=", $self->{'socket'}->connected(), 'p=', $self->{'socket'}->protocol() );
687             #$self->log( 'dev', 'timeout to', $self->{'Timeout_connected'});
688 0 0         $self->{'socket'}->timeout( $self->{'Timeout_connected'} ) if $self->{'Timeout_connected'};
689 0           $self->get_peer_addr();
690             #$self->get_my_addr();
691             #!$self->{'hostip'} ||= $self->{'host'};
692             #my $localmask ||= join '|', @{ $self->{'local_mask_rfc'} || [] }, @{ $self->{'local_mask'} || [] };
693 0 0         my $localmask ||= join '|', map { ref $_ eq 'ARRAY' ? @$_ : $_ }
  0            
694 0   0       grep { $_ } $self->{'local_mask_rfc'},
695             $self->{'local_mask'};
696             my $is_local_ip = sub ($) {
697             #$self->log( 'info', "test ip [$_[0]] in [$localmask] ");
698 0     0     return $_[0] =~ /^(?:$localmask)\./;
699 0           };
700 0 0 0       $self->log( 'info', "my internal ip detected, using passive mode", $self->{'myip'}, $self->{'hostip'}, $localmask ),
      0        
701             $self->{'M'} = 'P'
702             if !$self->{'M'}
703             and $is_local_ip->( $self->{'myip'} )
704             and !$is_local_ip->( $self->{'hostip'} );
705 0   0       $self->{'M'} ||= 'A';
706             #$self->log( 'info', "mode set [$self->{'M'}] ");
707 0           $self->log( 'info', "connect to $self->{'host'}($self->{'hostip'}):$self->{'port'} [me=$self->{'myip'}] ok ", );
708             #$self->log( $self, 'connected1 inited', "MT:$self->{'message_type'}", ' with' );
709             #$self->log( 'dev', 'ssltry'),IO::Socket::SSL->start_SSL($self->{'socket'}) if $self->{'protocol'} eq 'adcs';
710 0           $self->connect_aft();
711             }
712              
713             sub reconnect { #$self->{'reconnect'} ||= sub {
714 0     0 0   my $self = shift;
715             #$self->log( 'dev', 'reconnect');
716 0           $self->disconnect();
717 0           $self->{'status'} = 'reconnecting';
718             #!sleep $self->{'reconnect_sleep'};
719             #!$self->connect();
720             }
721              
722             sub listen { #$self->{'listen'} ||= sub {
723 0     0 0   my $self = shift;
724 0 0         $self->log( 'err', 'listen off', "[$self->{'Listen'}] [$self->{'M'}] [$self->{'allow_passive_ConnectToMe'}]" ), return
725             if !$self->{'Listen'};
726             #or ( $self->{'M'} eq 'P' and !$self->{'allow_passive_ConnectToMe'} ); #RENAME
727 0           $self->{'listener'} = 1;
728 0           $self->myport_generate();
729             #$self->log( 'dev', 'listen', "p=$self->{'myport'}; proto=$self->{'Proto'} cl=$self->{'socket_class'}",'sockopts', Dumper $self->{'socket_options'}, $self->{'socket_options_listen'} );
730 0           for ( 1 .. $self->{'myport_tries'} ) {
731 0           local @_ = (
732             'LocalPort' => $self->{'myport'},
733             #'Proto' => $self->{'Proto'} || 'tcp',
734             ( $self->{'Proto'} ? ( 'Proto' => $self->{'Proto'} ) : () ),
735             (
736             $self->{'Proto'} ne 'udp'
737             ? ( 'Listen' => $self->{'Listen'} )
738             : ()
739             ),
740             #( $self->{'Proto'} eq 'sctp' ? ( 'Type' => Socket::SOCK_STREAM ) : () ),
741             #( $self->{'nonblocking'} ? ( 'Blocking' => 0 ) : () ),
742             Blocking => 0,
743             #ReuseAddr => 1,
744 0           %{ $self->{'socket_options'} },
745 0 0         %{ $self->{'socket_options_listen'} },
    0          
746             );
747             #$self->log( 'dev', 'listen', $self->{'socket_class'}, @_);
748 0   0       eval { $self->{'socket'} ||= $self->{'socket_class'}->new(@_); };
  0            
749 0 0         $self->select_add(), last if $self->{'socket'};
750 0 0         $self->log( 'err', "listen [$_/$self->{'myport_tries'}] ($self->{'Listen'}) $self->{'myport'} socket error: $@" ),
751             $self->myport_generate(1),
752             unless $self->{'socket'};
753             }
754 0 0         $self->log( 'err', 'cant listen' ), return unless $self->{'socket'};
755 0           eval { $self->{'listener'} = $self->{'socket'}->sockhost; };
  0            
756 0           $self->log( 'info', "listening", $self->{'listener'}, "$self->{'myport'} $self->{'Proto'} with $self->{'socket_class'}" );
757 0 0         $self->{'accept'} = 1 if $self->{'Proto'} ne 'udp';
758 0           $self->{'status'} = 'listening';
759             #$self->select();
760             }
761              
762             sub disconnect { #$self->{'disconnect'} ||= sub {
763 0     0 0   my $self = shift;
764             #$self->log('dev', 'in disconnect', $self->{'status'}, caller);
765             #$self->log( 'dev', "[$self->{'number'}] status=",$self->{'status'}, $self->{'destroying'});
766 0           $self->handler('disconnect_bef');
767 0           $self->{'status'} = 'disconnected';
768 0 0         if ( $self->{'socket'} ) {
769             #$self->log( 'dev', "[$self->{'number'}] Closing socket",
770 0 0         $self->{'select'}->remove( $self->{'socket'} ) if $self->{'select'};
771 0 0         $self->{'select_send'}->remove( $self->{'socket'} ) if $self->{'select_send'};
772 0           delete $self->{'sockets'}{ $self->{'socket'} };
773             #$self->{'socket'}->shutdown(2);
774 0           $self->{'socket'}->close();
775 0           delete $self->{'socket'};
776             }
777             #delete $self->{'select'};
778             #$self->log('dev',"delclient($self->{'clients'}{$_}->{'number'})[$_][$self->{'clients'}{$_}]\n") for grep {$_} keys %{ $self->{'clients'} };
779             #$self->log('dev', 'run file_close');
780 0           $self->file_close();
781 0 0         if ( $self->{'disconnect_recursive'} ) {
782 0           for my $client (
  0            
783             grep {
784             #$self->{'clients'}{$_} and
785             !$self->{'clients'}{$_}{'auto_listen'}
786             }
787             #keys %{ $self->{'clients'} }
788             $self->clients_my()
789             )
790             {
791             #next if $self->{'clients'}{$client} eq $self;
792             #$self->log( 'dev', "destroy cli", $self->{'clients'}{$_}, ref $self->{'clients'}{$_} ),
793             #$self->{'clients'}{$client}->destroy()
794 0 0         $self->{'clients'}{$client}->disconnect() if ref $self->{'clients'}{$client};
795             #and $self->{'clients'}{$client}{'destroy'};
796 0           $self->{$_} += $self->{'clients'}{$client}{$_} for qw(bytes_recv bytes_send);
797             #%{$self->{'clients'}{$client}} = ();
798 0           delete $self->{'clients'}{$client};
799             }
800             }
801 0           $self->handler('disconnect_aft');
802 0           delete $self->{$_} for qw(NickList IpList PortList PortList_udp peers peers_cid peers_sid);
803             #$self->log( 'info', "disconnected", __FILE__, __LINE__ );
804             #$self->log('dev', caller($_)) for 0..5;
805             }
806             #$self->{'destroy'} ||= sub {
807             sub destroy {
808 0     0 0   my $self = shift;
809             #$self->log('dev', 'in destroy');
810 0           $self->disconnect(); # if ref $self and !$self->{'destroying'}++;
811             #!? delete $self->{$_} for keys %$self;
812 0           $self->info();
813 0           $self->{'status'} = 'destroy';
814 0 0         delete $self->{$_} for grep { ref $self->{$_} and !ref $self->{$_} eq 'CODE' } keys %$self;
  0            
815             #$self = {};
816             #!?%$self = ();
817             }
818              
819             sub recv { # $self->{'recv'} ||= sub {
820 0     0 0   my $self = shift;
821 0           my $client = shift;
822             #my $socket = shift;
823             #$self->log( 'dev', 'recv', $client, $self->{'socket'}, $self->{'accept'});
824 0 0         if (
825             $self->{'accept'}
826             #and $client eq $self->{'socket'}
827             )
828             {
829 0           local $_ = $self->{'socket'}->accept();
830 0 0         if ($_) {
831             #$self->log( 'traceDEV', 'DC::recv', 'accept', $self->{'incomingclass'} );
832 0 0         $self->log( 'err', 'cant accept, no incomingclass' ), return,
833             unless $self->{'incomingclass'};
834             #my $host = $_->peerhost;
835 0           my ($host) = socket_addr $_;
836             #; # || $self->{parent}{'allow'};
837             #$self->log( 'info', "incoming [$host] (".ref($_).")to $self->{'incomingclass'}", ($self->{'allow'} ? "allow=$self->{'allow'}" : ()) );
838 0 0         $self->log(
839             'info',
840             "incoming [$host] (" . ref($_) . ") to $self->{'incomingclass'}",
841             ( $self->{'allow'} ? "allow=$self->{'allow'}" : () )
842             );
843 0 0         if ( my $allow = $self->{'allow'} ) {
844             #my ( undef, $host ) = socket_addr $_;
845 0 0         $self->log( 'warn', "disallowed connect from $host" ), return
846             unless $host eq $allow;
847             }
848             #$self->log( 'dev', "incp[$self->{'protocol'}]");
849 0 0         $_ = $self->{'incomingclass'}->new(
850             #%$self, clear(),
851             'socket' => $_,
852             'LocalPort' => $self->{'myport'},
853             'incoming' => 1,
854             #'want' => \%{ $self->{'want'} }, 'NickList' => \%{ $self->{'NickList'} }, 'IpList' => \%{ $self->{'IpList'} }, 'PortList' => \%{ $self->{'PortList'} },
855             #'want' => $self->{'want'},
856             #'NickList' => $self->{'NickList'},
857             #'IpList' => $self->{'IpList'},
858             #'PortList' => $self->{'PortList'},
859             #'auto_listen' => 0, 'auto_connect' => 0,
860             'parent' => $self,
861             #'share_tth' => $self->{'share_tth'},
862             'status' => 'connected',
863             #$self->incomingopt(),
864 0           %{ $self->{'incomingopt'} || {} },
865             );
866 0 0 0       my $name = ( $_->{hostip} || $_->{host} ) . ( $_->{port} ? ':' : () ) . $_->{port};
867 0   0       $self->{'clients'}{$name} ||= $_;
868 0           $self->{'clients'}{$name}->select_add();
869             #$self->log( 'dev', 'child created', $_, $self->{'clients'}{$_});
870             #++$ret;
871             } else {
872 0           $self->log( 'err', "Accepting fail! ($_) [$self->{'Proto'}]", $!, $@ );
873             }
874             #next;
875 0           return;
876             }
877 0 0         $self->log( 'dev', "SOCKERR", $client, $self->{'socket'}, $self->{'select'} )
878             if $client ne $self->{'socket'};
879 0           $self->{'databuf'} = '';
880             #my $r;
881 0           my $recv = $self->{'recv'};
882 0 0 0       if ( (!defined( $self->{'recv_addr'} = $client->$recv( $self->{'databuf'}, POSIX::BUFSIZ, $self->{'recv_flags'} ) )
      0        
883             or !length( $self->{'databuf'} )) and $recv ~~ 'recv' )
884             {
885             #TODO not here
886 0 0 0       if (
    0          
887             $self->active()
888             and !$self->{'incoming'}
889             #and $self->{'reconnect_tries'}++ < $self->{'reconnects'}
890             )
891             {
892 0           $self->log( 'dcdbg',
893             "recv err, reconnect [$self->{'reconnect_tries'}/$self->{'reconnects'}]. d=[$self->{'databuf'}] i=[$self->{'incoming'}]"
894             );
895             #$self->log( 'dcdbg', "recv err, reconnect," );
896 0           $self->reconnect();
897             } elsif ( $self->{'status'} ne 'listening' ) {
898 0           $self->log( 'dcdbg', "recv err, destroy," );
899 0           $self->destroy();
900             }
901             } else {
902             #++$readed;
903             #++$ret;
904 0           $self->{bytes_recv} += length $self->{'databuf'};
905 0           $self->{activity} = time;
906             #$self->log( 'dcdmp', "[$self->{'number'}]", "raw recv ", length( $self->{'databuf'} ), $self->{'databuf'} );
907             }
908             #$self->log( 'dcdmp', "0rawrawrcv [fh:$self->{'filehandle'}]:", $self->{'databuf'} );
909 0 0         if ( $self->{'filehandle'} ) {
    0          
910 0           $self->file_write( \$self->{'databuf'} );
911             } elsif ( length $self->{'databuf'} ) {
912             #$self->log( 'dcdmp', "rawrawrcv:", $self->{'databuf'} );
913 0           $self->{'recv_buf'} .= $self->{'databuf'};
914             #$self->log( 'dcdmp', "rawrawbuf:", $self->{'recv_buf'} );
915 0 0 0       local $self->{'cmd_aft'} = "\x0A"
916             if !$self->{'adc'}
917             and $self->{'recv_buf'} =~ /^[BCDEFHITU][A-Z]{,5} /;
918             #$self->log( 'dcdbg', "[$self->{'number'}]", "raw to parse [$self->{'buf'}] sep[$self->{'cmd_aft'}]" ) unless $self->{'filehandle'};
919 0 0 0       $self->{'recv_buf'} .= $self->{'cmd_aft'}
920             if $self->{'Proto'} eq 'udp' and $self->{'status'} eq 'listening';
921 0           while ( $self->{'recv_buf'} =~ s/^(.*?)\Q$self->{'cmd_aft'}//s ) {
922 0           local $_ = $1;
923             #$self->log('dcdmp', 'DC::recv', "parse [$_]($self->{'cmd_aft'})");
924 0 0         last if $self->{'status'} eq 'destroy';
925             #$self->log( 'dcdbg',"[$self->{'number'}] dev cycle ",length $_," [$_]", );
926 0 0 0       last unless length $_ and length $self->{'cmd_aft'};
927 0 0         next unless length;
928 0 0         $self->get_peer_addr_recv() if $self->{'broadcast'};
929 0           $_ = Encode::decode $self->{charset_protocol}, $_, Encode::FB_WARN;
930             # $Encode::encode $self->{charset_console},
931 0           $self->parser($_);
932             #$self->log( 'dcdbg', "[$self->{'number'}]", "left to parse [$self->{'buf'}] sep[$self->{'cmd_aft'}] now was [$_]" );
933 0 0         last if ( $self->{'filehandle'} );
934             }
935 0 0 0       $self->file_write( \$self->{'recv_buf'} ), $self->{'recv_buf'} = ''
936             if length( $self->{'recv_buf'} )
937             and $self->{'filehandle'};
938             }
939             }
940              
941             sub select { #$self->{'select'} ||= sub {
942 0     0 0   my $self = shift;
943             #$self->{'recv_runned'}{ $self->{'number'} } = 1;
944 0   0       my $sleep = shift || $self->{'select_timeout'};
945 0           my $nosend = shift;
946 0           my $ret = 0;
947             #$self->connect_check();
948             #$self->log( 'dev', 'cant recv, ret' ),
949             #return unless $self->{'socket'} and ( $self->{'status'} eq 'listening' or $self->{'socket'}->connected );
950             #$self->{'select'} = IO::Select->new( $self->{'socket'} ) if !$self->{'select'} and $self->{'socket'};
951             #my ( $readed, $reads );
952             #$self->{'databuf'} = '';
953             #$self->log( 'dev', 'select', 'bef', $sleep, $nosend , ) if $nosend;
954 0 0         my ( $recv, $send, $exeption ) =
955             IO::Select->select( $self->{'select'}, ( $nosend ? undef : $self->{'select_send'} ), $self->{'select'}, $sleep );
956             #$self->log( 'traceD', 'DC::select', 'aft' , Dumper ($recv, $send, $exeption));
957             #schedule(10, sub { $self->log( 'dev', 'DC::select', 'aft' , Dumper ($recv, $send, $exeption), 'from', $self->{'select'}->handles() , 'and ', $self->{'select_send'}->handles()); });
958             #$self->{'select'}->remove(@$exeption) if $exeption;
959             #for ( keys %{ $self->{sockets} } ) {
960             #$self->log( 'tracez', 'C:', $self->{sockets}{$_}{socket}, $self->{sockets}{$_}{socket}->connected());
961             #}
962 0           for (@$exeption) {
963             #$self->log( 'dcdbg', 'exeption', $_, $self->{sockets}{$_}{number} ),
964             #$self->{'select'}->remove($_);
965             #can_run( $self->{sockets}{$_}, 'destroy' );
966 0           can_run( $self->{sockets}{$_}, 'reconnect' );
967             #$self->{sockets}{$_}->destroy() if ref $self->{sockets}{$_};
968 0           delete $self->{sockets}{$_};
969 0           ++$ret,;
970             }
971             #for (@$recv, @$send) {
972              
973             =hm
974             for ( keys %{ $self->{sockets} } ) {
975             #$self->log( 'dev', 'connected chk' , $self->{sockets}{$_}{socket}, $self->{sockets}{$_}{socket}->connected());
976             #$self->log( 'dev', 'connected call' ),
977             $self->{sockets}{$_}->connected(), ++$ret,
978             if $self->{sockets}{$_}{status} eq 'connecting_tcp' and $self->{sockets}{$_}{socket}->connected();
979             }
980             =cut
981 0           for (@$send) {
982 0 0 0       next unless $self->{sockets}{$_} and $self->{sockets}{$_}{socket};
983             #$self->log('connect test', $self->{sockets}{$_}{status}, $self->{sockets}{$_}{socket}->connected(), caller);
984             #$self->{'select'}->remove($_),
985 0 0         $self->{sockets}{$_}->connected(),
986             #can_run($self->{sockets}{$_}, 'connected'),
987             ++$ret,
988             if $self->{sockets}{$_}{status} eq 'connecting_tcp';
989             # and $self->{sockets}{$_}{socket}->connected();
990             #$self->log( 'err', 'no object for send handle',$_, ) , next , unless $self->{sockets}{$_};
991             #++$self->{sockets}{$_}{send_can};
992             #$self->log( 'dev', 'can_send', $_, $self->{sockets}{$_}{number}, $self->{sockets}{$_}{send_can} );
993             #$ret += $self->{sockets}{$_}->send_can();
994 0           $ret += can_run( $self->{sockets}{$_}, 'send_can' );
995 0 0         if ( $self->{sockets}{$_}{'filehandle_send'} ) {
996 0           $ret += $self->{sockets}{$_}->file_send_part();
997             }
998             #$self->{sockets}{$_}->send();
999             }
1000 0           for (@$recv) {
1001             #next unless $self->{sockets}{$_};
1002 0 0 0       $self->log( 'err', 'no object for recv handle', $_, Dumper $self->{sockets}{$_} ),
1003             can_run( $self->{'select'}, 'remove', $_ ), next,
1004             #if !$self->{sockets}{$_} or !ref $self->{sockets}{$_};
1005             if !ref $self->{sockets}{$_} or ref $self->{sockets}{$_} eq 'HASH';
1006             #$self->log( 'dev',ref $self->{sockets}{$_});
1007 0           $ret += $self->{sockets}{$_}->recv($_);
1008             }
1009             #if ( $self->{'filehandle_send'} ) { $self->file_send_part(); }
1010             #$self->{'recv_runned'}{ $self->{'number'} } = undef;
1011 0           return $ret;
1012             }
1013              
1014             =no
1015             sub wait { #$self->{'wait'} ||= sub {
1016             my $self = shift;
1017             my ( $waits, $wait_once ) = @_;
1018             $waits ||= $self->{'waits'};
1019             #$wait_once ||= $self->{'wait_once'};
1020             local $_;
1021             my $ret;
1022             #$self->log( 'dev', "start wait", $waits, caller, '::::', caller 1, );
1023             while ( --$waits > 0 and !$ret ) {
1024             #$ret += $self->select($wait_once);
1025             last unless $self->active();
1026             $ret += $self->select(undef, 1);
1027             #$self->log( 'dev', "wait", $waits, $ret);
1028             #sleep 0.1 if !$ret;
1029             }
1030             #$ret += $self->work($wait_once) while --$waits > 0 and !$ret;
1031             return $ret;
1032             }
1033             =cut
1034              
1035             sub finished { #$self->{'finished'} ||= sub {
1036 0     0 0   my $self = shift;
1037 0 0 0       $self->log( 'dcdev', 'not finished file:', "$self->{'filebytes'} / $self->{'filetotal'}", $self->{'peernick'} ), return 0
      0        
1038             if ($self->{'filebytes'}
1039             and $self->{'filetotal'}
1040             and $self->{'filebytes'} < $self->{'filetotal'} - 1 );
1041 0           local @_;
1042 0           $self->log( 'dcdev', 'not finished clients:', @_ ), return 0
1043             if @_ =
1044 0 0         grep { !$self->{'clients'}{$_}->finished() } $self->clients_my(); #keys %{ $self->{'clients'} };
1045 0           return 1;
1046             }
1047              
1048             sub wait_connect { #$self->{'wait_connect'} ||= sub {
1049 0     0 0   my $self = shift;
1050             #$self->log( 'dev', "wait_connect", $self->{'wait_connect_tries'});
1051 0   0       for ( 0 .. ( $_[0] || $self->{'wait_connect_tries'} ) ) {
1052             #$self->log('dev', 'ws', $self->{'status'}, $_, ( $_[0] , $self->{'wait_connect_tries'}));
1053 0 0         last if grep { $self->{'status'} eq $_ } qw(connected transfer disconnecting disconnected destroy), '';
  0            
1054             #$self->wait(1);
1055 0           $self->work(1);
1056             }
1057 0           return $self->{'status'};
1058             }
1059              
1060             sub wait_finish { #$self->{'wait_finish'} ||= sub {
1061 0     0 0   my $self = shift;
1062 0   0       my $time = time() + ( shift || $self->{'wait_finish'} );
1063 0           while ( $time > time() ) {
1064             #for ( 0 .. $self->{'wait_finish_tries'} ) {
1065 0 0         last if $self->finished();
1066             #$self->wait( undef, $self->{'wait_finish_by'} );
1067             #$self->log( 'dev', 'wait_finish', $_);
1068             #$self->wait();
1069 0           $self->work(1);
1070             #$self->work( undef, $self->{'wait_finish_by'} );
1071             }
1072 0           local @_;
1073 0           $self->info(),
1074             $self->log(
1075             'info',
1076             'finished, but clients still active:',
1077 0 0         map { "[$self->{'clients'}{$_}{'number'}]$_;st=$self->{'clients'}{$_}{'status'}" } @_
1078             ) if @_ = $self->clients_my(); #keys %{ $self->{'clients'} };
1079             }
1080              
1081             sub wait_clients { #$self->{'wait_clients'} ||= sub {
1082 0     0 0   my $self = shift;
1083             #for my $n ( 0 .. $self->{'wait_clients_tries'} ) {
1084 0   0       my $time = time() + ( shift || $self->{'wait_clients'} );
1085 0           while ( $time > time() ) {
1086 0           local @_;
1087             last
1088 0 0 0       if !$self->{'clients_max'}
1089             or $self->{'clients_max'} > ( @_ = $self->clients_my() ); #keys %{ $self->{'clients'} };
1090 0 0         $self->info() unless $_;
1091 0           $self->log(
1092             'info', "wait clients",
1093             scalar( @_ = $self->clients_my() ) . "/$self->{'clients_max'} ",
1094             int( $time - time() )
1095             );
1096             #$self->wait( undef, $self->{'wait_clients_by'} );
1097 0           $self->work(10);
1098             }
1099             }
1100             #sub wait_sleep { #$self->{'wait_sleep'} ||= sub {
1101             sub wait { #$self->{'wait_sleep'} ||= sub {
1102 0     0 0   my $self = shift;
1103 0           my $ret;
1104 0   0       my $time = time() + ( shift || 1 );
1105             #$self->log( 'dev', "wait_sleep", $time );
1106 0           while ( $time > time() ) {
1107 0 0         last unless $self->active();
1108             #$ret += $self->wait(@_);
1109 0   0       $ret += $self->select() || $self->select( 1, 1 );
1110             }
1111 0           return $ret;
1112             #$self->log( 'dev', "wait_sleep",$starttime , $how , time(), "==", $starttime + $how),
1113             #$self->work(@_) while $starttime + $how > time();
1114             }
1115              
1116             sub work { #$self->{'work'} ||= sub {
1117 0     0 0   my $self = shift;
1118             #$self->log( 'dev', 'work', ref $self->{parent}, $self->{parent});
1119 0 0         return $self->{parent}->work(@_) if is_object($self->{parent});
1120 0           my @params = @_;
1121             #$self->periodic();
1122             #$self->log( 'dev', 'work', @params);
1123 0           code_run $self->{'auto_work'}, @params; # if ref $self->{'auto_work'} eq 'CODE';
1124             schedule(
1125             1,
1126             our $___work_every ||= sub {
1127 0     0     my $self = shift;
1128 0           $self->connect_check();
1129 0 0         code_run( $_, $self ) for values %{ $self->{periodic} || {} };
  0            
1130             #print ("P:$_\n"),
1131             #$self->{periodic}{$_}->() for grep {ref$self->{periodic}{$_} eq 'CODE'}keys %{$self->{periodic} || {}};
1132             #$self->log('dev', 'work for', keys %{$self->{'clients'}});
1133 0           for (
1134 0           keys %{ $self->{'clients'} }
1135             #$self->clients_my()
1136             )
1137             {
1138 0 0 0       if (
      0        
      0        
      0        
      0        
      0        
1139             !$self->{'clients'}{$_}{'socket'}
1140             or !length $self->{'clients'}{$_}{'status'}
1141             or $self->{'clients'}{$_}{'status'} eq 'destroy'
1142             or ( $self->{'clients'}{$_}{'status'} ne 'listening'
1143             and $self->{'clients'}{$_}{'status'} ne 'working'
1144             and $self->{'clients'}{$_}{inactive_timeout}
1145             and time - $self->{'clients'}{$_}{activity} > $self->{'clients'}{$_}{inactive_timeout} )
1146             )
1147             {
1148 0           $self->log(
1149             'dev',
1150             "del client[$self->{'clients'}{$_}{'number'}][$_] socket=[$self->{'clients'}{$_}{'socket'}] status=[$self->{'clients'}{$_}{'status'}] listener=[$self->{'listener'}] last active=",
1151             int( time - $self->{'clients'}{$_}{activity} )
1152             );
1153             #(
1154             #!ref $self->{'clients'}{$_}{destroy} ? () :
1155             # $self->{'clients'}{$_}->destroy()
1156             #);
1157             #%{$self->{'clients'}{$_}} = (),
1158 0           delete $self->{'clients'}{$_};
1159             #$self->log('dev', "now clients", map { "$_" }sort keys %{ $self->{'clients'} });
1160 0           next;
1161             }
1162             #$ret += $self->{'clients'}{$_}->recv();
1163             #$self->log('dev', 'work', $self->{'clients'}{$_}{'number'}, $self->{'clients'}{$_}, $self);
1164             #next if $self->{'clients'}{$_} eq $self;
1165             #$self->{'clients'}{$_}->work();
1166             }
1167 0           for ( keys %{ $self->{'sockets'} } ) {
  0            
1168 0 0 0       next if $self->{'sockets'}{$_} and %{ $self->{'sockets'}{$_} };
  0            
1169 0           delete $self->{'sockets'}{$_};
1170             }
1171             #$self->log('dev', 'parent:', $self->{parent}, $self->{parent}{parent}, is_object($self->{parent}));
1172 0 0 0       if ( !$self->{parent} or !$self->{parent}{parent} ) { # first parent always autocreated on init
1173 0           code_run( $self->{$_}, $self ) for qw(worker); #auto_work
1174             }
1175             #$self->log('dev', 'work parent', scalar keys %{ $self->{parent}} , scalar keys %{ $self->{parent}{parent}} );
1176 0           for (
1177 0           grep { $self->{'clients'}{$_} ne $self }
  0            
1178             keys %{ $self->{'clients'} }
1179             #$self->clients_my()
1180             )
1181             {
1182             #$self->log('dev', 'starting work on', $self->{'clients'}{$_}{'number'}, $self,$self->{'clients'}{$_});
1183 0 0         $self->{'clients'}{$_}->work() if $self->{'clients'}{$_};
1184             }
1185             },
1186 0   0       $self
1187             );
1188             schedule(
1189             10,
1190             our $___work_downloader ||= sub {
1191 0     0     my $self = shift;
1192 0 0         return unless $self->active();
1193 0 0         return if $self->{'status'} eq 'listening';
1194 0           my $time = time;
1195 0           for my $tth ( keys %{ $self->{'downloading'} } ) {
  0            
1196 0 0 0       if ( $self->{'downloading'}{$tth}{connect_start}
1197             and $self->{'downloading'}{$tth}{connect_start} < $time - 60 )
1198             {
1199             #$self->log('dev', 'want', $tth,'no connection, return to want queue');
1200 0           $self->{'want_download'}{$tth} = $self->{'downloading'}{$tth};
1201 0           delete $self->{'downloading'}{$tth};
1202             }
1203             }
1204 0           for my $tth ( keys %{ $self->{'want_download'} } ) {
  0            
1205 0           delete $self->{'want_download'}{$tth}{connect_start};
1206             }
1207 0 0 0       if ( $self->{'queue_download'}
  0            
1208             and @{ $self->{'queue_download'} } )
1209             {
1210 0           my $file = shift @{ $self->{'queue_download'} };
  0            
1211 0           $self->search($file);
1212             }
1213             #if (!)
1214             #=todo
1215             #$self->log('dev', 'work want:', Dumper $self->{'want_download'});
1216 0           for my $tth (
  0            
1217 0           grep { keys %{ $self->{'want_download'}{$_} } }
  0            
1218             keys %{ $self->{'want_download'} }
1219             )
1220             {
1221 0   0       my $wdls = $self->{'want_download'}{$tth} || {};
1222 0 0 0       local @_ = (
      0        
1223             #grep { $wdls->{$_}{slotsfree} or $wdls->{$_}{SL} }
1224             sort {
1225 0           $wdls->{$a}{tries} <=> $wdls->{$b}{tries}
1226             or ( $wdls->{$b}{slotsfree} || $wdls->{$b}{SL} ) <=> ( $wdls->{$a}{slotsfree} || $wdls->{$a}{SL} )
1227             } keys %$wdls
1228             );
1229             #$self->log('dev', 'from can:', @_ );
1230 0 0         if ( my ($fromk) = $_[0] ) {
1231 0           my $from = $wdls->{$fromk};
1232 0           my ($filename);
1233 0           for my $file ( keys %{ $self->{'want_download_filename'}{$tth} } ) {
  0            
1234 0           my $partial = $file;
1235 0           $partial = $self->{'partial_prefix'} . $partial . $self->{'partial_ext'};
1236 0 0         $partial = Encode::encode $self->{charset_fs}, $file, Encode::FB_WARN
1237             if $self->{charset_fs};
1238 0 0         if ( -s $partial ) {
1239 0           $self->log( 'dev', 'already downloading: ', $file, -s $partial );
1240 0           $filename = $file;
1241 0           last;
1242             }
1243             }
1244             $filename //= (
1245 0           sort { $self->{'want_download_filename'}{$tth}{$a} <=> $self->{'want_download_filename'}{$tth}{$b} }
  0            
1246 0   0       keys %{ $self->{'want_download_filename'}{$tth} }
1247             )[0];
1248 0   0       $filename //= $from->{FN};
1249 0           $filename =~ s{^.*[/\\]}{}g;
1250             #$self->log( "selected22 [$filename] keys",( grep { $wdls->{$_}{slotsfree} or $wdls->{$_}{SL} } sort {$wdls->{$b}{tries} <=> $wdls->{$a}{tries} }keys %$wdls )," from", Dumper $from,);
1251             #my $dst = $self->{'get_dir'} . $filename;
1252 0   0       my $size = $from->{size} || $from->{SI} || 0;
1253             #my $sizenow = -s $dst || 0;
1254             #$self->log( 'dcdev', "selected23 -e $dst and ( !$size or $sizenow < $size" );
1255             #if ( !-e $dst or ( !$size or $sizenow < $size ) ) {
1256 0           ++$self->{'want_download'}{$tth}{$fromk}{tries};
1257 0   0       $self->get(
1258             $from->{nick} || $from->{CID} || $from->{NI},
1259             #'TTH/' . $tth,
1260             undef, $filename, undef, undef, $size, $tth
1261             );
1262 0           $self->{'downloading'}{$tth} = $self->{'want_download'}{$tth};
1263 0           $self->{'downloading'}{$tth}{connect_start} = $time;
1264 0           delete $self->{'want_download'}{$tth};
1265 0           last;
1266             #}
1267             #$work{'tthfrom'}{$s{tth}}
1268             }
1269             }
1270             #=cut
1271             },
1272 0   0       $self
1273             );
1274             schedule(
1275             [ $self->{dev_auto_dump_first} || 20, $self->{dev_auto_dump_every} || 100 ],
1276             our $dump_sub__ ||= sub {
1277 0     0     my $self = shift;
1278 0           $self->dumper();
1279             },
1280 0 0 0       $self
      0        
      0        
1281             ) if $self->{dev_auto_dump};
1282             #return
1283 0           $self->select( 1 || $self->{'work_sleep'} ); # if @{$self->{send_buffer_raw}|| []}; # maybe send
1284             #$self->log( 'dev', "work -> sleep", @params ),
1285 0 0         return $self->wait(@params) if @params;
1286             return
1287 0   0       $self->select( undef, 1 )
1288             || $self->select( $self->{'work_sleep'} )
1289             || $self->select( undef, 1 ); # unless @{$self->{send_buffer_raw}|| []};
1290             #return $self->select( $self->{'work_sleep'} );
1291             }
1292              
1293             sub dumper { #$self->{'dumper'} ||= sub {
1294 0     0 0   my $self = shift;
1295 0   0       my $file = $_[0] || $self->{dev_auto_dump_file} || $0 . ( $self->{dev_auto_dump_timed} ? '.' . time : () ) . '.dump';
1296 0 0         open my $fh, '>', $file or return;
1297 0           print $fh Dumper $self;
1298 0           close $fh;
1299 0           $self->log( 'dev', "Writed dump", -s $file );
1300             }
1301              
1302             sub parser { #$self->{'parser'} ||= sub {
1303 0     0 0   my $self = shift;
1304 0           for ( local @_ = @_ ) {
1305 0 0         $self->log(
1306             'dcdmp',
1307             "rawrcv["
1308             . (
1309             $self->{'recv_hostip'}
1310             ? $self->{'recv_hostip'} . ':' . $self->{'recv_port'}
1311             : $self->{'host'}
1312             )
1313             . "]:",
1314             $_
1315             );
1316 0           my ( $dst, $cmd, @param );
1317 0 0         if (/^[<*]/) {
1318 0 0         $cmd = ( $self->{'status'} eq 'connected' ? 'chatline' : 'welcome' );
1319             }
1320 0 0         s/^\x00*\$?([\w\-]+)\s*//, $cmd = $1 unless $cmd; # \x00 - ssl bug on recv
1321             #$self->log('dev',"cmd[", Dumper($cmd),"], adc=", $self->{'adc'} );
1322 0 0         if ( $self->{'adc'} ) {
1323 0           $cmd =~ s/^([BCDEFHIU])//, $dst = $1;
1324 0           @param = ( [$dst], split / / );
1325 0 0 0       if ( $dst eq 'B'
    0 0        
      0        
      0        
1326             or $dst eq 'F'
1327             or $dst eq 'U'
1328             or $self->{broadcast} )
1329             {
1330             #$self->log( 'dcdmp', "P0 $dst$cmd p=",(Dumper \@param));
1331             #push @{ $param[0] }, shift@param;
1332 0           push @{ $param[0] }, splice @param, 1, 1;
  0            
1333             #$self->log( 'dcdmp', "P0 $dst$cmd p=",(Dumper \@param));
1334 0 0         if ( $dst eq 'F' ) {
1335             #$self->log( 'dcdmp', 'feature'
1336 0           push @{ $param[0] }, splice @param, 1, 1 while $param[1] =~ /^[+\-]/;
  0            
1337             }
1338             #$self->log( 'dcdmp', "P1 $dst$cmd p=",(Dumper \@param));
1339             } elsif ( $dst eq 'D' or $dst eq 'E' ) {
1340             #push @{ $param[0] }, shift@param, shift@param;
1341 0           push @{ $param[0] }, splice @param, 1, 2;
  0            
1342             }
1343             #elsif ( $dst eq 'I' ) { push @{ $param[0] }, undef }
1344             } else {
1345 0           @param = ($_);
1346             }
1347             #$self->log( 'dcdmp', "P3 $dst$cmd p=",(Dumper \@param));
1348 0 0 0       $cmd = $dst . $cmd
1349             if !exists $self->{'parse'}{$cmd}
1350             and exists $self->{'parse'}{ $dst . $cmd };
1351             #$self->log( 'dcinf', "UNKNOWN PEERCMD:[$cmd]->($_) : please add \$dc->{'parse'}{'$cmd'} = sub { ... };" ),
1352 0 0 0 0     $self->{'parse'}{$cmd} = sub { }, $cmd = ( $self->{'status'} eq 'connected' ? 'chatline' : 'welcome' )
  0 0          
1353             if $self->{'nmdc'} and !exists $self->{'parse'}{$cmd};
1354 0 0 0       if ( $cmd eq 'chatline' or $cmd eq 'welcome' or $cmd eq 'To' ) {
      0        
1355             #$self->log( 'dev', 'RCV pre encode', ($self->{charset_chat} ), @param, Dumper \@param);
1356             #$_ = Encode::decode(($self->{charset_chat} ), $_) for @param;
1357             #! $_ = Encode::encode $self->{charset_internal}, Encode::decode $self->{charset_chat}, $_ for @param;
1358             #$self->log( 'dev', 'RCV postencode', @param, Dumper \@param);
1359             #Encode::encode $self->{charset_console},;
1360             } else {
1361             #$_ = Encode::encode $self->{charset_internal},
1362             #TODO $_ = Encode::decode($self->{charset_protocol}, $_), for @param;
1363             }
1364 0           my ( @ret, $ret );
1365             #$self->log( 'dcinf', "parsing", $cmd, @_ ,'with',$self->{'parse'}{$cmd}, ref $self->{'parse'}{$cmd});
1366 0           my @self;
1367             #@self = $self if $self->{'adc'};
1368 0           @self = $self; #if !$self->{'nmdc'};
1369             #$self->handler( @self, $cmd . '_parse_bef_bef', @param );
1370 0           $self->handler( @self, $cmd . '_parse_bef', @param );
1371 0 0         if ( ref $self->{'parse'}{$cmd} eq 'CODE' ) {
1372 0 0         if ( !exists $self->{'no_print'}{$cmd} ) {
1373 0           local $_ = $_;
1374 0           local @_ =
1375 0           map { "$_:$self->{'skip_print_'.$_}" }
1376 0 0         grep { $self->{ 'skip_print_' . $_ } }
1377 0           keys %{ $self->{'no_print'} || {} };
1378             #$self->log( 'dcdmp', "rcv: $dst$cmd p=[",(Dumper \@param),"] ", ( @_ ? ( ' [', @_, ']' ) : () ) );
1379             #$self->log( 'dcdmp', "rcv: $dst$cmd p=[", (map {ref $_ eq 'ARRAY'?@$_:$_}@param), "] ", ( @_ ? ( ' [', @_, ']' ) : () ) );
1380 0 0         $self->{ 'skip_print_' . $_ } = 0 for keys %{ $self->{'no_print'} || {} };
  0            
1381             } else {
1382 0 0         ++$self->{ 'skip_print_' . $cmd },
1383             if exists $self->{'no_print'}{$cmd};
1384             }
1385             #$self->handler( @self, $cmd . '_parse_bef', @param );
1386 0           @ret = $self->{'parse'}{$cmd}->( @self, @param );
1387 0 0         $ret = scalar @ret > 1 ? \@ret : $ret[0];
1388             #$self->handler( @self, $cmd . '_parse_aft', @param, $ret );
1389 0           ++$self->{'count_parse'}{$cmd};
1390             } else {
1391             #$self->log( 'dcinf', "unknown", $cmd, @_ ,'with',$self->{'parse'}{$cmd}, ref $self->{'parse'}{$cmd}, 'run=', @self, 'unknown', $cmd,@param,);
1392 0           $self->handler( @self, 'unknown', $cmd, @param, );
1393             }
1394             #if ($self->{'parent'}{'hub'}) { }
1395 0           $self->handler( @self, $cmd, @param, $ret );
1396             #$self->handler( @self, $cmd . '_parse_aft_aft', @param, $ret );
1397             }
1398             }
1399              
1400             sub send_can { #$self->{'send'} ||= sub {
1401 0     0 0   my $self = shift;
1402             #$self->log( 'dev', 'send_can');
1403 0           my $size;
1404 0           my $send = $self->{send};
1405 0 0         eval { $size += $self->{'socket'}->$send($_) for @_ ? @_ : @{ $self->{send_buffer_raw} }; } if $self->{'socket'};
  0 0          
  0            
1406 0           $self->{send_buffer_raw} = [];
1407 0           $self->{bytes_send} += $size;
1408 0 0         $self->log( 'err', 'send error', $@ ), $self->reconnect(), return $size if $@;
1409 0           $self->{activity} = time;
1410 0           return $size;
1411             }
1412              
1413             sub send { #$self->{'send'} ||= sub {
1414 0     0 0   my $self = shift;
1415 0 0         return if $self->{'listener'};
1416             # = join( '', @_ );
1417             #$self->{bytes_send} += length $_;
1418             #eval { $_ = $self->{'socket'}->send( join( '', @_ ) ); } if $self->{'socket'};
1419 0   0       push @{ $self->{send_buffer_raw} ||= [] }, @_;
  0            
1420 0           $self->select();
1421              
1422             =no
1423             unless ($self->{send_can}) {
1424             $self->{send_buffer_raw} = \@_;
1425             return 0;
1426             }
1427             if ($self->{send_buffer_raw}) {
1428             unshift @_, @{$self->{send_buffer_raw}};
1429             $self->{send_buffer_raw} = undef;
1430             }
1431             =cut
1432              
1433             #return unless @_;
1434             }
1435              
1436             sub sendcmd { #$self->{'sendcmd'} ||= sub {
1437 0     0 0   my $self = shift;
1438 0 0         return if $self->connect_check();
1439 0 0 0       return if $self->{'listener'} and !$self->{'broadcast'};
1440             #$self->{'log'}->( $self,'sendcmd0', @_);
1441 0 0 0       local @_ = @_, $_[0] .= splice @_, 1, 1
1442             if $self->{'adc'} and length $_[0] == 1;
1443 0           $self->log( 'dcdmp', 'sendcmd', $self->{number}, ':', @_ );
1444 0 0         push @{ $self->{'send_buffer'} }, $self->{'cmd_bef'} . join( $self->{'cmd_sep'}, @_ ) . $self->{'cmd_aft'}
  0            
1445             if @_;
1446 0           ++$self->{'count_sendcmd'}{ $_[0] };
1447 0 0 0       if ( ( $self->{'sendbuf'} and @_ )
  0 0 0        
1448             or !@{ $self->{'send_buffer'} || [] } )
1449             {
1450             } else {
1451 0 0         if ( $self->{'broadcast'} ) {
1452 0           $self->send_udp( $self->{'host'}, $self->{'port'}, join( '', @{ $self->{'send_buffer'} }, ) ),;
  0            
1453             } else {
1454 0 0         $self->log( 'err', "ERROR! no socket to send" ), return
1455             unless $self->{'socket'};
1456 0           $self->send( Encode::encode $self->{charset_protocol}, join( '', @{ $self->{'send_buffer'} }, ), Encode::FB_WARN );
  0            
1457             #local $_;
1458             #eval { $_ = $self->{'socket'}->send( join( '', @{ $self->{'send_buffer'} }, ) ); };
1459             #$self->log( 'err', 'send error', $@ ) if $@;
1460             }
1461             #$self->log( 'dcdmp', "we send [" . join( '', @{ $self->{'send_buffer'} } ) . "]:", $! );
1462 0           $self->{'send_buffer'} = [];
1463 0           $self->{'sendbuf'} = 0;
1464             }
1465             }
1466              
1467             sub sendcmd_all { #$self->{'sendcmd_all'} ||= sub {
1468 0     0 0   my $self = shift;
1469             #%{ $self->{'peers_sid'} }
1470             #eval {
1471 0           $_->sendcmd(@_) #, $self->wait_sleep( $self->{'cmd_recurse_sleep'} )
1472 0           for grep { $_ } values( %{ $self->{'clients'} } ); #, $self;
  0            
1473             }
1474              
1475             sub rcmd { #$self->{'rcmd'} ||= sub {
1476 0     0 0   my $self = shift;
1477 0           eval {
1478 0           eval { $_->cmd(@_) }, $self->wait( $self->{'cmd_recurse_sleep'} )
  0            
1479 0           for grep { $_ } values( %{ $self->{'clients'} } ), $self;
  0            
1480             };
1481             }
1482              
1483             sub get { #$self->{'get'} ||= sub {
1484 0     0 0   my ( $self, $nick, $file, $as, $from, $to, $size, $tth ) = @_; #TODO hash
1485             #$self->log( 'dcdev', 'wantcall', $self, $nick, $file, $as, $from, $to, $size);
1486             #my $size;
1487             #$size = $to unless $from;
1488             #$from, $to
1489 0           my ( $sid, $cid );
1490 0 0         $sid = $nick if $nick =~ /^[A-Z0-9]{4}$/;
1491 0 0         $cid = $nick if $nick =~ /^[A-Z0-9]{39}$/;
1492 0   0       $cid ||= $self->{peers}{$sid}{INF}{ID};
1493 0   0       $sid ||= $self->{peers}{$cid}{SID};
1494 0 0 0       $sid ||= $cid if $self->{broadcast};
1495 0 0 0       $file //= 'TTH/' . $tth if $tth;
1496 0   0       my $full = ( $as || $file );
1497 0 0         $full = $self->{'download_to'} . $full unless $full =~ m{[/\\]};
1498             #$self->log( 'dev', "cid[$cid] sid[$sid] nick[$nick] full[$full] as,file[$as || $file]");
1499 0           my $sizenow = -s $full;
1500 0 0         if ($sizenow) {
1501 0           $self->log( 'info', "file [$_] already exists size=$sizenow must be=$size" );
1502 0           return;
1503             #return if $size and $size == $sizenow;
1504             #$from = $sizenow if $sizenow < $size;
1505             }
1506             #$to ||= $size - $from;
1507             #todo by nick
1508 0           $self->wait_clients();
1509             #$self->{'want'}{ $self->{peers}{$cid}{'INF'}{'ID'} || $nick }{$file} = $as || $file || '';
1510             #$self->log( 'dcdev', "wantid: $self->{peers}{$cid}{'INF'}{'ID'} || $self->{peers}{$sid}{'INF'}{'ID'} || $nick");
1511 0   0       $self->{'want'}{ $self->{peers}{$cid}{'INF'}{'ID'} || $self->{peers}{$sid}{'INF'}{'ID'} || $nick }{$file} = {
      0        
      0        
1512             'filename' => $file,
1513             'fileas' => $as || $file || '',
1514             'file_recv_to' => $to,
1515             'file_recv_from' => $from,
1516             'file_recv_size' => $size,
1517             'file_recv_tth' => $tth,
1518             #'file_recv_full' => $full
1519             };
1520 0   0       my $peer =
1521             $self->{peers}{$cid}
1522             || $self->{peers}{$sid}
1523             || $self->{peers}{$nick}
1524             || {};
1525 0           $self->log( 'dbg', "getting [$nick] $file as $as sid=[$sid]:$self->{'myport'} p=$self->{'protocol_connect'}" ); #, Dumper $peer->{INF});
1526 0 0         if ( $self->{'adc'} ) {
1527             #my $token = $self->make_token($nick);
1528 0           local @_;
1529 0 0 0       if ( $self->{'M'} eq 'A'
      0        
1530             and $self->{'myip'}
1531             and !$self->{'passive_get'} )
1532             {
1533 0           @_ = ( 'CTM', $sid, $self->{'protocol_connect'}, $self->{'myport'}, $self->make_token($nick) );
1534             } else {
1535 0           @_ = ( 'RCM', $sid, $self->{'protocol_connect'}, $self->make_token($nick) );
1536             }
1537 0           $self->cmd( 'D', @_ );
1538             #$self->cmd( $dst, 'CTM', $peerid, $_[0], $self->{'myport'}, $_[1], )
1539             } else {
1540 0 0 0       $self->cmd( ( ( $self->{'M'} eq 'A' and $self->{'myip'} and !$self->{'passive_get'} ) ? '' : 'Rev' ) . 'ConnectToMe',
1541             $nick );
1542             }
1543             }
1544              
1545             sub file_select { #$self->{'file_select'} ||= sub {
1546 0     0 0   my $self = shift;
1547 0 0         return if length $self->{'filename'};
1548 0   0       my $peerid = $self->{'peerid'} || $self->{'peernick'};
1549             #$self->log( 'dcdev','file_select000',$peerid, $self->{'filename'}, $self->{'fileas'}, Dumper $self->{'want'});
1550 0           for my $file ( keys %{ $self->{'want'}{$peerid} } ) {
  0            
1551             #( $self->{'filename'}, $self->{'fileas'} ) = ( $_, $self->{'want'}{$peerid}{$_} );
1552 0           $self->{$_} = $self->{'want'}{$peerid}{$file}{$_} for keys %{ $self->{'want'}{$peerid}{$file} };
  0            
1553             #$self->log( 'dcdev', 'file_select1', $self->{'filename'}, $self->{'fileas'} );
1554 0 0         next unless defined $self->{'filename'};
1555 0           $self->{'filecurrent'} = $self->{'filename'};
1556             #delete $self->{'want'}{ $peerid }{$_} ; $self->{'filecurrent'}
1557             #$self->{'file_recv_from'}
1558             #$self->{'fileas'}
1559 0           last;
1560             }
1561 0           delete $self->{'downloading'}{ $self->{'file_recv_tth'} }{connect_start};
1562             #$self->log( 'dcdev', 'file_select2', $self->{'filename'}, $self->{'fileas'} );
1563 0 0         return unless defined $self->{'filename'};
1564 0 0         unless ( $self->{'filename'} ) {
1565 0 0 0       if ( $self->{'peers'}{$peerid}{'SUP'}{'BZIP'}
    0          
    0          
1566             or $self->{'NickList'}->{$peerid}{'XmlBZList'} )
1567             {
1568 0           $self->{'fileext'} = '.xml.bz2';
1569 0           $self->{'filename'} = 'files' . $self->{'fileext'};
1570             } elsif ( $self->{'adc'} ) {
1571 0           $self->{'fileext'} = '.xml';
1572 0           $self->{'filename'} = 'files' . $self->{'fileext'};
1573             } elsif ( $self->{'NickList'}->{$peerid}{'BZList'} ) {
1574 0           $self->{'fileext'} = '.bz2';
1575 0           $self->{'filename'} = 'MyList' . $self->{'fileext'};
1576             } else {
1577 0           $self->{'fileext'} = '.DcLst';
1578 0           $self->{'filename'} = 'MyList' . $self->{'fileext'};
1579             }
1580 0 0         $self->{'fileas'} .= $self->{'fileext'} if $self->{'fileas'};
1581 0           $self->{'file_recv_filelist'} = 1;
1582             }
1583 0   0       $self->{'file_recv_dest'} = ( $self->{'fileas'} || $self->{'filename'} );
1584 0           $self->{'file_recv_full'} = $self->{'file_recv_dest'};
1585 0 0         $self->{'file_recv_full'} = $self->{'download_to'} . $self->{'file_recv_full'}
1586             unless $self->{'file_recv_full'} =~ m{[/\\]};
1587             #$self->log('dcdev', '1full', $self->{'file_recv_full'});
1588             #$self->{'file_recv_dest'} = Encode::encode $self->{charset_fs}, $self->{'file_recv_dest'} if $self->{charset_fs}; # ne $self->{charset_protocol};
1589             #$self->{'file_recv_dest'}
1590             #$self->log( 'dcdev', "pre enc filename [$self->{'file_recv_dest'}] [$self->{charset_fs} ne $self->{charset_protocol}]");
1591             #$self->{'file_recv_dest'} = Encode::encode $self->{charset_fs}, Encode::decode $self->{charset_protocol},
1592             #$self->log( 'dcdev', "pst enc filename [$self->{'file_recv_dest'}]");
1593 0 0         mkdir_rec $self->{'partial_prefix'} if $self->{'partial_prefix'};
1594 0 0         $self->{'file_recv_partial'} =
1595             "$self->{'file_recv_dest'}" . ( $self->{'file_recv_tth'} ? '.' . $self->{'file_recv_tth'} : () ) . "$self->{'partial_ext'}";
1596 0 0         $self->{'file_recv_partial'} = $self->{'partial_prefix'} . $self->{'file_recv_partial'}
1597             unless $self->{'file_recv_partial'} =~ m{[/\\]};
1598 0 0         $self->{'file_recv_partial'} = Encode::encode $self->{charset_fs}, $self->{'file_recv_partial'}, Encode::FB_WARN
1599             if $self->{charset_fs};
1600 0           $self->{'filebytes'} = $self->{'file_recv_from'} = -s $self->{'file_recv_partial'};
1601 0 0 0       $self->{'file_recv_to'} ||= $self->{'file_recv_size'} - $self->{'file_recv_from'}
      0        
1602             if $self->{'file_recv_size'} and $self->{'file_recv_from'};
1603             #$self->log('dcdev', '1part', $self->{'file_recv_partial'});
1604             #$self->log('dcdev', 'file_select3', $self->{'filename'}, $self->{'fileas'},
1605             # 'part:', $self->{'file_recv_partial'}, 'full:', $self->{'file_recv_full'},
1606             # 'from', $self->{'file_recv_from'});
1607             }
1608              
1609             sub file_open { #$self->{'file_open'} ||= sub {
1610 0     0 0   my $self = shift;
1611             #$self->{'fileas'}=$_[0] if !length $self->{'fileas'} or length $_[0];
1612             #$self->{'filetotal'} = $_[1]if ! $self->{'filetotal'} or $_[1];
1613             #$self->{'filetotal'} //= $self->{'file_recv_size'}
1614             #$self->log('dcdev', '2part', $self->{'file_recv_partial'});
1615 0 0         my $oparam = $self->{'fileas'} eq '-' ? '>-' : '>>' . $self->{'file_recv_partial'};
1616 0           $self->handler( 'file_open_bef', $oparam );
1617             # $self->log( 'dbg', "file_open pre", $oparam, 'want bytes', $self->{'filetotal'}, 'as=', $self->{'fileas'}, 'f=', $self->{'filename'} );
1618             #$self->log( 'dcdev', "open [$oparam]" );
1619 0 0         open( $self->{'filehandle'}, $oparam )
1620             or $self->log( 'dcerr', "file_open error", $!, $oparam ),
1621             $self->handler( 'file_open_error', $!, $oparam ), return 1;
1622 0           binmode( $self->{'filehandle'} );
1623 0           $self->{'status'} = 'transfer';
1624 0           return 0;
1625             }
1626              
1627             sub file_write { #$self->{'file_write'} ||= sub {
1628 0     0 0   my $self = shift;
1629 0   0       $self->{'file_start_time'} ||= time;
1630 0 0         my $fh = $self->{'filehandle'}
1631             or $self->log( 'err', 'cant write, no filehandle' ), return;
1632 0           for my $databuf (@_) {
1633 0           $self->{'filebytes'} += length $$databuf;
1634             #$self->log( 'dcdbg', "($self->{'number'}) recv ".length($$databuf)." [$self->{'filebytes'}] of $self->{'filetotal'} file $self->{'filename'}" );
1635             #$self->log( 'dcdbg', "recv " . length($$databuf) . " [$$databuf]" ) if length $$databuf < 10;
1636 0           print $fh $$databuf;
1637             schedule(
1638             10,
1639             $self->{__stat_recv} ||= sub {
1640 0     0     my $self = shift;
1641 0           my $recv = shift;
1642             #my $read = shift;
1643             #our ( $lastmark, $lasttime );
1644 0   0       $self->log(
1645             'dev', "recv bytes", #length $self->{'file_send_buf'},
1646             "recv=[$recv] now [", $self->{'filebytes'},
1647             "] of [$self->{'filetotal'}], now", 's=',
1648             int( ( $self->{'filebytes'} - $self->{__stat_recv_lastmark} ) /
1649             ( time - $self->{__stat_recv_lasttime} or 1 ) ),
1650             "status=[$self->{'status'}]",
1651             ),
1652             $self->{__stat_recv_lastmark} = $self->{'filebytes'};
1653 0           $self->{__stat_recv_lasttime} = time;
1654             #if time - $lasttime > 1;
1655             },
1656 0   0       $self,
1657             length $$databuf,
1658             );
1659 0 0         $self->log( 'err', "file download error! extra bytes ($self->{'filebytes'}/$self->{'filetotal'}) " )
1660             if $self->{'filebytes'} > $self->{'filetotal'};
1661 0 0 0       $self->log(
1662             'info',
1663             "file complete ($self->{'filebytes'}) per",
1664             $self->float( time - $self->{'file_start_time'} ),
1665             's at', $self->float( $self->{'filebytes'} / ( ( time - $self->{'file_start_time'} ) or 1 ) ), 'b/s'
1666             ),
1667             #$self->disconnect(), $self->{'status'} = 'destroy',
1668             $self->file_close(),
1669             $self->{'file_start_time'} = 0, $self->{'filename'} = '',
1670             $self->{'fileas'} = '',
1671             delete $self->{'want'}{ $self->{'peerid'} }{ $self->{'filecurrent'} },
1672             $self->{'filecurrent'} = '', $self->{'file_recv_partial'} = '',
1673             $self->{'file_recv_from'} = $self->{'file_recv_to'} = undef,
1674             #!!?$self->destroy(),
1675             if $self->{'filebytes'} >= $self->{'filetotal'};
1676             }
1677             }
1678              
1679             sub file_close { #$self->{'file_close'} ||= sub {
1680 0     0 0   my $self = shift;
1681             #$self->log( 'dcerr', 'file_close', 1);
1682 0 0         if ( $self->{'filehandle'} ) {
1683             #$self->log( 'dcerr', 'file_close',2);
1684 0           close( $self->{'filehandle'} ), delete $self->{'filehandle'};
1685 0 0         if ( $self->{'filebytes'} == $self->{'filetotal'} ) {
1686 0 0         mkdir_rec $self->{'download_to'} if $self->{'download_to'};
1687 0 0         if ( length $self->{'partial_ext'} ) {
1688 0 0         local $self->{'file_recv_full'} = Encode::encode $self->{charset_fs}, $self->{'file_recv_full'}, Encode::FB_WARN
1689             if $self->{charset_fs}; # ne $self->{charset_protocol};
1690             #$self->log( 'dcdev', 'file_close', 3, $self->{'file_recv_partial'}, $self->{'file_recv_full'} );
1691 0 0         $self->log( 'dcerr', 'cant move finished file', $self->{'file_recv_partial'}, '=>', $self->{'file_recv_full'} )
1692             if !rename $self->{'file_recv_partial'}, $self->{'file_recv_full'};
1693             }
1694 0           delete $self->{'downloading'}{ $self->{'file_recv_tth'} };
1695 0   0       ( $self->{parent} || $self )->handler( 'file_recieved', $self->{'file_recv_full'}, $self->{'filename'} );
1696             }
1697             }
1698 0 0         if ( $self->{'downloading'}{ $self->{'file_recv_tth'} } ) {
1699             #$self->log( 'dev', "onclose: downloading [$self->{'file_recv_tth'}], b$self->{'filebytes'} <= t$self->{'filetotal'} || $self->{'file_recv_size'}" );
1700 0 0 0       if ( $self->{'filebytes'} <= $self->{'filetotal'}
1701             || $self->{'file_recv_size'} )
1702             {
1703 0           $self->{'want_download'}{ $self->{'file_recv_tth'} } = $self->{'downloading'}{ $self->{'file_recv_tth'} };
1704             } #else { }
1705 0           delete $self->{'downloading'}{ $self->{'file_recv_tth'} };
1706             }
1707             #$self->{'select_send'}->remove( $self->{'socket'} ),
1708 0 0 0       close( $self->{'filehandle_send'} ), delete $self->{'filehandle_send'},
1709             #$self->{'socket'}->flush(),
1710             if $self->{'select_send'} and $self->{'filehandle_send'};
1711 0           delete $self->{$_} for 'file_send_left', 'file_send_total', 'file_recv_filelist';
1712 0 0         $self->{'status'} = 'connected' if $self->{'status'} eq 'transfer';
1713             }
1714              
1715             sub file_send_tth { #$self->{'file_send_tth'} ||= sub {
1716 0     0 0   my $self = shift;
1717 0           my ( $file, $start, $size, $as ) = @_;
1718             #$self->log( 'dcdev', 'my share', $self->{'share_full'}, scalar keys %{$self->{'share_full'} }, 'p share', $self->{'parent'}{'share_full'}, scalar keys %{$self->{'parent'}{'share_full'} }, );
1719             #$self->{'share_tth'} ||=$self->{'parent'}{'share_tth'};
1720 0 0         if ( $self->{'share_full'}{$file} ) {
1721 0           $self->{'share_full'}{$file} =~ tr{\\}{/};
1722             #$self->log( 'dcdev', 'call send', $self->{'share_full'}{$file}, $start, $size, $as );
1723 0           $self->file_send( $self->{'share_full'}{$file}, $start, $size, $as );
1724 0           $self->search_stat_update( $file, 'hit' );
1725             } else {
1726 0           $self->log(
1727             'dcerr', 'send', 'cant find file',
1728             $file, $self->{'share_full'}{$file},
1729 0           'from', scalar keys %{ $self->{'share_full'} }
1730             );
1731 0           return 1;
1732             }
1733 0           return undef;
1734             }
1735              
1736             sub file_send { #$self->{'file_send'} ||= sub {
1737 0     0 0   my $self = shift;
1738             #$self->log( 'dcdev', 'file_send', Dumper \@_ );
1739 0           my ( $file, $start, $size, $as ) = @_;
1740 0   0       $start //= 0;
1741 0           my $filesize = -s $file;
1742 0 0         $size = $filesize - $start if $size <= 0;
1743 0 0 0       $self->log( 'dcerr', "cant find [$file]" ), $self->disconnect(), return
1744             if !-e $file
1745             or -d $file;
1746 0 0         if ( open $self->{'filehandle_send'}, '<', $file ) {
1747 0           binmode( $self->{'filehandle_send'} );
1748 0 0         seek( $self->{'filehandle_send'}, $start, SEEK_SET ) if $start;
1749 0           my $name = $file;
1750 0           $name =~ s{^.*[\\/]}{}g;
1751 0           $self->{'file_send_total'} = $filesize;
1752 0   0       $self->{'file_send_offset'} = $start || 0;
1753 0           $self->{'file_send_left'} = $size;
1754 0           $self->log( 'dev', "sendsize=$size filesize=$filesize from", $start, 'e', -e $file, $file, $self->{'file_send_total'} );
1755             #$self->{'filetotal'} = $self->{'file_send_offset'} + $self->{'file_send_left'};
1756 0 0         $self->file_close(), return if $start >= $self->{'file_send_total'};
1757 0 0         if ( $self->{'adc'} ) {
1758 0   0       $self->cmd( 'C', 'SND', 'file', $as || $name, $start, $size );
1759             } else {
1760 0   0       $self->cmd( 'ADCSND', 'file', $as || $name, $start, $size );
1761             }
1762 0           $self->{'status'} = 'transfer';
1763             #$self->file_send_part();
1764             #$self->{'select_send'}->add( $self->{'socket'} );
1765             } else {
1766 0           $self->file_close();
1767             }
1768             }
1769              
1770             sub file_send_part { #$self->{'file_send_part'} ||= sub {
1771             #psmisc::printlog 'call', 'file_send_part', @_;
1772 0     0 0   my $self = shift;
1773             #my ($file, $start, $size) = @_;
1774             #return unless $self->{'file_send_left'};
1775             #my $buf;
1776             #$self->disconnect(),
1777             return
1778 0 0 0       unless ( $self->{'socket'}
      0        
      0        
1779             and $self->{'socket'}->connected()
1780             and $self->{'filehandle_send'}
1781             and $self->{'file_send_left'} );
1782 0           my $read = $self->{'file_send_left'};
1783 0 0         $read = $self->{'file_send_by'}
1784             if $self->{'file_send_by'} < $self->{'file_send_left'};
1785 0           my $sent;
1786 0 0         if (0) {
    0          
1787 0           } elsif ( $INC{'Sys/Sendfile.pm'} ) { #works
1788             #$self->log( 'dev', 'using sys::sendfile ');
1789 0           $sent = Sys::Sendfile::sendfile( $self->{'socket'}, $self->{'filehandle_send'}, $read, $self->{'file_send_offset'} );
1790 0 0         $self->{'file_send_offset'} += $sent if $sent > 0;
1791             } elsif ( $INC{'Sys/Sendfile/FreeBSD.pm'} ) {
1792             #$self->log( 'dev', 'using sendfile freebsd');
1793 0           my $result = Sys::Sendfile::FreeBSD::sendfile(
1794             fileno( $self->{'filehandle_send'} ),
1795             fileno( $self->{'socket'} ),
1796             $self->{'file_send_offset'},
1797             $read, $sent
1798             );
1799 0 0         $self->{'file_send_offset'} += $sent if $sent > 0;
1800             #blocking
1801             #elsif ($INC{'IO/AIO.pm'}) {
1802             # $sent = IO::AIO::sendfile( fileno($self->{'socket'}), fileno($self->{'filehandle_send'}),$self->{'file_send_offset'}, $read );
1803             # $self->{'file_send_offset'} += $sent if $sent > 0;
1804             } else {
1805             #$self->log( 'dev', 'using read send');
1806 0 0         read( $self->{'filehandle_send'}, $self->{'file_send_buf'}, $read ),
1807             $self->{'file_send_offset'} = tell $self->{'filehandle_send'},
1808             unless length $self->{'file_send_buf'}; #$self->{'file_send_by'};
1809             #send $self->{'socket'},
1810             #$self->{'socket'}->send( buf, POSIX::BUFSIZ, $self->{'recv_flags'} )
1811             #my $sent;
1812             #$self->log( 'snd', length $self->{'file_send_buf'},
1813 0           $sent = $self->send_can( $self->{'file_send_buf'} );
1814             }
1815             schedule(
1816             10,
1817             $self->{__stat_} ||= sub {
1818 0     0     my $self = shift;
1819 0           my $sent = shift;
1820 0           my $read = shift;
1821             #our ( $lastmark, $lasttime );
1822 0   0       $self->log(
1823             'dev', "sent bytes", #length $self->{'file_send_buf'},
1824             "sent=[$sent] of buf [", length $self->{'file_send_buf'},
1825             "] by [$read:$self->{'file_send_by'}] left $self->{'file_send_left'}, now",
1826             $self->{'file_send_offset'}, 'of',
1827             $self->{'file_send_total'}, 's=',
1828             int( ( $self->{'file_send_offset'} - $self->{__stat_lastmark} ) /
1829             ( time - $self->{__stat_lasttime} or 1 ) ),
1830             "status=[$self->{'status'}]",
1831             ),
1832             $self->{__stat_lastmark} = $self->{'file_send_offset'};
1833 0           $self->{__stat_lasttime} = time;
1834             #if time - $lasttime > 1;
1835             },
1836 0   0       $self,
1837             $sent,
1838             $read
1839             );
1840             #$self->{activity} = time if $sent;
1841             #$self->{bytes_send} += $sent;
1842 0           $self->{'file_send_left'} -= $sent;
1843             #$self->log( 'dev', 'send end', $sent, $self->{'file_send_offset'}, $self->{'file_send_total'}, "left=[$self->{'file_send_left'}]");
1844 0           substr( $self->{'file_send_buf'}, 0, $sent ) = undef;
1845             #if (length $self->{'file_send_buf'}) { $self->log( 'info', 'sent small', $sent, 'todo', length $self->{'file_send_buf'}); }
1846             #$readed;
1847 0 0         if ( $self->{'file_send_left'} < 0 ) {
1848 0           $self->log( 'err', "oversend [$self->{'file_send_left'}]" );
1849 0           $self->{'file_send_left'} = 0;
1850             }
1851 0 0         if (
1852             #$readed < $self->{'file_send_by'} or
1853             $self->{'file_send_left'} <= 0
1854             )
1855             {
1856 0           $self->log(
1857             'dev', 'file completed', "r:", length $self->{'file_send_buf'},
1858             " by:$self->{'file_send_by'} left:$self->{'file_send_left'} total:$self->{'file_send_total'}",
1859             #caller 2
1860             );
1861 0           $self->file_close();
1862             #$self->{'status'} = 'connected';
1863             #?
1864             #$self->disconnect();
1865 0           $self->destroy();
1866             }
1867 0           return $sent;
1868             }
1869              
1870             sub file_send_parse { #$self->{'file_send_parse'} =
1871             #$self->{'ADCSND'} =
1872             #sub {
1873 0 0   0 0   my $self = shift if ref $_[0];
1874             #$self->log( 'cmd_adcSND', Dumper \@_);
1875             #my ( $dst, $peerid, $toid ) = @{ shift() };
1876 0 0         if ( $_[0] eq 'file' ) {
    0          
    0          
1877 0           my $file = $_[1];
1878 0 0         if ( $file =~ s{^TTH/}{} ) {
1879 0           return $self->file_send_tth( $file, $_[2], $_[3], $_[1] );
1880             } else {
1881             #$self->file_send($file, $_[2], $_[3]);
1882 0           return $self->file_send_tth( $file, $_[2], $_[3], $_[1] );
1883             }
1884             } elsif ( $_[0] eq 'list' ) {
1885 0           return $self->file_send_tth( 'files.xml.bz2', );
1886             } elsif ( $_[0] eq 'tthl' ) {
1887             #TODO!! now fake
1888 0           ( my $tth = $_[1] ) =~ s{^TTH/}{};
1889 0           eval q{
1890             use MIME::Base32 qw( RFC );
1891             $tth = MIME::Base32::decode $tth;
1892             };
1893 0 0         if ( $self->{'adc'} ) {
1894 0           $self->cmd( 'C', 'SND', $_[0], $_[1], $_[2], length $tth );
1895             } else {
1896 0           $self->cmd( 'ADCSND', $_[0], $_[1], $_[2], length $tth );
1897             }
1898 0           $self->send($tth);
1899             } else {
1900 0           $self->log( 'dcerr', 'SND', "unknown type", @_ );
1901 0           return 2;
1902             }
1903 0           return undef;
1904             }
1905              
1906             sub download { #$self->{'download'} ||= sub {
1907 0 0   0 0   my $self = shift if ref $_[0];
1908             #my $self = shift;
1909 0           my ($file) = @_;
1910             #$self->log('dev', "0s=[$self]; download [$file] now $self->{'want_download'}{$file} ", Dumper \@_);
1911 0   0       push @{ $self->{'queue_download'} ||= [] }, $file;
  0            
1912             #$self->log('dev', "1s=[$self]; download [$file] now $self->{'want_download'}{$file} ", Dumper \@_);
1913 0   0       $self->{'want_download'}{$file} ||= {};
1914             }
1915              
1916             sub get_peer_addr { #$self->{'get_peer_addr'} ||= sub () {
1917 0 0   0 0   my $self = shift if ref $_[0];
1918 0           my ($recv) = @_;
1919 0 0         return unless $self->{'socket'};
1920 0           eval {
1921 0           $self->{'port'} = $self->{'socket'}->peerport();
1922 0           $self->{'hostip'} = $self->{'socket'}->peerhost();
1923 0   0       $self->{'host'} ||= $self->{'hostip'};
1924             };
1925 0           return $self->{'hostip'};
1926              
1927             =no
1928             local @_ = socket_addr $self->{'socket'};
1929             #eval { @_ = unpack_sockaddr_in( getpeername( $self->{'socket'} ) || return ) };
1930             #return unless $_[1];
1931             #return unless $_[1] = inet_ntoa( $_[1] );
1932             $self->{'port'} = $_[0] if $_[0]; #;and !$self->{'incoming'};
1933             $self->{'hostip'} = $_[1], $self->{'host'} ||= $self->{'hostip'}
1934             if $_[1];
1935             return $self->{'hostip'};
1936             =cut
1937              
1938             }
1939              
1940             sub get_peer_addr_recv { #$self->{'get_peer_addr_recv'} ||= sub (;$) {
1941 0 0   0 0   my $self = shift if ref $_[0];
1942 0           my ($recv) = @_;
1943             #return unless $self->{'socket'};
1944 0   0       $recv ||= $self->{'recv_addr'};
1945 0           ( $self->{'recv_port'}, my $hostn ) = sockaddr_in($recv);
1946 0           $self->{'recv_host'} = gethostbyaddr( $hostn, AF_INET );
1947 0           $self->{'recv_hostip'} = inet_ntoa($hostn);
1948 0           return $self->{'hostip'};
1949             }
1950              
1951             sub get_my_addr { #$self->{'get_my_addr'} ||= sub {
1952 0 0   0 0   my $self = shift if ref $_[0];
1953             #my ($self) = @_;
1954 0 0         return unless $self->{'socket'};
1955             #$self->log('dev', 'saddr', $self->{'socket'}->sockhost(),$self->{'socket'}->sockport() );
1956 0   0       $self->{'myport'} ||= $self->{'socket'}->sockport();
1957             #$self->log('dev', 'myip was:', $self->{'myip'}, '->', $self->{'socket'}->sockhost());
1958 0   0       return $self->{'myip'} ||= $self->{'socket'}->sockhost();
1959              
1960             =no
1961             eval { @_ = unpack_sockaddr_in( getsockname( $self->{'socket'} ) || return ); };
1962             $self->log( 'dcerr', "cant get my ip [$@]", Dumper \@_ ) if $@;
1963             #$self->log('dcerr', "cant get my ip [0.0.0.0:$_[0]]"),
1964             return if $_[1] eq "\0\0\0\0";
1965             #$self->log('dev', "1my ip", Dumper \@_);
1966             #return unless $_[1];
1967             return unless $_[1] and $_[1] = inet_ntoa( $_[1] );
1968             #$self->log('dev', "2my ip", Dumper \@_);
1969             #return if $_[1] eq '0.0.0.0';
1970             #$self->{'log'}->('dev', "MYIP($self->{'myip'}) [$self->{'number'}] SOCKNAME $_[0],$_[1];");
1971             return $self->{'myip'} ||= $_[1];
1972             =cut
1973             }
1974              
1975             sub info { #$self->{'info'} ||= sub {
1976 0 0   0 0   my $self = shift if ref $_[0];
1977             #my $self = shift;
1978 0           $self->log(
1979             'info',
1980 0           map( {"$_=$self->{$_}"} grep { $self->{$_} } @{ $self->{'informative'} } ),
  0            
  0            
1981             #map( { $_ . '(' . scalar( keys %{ $self->{$_} } ) . ')=' . join( ',', sort keys %{ $self->{$_} } ) }
1982             #grep { keys %{ $self->{$_} } } @{ $self->{'informative_hash'} } )
1983 0           'clients:', scalar keys %{ $self->{'clients'} },
1984 0           map { "($self->{'clients'}{$_}{'number'})$_=$self->{'clients'}{$_}{'status'}" }
1985 0           sort keys %{ $self->{'clients'} },
1986             );
1987 0           $self->log(
1988             'dcdbg',
1989             "protocol stat",
1990             Dumper( {
1991 0           map { $_ => $self->{$_} }
1992 0 0         grep { $self->{$_} } qw(count_sendcmd count_parse)
1993             }
1994             ),
1995             ) unless $self->{'parent'};
1996             #( ref $self->{'clients'}{$_}{info} ? $self->{'clients'}{$_}->info() : () ) for sort keys %{ $self->{'clients'} };
1997             }
1998             #sub status {
1999             #now states:
2000             #listening connecting_tcp connecting connected reconnecting transfer disconnecting disconnected destroy
2001             #need checks:
2002             #\ connected?/ \-----/
2003             #\-----------------------active?-------------------------/
2004             #}
2005             #$self->{'active'} ||= sub {
2006             sub active {
2007             #my $self = shift;
2008 0 0   0 0   my $self = shift if ref $_[0];
2009             #$self->log('dev', 'active=', $self->{'status'});
2010 0           return $self->{'status'}
2011 0 0         if grep { $self->{'status'} eq $_ } qw(connecting_tcp connecting connected reconnecting listening transfer working);
2012 0           return 0;
2013             }
2014              
2015             sub every { #$self->{'every'} ||= sub {
2016 0     0 0   my ( $self, $sec, $func ) = ( shift, shift, shift );
2017 0 0 0       if ( ( $self->{'every_list'}{$func} + $sec < time )
2018             and ( ref $func eq 'CODE' ) )
2019             {
2020 0           $self->{'every_list'}{$func} = time;
2021 0           $func->(@_);
2022             }
2023             }
2024              
2025             sub adc_make_string { #$self->{'adc_make_string'} = sub (@) {
2026 0 0   0 0   my $self = shift if ref $_[0];
2027             join ' ', map {
2028 0 0         ref $_ eq 'ARRAY' ? @$_ : ref $_ eq 'HASH' ? do {
  0 0          
2029 0           my $h = $_;
2030 0           map { "$_$h->{$_}" } keys %$h;
  0            
2031             }
2032             : $_
2033             } @_;
2034             }
2035              
2036             sub cmd_adc { #$self->{'cmd_adc'} ||= sub {
2037 0     0 0   my ( $self, $dst, $cmd ) = ( shift, shift, shift );
2038             #$self->sendcmd( $dst, $cmd,map {ref $_ eq 'HASH'}@_);
2039             #$self->log( 'cmd_adc', $dst, $cmd, "SI[$self->{'INF'}{'SID'}]",Dumper \@_ );
2040 0 0 0       $self->sendcmd(
2041             $dst, $cmd,
2042             #map {ref $_ eq 'ARRAY' ? @$_:ref $_ eq 'HASH' ? each : $_) }@_
2043             ( #$self->{'broadcast'} ? $self->{'INF'}{'SID'} #$self->{'INF'}{'ID'}
2044             #:
2045             ( $dst eq 'C' or !length $self->{'INF'}{'SID'} )
2046             ? ()
2047             : $self->{'INF'}{'SID'}
2048             ),
2049             $self->adc_make_string(@_)
2050             #( $dst eq 'D' || !length $self->{'sid'} ? () : $self->{'sid'} ),
2051             );
2052             }
2053             #sub adc_string_decode ($) {
2054             sub adc_string_decode { #$self->{'adc_string_decode'} ||= sub ($) {
2055 0     0 0   my $self = shift;
2056 0           local ($_) = @_;
2057 0           s{\\s}{ }g;
2058 0           s{\\n}{\x0A}g;
2059 0           s{\\\\}{\\}g;
2060 0           $_;
2061             }
2062             #sub adc_string_encode ($) {
2063             sub adc_string_encode { #$self->{'adc_string_encode'} = sub ($) {
2064 0     0 0   my $self = shift;
2065 0           local ($_) = @_;
2066 0           s{\\}{\\\\}g;
2067 0           s{ }{\\s}g;
2068 0           s{\x0A}{\\n}g;
2069 0           $_;
2070             }
2071              
2072             sub adc_path_encode { #$self->{'adc_path_encode'} = sub ($) {
2073 0     0 0   my $self = shift;
2074 0           local ($_) = @_;
2075 0           s{^(\w:)}{/${1}_}g;
2076 0           s{\\}{/}g;
2077 0           $self->adc_string_encode($_);
2078             }
2079             #sub adc_strings_decode (\@) {
2080             sub adc_strings_decode { #$self->{'adc_strings_decode'} = sub (\@) {
2081 0     0 0   my $self = shift;
2082 0           map { $self->adc_string_decode($_) } @_;
  0            
2083             }
2084             #sub adc_strings_encode (\@) {
2085             sub adc_strings_encode { #$self->{'adc_strings_encode'} = sub (\@) {
2086 0     0 0   my $self = shift;
2087 0           map { $self->adc_string_encode($_) } @_;
  0            
2088             }
2089              
2090             sub adc_parse_named { #$self->{'adc_parse_named'} = sub (@) {
2091 0     0 0   my $self = shift;
2092             #sub adc_parse_named (@) {
2093             #my ($dst,$peerid) = @{ shift() };
2094             #$self->log('dev', "p0:", @_);
2095 0           local %_;
2096 0           for ( local @_ = @_ ) {
2097 0           s/^([A-Z][A-Z0-9])//;
2098             #my $name=
2099             #print "PARSE[$1=$_]\n",
2100 0           $_{$1} = $self->adc_string_decode($_);
2101             #$self->log('dev', "p1:$1=$_{$1}");
2102             }
2103 0           return \%_;
2104             #return ($dst,$peerid)
2105             }
2106              
2107             sub make_token { #$self->{'make_token'} = sub (;$) {
2108 0     0 0   my $self = shift;
2109 0           my $peerid = shift;
2110 0           my $token;
2111 0           local $_;
2112 0 0 0       $_ = $self->{'peers'}{$peerid}{'INF'}{I4}
2113             if $peerid and exists $self->{'peers'}{$peerid};
2114 0           s/\D//g;
2115 0           $token += $_;
2116 0           $_ = $self->{myip};
2117 0           s/\D//g;
2118 0           return $token + $_ + int time;
2119             }
2120              
2121             sub say { #$self->{'say'} = sub (@) {
2122 0     0 0   my $self = shift;
2123 0 0         @_ = $_[2] if $_[0] eq 'MSG';
2124             #local $_ = Encode::encode $self->{charset_console} , join ' ', @_;print $_, "\n";
2125 0           print Encode::encode( $self->{charset_console}, join( ' ', @_ ), Encode::FB_DEFAULT ), "\n";
2126             }
2127             #local %_ = (
2128             sub search { #'search' => sub {
2129 0 0   0 0   my $self = shift if ref $_[0];
2130             #$self->log( 'search', @_ );
2131 0 0 0       return $self->search_tth(@_)
2132             if length $_[0] == 39 and $_[0] =~ /^[0-9A-Z]+$/;
2133 0 0         return $self->search_string(@_) if length $_[0];
2134             }
2135              
2136             sub search_retry { #'search_retry' => sub {
2137 0 0   0 0   my $self = shift if ref $_[0];
2138 0 0         unshift( @{ $self->{'search_todo'} }, $self->{'search_last'} )
  0            
2139             if ref $self->{'search_last'} eq 'ARRAY';
2140 0           $self->{'search_last'} = undef;
2141             }
2142              
2143             sub search_buffer { #'search_buffer' => sub {
2144 0 0   0 0   my $self = shift if ref $_[0];
2145 0 0         push( @{ $self->{'search_todo'} }, [@_] ) if @_;
  0            
2146 0 0         return unless @{ $self->{'search_todo'} || return };
  0 0          
2147             #$self->log($self, 'search', Dumper \@_);
2148             #$self->log( 'dcdev', "search too fast [$self->{'search_every'}], len=", scalar @{ $self->{'search_todo'} } ) if @_ and scalar @{ $self->{'search_todo'} } > 1;
2149             return
2150 0 0         if time() - $self->{'search_last_time'} < $self->{'search_every'} + 2;
2151 0           $self->{'search_last'} = shift( @{ $self->{'search_todo'} } );
  0            
2152 0 0         $self->{'search_todo'} = undef unless @{ $self->{'search_todo'} };
  0            
2153 0           $self->search_send();
2154             #if ( $self->{'adc'} ) {
2155             #} else {
2156             #$self->sendcmd( 'Search', $self->{'M'} eq 'P' ? 'Hub:' . $self->{'Nick'} : "$self->{'myip'}:$self->{'myport_udp'}", join '?', @{ $self->{'search_last'} } );
2157             #}
2158 0           $self->{'search_last_time'} = time();
2159             }
2160              
2161             sub nick_generate { #'nick_generate' => sub {
2162 0 0   0 0   my $self = shift if ref $_[0];
2163 0   0       $self->{'nick_base'} ||= $self->{'Nick'};
2164 0   0       $self->{'Nick'} = $self->{'nick_base'} . int( rand( $self->{'nick_random'} || 100 ) );
2165             }
2166              
2167             sub clients_my { #'clients_my' => sub {
2168 0 0   0 0   my $self = shift if ref $_[0];
2169 0 0         grep { $self->{'clients'}{$_} and $self->{'clients'}{$_}{parent} eq $self }
  0            
2170 0           keys %{ $self->{'clients'} };
2171             }
2172             #);
2173             #$self->{$_} = $_{$_} for keys %_;
2174             #}
2175             #print "N:DC:CALLER=", caller, "\n";
2176             do {
2177 1     1   1192 use lib '../';
  1         693  
  1         6  
2178             __PACKAGE__->new( auto_work => 1, @ARGV ),;
2179             } unless caller;
2180             1;
2181             __END__