File Coverage

blib/lib/Net/OSCAR.pm
Criterion Covered Total %
statement 52 876 5.9
branch 0 394 0.0
condition 0 182 0.0
subroutine 18 186 9.6
pod n/a
total 70 1638 4.2


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
88             sockets and reads incoming commands from the OSCAR server on any connections which
89             have them. The C
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
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
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
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
2875              
2876             =item onsince
2877              
2878             Time that the user signed on to the service, in the same format as the C
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
3118             Simply call the L<"process_connections"> method with references to the lists of readers, writers,
3119             and errors given to you by C
3120             and connections that do belong to the object will be removed from the C
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;