line
stmt
bran
cond
sub
pod
time
code
1
package Net::OSCAR;
2
BEGIN {
3
4
4
145601
$Net::OSCAR::VERSION = '1.928';
4
}
5
6
$REVISION = '$Revision$';
7
8
=pod
9
10
=head1 NAME
11
12
Net::OSCAR - Implementation of AOL's OSCAR protocol for instant messaging (for interacting with AIM a.k.a. AOL IM a.k.a. AOL Instant Messenger - and ICQ, too!)
13
14
=head1 VERSION
15
16
version 1.928
17
18
=head1 SYNOPSIS
19
20
use Net::OSCAR qw(:standard);
21
22
sub im_in {
23
my($oscar, $sender, $message, $is_away) = @_;
24
print "[AWAY] " if $is_away;
25
print "$sender: $message\n";
26
}
27
28
$oscar = Net::OSCAR->new();
29
$oscar->set_callback_im_in(\&im_in);
30
$oscar->signon($screenname, $password);
31
while(1) {
32
$oscar->do_one_loop();
33
# Do stuff
34
}
35
36
=head1 INSTALLATION
37
38
=head2 HOW TO INSTALL
39
40
perl Build.PL
41
perl Build
42
perl Build test
43
perl Build install
44
45
See C for details.
46
Note that this requires that you have the perl module Module::Build installed.
47
If you don't, the traditional C
48
should still work.
49
50
=head2 DEPENDENCIES
51
52
This modules requires C and C. C is needed
53
to run the test suite, and C is needed to generate the XML parse tree
54
which is shipped with released versions.
55
56
=head1 INTRODUCTION
57
58
=head2 ABSTRACT
59
60
C implements the OSCAR protocol which is used by AOL's AOL Instant
61
Messenger service. To use the module, you create a C object,
62
register some functions as handlers for various events by using the module's
63
callback mechanism, and then continually make calls to the module's event
64
processing methods.
65
66
You probably want to use the C<:standard> parameter when importing this module
67
in order to have a few important constants added to your namespace. See
68
L<"CONSTANTS"> below for a list of the constants exported by the C<:standard> tag.
69
70
No official documentation exists for the OSCAR protocol, so it had to be figured
71
out by analyzing traffic generated by AOL's official AOL Instant Messenger client.
72
Source code from the Gaim client, the protocol analysis provided by the Ethereal
73
network sniffer, and the Alexander Shutko's website
74
Ehttp://iserverd1.khstu.ru/oscar/E were also used as references.
75
76
This module strives to be as compatible with C as possible at the API level, but some
77
protocol-level differences prevent total compatibility. The TOC protocol implemented
78
by C is simpler than OSCAR and has official reference documentation from AOL,
79
but it only provides a small subset of the full C functionality.
80
See the section on L for more information.
81
82
=head2 EVENT PROCESSING OVERVIEW
83
84
Event processing is the implementation of C within the framework of your
85
program, so that your program can respond to things happening on the OSCAR servers while
86
still doing everything else that you need it to do, such as accepting user input. There are three main ways for the module to handle event processing. The simplest is to
87
call the L method, which performs a C call on all the object's
88
sockets and reads incoming commands from the OSCAR server on any connections which
89
have them. The C call has a default timeout of 0.01 seconds which can
90
be adjusted using the L method. This means that every time you call L,
91
it will pause for that interval if there are no messages from the OSCAR server.
92
If you need lower overhead, want better performance, or need to handle many Net::OSCAR objects and/or other files and sockets
93
at once, see L below.
94
95
=head2 FUNCTIONALITY
96
97
C pretends to be WinAIM 5.5.3595. It supports remote buddylists
98
including permit and deny settings. It also supports chat, buddy icons,
99
and extended status messages. At the present time, setting and retrieving of
100
directory information is not supported; nor are email privacy settings,
101
voice chat, stock ticker, file transfer, direct IM, and many other of the
102
official AOL Instant Messenger client's features.
103
104
=head2 TERMINOLOGY
105
106
When you sign on with the OSCAR service, you are establishing an OSCAR session.
107
108
=head2 CALLBACKS
109
110
C uses a callback mechanism to notify you about different events.
111
A callback is a function provided by you which C will call
112
when a certain event occurs. To register a callback, calling the C method
113
with a code reference as a parameter. For instance, you might call
114
C<$oscar-Eset_callback_error(\&got_error);>. Your callback function will
115
be passed parameters which are different for each callback type (and are
116
documented below). The first parameter to each callback function will be
117
the C object which generated the callback. This is useful
118
when using multiple C objects.
119
120
=head1 REFERENCE
121
122
=cut
123
124
4
4
117
use 5.006_001;
4
15
4
160
125
4
4
29
use strict;
4
7
4
279
126
4
4
20
use vars qw($REVISION @ISA @EXPORT_OK %EXPORT_TAGS $NODESTROY);
4
7
4
355
127
4
4
20
use Carp;
4
7
4
341
128
4
4
22
use Scalar::Util qw(weaken);
4
7
4
313
129
4
4
126
use Digest::MD5 qw(md5);
4
8
4
205
130
4
4
5766
use Socket;
4
26617
4
2503
131
4
4
3165
use Net::OSCAR::Common qw(:all);
4
11
4
1843
132
4
4
2312
use Net::OSCAR::Constants;
4
12
4
560
133
4
4
2214
use Net::OSCAR::Utility;
4
11
4
681
134
4
4
2416
use Net::OSCAR::Connection;
4
15
4
160
135
4
4
44
use Net::OSCAR::Callbacks;
4
8
4
73
136
4
4
22
use Net::OSCAR::TLV;
4
9
4
270
137
4
4
25
use Net::OSCAR::Buddylist;
4
6
4
111
138
4
4
25
use Net::OSCAR::Screenname;
4
7
4
96
139
4
4
22
use Net::OSCAR::_BLInternal;
4
9
4
106
140
4
4
21
use Net::OSCAR::XML;
4
8
4
61892
141
142
$NODESTROY = 0;
143
144
require Exporter;
145
@ISA = qw(Exporter);
146
@EXPORT_OK = @Net::OSCAR::Common::EXPORT_OK;
147
%EXPORT_TAGS = %Net::OSCAR::Common::EXPORT_TAGS;
148
149
Net::OSCAR::XML::load_xml();
150
151
=pod
152
153
=head2 BASIC FUNCTIONALITY
154
155
=head3 METHODS
156
157
=over 4
158
159
=item new ([capabilities =E CAPABILITIES], [rate_manage =E RATE_MANAGE_MODE])
160
161
Creates a new C object. You may optionally
162
pass a hash to set some parameters for the object.
163
164
=over 4
165
166
=item capabilities
167
168
A listref of optional features that your client supports.
169
Valid capabilities are:
170
171
=over 4
172
173
=item extended_status
174
175
iChat-style extended status messages
176
177
=item buddy_icons
178
179
=item file_transfer
180
181
=item file_sharing
182
183
=item typing_status
184
185
Typing status notification
186
187
=item buddy_list_transfer
188
189
=back
190
191
=item rate_manage
192
193
Which mechanism will your application be using to deal with
194
the sending rates which the server enforces on the client?
195
See L<"RATE LIMIT OVERVIEW"> for more information on the subject.
196
197
=over 4
198
199
=item OSCAR_RATE_MANAGE_NONE
200
201
=item OSCAR_RATE_MANAGE_AUTO
202
203
=item OSCAR_RATE_MANAGE_MANUAL
204
205
=back
206
207
=back
208
209
$oscar = Net::OSCAR->new(capabilities => [qw(extended_status typing_status)], rate_manage => OSCAR_RATE_MANAGE_AUTO);
210
211
=cut
212
213
sub new($) {
214
0
0
0
my $class = ref($_[0]) || $_[0] || "Net::OSCAR";
215
0
shift;
216
217
0
my $self = {
218
options => {},
219
_parameters => [@_]
220
};
221
0
bless $self, $class;
222
223
0
my(%parameters) = @_;
224
0
0
if(my($badparam) = grep { $_ ne "capabilities" and $_ ne "rate_manage" } keys %parameters) {
0
0
225
0
croak "Invalid parameter '$badparam' passed to Net::OSCAR::new.";
226
}
227
0
0
if($parameters{capabilities}) {
228
0
0
0
if(my($badcap) = grep { $_ ne "extended_status" and $_ ne "buddy_icons" and $_ ne "file_transfer" and $_ ne "file_sharing" and $_ ne "typing_status" and $_ ne "file_transfer" and $_ ne "buddy_list_transfer" } @{$parameters{capabilities}}) {
0
0
0
0
0
0
0
229
0
croak "Invalid capability '$badcap' passed to Net::OSCAR::new.";
230
}
231
}
232
0
0
if($parameters{rate_manage}) {
233
0
0
0
if($parameters{rate_manage} < OSCAR_RATE_MANAGE_NONE or $parameters{rate_manage} > OSCAR_RATE_MANAGE_MANUAL) {
0
234
0
croak "Invalid rate_manage value '$parameters{rate_manage}' passed to Net::OSCAR::new.";
235
} elsif($parameters{rate_manage} == OSCAR_RATE_MANAGE_AUTO) {
236
0
croak "OSCAR_RATE_MANAGE_AUTO hasn't been implemented yet!";
237
} else {
238
0
$self->{rate_manage_mode} = $parameters{rate_manage};
239
0
0
if($self->{rate_manage_mode} != OSCAR_RATE_MANAGE_NONE) {
240
0
require Net::OSCAR::MethodInfo;
241
}
242
}
243
} else {
244
0
$self->{rate_manage_mode} = OSCAR_RATE_MANAGE_NONE;
245
}
246
247
0
$self->{LOGLEVEL} = OSCAR_DBG_WARN;
248
0
$self->{SNDEBUG} = 0;
249
0
$self->{__BLI_locked} = 0;
250
0
$self->{__BLI_commit_later} = 0;
251
252
0
$self->{description} = "OSCAR session";
253
0
$self->{userinfo} = bltie;
254
0
$self->{services} = tlv;
255
0
$self->{svcqueues} = tlv;
256
0
$self->{listener} = undef;
257
0
$self->{rv_proposals} = {};
258
0
$self->{pass_is_hashed} = 0;
259
0
$self->{stealth} = 0;
260
0
$self->{icq_meta_info_cache} = {};
261
0
$self->{ip} = 0;
262
263
0
$self->{ft_ip} = undef;
264
0
$self->{rv_neg_mode} = OSCAR_RV_AUTO;
265
0
$self->{bl_limits} = {
266
buddies => 0,
267
groups => 0,
268
permits => 0,
269
denies => 0
270
};
271
272
0
$self->{timeout} = 0.01;
273
0
$self->{capabilities} = {};
274
275
0
0
if($parameters{capabilities}) {
276
0
$self->{capabilities}->{$_} = 1 foreach @{$parameters{capabilities}};
0
277
}
278
279
# Set default callbacks
280
0
$self->set_callback_snac_unknown(\&Net::OSCAR::Callbacks::default_snac_unknown);
281
282
0
return $self;
283
}
284
285
=pod
286
287
=item signon (HASH)
288
289
=item signon (SCREENNAME, PASSWORD[, HOST, PORT]
290
291
Sign on to the OSCAR service. You can specify an
292
alternate host/port to connect to. The default is
293
login.oscar.aol.com port 5190.
294
295
The non-hash form of C is obsolete and is only provided for compatibility with C.
296
If you use a hash to pass parameters to this function, here are the valid keys:
297
298
=over 4
299
300
=item screenname
301
302
=item password
303
304
Screenname and password are mandatory. The other keys are optional.
305
In the special case of password being present but undefined, the
306
auth_challenge callback will be used - see L<"auth_challenge"> for details.
307
308
=item stealth
309
310
Use this to sign on with stealth mode activated. Using this, as opposed
311
to signon on without this setting and then calling L<"set_stealth">, will prevent
312
the user from showing as online for a brief interval after signon. See L<"set_stealth">
313
for information about stealth mode.
314
315
=item pass_is_hashed
316
317
If you want to give Net::OSCAR the MD5 hash of the password instead of the password
318
itself, use the MD5'd password in the password key and also set this key. The
319
benefit of this is that, if your application saves user passwords, you can save
320
them in hashed form and don't need to store the plaintext.
321
322
=item local_ip
323
324
If you have more than one IP address with a route to the internet, this
325
parameter can be used to specify which to use as the source IP for outgoing
326
connections.
327
328
=item local_port
329
330
This controls which port Net::OSCAR will listen on for incoming direct connections.
331
If not specified, a random port will be selected.
332
333
=item host
334
335
=item port
336
337
=item proxy_type
338
339
Either "SOCKS4", "SOCKS5", "HTTP", or HTTPS. This and C must be specified if you wish to use a proxy.
340
C, C, C are optional. Note that proxy support
341
is considered experimental. You will need to have the C module installed for
342
SOCKS proxying or the C module installed for HTTP proxying.
343
344
=item proxy_host
345
346
=item proxy_port
347
348
=item proxy_username
349
350
=item proxy_password
351
352
=back
353
354
If the screenname is all-numeric, it will automatically be treated
355
as an ICQ UIN instead of an AIM screenname.
356
357
=cut
358
359
sub signon($@) {
360
0
0
my($self, $password, $host, %args);
361
0
$self = shift;
362
363
# Determine whether caller is using hash-method or old method of passing parms.
364
# Note that this breaks if caller passes in both a host and a port using the old way.
365
# But hey, that's why it's deprecated!
366
0
0
if(@_ < 3) {
367
0
0
$args{screenname} = shift @_ or return $self->crapout($self->{services}->{0+CONNTYPE_BOS}, "You must specify a username to sign on with!");
368
0
0
$args{password} = shift @_ or return $self->crapout($self->{services}->{0+CONNTYPE_BOS}, "You must specify a password to sign on with!");;
369
0
0
$args{host} = shift @_ if @_;
370
0
0
$args{port} = shift @_ if @_;
371
} else {
372
0
%args = @_;
373
0
0
0
return $self->crapout($self->{services}->{0+CONNTYPE_BOS}, "You must specify a username and password to sign on with!") unless $args{screenname} and exists($args{password});
374
}
375
376
0
my %defaults = OSCAR_SVC_AIM;
377
0
0
%defaults = OSCAR_SVC_ICQ if $args{screenname} =~ /^\d+$/;
378
0
foreach my $key(keys %defaults) {
379
0
0
$args{$key} ||= $defaults{$key};
380
}
381
0
0
0
return $self->crapout($self->{services}->{0+CONNTYPE_BOS}, "MD5 authentication not available for this service (you must define a password.)") if !defined($args{password}) and $args{hashlogin};
382
0
$self->{screenname} = Net::OSCAR::Screenname->new(\$args{screenname});
383
384
# We set BOS to the login connection so that our error handlers pick up errors on this connection as fatal.
385
0
0
$args{host} ||= "login.oscar.aol.com";
386
0
0
$args{port} ||= 5190;
387
388
389
0
($self->{screenname}, $password, $host, $self->{port},
390
$self->{proxy_type}, $self->{proxy_host}, $self->{proxy_port},
391
$self->{proxy_username}, $self->{proxy_password}, $self->{local_ip},
392
$self->{local_port}, $self->{pass_is_hashed}, $self->{stealth}) =
393
delete @args{qw(screenname password host port proxy_type proxy_host proxy_port proxy_username proxy_password local_ip local_port pass_is_hashed stealth)};
394
395
0
$self->{svcdata} = \%args;
396
397
0
0
if(defined($self->{proxy_type})) {
398
0
$self->{proxy_type} = uc($self->{proxy_type});
399
0
0
die "You must specify proxy_host if proxy_type is specified!\n" unless $self->{proxy_host};
400
0
0
0
if($self->{proxy_type} eq "HTTP" or $self->{proxy_type} eq "HTTPS") {
401
0
$self->{http_proxy} = LWP::UserAgent->new(
402
agent => "Mozilla/4.08 [en] (WinNT; U ;Nav)",
403
keep_alive => 1,
404
timeout => 30,
405
);
406
0
0
0
die "HTTPS not supported by your LWP::UserAgent\n" if $self->{proxy_type} eq "HTTPS" and !$self->{http_proxy}->is_protocol_supported("https");
407
408
0
my $proxyurl = lc($self->{proxy_type}) . "://$self->{proxy_host}";
409
0
0
$proxyurl .= ":$self->{proxy_port}" if $self->{proxy_port};
410
0
$proxyurl .= "/";
411
0
$self->{http_proxy}->proxy('http', $proxyurl);
412
}
413
}
414
415
0
$self->{services}->{0+CONNTYPE_BOS} = $self->addconn(auth => $password, conntype => CONNTYPE_LOGIN, description => "login", peer => $host);
416
}
417
418
=pod
419
420
=item signoff
421
422
Sign off from the OSCAR service.
423
424
=cut
425
426
sub signoff($) {
427
0
0
my $self = shift;
428
0
foreach my $connection(@{$self->{connections}}) {
0
429
0
$self->delconn($connection);
430
}
431
0
my $screenname = $self->{screenname};
432
0
%$self = ();
433
0
$self->{screename} = $screenname; # Useful for post-mortem processing in multiconnection apps
434
}
435
436
=pod
437
438
=back
439
440
=head3 CALLBACKS
441
442
=over 4
443
444
=item signon_done (OSCAR)
445
446
Called when the user is completely signed on to the service.
447
448
=back
449
450
=head2 BUDDIES AND BUDDYLISTS
451
452
See also L<"OTHER USERS"> for methods which pertain to any other user, regardless of
453
whether they're on the buddylist or not.
454
455
=head3 METHODS
456
457
=over 4
458
459
=item findbuddy (BUDDY)
460
461
In scalar context, returns the name of the group that BUDDY is in, or undef if
462
BUDDY could not be found in any group. If BUDDY is in multiple
463
groups, will return the first one we find.
464
465
In list context, returns a two-element list consisting of the group
466
name followed by the group hashref (or the empty list of the buddy
467
is not found.)
468
469
=cut
470
471
sub findbuddy($$) {
472
0
0
my($self, $buddy) = @_;
473
474
0
while(my($grpname, $group) = each(%{$self->{buddies}})) {
0
475
next if
476
0
0
0
$grpname eq "__BLI_DIRTY" or
0
0
477
!$group or
478
not $group->{members}->{$buddy} or
479
$group->{members}->{$buddy}->{__BLI_DELETED};
480
481
0
hash_iter_reset(\%{$self->{buddies}}); # Reset the iterator
0
482
0
0
return wantarray ? ($grpname, $group) : $grpname;
483
}
484
0
return;
485
}
486
487
=pod
488
489
=item commit_buddylist
490
491
Sends your modified buddylist to the OSCAR server. Changes to the buddylist
492
won't actually take effect until this method is called. Methods that change
493
the buddylist have a warning about needing to call this method in their
494
documentation. After calling this method, your program B not call
495
it again until either the L or L callbacks
496
are received.
497
498
=item rollback_buddylist
499
500
Revert changes you've made to the buddylist, assuming you haven't called
501
L<"commit_buddylist"> since making them.
502
503
=item reorder_groups (GROUPS)
504
505
Changes the ordering of the groups in your buddylist. Call L<"commit_buddylist"> to
506
save the
507
new order on the OSCAR server.
508
509
=item reorder_buddies (GROUP, BUDDIES)
510
511
Changes the ordering of the buddies in a group on your buddylist.
512
Call L<"commit_buddylist"> to save the new order on the OSCAR server.
513
514
=cut
515
516
sub commit_buddylist($) {
517
0
0
my($self) = shift;
518
0
0
return must_be_on($self) unless $self->{is_on};
519
520
0
0
if($self->{__BLI_locked}) {
521
# If the server is modifying the buddylist,
522
# wait until its done to do the commit.
523
0
$self->{__BLI_commit_later} = 1;
524
0
return;
525
}
526
527
0
Net::OSCAR::_BLInternal::NO_to_BLI($self);
528
529
# If user set icon to same as old icon, server won't request an upload.
530
# Send a buddy_icon_uploaded callback anyway.
531
0
0
0
if($self->{icon_md5sum_old} and $self->{icon_md5sum} eq $self->{icon_md5sum_old}) {
532
0
$self->callback_buddy_icon_uploaded();
533
}
534
535
0
delete $self->{icon_md5sum_old};
536
}
537
538
sub rollback_buddylist($) {
539
0
0
my($self) = shift;
540
0
0
return must_be_on($self) unless $self->{is_on};
541
0
Net::OSCAR::_BLInternal::BLI_to_NO($self);
542
}
543
544
sub reorder_groups($@) {
545
0
0
my $self = shift;
546
0
0
return must_be_on($self) unless $self->{is_on};
547
0
my @groups = @_;
548
0
tied(%{$self->{buddies}})->setorder(@groups);
0
549
0
$self->{buddies}->{__BLI_DIRTY} = 1;
550
}
551
552
sub reorder_buddies($$@) {
553
0
0
my $self = shift;
554
0
0
return must_be_on($self) unless $self->{is_on};
555
0
my $group = shift;
556
0
my @buddies = @_;
557
0
tied(%{$self->{buddies}->{$group}->{members}})->setorder(@buddies);
0
558
0
$self->{buddies}->{$group}->{__BLI_DIRTY} = 1;
559
}
560
561
=pod
562
563
=item rename_group (OLDNAME, NEWNAME)
564
565
Renames a group. Call L<"commit_buddylist"> for the change to take effect.
566
567
=item add_buddy (GROUP, BUDDIES)
568
569
Adds buddies to the given group on your buddylist. If the group does not exist,
570
it will be created. Call L<"commit_buddylist"> for the change to take effect.
571
572
=item remove_buddy (GROUP, BUDDIES)
573
574
See L.
575
576
=item add_group (GROUP)
577
578
Creates a new, empty group. Call L<"commit_buddylist"> for the change to take effect.
579
580
=item remove_group (GROUP)
581
582
See L. Any buddies in the group will be removed from the group first.
583
584
=cut
585
586
sub rename_group($$$) {
587
0
0
my($self, $oldgroup, $newgroup) = @_;
588
0
0
return must_be_on($self) unless $self->{is_on};
589
0
0
return send_error($self, $self->{services}->{0+CONNTYPE_BOS}, 0, "That group does not exist", 0) unless exists $self->{buddies}->{$oldgroup};
590
591
0
$self->{buddies}->{$newgroup} = $self->{buddies}->{$oldgroup};
592
0
$self->{buddies}->{$newgroup}->{__BLI_DIRTY} = 1;
593
0
delete $self->{buddies}->{$oldgroup};
594
}
595
596
sub add_buddy($$@) {
597
0
0
my($self, $group, @buddies) = @_;
598
0
$self->mod_buddylist(MODBL_ACTION_ADD, MODBL_WHAT_BUDDY, $group, @buddies);
599
}
600
601
sub remove_buddy($$@) {
602
0
0
my($self, $group, @buddies) = @_;
603
0
$self->mod_buddylist(MODBL_ACTION_DEL, MODBL_WHAT_BUDDY, $group, @buddies);
604
}
605
606
sub add_group($$) {
607
0
0
my($self, $group) = @_;
608
0
$self->mod_buddylist(MODBL_ACTION_ADD, MODBL_WHAT_GROUP, $group);
609
}
610
611
sub remove_group($$) {
612
0
0
my($self, $group) = @_;
613
0
0
return send_error($self, $self->{services}->{0+CONNTYPE_BOS}, 0, "That group does not exist", 0) unless exists $self->{buddies}->{$group};
614
0
0
$self->remove_buddy($group, $self->buddies($group)) if $self->buddies($group);
615
0
$self->mod_buddylist(MODBL_ACTION_DEL, MODBL_WHAT_GROUP, $group);
616
}
617
618
619
=item groups
620
621
Returns a list of groups in the user's buddylist.
622
623
=item buddies (GROUP)
624
625
Returns the names of the buddies in the specified group in the user's buddylist.
626
The names may not be formatted - that is, they may have spaces and capitalization
627
removed. The names are C objects, so you don't have to
628
worry that they're case and whitespace insensitive when using them for comparison.
629
630
=item buddy (BUDDY[, GROUP])
631
632
Returns information about a buddy on the user's buddylist. This information is
633
a hashref as per L below.
634
635
=cut
636
637
0
0
0
sub groups($) { return grep {$_ and $_ ne "__BLI_DIRTY"} keys %{shift->{buddies}}; }
0
0
638
sub buddies($;$) {
639
0
0
my($self, $group) = @_;
640
641
0
0
if($group) {
642
0
my $grp = $self->{buddies}->{$group};
643
644
0
return grep {
645
0
not $grp->{members}->{$_}->{__BLI_DELETED}
646
0
} keys %{$grp->{members}};
647
}
648
649
0
my @buddies;
650
0
while(my($grpname, $group) = each(%{$self->{buddies}})) {
0
651
0
0
0
next if !$grpname or $grpname eq "__BLI_DIRTY";
652
0
push @buddies, grep { not $group->{members}->{$_}->{__BLI_DELETED} } keys %{$group->{members}};
0
0
653
}
654
0
return @buddies;
655
}
656
sub buddy($$;$) {
657
0
0
my($self, $buddy, $grpname) = @_;
658
0
my $group;
659
660
0
0
if(!$grpname) {
661
0
0
($grpname, $group) = $self->findbuddy($buddy) or return;
662
} else {
663
0
0
$group = $self->{buddies}->{$grpname} or return;
664
}
665
666
0
my $ret = $group->{members}->{$buddy};
667
0
0
return $ret->{__BLI_DELETED} ? undef : $ret;
668
669
0
0
return $self->{userinfo}->{$buddy} || undef;
670
}
671
672
=pod
673
674
=item set_buddy_comment (GROUP, BUDDY[, COMMENT])
675
676
Set a brief comment about a buddy. You must call L<"commit_buddylist"> to save
677
the comment to the server. If COMMENT is undefined, the comment is
678
deleted.
679
680
=item set_buddy_alias (GROUP, BUDDY[, ALIAS])
681
682
Set an alias for a buddy. You must call L<"commit_buddylist"> to save
683
the comment to the server. If ALIAS is undefined, the alias is
684
deleted.
685
686
=cut
687
688
sub set_buddy_comment($$$;$) {
689
0
0
my($self, $group, $buddy, $comment) = @_;
690
0
0
return must_be_on($self) unless $self->{is_on};
691
692
0
my $bud = $self->{buddies}->{$group}->{members}->{$buddy};
693
0
$bud->{comment} = $comment;
694
0
$bud->{__BLI_DIRTY} = 1;
695
}
696
697
sub set_buddy_alias($$$;$) {
698
0
0
my($self, $group, $buddy, $alias) = @_;
699
0
0
return must_be_on($self) unless $self->{is_on};
700
701
0
my $bud = $self->{buddies}->{$group}->{members}->{$buddy};
702
0
$bud->{alias} = $alias;
703
0
$bud->{__BLI_DIRTY} = 1;
704
}
705
706
=pod
707
708
=item buddylist_limits
709
710
Returns a hash containing the maximum number of buddylist entries
711
of various types. The keys in the hash are:
712
713
=over 4
714
715
=item *
716
717
buddies
718
719
=item *
720
721
groups
722
723
=item *
724
725
permits
726
727
=item *
728
729
denies
730
731
=back
732
733
So, the maximum number of buddies allowed on a buddylist is stored in the C key.
734
Please note that buddylist storage has some overhead, so the actual number of items you
735
can have on a buddylist may be slightly less than advertised.
736
737
If the OSCAR server did not inform us of the limits, values of 0 will be used.
738
739
=cut
740
741
0
0
sub buddylist_limits($) { return %{shift->{bl_limits}}; }
0
742
743
=pod
744
745
=back
746
747
=head3 CALLBACKS
748
749
=over 4
750
751
=item buddy_in (OSCAR, SCREENNAME, GROUP, BUDDY DATA)
752
753
SCREENNAME (in buddy group GROUP) has signed on, or their information has
754
changed. BUDDY DATA is the same as that returned by the L method.
755
756
=item buddy_out (OSCAR, SCREENNAME, GROUP)
757
758
Called when a buddy has signed off (or added us to their deny list.)
759
760
=item buddylist_error (OSCAR, ERROR, WHAT)
761
762
This is called when there is an error commiting changes to the buddylist.
763
C is the error number. C is a string describing which buddylist
764
change failed. C will revert the failed change to
765
its state before C was called. Note that the
766
buddylist contains information other than the user's buddies - see
767
any method which says you need to call C to have its
768
changes take effect.
769
770
=item buddylist_ok (OSCAR)
771
772
This is called when your changes to the buddylist have been successfully commited.
773
774
=item buddylist_changed (OSCAR, CHANGES)
775
776
This is called when your buddylist is changed by the server.
777
The most common reason for this to happen is if the screenname you are signed
778
on with is also signed on somewhere else, and the buddylist is changed in
779
the other session.
780
781
Currently, only changes to buddies and groups will be listed in C.
782
Changes to privacy settings and any other portions of the buddylist will
783
not be included in the list in the current version of C.
784
785
C is a list of hash references, one for each change to the buddylist,
786
with the following keys:
787
788
=over 4
789
790
=item *
791
792
type: Either C or C. This indicates
793
if the change was to a buddy or a group.
794
795
=item *
796
797
action: Either C or C. This indicates
798
whether the change was an addition/modification or a deletion.
799
800
=item *
801
802
group: The name of the group which the modification took place in. For
803
C, this will be the name of the group which the
804
changed buddy was changed in; for C, this will
805
be the name of the group which was changed.
806
807
=item *
808
809
buddy: This key is only present for C. It's the name
810
of the buddy which was changed.
811
812
=back
813
814
The C constants come from C, and
815
are included in the C<:standard> export list.
816
817
=back
818
819
=head2 PRIVACY
820
821
C supports privacy controls. Our visibility setting, along
822
with the contents of the permit and deny lists, determines who can
823
contact us. Visibility can be set to permit or deny everyone, permit only
824
those on the permit list, deny only those on the deny list, or permit
825
everyone on our buddylist.
826
827
=head3 METHODS
828
829
=over 4
830
831
=item add_permit (BUDDIES)
832
833
Add buddies to your permit list. Call L<"commit_buddylist"> for the
834
change to take effect.
835
836
=item add_deny (BUDDIES)
837
838
See L.
839
840
=item remove_permit (BUDDIES)
841
842
See L.
843
844
=item remove_deny (BUDDIES)
845
846
See L.
847
848
=item get_permitlist
849
850
Returns a list of all members of the permit list.
851
852
=item get_denylist
853
854
Returns a list of all members of the deny list.
855
856
=item visibility
857
858
Returns the user's current visibility setting. See L.
859
860
=cut
861
862
0
0
sub add_permit($@) { shift->mod_permit(MODBL_ACTION_ADD, "permit", @_); }
863
0
0
sub add_deny($@) { shift->mod_permit(MODBL_ACTION_ADD, "deny", @_); }
864
0
0
sub remove_permit($@) { shift->mod_permit(MODBL_ACTION_DEL, "permit", @_); }
865
0
0
sub remove_deny($@) { shift->mod_permit(MODBL_ACTION_DEL, "deny", @_); }
866
0
0
sub get_permitlist($) { return keys %{shift->{permit}}; }
0
867
0
0
sub get_denylist(@) { return keys %{shift->{deny}}; }
0
868
0
0
sub visibility($) { return shift->{visibility}; }
869
870
871
=pod
872
873
=item set_visibility (MODE)
874
875
Sets the visibility mode, which determines how the permit and deny lists
876
are interpreted. Note that if you're looking for the feature which will prevent
877
a user from showing up as online on any buddy list while not affecting anything else,
878
the droids you're looking for are L<"is_stealth">/L<"set_stealth">.
879
880
The visibility mode may be:
881
882
=over 4
883
884
=item *
885
886
VISMODE_PERMITALL: Permit everybody.
887
888
=item *
889
890
VISMODE_DENYALL: Deny everybody.
891
892
=item *
893
894
VISMODE_PERMITSOME: Permit only those on your permit list.
895
896
=item *
897
898
VISMODE_DENYSOME: Deny only those on your deny list.
899
900
=item *
901
902
VISMODE_PERMITBUDS: Same as VISMODE_PERMITSOME, but your permit list is made to be
903
the same as the buddies from all the various groups in your
904
buddylist (except the deny group!) Adding and removing buddies
905
maintains this relationship. You shouldn't manually alter the
906
permit or deny groups when using this visibility mode.
907
908
=back
909
910
These constants are contained in the C package,
911
and will be imported into your namespace if you import C
912
with the C<:standard> parameter.
913
914
When someone is permitted, they can see when you are online and
915
send you messages. When someone is denied, they can't see when
916
you are online or send you messages. You cannot see them or
917
send them messages. You can talk to them if you are in the same
918
chatroom, although neither of you can invite the other one into
919
a chatroom.
920
921
Call L<"commit_buddylist"> for the change to take effect.
922
923
=cut
924
925
sub set_visibility($$) {
926
0
0
my($self, $vismode) = @_;
927
928
0
0
return must_be_on($self) unless $self->{is_on};
929
0
$self->{visibility} = $vismode;
930
}
931
932
=pod
933
934
=item is_stealth
935
936
=item set_stealth STEALTH_STATUS
937
938
These methods deal with "stealth mode". When the user is in stealth mode, she won't
939
show up as online on anyone's buddylist. However, for all other purposes, she will be online
940
as usual. Any restrictions, imposed by the visibility mode (see L<"set_visibility">),
941
on who can communicate with her will remain in effect.
942
943
Stealth state can be changed by another signon of the user's
944
screenname. So, if you want your application to be aware of the stealth state,
945
C won't cut it; there's a L<"stealth_changed"> callback which will serve
946
nicely.
947
948
=cut
949
950
0
0
sub is_stealth($) { return shift->{stealth}; }
951
sub set_stealth($$) {
952
0
0
my($self, $new_state) = @_;
953
0
0
$self->svcdo(CONNTYPE_BOS, protobit => "set_extended_status", protodata => {
954
stealth => {state => $new_state ? 0x100 : 0}
955
});
956
}
957
958
=pod
959
960
=item set_group_permissions (NEWPERMS)
961
962
Set group permissions. This lets you block any OSCAR users or any AOL users.
963
C should be a list of zero or more of the following constants:
964
965
=over 4
966
967
=item GROUPPERM_OSCAR
968
969
Permit AOL Instant Messenger users to contact you.
970
971
=item GROUPPERM_AOL
972
973
Permit AOL subscribers to contact you.
974
975
=back
976
977
Call L<"commit_buddylist"> for the change to take effect.
978
979
=cut
980
981
sub set_group_permissions($@) {
982
0
0
my($self, @perms) = @_;
983
0
my $perms = 0xFFFFFF00;
984
985
0
0
return must_be_on($self) unless $self->{is_on};
986
0
foreach my $perm (@perms) { $perms |= $perm; }
0
987
0
$self->{groupperms} = $perms;
988
}
989
990
=pod
991
992
=item group_permissions
993
994
Returns current group permissions. The return value is a list like the one
995
that L<"set_group_permissions"> wants.
996
997
=cut
998
999
sub group_permissions($) {
1000
0
0
my $self = shift;
1001
0
my @retval = ();
1002
1003
0
foreach my $perm (GROUPPERM_OSCAR, GROUPPERM_AOL) {
1004
0
0
push @retval, $perm if $self->{groupperms} & $perm;
1005
}
1006
0
return @retval;
1007
}
1008
1009
=pod
1010
1011
=back
1012
1013
=head2 OTHER USERS
1014
1015
See also L<"BUDDIES AND BUDDYLISTS">.
1016
1017
=head3 METHODS
1018
1019
=over 4
1020
1021
=item get_info (WHO)
1022
1023
Requests a user's information, which includes their profile and idle time.
1024
See the L callback for more information.
1025
1026
=item get_away (WHO)
1027
1028
Similar to L, except requests the user's away message instead of
1029
their profile.
1030
1031
=cut
1032
1033
sub get_info($$) {
1034
0
0
my($self, $screenname) = @_;
1035
0
0
return must_be_on($self) unless $self->{is_on};
1036
1037
0
$self->svcdo(CONNTYPE_BOS, reqdata => $screenname, protobit => "get_info", protodata => {screenname => $screenname});
1038
}
1039
sub get_away($$) {
1040
0
0
my($self, $screenname) = @_;
1041
0
0
return must_be_on($self) unless $self->{is_on};
1042
1043
0
$self->svcdo(CONNTYPE_BOS, reqdata => $screenname, protobit => "get_away", protodata => {screenname => $screenname});
1044
}
1045
1046
1047
=pod
1048
1049
=item send_im (WHO, MESSAGE[, AWAY])
1050
1051
Sends someone an instant message. If the message is an automated reply generated,
1052
perhaps, because you have an away message set, give the AWAY parameter a non-zero
1053
value. Note that C will not handle sending away messages to people who
1054
contact you when you are away - you must perform this yourself if you want it done.
1055
1056
Returns a "request ID" that you can use in the C callback to identify the message.
1057
If the message was too long to send, returns zero.
1058
1059
=cut
1060
1061
sub send_im($$$;$) {
1062
0
0
my($self, $to, $msg, $away) = @_;
1063
0
0
return must_be_on($self) unless $self->{is_on};
1064
1065
0
0
if(!$self->{svcdata}->{hashlogin}) {
1066
0
0
return 0 if length($msg) >= 7987;
1067
} else {
1068
0
0
return 0 if length($msg) > 2000;
1069
}
1070
1071
0
my %protodata;
1072
0
$protodata{message} = $msg;
1073
1074
0
0
if($away) {
1075
0
$protodata{is_automatic} = {};
1076
} else {
1077
0
$protodata{request_server_confirmation} = {};
1078
}
1079
1080
0
0
0
if($self->{capabilities}->{buddy_icons} and $self->{icon_checksum} and $self->{icon_timestamp} and
0
0
0
1081
(!exists($self->{userinfo}->{$to}) or
1082
!exists($self->{userinfo}->{to}->{icon_timestamp_received}) or
1083
$self->{icon_timestamp} > $self->{userinfo}->{$to}->{icon_timestamp_received})
1084
) {
1085
0
$self->log_print(OSCAR_DBG_DEBUG, "Informing $to about our buddy icon.");
1086
0
0
$self->{userinfo}->{$to} ||= {};
1087
0
$self->{userinfo}->{$to}->{icon_timestamp_received} = $self->{icon_timestamp};
1088
1089
0
$protodata{icon_data}->{"icon_".$_} = $self->{"icon_".$_} foreach qw(length checksum timestamp);
1090
}
1091
1092
0
my $flags2 = 0;
1093
0
0
if($self->{capabilities}->{typing_status}) {
1094
0
$flags2 = 0xB;
1095
}
1096
1097
0
my($req_id) = $self->send_message($to, 1, protoparse($self, "standard_IM_footer")->pack(%protodata), $flags2);
1098
0
return $req_id;
1099
}
1100
1101
=pod
1102
1103
=item send_typing_status (RECIPIENT, STATUS)
1104
1105
Send a typing status change to another user. Send these messages
1106
to implement typing status notification. Valid values for C are:
1107
1108
=over 4
1109
1110
=item *
1111
1112
TYPINGSTATUS_STARTED: The user has started typing to the recipient.
1113
This indicates that typing is actively taking place.
1114
1115
=item *
1116
1117
TYPINGSTATUS_TYPING: The user is typing to the recipient. This
1118
indicates that there is text in the message input area, but
1119
typing is not actively taking place at the moment.
1120
1121
=item *
1122
1123
TYPINGSTATUS_FINISHED: The user has finished typing to the recipient.
1124
This should be sent when the user starts to compose a message, but
1125
then erases all of the text in the message input area.
1126
1127
=back
1128
1129
=cut
1130
1131
sub send_typing_status($$$) {
1132
0
0
my($self, $recipient, $status) = @_;
1133
1134
0
0
croak "This client does not support typing status notifications." unless $self->{capabilities}->{typing_status};
1135
0
0
0
return unless exists $self->{userinfo}->{$recipient} and $self->{userinfo}->{$recipient}->{typing_status};
1136
1137
0
$self->svcdo(CONNTYPE_BOS, protobit => "typing_notification", protodata => {
1138
screenname => $recipient,
1139
typing_status => $status
1140
});
1141
}
1142
1143
1144
=pod
1145
1146
=item evil (WHO[, ANONYMOUSLY])
1147
1148
C, or C, a user. Evilling a user increases their evil level,
1149
which makes them look bad and decreases the rate at which they can send
1150
messages. Evil level gradually decreases over time. If the second
1151
parameter is non-zero, the evil will be done anonymously, which does
1152
not increase the user's evil level by as much as a standard evil.
1153
1154
You can't always evil someone. You can only do it when they do something
1155
like send you an instant message.
1156
1157
=cut
1158
1159
sub evil($$;$) {
1160
0
0
my($self, $who, $anon) = @_;
1161
0
0
return must_be_on($self) unless $self->{is_on};
1162
1163
0
0
$self->svcdo(CONNTYPE_BOS, reqdata => $who, protobit => "outgoing_warning", protodata => {
1164
is_anonymous => $anon ? 1 : 0,
1165
screenname => $who
1166
});
1167
}
1168
1169
=pod
1170
1171
=item get_icon (SCREENNAME, MD5SUM)
1172
1173
Gets a user's buddy icon. See L for details. To make
1174
sure this method isn't called excessively, please check the
1175
C and C data, which are available
1176
via the L method (even for people not on the user's buddy
1177
list.) The MD5 checksum of a user's icon will be in the
1178
C key returned by L.
1179
1180
You should receive a L callback in
1181
response to this method.
1182
1183
=cut
1184
1185
sub get_icon($$$) {
1186
0
0
my($self, $screenname, $md5sum) = @_;
1187
1188
0
0
carp "This client does not support buddy icons!" unless $self->{capabilities}->{buddy_icons};
1189
1190
0
$self->svcdo(CONNTYPE_ICON, protobit => "buddy_icon_download", protodata => {
1191
screenname => $screenname,
1192
md5sum => $md5sum
1193
});
1194
}
1195
1196
=pod
1197
1198
=back
1199
1200
=head3 CALLBACKS
1201
1202
=over 4
1203
1204
=item new_buddy_icon (OSCAR, SCREENNAME, BUDDY DATA)
1205
1206
This is called when someone, either someone the user is talking with or someone on
1207
their buddylist, has a potentially new buddy icon. The buddy data is guaranteed
1208
to have at least C available; C and C
1209
may not be. Specifically, if C found out about the buddy icon
1210
through a buddy status update (the sort that triggers a L callback),
1211
these data will B be available; if C found out about the
1212
icon via an incoming IM from the person, these data B be available.
1213
1214
Upon receiving this callback, an application should use the C
1215
to search for the icon in its cache, and call L if it can't find it.
1216
If the C, which is what needs to get passed to L, is not present
1217
in the buddy data, use L to request the information for the user,
1218
and then call L from the L callback.
1219
1220
=item buddy_icon_downloaded (OSCAR, SCREENNAME, ICONDATA)
1221
1222
This is called when a user's buddy icon is successfully downloaded from the server.
1223
1224
=item typing_status (OSCAR, SCREENNAME, STATUS)
1225
1226
Called when someone has sent us a typing status notification message.
1227
See L for a description of the different statuses.
1228
1229
=item im_ok (OSCAR, TO, REQID)
1230
1231
Called when an IM to C is successfully sent.
1232
REQID is the request ID of the IM as returned by C.
1233
1234
=item im_in (OSCAR, FROM, MESSAGE[, AWAY])
1235
1236
Called when someone sends you an instant message. If the AWAY parameter
1237
is non-zero, the message was generated as an automatic reply, perhaps because
1238
you sent that person a message and they had an away message set.
1239
1240
=item buddylist_in (OSCAR, FROM, BUDDYLIST)
1241
1242
Called when someone sends you a buddylist. You must set the L<"buddy_list_transfer">
1243
capability for buddylists to be sent to you. The buddylist will be a C
1244
hashref whose keys are the groups and whose values are listrefs of C
1245
strings for the buddies in the group.
1246
1247
=item buddy_info (OSCAR, SCREENNAME, BUDDY DATA)
1248
1249
Called in response to a L or L request.
1250
BUDDY DATA is the same as that returned by the L method,
1251
except that one of two additional keys, C and C,
1252
may be present.
1253
1254
=back
1255
1256
=head2 THE SIGNED-ON USER
1257
1258
These methods deal with the user who is currently signed on using a particular
1259
C object.
1260
1261
=head3 METHODS
1262
1263
=over 4
1264
1265
=item email
1266
1267
Returns the email address currently assigned to the user's account.
1268
1269
=item screenname
1270
1271
Returns the user's current screenname, including all capitalization and spacing.
1272
1273
=item is_on
1274
1275
Returns true if the user is signed on to the OSCAR service. Otherwise,
1276
returns false.
1277
1278
=cut
1279
1280
0
0
sub email($) { return shift->{email}; }
1281
0
0
sub screenname($) { return shift->{screenname}; }
1282
0
0
sub is_on($) { return shift->{is_on}; }
1283
1284
=item profile
1285
1286
Returns your current profile.
1287
1288
=cut
1289
1290
0
0
sub profile($) { return shift->{profile}; }
1291
1292
=pod
1293
1294
=item set_away (MESSAGE)
1295
1296
Sets the user's away message, also marking them as being away.
1297
If the message is undef or the empty string, the user will be
1298
marked as no longer being away. See also L<"get_away">.
1299
1300
=cut
1301
1302
sub set_away($$) {
1303
0
0
my($self, $awaymsg) = @_;
1304
0
0
return must_be_on($self) unless $self->{is_on};
1305
1306
# Because we use !defined(awaymsg) to indicate
1307
# that we just want to set the profile, force
1308
# it to be defined.
1309
0
0
$awaymsg = "" unless defined($awaymsg);
1310
1311
0
shift->set_info(undef, $awaymsg);
1312
}
1313
1314
=pod
1315
1316
=item set_extended_status (MESSAGE)
1317
1318
Sets the user's extended status message. This requires the
1319
C object to have been created with the C
1320
capability. Currently, the only clients which support extended
1321
status messages are Net::OSCAR, Gaim, and iChat. If the message
1322
is undef or the empty string, the user's extended status
1323
message will be cleared. Use L<"get_info"> to get another
1324
user's extended status.
1325
1326
=cut
1327
1328
sub set_extended_status($$) {
1329
0
0
my($self, $status) = @_;
1330
0
0
croak "This client does not support extended status messages." unless $self->{capabilities}->{extended_status};
1331
1332
0
0
$status ||= "";
1333
1334
0
$self->log_print(OSCAR_DBG_NOTICE, "Setting extended status.");
1335
0
$self->svcdo(CONNTYPE_BOS, protobit => "set_extended_status", protodata => {
1336
status_message => {message => $status}
1337
});
1338
}
1339
1340
=pod
1341
1342
=item set_info (PROFILE)
1343
1344
Sets the user's profile. Call L<"commit_buddylist"> to have
1345
the new profile saved into the buddylist, so that it will be
1346
set the next time the screenname is signed on. (This is a
1347
Net::OSCAR-specific feature, so other clients will not pick
1348
up the profile from the buddylist.)
1349
1350
Note that Net::OSCAR stores the user's profile in the server-side buddylist, so
1351
if L<"commit_buddylist"> is called after setting the profile with this method,
1352
the user will automatically get that same profile set whenever they sign on
1353
through Net::OSCAR. See the file C, included with the C distribution,
1354
for details of how we're storing this data.
1355
1356
Use L<"get_info"> to retrieve another user's profile.
1357
1358
=cut
1359
1360
sub set_info($$;$) {
1361
0
0
my($self, $profile, $awaymsg) = @_;
1362
1363
0
0
return must_be_on($self) unless $self->{services}->{0+CONNTYPE_BOS};
1364
0
$self->log_print(OSCAR_DBG_NOTICE, "Setting user information.");
1365
1366
0
my %protodata;
1367
0
$protodata{capabilities} = $self->capabilities();
1368
1369
0
0
if(defined($profile)) {
1370
0
$protodata{profile_mimetype} = 'text/aolrtf; charset="us-ascii"';
1371
0
$protodata{profile} = $profile;
1372
0
$self->{profile} = $profile;
1373
}
1374
1375
0
0
if(defined($awaymsg)) {
1376
0
$protodata{awaymsg_mimetype} = 'text/aolrtf; charset="us-ascii"';
1377
0
$protodata{awaymsg} = $awaymsg;
1378
}
1379
1380
0
$self->svcdo(CONNTYPE_BOS, protobit => "set_info", protodata => \%protodata);
1381
}
1382
1383
=pod
1384
1385
=item set_icon (ICONDATA)
1386
1387
Sets the user's buddy icon. The C object must have been created
1388
with the C capability to use this. C must be less
1389
than 4kb, should be 48x48 pixels, and should be BMP, GIF, or JPEG image data.
1390
You must call L for this change to take effect. If
1391
C is the empty string, the user's buddy icon will be removed.
1392
1393
When reading the icon data from a file, make sure to call C
1394
on the file handle.
1395
1396
Note that if the user's buddy icon was previously set with Net::OSCAR,
1397
enough data will be stored in the server-side buddylist that this will
1398
not have to be called every time the user signs on. However, other clients
1399
do not store the extra data in the buddylist, so if the user previously
1400
set a buddy icon with a non-Net::OSCAR-based client, this method will
1401
need to be called in order for the user's buddy icon to be set properly.
1402
1403
See the file C, included with the C distribution,
1404
for details of how we're storing this data.
1405
1406
You should receive a L callback in response to this
1407
method.
1408
1409
Use L<"get_icon"> to retrieve another user's icon.
1410
1411
=cut
1412
1413
sub set_icon($$) {
1414
0
0
my($self, $icon) = @_;
1415
1416
0
0
carp "This client does not support buddy icons!" unless $self->{capabilities}->{buddy_icons};
1417
1418
0
0
if($icon) {
1419
0
$self->{icon} = $icon;
1420
0
0
$self->{icon_md5sum_old} = $self->{icon_md5sum} || "";
1421
0
$self->{icon_md5sum} = pack("n", 0x10) . md5($icon);
1422
0
$self->{icon_checksum} = $self->icon_checksum($icon);
1423
0
$self->{icon_timestamp} = time;
1424
0
$self->{icon_length} = length($icon);
1425
} else {
1426
0
delete $self->{icon};
1427
0
delete $self->{icon_md5sum};
1428
0
delete $self->{icon_checksum};
1429
0
delete $self->{icon_timestamp};
1430
0
delete $self->{icon_length};
1431
}
1432
}
1433
1434
1435
=pod
1436
1437
=item change_password (CURRENT PASSWORD, NEW PASSWORD)
1438
1439
Changes the user's password.
1440
1441
=cut
1442
1443
sub change_password($$$) {
1444
0
0
my($self, $currpass, $newpass) = @_;
1445
0
0
return must_be_on($self) unless $self->{is_on};
1446
1447
0
0
if($self->{adminreq}->{0+ADMIN_TYPE_PASSWORD_CHANGE}) {
1448
0
$self->callback_admin_error(ADMIN_TYPE_PASSWORD_CHANGE, ADMIN_ERROR_REQPENDING);
1449
0
return;
1450
} else {
1451
0
$self->{adminreq}->{0+ADMIN_TYPE_PASSWORD_CHANGE}++;
1452
}
1453
1454
0
$self->svcdo(CONNTYPE_ADMIN, protobit => "change_account_info", protodata => {
1455
newpass => $newpass,
1456
oldpass => $currpass
1457
});
1458
}
1459
1460
=pod
1461
1462
=item confirm_account
1463
1464
Confirms the user's account. This can be used when the user's account is in the trial state,
1465
as determined by the presence of the C key in the information given when the user's
1466
information is requested.
1467
1468
=cut
1469
1470
sub confirm_account($) {
1471
0
0
my($self) = shift;
1472
0
0
return must_be_on($self) unless $self->{is_on};
1473
1474
0
0
if($self->{adminreq}->{0+ADMIN_TYPE_ACCOUNT_CONFIRM}) {
1475
0
$self->callback_admin_error(ADMIN_TYPE_ACCOUNT_CONFIRM, ADMIN_ERROR_REQPENDING);
1476
0
return;
1477
} else {
1478
0
$self->{adminreq}->{0+ADMIN_TYPE_ACCOUNT_CONFIRM}++;
1479
}
1480
1481
0
$self->svcdo(CONNTYPE_ADMIN, protobit => "confirm_account_request");
1482
}
1483
1484
=pod
1485
1486
=item change_email (NEW EMAIL)
1487
1488
Requests that the email address registered to the user's account be changed.
1489
This causes the OSCAR server to send an email to both the new address and the
1490
old address. To complete the change, the user must follow instructions contained
1491
in the email sent to the new address. The email sent to the old address contains
1492
instructions which allow the user to cancel the change within three days of the
1493
change request. It is important that the user's current email address be
1494
known to the OSCAR server so that it may email the account password if the
1495
user forgets it.
1496
1497
=cut
1498
1499
sub change_email($$) {
1500
0
0
my($self, $newmail) = @_;
1501
0
0
return must_be_on($self) unless $self->{is_on};
1502
1503
0
0
if($self->{adminreq}->{0+ADMIN_TYPE_EMAIL_CHANGE}) {
1504
0
$self->callback_admin_error(ADMIN_TYPE_EMAIL_CHANGE, ADMIN_ERROR_REQPENDING);
1505
0
return;
1506
} else {
1507
0
$self->{adminreq}->{0+ADMIN_TYPE_EMAIL_CHANGE}++;
1508
}
1509
1510
0
$self->svcdo(CONNTYPE_ADMIN, protobit => "change_account_info", protodata => {
1511
new_email => $newmail
1512
});
1513
}
1514
1515
=pod
1516
1517
=item format_screenname (NEW FORMAT)
1518
1519
Allows the capitalization and spacing of the user's screenname to be changed.
1520
The new format must be the same as the user's current screenname, except that
1521
case may be changed and spaces may be inserted or deleted.
1522
1523
=cut
1524
1525
sub format_screenname($$) {
1526
0
0
my($self, $newname) = @_;
1527
0
0
return must_be_on($self) unless $self->{is_on};
1528
1529
0
0
if($self->{adminreq}->{0+ADMIN_TYPE_SCREENNAME_FORMAT}) {
1530
0
$self->callback_admin_error(ADMIN_TYPE_SCREENNAME_FORMAT, ADMIN_ERROR_REQPENDING);
1531
0
return;
1532
} else {
1533
0
$self->{adminreq}->{0+ADMIN_TYPE_SCREENNAME_FORMAT}++;
1534
}
1535
1536
0
$self->svcdo(CONNTYPE_ADMIN, protobit => "change_account_info", protodata => {
1537
new_screenname => $newname
1538
});
1539
}
1540
1541
=pod
1542
1543
=item set_idle (TIME)
1544
1545
Sets the user's idle time in seconds. Set to zero to mark the user as
1546
not being idle. Set to non-zero once the user becomes idle. The OSCAR
1547
server will automatically increment the user's idle time once you mark
1548
the user as being idle.
1549
1550
=cut
1551
1552
sub set_idle($$) {
1553
0
0
my($self, $time) = @_;
1554
0
0
return must_be_on($self) unless $self->{is_on};
1555
0
$self->svcdo(CONNTYPE_BOS, protobit => "set_idle", protodata => {duration => $time});
1556
}
1557
1558
=pod
1559
1560
=back
1561
1562
=head3 CALLBACKS
1563
1564
=over 4
1565
1566
=item admin_error (OSCAR, REQTYPE, ERROR, ERRURL)
1567
1568
This is called when there is an error performing an administrative function - changing
1569
your password, formatting your screenname, changing your email address, or confirming your
1570
account. REQTYPE is a string describing the type of request which generated the error.
1571
ERROR is an error message. ERRURL is an http URL which the user may visit for more
1572
information about the error.
1573
1574
=item admin_ok (OSCAR, REQTYPE)
1575
1576
This is called when an administrative function succeeds. See L for more info.
1577
1578
=item buddy_icon_uploaded (OSCAR)
1579
1580
This is called when the user's buddy icon is successfully uploaded to the server.
1581
1582
=item stealth_changed (OSCAR, NEW_STEALTH_STATE)
1583
1584
This is called when the user's stealth state changes. See L<"is_stealth"> and L<"set_stealth">
1585
for information on stealth.
1586
1587
=item extended_status (OSCAR, STATUS)
1588
1589
Called when the user's extended status changes. This will normally
1590
be sent in response to a successful L call.
1591
1592
=item evil (OSCAR, NEWEVIL[, FROM])
1593
1594
Called when your evil level changes. NEWEVIL is your new evil level,
1595
as a percentage (accurate to tenths of a percent.) ENEMY is undef
1596
if the evil was anonymous (or if the message was triggered because
1597
your evil level naturally decreased), otherwise it is the screenname
1598
of the person who sent us the evil. See the L<"evil"> method for
1599
more information on evils.
1600
1601
=back
1602
1603
=head2 FILE TRANSFER AND DIRECT CONNECTIONS
1604
1605
=over 4
1606
1607
=item file_send SCREENNAME MESSAGE FILEREFS
1608
1609
C can be undef to have Net::OSCAR read the file,
1610
a file handle, or the data to send.
1611
1612
=cut
1613
1614
sub file_send($$@) {
1615
0
0
my($self, $screenname, $message, @filerefs) = @_;
1616
1617
0
my $connection = $self->addconn(conntype => CONNTYPE_DIRECT_IN);
1618
0
my($port) = sockaddr_in(getsockname($connection->{socket}));
1619
1620
0
my $size = 0;
1621
0
$size += length($_->{data}) foreach @filerefs;
1622
1623
0
my %svcdata = (
1624
file_count_status => (@filerefs > 1 ? 2 : 1),
1625
file_count => scalar(@filerefs),
1626
size => $size,
1627
0
0
files => [map {$_->{name}} @filerefs]
1628
);
1629
1630
0
my $cookie = randchars(8);
1631
0
my($ip) = unpack("N", inet_aton($self->{services}->{CONNTYPE_BOS()}->local_ip()));
1632
0
my %protodata = (
1633
capability => OSCAR_CAPS()->{filexfer}->{value},
1634
charset => "us-ascii",
1635
cookie => $cookie,
1636
invitation_msg => $message,
1637
language => 101,
1638
push_pull => 1,
1639
status => "propose",
1640
client_1_ip => $ip,
1641
client_2_ip => $ip,
1642
port => $port,
1643
proxy_ip => unpack("N", inet_aton("63.87.248.248")), # TODO: What's this really supposed to be?
1644
svcdata_charset => "us-ascii",
1645
svcdata => protoparse($self, "file_transfer_rendezvous_data")->pack(%svcdata)
1646
);
1647
1648
0
my($req_id) = $self->send_message($screenname, 2, pack("nn", 3, 0) . protoparse($self, "rendezvous_IM")->pack(%protodata), 0, $cookie);
1649
1650
0
$self->{rv_proposals}->{$cookie} = $connection->{rv} = {
1651
cookie => $cookie,
1652
sender => $self->{screenname},
1653
recipient => $screenname,
1654
peer => $screenname,
1655
type => "filexfer",
1656
connection => $connection,
1657
ft_state => "listening",
1658
direction => "send",
1659
accepted => 0,
1660
0
filenames => [map {$_->{name}} @filerefs],
1661
0
data => [map {$_->{data}} @filerefs],
1662
using_proxy => 0,
1663
tried_proxy => 0,
1664
tried_listen => 1,
1665
tried_connect => 0,
1666
total_size => $size,
1667
file_count => scalar(@filerefs)
1668
};
1669
1670
0
return ($req_id, $cookie);
1671
}
1672
1673
=pod
1674
1675
=back
1676
1677
=head2 EVENT PROCESSING
1678
1679
=head3 METHODS
1680
1681
=over 4
1682
1683
=item do_one_loop
1684
1685
Processes incoming data from our connections to the various
1686
OSCAR services. This method reads one command from any
1687
connections which have data to be read. See the
1688
L method to set the timeout interval used
1689
by this method.
1690
1691
=cut
1692
1693
sub do_one_loop($) {
1694
0
0
my $self = shift;
1695
0
my $timeout = $self->{timeout};
1696
1697
0
0
0
undef $timeout if defined($timeout) and $timeout == -1;
1698
1699
0
my($rin, $win, $ein) = ('', '', '');
1700
1701
0
foreach my $connection(@{$self->{connections}}) {
0
1702
0
0
next unless exists($connection->{socket});
1703
0
0
0
if($connection->{connected}) {
0
1704
0
vec($rin, fileno $connection->{socket}, 1) = 1;
1705
} elsif(!$connection->{connected} or $connection->{outbuff}) {
1706
0
vec($win, fileno $connection->{socket}, 1) = 1;
1707
}
1708
}
1709
0
$ein = $rin | $win;
1710
1711
0
0
return unless $ein;
1712
0
my $nfound = select($rin, $win, $ein, $timeout);
1713
0
0
0
$self->process_connections(\$rin, \$win, \$ein) if $nfound and $nfound != -1;
1714
}
1715
1716
=pod
1717
1718
=item process_connections (READERSREF, WRITERSREF, ERRORSREF)
1719
1720
Use this method when you want to implement your own C
1721
statement for event processing instead of using C's
1722
L method. The parameters are references to the
1723
readers, writers, and errors parameters used by the select
1724
statement. The method will ignore all connections which
1725
are not C objects or which are
1726
C objects from a different C
1727
object. It modifies its arguments so that its connections
1728
are removed from the connection lists. This makes it very
1729
convenient for use with multiple C objects or
1730
use with a C-based event loop that you are also
1731
using for other purposes.
1732
1733
See the L method for a way to get the necessary
1734
bit vectors to use in your C.
1735
1736
=cut
1737
1738
sub process_connections($\$\$\$) {
1739
0
0
my($self, $readers, $writers, $errors) = @_;
1740
1741
# Filter out our connections and remove them from the to-do list
1742
0
foreach my $connection(@{$self->{connections}}) {
0
1743
0
my($read, $write) = (0, 0);
1744
0
0
next unless $connection->fileno;
1745
0
0
if($connection->{connected}) {
1746
0
0
next unless vec($$readers | $$errors, $connection->fileno, 1);
1747
0
vec($$readers, $connection->fileno, 1) = 0;
1748
0
$read = 1;
1749
}
1750
0
0
0
if(!$connection->{connected} or $connection->{outbuff}) {
1751
0
0
next unless vec($$writers | $$errors, $connection->fileno, 1);
1752
0
vec($$writers, $connection->fileno, 1) = 0;
1753
0
$write = 1;
1754
}
1755
0
0
if(vec($$errors, $connection->fileno, 1)) {
1756
0
vec($$errors, $connection->fileno, 1) = 0;
1757
0
$connection->{sockerr} = 1;
1758
0
$connection->disconnect();
1759
} else {
1760
0
$connection->process_one($read, $write);
1761
}
1762
}
1763
}
1764
1765
=pod
1766
1767
=back
1768
1769
=head3 CALLBACKS
1770
1771
=over 4
1772
1773
=item connection_changed (OSCAR, CONNECTION, STATUS)
1774
1775
Called when the status of a connection changes. The status is "read" if we
1776
should call L<"process_one"> on the connection when C indicates that
1777
the connection is ready for reading, "write" if we should call
1778
L<"process_one"> when the connection is ready for writing, "readwrite" if L<"process_one">
1779
should be called in both cases, or "deleted" if the connection has been deleted.
1780
1781
C is a C object.
1782
1783
Users of this callback may also be interested in the L<"get_filehandle">
1784
method of C.
1785
1786
=back
1787
1788
=head2 CHATS
1789
1790
=head3 METHODS
1791
1792
=over 4
1793
1794
=item chat_join (NAME[, EXCHANGE])
1795
1796
Creates (or joins?) a chatroom. The exchange parameter should probably not be
1797
specified unless you know what you're doing. Do not use this method
1798
to accept invitations to join a chatroom - use the L<"chat_accept"> method
1799
for that.
1800
1801
=cut
1802
1803
sub chat_join($$;$) {
1804
0
0
my($self, $name, $exchange) = @_;
1805
0
0
return must_be_on($self) unless $self->{is_on};
1806
0
0
$exchange ||= 4;
1807
1808
0
my $reqid = (8<<16) | (unpack("n", randchars(2)))[0];
1809
0
$self->{chats}->{pack("N", $reqid)} = $name;
1810
0
$self->svcdo(CONNTYPE_CHATNAV, reqid => $reqid, protobit => "chat_navigator_room_create", protodata => {
1811
exchange => $exchange,
1812
name => $name
1813
});
1814
}
1815
1816
=pod
1817
1818
=item chat_accept (CHATURL)
1819
1820
Use this to accept an invitation to join a chatroom.
1821
1822
=item chat_decline (CHATURL)
1823
1824
Use this to decline an invitation to join a chatroom.
1825
1826
=cut
1827
1828
sub chat_accept($$) {
1829
0
0
my($self, $url) = @_;
1830
0
0
return must_be_on($self) unless $self->{is_on};
1831
1832
0
$self->log_print(OSCAR_DBG_NOTICE, "Accepting chat invite for $url.");
1833
0
my($rv) = grep { $_->{chat_url} eq $url } values %{$self->{rv_proposals}};
0
0
1834
0
0
return unless $rv;
1835
1836
0
$self->svcdo(CONNTYPE_CHATNAV, protobit => "chat_invitation_accept", protodata => {
1837
exchange => $rv->{exchange},
1838
url => $url
1839
});
1840
1841
1842
0
my $reqid = pack("n", 4);
1843
0
$reqid .= randchars(2);
1844
0
($reqid) = unpack("N", $reqid);
1845
1846
0
$self->{chats}->{$reqid} = $rv;
1847
0
$self->svcdo(CONNTYPE_BOS, protobit => "service_request", reqid => $reqid, protodata => {
1848
type => CONNTYPE_CHAT,
1849
chat => {
1850
exchange => $rv->{exchange},
1851
url => $url
1852
}
1853
});
1854
}
1855
1856
sub chat_decline($$) {
1857
0
0
my($self, $url) = @_;
1858
0
0
return must_be_on($self) unless $self->{is_on};
1859
1860
0
$self->log_print(OSCAR_DBG_NOTICE, "Declining chat invite for $url.");
1861
0
my($rv) = grep { $_->{chat_url} eq $url } values %{$self->{rv_proposals}};
0
0
1862
0
0
return unless $rv;
1863
1864
0
$self->svcdo(CONNTYPE_BOS, protobit => "chat_invitation_decline", protodata => {
1865
cookie => $rv->{cookie},
1866
screenname => $rv->{sender},
1867
});
1868
1869
0
delete $self->{rv_proposals}->{$rv->{cookie}};
1870
}
1871
1872
=pod
1873
1874
=back
1875
1876
=head3 CALLBACKS
1877
1878
=over 4
1879
1880
=item chat_buddy_in (OSCAR, SCREENNAME, CHAT, BUDDY DATA)
1881
1882
SCREENNAME has entered CHAT. BUDDY DATA is the same as that returned by
1883
the L method.
1884
1885
=item chat_buddy_out (OSCAR, SCREENNAME, CHAT)
1886
1887
Called when someone leaves a chatroom.
1888
1889
=item chat_im_in (OSCAR, FROM, CHAT, MESSAGE)
1890
1891
Called when someone says something in a chatroom. Note that you
1892
receive your own messages in chatrooms unless you specify the
1893
NOREFLECT parameter in L.
1894
1895
=item chat_invite (OSCAR, WHO, MESSAGE, CHAT, CHATURL)
1896
1897
Called when someone invites us into a chatroom. MESSAGE is the message
1898
that they specified on the invitation. CHAT is the name of the chatroom.
1899
CHATURL is a chat URL and not a C object. CHATURL can
1900
be passed to the L method to accept the invitation.
1901
1902
=item chat_joined (OSCAR, CHATNAME, CHAT)
1903
1904
Called when you enter a chatroom. CHAT is the C
1905
object for the chatroom.
1906
1907
=item chat_closed (OSCAR, CHAT, ERROR)
1908
1909
Your connection to CHAT (a C object) was severed due to ERROR.
1910
1911
=back
1912
1913
=head2 RATE LIMITS
1914
1915
See L<"RATE LIMIT OVERVIEW"> for more information on rate limits.
1916
1917
=head3 METHODS
1918
1919
=over 4
1920
1921
=item rate_level (OSCAR, METHODNAME[, CHAT])
1922
1923
Returns the rate level (one of C, C, C, C)
1924
which the OSCAR session is currently at for the C (or C) method named
1925
C right now. This only makes sense for methods which send information to the OSCAR
1926
server, such as C, but if you pass in a method name which doesn't make sense (or isn't
1927
actually a C method, or which isn't rate-limited), we'll gladly an empty list. B
1928
not available if your application is using L<"OSCAR_RATE_MANAGE_NONE">.>
1929
1930
If C is C, you should also pass the C
1931
object to get rate information on (as the C parameter.)
1932
1933
=cut
1934
1935
sub _rate_level($$$) {
1936
0
0
my($oscar, $level, $levels) = @_;
1937
1938
0
0
if($level <= $levels->{disconnect}) {
0
0
1939
0
return RATE_DISCONNECT;
1940
} elsif($level <= $levels->{limit}) {
1941
0
return RATE_LIMIT;
1942
} elsif($level <= $levels->{alert}) {
1943
0
return RATE_ALERT;
1944
} else {
1945
0
return RATE_CLEAR;
1946
}
1947
}
1948
1949
sub _rate_lookup($$;$) {
1950
0
0
my($oscar, $method, $chat) = @_;
1951
0
0
croak "Rate methods not supported when using OSCAR_RATE_MANAGE_NONE!" if $oscar->{rate_manage_mode} == OSCAR_RATE_MANAGE_NONE;
1952
1953
0
print "rate_lookup $method\n";
1954
0
0
my $key = $Net::OSCAR::MethodInfo::methods{$method} or return;
1955
0
print "\tFound key\n";
1956
0
0
my $conn = $chat || $oscar->connection_for_family(unpack("n", $key));
1957
0
print "\tFound connection\n";
1958
0
0
my $class = $conn->{rate_limits}->{classmap}->{$key} or return;
1959
0
print "\tFound class\n";
1960
0
return $conn->{rate_limits}->{$class};
1961
}
1962
1963
sub rate_level($$;$) {
1964
0
0
my($oscar, $method, $chat) = @_;
1965
0
0
my $rinfo = $oscar->_rate_lookup($method, $chat) or return;
1966
1967
0
return $oscar->_rate_level($rinfo->{current_state}, $rinfo->{levels});
1968
}
1969
1970
=pod
1971
1972
=item rate_limits (OSCAR, METHODNAME[, CHAT])
1973
1974
Similar to L<"rate_level">. This returns the boundaries of the different rate level
1975
categories for the given method name, in the form of a hash with the following keys
1976
(this won't make sense if you don't know how the current level is calculated; see below):
1977
1978
=over 4
1979
1980
=item window_size
1981
1982
=item levels
1983
1984
A hashref with keys for each of the levels. Each key is the name of a level,
1985
and the value for that key is the threshold for that level.
1986
1987
=over 4
1988
1989
=item clear
1990
1991
=item alert
1992
1993
=item limit
1994
1995
=item disconnect
1996
1997
=back
1998
1999
=item last_time
2000
2001
The time at which the last command to affect this rate level was sent.
2002
2003
=item current_state
2004
2005
The session's current rate level.
2006
2007
=back
2008
2009
Every time a command is sent to the OSCAR server, the level is recalculated according to the formula
2010
(from Alexandr Shutko's OSCAR documentation, L :
2011
2012
NewLevel = (Window - 1)/Window * OldLevel + 1/Window * CurrentTimeDiff
2013
2014
C is the difference between the current system time and C.
2015
2016
=cut
2017
2018
sub rate_limits($$;$) {
2019
0
0
my($oscar, $method, $chat) = @_;
2020
0
return $oscar->_rate_lookup($method, $chat);
2021
}
2022
2023
=pod
2024
2025
=item would_make_rate_level (OSCAR, METHODNAME[, CHAT])
2026
2027
Returns the rate level which your session would be at if C were sent right now.
2028
See L<"rate_level"> for more information.
2029
2030
=cut
2031
2032
sub _compute_rate($$) {
2033
0
0
my($oscar, $rinfo) = @_;
2034
2035
0
my $level = $rinfo->{current_state};
2036
0
my $window = $rinfo->{window_size};
2037
0
my $timediff = (millitime() - $rinfo->{time_offset}) - $rinfo->{last_time};
2038
0
return ($window - 1)/$window * $level + 1/$window * $timediff;
2039
}
2040
2041
sub would_make_rate_level($$;$) {
2042
0
0
my($oscar, $method, $chat) = @_;
2043
0
0
my $rinfo = $oscar->_rate_lookup($method, $chat) or return;
2044
2045
0
return $oscar->_rate_level($oscar->_compute_rate($rinfo), $rinfo->{levels});
2046
}
2047
2048
=pod
2049
2050
=back
2051
2052
=head3 CALLBACKS
2053
2054
=over 4
2055
2056
=item rate_alert (OSCAR, LEVEL, CLEAR, WINDOW, WORRISOME, VIRTUAL)
2057
2058
This is called when you are sending commands to OSCAR too quickly.
2059
2060
C is one of C, C, C, or C from the C
2061
package (they are imported into your namespace if you import C with the C<:standard>
2062
parameter.) C means that you're okay. C means you should slow down. C
2063
means that the server is ignoring messages from you until you slow down. C means you're
2064
about to be disconnected.
2065
2066
C and C tell you the maximum speed you can send in order to maintain C standing.
2067
You must send no more than C commands in C milliseconds. If you just want to keep it
2068
simple, you can just not send any commands for C milliseconds and you'll be fine.
2069
2070
C is nonzero if C thinks that the alert is anything worth
2071
worrying about. Otherwise it is zero. This is very rough, but it's a good way
2072
for the lazy to determine whether or not to bother passing the alert on to
2073
their users.
2074
2075
A C rate limit is one which your application would have incurred,
2076
but you're using L, so we
2077
stopped something from being sent out.
2078
2079
=back
2080
2081
=head2 MISCELLANEOUS
2082
2083
=head3 METHODS
2084
2085
=over 4
2086
2087
=item timeout ([NEW TIMEOUT])
2088
2089
Gets or sets the timeout value used by the L method.
2090
The default timeout is 0.01 seconds.
2091
2092
=cut
2093
2094
sub timeout($;$) {
2095
0
0
my($self, $timeout) = @_;
2096
0
0
return $self->{timeout} unless $timeout;
2097
0
$self->{timeout} = $timeout;
2098
}
2099
2100
=pod
2101
2102
=item loglevel ([LOGLEVEL[, SCREENNAME DEBUG]])
2103
2104
Gets or sets the level of logging verbosity. If this is non-zero, varing amounts of information will be printed
2105
to standard error (unless you have a L<"log"> callback defined). Higher loglevels will give you more information.
2106
If the optional screenname debug parameter is non-zero,
2107
debug messages will be prepended with the screenname of the OSCAR session which is generating
2108
the message (but only if you don't have a L<"log"> callback defined). This is useful when you have multiple C objects.
2109
2110
See the L<"log"> callback for more information.
2111
2112
=cut
2113
2114
sub loglevel($;$$) {
2115
0
0
my $self = shift;
2116
0
0
return $self->{LOGLEVEL} unless @_;
2117
0
$self->{LOGLEVEL} = shift;
2118
0
0
$self->{SNDEBUG} = shift if @_;
2119
}
2120
2121
=pod
2122
2123
=item auth_response (MD5_DIGEST[, PASS_IS_HASHED])
2124
2125
Provide a response to an authentication challenge - see the L<"auth_challenge">
2126
callback for details.
2127
2128
=cut
2129
2130
sub auth_response($$$) {
2131
0
0
my($self, $digest, $pass_is_hashed) = @_;
2132
2133
0
0
if($pass_is_hashed) {
2134
0
$self->{pass_is_hashed} = 1;
2135
} else {
2136
0
$self->{pass_is_hashed} = 0;
2137
}
2138
2139
0
$self->log_print(OSCAR_DBG_SIGNON, "Got authentication response - proceeding with signon");
2140
0
$self->{auth_response} = $digest;
2141
0
my %data = signon_tlv($self);
2142
0
$self->svcdo(CONNTYPE_BOS, protobit => "signon", protodata => {%data});
2143
}
2144
2145
=pod
2146
2147
=item clone
2148
2149
Clones the object. This creates a new C object whose callbacks,
2150
loglevel, screenname debugging, and timeout are the same as those of the
2151
current object. This is provided as a convenience when using multiple
2152
C objects in order to allow you to set those parameters once
2153
and then call the L method on the object returned by clone.
2154
2155
=cut
2156
2157
sub clone($) {
2158
0
0
my $self = shift;
2159
0
my $clone = $self->new(@{$self->{_parameters}}); # Born in a science lab late one night
0
2160
# Without a mother or a father
2161
# Just a test tube and womb with a view...
2162
2163
# Okay, now we don't want to just copy the reference.
2164
# If we did that, changing ourself would change the clone.
2165
0
$clone->{callbacks} = { %{$self->{callbacks}} };
0
2166
2167
0
$clone->{LOGLEVEL} = $self->{LOGLEVEL};
2168
0
$clone->{SNDEBUG} = $self->{SNDEBUG};
2169
0
$clone->{timeout} = $self->{timeout};
2170
2171
0
foreach my $c (@{$clone->{connections}}) {
0
2172
0
$c->{buffer} = \"";
2173
}
2174
2175
0
return $clone;
2176
}
2177
2178
=pod
2179
2180
=item buddyhash
2181
2182
Returns a reference to a tied hash which automatically normalizes its keys upon a fetch.
2183
Use this for hashes whose keys are AIM screennames since AIM screennames with different
2184
capitalization and spacing are considered equivalent.
2185
2186
The keys of the hash as returned by the C and C functions will be
2187
C objects, so you they will automagically be compared
2188
without regards to case and whitespace.
2189
2190
=cut
2191
2192
0
0
sub buddyhash($) { bltie; }
2193
2194
=pod
2195
2196
=item findconn (FILENO)
2197
2198
Finds the connection that is using the specified file number, or undef
2199
if the connection could not be found. Returns a C
2200
object.
2201
2202
=cut
2203
2204
sub findconn($$) {
2205
0
0
my($self, $target) = @_;
2206
0
my($conn) = grep { fileno($_->{socket}) == $target } @{$self->{connections}};
0
0
2207
0
return $conn;
2208
}
2209
2210
=pod
2211
2212
=item selector_filenos
2213
2214
Returns a list whose first element is a vec of all filehandles that we care
2215
about reading from and whose second element is a vec of all filehandles that
2216
we care about writing to. See the L<"process_connections"> method for details.
2217
2218
=cut
2219
2220
sub selector_filenos($) {
2221
0
0
my $self = shift;
2222
0
my($rin, $win) = ('', '');
2223
2224
0
foreach my $connection(@{$self->{connections}}) {
0
2225
0
0
next unless $connection->{socket};
2226
0
0
if($connection->{connected}) {
2227
0
my $n = fileno($connection->{socket});
2228
0
vec($rin, $n, 1) = 1;
2229
}
2230
0
0
0
if(!$connection->{connected} or $connection->{outbuff}) {
2231
0
my $n = fileno($connection->{socket});
2232
0
vec($win, $n, 1) = 1;
2233
}
2234
}
2235
0
return ($rin, $win);
2236
}
2237
2238
=item icon_checksum (ICONDATA)
2239
2240
Returns a checksum of the buddy icon. Use this in conjunction with the
2241
C buddy info key to cache buddy icons.
2242
2243
=cut
2244
2245
sub icon_checksum($$) {
2246
0
0
my($self, $icon) = @_;
2247
2248
0
my $sum = 0;
2249
0
my $i = 0;
2250
0
for($i = 0; $i+1 < length($icon); $i += 2) {
2251
0
$sum += (ord(substr($icon, $i+1, 1)) << 8) + ord(substr($icon, $i, 1));
2252
}
2253
2254
0
0
$sum += ord(substr($icon, $i, 1)) if $i < length($icon);
2255
2256
0
$sum = (($sum & 0xFFFF0000) >> 16) + ($sum & 0x0000FFFF);
2257
2258
0
return $sum;
2259
}
2260
2261
=pod
2262
2263
=item get_app_data ([GROUP[, BUDDY]])
2264
2265
Gets application-specific data. Returns a hashref whose keys are app-data IDs.
2266
IDs with high-order byte 0x0001 are reserved for non-application-specific usage
2267
and must be registered with the C list.
2268
If you wish to set application-specific data, you should reserve a high-order
2269
byte for your application by emailing C.
2270
This data is stored in your server-side buddylist and so will be persistent,
2271
even across machines.
2272
2273
If C is present, a hashref for accessing data specific to that group
2274
is returned.
2275
2276
If C is present, a hashref for accessing data specific to that buddy
2277
is returned.
2278
2279
Call L<"commit_buddylist"> to have the new data saved on the OSCAR server.
2280
2281
=cut
2282
2283
sub get_app_data($;$$) {
2284
0
0
my($self, $group, $buddy) = @_;
2285
2286
# We don't track changes to the contents of these hashes,
2287
# so mark as dirty and let BLI figure out whether anything really changed.
2288
0
0
0
if($group and $buddy) {
0
2289
0
my $bud = $self->{buddies}->{$group}->{members}->{$buddy};
2290
0
$bud->{__BLI_DIRTY} = 1;
2291
0
return $bud->{data};
2292
} elsif($group) {
2293
0
my $grp = $self->{buddies}->{$group};
2294
0
$grp->{__BLI_DIRTY} = 1;
2295
0
return $grp->{data};
2296
} else {
2297
0
return $self->{appdata};
2298
}
2299
}
2300
2301
=pod
2302
2303
=item chat_invite (CHAT, MESSAGE, WHO)
2304
2305
Deprecated. Provided for compatibility with C.
2306
Use the appropriate method of the C object
2307
instead.
2308
2309
=cut
2310
2311
sub chat_invite($$$@) {
2312
0
0
my($self, $chat, $msg, @who) = @_;
2313
0
0
return must_be_on($self) unless $self->{is_on};
2314
0
foreach my $who(@who) { $chat->{connection}->invite($who, $msg); }
0
2315
}
2316
2317
=pod
2318
2319
=item chat_leave (CHAT)
2320
2321
Deprecated. Provided for compatibility with C.
2322
Use the appropriate method of the C object
2323
instead.
2324
2325
=item chat_send (CHAT, MESSAGE)
2326
2327
Deprecated. Provided for compatibility with C.
2328
Use the appropriate method of the C object
2329
instead.
2330
2331
=cut
2332
2333
0
0
sub chat_leave($$) { $_[1]->part(); }
2334
0
0
sub chat_send($$$) { $_[1]->chat_send($_[2]); }
2335
2336
=pod
2337
2338
=back
2339
2340
=head3 CALLBACKS
2341
2342
=over 4
2343
2344
=item auth_challenge (OSCAR, CHALLENGE, HASHSTR)
2345
2346
B: AOL Instant Messenger has changed their encryption
2347
mechanisms; instead of using the password in the hash, you B now use
2348
the MD5 hash of the password. This allows your application to save the user's
2349
password in hashed form instead of plaintext if you're saving passwords.
2350
You must pass an extra parameter to C indicating that you are
2351
using the new encryption scheme. See below for an example.
2352
2353
OSCAR uses an MD5-based challenge/response system for authentication so that the
2354
password is never sent in plaintext over the network. When a user wishes to sign on,
2355
the OSCAR server sends an arbitrary number as a challenge. The client must respond
2356
with the MD5 digest of the concatenation of, in this order, the challenge, the password,
2357
and an additional hashing string (currently always the string
2358
"AOL Instant Messenger (SM)", but it is possible that this might change in the future.)
2359
2360
If password is undefined in L<"signon">, this callback will be triggered when the
2361
server sends a challenge during the signon process. The client must reply with
2362
the MD5 digest of CHALLENGE . MD5(password) . HASHSTR. For instance, using the
2363
L module:
2364
2365
my($oscar, $challenge, $hashstr) = @_;
2366
my $md5 = Digest::MD5->new;
2367
$md5->add($challenge);
2368
$md5->add(md5("password"));
2369
$md5->add($hashstr);
2370
$oscar->auth_response($md5->digest, 1);
2371
2372
Note that this functionality is only available for certain services. It is
2373
available for AIM but not ICQ. Note also that the MD5 digest must be in binary
2374
form, not the more common hex or base64 forms.
2375
2376
=item log (OSCAR, LEVEL, MESSAGE)
2377
2378
Use this callback if you don't want the log_print methods to just print to STDERR.
2379
It is called when even C of level C is called. The levels are,
2380
in order of increasing importance:
2381
2382
=over 4
2383
2384
=item OSCAR_DBG_NONE
2385
2386
Really only useful for setting in the L<"loglevel"> method. No information will
2387
be logged. The default loglevel.
2388
2389
=item OSCAR_DBG_PACKETS
2390
2391
Hex dumps of all incoming/outgoing packets.
2392
2393
=item OSCAR_DBG_DEBUG
2394
2395
Information useful for debugging C, and precious little else.
2396
2397
=item OSCAR_DBG_SIGNON
2398
2399
Like C, but only for the signon process; this is where
2400
problems are most likely to occur, so we provide this for the common case of
2401
people who only want a lot of information during signon. This may be deprecated
2402
some-day and be replaced by a more flexible facility/level system, ala syslog.
2403
2404
=item OSCAR_DBG_NOTICE
2405
2406
=item OSCAR_DBG_INFO
2407
2408
=item OSCAR_DBG_WARN
2409
2410
=back
2411
2412
Note that these symbols are imported into your namespace if and only if you use
2413
the C<:loglevels> or C<:all> tags when importing the module (e.g. C.)
2414
2415
Also note that this callback is only triggered for events whose level is greater
2416
than or equal to the loglevel for the OSCAR session. The L<"loglevel"> method
2417
allows you to get or set the loglevel.
2418
2419
=back
2420
2421
=head2 ERROR HANDLING
2422
2423
=head3 CALLBACKS
2424
2425
=over 4
2426
2427
=item error (OSCAR, CONNECTION, ERROR, DESCRIPTION, FATAL)
2428
2429
Called when any sort of error occurs (except see L below and
2430
L in L.)
2431
2432
C is the particular connection which generated the error - the C method of
2433
C may be useful, as may be getting C<$connection-E{description}>.
2434
C is a nicely formatted description of the error. C is an error number.
2435
2436
If C is non-zero, the error was fatal and the connection to OSCAR has been
2437
closed.
2438
2439
=item snac_unknown (OSCAR, CONNECTION, SNAC, DATA)
2440
2441
Called when Net::OSCAR receives a message from the OSCAR server which
2442
it doesn't known how to handle. The default handler for this callback
2443
will print out the unknown SNAC.
2444
2445
C is the C object on which the unknkown
2446
message was received. C is a hashref with keys such as C, C, C, and
2447
C.
2448
2449
=back
2450
2451
=cut
2452
2453
sub do_callback($@) {
2454
0
0
my $callback = shift;
2455
0
0
return unless $_[0]->{callbacks}->{$callback};
2456
0
&{$_[0]->{callbacks}->{$callback}}(@_);
0
2457
}
2458
0
0
sub set_callback { $_[1]->{callbacks}->{$_[0]} = $_[2]; }
2459
2460
0
0
sub callback_error(@) { do_callback("error", @_); }
2461
0
0
sub callback_buddy_in(@) { do_callback("buddy_in", @_); }
2462
0
0
sub callback_buddy_out(@) { do_callback("buddy_out", @_); }
2463
0
0
sub callback_im_in(@) { do_callback("im_in", @_); }
2464
0
0
sub callback_chat_joined(@) { do_callback("chat_joined", @_); }
2465
0
0
sub callback_chat_buddy_in(@) { do_callback("chat_buddy_in", @_); }
2466
0
0
sub callback_chat_buddy_out(@) { do_callback("chat_buddy_out", @_); }
2467
0
0
sub callback_chat_im_in(@) { do_callback("chat_im_in", @_); }
2468
0
0
sub callback_chat_invite(@) { do_callback("chat_invite", @_); }
2469
0
0
sub callback_buddy_info(@) { do_callback("buddy_info", @_); }
2470
0
0
sub callback_evil(@) { do_callback("evil", @_); }
2471
0
0
sub callback_chat_closed(@) { do_callback("chat_closed", @_); }
2472
0
0
sub callback_buddylist_error(@) { do_callback("buddylist_error", @_); }
2473
0
0
sub callback_buddylist_ok(@) { do_callback("buddylist_ok", @_); }
2474
0
0
sub callback_buddylist_changed(@) { do_callback("buddylist_changed", @_); }
2475
0
0
sub callback_admin_error(@) { do_callback("admin_error", @_); }
2476
0
0
sub callback_admin_ok(@) { do_callback("admin_ok", @_); }
2477
0
0
sub callback_new_buddy_icon(@) { do_callback("new_buddy_icon", @_); }
2478
0
0
sub callback_buddy_icon_uploaded(@) { do_callback("buddy_icon_uploaded", @_); }
2479
0
0
sub callback_buddy_icon_downloaded(@) { do_callback("buddy_icon_downloaded", @_); }
2480
0
0
sub callback_rate_alert(@) { do_callback("rate_alert", @_); }
2481
0
0
sub callback_signon_done(@) { do_callback("signon_done", @_); }
2482
0
0
sub callback_log(@) { do_callback("log", @_); }
2483
0
0
sub callback_typing_status(@) { do_callback("typing_status", @_); }
2484
0
0
sub callback_extended_status(@) { do_callback("extended_status", @_); }
2485
0
0
sub callback_im_ok(@) { do_callback("im_ok", @_); }
2486
0
0
sub callback_connection_changed(@) { do_callback("connection_changed", @_); }
2487
0
0
sub callback_auth_challenge(@) { do_callback("auth_challenge", @_); }
2488
0
0
sub callback_stealth_changed(@) { do_callback("stealth_changed", @_); }
2489
0
0
sub callback_snac_unknown(@) { do_callback("snac_unknown", @_); }
2490
0
0
sub callback_rendezvous_reject(@) { do_callback("rendezvous_reject", @_); }
2491
0
0
sub callback_rendezvous_accept(@) { do_callback("rendezvous_accept", @_); }
2492
0
0
sub callback_buddylist_in(@) { do_callback("buddylist_in", @_); }
2493
2494
0
0
sub set_callback_error($\&) { set_callback("error", @_); }
2495
0
0
sub set_callback_buddy_in($\&) { set_callback("buddy_in", @_); }
2496
0
0
sub set_callback_buddy_out($\&) { set_callback("buddy_out", @_); }
2497
0
0
sub set_callback_im_in($\&) { set_callback("im_in", @_); }
2498
0
0
sub set_callback_chat_joined($\&) { set_callback("chat_joined", @_); }
2499
0
0
sub set_callback_chat_buddy_in($\&) { set_callback("chat_buddy_in", @_); }
2500
0
0
sub set_callback_chat_buddy_out($\&) { set_callback("chat_buddy_out", @_); }
2501
0
0
sub set_callback_chat_im_in($\&) { set_callback("chat_im_in", @_); }
2502
0
0
sub set_callback_chat_invite($\&) { set_callback("chat_invite", @_); }
2503
0
0
sub set_callback_buddy_info($\&) { set_callback("buddy_info", @_); }
2504
0
0
sub set_callback_evil($\&) { set_callback("evil", @_); }
2505
0
0
sub set_callback_chat_closed($\&) { set_callback("chat_closed", @_); }
2506
0
0
sub set_callback_buddylist_error($\&) { set_callback("buddylist_error", @_); }
2507
0
0
sub set_callback_buddylist_ok($\&) { set_callback("buddylist_ok", @_); }
2508
0
0
sub set_callback_buddylist_changed($\&) { set_callback("buddylist_changed", @_); }
2509
0
0
sub set_callback_admin_error($\&) { set_callback("admin_error", @_); }
2510
0
0
sub set_callback_admin_ok($\&) { set_callback("admin_ok", @_); }
2511
sub set_callback_new_buddy_icon($\&) {
2512
0
0
0
croak "This client does not support buddy icons." unless $_[0]->{capabilities}->{buddy_icons};
2513
0
set_callback("new_buddy_icon", @_);
2514
}
2515
sub set_callback_buddy_icon_uploaded($\&) {
2516
0
0
0
croak "This client does not support buddy icons." unless $_[0]->{capabilities}->{buddy_icons};
2517
0
set_callback("buddy_icon_uploaded", @_);
2518
}
2519
sub set_callback_buddy_icon_downloaded($\&) {
2520
0
0
0
croak "This client does not support buddy icons." unless $_[0]->{capabilities}->{buddy_icons};
2521
0
set_callback("buddy_icon_downloaded", @_);
2522
}
2523
0
0
sub set_callback_rate_alert($\&) { set_callback("rate_alert", @_); }
2524
0
0
sub set_callback_signon_done($\&) { set_callback("signon_done", @_); }
2525
0
0
sub set_callback_log($\&) { set_callback("log", @_); }
2526
sub set_callback_typing_status($\&) {
2527
0
0
0
croak "This client does not support typing status notification." unless $_[0]->{capabilities}->{typing_status};
2528
0
set_callback("typing_status", @_);
2529
}
2530
sub set_callback_extended_status($\&) {
2531
0
0
0
croak "This client does not support extended status messages." unless $_[0]->{capabilities}->{extended_status};
2532
0
set_callback("extended_status", @_);
2533
}
2534
0
0
sub set_callback_im_ok($\&) { set_callback("im_ok", @_); }
2535
0
0
sub set_callback_connection_changed($\&) { set_callback("connection_changed", @_); }
2536
0
0
sub set_callback_auth_challenge($\&) { set_callback("auth_challenge", @_); }
2537
0
0
sub set_callback_stealth_changed($\&) { set_callback("stealth_changed", @_); }
2538
0
0
sub set_callback_snac_unknown($\&) { set_callback("snac_unknown", @_); }
2539
0
0
sub set_callback_rendezvous_reject($\&) { set_callback("snac_rendezvous_reject", @_); }
2540
0
0
sub set_callback_rendezvous_accept($\&) { set_callback("snac_rendezvous_accept", @_); }
2541
sub set_callback_buddylist_in($\&) {
2542
0
0
0
croak "This client does not support buddy list transfer." unless $_[0]->{capabilities}->{buddy_list_transfer};
2543
0
set_callback("buddylist_in", @_);
2544
}
2545
2546
=pod
2547
2548
=head1 CHAT CONNECTIONS
2549
2550
Aside from the methods listed here, there are a couple of methods of the
2551
C object that are important for implementing chat
2552
functionality. C is a descendent of C.
2553
2554
=over 4
2555
2556
=item invite (WHO, MESSAGE)
2557
2558
Invite somebody into the chatroom.
2559
2560
=item chat_send (MESSAGE[, NOREFLECT[, AWAY]])
2561
2562
Sends a message to the chatroom. If the NOREFLECT parameter is
2563
present, you will not receive the message as an incoming message
2564
from the chatroom. If AWAY is present, the message was generated
2565
as an automatic reply, perhaps because you have an away message set.
2566
2567
=item part
2568
2569
Leave the chatroom.
2570
2571
=item url
2572
2573
Returns the URL for the chatroom. Use this to associate a chat invitation
2574
with the chat_joined that C sends when you've join the chatroom.
2575
2576
=item name
2577
2578
Returns the name of the chatroom.
2579
2580
=item exchange
2581
2582
Returns the exchange of the chatroom.
2583
This is normally 4 but can be 5 for certain chatrooms.
2584
2585
=back
2586
2587
=head1 RATE LIMIT OVERVIEW
2588
2589
The OSCAR server has the ability to specify restrictions on the rate at which
2590
the client, your application, can send it commands. These constraints can be independently
2591
set and tracked for different classes of command, so there might be one limit on how
2592
fast you can send IMs and another on how fast you can request away messages.
2593
If your application exceeds these limits, the OSCAR server may start ignoring it or
2594
may even disconnect your session.
2595
2596
See also the reference section on L.
2597
2598
=head2 RATE MANAGEMENT MODES
2599
2600
C supports three different schemes for managing these limits. Pass the
2601
scheme you want to use as the value of the C key when you invoke the
2602
L<"new"> method.
2603
2604
=head3 OSCAR_RATE_MANAGE_NONE
2605
2606
The default. C will not keep track of what the limits are,
2607
much less how close you're coming to reaching them. If the OSCAR server complains
2608
that you are sending too fast, your L<"rate_alert"> callback will be triggered.
2609
2610
=head3 OSCAR_RATE_MANAGE_AUTO
2611
2612
In this mode, C will prevent your application from exceeding the limits.
2613
If you try to send a command which would cause the limits to be exceeded, your
2614
command will be queued. You will be notified when this happens via the L<"rate_alert">
2615
callback. B's
2616
L.>
2617
2618
=head3 OSCAR_RATE_MANAGE_MANUAL
2619
2620
In this mode, C will track what the limits are and how close you're
2621
coming to reaching them, but won't do anything about it. Your application should use the
2622
L<"rate_level">, L<"rate_limits">, and L<"would_make_rate_level"> methods to
2623
control its own rate.
2624
2625
=head1 TIME-DELAYED EVENTS
2626
2627
=head1 CONSTANTS
2628
2629
The following constants are defined when C is imported with the
2630
C<:standard> tag. Unless indicated otherwise, the constants are magical
2631
scalars - they return different values in string and numeric contexts (for
2632
instance, an error message and an error number.)
2633
2634
=over 4
2635
2636
=item ADMIN_TYPE_PASSWORD_CHANGE
2637
2638
=item ADMIN_TYPE_EMAIL_CHANGE
2639
2640
=item ADMIN_TYPE_SCREENNAME_FORMAT
2641
2642
=item ADMIN_TYPE_ACCOUNT_CONFIRM
2643
2644
=item ADMIN_ERROR_UNKNOWN
2645
2646
=item ADMIN_ERROR_BADPASS
2647
2648
=item ADMIN_ERROR_BADINPUT
2649
2650
=item ADMIN_ERROR_BADLENGTH
2651
2652
=item ADMIN_ERROR_TRYLATER
2653
2654
=item ADMIN_ERROR_REQPENDING
2655
2656
=item ADMIN_ERROR_CONNREF
2657
2658
=item VISMODE_PERMITALL
2659
2660
=item VISMODE_DENYALL
2661
2662
=item VISMODE_PERMITSOME
2663
2664
=item VISMODE_DENYSOME
2665
2666
=item VISMODE_PERMITBUDS
2667
2668
=item RATE_CLEAR
2669
2670
=item RATE_ALERT
2671
2672
=item RATE_LIMIT
2673
2674
=item RATE_DISCONNECT
2675
2676
=item OSCAR_RATE_MANAGE_NONE
2677
2678
=item OSCAR_RATE_MANAGE_AUTO
2679
2680
=item OSCAR_RATE_MANAGE_MANUAL
2681
2682
=item GROUPPERM_OSCAR
2683
2684
=item GROUPPERM_AOL
2685
2686
=item TYPINGSTATUS_STARTED
2687
2688
=item TYPINGSTATUS_TYPING
2689
2690
=item TYPINGSTATUS_FINISHED
2691
2692
=back
2693
2694
=head1 Net::AIM Compatibility
2695
2696
Here are the major differences between the C interface
2697
and the C interface:
2698
2699
=over 4
2700
2701
=item *
2702
2703
No get/set method.
2704
2705
=item *
2706
2707
No newconn/getconn method.
2708
2709
=item *
2710
2711
No group parameter for add_permit or add_deny.
2712
2713
=item *
2714
2715
Many differences in chat handling.
2716
2717
=item *
2718
2719
No chat_whisper.
2720
2721
=item *
2722
2723
No encode method - it isn't needed.
2724
2725
=item *
2726
2727
No send_config method - it isn't needed.
2728
2729
=item *
2730
2731
No send_buddies method - we don't keep a separate local buddylist.
2732
2733
=item *
2734
2735
No normalize method - it isn't needed. Okay, there is a normalize
2736
function in C, but I can't think of any reason
2737
why it would need to be used outside of the module internals. C
2738
provides the same functionality through the C class.
2739
2740
=item *
2741
2742
Different callbacks with different parameters.
2743
2744
=back
2745
2746
=head1 MISCELLANEOUS INFO
2747
2748
There are two programs included with the C distribution.
2749
C is half a reference implementation of a C client
2750
and half a tool for testing this library. C is a tool designed
2751
for analyzing the OSCAR protocol from libpcap-format packet captures, but
2752
it isn't particularly well-maintained; the Ethereal sniffer does a good
2753
job at this nowadays.
2754
2755
There is a class C. OSCAR screennames
2756
are case and whitespace insensitive, and if you do something like
2757
C<$buddy = new Net::OSCAR::Screenname "Matt Sachs"> instead of
2758
C<$buddy = "Matt Sachs">, this will be taken care of for you when
2759
you use the string comparison operators (eq, ne, cmp, etc.)
2760
2761
C, the class used for connection objects,
2762
has some methods that may or may not be useful to you.
2763
2764
=over 4
2765
2766
=item get_filehandle
2767
2768
Returns the filehandle used for the connection. Note that this is a method
2769
of C, not C.
2770
2771
=item process_one (CAN_READ, CAN_WRITE, HAS_ERROR)
2772
2773
Call this when a C is ready for reading and/or
2774
writing. You might call this yourself instead of using L<"process_connections">
2775
when, for instance, using the L<"connection_changed"> callback in conjunction with
2776
C instead of C. The C and C parameters
2777
should be non-zero if the connection is ready for the respective operations to be
2778
performed and zero otherwise. If and only if there was a socket error with the
2779
connection, set C to non-zero.
2780
2781
=item session
2782
2783
Returns the C object associated with this C.
2784
2785
=back
2786
2787
=head1 USER INFORMATION
2788
2789
Methods which return information about a user, such as L<"buddy">, will return
2790
the information in the form of a hash. The keys of the hash are the following --
2791
note that any of these may be absent.
2792
2793
=over 4
2794
2795
=item online
2796
2797
The user is signed on. If this key is not present, all of the other keys may not
2798
be present.
2799
2800
=item screenname
2801
2802
The formatted version of the user's screenname. This includes all spacing and
2803
capitalization. This is a C object, so you don't have to
2804
worry about the fact that it's case and whitespace insensitive when comparing it.
2805
2806
=item comment
2807
2808
A user-defined comment associated with the buddy. See L<"set_buddy_comment">.
2809
Note that this key will be present but undefined if there is no comment.
2810
2811
=item alias
2812
2813
A user-defined alias for the buddy. See L<"set_buddy_alias">.
2814
Note that this key will be present but undefined if there is no alias.
2815
2816
=item extended_status
2817
2818
The user's extended status message, if one is set, will be in this key.
2819
This requires that you set the C capability when
2820
creating the C object.
2821
2822
=item trial
2823
2824
The user's account has trial status.
2825
2826
=item aol
2827
2828
The user is accessing the AOL Instant Messenger service from America OnLine.
2829
2830
=item free
2831
2832
Opposite of aol.
2833
2834
=item away
2835
2836
The user is away.
2837
2838
=item admin
2839
2840
The user is an administrator.
2841
2842
=item mobile
2843
2844
The user is using a mobile device.
2845
2846
=item typing_status
2847
2848
The user is known to support typing status notification. We only find this out if they send us an IM.
2849
2850
=item capabilities
2851
2852
The user's capabilities. This is a reference to a hash whose keys are the user's capabilities, and
2853
whose values are descriptions of their respective capabilities.
2854
2855
=item icon
2856
2857
The user's buddy icon, if available.
2858
2859
=item icon_checksum
2860
2861
The checksum time of the user's buddy icon, if available. Use this, in conjunction with
2862
the L method, to cache buddy icons.
2863
2864
=item icon_timestamp
2865
2866
The modification timestamp of the user's buddy icon, if available.
2867
2868
=item icon_length
2869
2870
The length of the user's buddy icon, if available.
2871
2872
=item membersince
2873
2874
Time that the user's account was created, in the same format as the C function.
2875
2876
=item onsince
2877
2878
Time that the user signed on to the service, in the same format as the C function.
2879
2880
=item idle_since
2881
2882
Time, in seconds since Jan 1st 1970, since which the user has been idle. This will only
2883
be present if the user is idle. To figure out how long the user has been idle for,
2884
subtract this value from C .
2885
2886
=item evil
2887
2888
Evil (warning) level for the user.
2889
2890
=back
2891
2892
Some keys; namely, C and C, may be available for people
2893
who the user has communicated with but who are not on the user's buddylist.
2894
2895
=cut
2896
2897
2898
=pod
2899
2900
=head1 ICQ-SPECIFIC INFORMATION
2901
2902
ICQ support isn't nearly as well-tested as AIM support, and ICQ-specific
2903
features aren't being particularly actively developed. Patches for ICQ-isms
2904
are welcome. The initial patch enabling us to sign on to ICQ was provided by Sam Wong.
2905
2906
=head2 ICQ METHODS
2907
2908
=over 4
2909
2910
=item get_icq_info (UIN)
2911
2912
Requests ICQ-specific information. See also the L<"buddy_icq_info"> callback.
2913
2914
=cut
2915
2916
sub get_icq_info($$) {
2917
0
0
my($self, $uin) = @_;
2918
2919
0
$self->svcdo(CONNTYPE_BOS, protobit => "ICQ_meta_request", protodata => {
2920
our_uin => $self->{screenname},
2921
type => 2000,
2922
seqno => ++$self->{bos}->{icq_seqno},
2923
typedata => protoparse($self, "ICQ_meta_info_request")->pack(uin => $uin)
2924
});
2925
}
2926
2927
=pod
2928
2929
=back
2930
2931
=head2 ICQ CALLBACKS
2932
2933
=over 4
2934
2935
=item buddy_icq_info (OSCAR, UIN, ICQ DATA)
2936
2937
The result of a L<"get_icq_info"> call. Data is a hashref with the following keys, the value
2938
of each key is a either a hashref or undefined:
2939
2940
=over 4
2941
2942
=item basic
2943
2944
=over 4
2945
2946
=item nickname
2947
2948
=item firstname
2949
2950
=item lastname
2951
2952
=item email
2953
2954
=item gmt_offset
2955
2956
=item authorization
2957
2958
=item web_aware
2959
2960
=item direct_connect_permissions
2961
2962
=item publish_primary_email
2963
2964
=back
2965
2966
=item home
2967
2968
=over 4
2969
2970
=item city
2971
2972
=item state
2973
2974
=item phone_num
2975
2976
=item fax_num
2977
2978
=item address
2979
2980
=item cell_phone_num
2981
2982
=item zip_code
2983
2984
=item country_code
2985
2986
=back
2987
2988
=item office
2989
2990
=over 4
2991
2992
=item city
2993
2994
=item state
2995
2996
=item phone_num
2997
2998
=item fax_num
2999
3000
=item address
3001
3002
=item zip_code
3003
3004
=item country_code
3005
3006
=item company
3007
3008
=item department
3009
3010
=item position
3011
3012
=item occupation
3013
3014
=item office_website
3015
3016
=back
3017
3018
=item background
3019
3020
=over 4
3021
3022
=item age
3023
3024
=item gender
3025
3026
=item homepage
3027
3028
=item birth_year
3029
3030
=item birth_month
3031
3032
=item birth_day
3033
3034
=item spoken_languages
3035
3036
This key is a listref containing the langauges the user speaks.
3037
3038
=item origin_city
3039
3040
=item origin_state
3041
3042
=item origin_country
3043
3044
=item marital_status
3045
3046
=back
3047
3048
=item notes
3049
3050
This key is a simple scalar.
3051
3052
=item email_addresses
3053
3054
This key is a listref, each element of which is a hashref with the following keys:
3055
3056
=over 4
3057
3058
=item publish
3059
3060
=item address
3061
3062
=back
3063
3064
=item interests
3065
3066
This key is a listref, each element of which is a hashref with the following keys:
3067
3068
=over 4
3069
3070
=item category
3071
3072
=item interest
3073
3074
=back
3075
3076
=item past_affiliations
3077
3078
This key is a listref, each element of which is a hashref with the following keys:
3079
3080
=over 4
3081
3082
=item category
3083
3084
=item affiliation
3085
3086
=back
3087
3088
=item present_affiliations
3089
3090
As per above.
3091
3092
=item homepage
3093
3094
=over 4
3095
3096
=item category
3097
3098
=item keywords
3099
3100
=back
3101
3102
=back
3103
3104
=back
3105
3106
=cut
3107
3108
0
0
sub callback_buddy_icq_info(@) { do_callback("buddy_icq_info", @_); }
3109
0
0
sub set_callback_buddy_icq_info($\&) { set_callback("buddy_icq_info", @_); }
3110
3111
3112
=pod
3113
3114
=head1 HIGH-PERFORMANCE EVENT PROCESSING
3115
3116
A second way of doing event processing is designed to make it easy to integrate C into
3117
an existing C-based event loop, especially one where you have many C objects.
3118
Simply call the L<"process_connections"> method with references to the lists of readers, writers,
3119
and errors given to you by C. Connections that don't belong to the object will be ignored,
3120
and connections that do belong to the object will be removed from the C lists so that you
3121
can use the lists for your own purposes.
3122
Here is an example that demonstrates how to use this method with multiple C objects:
3123
3124
my $ein = $rin | $win;
3125
select($rin, $win, $ein, 0.01);
3126
foreach my $oscar(@oscars) {
3127
$oscar->process_connections(\$rin, \$win, \$ein);
3128
}
3129
3130
# Now $rin, $win, and $ein only have the file descriptors not
3131
# associated with any of the OSCAR objects in them - we can
3132
# process our events.
3133
3134
The third way of doing connection processing uses the L<"connection_changed">
3135
callback in conjunction with C's L<"process_one"> method.
3136
This method, in conjunction with C, probably offers the highest performance
3137
in situations where you have a long-lived application which creates and destroys many
3138
C sessions; that is, an application whose list of file descriptors to
3139
monitor will likely be sparse. However, this method is the most complicated.
3140
What you need to do is call C inside of the L<"connection_changed">
3141
callback. That part's simple. The tricky bit is figuring out which
3142
C's to call and how to call them. My recommendation
3143
for doing this is to use a hashmap whose keys are the file descriptors of everything
3144
you're monitoring in the C - the FDs can be retrieved by doing
3145
Cget_filehandle)> inside of the L<"connection_changed"> -
3146
and then calling C<@handles = $poll-Ehandles(POLLIN | POLLOUT | POLLERR | POLLHUP)>
3147
and walking through the handles.
3148
3149
For optimum performance, use the L<"connection_changed"> callback.
3150
3151
=head1 HISTORY
3152
3153
=over 4
3154
3155
=item *
3156
3157
1.925, 2006-02-06
3158
3159
=over 4
3160
3161
=item *
3162
3163
Many buddylist performance enhancements and bug fixes.
3164
3165
=item *
3166
3167
Added support for receiving dynamic buddylist changes from the server
3168
(C.)
3169
3170
=item *
3171
3172
Add support buddylist transfer (C.)
3173
3174
=item *
3175
3176
Miscellaneous performance and scalability enhancements.
3177
3178
=item *
3179
3180
Added experimental migration support.
3181
3182
=item *
3183
3184
Added advanced rate limit management API.
3185
3186
=item *
3187
3188
Added C server for testing.
3189
3190
=item *
3191
3192
Audited screennames exposed to application to verify that they are
3193
C objects everywhere.
3194
3195
=item *
3196
3197
Began work on file transfer.
3198
3199
=item *
3200
3201
Connection status fix for compatibility with POE.
3202
3203
=back
3204
3205
=item *
3206
3207
1.907, 2004-09-22
3208
3209
=over 4
3210
3211
=item *
3212
3213
Fixed assert failure on certain invalid input ("Buddy Trikill" crash)
3214
3215
=back
3216
3217
=item *
3218
3219
1.906, 2004-08-28
3220
3221
=over 4
3222
3223
=item *
3224
3225
Reorganized documentation
3226
3227
=back
3228
3229
=item *
3230
3231
1.904, 2004-08-26
3232
3233
=over 4
3234
3235
=item *
3236
3237
Add $Net::OSCAR::XML::NO_XML_CACHE to prevent use of cached XML parse tree,
3238
and skip tests if we can't load Test::More or XML::Parser.
3239
3240
=back
3241
3242
=item *
3243
3244
1.903, 2004-08-26
3245
3246
=over 4
3247
3248
=item *
3249
3250
Generate XML parse tree at module build time so that users don't need to have
3251
XML::Parser and expat installed.
3252
3253
=back
3254
3255
=item *
3256
3257
1.902, 2004-08-26
3258
3259
=over 4
3260
3261
=item *
3262
3263
Fixes to buddy icon upload and chat invitation decline
3264
3265
=item *
3266
3267
Increase performance by doing lazy generation of certain debugging info
3268
3269
=back
3270
3271
=item *
3272
3273
1.901, 2004-08-24
3274
3275
=over 4
3276
3277
=item *
3278
3279
Lots of buddylist-handling bug fixes; should fix intermittent buddylist modification errors
3280
and errors only seen when modifying certain screennames; Roy C. rocks.
3281
3282
=item *
3283
3284
We now require Perl 5.6.1.
3285
3286
=item *
3287
3288
Workaround for bug in Perl pre-5.8.4 which manifested as a "'basic OSCAR services' isn't numeric"
3289
warning followed by the program freezing.
3290
3291
=item *
3292
3293
C and C methods added.
3294
3295
=item *
3296
3297
Fixed a potential memory leak which could impact programs which create many transient Net::OSCAR
3298
objects.
3299
3300
=back
3301
3302
=item *
3303
3304
1.900, 2004-08-17
3305
3306
=over 4
3307
3308
=item *
3309
3310
Wrote new XML-based protocol back-end with reasonably comprehensive test-suite.
3311
Numerous protocol changes; we now emulate AOL's version 5.5 client.
3312
3313
=item *
3314
3315
Rewrote snacsnatcher, an OSCAR protocol analysis tool
3316
3317
=item *
3318
3319
Reorganized documentation
3320
3321
=item *
3322
3323
ICQ meta-info support: get_icq_info method, buddy_icq_info callback
3324
3325
=item *
3326
3327
Stealth mode support: is_stealth and set_stealth methods, stealth_changed callback, stealth signon key
3328
3329
=item *
3330
3331
More flexible unknown SNAC handling: snac_unknown callback
3332
3333
=item *
3334
3335
Application can give Net::OSCAR the MD5-hashed password instead of the cleartext password
3336
(pass_is_hashed signon key). This is useful if your application is storing user passwords.
3337
3338
=item *
3339
3340
Inability to set blocking on Win32 is no longer fatal. Silly platform.
3341
3342
=item *
3343
3344
Fixed chat functionality.
3345
3346
=back
3347
3348
=item *
3349
3350
1.11, 2004-02-13
3351
3352
=over 4
3353
3354
=item *
3355
3356
Fixed presence-related problems modifying some buddylists
3357
3358
=back
3359
3360
=item *
3361
3362
1.10, 2004-02-10
3363
3364
=over 4
3365
3366
=item *
3367
3368
Fixed idle time handling; user info hashes now have an 'idle_since' key,
3369
which you should use instead of the old 'idle' key. Subtract C
3370
from C to get the length of time for which the user has been idle.
3371
3372
=item *
3373
3374
Fixed buddylist type 5 handling; this fixes problems modifying the buddylists
3375
of recently-created screennames.
3376
3377
=back
3378
3379
=item *
3380
3381
1.01, 2004-01-06
3382
3383
=over 4
3384
3385
=item *
3386
3387
Fixed buddy ID generation (problems adding buddies)
3388
3389
=back
3390
3391
=item *
3392
3393
1.00, 2004-01-03
3394
3395
=over 4
3396
3397
=item *
3398
3399
Documented requirement to wait for buddylist_foo callback between calls to commit_buddylist
3400
3401
=item *
3402
3403
Fixed handling of idle time (zoyboy22)
3404
3405
=item *
3406
3407
More flexible signon method
3408
3409
=item *
3410
3411
Added buddy alias support
3412
3413
=item *
3414
3415
Buddy icon support
3416
3417
=item *
3418
3419
Typing notification support
3420
3421
=item *
3422
3423
mac.com screenname support
3424
3425
=item *
3426
3427
Support for communicating with ICQ users from AIM
3428
3429
=item *
3430
3431
iChat extended status message support
3432
3433
=item *
3434
3435
We now emulate AOL Instant Messenger for Windows 5.2
3436
3437
=item *
3438
3439
We now parse the capabilities of other users
3440
3441
=item *
3442
3443
Attempts at Win32 (non-cygwin) support
3444
3445
=back
3446
3447
=item *
3448
3449
0.62, 2002-02-25
3450
3451
=over 4
3452
3453
=item *
3454
3455
Error handling slightly improved; error 29 is no longer unknown.
3456
3457
=item *
3458
3459
A minor internal buddylist enhancement
3460
3461
=item *
3462
3463
snacsnatcher fixes
3464
3465
=back
3466
3467
=item *
3468
3469
0.61, 2002-02-17
3470
3471
=over 4
3472
3473
=item *
3474
3475
Fixed connection handling
3476
3477
=back
3478
3479
=item *
3480
3481
0.60, 2002-02-17
3482
3483
=over 4
3484
3485
=item *
3486
3487
Various connection_changed fixes, including the new readwrite status.
3488
3489
=item *
3490
3491
Added Net::OSCAR::Connection::session method
3492
3493
=item *
3494
3495
Improved Net::OSCAR::Connection::process_one, documented it, and documented using it
3496
3497
=back
3498
3499
=item *
3500
3501
0.59, 2002-02-15
3502
3503
=over 4
3504
3505
=item *
3506
3507
Protocol fixes - solves problem with AOL calling us an unauthorized client
3508
3509
=item *
3510
3511
Better handling of socket errors, especially when writing
3512
3513
=item *
3514
3515
Minor POD fixes
3516
3517
=back
3518
3519
=item *
3520
3521
0.58, 2002-01-20
3522
3523
=over 4
3524
3525
=item *
3526
3527
Send buddylist deletions before adds - needed for complex BL mods (loadbuddies)
3528
3529
=item *
3530
3531
Added hooks to allow client do MD5 digestion for authentication (auth_challenge
3532
callback, Net::OSCAR::auth_response method)
3533
3534
=back
3535
3536
=item *
3537
3538
0.57, 2002-01-16
3539
3540
=over 4
3541
3542
=item *
3543
3544
Send callback_chat_joined correctly when joining an existing chat
3545
3546
=item *
3547
3548
Don't activate OldPerl fixes for perl 5.6.0
3549
3550
=item *
3551
3552
Ignore chats that we're already in
3553
3554
=back
3555
3556
=item *
3557
3558
0.56, 2002-01-16
3559
3560
=over 4
3561
3562
=item *
3563
3564
Fixed rate handling
3565
3566
=item *
3567
3568
Send multiple buddylist modifications per SNAC
3569
3570
=item *
3571
3572
Detect when someone else signs on with your screenname
3573
3574
=item *
3575
3576
Corrected attribution of ICQ support
3577
3578
=back
3579
3580
=item *
3581
3582
0.55, 2001-12-29
3583
3584
=over 4
3585
3586
=item *
3587
3588
Preliminary ICQ support, courtesy of SDiZ Chen (actually, Sam Wong).
3589
3590
=item *
3591
3592
Restored support for pre-5.6 perls - reverted from C to C.
3593
3594
=item *
3595
3596
Corrected removal of buddylist entries and other buddylist-handling improvements
3597
3598
=item *
3599
3600
Improved rate handling - new C parameter to rate_alert callback
3601
3602
=item *
3603
3604
Removed remaining C from C
3605
3606
=item *
3607
3608
Added is_on method
3609
3610
=back
3611
3612
=item *
3613
3614
0.50, 2001-12-23
3615
3616
=over 4
3617
3618
=item *
3619
3620
Fixes for the "crap out on 'connection reset by peer'" and "get stuck and slow down in Perl_sv_2bool" bugs!
3621
3622
=item *
3623
3624
Correct handling of very large (over 100 items) buddylists.
3625
3626
=item *
3627
3628
We can now join exchange 5 chats.
3629
3630
=item *
3631
3632
Fixes in modifying permit mode.
3633
3634
=item *
3635
3636
Updated copyright notice courtesy of AOL's lawyers.
3637
3638
=item *
3639
3640
Switch to IO::Socket for portability in set_blocking.
3641
3642
=back
3643
3644
=item *
3645
3646
0.25, 2001-11-26
3647
3648
=over 4
3649
3650
=item *
3651
3652
Net::OSCAR is now in beta!
3653
3654
=item *
3655
3656
We now work with perl 5.005 and even 5.004
3657
3658
=item *
3659
3660
Try to prevent weird Net::OSCAR::Screenname bug where perl gets stuck in Perl_sv_2bool
3661
3662
=item *
3663
3664
Fixed problems with setting visibility mode and adding to deny list (thanks, Philip)
3665
3666
=item *
3667
3668
Added some methods to allow us to be POE-ified
3669
3670
=item *
3671
3672
Added guards around a number of methods to prevent the user from trying to do stuff before s/he's finished signing on.
3673
3674
=item *
3675
3676
Fix *incredibly* stupid error in NO_to_BLI that ate group names
3677
3678
=item *
3679
3680
Fixed bad bug in log_printf
3681
3682
=item *
3683
3684
Buddylist error handling changes
3685
3686
=item *
3687
3688
Added chat_decline command
3689
3690
=item *
3691
3692
Signon, signoff fixes
3693
3694
=item *
3695
3696
Allow AOL screennames to sign on
3697
3698
=item *
3699
3700
flap_get crash fixes
3701
3702
=back
3703
3704
=item *
3705
3706
0.09, 2001-10-01
3707
3708
=over 4
3709
3710
=item *
3711
3712
Crash and undefined value fixes
3713
3714
=item *
3715
3716
New method: im_ok
3717
3718
=item *
3719
3720
New method: rename_group, should fix "Couldn't get group name" error.
3721
3722
=item *
3723
3724
Fix for buddy_in callback and data
3725
3726
=item *
3727
3728
Better error handling when we can't resolve a host
3729
3730
=item *
3731
3732
Vastly improved logging infrastructure - debug_print(f) replaced with log_print(f). debug_print callback is now called log and has an extra parameter.
3733
3734
=item *
3735
3736
Fixed MANIFEST - we don't actually use Changes (and we do use Screenname.pm)
3737
3738
=item *
3739
3740
blinternal now automagically enforces the proper structure (the right things become Net::OSCAR::TLV tied hashes and the name and data keys are automatically created) upon vivification. So, you can do $bli-E{0}-E{1}-E{2}-E{data}-E{0x3} = "foo" without worrying if 0, 1, 2, or data have been tied. Should close bug #47.
3741
3742
=back
3743
3744
=item *
3745
3746
0.08, 2001-09-07
3747
3748
=over 4
3749
3750
=item *
3751
3752
Totally rewritten buddylist handling. It is now much cleaner, bug-resistant,
3753
and featureful.
3754
3755
=item *
3756
3757
Many, many internal changes that I don't feel like enumerating.
3758
Hey, there's a reason that I haven't declared the interface stable yet! ;)
3759
3760
=item *
3761
3762
New convenience object: Net::OSCAR::Screenname
3763
3764
=item *
3765
3766
Makefile.PL: Fixed perl version test and compatibility with BSD make
3767
3768
=back
3769
3770
=item *
3771
3772
0.07, 2001-08-13
3773
3774
=over 4
3775
3776
=item *
3777
3778
A bunch of Makefile.PL fixes
3779
3780
=item *
3781
3782
Fixed spurious admin_error callback and prevent user from having multiple
3783
pending requests of the same type. (closes #39)
3784
3785
=item *
3786
3787
Head off some potential problems with set_visibility. (closes #34)
3788
3789
=item *
3790
3791
Removed connections method, added selector_filenos
3792
3793
=item *
3794
3795
Added error number 29 (too many recent signons from your site) to Net::OSCAR::Common.
3796
3797
=item *
3798
3799
We now explicitly perl 5.6.0 or newer.
3800
3801
=back
3802
3803
=item *
3804
3805
0.06, 2001-08-12
3806
3807
=over 4
3808
3809
=item *
3810
3811
Prevent sending duplicate signon_done messages
3812
3813
=item *
3814
3815
Don't addconn after crapping out!
3816
3817
=item *
3818
3819
Don't try to delconn unless we have connections.
3820
3821
=item *
3822
3823
delete returns the correct value now in Net::OSCAR::Buddylist.
3824
3825
=item *
3826
3827
Don't use warnings if $] E= 5.005
3828
3829
=item *
3830
3831
evil is a method, not a manpage (doc fix)
3832
3833
=item *
3834
3835
Added buddyhash method.
3836
3837
=item *
3838
3839
Added a debug_print callback.
3840
3841
=item *
3842
3843
Clarified process_connections method in documentation
3844
3845
=item *
3846
3847
You can now specify an alternate host/port in signon
3848
3849
=item *
3850
3851
Added name method to Chat.
3852
3853
=item *
3854
3855
permit list and deny list are no longer part of buddylist
3856
3857
=item *
3858
3859
Rewrote buddylist parsing (again!)
3860
3861
=item *
3862
3863
No more default profile.
3864
3865
=item *
3866
3867
Fix bug when storing into an already-existing key in Net::OSCAR::Buddylist.
3868
3869
=item *
3870
3871
snacsnatcher: Remove spurious include of Net::OSCAR::Common
3872
3873
=item *
3874
3875
We don't need to handle VISMODE_PERMITBUDS ourself - the server takes care of it.
3876
Thanks, VB!
3877
3878
=item *
3879
3880
Makefile.PL: Lots of way cool enhancements to make dist:
3881
3882
=over 4
3883
3884
=item -
3885
3886
It modifies the version number for us
3887
3888
=item -
3889
3890
It does a CVS rtag
3891
3892
=item -
3893
3894
It updates the HTML documentation on zevils and the README.
3895
3896
=back
3897
3898
=item *
3899
3900
Added HISTORY and INSTALLATION section to POD.
3901
3902
=back
3903
3904
=item *
3905
3906
0.05, 2001-08-08
3907
3908
=over 4
3909
3910
=item *
3911
3912
Don't send signon_done until after we get buddylist.
3913
3914
=item *
3915
3916
Added signoff method.
3917
3918
=item *
3919
3920
Fixed typo in documentation
3921
3922
=item *
3923
3924
Fixed chat_invite parm count
3925
3926
=item *
3927
3928
Added Scalar::Utils::dualvar variables, especially to Common.pm.
3929
dualvar variables return different values in numeric and string context.
3930
3931
=item *
3932
3933
Added url method for Net::OSCAR::Chat (closes #31)
3934
3935
=item *
3936
3937
Divide evil by 10 in extract_userinfo (closes #30)
3938
3939
=item *
3940
3941
chat_invite now exposes chatname (closes #32)
3942
3943
=item *
3944
3945
Removed unnecessary and warning-generating session length from extract_userinfo
3946
3947
=back
3948
3949
=item *
3950
3951
0.01, 2001-08-02
3952
3953
=over 4
3954
3955
=item *
3956
3957
Initial release.
3958
3959
=back
3960
3961
=back
3962
3963
=head1 SUPPORT
3964
3965
See http://www.zevils.com/programs/net-oscar/ for support, including
3966
a mailing list and bug-tracking system.
3967
3968
=head1 AUTHOR
3969
3970
Matthew Sachs Ematthewg@zevils.comE.
3971
3972
=head1 CREDITS
3973
3974
AOL, for creating the AOL Instant Messenger service, even though they aren't terribly helpful to
3975
developers of third-party clients.
3976
3977
Apple Computer for help with mac.com support.
3978
3979
The users of IMIRC for being reasonably patient while this module was developed. Ehttp://www.zevils.com/programs/imirc/E
3980
3981
Bill Atkins for typing status notification and mobile user support. Ehttp://www.milkbone.org/E
3982
3983
Jayson Baker for some last-minute debugging help.
3984
3985
Roy Camp for loads of bug reports and ideas and helping with user support.
3986
3987
Rocco Caputo for helping to work out the hooks that let use be used with
3988
POE. Ehttp://poe.perl.org/E
3989
3990
Mark Doliner for help with remote buddylists. Ehttp://kingant.net/libfaim/ReadThis.htmlE
3991
3992
Adam Fritzler and the libfaim team for their documentation and an OSCAR implementation that
3993
was used to help figure out a lot of the protocol details. Ehttp://www.zigamorph.net/faim/protocol/E
3994
3995
The gaim team - the source to their libfaim client was also very helpful. Ehttp://gaim.sourceforge.net/E
3996
3997
Nick Gray for sponsoring scalability work.
3998
3999
John "VBScript" for a lot of technical assistance, including the explanation of rates.
4000
4001
Jonathon Wodnicki for additional help with typing status notification.
4002
4003
Sam Wong Esam@uhome.netE for a patch implementing ICQ2000 support.
4004
4005
=head1 LEGAL
4006
4007
Copyright (c) 2001 Matthew Sachs. All rights reserved.
4008
This program is free software; you can redistribute it and/or modify it under the
4009
same terms as Perl itself. B and B are registered trademarks
4010
owned by America Online, Inc. The B mark is owned by America
4011
Online, Inc. B is a trademark and/or servicemark of ICQ. C is not
4012
endorsed by, or affiliated with, America Online, Inc or ICQ. B and B
4013
are registered trademarks of Apple Computer, Inc. C is not endorsed by,
4014
or affiliated with, Apple Computer, Inc or iChat.
4015
4016
=cut
4017
4018
4019
4020
### Private methods
4021
4022
sub addconn($@) {
4023
0
0
my $self = shift;
4024
0
my %data = @_;
4025
4026
0
$data{session} = $self;
4027
0
weaken($data{session});
4028
4029
0
my $connection;
4030
0
my $conntype = $data{conntype};
4031
0
0
$data{description} ||= $conntype;
4032
4033
0
0
if($conntype == CONNTYPE_CHAT) {
0
0
0
4034
0
require Net::OSCAR::Connection::Chat;
4035
0
$connection = Net::OSCAR::Connection::Chat->new(%data);
4036
} elsif($conntype == CONNTYPE_DIRECT_IN) {
4037
0
require Net::OSCAR::Connection::Direct;
4038
0
$connection = Net::OSCAR::Connection::Direct->new(%data);
4039
0
$connection->listen();
4040
} elsif($conntype == CONNTYPE_DIRECT_OUT) {
4041
0
require Net::OSCAR::Connection::Direct;
4042
0
$connection = Net::OSCAR::Connection::Direct->new(%data);
4043
} elsif($conntype == CONNTYPE_SERVER) {
4044
0
require Net::OSCAR::Connection::Server;
4045
0
$connection = Net::OSCAR::Connection::Server->new(%data);
4046
} else {
4047
0
$connection = Net::OSCAR::Connection->new(%data);
4048
# We set the connection to 1 to indicate that it is in progress but not ready for SNAC-sending yet.
4049
0
0
$self->{services}->{$conntype} = 1 unless $conntype == CONNTYPE_CHAT;
4050
}
4051
4052
0
0
if($conntype == CONNTYPE_BOS) {
4053
0
$self->{services}->{$conntype} = $connection;
4054
}
4055
4056
0
push @{$self->{connections}}, $connection;
0
4057
0
$self->callback_connection_changed($connection, $connection->{state});
4058
0
return $connection;
4059
}
4060
4061
sub delconn($$) {
4062
0
0
my($self, $connection) = @_;
4063
4064
0
0
return unless $self->{connections};
4065
0
0
$self->callback_connection_changed($connection, "deleted") if $connection->{socket};
4066
0
for(my $i = scalar @{$self->{connections}} - 1; $i >= 0; $i--) {
0
4067
0
0
next unless $self->{connections}->[$i] == $connection;
4068
0
$connection->log_print(OSCAR_DBG_NOTICE, "Closing.");
4069
0
splice @{$self->{connections}}, $i, 1;
0
4070
0
0
if(!$connection->{sockerr}) {
4071
0
eval {
4072
0
0
0
if($connection->{socket} and $connection->{conntype} != CONNTYPE_DIRECT_IN and $connection->{conntype} != CONNTYPE_DIRECT_OUT) {
0
4073
0
$connection->flap_put("", FLAP_CHAN_CLOSE);
4074
}
4075
0
0
close $connection->{socket} if $connection->{socket};
4076
};
4077
} else {
4078
0
0
delete $self->{services}->{$connection->{conntype}} unless $connection->{conntype} == CONNTYPE_CHAT;
4079
4080
0
0
0
if($connection->{conntype} == CONNTYPE_BOS or ($connection->{conntype} == CONNTYPE_LOGIN and !$connection->{closing})) {
0
0
0
4081
0
delete $connection->{socket};
4082
0
return $self->crapout($connection, "Lost connection to BOS");
4083
} elsif($connection->{conntype} == CONNTYPE_ADMIN) {
4084
0
0
$self->callback_admin_error("all", ADMIN_ERROR_CONNREF, undef) if scalar(keys(%{$self->{adminreq}}));
0
4085
} elsif($connection->{conntype} == CONNTYPE_CHAT) {
4086
0
$self->callback_chat_closed($connection, "Lost connection to chat");
4087
} else {
4088
0
$self->log_print(OSCAR_DBG_NOTICE, "Closing connection ", $connection->{conntype});
4089
}
4090
}
4091
0
delete $connection->{socket};
4092
0
return 1;
4093
}
4094
0
return 0;
4095
}
4096
4097
sub DESTROY {
4098
0
0
my $self = shift;
4099
0
0
return if $Net::OSCAR::NODESTROY;
4100
4101
0
foreach my $connection(@{$self->{connections}}) {
0
4102
0
0
0
next unless $connection->{socket} and not $connection->{sockerr};
4103
0
$connection->flap_put("", FLAP_CHAN_CLOSE);
4104
0
0
close $connection->{socket} if $connection->{socket};
4105
}
4106
}
4107
4108
sub findgroup($$) {
4109
0
0
my($self, $groupid) = @_;
4110
0
my($group, $currgroup, $currid);
4111
4112
0
my $thegroup = undef;
4113
4114
0
while(($group, $currgroup) = each(%{$self->{buddies}})) {
0
4115
0
0
next if $group eq "__BLI_DIRTY";
4116
0
0
0
next unless exists($currgroup->{groupid}) and $groupid == $currgroup->{groupid};
4117
0
0
next if $currgroup->{__BLI_DELETED};
4118
0
$thegroup = $group;
4119
0
hash_iter_reset(\%{$self->{buddies}}); # Reset the iterator
0
4120
0
last;
4121
}
4122
0
return $thegroup;
4123
}
4124
4125
sub findbuddy_byid($$$) {
4126
0
0
my($self, $buddies, $bid) = @_;
4127
4128
0
while(my($buddy, $value) = each(%$buddies)) {
4129
0
0
0
if($value->{buddyid} == $bid and !$value->{__BLI_DELETED}) {
4130
0
hash_iter_reset(\%$buddies); # reset the iterator
4131
0
return $buddy;
4132
}
4133
}
4134
0
return undef;
4135
}
4136
4137
sub newid($;$) {
4138
0
0
my($self, $group) = @_;
4139
0
my $id = 4;
4140
0
my %ids = ();
4141
4142
0
0
if($group) {
4143
0
%ids = map { $_->{buddyid} => 1 } values %$group;
0
4144
0
0
do { ++$id; } while($ids{$id}) or $id < 4;
0
4145
} else {
4146
0
do { $id = ++$self->{nextid}->{__GROUPID__}; } while($self->findgroup($id));
0
4147
}
4148
0
return $id;
4149
}
4150
4151
sub capabilities($) {
4152
0
0
my $self = shift;
4153
4154
0
my @caps;
4155
4156
0
push @caps, OSCAR_CAPS()->{chat}->{value}, OSCAR_CAPS()->{interoperate}->{value};
4157
0
0
push @caps, OSCAR_CAPS()->{extstatus}->{value} if $self->{capabilities}->{extended_status};
4158
0
0
push @caps, OSCAR_CAPS()->{buddyicon}->{value} if $self->{capabilities}->{buddy_icons};
4159
0
0
push @caps, OSCAR_CAPS()->{filexfer}->{value} if $self->{capabilities}->{file_transfer};
4160
0
0
push @caps, OSCAR_CAPS()->{fileshare}->{value} if $self->{capabilities}->{file_sharing};
4161
0
0
push @caps, OSCAR_CAPS()->{sendlist}->{value} if $self->{capabilities}->{buddy_list_transfer};
4162
4163
0
return \@caps;
4164
}
4165
4166
sub mod_permit($$$@) {
4167
0
0
my($self, $action, $group, @buddies) = @_;
4168
4169
0
0
return must_be_on($self) unless $self->{is_on};
4170
0
0
if($action == MODBL_ACTION_ADD) {
4171
0
foreach my $buddy(@buddies) {
4172
0
0
next if exists($self->{$group}->{$buddy});
4173
0
$self->{$group}->{$buddy}->{buddyid} = $self->newid($self->{$group});
4174
}
4175
} else {
4176
0
foreach my $buddy(@buddies) {
4177
0
delete $self->{$group}->{$buddy};
4178
}
4179
}
4180
}
4181
4182
sub mod_buddylist($$$$;@) {
4183
0
0
my($self, $action, $what, $group, @buddies) = @_;
4184
0
0
return must_be_on($self) unless $self->{is_on};
4185
4186
0
0
if($group eq "__BLI_DIRTY") {
4187
0
send_error($self, $self->{bos}, "Invalid group name", "__BLI_DIRTY is a reserved group name.", 0);
4188
0
return;
4189
}
4190
4191
0
0
@buddies = ($group) if $what == MODBL_WHAT_GROUP;
4192
4193
0
0
0
if($what == MODBL_WHAT_GROUP and $action == MODBL_ACTION_ADD) {
0
0
0
0
0
0
4194
0
0
0
return if exists $self->{buddies}->{$group} and !$self->{buddies}->{$group}->{__BLI_DELETED};
4195
4196
0
$self->{buddies}->{__BLI_DIRTY} = 1;
4197
4198
# Maybe group was deleted and then recreated
4199
0
0
if(exists $self->{buddies}->{$group}) {
4200
0
my $grp = $self->{buddies}->{$group};
4201
0
$grp->{__BLI_DIRTY} = 1;
4202
0
$grp->{__BLI_DELETED} = 0;
4203
0
$grp->{data} = tlv();
4204
0
$_->{__BLI_DELETED} = 1 foreach values %{$grp->{members}};
0
4205
} else {
4206
0
$self->{buddies}->{$group} = {
4207
groupid => $self->newid(),
4208
members => bltie(),
4209
data => tlv(),
4210
__BLI_DIRTY => 1,
4211
__BLI_DELETED => 0,
4212
};
4213
}
4214
} elsif($what == MODBL_WHAT_GROUP and $action == MODBL_ACTION_DEL) {
4215
0
0
return unless exists $self->{buddies}->{$group};
4216
0
$self->{buddies}->{__BLI_DIRTY} = 1;
4217
0
$self->{buddies}->{$group}->{__BLI_DELETED} = 1;
4218
} elsif($what == MODBL_WHAT_BUDDY and $action == MODBL_ACTION_ADD) {
4219
4220
0
0
0
$self->mod_buddylist(MODBL_ACTION_ADD, MODBL_WHAT_GROUP, $group) unless
4221
exists $self->{buddies}->{$group} and
4222
not $self->{buddies}->{$group}->{__BLI_DELETED};
4223
4224
0
my $grp = $self->{buddies}->{$group};
4225
0
0
@buddies = grep {
4226
0
not (
4227
exists $grp->{members}->{$_} and
4228
not $grp->{members}->{$_}->{__BLI_DELETED}
4229
)
4230
} @buddies;
4231
0
0
return unless @buddies;
4232
4233
0
$grp->{__BLI_DIRTY} = 1;
4234
4235
0
foreach my $buddy(@buddies) {
4236
# Buddy may have been deleted and recreated
4237
0
0
if(exists($grp->{members}->{$buddy})) {
4238
0
my $bud = $grp->{members}->{$buddy};
4239
0
$bud->{__BLI_DIRTY} = 1;
4240
0
$bud->{__BLI_DELETED} = 0;
4241
0
$bud->{data} = tlv();
4242
0
$bud->{comment} = undef;
4243
0
$bud->{alias} = undef;
4244
} else {
4245
0
$grp->{members}->{$buddy} = {
4246
buddyid => $self->newid($grp->{members}),
4247
screenname => Net::OSCAR::Screenname->new($buddy),
4248
data => tlv(),
4249
online => 0,
4250
comment => undef,
4251
alias => undef,
4252
__BLI_DIRTY => 1,
4253
__BLI_DELETED => 0,
4254
};
4255
}
4256
}
4257
} elsif($what == MODBL_WHAT_BUDDY and $action == MODBL_ACTION_DEL) {
4258
0
0
return unless exists $self->{buddies}->{$group};
4259
4260
0
my $grp = $self->{buddies}->{$group};
4261
0
0
@buddies = grep {
4262
0
exists $grp->{members}->{$_} and
4263
not $grp->{members}->{$_}->{__BLI_DELETED}
4264
} @buddies;
4265
0
0
return unless @buddies;
4266
4267
0
$grp->{__BLI_DIRTY} = 1;
4268
4269
0
foreach my $buddy(@buddies) {
4270
0
$grp->{members}->{$buddy}->{__BLI_DELETED} = 1;
4271
}
4272
0
$self->mod_buddylist(MODBL_ACTION_DEL, MODBL_WHAT_GROUP, $group) unless scalar
4273
0
grep { not $grp->{members}->{$_}->{__BLI_DELETED} }
4274
0
0
keys %{$grp->{members}};
4275
}
4276
}
4277
4278
sub postprocess_userinfo($$) {
4279
0
0
my($self, $userinfo) = @_;
4280
4281
0
Net::OSCAR::Screenname->new(\$userinfo->{screenname});
4282
4283
0
0
if($userinfo->{idle}) {
4284
0
$userinfo->{idle} *= 60;
4285
0
$userinfo->{idle_since} = time() - $userinfo->{idle};
4286
}
4287
0
0
$userinfo->{evil} /= 10 if exists($userinfo->{evil});
4288
0
0
if(exists($userinfo->{flags})) {
4289
0
my $flags = $userinfo->{flags};
4290
0
$userinfo->{trial} = $flags & 0x1;
4291
0
$userinfo->{admin} = $flags & 0x2;
4292
0
$userinfo->{aol} = $flags & 0x4;
4293
0
$userinfo->{pay} = $flags & 0x8;
4294
0
$userinfo->{free} = $flags & 0x10;
4295
0
$userinfo->{away} = $flags & 0x20;
4296
0
$userinfo->{mobile} = $flags & 0x80;
4297
}
4298
4299
0
0
if(exists($userinfo->{capabilities})) {
4300
0
my $capabilities = delete $userinfo->{capabilities};
4301
0
foreach my $capability (@$capabilities) {
4302
0
$self->log_print(OSCAR_DBG_DEBUG, "Got a capability.");
4303
0
0
if(OSCAR_CAPS_INVERSE()->{$capability}) {
4304
0
my $capname = OSCAR_CAPS_INVERSE()->{$capability};
4305
0
$self->log_print(OSCAR_DBG_DEBUG, "Got capability $capname.");
4306
0
$userinfo->{capabilities}->{$capname} = OSCAR_CAPS()->{$capname}->{description};
4307
} else {
4308
0
0
$self->log_print_cond(OSCAR_DBG_INFO, sub { "Unknown capability: ", hexdump($capability) });
0
4309
}
4310
}
4311
}
4312
4313
0
0
if(exists($userinfo->{icon_md5sum})) {
4314
0
0
0
if(!exists($self->{userinfo}->{$userinfo->{screenname}})
0
4315
or !exists($self->{userinfo}->{$userinfo->{screenname}}->{icon_md5sum})
4316
or $self->{userinfo}->{$userinfo->{screenname}}->{icon_md5sum} ne $userinfo->{icon_md5sum}) {
4317
0
$self->callback_new_buddy_icon($userinfo->{screenname}, $userinfo);
4318
}
4319
}
4320
}
4321
4322
sub send_message($$$$;$$) {
4323
0
0
my($self, $recipient, $channel, $body, $flags2, $cookie) = @_;
4324
0
0
$flags2 ||= 0;
4325
4326
0
my $reqid = (8<<16) | (unpack("n", randchars(2)))[0];
4327
0
0
my %protodata = (
4328
cookie => $cookie ? $cookie : randchars(8),
4329
channel => $channel,
4330
screenname => $recipient,
4331
message_body => $body,
4332
);
4333
0
$self->svcdo(CONNTYPE_BOS, reqdata => $recipient, reqid => $reqid, protobit => "outgoing_IM", protodata => \%protodata, flags2 => $flags2);
4334
4335
0
return ($reqid, $protodata{cookie});
4336
}
4337
4338
sub rendezvous_revise($$;$) {
4339
0
0
my($self, $cookie, $ip) = @_;
4340
0
0
return unless exists($self->{rv_proposals}->{$cookie});
4341
0
my $proposal = $self->{rv_proposals}->{$cookie};
4342
4343
0
0
if($proposal->{connection}) {
4344
0
$self->delconn($proposal->{connection});
4345
0
delete $proposal->{connection};
4346
}
4347
4348
0
0
if(!$ip) {
4349
0
croak "OSCAR server FT proxy not yet supported!";
4350
}
4351
4352
0
my $connection = $self->addconn(conntype => CONNTYPE_DIRECT_IN);
4353
0
my($port) = sockaddr_in(getsockname($connection->{socket}));
4354
4355
0
my %protodata = (
4356
capability => OSCAR_CAPS()->{filexfer}->{value},
4357
cookie => $proposal->{cookie},
4358
status => "propose",
4359
client_1_ip => $self->{ip},
4360
client_2_ip => $self->{ip},
4361
port => $port,
4362
);
4363
0
$proposal->{connection} = $connection;
4364
0
$proposal->{ft_state} = "listening";
4365
0
$proposal->{accepted} = 0;
4366
0
$proposal->{tried_listen} = 1;
4367
4368
0
my($req_id) = $self->send_message($proposal->{peer}, 2, protoparse($self, "rendezvous_IM")->pack(%protodata), 0, $cookie);
4369
}
4370
4371
sub rendezvous_proxy_host($) {
4372
0
0
return "ars.oscar.aol.com";
4373
}
4374
4375
sub rendezvous_negotiate($$) {
4376
0
0
my($self, $cookie) = @_;
4377
0
0
return unless exists($self->{rv_proposals}->{$cookie});
4378
0
my $proposal = $self->{rv_proposals}->{$cookie};
4379
4380
0
0
0
if($proposal->{tried_connect} or !$proposal->{ip} or $proposal->{ip} eq "0.0.0.0" or $proposal->{ip} eq "255.255.255.255") {
0
0
4381
0
$self->log_print(OSCAR_DBG_DEBUG, "Negotiating rendezvous.");
4382
4383
# If we haven't tried hosting the connection and it
4384
# doesn't look like we're behind NAT, or we have
4385
# a designated file transfer IP, try hosting.
4386
# Otherwise, use the proxy.
4387
#
4388
0
0
0
if(!$proposal->{tried_listen} and
0
0
0
4389
$self->{ft_ip} or ($self->{ip} and $self->{bos}->local_ip eq $self->{ip})
4390
) {
4391
0
$self->log_print(OSCAR_DBG_DEBUG, "Hosting.");
4392
0
0
$self->rendezvous_revise($cookie, $self->{ft_ip} || $self->{ip});
4393
0
$proposal->{using_proxy} = 0;
4394
0
$proposal->{tried_listen} = 1;
4395
0
$proposal->{ft_state} = "listening";
4396
0
return;
4397
} elsif(!$proposal->{tried_proxy}) {
4398
0
$self->log_print(OSCAR_DBG_DEBUG, "Using proxy.");
4399
0
$proposal->{using_proxy} = 1;
4400
0
$proposal->{tried_proxy} = 1;
4401
0
$proposal->{ft_state} = "proxy_connect";
4402
0
$proposal->{ip} = $self->rendezvous_proxy_host();
4403
} else {
4404
0
$self->rendezvous_reject($cookie);
4405
0
$self->log_printf(OSCAR_DBG_WARN, "Couldn't figure out how to connect for file transfer (%s, %s).", $proposal->{ip}, $proposal->{proxy});
4406
0
return;
4407
}
4408
} else {
4409
0
$proposal->{using_proxy} = 0;
4410
0
$proposal->{tried_connect} = 1;
4411
0
$proposal->{ft_state} = "connecting";
4412
}
4413
4414
0
return 1;
4415
}
4416
4417
sub rendezvous_accept($$) {
4418
0
0
my($self, $cookie) = @_;
4419
0
0
return unless exists($self->{rv_proposals}->{$cookie});
4420
0
my $proposal = $self->{rv_proposals}->{$cookie};
4421
4422
0
0
return unless $self->rendezvous_negotiate($cookie);
4423
4424
0
$self->log_printf(OSCAR_DBG_INFO, "Establishing rendezvous connection to %s:%d", $proposal->{ip}, $proposal->{port});
4425
0
0
$proposal->{ip} .= ":" . $proposal->{port} if $proposal->{port};
4426
0
my $newconn = $self->addconn(
4427
conntype => CONNTYPE_DIRECT_OUT,
4428
peer => $proposal->{ip},
4429
0
description => "transfer of files: " . join(", ", @{$proposal->{filenames}}),
4430
rv => $proposal,
4431
);
4432
0
$proposal->{connection} = $newconn;
4433
}
4434
4435
sub rendezvous_reject($$) {
4436
0
0
my($self, $cookie) = @_;
4437
4438
0
0
return unless exists($self->{rv_proposals}->{$cookie});
4439
0
my $proposal = delete $self->{rv_proposals}->{$cookie};
4440
4441
0
my %protodata;
4442
0
$protodata{status} = "cancel";
4443
0
$protodata{cookie} = $cookie;
4444
0
0
$protodata{capability} = OSCAR_CAPS()->{$proposal->{type}} ? OSCAR_CAPS()->{$proposal->{type}}->{value} : $proposal->{type};
4445
4446
0
return $self->send_message($proposal->{sender}, 2, protoparse($self, "rendezvous_IM")->pack(%protodata));
4447
}
4448
4449
sub svcdo($$%) {
4450
0
0
my($self, $service, %data) = @_;
4451
4452
0
0
0
if($self->{services}->{$service} and ref($self->{services}->{$service})) {
4453
0
$self->{services}->{$service}->proto_send(%data);
4454
} else {
4455
0
push @{$self->{svcqueues}->{$service}}, \%data;
0
4456
0
0
$self->svcreq($service) unless $self->{services}->{$service};
4457
}
4458
}
4459
4460
sub svcreq($$;@) {
4461
0
0
my($self, $svctype, @extradata) = @_;
4462
4463
0
$self->log_print(OSCAR_DBG_INFO, "Sending service request for servicetype $svctype.");
4464
0
$self->svcdo(CONNTYPE_BOS, protobit => "service_request", protodata => {type => $svctype, @extradata});
4465
}
4466
4467
sub crapout($$$;$) {
4468
0
0
my($self, $connection, $reason, $errno) = @_;
4469
0
0
send_error($self, $connection, $errno || 0, $reason, 1);
4470
0
$self->signoff();
4471
}
4472
4473
sub must_be_on($) {
4474
0
0
my $self = shift;
4475
0
send_error($self, $self->{services}->{0+CONNTYPE_BOS}, 0, "You have not finished signing on.", 0);
4476
}
4477
4478
4479
sub server($%) {
4480
0
0
my $self = shift;
4481
0
my %data = @_;
4482
0
$self->{$_} = $data{$_} foreach keys %data;
4483
0
$self->addconn(conntype => CONNTYPE_SERVER);
4484
}
4485
4486
sub connection_for_family($$) {
4487
0
0
my($self, $family) = @_;
4488
4489
0
my $bos = $self->{services}->{0+CONNTYPE_BOS};
4490
0
0
if($bos->{families}->{$family}) {
4491
0
return $bos;
4492
}
4493
4494
0
foreach my $connection (@{$self->{session}->{connections}}) {
0
4495
0
0
next unless $connection->{families}->{$family};
4496
0
$connection->log_print(OSCAR_DBG_WARN, "Found connection for unsupported SNAC.");
4497
0
return $connection;
4498
}
4499
4500
0
return;
4501
}
4502
4503
1;