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; |