line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=pod |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Net::OSCAR::Utility -- internal utility functions for Net::OSCAR |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 VERSION |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
version 1.928 |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=cut |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
package Net::OSCAR::Utility; |
14
|
|
|
|
|
|
|
BEGIN { |
15
|
5
|
|
|
5
|
|
144
|
$Net::OSCAR::Utility::VERSION = '1.928'; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
$REVISION = '$Revision$'; |
19
|
|
|
|
|
|
|
|
20
|
5
|
|
|
5
|
|
28
|
use strict; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
173
|
|
21
|
5
|
|
|
5
|
|
25
|
use vars qw(@ISA @EXPORT); |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
362
|
|
22
|
5
|
|
|
5
|
|
29
|
use Digest::MD5 qw(md5); |
|
5
|
|
|
|
|
17
|
|
|
5
|
|
|
|
|
224
|
|
23
|
5
|
|
|
5
|
|
25
|
use Carp; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
291
|
|
24
|
|
|
|
|
|
|
|
25
|
5
|
|
|
5
|
|
25
|
use Net::OSCAR::TLV; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
230
|
|
26
|
5
|
|
|
5
|
|
33
|
use Net::OSCAR::Common qw(:loglevels); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
3169
|
|
27
|
5
|
|
|
5
|
|
456
|
use Net::OSCAR::Constants; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
21326
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
require Exporter; |
30
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
31
|
|
|
|
|
|
|
@EXPORT = qw( |
32
|
|
|
|
|
|
|
randchars log_print log_printf log_print_cond log_printf_cond hexdump normalize tlv_decode tlv_encode send_error bltie |
33
|
|
|
|
|
|
|
signon_tlv encode_password send_versions hash_iter_reset millitime |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
eval { |
37
|
|
|
|
|
|
|
require Time::HiRes; |
38
|
|
|
|
|
|
|
}; |
39
|
|
|
|
|
|
|
our $finetime = $@ ? 0 : 1; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub millitime() { |
43
|
0
|
0
|
|
0
|
0
|
0
|
my $time = $finetime ? Time::HiRes::time() : time(); |
44
|
0
|
|
|
|
|
0
|
return int($time * 1000); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub randchars($) { |
48
|
0
|
|
|
0
|
0
|
0
|
my $count = shift; |
49
|
0
|
|
|
|
|
0
|
my $retval = ""; |
50
|
0
|
|
|
|
|
0
|
for(my $i = 0; $i < $count; $i++) { $retval .= chr(int(rand(256))); } |
|
0
|
|
|
|
|
0
|
|
51
|
0
|
|
|
|
|
0
|
return $retval; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub log_print($$@) { |
56
|
0
|
|
|
0
|
0
|
0
|
my($obj, $level) = (shift, shift); |
57
|
0
|
0
|
|
|
|
0
|
my $session = exists($obj->{session}) ? $obj->{session} : $obj; |
58
|
0
|
0
|
0
|
|
|
0
|
return unless defined($session->{LOGLEVEL}) and $session->{LOGLEVEL} >= $level; |
59
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
0
|
my $message = ""; |
61
|
0
|
0
|
|
|
|
0
|
$message .= $obj->{description}. ": " if $obj->{description}; |
62
|
0
|
|
|
|
|
0
|
$message .= join("", @_). "\n"; |
63
|
|
|
|
|
|
|
|
64
|
0
|
0
|
|
|
|
0
|
if($session->{callbacks}->{log}) { |
65
|
0
|
|
|
|
|
0
|
$session->callback_log($level, $message); |
66
|
|
|
|
|
|
|
} else { |
67
|
0
|
0
|
|
|
|
0
|
$message = "(".$session->{screenname}.") $message" if $session->{SNDEBUG}; |
68
|
0
|
|
|
|
|
0
|
print STDERR $message; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub log_printf($$@) { |
73
|
0
|
|
|
0
|
0
|
0
|
my($obj, $level, $fmtstr) = (shift, shift, shift); |
74
|
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
0
|
$obj->log_print($level, sprintf($fmtstr, @_)); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub log_printf_cond($$&) { |
79
|
0
|
|
|
0
|
0
|
0
|
my($obj, $level, $sub) = @_; |
80
|
0
|
0
|
|
|
|
0
|
my $session = exists($obj->{session}) ? $obj->{session} : $obj; |
81
|
0
|
0
|
0
|
|
|
0
|
return unless defined($session->{LOGLEVEL}) and $session->{LOGLEVEL} >= $level; |
82
|
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
0
|
log_printf($obj, $level, &$sub); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub log_print_cond($$&) { |
87
|
0
|
|
|
0
|
0
|
0
|
my($obj, $level, $sub) = @_; |
88
|
0
|
0
|
|
|
|
0
|
my $session = exists($obj->{session}) ? $obj->{session} : $obj; |
89
|
0
|
0
|
0
|
|
|
0
|
return unless defined($session->{LOGLEVEL}) and $session->{LOGLEVEL} >= $level; |
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
0
|
log_print($obj, $level, &$sub); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub hexdump($;$) { |
95
|
0
|
|
|
0
|
0
|
0
|
my $stuff = shift; |
96
|
0
|
|
0
|
|
|
0
|
my $forcehex = shift || 0; |
97
|
0
|
|
|
|
|
0
|
my $retbuff = ""; |
98
|
0
|
|
|
|
|
0
|
my @stuff; |
99
|
|
|
|
|
|
|
|
100
|
0
|
0
|
|
|
|
0
|
return "" unless defined($stuff); |
101
|
0
|
|
|
|
|
0
|
for(my $i = 0; $i < length($stuff); $i++) { |
102
|
0
|
|
|
|
|
0
|
push @stuff, substr($stuff, $i, 1); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
0
|
0
|
0
|
|
|
0
|
return $stuff unless $forcehex or grep { $_ lt chr(0x20) or $_ gt chr(0x7E) } @stuff; |
|
0
|
0
|
|
|
|
0
|
|
106
|
0
|
|
|
|
|
0
|
while(@stuff) { |
107
|
0
|
|
|
|
|
0
|
my $i = 0; |
108
|
0
|
|
|
|
|
0
|
$retbuff .= "\n\t"; |
109
|
0
|
|
|
|
|
0
|
my @currstuff = splice(@stuff, 0, 16); |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
0
|
foreach my $currstuff(@currstuff) { |
112
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 4; |
113
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 8; |
114
|
0
|
|
|
|
|
0
|
$retbuff .= sprintf "%02X ", ord($currstuff); |
115
|
0
|
|
|
|
|
0
|
$i++; |
116
|
|
|
|
|
|
|
} |
117
|
0
|
|
|
|
|
0
|
for(; $i < 16; $i++) { |
118
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 4; |
119
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 8; |
120
|
0
|
|
|
|
|
0
|
$retbuff .= " "; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
0
|
$retbuff .= " "; |
124
|
0
|
|
|
|
|
0
|
$i = 0; |
125
|
0
|
|
|
|
|
0
|
foreach my $currstuff(@currstuff) { |
126
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 4; |
127
|
0
|
0
|
|
|
|
0
|
$retbuff .= " " unless $i % 8; |
128
|
0
|
0
|
0
|
|
|
0
|
if($currstuff ge chr(0x20) and $currstuff le chr(0x7E)) { |
129
|
0
|
|
|
|
|
0
|
$retbuff .= $currstuff; |
130
|
|
|
|
|
|
|
} else { |
131
|
0
|
|
|
|
|
0
|
$retbuff .= "."; |
132
|
|
|
|
|
|
|
} |
133
|
0
|
|
|
|
|
0
|
$i++; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
0
|
|
|
|
|
0
|
return $retbuff; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub normalize($) { |
140
|
4
|
|
|
4
|
0
|
6
|
my $temp = shift; |
141
|
4
|
50
|
|
|
|
13
|
$temp =~ tr/ //d if $temp; |
142
|
4
|
50
|
|
|
|
21
|
return $temp ? lc($temp) : ""; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub tlv_decode($;$) { |
146
|
0
|
|
|
0
|
0
|
0
|
my($tlv, $tlvcnt) = @_; |
147
|
0
|
|
|
|
|
0
|
my($type, $len, $value, %retval); |
148
|
0
|
|
|
|
|
0
|
my $currtlv = 0; |
149
|
0
|
|
|
|
|
0
|
my $strpos = 0; |
150
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
0
|
my $retval = tlv; |
152
|
|
|
|
|
|
|
|
153
|
0
|
0
|
|
|
|
0
|
$tlvcnt = 0 unless $tlvcnt; |
154
|
0
|
|
0
|
|
|
0
|
while(length($tlv) >= 4 and (!$tlvcnt or $currtlv < $tlvcnt)) { |
|
|
|
0
|
|
|
|
|
155
|
0
|
|
|
|
|
0
|
($type, $len) = unpack("nn", $tlv); |
156
|
0
|
0
|
|
|
|
0
|
$len = 0x2 if $type == 0x13; |
157
|
0
|
|
|
|
|
0
|
$strpos += 4; |
158
|
0
|
|
|
|
|
0
|
substr($tlv, 0, 4) = ""; |
159
|
0
|
0
|
|
|
|
0
|
if($len) { |
160
|
0
|
|
|
|
|
0
|
($value) = substr($tlv, 0, $len, ""); |
161
|
|
|
|
|
|
|
} else { |
162
|
0
|
|
|
|
|
0
|
$value = ""; |
163
|
|
|
|
|
|
|
} |
164
|
0
|
|
|
|
|
0
|
$strpos += $len; |
165
|
0
|
0
|
|
|
|
0
|
$currtlv++ unless $type == 0; |
166
|
0
|
|
|
|
|
0
|
$retval->{$type} = $value; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
0
|
0
|
|
|
|
0
|
return $tlvcnt ? ($retval, $strpos) : $retval; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub tlv_encode($) { |
173
|
0
|
|
|
0
|
0
|
0
|
my $tlv = shift; |
174
|
0
|
|
|
|
|
0
|
my($buffer, $type, $value) = ("", 0, ""); |
175
|
|
|
|
|
|
|
|
176
|
0
|
0
|
0
|
|
|
0
|
confess "You must use a tied Net::OSCAR::TLV hash!" |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
177
|
|
|
|
|
|
|
unless defined($tlv) and ref($tlv) eq "HASH" and defined(tied(%$tlv)) and tied(%$tlv)->isa("Net::OSCAR::TLV"); |
178
|
0
|
|
|
|
|
0
|
while (($type, $value) = each %$tlv) { |
179
|
0
|
|
0
|
|
|
0
|
$value ||= ""; |
180
|
0
|
|
|
|
|
0
|
$buffer .= pack("nna*", $type, length($value), $value); |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
} |
183
|
0
|
|
|
|
|
0
|
return $buffer; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub send_error($$$$$;@) { |
187
|
0
|
|
|
0
|
0
|
0
|
my($oscar, $connection, $error, $desc, $fatal, @reqdata) = @_; |
188
|
0
|
|
|
|
|
0
|
$desc = sprintf $desc, @reqdata; |
189
|
0
|
|
|
|
|
0
|
$oscar->callback_error($connection, $error, $desc, $fatal); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub bltie(;$) { |
193
|
2
|
|
|
2
|
0
|
4
|
my $retval = {}; |
194
|
2
|
|
|
|
|
12
|
tie %$retval, "Net::OSCAR::Buddylist", @_; |
195
|
2
|
|
|
|
|
4
|
return $retval; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub signon_tlv($;$$) { |
199
|
0
|
|
|
0
|
0
|
|
my($session, $password, $key) = @_; |
200
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
|
my %protodata = ( |
202
|
|
|
|
|
|
|
screenname => $session->{screenname}, |
203
|
|
|
|
|
|
|
clistr => $session->{svcdata}->{clistr}, |
204
|
|
|
|
|
|
|
supermajor => $session->{svcdata}->{supermajor}, |
205
|
|
|
|
|
|
|
major => $session->{svcdata}->{major}, |
206
|
|
|
|
|
|
|
minor => $session->{svcdata}->{minor}, |
207
|
|
|
|
|
|
|
subminor => $session->{svcdata}->{subminor}, |
208
|
|
|
|
|
|
|
build => $session->{svcdata}->{build}, |
209
|
|
|
|
|
|
|
subbuild => $session->{svcdata}->{subbuild}, |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
|
212
|
0
|
0
|
|
|
|
|
if($session->{svcdata}->{hashlogin}) { |
213
|
0
|
|
|
|
|
|
$protodata{password} = encode_password($session, $password); |
214
|
|
|
|
|
|
|
} else { |
215
|
0
|
0
|
|
|
|
|
if($session->{auth_response}) { |
216
|
0
|
|
|
|
|
|
$protodata{auth_response} = delete $session->{auth_response}; |
217
|
0
|
0
|
|
|
|
|
$protodata{pass_is_hashed} = "" if delete $session->{pass_is_hashed}; |
218
|
|
|
|
|
|
|
} else { |
219
|
|
|
|
|
|
|
# As of AIM 5.5, the password can be MD5'd before |
220
|
|
|
|
|
|
|
# going into the things-to-cat-together-and-MD5. |
221
|
|
|
|
|
|
|
# This lets applications that store AIM passwords |
222
|
|
|
|
|
|
|
# store the MD5'd password. We do it by default |
223
|
|
|
|
|
|
|
# because, well, AIM for Windows does. We support |
224
|
|
|
|
|
|
|
# the old way to preserve compatibility with |
225
|
|
|
|
|
|
|
# our auth_challenge/auth_response API. |
226
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
$protodata{pass_is_hashed} = ""; |
228
|
0
|
0
|
|
|
|
|
my $hashpass = $session->{pass_is_hashed} ? $password : md5($password); |
229
|
|
|
|
|
|
|
|
230
|
0
|
|
|
|
|
|
$protodata{auth_response} = encode_password($session, $hashpass, $key); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
return %protodata; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub encode_password($$;$) { |
238
|
0
|
|
|
0
|
0
|
|
my($session, $password, $key) = @_; |
239
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
|
if(!$session->{svcdata}->{hashlogin}) { # Use new SNAC-based method |
241
|
0
|
|
|
|
|
|
my $md5 = Digest::MD5->new; |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
|
$md5->add($key); |
244
|
0
|
|
|
|
|
|
$md5->add($password); |
245
|
0
|
|
|
|
|
|
$md5->add("AOL Instant Messenger (SM)"); |
246
|
0
|
|
|
|
|
|
return $md5->digest(); |
247
|
|
|
|
|
|
|
} else { # Use old roasting method. Courtesy of SDiZ Cheng. |
248
|
0
|
|
|
|
|
|
my $ret = ""; |
249
|
0
|
|
|
|
|
|
my @pass = map {ord($_)} split(//, $password); |
|
0
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
|
251
|
0
|
|
|
|
|
|
my @encoding_table = map {hex($_)} qw( |
|
0
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
F3 26 81 C4 39 86 DB 92 71 A3 B9 E6 53 7A 95 7C |
253
|
|
|
|
|
|
|
); |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
for(my $i = 0; $i < length($password); $i++) { |
256
|
0
|
|
|
|
|
|
$ret .= chr($pass[$i] ^ $encoding_table[$i]); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
return $ret; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub send_versions($$;$) { |
264
|
0
|
|
|
0
|
0
|
|
my($connection, $send_tools, $server) = @_; |
265
|
0
|
|
|
|
|
|
my $conntype = $connection->{conntype}; |
266
|
0
|
|
|
|
|
|
my @services; |
267
|
|
|
|
|
|
|
|
268
|
0
|
0
|
0
|
|
|
|
if($conntype != CONNTYPE_BOS and !$server) { |
269
|
0
|
|
|
|
|
|
@services = (1, $conntype); |
270
|
|
|
|
|
|
|
} else { |
271
|
0
|
|
|
|
|
|
@services = sort {$b <=> $a} grep {not OSCAR_TOOLDATA()->{$_}->{nobos}} keys %{OSCAR_TOOLDATA()}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
|
my %protodata = (service => []); |
275
|
0
|
|
|
|
|
|
foreach my $service (@services) { |
276
|
0
|
|
|
|
|
|
my %service = ( |
277
|
|
|
|
|
|
|
service_id => $service, |
278
|
|
|
|
|
|
|
service_version => OSCAR_TOOLDATA->{$service}->{version} |
279
|
|
|
|
|
|
|
); |
280
|
0
|
0
|
|
|
|
|
if($send_tools) { |
281
|
0
|
|
|
|
|
|
$service{tool_id} = OSCAR_TOOLDATA->{$service}->{toolid}; |
282
|
0
|
|
|
|
|
|
$service{tool_version} = OSCAR_TOOLDATA->{$service}->{toolversion}; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
0
|
|
|
|
|
|
push @{$protodata{service}}, \%service; |
|
0
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
0
|
0
|
|
|
|
|
if($send_tools) { |
|
|
0
|
|
|
|
|
|
289
|
0
|
|
|
|
|
|
$connection->proto_send(protobit => "set_tool_versions", protodata => \%protodata, nopause => 1); |
290
|
|
|
|
|
|
|
} elsif($server) { |
291
|
0
|
|
|
|
|
|
$connection->proto_send(protobit => "host_versions", protodata => \%protodata, nopause => 1); |
292
|
|
|
|
|
|
|
} else { |
293
|
0
|
|
|
|
|
|
$connection->proto_send(protobit => "set_service_versions", protodata => \%protodata, nopause => 1); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# keys(%foo) in void context, the standard way of reseting |
298
|
|
|
|
|
|
|
# a hash iterator, appears to leak memory. |
299
|
|
|
|
|
|
|
# |
300
|
|
|
|
|
|
|
sub hash_iter_reset($) { |
301
|
0
|
|
|
0
|
0
|
|
while((undef, undef) = each(%{$_[0]})) {} |
|
0
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
1; |