File Coverage

blib/lib/Net/OSCAR/Connection.pm
Criterion Covered Total %
statement 46 401 11.4
branch 0 188 0.0
condition 0 91 0.0
subroutine 16 44 36.3
pod 0 25 0.0
total 62 749 8.2


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Net::OSCAR::Connection -- individual Net::OSCAR service connection
6              
7             =head1 VERSION
8              
9             version 1.928
10              
11             =cut
12              
13             package Net::OSCAR::Connection;
14             BEGIN {
15 4     4   133 $Net::OSCAR::Connection::VERSION = '1.928';
16             }
17              
18             $REVISION = '$Revision$';
19              
20 4     4   24 use strict;
  4         9  
  4         122  
21 4     4   21 use Carp;
  4         8  
  4         359  
22 4     4   23 use Socket;
  4         13  
  4         2704  
23 4     4   3776 use Symbol;
  4         4262  
  4         298  
24 4     4   27 use Digest::MD5;
  4         7  
  4         132  
25 4     4   21 use Fcntl;
  4         10  
  4         19231  
26 4     4   3631 use POSIX qw(:errno_h);
  4         32968  
  4         31  
27 4     4   16833 use Scalar::Util qw(weaken);
  4         11  
  4         211  
28 4     4   23 use List::Util qw(max);
  4         8  
  4         375  
29              
30 4     4   26 use Net::OSCAR::Common qw(:all);
  4         9  
  4         1376  
31 4     4   28 use Net::OSCAR::Constants;
  4         9  
  4         502  
32 4     4   23 use Net::OSCAR::Utility;
  4         7  
  4         436  
33 4     4   20 use Net::OSCAR::TLV;
  4         16  
  4         182  
34 4     4   4481 use Net::OSCAR::Callbacks;
  4         12  
  4         137  
35 4     4   36 use Net::OSCAR::XML;
  4         11  
  4         23774  
