You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
10 KiB
395 lines
10 KiB
package Net::OSCAR::Common; |
|
|
|
$VERSION = '0.62'; |
|
|
|
use strict; |
|
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION); |
|
use Scalar::Util; |
|
use Net::OSCAR::TLV; |
|
use Carp; |
|
require Exporter; |
|
@ISA = qw(Exporter); |
|
|
|
%EXPORT_TAGS = ( |
|
standard => [qw( |
|
ADMIN_TYPE_PASSWORD_CHANGE |
|
ADMIN_TYPE_EMAIL_CHANGE |
|
ADMIN_TYPE_SCREENNAME_FORMAT |
|
ADMIN_TYPE_ACCOUNT_CONFIRM |
|
ADMIN_ERROR_UNKNOWN |
|
ADMIN_ERROR_BADPASS |
|
ADMIN_ERROR_BADINPUT |
|
ADMIN_ERROR_BADLENGTH |
|
ADMIN_ERROR_TRYLATER |
|
ADMIN_ERROR_REQPENDING |
|
ADMIN_ERROR_CONNREF |
|
VISMODE_PERMITALL |
|
VISMODE_DENYALL |
|
VISMODE_PERMITSOME |
|
VISMODE_DENYSOME |
|
VISMODE_PERMITBUDS |
|
RATE_CLEAR |
|
RATE_ALERT |
|
RATE_LIMIT |
|
RATE_DISCONNECT |
|
GROUPPERM_OSCAR |
|
GROUPPERM_AOL |
|
OSCAR_SVC_AIM |
|
OSCAR_SVC_ICQ |
|
MTN_TYPING_DONE |
|
MTN_TEXT_TYPED |
|
MTN_TYPING_BEGUN |
|
)], |
|
loglevels => [qw( |
|
OSCAR_DBG_NONE |
|
OSCAR_DBG_WARN |
|
OSCAR_DBG_INFO |
|
OSCAR_DBG_SIGNON |
|
OSCAR_DBG_NOTICE |
|
OSCAR_DBG_DEBUG |
|
OSCAR_DBG_PACKETS |
|
)], |
|
all => [qw( |
|
OSCAR_DBG_NONE OSCAR_DBG_WARN OSCAR_DBG_INFO OSCAR_DBG_SIGNON OSCAR_DBG_NOTICE OSCAR_DBG_DEBUG OSCAR_DBG_PACKETS |
|
ADMIN_TYPE_PASSWORD_CHANGE ADMIN_TYPE_EMAIL_CHANGE ADMIN_TYPE_SCREENNAME_FORMAT ADMIN_TYPE_ACCOUNT_CONFIRM |
|
ADMIN_ERROR_UNKNOWN ADMIN_ERROR_BADPASS ADMIN_ERROR_BADINPUT ADMIN_ERROR_BADLENGTH ADMIN_ERROR_TRYLATER ADMIN_ERROR_REQPENDING ADMIN_ERROR_CONNREF |
|
VISMODE_PERMITALL VISMODE_DENYALL VISMODE_PERMITSOME VISMODE_DENYSOME VISMODE_PERMITBUDS RATE_CLEAR RATE_ALERT RATE_LIMIT RATE_DISCONNECT |
|
FLAP_CHAN_NEWCONN FLAP_CHAN_SNAC FLAP_CHAN_ERR FLAP_CHAN_CLOSE |
|
CONNTYPE_LOGIN CONNTYPE_BOS CONNTYPE_ADMIN CONNTYPE_CHAT CONNTYPE_CHATNAV |
|
MODBL_ACTION_ADD MODBL_ACTION_DEL MODBL_WHAT_BUDDY MODBL_WHAT_GROUP MODBL_WHAT_PERMIT MODBL_WHAT_DENY |
|
GROUPPERM_OSCAR GROUPPERM_AOL OSCAR_SVC_AIM OSCAR_SVC_ICQ |
|
MTN_TYPING_DONE MTN_TEXT_TYPED MTN_TYPING_BEGUN |
|
BUDTYPES |
|
ENCODING |
|
ERRORS |
|
randchars log_print log_printf hexdump normalize tlv_decode tlv_encode tlv send_error tlvtie bltie signon_tlv encode_password |
|
)] |
|
); |
|
@EXPORT_OK = map { @$_ } values %EXPORT_TAGS; |
|
|
|
use constant OSCAR_DBG_NONE => 0; |
|
use constant OSCAR_DBG_WARN => 1; |
|
use constant OSCAR_DBG_INFO => 2; |
|
use constant OSCAR_DBG_SIGNON => 3; |
|
use constant OSCAR_DBG_NOTICE => 4; |
|
use constant OSCAR_DBG_DEBUG => 6; |
|
use constant OSCAR_DBG_PACKETS => 10; |
|
|
|
use constant ADMIN_TYPE_PASSWORD_CHANGE => 1; |
|
use constant ADMIN_TYPE_EMAIL_CHANGE => 2; |
|
use constant ADMIN_TYPE_SCREENNAME_FORMAT => 3; |
|
use constant ADMIN_TYPE_ACCOUNT_CONFIRM => 4; |
|
|
|
use constant ADMIN_ERROR_UNKNOWN => 0; |
|
use constant ADMIN_ERROR_BADPASS => 1; |
|
use constant ADMIN_ERROR_BADINPUT => 2; |
|
use constant ADMIN_ERROR_BADLENGTH => 3; |
|
use constant ADMIN_ERROR_TRYLATER => 4; |
|
use constant ADMIN_ERROR_REQPENDING => 5; |
|
use constant ADMIN_ERROR_CONNREF => 6; |
|
|
|
use constant FLAP_CHAN_NEWCONN => 0x01; |
|
use constant FLAP_CHAN_SNAC => 0x02; |
|
use constant FLAP_CHAN_ERR => 0x03; |
|
use constant FLAP_CHAN_CLOSE => 0x04; |
|
|
|
use constant CONNTYPE_LOGIN => 0; |
|
use constant CONNTYPE_BOS => 0x2; |
|
use constant CONNTYPE_ADMIN => 0x7; |
|
use constant CONNTYPE_CHAT => 0xE; |
|
use constant CONNTYPE_CHATNAV => 0xD; |
|
|
|
use constant MODBL_ACTION_ADD => 0x1; |
|
use constant MODBL_ACTION_DEL => 0x2; |
|
|
|
use constant MODBL_WHAT_BUDDY => 0x1; |
|
use constant MODBL_WHAT_GROUP => 0x2; |
|
use constant MODBL_WHAT_PERMIT => 0x3; |
|
use constant MODBL_WHAT_DENY => 0x4; |
|
|
|
use constant VISMODE_PERMITALL => 0x1; |
|
use constant VISMODE_DENYALL => 0x2; |
|
use constant VISMODE_PERMITSOME => 0x3; |
|
use constant VISMODE_DENYSOME => 0x4; |
|
use constant VISMODE_PERMITBUDS => 0x5; |
|
|
|
use constant GROUP_PERMIT => 0x0002; |
|
use constant GROUP_DENY => 0x0003; |
|
|
|
use constant MTN_TYPING_DONE => 0x00; |
|
use constant MTN_TEXT_TYPED => 0x01; |
|
use constant MTN_TYPING_BEGUN => 0x02; |
|
|
|
use constant RATE_CLEAR => 1; |
|
use constant RATE_ALERT => 2; |
|
use constant RATE_LIMIT => 3; |
|
use constant RATE_DISCONNECT => 4; |
|
|
|
use constant GROUPPERM_OSCAR => 0x18; |
|
use constant GROUPPERM_AOL => 0x04; |
|
|
|
use constant OSCAR_SVC_AIM => ( |
|
host => 'login.oscar.aol.com', |
|
port => 5190, |
|
supermajor => 0x0109, |
|
major => 5, |
|
minor => 0, |
|
subminor => 0, |
|
build => 2938, |
|
subbuild => 0x9F, |
|
clistr => "AOL Instant Messenger (SM), version 5.0.2938/WIN32", |
|
hashlogin => 1, |
|
); |
|
use constant OSCAR_SVC_ICQ => ( # Courtesy of SDiZ Cheng |
|
host => 'login.icq.com', |
|
port => 5190, |
|
supermajor => 266, |
|
major => 4, |
|
minor => 63, |
|
subminor => 1, |
|
build => 3279, |
|
subbuild => 85, |
|
clistr => "ICQ Inc. - Product of ICQ (TM).200b.4.63.1.3279.85", |
|
hashlogin => 1, |
|
); |
|
|
|
use constant BUDTYPES => ("buddy", "group", "permit entry", "deny entry", "visibility/misc. data", "presence"); |
|
|
|
use constant ENCODING => 'text/aolrtf; charset="us-ascii"'; |
|
|
|
# I'm not 100% sure about error 29 |
|
use constant ERRORS => split(/\n/, <<EOF); |
|
Invalid error |
|
Invalid SNAC |
|
Sending too fast to host |
|
Sending too fast to client |
|
%s is not logged in, so the attempted operation (sending an IM, getting user information) was unsuccessful |
|
Service unavailable |
|
Service not defined |
|
Obsolete SNAC |
|
Not supported by host |
|
Not supported by client |
|
Refused by client |
|
Reply too big |
|
Responses lost |
|
Request denied |
|
Busted SNAC payload |
|
Insufficient rights |
|
%s is in your permit or deny list |
|
Too evil (sender) |
|
Too evil (receiver) |
|
User temporarily unavailable |
|
No match |
|
List overflow |
|
Request ambiguous |
|
Queue full |
|
Not while on AOL |
|
Unknown error 25 |
|
Unknown error 26 |
|
Unknown error 27 |
|
Unknown error 28 |
|
There have been too many recent signons from this address. Please wait a few minutes and try again. |
|
EOF |
|
|
|
sub randchars($) { |
|
my $count = shift; |
|
my $retval = ""; |
|
for(my $i = 0; $i < $count; $i++) { $retval .= chr(int(rand(256))); } |
|
return $retval; |
|
} |
|
|
|
sub log_print($$@) { |
|
my($obj, $level) = (shift, shift); |
|
my $session = exists($obj->{session}) ? $obj->{session} : $obj; |
|
return unless defined($session->{LOGLEVEL}) and $session->{LOGLEVEL} >= $level; |
|
|
|
my $message = ""; |
|
$message .= $obj->{description}. ": " if $obj->{description}; |
|
$message .= join("", @_). "\n"; |
|
|
|
if($session->{callbacks}->{log}) { |
|
$session->callback_log($level, $message); |
|
} else { |
|
$message = "(".$session->{screenname}.") $message" if $session->{SNDEBUG}; |
|
print STDERR $message; |
|
} |
|
} |
|
|
|
sub log_printf($$$@) { |
|
my($obj, $level, $fmtstr) = (shift, shift, shift); |
|
|
|
$obj->log_print($level, sprintf($fmtstr, @_)); |
|
} |
|
|
|
sub hexdump($) { |
|
my $stuff = shift; |
|
my $retbuff = ""; |
|
my @stuff; |
|
|
|
for(my $i = 0; $i < length($stuff); $i++) { |
|
push @stuff, substr($stuff, $i, 1); |
|
} |
|
|
|
return $stuff unless grep { $_ lt chr(0x20) or $_ gt chr(0x7E) } @stuff; |
|
while(@stuff) { |
|
my $i = 0; |
|
$retbuff .= "\n\t"; |
|
my @currstuff = splice(@stuff, 0, 16); |
|
|
|
foreach my $currstuff(@currstuff) { |
|
$retbuff .= " " unless $i % 4; |
|
$retbuff .= " " unless $i % 8; |
|
$retbuff .= sprintf "%02X ", ord($currstuff); |
|
$i++; |
|
} |
|
for(; $i < 16; $i++) { |
|
$retbuff .= " " unless $i % 4; |
|
$retbuff .= " " unless $i % 8; |
|
$retbuff .= " "; |
|
} |
|
|
|
$retbuff .= " "; |
|
$i = 0; |
|
foreach my $currstuff(@currstuff) { |
|
$retbuff .= " " unless $i % 4; |
|
$retbuff .= " " unless $i % 8; |
|
if($currstuff ge chr(0x20) and $currstuff le chr(0x7E)) { |
|
$retbuff .= $currstuff; |
|
} else { |
|
$retbuff .= "."; |
|
} |
|
$i++; |
|
} |
|
} |
|
return $retbuff; |
|
} |
|
|
|
sub normalize($) { |
|
my $temp = shift; |
|
$temp =~ tr/ //d if $temp; |
|
return $temp ? lc($temp) : ""; |
|
} |
|
|
|
sub tlv_decode($;$) { |
|
my($tlv, $tlvcnt) = @_; |
|
my($type, $len, $value, %retval); |
|
my $currtlv = 0; |
|
my $strpos = 0; |
|
|
|
tie %retval, "Net::OSCAR::TLV"; |
|
|
|
$tlvcnt = 0 unless $tlvcnt; |
|
while(length($tlv) >= 4 and (!$tlvcnt or $currtlv < $tlvcnt)) { |
|
($type, $len) = unpack("nn", $tlv); |
|
$len = 0x2 if $type == 0x13; |
|
$strpos += 4; |
|
substr($tlv, 0, 4) = ""; |
|
if($len) { |
|
($value) = substr($tlv, 0, $len, ""); |
|
} else { |
|
$value = ""; |
|
} |
|
$strpos += $len; |
|
$currtlv++ unless $type == 0; |
|
$retval{$type} = $value; |
|
} |
|
|
|
return $tlvcnt ? (\%retval, $strpos) : \%retval; |
|
} |
|
|
|
sub tlv(@) { |
|
my %tlv = (); |
|
tie %tlv, "Net::OSCAR::TLV"; |
|
while(@_) { my($key, $value) = (shift, shift); $tlv{$key} = $value; } |
|
return tlv_encode(\%tlv); |
|
} |
|
|
|
sub tlv_encode($) { |
|
my $tlv = shift; |
|
my($buffer, $type, $value) = ("", 0, ""); |
|
|
|
confess "You must use a tied Net::OSCAR::TLV hash!" unless defined($tlv) and ref($tlv) eq "HASH" and defined(%$tlv) and tied(%$tlv)->isa("Net::OSCAR::TLV"); |
|
while (($type, $value) = each %$tlv) { |
|
$value ||= ""; |
|
$buffer .= pack("nna*", $type, length($value), $value); |
|
|
|
} |
|
return $buffer; |
|
} |
|
|
|
sub send_error($$$$$;@) { |
|
my($oscar, $connection, $error, $desc, $fatal, @reqdata) = @_; |
|
$desc = sprintf $desc, @reqdata; |
|
$oscar->callback_error($connection, $error, $desc, $fatal); |
|
} |
|
|
|
sub bltie(;$) { |
|
my $retval = {}; |
|
tie %$retval, "Net::OSCAR::Buddylist", @_; |
|
return $retval; |
|
} |
|
|
|
sub tlvtie(;$) { |
|
my $retval = {}; |
|
tie %$retval, "Net::OSCAR::TLV", shift; |
|
return $retval; |
|
} |
|
|
|
sub signon_tlv($;$$) { |
|
my($session, $password, $key) = @_; |
|
|
|
my %tlv = ( |
|
0x01 => $session->{screenname}, |
|
0x03 => $session->{svcdata}->{clistr}, |
|
0x16 => pack("n", $session->{svcdata}->{supermajor}), |
|
0x17 => pack("n", $session->{svcdata}->{major}), |
|
0x18 => pack("n", $session->{svcdata}->{minor}), |
|
0x19 => pack("n", $session->{svcdata}->{subminor}), |
|
0x1A => pack("n", $session->{svcdata}->{build}), |
|
0x14 => pack("N", $session->{svcdata}->{subbuild}), |
|
0x0F => "en", # lang |
|
0x0E => "us", # country |
|
); |
|
|
|
if($session->{svcdata}->{hashlogin}) { |
|
$tlv{0x02} = encode_password($session, $password); |
|
} else { |
|
if($session->{auth_response}) { |
|
($tlv{0x25}) = delete $session->{auth_response}; |
|
} else { |
|
$tlv{0x25} = encode_password($session, $password, $key); |
|
} |
|
$tlv{0x4A} = pack("C", 1); |
|
} |
|
|
|
return %tlv; |
|
} |
|
|
|
sub encode_password($$;$) { |
|
my($session, $password, $key) = @_; |
|
|
|
if(!$session->{svcdata}->{hashlogin}) { # Use new SNAC-based method |
|
my $md5 = Digest::MD5->new; |
|
|
|
$md5->add($key); |
|
$md5->add($password); |
|
$md5->add("AOL Instant Messenger (SM)"); |
|
return $md5->digest(); |
|
} else { # Use old roasting method. Courtesy of SDiZ Cheng. |
|
my $ret = ""; |
|
my @pass = map {ord($_)} split(//, $password); |
|
|
|
my @encoding_table = map {hex($_)} qw( |
|
F3 26 81 C4 39 86 DB 92 71 A3 B9 E6 53 7A 95 7C |
|
); |
|
|
|
for(my $i = 0; $i < length($password); $i++) { |
|
$ret .= chr($pass[$i] ^ $encoding_table[$i]); |
|
} |
|
|
|
return $ret; |
|
} |
|
} |
|
|
|
|
|
1;
|
|
|