36              
37             if($^O eq "MSWin32") {
38             eval '*F_GETFL = sub {0};';
39             eval '*F_SETFL = sub {0};';
40             eval '*O_NONBLOCK = sub {0}; ';
41             }
42              
43             sub new($@) {
44 0     0 0   my($class, %data) = @_;
45 0   0       $class = ref($class) || $class || "Net::OSCAR::Connection";
46 0           my $self = { %data };
47              
48             # Avoid circular references
49 0           weaken($self->{session});
50              
51 0           bless $self, $class;
52 0           $self->{seqno} = 0;
53 0           $self->{icq_seqno} = 0;
54 0           $self->{outbuff} = "";
55 0   0       $self->{state} ||= "write";
56 0 0         $self->{paused} = 0 unless $self->{paused};
57 0           $self->{families} = {};
58 0           $self->{buffsize} = 65535;
59 0           $self->{buffer} = \"";
60              
61 0 0         $self->connect($self->{peer}) if exists($self->{peer});
62              
63 0           return $self;
64             }
65              
66             sub pause($) {
67 0     0 0   my $self = shift;
68 0   0       $self->{pause_queue} ||= [];
69 0           $self->{paused} = 1;
70             }
71              
72             sub unpause($) {
73 0     0 0   my $self = shift;
74 0 0         return unless $self->{paused};
75 0           $self->{paused} = 0;
76              
77 0           $self->log_print(OSCAR_DBG_WARN, "Flushing pause queue");
78 0           foreach my $item(@{$self->{pause_queue}}) {
  0            
79 0           $self->log_printf(OSCAR_DBG_WARN, "Flushing SNAC 0x%04X/0x%04X", $item->{family}, $item->{subtype});
80 0           $self->snac_put(%$item);
81             }
82 0           $self->log_print(OSCAR_DBG_WARN, "Pause queue flushed");
83              
84 0           delete $self->{pause_queue};
85             }
86              
87             sub proto_send($%) {
88 0     0 0   my($self, %data) = @_;
89 0   0       $data{protodata} ||= {};
90              
91 0           my %snac = protobit_to_snac($data{protobit}); # or croak "Couldn't find protobit $data{protobit}";
92 0 0         confess "BAD SELF!" unless ref($self);
93 0 0         confess "BAD DATA!" unless ref($data{protodata});
94              
95 0           $snac{data} = protoparse($self->{session}, $data{protobit})->pack(%{$data{protodata}});
  0            
96 0           foreach (qw(reqdata reqid flags1 flags2)) {
97 0 0         $snac{$_} = $data{$_} if exists($data{$_});
98             }
99              
100 0 0         if(exists($snac{family})) {
101 0 0 0       if($snac{family} == -1 and exists($data{family})) {
102 0           $snac{family} = $data{family};
103             }
104              
105 0 0 0       if($self->{paused} and !$data{nopause}) {
106 0           $self->log_printf(OSCAR_DBG_WARN, "Adding SNAC 0x%04X/0x%04X to pause queue", $snac{family}, $snac{subtype});
107 0           push @{$self->{pause_queue}}, \%snac;
  0            
108             } else {
109 0           $self->log_printf(OSCAR_DBG_DEBUG, "Put SNAC 0x%04X/0x%04X: %s", $snac{family}, $snac{subtype}, $data{protobit});
110 0           $self->snac_put(%snac);
111             }
112             } else {
113 0   0       $snac{channel} ||= 0+FLAP_CHAN_SNAC;
114 0           $self->log_printf(OSCAR_DBG_DEBUG, "Putting raw FLAP: %s", $data{protobit});
115 0           $self->flap_put($snac{data}, $snac{channel});
116             }
117             }
118              
119              
120              
121             sub fileno($) {
122 0     0 0   my $self = shift;
123 0 0         return undef unless $self->{socket};
124 0           return fileno $self->{socket};
125             }
126              
127             sub flap_encode($$;$) {
128 0     0 0   my ($self, $msg, $channel) = @_;
129              
130 0   0       $channel ||= FLAP_CHAN_SNAC;
131 0           return protoparse($self->{session}, "flap")->pack(
132             channel => $channel,
133             seqno => ++$self->{seqno},
134             msg => $msg
135             );
136             }
137              
138             sub write($$) {
139 0     0 0   my($self, $data) = @_;
140              
141 0 0         my $had_outbuff = 1 if $self->{outbuff};
142 0           $self->{outbuff} .= $data;
143              
144 0           my $nchars = syswrite($self->{socket}, $self->{outbuff}, length($self->{outbuff}));
145 0 0         if(!defined($nchars)) {
146 0 0         return "" if $! == EAGAIN;
147 0           $self->log_print(OSCAR_DBG_NOTICE, "Couldn't write to socket: $!");
148 0           $self->{sockerr} = 1;
149 0           $self->disconnect();
150 0           return undef;
151             }
152              
153 0           my $wrote = substr($self->{outbuff}, 0, $nchars, "");
154              
155 0 0         if($self->{outbuff}) {
    0          
156 0           $self->log_print(OSCAR_DBG_NOTICE, "Couldn't do complete write - had to buffer ", length($self->{outbuff}), " bytes.");
157 0           $self->{state} = "readwrite";
158 0           $self->{session}->callback_connection_changed($self, "readwrite");
159 0           return 0;
160             } elsif($had_outbuff) {
161 0           $self->{state} = "read";
162 0           $self->{session}->callback_connection_changed($self, "read");
163 0           return 1;
164             }
165 0     0     $self->log_print_cond(OSCAR_DBG_PACKETS, sub { "Put '", hexdump($wrote), "'" });
  0            
166              
167 0           return 1;
168             }
169              
170             sub flap_put($;$$) {
171 0     0 0   my($self, $msg, $channel) = @_;
172 0           my $had_outbuff = 0;
173              
174 0   0       $channel ||= FLAP_CHAN_SNAC;
175              
176 0 0 0       return unless $self->{socket} and CORE::fileno($self->{socket}) and getpeername($self->{socket}); # and !$self->{socket}->error;
      0        
177              
178 0 0         $msg = $self->flap_encode($msg, $channel) if $msg;
179 0           $self->write($msg);
180             }
181              
182             # We need to do non-buffered reading so that stdio's buffers don't screw up select, poll, etc.
183             # Thus, for efficiency, we do our own buffering.
184             # To prevent a single OSCAR conneciton from monopolizing processing time, for instance if it has
185             # a flood of incoming data wide enough that we never run out of stuff to read, we'll only fill
186             # the buffer once per call to process_one.
187             #
188             # no_reread value of 2 indicates that we should only read if we have to
189             sub read($$;$) {
190 0     0 0   my($self, $len, $no_reread) = @_;
191 0   0       $no_reread ||= 0;
192              
193 0   0       $self->{buffsize} ||= $len;
194 0           my $buffsize = $self->{buffsize};
195 0 0         $buffsize = $len if $len > $buffsize;
196 0           my $readlen;
197 0 0         if($no_reread == 2) {
198 0           $readlen = $len - length(${$self->{buffer}});
  0            
199             } else {
200 0           $readlen = $buffsize - length(${$self->{buffer}});
  0            
201             }
202              
203 0 0 0       if($readlen > 0 and $no_reread != 1) {
204 0           my $buffer = "";
205 0           my $nchars = sysread($self->{socket}, $buffer, $buffsize - length(${$self->{buffer}}));
  0            
206 0 0         if(${$self->{buffer}}) {
  0            
207 0           ${$self->{buffer}} .= $buffer;
  0            
208             } else {
209 0           $self->{buffer} = \$buffer;
210             }
211              
212 0 0 0       if(!${$self->{buffer}} and !defined($nchars)) {
  0 0 0        
213 0 0         return "" if $! == EAGAIN;
214 0           $self->log_print(OSCAR_DBG_NOTICE, "Couldn't read from socket: $!");
215 0           $self->{sockerr} = 1;
216 0           $self->disconnect();
217 0           return undef;
218             } elsif(!${$self->{buffer}} and $nchars == 0) { # EOF
219 0           $self->log_print(OSCAR_DBG_NOTICE, "Got EOF on socket");
220 0           $self->{sockerr} = 1;
221 0           $self->disconnect();
222 0           return undef;
223             }
224             }
225              
226 0 0         if(length(${$self->{buffer}}) < $len) {
  0            
227 0           return "";
228             } else {
229 0           my $ret;
230 0           delete $self->{buffsize};
231 0 0         if(length(${$self->{buffer}}) == $len) {
  0            
232 0           $ret = $self->{buffer};
233 0           $self->{buffer} = \"";
234             } else {
235 0           $ret = \substr(${$self->{buffer}}, 0, $len, "");
  0            
236             }
237 0     0     $self->log_print_cond(OSCAR_DBG_PACKETS, sub { "Got '", hexdump($$ret), "'" });
  0            
238 0           return $$ret;
239             }
240             }
241              
242             sub flap_get($;$) {
243 0     0 0   my ($self, $no_reread) = @_;
244 0           my $socket = $self->{socket};
245 0           my ($buffer, $channel, $len);
246 0           my $nchars;
247              
248 0 0         if(!$self->{buff_gotflap}) {
249 0           my $header = $self->read(6, $no_reread);
250 0 0         if(!defined($header)) {
    0          
251 0           return undef;
252             } elsif($header eq "") {
253 0           return "";
254             }
255              
256 0           $self->{buff_gotflap} = 1;
257 0           (undef, $self->{channel}, undef, $self->{flap_size}) =
258             unpack("CCnn", $header);
259             }
260              
261 0 0         if($self->{flap_size} > 0) {
262 0   0       my $data = $self->read($self->{flap_size}, $no_reread || 2);
263 0 0         if(!defined($data)) {
    0          
264 0           return undef;
265             } elsif($data eq "") {
266 0           return "";
267             }
268              
269 0     0     $self->log_print_cond(OSCAR_DBG_PACKETS, sub { "Got ", hexdump($data) });
  0            
270 0           delete $self->{buff_gotflap};
271 0           return $data;
272             } else {
273 0           delete $self->{buff_gotflap};
274 0           return "";
275             }
276             }
277              
278             sub snac_encode($%) {
279 0     0 0   my($self, %snac) = @_;
280              
281 0   0       $snac{family} ||= 0;
282 0   0       $snac{subtype} ||= 0;
283 0   0       $snac{flags1} ||= 0;
284 0   0       $snac{flags2} ||= 0;
285 0   0       $snac{data} ||= "";
286 0   0       $snac{reqdata} ||= "";
287 0   0       $snac{reqid} ||= ($snac{subtype}<<16) | (unpack("n", randchars(2)))[0];
288 0 0         $self->{reqdata}->[$snac{family}]->{pack("N", $snac{reqid})} = $snac{reqdata} if $snac{reqdata};
289              
290 0           my $snac = protoparse($self->{session}, "snac")->pack(%snac);
291 0           return $snac;
292             }
293              
294             sub snac_put($%) {
295 0     0 0   my($self, %snac) = @_;
296              
297 0 0 0       if($snac{family} and !$self->{families}->{$snac{family}}) {
298 0           $self->log_printf(OSCAR_DBG_WARN, "Tried to send unsupported SNAC 0x%04X/0x%04X", $snac{family}, $snac{subtype});
299              
300 0           my $newconn = $self->{session}->connection_for_family($snac{family});
301 0 0         if($newconn) {
302 0           return $newconn->snac_put(%snac);
303             } else {
304 0           $self->{session}->crapout($self, "Couldn't find supported connection for SNAC 0x%04X/0x%04X", $snac{family}, $snac{subtype});
305             }
306             } else {
307 0   0       $snac{channel} ||= 0+FLAP_CHAN_SNAC;
308 0 0 0       confess "No family/subtype" unless exists($snac{family}) and exists($snac{subtype});
309              
310 0 0 0       if($self->{session}->{rate_manage_mode} != OSCAR_RATE_MANAGE_NONE and $self->{rate_limits}) {
311 0           my $key = $self->{rate_limits}->{classmap}->{pack("nn", $snac{family}, $snac{subtype})};
312 0 0         if($key) {
313 0           my $rinfo = $self->{rate_limits}->{$key};
314 0 0         if($rinfo) {
315 0           $rinfo->{current_state} = max(
316             $rinfo->{max},
317             $self->{session}->_compute_rate($rinfo)
318             );
319 0           $rinfo->{last_time} = millitime() - $rinfo->{time_offset};
320             }
321             }
322             }
323              
324 0           $self->flap_put($self->snac_encode(%snac), $snac{channel});
325             }
326             }
327              
328             sub snac_get($;$) {
329 0     0 0   my($self, $no_reread) = @_;
330 0 0         my $snac = $self->flap_get($no_reread) or return 0;
331 0           return $self->snac_decode($snac);
332             }
333              
334             sub snac_decode($$) {
335 0     0 0   my($self, $snac) = @_;
336 0           my(%data) = protoparse($self->{session}, "snac")->unpack($snac);
337              
338 0 0         if($data{flags1} & 0x80) {
339 0           my($minihdr_len) = unpack("n", $data{data});
340 0           $self->log_print(OSCAR_DBG_DEBUG, "Got miniheader of length $minihdr_len");
341 0           substr($data{data}, 0, 2+$minihdr_len) = "";
342             }
343              
344 0           return \%data;
345             }
346              
347             sub snac_dump($$) {
348 0     0 0   my($self, $snac) = @_;
349 0           return "family=".$snac->{family}." subtype=".$snac->{subtype};
350             }
351              
352             sub disconnect($) {
353 0     0 0   my($self) = @_;
354              
355 0           $self->{session}->delconn($self);
356             }
357              
358             sub set_blocking($$) {
359 0     0 0   my $self = shift;
360 0           my $blocking = shift;
361 0           my $flags = 0;
362              
363 0 0         if($^O ne "MSWin32") {
364 0           fcntl($self->{socket}, F_GETFL, $flags);
365 0 0         if($blocking) {
366 0           $flags &= ~O_NONBLOCK;
367             } else {
368 0           $flags |= O_NONBLOCK;
369             }
370 0           fcntl($self->{socket}, F_SETFL, $flags);
371             } else {
372             # Cribbed from http://nntp.x.perl.org/group/perl.perl5.porters/42198
373 0 0         ioctl($self->{socket},
374             0x80000000 | (4 << 16) | (ord('f') << 8) | 126,
375             $blocking
376             ) or warn "Couldn't set Win32 blocking: $!\n";
377             }
378              
379 0           return $self->{socket};
380             }
381              
382              
383             sub connect($$) {
384 0     0 0   my($self, $host) = @_;
385 0           my $temp;
386             my $port;
387              
388 0 0         return $self->{session}->crapout($self, "Empty host!") unless $host;
389 0           $host =~ s/:(.+)//;
390 0 0         if(!$1) {
391 0 0         if(exists($self->{session})) {
392 0           $port = $self->{session}->{port};
393             } else {
394 0           return $self->{session}->crapout($self, "No port!");
395             }
396             } else {
397 0           $port = $1;
398 0 0         if($port =~ /^[^0-9]/) {
399 0           $port = $self->{session}->{port};
400             }
401             }
402 0           $self->{host} = $host;
403 0           $self->{port} = $port;
404              
405 0           $self->log_print(OSCAR_DBG_NOTICE, "Connecting to $host:$port.");
406 0 0         if(defined($self->{session}->{proxy_type})) {
407 0 0 0       if($self->{session}->{proxy_type} eq "SOCKS4" or $self->{session}->{proxy_type} eq "SOCKS5") {
    0 0        
408 0 0         require Net::SOCKS or die "SOCKS proxying not available - couldn't load Net::SOCKS: $!\n";
409              
410 0           my $socksver;
411 0 0         if($self->{session}->{proxy_type} eq "SOCKS4") {
412 0           $socksver = 4;
413             } else {
414 0           $socksver = 5;
415             }
416              
417 0   0       my %socksargs = (
418             socks_addr => $self->{session}->{proxy_host},
419             socks_port => $self->{session}->{proxy_port} || 1080,
420             protocol_version => $socksver
421             );
422 0 0         $socksargs{user_id} = $self->{session}->{proxy_username} if exists($self->{session}->{proxy_username});
423 0 0         $socksargs{user_password} = $self->{session}->{proxy_password} if exists($self->{session}->{proxy_password});
424 0 0         $self->{socks} = new Net::SOCKS(%socksargs) or return $self->{session}->crapout($self, "Couldn't connect to SOCKS proxy: $@");
425              
426 0 0         $self->{socket} = $self->{socks}->connect(peer_addr => $host, peer_port => $port) or return $self->{session}->crapout({}, "Couldn't establish connection via SOCKS: $@\n");
427              
428 0           $self->{ready} = 0;
429 0           $self->{connected} = 1;
430 0           $self->set_blocking(0);
431             } elsif($self->{session}->{proxy_type} eq "HTTP" or $self->{session}->{proxy_type} eq "HTTPS") {
432              
433 0           require MIME::Base64;
434              
435 0           my $authen = $self->{session}->{proxy_username};
436 0 0         $authen .= ":$self->{session}->{proxy_password}" if $self->{session}->{proxy_password};
437 0 0         $authen = encode_base64 $authen if $authen;
438              
439 0           my $request = "CONNECT $host:$port HTTP/1.1\r\n";
440 0 0         $request .= "Proxy-Authorization: Basic $authen\r\n" if $authen;
441 0           $request .= "User-Agent: Net::OSCAR\r\n";
442 0           $request .= "\r\n";
443              
444 0           $self->{socket} = gensym;
445 0           socket($self->{socket}, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
446 0 0         if($self->{session}->{local_ip}) {
447 0 0         bind($self->{socket}, sockaddr_in(0, inet_aton($self->{session}->{local_ip}))) or croak "Couldn't bind to desired IP: $!\n";
448             }
449 0           $self->set_blocking(0);
450              
451 0 0         my $addr = inet_aton($self->{session}{proxy_host}) or return $self->{session}->crapout($self, "Couldn't resolve $self->{session}{proxy_host}.");
452 0 0         if(!connect($self->{socket}, sockaddr_in($self->{session}{proxy_port}, $addr))) {
453 0 0         return $self->{session}->crapout($self, "Couldn't connect to $self->{session}{proxy_host}:$self->{session}{proxy_port}: $!")
454             unless $! == EINPROGRESS;
455             }
456              
457             # TODO: I don't know what happens if authentication or connection fails
458             #
459 0           my $buffer;
460 0           syswrite ($self->{socket}, $request);
461 0 0         sysread ($self->{socket}, $buffer, 1024)
462             or return $self->{session}->crapout($self, "Couldn't read from $self->{session}{proxy_host}:$self->{session}{proxy_port}: $!");
463              
464 0 0         return $self->{session}->crapout($self, "Couldn't connect to proxy: $self->{session}{proxy_host}:$self->{session}{proxy_port}: $!")
465             unless $buffer =~ /connection\s+established/i;
466              
467 0           $self->set_blocking(0);
468 0           $self->{ready} = 0;
469 0           $self->{connected} = 1;
470             } else {
471 0           die "Unknown proxy_type $self->{session}->{proxy_type} - valid types are SOCKS4, SOCKS5, HTTP, and HTTPS\n";
472             }
473             } else {
474 0           $self->{socket} = gensym;
475 0           socket($self->{socket}, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
476 0 0         if($self->{session}->{local_ip}) {
477 0 0         bind($self->{socket}, sockaddr_in(0, inet_aton($self->{session}->{local_ip}))) or croak "Couldn't bind to desired IP: $!\n";
478             }
479 0           $self->set_blocking(0);
480              
481 0 0         my $addr = inet_aton($host) or return $self->{session}->crapout($self, "Couldn't resolve $host.");
482 0 0         if(!connect($self->{socket}, sockaddr_in($port, $addr))) {
483 0 0         return 1 if $! == EINPROGRESS;
484 0           return $self->{session}->crapout($self, "Couldn't connect to $host:$port: $!");
485             }
486              
487 0           $self->{ready} = 0;
488 0           $self->{connected} = 0;
489             }
490              
491 0 0         binmode($self->{socket}) or return $self->{session}->crapout($self, "Couldn't set binmode: $!");
492 0           return 1;
493             }
494              
495             sub listen($$) {
496 0     0 0   my($self, $port) = @_;
497 0           my $temp;
498              
499 0   0       $self->{host} = $self->{local_addr} || "0.0.0.0";
500 0           $self->{port} = $port;
501              
502 0           $self->log_print(OSCAR_DBG_NOTICE, "Listening.");
503 0 0         if(defined($self->{session}->{proxy_type})) {
504 0           die "Proxying not support for listening sockets.\n";
505             } else {
506 0           $self->{socket} = gensym;
507 0           socket($self->{socket}, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
508              
509 0 0         setsockopt($self->{socket}, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or return $self->{session}->crapout($self, "Couldn't set listen socket options: $!");
510            
511 0   0       my $sockaddr = sockaddr_in($self->{session}->{local_port} || $port || 0, inet_aton($self->{session}->{local_ip} || 0));
      0        
512 0 0         bind($self->{socket}, $sockaddr) or return $self->{session}->crapout("Couldn't bind to desired IP: $!");
513 0           $self->set_blocking(0);
514 0 0         listen($self->{socket}, SOMAXCONN) or return $self->{session}->crapout("Couldn't listen: $!");
515              
516 0           $self->{state} = "read";
517 0           $self->{rv}->{ft_state} = "listening";
518             }
519              
520 0 0         binmode($self->{socket}) or return $self->{session}->crapout("Couldn't set binmode: $!");
521 0           return 1;
522             }
523              
524              
525              
526 0     0 0   sub get_filehandle($) { shift->{socket}; }
527              
528             # $read/$write tell us if select indicated readiness to read and/or write
529             # Ditto for $error
530             sub process_one($;$$$) {
531 0     0 0   my($self, $read, $write, $error) = @_;
532 0           my $snac;
533              
534 0 0         if($error) {
535 0           $self->{sockerr} = 1;
536 0           return $self->disconnect();
537             }
538              
539 0 0 0       if($write && $self->{outbuff}) {
540 0           $self->log_print(OSCAR_DBG_DEBUG, "Flushing output buffer.");
541 0           $self->flap_put();
542             }
543              
544 0 0 0       if($write && !$self->{connected}) {
    0 0        
    0          
545 0           $self->log_print(OSCAR_DBG_NOTICE, "Connected.");
546 0           $self->{connected} = 1;
547 0           $self->{state} = "read";
548 0           $self->{session}->callback_connection_changed($self, "read");
549 0           return 1;
550             } elsif($read && !$self->{ready}) {
551 0           $self->log_print(OSCAR_DBG_DEBUG, "Getting connack.");
552 0           my $flap = $self->flap_get();
553 0 0         if(!defined($flap)) {
554 0           $self->log_print(OSCAR_DBG_NOTICE, "Couldn't connect.");
555 0           return 0;
556             } else {
557 0           $self->log_print(OSCAR_DBG_DEBUG, "Got connack.");
558             }
559              
560 0 0         return $self->{session}->crapout($self, "Got bad connack from server") unless $self->{channel} == FLAP_CHAN_NEWCONN;
561              
562 0 0         if($self->{conntype} == CONNTYPE_LOGIN) {
563 0           $self->log_print(OSCAR_DBG_DEBUG, "Got connack. Sending connack.");
564 0 0         $self->flap_put(pack("N", 1), FLAP_CHAN_NEWCONN) unless $self->{session}->{svcdata}->{hashlogin};
565 0           $self->log_print(OSCAR_DBG_SIGNON, "Connected to login server.");
566 0           $self->{ready} = 1;
567 0           $self->{families} = {23 => 1};
568              
569 0 0         if(!$self->{session}->{svcdata}->{hashlogin}) {
570 0           $self->proto_send(protobit => "initial_signon_request",
571             protodata => {screenname => $self->{session}->{screenname}},
572             nopause => 1
573             );
574             } else {
575 0           $self->proto_send(protobit => "ICQ_signon_request",
576             protodata => {signon_tlv($self->{session}, delete($self->{auth}))},
577             nopause => 1
578             );
579             }
580             } else {
581 0           $self->log_print(OSCAR_DBG_NOTICE, "Sending BOS-Signon.");
582 0           $self->proto_send(protobit => "BOS_signon",
583             reqid => 0x01000000 | (unpack("n", substr($self->{auth}, 0, 2)))[0],
584             protodata => {cookie => substr(delete($self->{auth}), 2)},
585             nopause => 1
586             );
587             }
588 0           $self->log_print(OSCAR_DBG_DEBUG, "SNAC time.");
589 0           $self->{ready} = 1;
590             } elsif($read) {
591 0           my $no_reread = 0;
592 0           while(1) {
593 0 0         if(!$self->{session}->{svcdata}->{hashlogin}) {
594 0 0         $snac = $self->snac_get($no_reread) or return 0;
595 0           Net::OSCAR::Callbacks::process_snac($self, $snac);
596             } else {
597 0 0         my $data = $self->flap_get($no_reread) or return 0;
598 0           $snac = {data => $data, reqid => 0, family => 0x17, subtype => 0x3};
599 0 0         if($self->{channel} == FLAP_CHAN_CLOSE) {
600 0           $self->{conntype} = CONNTYPE_LOGIN;
601 0           $self->{family} = 0x17;
602 0           $self->{subtype} = 0x3;
603 0           $self->{data} = $data;
604 0           $self->{reqid} = 0;
605 0           $self->{reqdata}->[0x17]->{pack("N", 0)} = "";
606 0           Net::OSCAR::Callbacks::process_snac($self, $snac);
607             } else {
608 0           my $snac = $self->snac_decode($data);
609 0 0         if($snac) {
610 0           Net::OSCAR::Callbacks::process_snac($self, $snac);
611             } else {
612 0           return 0;
613             }
614             }
615             }
616             } continue {
617 0           $no_reread = 1;
618             }
619             }
620             }
621              
622             sub ready($) {
623 0     0 0   my($self) = shift;
624              
625 0 0         return if $self->{sentready}++;
626 0           send_versions($self, 1);
627 0           $self->unpause();
628             }
629              
630 0     0 0   sub session($) { return shift->{session}; }
631              
632             sub peer_ip($) {
633 0     0 0   my($self) = @_;
634              
635 0           my $sockaddr = getpeername($self->{socket});
636 0           my($port, $iaddr) = sockaddr_in($sockaddr);
637 0           return inet_ntoa($iaddr);
638             }
639              
640             sub local_ip($) {
641 0     0 0   my($self) = @_;
642              
643 0           my $sockaddr = getsockname($self->{socket});
644 0           my($port, $iaddr) = sockaddr_in($sockaddr);
645 0           return inet_ntoa($iaddr);
646             }
647              
648             1;