File Coverage

blib/lib/Net/AOLIM.pm
Criterion Covered Total %
statement 9 540 1.6
branch 0 210 0.0
condition 0 76 0.0
subroutine 3 52 5.7
pod 46 49 93.8
total 58 927 6.2


line stmt bran cond sub pod time code
1             package Net::AOLIM;
2              
3 1     1   7608 use IO::Socket;
  1         38742  
  1         7  
4 1     1   2652 use IO::Select;
  1         2558  
  1         89  
5             require 5.001;
6              
7 1     1   9 use vars qw($VERSION $AUTOLOAD);
  1         8  
  1         8316  
8              
9             =pod
10              
11             =head1 NAME
12              
13             Net::AOLIM - Object-Oriented interface to the AOL Instant Messenger TOC client protocol
14              
15             =head1 SYNOPSIS
16              
17             The really short form:
18              
19             use Net::AOLIM;
20             $aim = Net::AOLIM->new('username' => $user,
21             'password' => $pass,
22             'callback' => \&handler);
23              
24             $aim->signon;
25              
26             $aim->toc_send_im($destuser, $message);
27              
28             =cut
29              
30             ###################################################################
31             # Copyright 2000-02 Riad Wahby All rights reserved #
32             # This program is free software. You may redistribute it and/or #
33             # modify it under the same terms as Perl itself. #
34             ###################################################################
35              
36             # subroutine declarations
37             sub new;
38             sub signon;
39             sub read_sflap_packet;
40             sub send_sflap_packet;
41             sub srv_socket;
42             sub pw_roast;
43             sub norm_uname;
44             sub toc_format_msg;
45             sub toc_format_login_msg;
46             sub toc_send_im;
47             sub add_buddies;
48             sub remove_buddies;
49             sub add_online_buddies;
50             sub remove_online_buddies;
51             sub set_srv_buddies;
52             sub current_buddies;
53             sub current_permits;
54             sub current_denies;
55             sub im_permit;
56             sub im_deny;
57             sub add_im_permit;
58             sub add_im_deny;
59             sub im_deny_all;
60             sub add_im_deny_all;
61             sub im_permit_all;
62             sub add_im_permit_all;
63             sub toc_set_config;
64             sub toc_evil;
65             sub toc_chat_join;
66             sub toc_chat_send;
67             sub toc_chat_whisper;
68             sub toc_chat_evil;
69             sub toc_chat_invite;
70             sub toc_chat_leave;
71             sub toc_chat_accept;
72             sub toc_get_info;
73             sub toc_set_info;
74             sub toc_set_away;
75             sub toc_get_dir;
76             sub toc_set_dir;
77             sub toc_dir_search;
78             sub toc_set_idle;
79             sub ui_add_fh;
80             sub ui_del_fh;
81             sub ui_all_fh;
82             sub ui_exists_fh;
83             sub ui_set_callback;
84             sub ui_get_callback;
85             sub ui_dataget;
86              
87             #
88             # some constants to use, including error codes.
89             # :-) the curse of ex-C-programmers--no #defines
90             #
91              
92             # max packet length
93             $MAX_PACKLENGTH = 65535;
94              
95             # SFLAP types
96             $SFLAP_TYPE_SIGNON = 1;
97             $SFLAP_TYPE_DATA = 2;
98             $SFLAP_TYPE_ERROR = 3;
99             $SFLAP_TYPE_SIGNOFF = 4;
100             $SFLAP_TYPE_KEEPALIVE = 5;
101             $SFLAP_MAX_LENGTH = 1024;
102              
103             # return codes
104             $SFLAP_SUCCESS = 0;
105             $SFLAP_ERR_UNKNOWN = 1;
106             $SFLAP_ERR_ARGS = 2;
107             $SFLAP_ERR_LENGTH = 3;
108             $SFLAP_ERR_READ = 4;
109             $SFLAP_ERR_SEND = 5;
110              
111             # misc SFLAP constants
112             $SFLAP_FLAP_VERSION = 1;
113             $SFLAP_TLV_TAG = 1;
114             $SFLAP_HEADER_LEN = 6;
115              
116             # Net::AOLIM version
117             $VERSION = "1.61";
118              
119             # number of arguments that server messages have:
120             %SERVER_MSG_ARGS = ( 'SIGN_ON' => 1,
121             'CONFIG' => 1,
122             'NICK' => 1,
123             'IM_IN' => 3,
124             'UPDATE_BUDDY' => 6,
125             'ERROR' => 2,
126             'EVILED' => 2,
127             'CHAT_JOIN' => 2,
128             'CHAT_IN' => 4,
129             'CHAT_UPDATE_BUDDY' => 0,
130             'CHAT_INVITE' => 4,
131             'CHAT_LEFT' => 1,
132             'GOTO_URL' => 2,
133             'DIR_STATUS' => 2,
134             'PAUSE' => 0 );
135              
136             =pod
137              
138             =head1 NOTES
139              
140             Error conditions will be stored in $main::IM_ERR, with any arguments
141             to the error condition stored in $main::IM_ERR_ARGS.
142              
143             The hash %Net::AOLIM::ERROR_MSGS contains english translations of all of
144             the error messages that are either internal to the module or
145             particular to the TOC protocol.
146              
147             Errors may take arguments indicating a more specific failure
148             condition. In this case, they will either be stored in
149             $main::IM_ERR_ARGS or they will come from the server ERROR message.
150             To insert the arguments in the proper place, use a construct similar
151             to:
152              
153             $ERROR = $Net::AOLIM::ERROR_MSGS{$IM_ERR};
154             $ERROR =~ s/\$ERR_ARG/$IM_ERR_ARGS/g;
155              
156             This assumes that the error code is stored in $IM_ERR and the error
157             argument is stored in $IM_ERR_ARGS.
158              
159             All methods will return undef on error, and will set $main::IM_ERR and
160             $main::IM_ERR_ARGS as appropriate.
161              
162             It seems that TOC servers won't acknowledge a login unless at least
163             one buddy is added before toc_init_done is sent. Thus, as of version
164             1.6, Net::AOLIM will add the current user to group "Me" if you don't
165             create your buddy list before calling signon(). Don't bother removing
166             this if you have added your buddies; it'll automagically disappear.
167              
168             =cut
169              
170             %ERROR_MSGS = ( 0 => 'Success',
171             1 => 'Net::AOLIM Error: Unknown',
172             2 => 'Net::AOLIM Error: Incorrect Arguments',
173             3 => 'Net::AOLIM Error: Exceeded Max Packet Length (1024)',
174             4 => 'Net::AOLIM Error: Reading from server',
175             5 => 'Net::AOLIM Error: Sending to server',
176             6 => 'Net::AOLIM Error: Login timeout',
177             901 => 'General Error: $ERR_ARG not currently available',
178             902 => 'General Error: Warning of $ERR_ARG not currently available',
179             903 => 'General Error: A message has been dropped, you are exceeding the server speed limit',
180             950 => 'Chat Error: Chat in $ERR_ARG is unavailable',
181             960 => 'IM and Info Error: You are sending messages too fast to $ERR_ARG',
182             961 => 'IM and Info Error: You missed an IM from $ERR_ARG because it was too big',
183             962 => 'IM and Info Error: You missed an IM from $ERR_ARG because it was sent too fast',
184             970 => 'Dir Error: Failure',
185             971 => 'Dir Error: Too many matches',
186             972 => 'Dir Error: Need more qualifiers',
187             973 => 'Dir Error: Dir service temporarily unavailble',
188             974 => 'Dir Error: Email lookup restricted',
189             975 => 'Dir Error: Keyword ignored',
190             976 => 'Dir Error: No keywords',
191             977 => 'Dir Error: Language not supported',
192             978 => 'Dir Error: Country not supported',
193             979 => 'Dir Error: Failure unknown $ERR_ARG',
194             980 => 'Auth Error: Incorrect nickname or password',
195             981 => 'Auth Error: The service is temporarily unavailable',
196             982 => 'Auth Error: Your warning level is too high to sign on',
197             983 => 'Auth Error: You have been connecting and disconnecting too frequently. Wait 10 minutes and try again. If you continue to try, you will need to wait even longer.',
198             989 => 'Auth Error: An unknown signon error has occurred $ERR_ARG' );
199              
200             =pod
201              
202             =head1 DESCRIPTION
203              
204             This section documents every member function of the Net::AOLIM class.
205              
206             =head2 $Net::AOLIM->new()
207              
208             This is the Net::AOLIM Constructor.
209              
210             It should be called with following arguments (items with default
211             values are optional):
212              
213             'username' => login
214             'password' => password
215             'callback' => \&callbackfunction
216             'server' => servername (default toc.oscar.aol.com)
217             'port' => port number (default 1234)
218             'allow_srv_settings' => <1 | 0> (default 1)
219             'login_server' => login server (default login.oscar.aol.com)
220             'login_port' => login port (default 5198)
221             'login_timeout' => timeout in seconds to wait for a response to the
222             toc_signon packet. Default is 0 (infinite)
223             'aim_agent' => agentname (max 200 char)
224             Default is AOLIM:$Version VERSION$
225             There have been some reports that changing this
226             may cause TOC servers to stop responding to signon
227             requests
228              
229             callback is the callback function that handles incoming data from the
230             server (already digested into command plus args). This is the meat of
231             the client program.
232              
233             allow_srv_settings is a boolean that dictates whether the object
234             should modify the user configuration on the server. If
235             allow_srv_settings is false, the server settings will be ignored and
236             will not be modified. Otherwise, the server settings will be read in
237             and parsed, and will be modified by calls that modify the buddy list.
238              
239             aim_agent is the name of the client program as reported to the TOC
240             server
241              
242             Returns a blessed instantiation of Net::AOLIM.
243              
244             =cut
245              
246             sub new
247             {
248 0     0 1   my $whatami = shift @_;
249            
250 0           while ($key = shift @_)
251             {
252 0 0         if ($var = shift @_)
253             {
254 0           $args{$key} = $var;
255             }
256             }
257            
258 0 0 0       unless ((defined $args{'username'}) && (defined $args{'password'}) && (defined $args{'callback'}))
      0        
259             {
260 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
261 0           return undef;
262             }
263            
264 0 0         ($args{'allow_srv_settings'} = 1) unless (defined $args{'allow_srv_settings'});
265 0   0       $args{'server'} ||= 'toc.oscar.aol.com';
266 0   0       $args{'port'} ||= 1234;
267 0   0       $args{'login_server'} ||= 'login.oscar.aol.com';
268 0   0       $args{'login_port'} ||= 5198;
269 0   0       $args{'aim_agent'} ||= 'AOLIM:$Version ' . $VERSION . "\$";
270 0   0       $args{'login_timeout'} ||= undef();
271              
272             # Make a new instance of instmsg and bless it.
273              
274 0           my $new_instmsg = { 'username' => $args{'username'},
275             'password' => $args{'password'},
276             'server' => $args{'server'},
277             'port' => $args{'port'},
278             'allow_srv_settings' => $args{'allow_srv_settings'},
279             'roastedp' => pw_roast('', $args{'password'}),
280             'unamenorm' => norm_uname('', $args{'username'}),
281             'im_socket' => '',
282             'client_seq_number' => time % 65536,
283             'login_server' => $args{'login_server'},
284             'login_port' => $args{'login_port'},
285             'buddies' => {},
286             'permit' => [],
287             'deny' => [],
288             'callback' => $args{'callback'},
289             'callbacks' => {},
290             'permit_mode' => '1',
291             'sel' => IO::Select->new(),
292             'pause' => '0',
293             'aim_agent' => $args{'aim_agent'},
294             'login_timeout' => $args{'login_timeout'},
295             };
296              
297 0           bless $new_instmsg, $whatami;
298 0           $main::IM_ERR = 0;
299 0           return $new_instmsg;
300             }
301              
302             ######################################################
303             # SOCKET LEVEL FUNCTIONS
304             # the functions here operate at the socket level
305             #
306             # signon is included here because it is the function
307             # that actually creates the socket
308             ######################################################
309              
310             =pod
311              
312             =head2 $aim->signon()
313              
314             Call this after calling C and after setting initial buddy
315             listings with C, C, C,
316             C, and C as necessary.
317              
318             Returns undef on failure, setting $main::IM_ERR and $main::IM_ERR_ARGS
319             as appropriate. Returns 0 on success.
320              
321             This function is also called every time we receive a SIGN_ON packet
322             from the server. This is because we are required to react in a
323             specific way to the SIGN_ON packet, and this method contains all
324             necessary functionality. We should only receive SIGN_ON while
325             connected if we have first received a PAUSE (see the B
326             documentation included with this package for details of how PAUSE
327             works).
328              
329             =cut
330              
331             sub signon
332             {
333             #
334             # call this after new() to sign on to the IM service
335             #
336             # takes no arguments
337             #
338             # returns 0 on success, undef on failure. If failure,
339             # check $main::IM_ERR for reason.
340             #
341 0     0 1   my $imsg = $_[0];
342 0           my $im_socket = \$imsg->{'im_socket'};
343              
344 0 0         unless ($imsg->{'pause'})
345             {
346             # unless we're coming off a pause, make our socket
347 0 0         $$im_socket = IO::Socket::INET->new(PeerAddr => $imsg->{'server'},
348             PeerPort => $imsg->{'port'},
349             Proto => 'tcp',
350             Type => SOCK_STREAM)
351             or die "Couldn't connect to server: $!";
352              
353 0           $$im_socket->autoflush(1);
354              
355             # add this filehandle to the select loop that we will later use
356 0           $imsg->{'sel'}->add($$im_socket);
357              
358 0           my $so_srv_sflap_signon;
359             my $so_srv_version;
360 0           my $so_sflap_signon;
361 0           my $so_toc_ascii;
362 0           my $so_toc_srv_so;
363 0           my $so_toc_srv_config;
364 0           my $so_toc_srv_config_msg;
365 0           my $so_toc_srv_config_rest;
366 0           my $so_init_done;
367            
368             # send a FLAPON to initiate the connection; this is the only time
369             # that stuff should be printed directly to the server without
370             # using send_sflap_packet
371 0           syswrite $$im_socket,"FLAPON\r\n\r\n";
372              
373 0 0         return undef unless (defined ($so_srv_sflap_signon = $imsg->read_sflap_packet()));
374              
375 0           $ulen = length $imsg->{'unamenorm'};
376            
377 0           $so_sflap_signon = pack "Nnna".$ulen, 1, 1, $ulen, $imsg->{'unamenorm'};
378            
379            
380 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_SIGNON, $so_sflap_signon, 1, 1)));
381            
382 0           $so_toc_ascii = $imsg->toc_format_login_msg('toc_signon',$imsg->{'login_server'},$imsg->{'login_port'},$imsg->{'unamenorm'},$imsg->{'roastedp'},'english',$imsg->{'aim_agent'});
383            
384 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $so_toc_ascii, 0, 0)));
385            
386 0           my @ready = $imsg->{'sel'}->can_read($imsg->{'login_timeout'});
387            
388 0 0         if (scalar(@ready) > 0)
389             {
390 0 0         return undef unless (defined ($so_toc_srv_so = $imsg->read_sflap_packet()));
391             }
392             else
393             {
394 0           $main::IM_ERR = 6;
395 0           return undef;
396             }
397            
398 0 0         unless ($so_toc_srv_so =~ /SIGN_ON/)
399             {
400             # we didn't sign on successfully
401 0 0         if ($so_toc_srv_so =~ /ERROR:(.*)/)
402             {
403             # if we get an error code from the server, send it
404             # back in $main::IM_ERR
405 0           ($main::IM_ERR, $main::IM_ERR_ARG) = split (/:/, $1, 2);
406             }
407             else
408             {
409 0           $main::IM_ERR = $SFLAP_ERR_UNKNOWN;
410             }
411 0           return undef;
412             }
413             }
414            
415             # we can't possibly be paused at this point; make sure $imsg->{'pause'} = 0
416 0           $imsg->{'pause'} = 0;
417              
418             # have to call toc_set_config before we finish init
419 0 0         return undef unless (defined $imsg->toc_set_config());
420              
421             # now we finish the signon with an init_done
422 0           $so_init_done = $imsg->toc_format_msg('toc_init_done');
423            
424            
425 0 0         return undef unless (defined $imsg->send_sflap_packet($SFLAP_TYPE_DATA, $so_init_done, 0, 0));
426              
427 0           return $SFLAP_SUCCESS;
428             }
429              
430             =pod
431              
432             =head2 $aim->read_sflap_packet()
433              
434             This method returns data from a single waiting SFLAP packet on the
435             server socket. The returned value is the payload section of the SFLAP
436             packet which is completely unparsed.
437              
438             Most users will never need to call this method.
439              
440             For more information, see B below and the B
441             manpage.
442              
443             =cut
444              
445             sub read_sflap_packet
446             {
447             #
448             # read an sflap packet, including a safe
449             # method of making sure that we get all
450             # the info in the sflap packet
451             #
452             # takes no arguments
453             #
454             # returns the read data upon success, or undef if an error
455             # occurs (and the errno appears in $main::IM_ERR)
456             #
457 0     0 1   my $imsg = shift @_;
458 0           my ($rsp_header, $rsp_recv_packet);
459 0           my ($rsp_ast, $rsp_type, $rsp_seq_new, $rsp_dlen);
460 0           my ($rsp_decoded);
461 0           my $im_socket = \$imsg->{'im_socket'};
462              
463             # unless we get a valid read, we return an unknown error
464              
465 0 0 0       unless (defined(sysread $$im_socket, $rsp_header, $SFLAP_HEADER_LEN, 0) && (length($rsp_header) == $SFLAP_HEADER_LEN))
466             {
467 0           $main::IM_ERR = $SFLAP_ERR_READ;
468 0           return undef;
469             }
470              
471             # Now we read the info off the packet, including the data length and the
472             # sequence number
473 0           ($rsp_ast,$rsp_type,$rsp_seq_new,$rsp_dlen) = unpack "aCnn", $rsp_header;
474              
475             # now we pull down more bytes equal to the length field in
476             # the previous read
477              
478 0 0 0       unless (defined(sysread $$im_socket, $rsp_recv_packet, $rsp_dlen, 0) && (length($rsp_recv_packet) == $rsp_dlen))
479             {
480 0           $main::IM_ERR = $SFLAP_ERR_READ;
481 0           return undef;
482             }
483              
484             # if it's a signon packet, we read the version number
485 0 0 0       if (($rsp_type == $SFLAP_TYPE_SIGNON) && ($rsp_dlen == 4))
486             {
487 0           ($rsp_decoded) = unpack "N", $rsp_recv_packet;
488 0           $main::IM_ERR = $SFLAP_SUCCESS;
489 0           return $rsp_decoded;
490             }
491             # otherwise, we just read it as ASCII
492             else
493             {
494 0           ($rsp_decoded) = unpack "a*", $rsp_recv_packet;
495 0           $main::IM_ERR = $SFLAP_SUCCESS;
496 0           return $rsp_decoded;
497             }
498              
499             # if we fall through to here, something's wrong; return an
500             # unknown error
501 0           $main::IM_ERR = $SFLAP_ERR_UNKNOWN;
502 0           return undef;
503             }
504              
505             =pod
506              
507             =head2 $aim->send_sflap_packet($type, $data, $formatted, $noterm)
508              
509             This method sends an SFLAP packet to the server.
510              
511             C<$type> is one of the SFLAP types (see B).
512              
513             C<$data> is the payload to send.
514              
515             If C<$formatted> evaluates to true, the data is assumed to be the
516             completely formed payload of the SFLAP packet; otherwise, the payload
517             will be packed as necessary. This defaults to 0. In either case, the
518             header is prepended to the payload.
519              
520             If C<$noterm> evaluates to true, the payload will not be terminated
521             with a '\0'. Otherwise, it will be terminated. If C<$formatted> is
522             true, this option is ignored and no null is appended. This defaults
523             to 0.
524              
525             Most users will never need to use this method.
526              
527             For more information, see B and B below.
528              
529             =cut
530              
531             sub send_sflap_packet
532             {
533             #
534             # take data, manufacture an SFLAP header,
535             # and send off the info.
536             #
537             # takes four arguments:
538             #
539             # sflap_type: gives the type to include in the header
540             # sflap_data: either ASCII or a preformatted string to
541             # send as the payload
542             # already_formatted: set to 1 to prevent the formatting
543             # of sflap_data as ASCII (if it has already
544             # been formatted). Defaults to 0
545             # no_null_terminate: set to 1 to prevent the addition of
546             # a null terminator to the data. Default 0.
547             # No null termination is added if already_formatted
548             # is set.
549             #
550             # returns undef if unsuccessful, and puts the error in $main::IM_ERR
551             # otherwise returns 0
552             #
553              
554 0     0 1   my $imsg = shift @_;
555 0           my $im_socket = \$imsg->{'im_socket'};
556              
557             # arguments
558 0           my $sflap_type = $_[0];
559 0           my $sflap_data = $_[1];
560 0           my $already_formatted = $_[2];
561 0           my $no_null_terminate = $_[3];
562              
563 0 0 0       unless ((defined $sflap_type) && (defined $sflap_data) && (defined $already_formatted) && (defined $no_null_terminate))
      0        
      0        
564             {
565 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
566 0           return undef;
567             }
568              
569             # internal variables
570 0           my ($ssp_header, $ssp_data, $ssp_packet, $ssp_datalen);
571              
572 0 0         if ($already_formatted)
573             {
574             # we don't have to modify the data
575 0           $ssp_data = $sflap_data;
576 0           $ssp_datalen = length $sflap_data;
577 0           $ssp_header = pack "aCnn", "*", $sflap_type, $imsg->{'client_seq_number'}, $ssp_datalen;
578 0           $ssp_packet = $ssp_header . $ssp_data;
579             }
580             else
581             {
582 0 0         unless ($no_null_terminate)
583             {
584             # we need to be sure that there's only one \0 at the end of
585             # the string
586 0           $sflap_data =~ s/\0*$//;
587 0           $sflap_data .= "\0";
588             }
589            
590             # now we calculate the length and make the packet
591 0           $ssp_datalen = length $sflap_data;
592 0           $ssp_data = pack "a".$ssp_datalen, $sflap_data;
593 0           $ssp_header = pack "aCnn", "*", $sflap_type, $imsg->{'client_seq_number'}, $ssp_datalen;
594 0           $ssp_packet = $ssp_header . $ssp_data;
595             }
596              
597             # if the packet is too long, return an error
598             # our connection will be dropped otherwise
599 0 0         if ((length $ssp_packet) >= $SFLAP_MAX_LENGTH)
600             {
601 0           $main::IM_ERR = $SFLAP_ERR_LENGTH;
602 0           return undef;
603             }
604              
605             # if we are successful we return 0
606 0 0         if (syswrite $$im_socket,$ssp_packet)
607             {
608 0           $$im_socket->flush();
609 0           $imsg->{'client_seq_number'}++;
610 0           return $SFLAP_SUCCESS;
611             }
612              
613             # if we fall through to here, we have a problem
614 0           $main::IM_ERR = $SFLAP_ERR_SEND;
615 0           return undef;
616             }
617              
618             =cut
619              
620             =head2 $aim->srv_socket()
621              
622             This method returns a reference to the socket to which the server is
623             connected. It must be dereferenced before it can be used. Thus:
624              
625             C<$foo = $aim-Esrv_socket();>
626             C
627              
628             Most users will never need to directly access the server socket.
629              
630             For more information, see the B manpage and B
631             OWN> below.
632              
633             =cut
634              
635             sub srv_socket
636             {
637             #
638             # takes no arguments
639             #
640             # returns a reference to the socket on which we communicate
641             # with the server
642             #
643 0     0 1   my $imsg = shift @_;
644              
645 0           return \$imsg->{'im_socket'};
646             }
647              
648             ########################################################
649             # MISCELLANEOUS FUNCTIONS
650             # these serve important functions, but
651             # are not directly accessed by the user
652             # of the Net::AOLIM package
653             ########################################################
654              
655             =pod
656              
657             =head2 $aim->pw_roast($password)
658              
659             This method returns the 'roasted' version of a password. A roasted
660             password is the original password XORed with the roast string
661             'Tic/Toc' (which is repeated until the length is the same as the
662             password length).
663              
664             This method is called automatically in $aim->signon. Most users will
665             never need this method.
666              
667             For more information, see the B manpage and B
668             OWN> below.
669              
670             =cut
671              
672             sub pw_roast
673             {
674             #
675             # this takes one argument, the
676             # password, and returns the roasted
677             # string
678             #
679 0     0 1   my $imsg = shift @_;
680 0           my $pr_password = $_[0];
681 0           my $pr_len = (length $pr_password) * 8;
682 0           my $pr_roasted;
683             my $pr_roasted_bits;
684 0           my $pr_roast_string = '01010100011010010110001100101111010101000110111101100011';
685 0           my $pr_password_bits = unpack("B*", pack("a".$pr_len, $pr_password));
686              
687 0 0         unless (defined $pr_password)
688             {
689 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
690 0           return undef;
691             }
692            
693 0           for ($i = 0; $i < $pr_len; $i++)
694             {
695 0           my $bit1 = substr $pr_password_bits, $i, 1;
696 0           my $bit2 = substr $pr_roast_string, ($i % 56), 1;
697 0           my $newbit = $bit1 ^ $bit2;
698 0           $pr_roasted_bits .= $newbit;
699             }
700              
701 0           $pr_roasted = "0x" . (unpack "H*", (pack "B*", $pr_roasted_bits));
702              
703 0           return $pr_roasted;
704             }
705              
706             =pod
707              
708             =head2 $aim->norm_uname($username)
709              
710             This method returns the 'normalized' version of a username. A
711             normalized username has all spaces removed and is all lowercase. All
712             usernames sent to the server should be normalized first if they are an
713             argument to a TOC command.
714              
715             All methods in this class automatically normalize username arguments
716             to the server; thus, most users will never use this method.
717              
718             For more information, see the B manpage and B
719             OWN> below.
720              
721             =cut
722              
723             sub norm_uname
724             {
725             #
726             # this takes one argument, the
727             # username to normalize
728             #
729             # returns the normalized username
730             #
731 0     0 1   my $imsg = shift @_;
732 0           my $nu_username = $_[0];
733              
734 0 0         unless (defined $nu_username)
735             {
736 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
737 0           return undef;
738             }
739              
740 0           $nu_username =~ s/ //g;
741 0           $nu_username = "\L$nu_username\E";
742             }
743              
744             =pod
745              
746             =head2 $aim->toc_format_msg($command[, $arg1[, arg2[, ...]]])
747              
748             This method formats a message properly for sending to the TOC server.
749             That is, it is escaped and quoted, and the fields are appended with
750             spaces as specified by the protocol.
751              
752             Note that all methods in this class automatically format messages
753             appropriately; most users will never need to call this method.
754              
755             See B and B below.
756              
757             =cut
758              
759             sub toc_format_msg
760             {
761             #
762             # this takes at least one argument.
763             # the first argument will be returned unaltered
764             # at the beginning of the string which is a
765             # join (with spaces) of the remaining arguments
766             # after they have been properly escaped and quoted.
767             #
768 0     0 1   my $imsg = shift @_;
769 0           my $toc_command = shift @_;
770 0           my $escaped;
771             my $finalmsg;
772            
773 0 0         unless (defined $toc_command)
774             {
775 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
776 0           return undef;
777             }
778              
779 0 0         if (@_)
780             {
781 0           foreach $arg (@_)
782             {
783 0           $escaped = $arg;
784 0           $escaped =~ s/([\$\{\}\[\]\(\)\"\\\'])/\\$1/g;
785 0           $finalmsg .= ' "' . $escaped. '"';
786             }
787             }
788             else
789             {
790 0           $finalmsg = "";
791             }
792              
793 0           $finalmsg = $toc_command . $finalmsg;
794            
795 0           return $finalmsg;
796             }
797              
798             =pod
799              
800             =head2 $aim->toc_format_login_msg($command[, $arg1[, arg2[, ...]]])
801              
802             This method formats a login message properly for sending to the TOC
803             server. That is, all fields are escaped, but only the user_agent
804             field is quoted. Fields are separated with spaces as specified in the
805             TOC protocol.
806              
807             Note that the login procedure calls this function automatically; the
808             user will probably never need to use it.
809              
810             See B and B below.
811              
812             =cut
813              
814             sub toc_format_login_msg
815             {
816             #
817             # this takes at least one argument.
818             # the first argument will be returned unaltered
819             # at the beginning of the string which is a
820             # join (with spaces) of the remaining arguments
821             # after they have been properly escaped and quoted.
822             #
823 0     0 1   my $imsg = shift @_;
824 0           my $toc_command = shift @_;
825 0           my $useragentstr = pop @_;
826 0           my $escaped;
827             my $finalmsg;
828            
829 0 0         unless (defined $toc_command)
830             {
831 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
832 0           return undef;
833             }
834              
835 0 0         if (@_)
836             {
837 0           foreach $arg (@_)
838             {
839 0           $escaped = $arg;
840 0           $escaped =~ s/([\$\{\}\[\]\(\)\"\\\'])/\\$1/g;
841 0           $finalmsg .= ' ' . $escaped. '';
842             }
843             }
844             else
845             {
846 0           $finalmsg = "";
847             }
848              
849 0           $useragentstr =~ s/([\$\{\}\[\]\(\)\"\\\'])/\\$1/g;
850              
851 0           $finalmsg = $toc_command . $finalmsg . ' "' . $useragentstr . '"';
852            
853 0           return $finalmsg;
854             }
855              
856             ############################################################
857             # TOC Interface functions
858             #
859             # These are the functions that the Net::AOLIM package user
860             # will most often interface with; these are basically
861             # directly mapped to TOC functions of the same name
862             ############################################################
863              
864             =pod
865              
866             =head2 $aim->toc_send_im($uname, $msg, $auto)
867              
868             This method sends an IM message C<$msg> to the user specified by
869             C<$uname>. The third argument indicates whether or not this IM should
870             be sent as an autoreply, which may produce different behavior from the
871             remote client (but has no direct effect on the content of the IM).
872              
873             =cut
874              
875             sub toc_send_im
876             {
877             #
878             # takes three arguments:
879             #
880             # tsi_uname: the username to send the packet to
881             # tsi_msg: the message to send
882             # tsi_auto: if this should be an autoreply packet, set
883             # this to true
884             #
885             # returns $TOC_SUCCESS on success, or undef on
886             # error (and $main::IM_ERR is set with an error code)
887             #
888 0     0 1   my $imsg = shift @_;
889 0           my $tsi_uname = $_[0];
890 0           my $tsi_msg = $_[1];
891              
892 0 0 0       unless ((defined $imsg) && (defined $tsi_uname) && (defined $tsi_msg))
      0        
893             {
894 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
895 0           return undef;
896             }
897              
898 0           my $tsi_full_msg = $imsg->toc_format_msg("toc_send_im",$imsg->norm_uname($tsi_uname),$tsi_msg);
899              
900 0 0         if ($tsi_auto)
901             {
902 0           $tsi_full_msg .= " auto";
903             }
904              
905            
906 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsi_full_msg, 0, 0)));
907              
908 0           return $TOC_SUCCESS;
909             }
910              
911             #*****************************************************
912             # Buddy functions
913             #
914             # all of these have to do with buddy functions, such
915             # as adding and removing buddies from your buddy list
916             #*****************************************************
917              
918             =pod
919              
920             =head2 $aim->add_buddies($group, $buddy1[, $buddy2[, ...]])
921              
922             This method, which should only be called B, adds
923             buddies to the initial local buddy list in group C<$group>. Once
924             C is called, use add_online_buddies instead.
925              
926             =cut
927              
928             sub add_buddies
929             {
930             #
931             # takes at least two arguments.
932             #
933             # the first argument is the name of
934             # the group that the names after it will
935             # be added to.
936             #
937             # each arg is taken to be a buddy
938             # in the user's buddy list which is
939             # sent during signon.
940             #
941 0     0 1   my $imsg = shift @_;
942 0           my $ib_group = shift @_;
943            
944 0 0 0       unless ((defined $ib_group) && (defined $_[0]))
945             {
946 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
947 0           return undef;
948             }
949              
950 0 0         ($ { $imsg->{'buddies'} }{$ib_group} = []) unless (scalar @{$ { $imsg->{'buddies'} }{$ib_group}});
  0            
  0            
  0            
951            
952 0           my @norm_buddies;
953              
954 0           foreach $buddy (@_)
955             {
956 0           my $norm_buddy = $imsg->norm_uname($buddy);
957 0           unshift @norm_buddies, $norm_buddy;
958             }
959              
960 0           my %union;
961              
962 0           foreach $e (@norm_buddies, @ { $ { $imsg->{'buddies'}}{$ib_group}})
  0            
  0            
963             {
964 0           $union{$e}++;
965             }
966              
967 0           @ { $ { $imsg->{'buddies'}}{$ib_group}} = keys %union;
  0            
  0            
968             }
969              
970             sub remove_buddies
971             {
972             #
973             # takes at least one argument
974             #
975             # each argument is taken to be
976             # a buddy which will be removed
977             # from the buddy list
978             #
979 0     0 0   my $imsg = shift @_;
980              
981 0 0         unless (defined $_[0])
982             {
983 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
984 0           return undef;
985             }
986              
987 0           my @norm_buddies;
988              
989 0           foreach $buddy (@_)
990             {
991 0           my $norm_buddy = $imsg->norm_uname($buddy);
992 0           unshift @norm_buddies, $norm_buddy;
993             }
994              
995 0           foreach $group (keys %{$imsg->{'buddies'}})
  0            
996             {
997 0           my %temp;
998            
999 0           map {$temp{$_} = 1;} @ { $ { $imsg->{'buddies'} } {$group} };
  0            
  0            
  0            
1000 0           map {delete $temp{$_};} @norm_buddies;
  0            
1001            
1002 0           @ { $ { $imsg->{'buddies'} } {$group} } = keys %temp;
  0            
  0            
1003              
1004 0 0         unless (scalar @ { $ { $imsg->{'buddies'} } {$group} })
  0            
  0            
1005             {
1006 0           delete $ { $imsg->{'buddies'} }{$group};
  0            
1007             }
1008             }
1009             }
1010              
1011             =pod
1012              
1013             =head2 $aim->add_online_buddies($group, $buddy1[, $buddy2[, ...]])
1014              
1015             This method takes the same arguments as C, but is
1016             intended for use after C has been called.
1017              
1018             If allow_srv_settings is true (see C), it will also set the
1019             settings on the server to the new settings.
1020              
1021             =cut
1022              
1023             sub add_online_buddies
1024             {
1025             #
1026             # takes at least two arguments
1027             #
1028             # this should be called only after signon
1029             # adds all arguments after the firist as buddies
1030             # to the buddy list. the first argument is
1031             # the name of the group in which to add them
1032             #
1033             # if you want to add people to your initial buddy
1034             # list, us im_buddies()
1035             #
1036             # returns undef on error
1037             #
1038 0     0 1   my $imsg = shift @_;
1039              
1040 0 0         return undef unless (defined $imsg->add_buddies(@_));
1041              
1042 0           $imsg->toc_set_config();
1043             }
1044              
1045             =pod
1046              
1047             =head2 $aim->remove_online_buddies($buddy1[, $buddy2[, ...]])
1048              
1049             Removes all arguments from the buddy list (removes from all groups).
1050              
1051             If allow_srv_settings is true (see C), it will also set the
1052             settings on the server to the new settings.
1053              
1054             =cut
1055              
1056             sub remove_online_buddies
1057             {
1058             #
1059             # takes at least one argument
1060             #
1061             # this should be called only after signon
1062             # removes all arguments from the buddy list.
1063             #
1064             # returns undef on error
1065             #
1066 0     0 1   my $imsg = shift @_;
1067            
1068 0 0         return undef unless (defined $imsg->remove_buddies(@_));
1069              
1070 0           my $rob_message = $imsg->toc_format_msg('toc_remove_buddy', @_);
1071              
1072            
1073 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $rob_message, 0, 0)));
1074            
1075 0 0         if ($imsg->{'allow_srv_settings'})
1076             {
1077 0           $imsg->toc_set_config();
1078             }
1079             }
1080              
1081             sub set_srv_buddies
1082             {
1083             #
1084             # adds buddies in our list from the server
1085             #
1086             # takes one argument, the CONFIG string from the
1087             # server
1088             #
1089 0     0 0   my $imsg = shift @_;
1090 0           my $srv_buddy_list = $_[0];
1091            
1092 0 0         return unless ($imsg->{'allow_srv_settings'});
1093              
1094 0           $srv_buddy_list =~ s/^CONFIG://;
1095              
1096 0 0         return unless (@srv_buddies = split "\n", $srv_buddy_list);
1097              
1098 0           for ($i=0; $i < scalar (@srv_buddies); $i++)
1099             {
1100 0 0         if ($srv_buddies[$i] =~ /^g\s*(.*)/)
1101             {
1102 0           my $group = $1;
1103 0           my $continue = 1;
1104 0           $i++;
1105              
1106 0           my @buddylist;
1107              
1108 0           while ($continue)
1109             {
1110 0 0         if ($srv_buddies[$i] =~ /^b\s*(.*)/)
1111             {
1112 0           unshift @buddylist, $1;
1113 0           $i++;
1114             }
1115             else
1116             {
1117 0           $i--;
1118 0           $continue = 0;
1119             }
1120             }
1121              
1122 0           my %union;
1123              
1124 0           foreach $e (@buddylist, @ { $ { $imsg->{'buddies'}}{$group}})
  0            
  0            
1125             {
1126 0           $union{$e}++;
1127             }
1128              
1129 0           @{ $ { $imsg->{'buddies'}}{$group}} = keys %union;
  0            
  0            
1130             }
1131             }
1132             }
1133              
1134             =pod
1135              
1136             =head2 $aim->current_buddies(\%buddyhash)
1137              
1138             This method fills the hash referenced by C<\%buddyhash> with the
1139             currently stored buddy information. Each key in the returned hash is
1140             the name of a buddy group, and the corresponding value is a list of
1141             the members of that group.
1142              
1143             =cut
1144              
1145             sub current_buddies
1146             {
1147             #
1148             # takes one argument, a pointer to a hash that should
1149             # be filled with the current users such that each hash
1150             # key is a buddy group and the corresponding value is a
1151             # list of buddies in that group. Thus,
1152             #
1153             # @{$hash{"foo"}}
1154             #
1155             # is the list of users in the group called foo
1156             #
1157 0     0 1   my $imsg = shift @_;
1158 0           my $buddyhash = $_[0];
1159              
1160 0 0         unless (defined $buddyhash)
1161             {
1162 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1163 0           return undef;
1164             }
1165              
1166 0           %$buddyhash = % { $imsg->{'buddies'}};
  0            
1167             }
1168              
1169             =pod
1170              
1171             =head2 $aim->current_permits()
1172              
1173             This method takes no arguments. It returns the current 'permit' list.
1174              
1175             =cut
1176              
1177             sub current_permits
1178             {
1179             #
1180             # takes no arguments
1181             #
1182             # returns a list of the people currently on the "permit" list
1183             #
1184 0     0 1   my $imsg = shift @_;
1185            
1186 0           return @ {$imsg->{'permit'}};
  0            
1187             }
1188              
1189             =pod
1190              
1191             =head2 $aim->current_denies()
1192              
1193             This method takes no arguments. It returns the current 'deny' list.
1194              
1195             =cut
1196              
1197             sub current_denies
1198             {
1199             #
1200             # takes no arguments
1201             #
1202             # returns a list of the people currently on the "deny" list
1203             #
1204 0     0 1   my $imsg = shift @_;
1205            
1206 0           return @ {$imsg->{'deny'}};
  0            
1207             }
1208              
1209             #*********************************************************
1210             # ACCESS PERMISSION OPTIONS
1211             #
1212             # these functions affect the users that are permitted to
1213             # see you; interfaces are provided for both online and
1214             # offline specification of permissions
1215              
1216             =pod
1217              
1218             =head2 $aim->im_permit($user1[, $user2[, ...]])
1219              
1220             This method should only be called B. It adds all
1221             arguments to the current permit list and deletes the current deny
1222             list. It also sets the permit mode to 'permit some'.
1223              
1224             If you would like to do this while online, use the C
1225             method instead.
1226              
1227             =cut
1228              
1229             sub im_permit
1230             {
1231             #
1232             # takes at least one argument
1233             #
1234             # each arg is one person to be added
1235             # to the user's permit list. If a permit
1236             # list is used, only people on the permit
1237             # list will be allowed
1238             #
1239 0     0 1   my $imsg = shift @_;
1240 0           $imsg->{'permit_mode'} = 3;
1241             # if we permit, we can't deny
1242 0           $imsg->{'deny'} = [];
1243              
1244 0 0         unless (defined $_[0])
1245             {
1246 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1247 0           return undef;
1248             }
1249              
1250 0           my @norm_permits;
1251              
1252 0           foreach $permit (@_)
1253             {
1254 0           my $norm_permit = $imsg->norm_uname($permit);
1255 0           unshift @norm_permits, $norm_permit;
1256             }
1257              
1258 0           my %union;
1259              
1260 0           foreach $e (@norm_permits, @{ $imsg->{'permit'}})
  0            
1261             {
1262 0           $union{$e}++;
1263             }
1264              
1265 0           @{ $imsg->{'permit'}} = keys %union;
  0            
1266             }
1267              
1268             =pod
1269              
1270             =head2 $aim->im_deny($user1[, $user2[, ...]])
1271              
1272             This method should only be called B. It adds all
1273             arguments to the current deny list and deletes the current permit
1274             list. It also sets the permit mode to 'deny some'.
1275              
1276             If you would like to do this while online, use the C
1277             method instead.
1278              
1279             =cut
1280              
1281             sub im_deny
1282             {
1283             #
1284             # takes at least one argument
1285             #
1286             # each arg is one person to be added
1287             # to the user's deny list. If a deny
1288             # list is used, only people on the deny
1289             # list will be denied
1290             #
1291 0     0 1   my $imsg = shift @_;
1292 0           $imsg->{'permit_mode'} = 4;
1293             # if we deny, we can't permit
1294 0           $imsg->{'permit'} = [];
1295              
1296 0 0         unless (defined $_[0])
1297             {
1298 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1299 0           return undef;
1300             }
1301              
1302 0           my @norm_denies;
1303              
1304 0           foreach $deny (@_)
1305             {
1306 0           my $norm_deny = $imsg->norm_uname($deny);
1307 0           unshift @norm_denies, $norm_deny;
1308             }
1309              
1310 0           my %union;
1311              
1312 0           foreach $e (@norm_denies, @ { $imsg->{'deny'}})
  0            
1313             {
1314 0           $union{$e}++;
1315             }
1316              
1317 0           @ { $imsg->{'deny'}} = keys %union;
  0            
1318             }
1319              
1320             =pod
1321              
1322             =head2 $aim->add_im_permit($user1[, $user2[, ...]])
1323              
1324             This is the method that should be called if you are online and wish to
1325             add users to the permit list. It will, as a consequence, delete the
1326             current deny list and set the current mode to 'permit some'.
1327              
1328             =cut
1329              
1330             sub add_im_permit
1331             {
1332             #
1333             # takes at least one argument
1334             #
1335             # each argument is added to the permit
1336             # list. If a permit list is used, only
1337             # the people on the permit list will
1338             # be allowed.
1339             #
1340             # this should only be called after signon is completed
1341             # if you want to do permit before then, use im_permit
1342             #
1343 0     0 1   my $imsg = shift @_;
1344              
1345 0 0         return undef unless (defined $imsg->im_permit(@_));
1346            
1347 0           $imsg->toc_set_config();
1348             }
1349              
1350             =pod
1351              
1352             =head2 $aim->add_im_deny($user1[, $user2[, ...]])
1353              
1354             This is the method that should be used if you are online and wish to
1355             add users to the deny list. It will, as a consequence, delete the
1356             current permit list and set the current mode to 'deny some'.
1357              
1358             =cut
1359              
1360             sub add_im_deny
1361             {
1362             #
1363             # takes at least one argument
1364             #
1365             # each argument is added to the deny
1366             # list. If a deny list is used, only
1367             # the people in the deny list will be
1368             # banned
1369             #
1370             # this should be called after signon is completed
1371             # if you want to do deny before then, use im_deny
1372             #
1373 0     0 1   my $imsg = shift @_;
1374              
1375 0 0         return undef unless (defined $imsg->im_deny(@_));
1376            
1377 0           $imsg->toc_set_config();
1378             }
1379              
1380             =pod
1381              
1382             =head2 $aim->im_deny_all()
1383              
1384             This method should be called only B. It will delete
1385             both the permit and deny list and set the mode to 'deny all'.
1386              
1387             =cut
1388              
1389             sub im_deny_all
1390             {
1391             #
1392             # takes no arguments
1393             #
1394             # sets mode to deny all
1395             #
1396 0     0 1   my $imsg = shift @_;
1397 0           $imsg->{'permit_mode'} = 2;
1398              
1399             # clear the permit and deny lists
1400 0           $imsg->{'permit'} = [];
1401 0           $imsg->{'deny'} = [];
1402             }
1403              
1404             =pod
1405              
1406             =head2 $aim->im_permit_all()
1407              
1408             This method should be called only B. It will delete
1409             both the permit and deny list and set the mode to 'permit all'.
1410              
1411             =cut
1412              
1413             sub im_permit_all
1414             {
1415             #
1416             # takes no arguments
1417             #
1418             # sets mode to allow all
1419             #
1420 0     0 1   my $imsg = shift @_;
1421 0           $imsg->{'permit_mode'} = 1;
1422              
1423 0           $imsg->{'permit'} = [];
1424 0           $imsg->{'deny'} = [];
1425             }
1426              
1427             =pod
1428              
1429             =head2 $aim->add_im_deny_all()
1430              
1431             This is the method that should be used if you are online and wish to
1432             go into 'deny all' mode. It will also delete both the permit and deny
1433             lists.
1434              
1435             =cut
1436              
1437             sub add_im_deny_all
1438             {
1439             #
1440             # takes no arguments
1441             #
1442             # sets mode to deny all
1443             #
1444             # use this only when connected; otherwise,
1445             # if you want to set before connecting, use
1446             # im_deny_all
1447             #
1448 0     0 1   my $imsg = shift @_;
1449            
1450 0           $imsg->im_deny_all;
1451              
1452 0           my $aida_message = $imsg->toc_format_msg('toc_add_permit');
1453              
1454            
1455 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aida_message, 0, 0)));
1456              
1457 0 0         if ($imsg->{'allow_srv_settings'})
1458             {
1459 0           $imsg->toc_set_config;
1460             }
1461             }
1462              
1463             =pod
1464              
1465             =head2 $aim->add_im_permit_all()
1466              
1467             This is the method that should be used if you are online and wish to
1468             go into 'permit all' mode. It will also delete both the permit and
1469             deny lists.
1470              
1471             =cut
1472              
1473             sub add_im_permit_all
1474             {
1475             #
1476             # takes no arguments
1477             #
1478             # sets mode to allow all
1479             #
1480             # use this only when connected; otherwise,
1481             # if you want to set before connecting, use
1482             # im_permit_all
1483             #
1484 0     0 1   my $imsg = shift @_;
1485              
1486 0           $imsg->im_permit_all;
1487            
1488 0           my $aipa_message = $imsg->toc_format_msg('toc_add_deny');
1489              
1490            
1491 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aipa_message, 0, 0)));
1492              
1493 0 0         if ($imsg->{'allow_srv_settings'})
1494             {
1495 0           $imsg->toc_set_config;
1496             }
1497             }
1498              
1499             sub toc_set_config
1500             {
1501             #
1502             # takes no arguments
1503             #
1504             # sets the config on the server
1505             # so that it is carried from session
1506             # to session by the server
1507             #
1508             # this is called at signon and
1509             # after each call to add_im_buddies
1510             # or remove_im_buddies
1511             #
1512             # In V1.6, this function was modified so that
1513             # if there are no currently defined buddies,
1514             # the current user is set as a buddy in group
1515             # "Me". This is necessary because an empty
1516             # buddy list will cause signon to fail.
1517             #
1518             # returns undef on error
1519             #
1520 0     0 0   my $imsg = shift @_;
1521            
1522 0           my $tsc_config_info;
1523             my $tsc_packet;
1524 0           my $tsc_permit_mode = $imsg->{'permit_mode'};
1525              
1526 0 0         if (scalar(keys %{$imsg->{'buddies'}}))
  0            
1527             {
1528 0           foreach $group (keys %{$imsg->{'buddies'}})
  0            
1529             {
1530 0           my $aob_message = $imsg->toc_format_msg('toc_add_buddy', $group, @ { $ { $imsg->{'buddies'} } {$group} });
  0            
  0            
1531              
1532 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aob_message, 0, 0)));
1533            
1534 0 0         if ($imsg->{'allow_srv_settings'})
1535             {
1536 0           $tsc_config_info .= "g $group\n";
1537            
1538 0           foreach $buddy (@ { $ { $imsg->{'buddies'} } {$group} })
  0            
  0            
1539             {
1540 0           $tsc_config_info .= "b $buddy\n";
1541             }
1542             }
1543             }
1544             }
1545             else
1546             {
1547 0           my $aob_message = $imsg->toc_format_msg('toc_add_buddy', 'Me', $imsg->{'username'});
1548 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aob_message, 0, 0)));
1549             }
1550            
1551 0 0         if (scalar @ { $imsg->{'permit'} })
  0            
1552             {
1553 0           my $aip_message = $imsg->toc_format_msg('toc_add_permit', @ { $imsg->{'permit'} });
  0            
1554            
1555 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aip_message, 0, 0)));
1556            
1557 0 0         if ($imsg->{'allow_srv_settings'})
1558             {
1559 0           foreach $permit (@ { $imsg->{'permit'} })
  0            
1560             {
1561 0           $tsc_config_info .= "p $permit\n";
1562             }
1563             }
1564             }
1565              
1566 0 0         if (scalar @ { $imsg->{'deny'} })
  0            
1567             {
1568 0           my $aid_message = $imsg->toc_format_msg('toc_add_deny', @_);
1569            
1570            
1571 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $aid_message, 0, 0,)));
1572            
1573 0 0         if ($imsg->{'allow_srv_settings'})
1574             {
1575 0           foreach $deny (@ { $imsg->{'deny'} })
  0            
1576             {
1577 0           $tsc_config_info .= "d $deny\n";
1578             }
1579             }
1580             }
1581              
1582 0 0         if ($imsg->{'allow_srv_settings'})
1583             {
1584 0           $tsc_config_info .= "m $tsc_permit_mode\n";
1585 0           $tsc_config_info = "{" . $tsc_config_info . "}";
1586              
1587 0           $tsc_packet = 'toc_set_config ' . $tsc_config_info . "\0";
1588            
1589 0 0         return undef unless (defined $imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsc_packet, 1, 1));
1590             }
1591             }
1592              
1593             =pod
1594              
1595             =head2 $aim->toc_evil($user, $anon)
1596              
1597             This method will apply 'evil' to the specified user C<$user>. If
1598             C<$anon> evaluates to true, the evil will be done anonymously.
1599              
1600             =cut
1601              
1602             sub toc_evil
1603             {
1604             #
1605             # takes two arguments
1606             #
1607             # the first argument is the
1608             # username to evil
1609             # the second argument should be
1610             # 1 if the evil should be sent
1611             # anonymously
1612             #
1613             # returns undef if an error occurs
1614             #
1615 0     0 1   my $imsg = shift @_;
1616 0           my $te_user = $_[0];
1617 0 0         my $te_anon = ($_[1] ? 'anon' : 'norm');
1618              
1619 0 0 0       unless ((defined $te_user) && (defined $te_anon))
1620             {
1621 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1622 0           return undef;
1623             }
1624              
1625 0           my $te_evil_msg = $imsg->toc_format_msg('toc_evil', $imsg->norm_uname($te_user), $te_anon);
1626              
1627            
1628 0 0         return undef unless (defined $imsg->send_sflap_packet($SFLAP_TYPE_DATA, $te_evil_msg, 0, 0));
1629             }
1630              
1631             =pod
1632              
1633             =head2 $aim->toc_chat_join($exchange, $room_name)
1634              
1635             This method will join the chat room specified by C<$exchange> and
1636             C<$room_name>. Currently, the only valid value for C<$exchange> is 4.
1637              
1638             See the B manpage included with this package for more
1639             information on chatting.
1640              
1641             =cut
1642              
1643             sub toc_chat_join
1644             {
1645             #
1646             # takes two arguments
1647             #
1648             # exchange : the chat room exchange number to use
1649             # room_name : the name of the room to join
1650             #
1651             # returns undef on error
1652             #
1653             # this function does not get the chat room ID;
1654             # that is handled when the server sends back the
1655             # CHAT_JOIN packet, and we have a handler for that
1656             # in the incoming handler
1657             #
1658 0     0 1   my $imsg = shift @_;
1659 0           my $tcj_exchange = $_[0];
1660 0           my $tcj_room_name = $_[1];
1661              
1662 0           $tcj_room_name =~ s/\s+/ /g;
1663              
1664              
1665 0 0 0       unless ((defined $tcj_exchange) && (defined $tcj_room_name))
1666             {
1667 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1668 0           return undef;
1669             }
1670              
1671 0           my $tcj_message = $imsg->toc_format_msg('toc_chat_join', $tcj_exchange, $tcj_room_name);
1672              
1673            
1674            
1675 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tcj_message, 0, 0)));
1676             }
1677              
1678             =pod
1679              
1680             =head2 $aim->toc_chat_send($roomid, $message)
1681              
1682             This method will send the message C<$message> to the room C<$roomid>
1683             (which should be the room ID provided by the server in response to a
1684             toc_chat_join or toc_accept_invite).
1685              
1686             You will receive this message back from the server as well, so your UI
1687             does not have to handle this message in a special way.
1688              
1689             =cut
1690              
1691             sub toc_chat_send
1692             {
1693             #
1694             # takes two arguments
1695             #
1696             # roomid : the chat room ID as returned by the CHAT_JOIN server message
1697             # message: the message to send to the chat room
1698             #
1699             # no mirroring is necessary; the message will come to you by way of the
1700             # server, so you'll see your own message automatically
1701             #
1702             # returns undef on error
1703             #
1704 0     0 1   my $imsg = shift @_;
1705 0           my $tcs_roomid = $_[0];
1706 0           my $tcs_msgtext = $_[1];
1707              
1708 0 0 0       unless ((defined $tcs_roomid) && (defined $tcs_msgtext))
1709             {
1710 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1711 0           return undef;
1712             }
1713              
1714 0           my $tcs_message = $imsg->toc_format_msg('toc_chat_send', $tcs_roomid, $tcs_msgtext);
1715              
1716            
1717 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tcs_message, 0, 0)));
1718             }
1719              
1720             =pod
1721              
1722             =head2 $aim->toc_chat_whisper($roomid, $dstuser, $message)
1723              
1724             This method sends the message C<$message> to C<$dstuser> in the room
1725             C<$roomid>.
1726              
1727             The server will B send you a copy of this message, so your user
1728             interface should have a special case for displaying outgoing whispers.
1729              
1730             =cut
1731              
1732             sub toc_chat_whisper
1733             {
1734             #
1735             # takes three arguments:
1736             #
1737             # roomid : the chat room ID as returned by the CHAT_JOIN server message
1738             # dstuser: the user to whom the whisper should be directed
1739             # message: the message to send to the user as a whisper
1740             #
1741             # you should mirror this to your UI if you want to see it go there as well,
1742             # because the server will not send you a copy of this message as it does with
1743             # regular chat messages.
1744             #
1745 0     0 1   my $imsg = shift @_;
1746 0           my $tcw_roomid = $_[0];
1747 0           my $tcw_dstuser = $_[1];
1748 0           my $tcw_msgtext = $_[2];
1749              
1750 0 0 0       unless ((defined $tcw_roomid) && (defined $tcw_dstuser) && (defined $tcw_msgtext))
      0        
1751             {
1752 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1753 0           return undef;
1754             }
1755              
1756 0           my $tcw_message = $imsg->toc_format_msg('toc_chat_whisper', $tcw_roomid, $imsg->norm_uname($tcw_dstuser), $tcw_msgtext);
1757            
1758            
1759 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tcs_message, 0, 0)));
1760             }
1761              
1762             =pod
1763              
1764             =head2 $aim->toc_chat_evil($roomid, $dstuser, $anon)
1765              
1766             This will apply evil to the user C<$dstuser> in room C<$room>. If
1767             C<$anon> evaluates to true, it will be applied anonymously.
1768              
1769             Please note that this functionality is currently disabled by the TOC
1770             servers.
1771              
1772             =cut
1773              
1774             sub toc_chat_evil
1775             {
1776             #
1777             # takes three arguments:
1778             #
1779             # roomid : the chat room ID as returned by the CHAT_JOIN server message
1780             # dstuser: the user that should be eviled
1781             # isanon : should be 1 if the evil should be registered anonymously
1782             #
1783             # returns undef on error
1784             #
1785             # the chat evil functionality is currently disabled at the server end
1786             #
1787 0     0 1   my $imsg = shift @_;
1788 0           my $tce_roomid = $_[0];
1789 0           my $tce_dstuser = $_[1];
1790 0 0         my $tce_anon = ($_[2] ? 'anon' : 'norm');
1791              
1792 0 0 0       unless ((defined $tce_roomid) && (defined $tce_dstuser) && (defined $tce_anon))
      0        
1793             {
1794 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1795 0           return undef;
1796             }
1797              
1798 0           my $tce_message = $imsg->toc_format_msg('toc_chat_evil', $tce_roomid, $imsg->norm_uname($tce_dstuser), $tce_anon);
1799            
1800            
1801 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tce_message, 0, 0)));
1802             }
1803              
1804             =pod
1805              
1806             =head2 $aim->toc_chat_invite($roomid, $msgtext, $buddy1[, $buddy2[, ...]])
1807              
1808             This method will invite all users C<$buddy1..$buddyN> to room
1809             C<$roomid> with invitation text C<$msgtext>.
1810              
1811             =cut
1812              
1813             sub toc_chat_invite
1814             {
1815             #
1816             # takes at least three arguments:
1817             #
1818             # roomid : the chat room ID as returned by the CHAT_JOIN server message
1819             # msgtext: the text of the invitation message
1820             # buddy1...buddyn : the buddies to invite to the room. You can have as many
1821             # as you'd like, up to the max message length (1024)
1822             #
1823             # returns undef on error
1824             #
1825 0     0 1   my $imsg = shift @_;
1826 0           my $tci_roomid = shift @_;
1827 0           my $tci_msgtext = shift @_;
1828 0           my @tci_buddies = @_;
1829              
1830 0 0 0       unless ((defined $tci_roomid) && (defined $tci_msgtext) && (@tci_buddies))
      0        
1831             {
1832 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1833 0           return undef;
1834             }
1835              
1836 0           while (my $tci_tmp_buddy = shift @_)
1837             {
1838 0           push @tci_buddies, $imsg->norm_uname($tci_tmp_buddy);
1839             }
1840              
1841 0           my $tci_message = $imsg->toc_format_msg('toc_chat_invite', $tci_roomid, $tci_msgtext, @tci_buddies);
1842              
1843            
1844 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tci_message, 0, 0)));
1845             }
1846              
1847             =pod
1848              
1849             =head2 $aim->toc_chat_leave($roomid)
1850              
1851             This method will notify the server that you have left room C<$roomid>.
1852              
1853             =cut
1854              
1855             sub toc_chat_leave
1856             {
1857             #
1858             # takes one argument:
1859             #
1860             # roomid : the room ID as returned by the CHAT_JOIN server message
1861             #
1862             # returns undef on error
1863             #
1864 0     0 1   my $imsg = shift @_;
1865 0           my $tcl_roomid = $_[0];
1866              
1867 0 0         unless (defined $tcl_roomid)
1868             {
1869 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1870             }
1871              
1872 0           my $tcl_message = $imsg->toc_format_msg('toc_chat_leave', $tcl_roomid);
1873              
1874            
1875 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tcl_message, 0, 0)));
1876             }
1877              
1878             =pod
1879              
1880             =head2 $aim->toc_chat_accept($roomid)
1881              
1882             This method accepts a chat invitation to room C<$roomid>. You do not
1883             have to send a C message if you have been invited and
1884             accept with this method.
1885              
1886             =cut
1887              
1888             sub toc_chat_accept
1889             {
1890             #
1891             # takes one argument:
1892             #
1893             # roomid : the room ID as given by the CHAT_INVITE server message
1894             #
1895             # returns undef on error
1896             #
1897 0     0 1   my $imsg = shift @_;
1898 0           my $tca_roomid = $_[0];
1899              
1900 0 0         unless (defined $tca_roomid)
1901             {
1902 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1903 0           return undef;
1904             }
1905              
1906 0           my $tcl_message = $imsg->toc_format_msg('toc_chat_accept', $tca_roomid);
1907            
1908 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tcl_message, 0, 0)));
1909             }
1910              
1911             =pod
1912              
1913             =head2 $aim->toc_get_info($username)
1914              
1915             This method requests info on user C<$username>. See B for more
1916             information on what the server returns.
1917              
1918             =cut
1919              
1920             sub toc_get_info
1921             {
1922             #
1923             # takes one argument:
1924             #
1925             # username: the username of the person on whom to get info
1926             #
1927             # returns undef on error
1928             #
1929 0     0 1   my $imsg = shift @_;
1930 0           my $tgi_username = $_[0];
1931              
1932 0 0         unless (defined $tgi_username)
1933             {
1934 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1935 0           return undef;
1936             }
1937              
1938 0           my $tgi_message = $imsg->toc_format_msg('toc_get_info', $tgi_username);
1939            
1940 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tgi_message, 0, 0)));
1941             }
1942              
1943             =pod
1944              
1945             =head2 $aim->toc_set_info($info)
1946              
1947             This method sets the information for the current user to the ASCII
1948             text (HTML formatted) contained in C<$info>.
1949              
1950             =cut
1951              
1952             sub toc_set_info
1953             {
1954             #
1955             # takes one argument:
1956             #
1957             # information : the information of the user as HTML
1958             #
1959             # returns undef on error
1960             #
1961 0     0 1   my $imsg = shift @_;
1962 0           my $tsi_info = $_[0];
1963              
1964 0 0         unless (defined $tsi_info)
1965             {
1966 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
1967 0           return undef;
1968             }
1969              
1970 0           my $tsi_message = $imsg->toc_format_msg('toc_set_info', $tsi_info);
1971            
1972 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsi_message, 0, 0)));
1973             }
1974              
1975             =pod
1976              
1977             =head2 $aim->toc_set_away($msg)
1978              
1979             This method sets or unsets the away message. If C<$msg> is undefined,
1980             away is unset. Otherwise, away is set with the message in C<$msg>.
1981              
1982             =cut
1983              
1984             sub toc_set_away
1985             {
1986             #
1987             # takes zero or one arguments:
1988             #
1989             # awaymsg: the away message. If not specified, the away status is unset
1990             #
1991 0     0 1   my $imsg = shift @_;
1992 0           my $tsa_awaymsg = $_[0];
1993              
1994 0           my $tsa_message = $imsg->toc_format_msg('toc_set_away', $tsa_awaymsg);
1995            
1996 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsa_message, 0, 0)));
1997             }
1998              
1999             =pod
2000              
2001             =head2 $aim->toc_get_dir($username)
2002              
2003             This method sends a request to the server for directory information on
2004             C<$username>. See B for information on what the server will return.
2005              
2006             =cut
2007              
2008             sub toc_get_dir
2009             {
2010             #
2011             # takes one argument
2012             #
2013             # username : the username of the person whose dir info to retrieve
2014             #
2015 0     0 1   my $imsg = shift @_;
2016 0           my $tgd_username = $_[0];
2017              
2018 0 0         unless (defined $tgd_username)
2019             {
2020 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2021 0           return undef;
2022             }
2023              
2024 0           my $tgd_message = $imsg->toc_format_msg('toc_get_dir', $imsg->norm_uname($tgd_username));
2025            
2026 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tgd_message, 0, 0)));
2027             }
2028              
2029             =pod
2030              
2031             =head2 $aim->toc_set_dir($userinfo)
2032              
2033             This method sets the information on the current user to the string
2034             provided as C<$userinfo>. See B for more information on the
2035             format of the C<$userinfo> string.
2036              
2037             =cut
2038              
2039             sub toc_set_dir
2040             {
2041             #
2042             # takes one argument
2043             #
2044             # userinfo : the user information for the TOC directory. This should be specified as
2045             # "first name":"middle name":"last name":"maiden name":"city":"state":"country":"email":"allow web searches"
2046             #
2047 0     0 1   my $imsg = shift @_;
2048 0           my $tsd_userinfo = $_[0];
2049              
2050 0 0         unless (defined $tsd_userinfo)
2051             {
2052 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2053 0           return undef;
2054             }
2055              
2056 0           my $tsd_message = $imsg->toc_format_msg('toc_set_dir', $tsd_userinfo);
2057            
2058 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsd_message, 0, 0)));
2059             }
2060              
2061             =pod
2062              
2063             =head2 $aim->toc_dir_search($searchstr)
2064              
2065             This method will search the directory using C<$searchstr>. See
2066             B for more information on how this string should look.
2067              
2068             =cut
2069              
2070             sub toc_dir_search
2071             {
2072             #
2073             # takes one argument
2074             #
2075             # searchstr : the string of information to search for. This should be specified as
2076             # "first name":"middle name":"last name":"maiden name":"city":"state":"country":"email"
2077             #
2078 0     0 1   my $imsg = shift @_;
2079 0           my $tds_searchstr = $_[0];
2080              
2081 0 0         unless (defined $tds_searchstr)
2082             {
2083 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2084 0           return undef;
2085             }
2086              
2087 0           my $tds_message = $imsg->toc_format_msg('toc_dir_search', $tds_searchstr);
2088            
2089 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tds_message, 0, 0)));
2090             }
2091              
2092             =pod
2093              
2094             =head2 $aim->toc_set_idle($seconds)
2095              
2096             This method sets the number of seconds that the client has been idle.
2097             If it is 0, the idle is cleared. Otherwise, the idle is set and the
2098             server will continue to count up the idle time (thus, you need only
2099             call C once in order to become idle).
2100              
2101             =cut
2102              
2103             sub toc_set_idle
2104             {
2105             #
2106             # takes one argument:
2107             #
2108             # seconds : the number of seconds the user has been idle. use 0 to clear the
2109             # idle counter and stop idle counting. Setting it to any other
2110             # value will make the server set that idle time and continue to increment
2111             # the idle time, so only one is necessary to start idle timing
2112             #
2113             # returns undef on error
2114             #
2115 0     0 1   my $imsg = shift @_;
2116 0           my $tsi_seconds = $_[0];
2117              
2118 0 0         unless (defined $tsi_seconds)
2119             {
2120 0           $tsi_seconds = 0;
2121             }
2122              
2123 0           my $tsi_message = $imsg->toc_format_msg('toc_set_idle', $tsi_seconds);
2124            
2125 0 0         return undef unless (defined ($imsg->send_sflap_packet($SFLAP_TYPE_DATA, $tsi_message, 0, 0)));
2126             }
2127              
2128             #*****************************************************
2129             # Module interface/data movement functions
2130             #
2131             # these functions have to do with checking whether input
2132             # is ready and allowing the user to request that we block
2133             # on the filehandles that we have in our select loop
2134             # (including user-added filehandles) until something happens
2135             #*****************************************************
2136              
2137             =pod
2138              
2139             =head2 $aim->ui_add_fh($filehandle, \&callback)
2140              
2141             This method will add a filehandle to the C loop that will be
2142             called with C. If information is found to be on that
2143             filehandle, the callback will be executed. It is the responsibility
2144             of the callback to read the data off the socket.
2145              
2146             B
2147             is unreliable at best. Avoid the use of read(), EFHE, and print();
2148             instead, use sysread() and syswrite()>
2149              
2150             =cut
2151              
2152             sub ui_add_fh
2153             {
2154             #
2155             # takes two arguments:
2156             #
2157             # filehandle : a filehandle to add to the select loop
2158             # this should be a reference to the filehandle (or
2159             # a scalar containing the reference, such as the one
2160             # returned by IO::Socket)
2161             # callback : the callback function to call when data comes
2162             # over the selected filehandle. This function will
2163             # be called with the data that came over the filehandle
2164             # as the argument. This should be passed as a reference
2165             # to the function
2166             #
2167 0     0 1   my $imsg = shift @_;
2168 0           my $fh = $_[0];
2169 0           my $cb = $_[1];
2170              
2171 0 0 0       unless ((defined $fh) && (defined $cb))
2172             {
2173 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2174 0           return undef;
2175             }
2176              
2177 0           $imsg->{'sel'}->add($fh);
2178 0           $ { $imsg->{'callbacks'} }{$fh} = $cb;
  0            
2179             }
2180              
2181             =pod
2182              
2183             =head2 $aim->ui_del_fh($filehandle)
2184              
2185             The filehandle C<$filehandle> will be removed from the C
2186             loop and it will no longer be checked for input nor its callback
2187             activated.
2188              
2189             =cut
2190              
2191             sub ui_del_fh
2192             {
2193             #
2194             # takes one argument:
2195             #
2196             # filehandle : the filehandle to delete from the select loop
2197             # this should be the same reference or scalar that
2198             # was passed to ui_add_fh
2199             #
2200 0     0 1   my $imsg = shift @_;
2201 0           my $fh = $_[0];
2202              
2203 0 0         unless (defined $fh)
2204             {
2205 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2206 0           return undef;
2207             }
2208            
2209 0           $imsg->{'sel'}->remove($fh);
2210 0           delete $ { $imsg->{'callbacks'} }{$fh};
  0            
2211             }
2212              
2213             =pod
2214              
2215             =head2 $aim->ui_all_fh()
2216              
2217             This method returns a list of all filehandles currently in the
2218             C loop.
2219              
2220             =cut
2221              
2222             sub ui_all_fh
2223             {
2224             #
2225             # takes no arguments
2226             #
2227             # returns a list of all the current filehandles
2228             # in the select loop
2229             #
2230 0     0 1   my $imsg = shift @_;
2231              
2232 0           return $imsg->{'sel'}->handles();
2233             }
2234              
2235             =pod
2236              
2237             =head2 $aim->ui_exists_fh($filehandle)
2238              
2239             This method will return true if C<$filehandle> is in the select loop.
2240             Otherwise, it will return undefined.
2241              
2242             =cut
2243              
2244             sub ui_exists_fh
2245             {
2246             #
2247             # takes one argument
2248             #
2249             # filehandle : the filehandle to check for existence in
2250             # the select loop
2251             #
2252             # returns a true value if filehandle is in the loop, and
2253             # undefined otherwise
2254             #
2255 0     0 1   my $imsg = shift @_;
2256 0           my $fh = $_[0];
2257              
2258 0           return $imsg->{'sel'}->exists($fh);
2259             }
2260              
2261             =pod
2262              
2263             =head2 $aim->ui_set_callback(\&callback)
2264              
2265             This method will change the callback function for the server socket to
2266             the method referenced by \&callback. This allows you to change the
2267             callback from the one specified when the object was created. (Imagine
2268             the possibilities--dynamically created callback functions using
2269             C... mmmm...)
2270              
2271             =cut
2272              
2273             sub ui_set_callback
2274             {
2275             #
2276             # takes one argument:
2277             #
2278             # callback : a reference to the callback function
2279             # for incoming remote data
2280             #
2281             # to set the callback for a user-defined filehandle,
2282             # use the ui_add_fh function
2283             #
2284 0     0 1   my $imsg = shift @_;
2285 0           my $callback = $_[0];
2286 0           my $im_socket = \$imsg->{'im_socket'};
2287              
2288 0 0         unless (defined $callback)
2289             {
2290 0           $main::IM_ERR = $SFLAP_ERR_ARGS;
2291 0           return undef;
2292             }
2293              
2294 0           $imsg->{'callback'} = $callback;
2295             }
2296              
2297             =pod
2298              
2299             =head2 $aim->ui_get_callback($filehandle)
2300              
2301             This method returns a reference to the callback associated with
2302             $filehandle, or the callback associated with the server socket if
2303             $filehandle is undefined.
2304              
2305             =cut
2306              
2307             sub ui_get_callback
2308             {
2309             #
2310             # takes zero or one arguments:
2311             #
2312             # filehandle : the filehandle whose callback should be returned
2313             #
2314             # if filehandle is not specified, the a reference to the callback
2315             # for the server socket is returned
2316             #
2317 0     0 1   my $imsg = shift @_;
2318 0           my $fh = $_[0];
2319              
2320 0 0         if (defined $fh)
2321             {
2322 0           return $ { $imsg->{'callbacks'}}{$fh};
  0            
2323             }
2324             else
2325             {
2326 0           return $imsg->{'callback'};
2327             }
2328             }
2329              
2330             =pod
2331              
2332             =head2 $aim->ui_dataget($timeout)
2333              
2334             This is the workhorse method in this object. When this method is
2335             called, it will go through a single C loop to find if any
2336             filehandles are ready for reading. If $timeout is defined, the
2337             C timeout will be that number of seconds (fractions are OK).
2338             Otherwise, C will block.
2339              
2340             For each filehandle that is ready for reading, this function will call
2341             the appropriate callback function. It is the responsibility of the
2342             callback to read the data off the filehandle and handle it
2343             appropriately. The exception to this rule is the server socket, whose
2344             data will be read and passed to the server socket callback function.
2345             All pasrsing of data from the server into edible chunks will be done
2346             for you before the server socket callback function is called. From
2347             there, it is up to to the client program to parse the server responses
2348             appropriately. They will be passed such that each field in the server
2349             response is one argument to the callback (the number of arguments will
2350             be correct). For more information on the information coming from the
2351             server, see B.
2352              
2353             This method returns undef on an error (including errors from
2354             callbacks, which should be signified by returning undef) and returns
2355             the number of filehandles that were read otherwise.
2356              
2357             =cut
2358              
2359             sub ui_dataget
2360             {
2361             #
2362             # takes zero or one arguments:
2363             #
2364             # time : the time in seconds to wait for the selects to return
2365             #
2366             # if time is undef(), then the call will block
2367             #
2368             # for each filehandle that returns something, the matching
2369             # callback function will be called to read the data and handle
2370             # it.
2371             #
2372             # returns undef on error
2373             #
2374 0     0 1   my $imsg = shift @_;
2375 0           my $timeout = $_[0];
2376 0           my $recv_buffer = "";
2377 0           my @ready = ();
2378 0           my $im_socket = \$imsg->{'im_socket'};
2379              
2380 0           @ready = $imsg->{'sel'}->can_read($timeout);
2381              
2382 0           foreach $rfh (@ready)
2383             {
2384 0 0         if ($rfh == $$im_socket)
2385             {
2386 0 0         return undef unless defined($recv_buffer = $imsg->read_sflap_packet());
2387 0           ($tp_type, $tp_tmp) = split(/:/, $recv_buffer, 2);
2388            
2389             # pause if we've been told to by the server
2390 0 0         if ($tp_type eq 'PAUSE')
    0          
    0          
2391             {
2392 0           $imsg->{'pause'} = 1;
2393             }
2394             # re-run signon if we're getting a new SIGN_ON packet
2395             elsif ($tp_type eq 'SIGN_ON')
2396             {
2397 0           $imsg->signon;
2398             }
2399             # handle CONFIG packets from the server, respecting
2400             # the allow_srv_settings flag from the user
2401             elsif ($tp_type eq 'CONFIG')
2402             {
2403 0           $imsg->set_srv_buddies($tp_tmp);
2404             }
2405            
2406 0           &{$imsg->{'callback'}}($tp_type, split(/:/,$tp_tmp,$SERVER_MSG_ARGS{$tp_type}));
  0            
2407             }
2408             else
2409             {
2410 0 0         return undef unless (&{$ { $imsg->{'callbacks'}}{$rfh}});
  0            
  0            
2411             }
2412             }
2413 0           return scalar(@ready);
2414             }
2415              
2416             =pod
2417              
2418             =head1 ROLLING YOUR OWN
2419              
2420             This section deals with usage that deals directly with the server
2421             connection and bypasses the ui_* interface and/or the toc_* interface.
2422             If you are happy calling ui_dataget et al., do not bother reading this
2423             section. If, however, you plan not to use the provided interfaces, or
2424             if you want to know more of what is going on, continue on.
2425              
2426             First of all, if you do not plan to use the provided interface to the
2427             server socket, you will need to be able to access the server socket
2428             directly. In order to do this, use $aim-Esrv_socket:
2429              
2430             $srv_sock = $aim->srv_socket;
2431              
2432             This will return a B to the socket. You will need to
2433             dereference it in order to use it.
2434              
2435             In general, however, even if you are rolling your own, you will
2436             probably not need to use C or the like.
2437             C will handle unwrapping the data coming from the
2438             server and will return the payload of the packet as a single scalar.
2439             Using this will give you the data coming from the server in a form
2440             that you can C to get the message and its arguments. In
2441             order to facilitate such splitting, C<%Net::AOLIM::SERVER_MSG_ARGS> is
2442             supplied. For each valid server message,
2443             C<$Net::AOLIM::SERVER_MSG_ARGS{$msg}> will return one less than the
2444             proper number of splits to perform on the data coming from the server.
2445             The intended use is such:
2446              
2447             ($msg, $rest) = split(/:/, $aim->read_sflap_packet(), 2);
2448             @msg_args = split(/:/, $rest, $Net::AOLIM::SERVER_MSG_ARGS{$msg});
2449              
2450             Now you have the server message in C<$msg> and the arguments in
2451             C<@msg_args>.
2452              
2453             To send packets to the server without having to worry about making
2454             SFLAP packets, use C. If you have a string to
2455             send to the server (which is not formatted), you would use:
2456              
2457             $aim->send_sflap_packet($SFLAP_TYPE_DATA, $message, 0, 0);
2458              
2459             The SFLAP types (listed in B are:
2460              
2461             $SFLAP_TYPE_SIGNON
2462             $SFLAP_TYPE_DATA
2463             $SFLAP_TYPE_ERROR
2464             $SFLAP_TYPE_SIGNOFF
2465             $SFLAP_TYPE_KEEPALIVE
2466              
2467             Most of the time you will use $SFLAP_TYPE_DATA.
2468              
2469             If you want to roll your own messages, read the code for
2470             C and you should be able to figure it out. Note
2471             that the header is always supplied by C.
2472             Specifying C will only make C assume
2473             that C<$message> is a preformatted payload. Specifying C<$noterm>
2474             will prevent C from adding a trailing '\0' to the
2475             payload. If it is already formatted, C will ignore
2476             C<$noterm>.
2477              
2478             Messages sent to the server should be escaped and formatted properly
2479             as defined in B. C<$aim-Etoc_format_msg> will do just this;
2480             supply it with the TOC command and the arguments to the TOC command
2481             (each as separate strings) and it will return a single string that is
2482             formatted appropriately.
2483              
2484             All usernames sent as TOC command arguments must be normalized (see
2485             B). C<$aim-Enorm_uname()> will do just this. Make sure to
2486             normalize usernames before passing them as arguments to
2487             C<$aim-Etoc_format_msg()>.
2488              
2489             C performs roasting as defined in B. It is not very
2490             exciting. I do not see why it is that you would ever need to do this,
2491             as C<$aim-Esignon()> handles this for you (and the roasted password is
2492             stored in C<$aim-E{'roastedp'}>). However, if you want to play with
2493             it, there it is.
2494              
2495             =head1 EXAMPLES
2496              
2497             See the file F for an example of how to interact with
2498             this class.
2499              
2500             =head1 FILES
2501              
2502             F
2503            
2504             A sample client that demonstrates how this object could be used.
2505              
2506             =head1 SEE ALSO
2507              
2508             See also B.
2509              
2510             =head1 AUTHOR
2511              
2512             Copyright 2000-02 Riad Wahby EBE All rights reserved
2513             This program is free software. You may redistribute it and/or
2514             modify it under the same terms as Perl itself.
2515              
2516             =head1 HISTORY
2517              
2518             B<0.01>
2519              
2520             Initial Beta Release. (7/7/00)
2521              
2522             B<0.1>
2523              
2524             First public (CPAN) release. (7/14/00)
2525              
2526             B<0.11>
2527              
2528             Re-release under a different name with minor changes to the
2529             documentation. (7/16/00)
2530              
2531             B<0.12>
2532              
2533             Minor modification to fix a condition in which the server's
2534             connection closing could cause an infinite loop.
2535              
2536             B<1.0>
2537              
2538             Changed the client agent string to TOC1.0 to fix a problem where
2539             connections were sometimes ignored. Also changed the default signon
2540             port to 5198 and the login port to 1234.
2541              
2542             B<1.1>
2543              
2544             Changed the client agent string again, this time to what seems
2545             like the "correct" format, which is
2546             PROGRAM:$Version info$
2547             Also added the ability to set a login timeout in case the SIGN_ON
2548             packet never comes.
2549              
2550             B<1.2>
2551              
2552             Fixed a bug in toc_chat_invite that made it ignore some of its
2553             arguments. This should fix various problems with using this
2554             subroutine. Thanks to Mike Golvach for pointing this out.
2555              
2556             B<1.3>
2557              
2558             Changed (defined @tci_buddies) to (@tci_buddies) in toc_chat_invite.
2559             Fixed a potential infinite loop in set_srv_buddies involving an
2560             off-by-one error in a for() test. Thanks to Bruce Winter for
2561             pointing this out.
2562              
2563             B<1.4>
2564              
2565             Changed the way that Net::AOLIM sends the login command string
2566             because AOL apparently changed their server software, breaking the
2567             previous implementation. The new method requires that only the
2568             user agent string be in double quotes; all other fields should not
2569             be quoted. Note that this does not affect the user interface at
2570             all---it's all handled internally. Thanks to Bruce Winter, Fred
2571             Frey, Aryeh Goldsmith, and tik for help in tracking down and
2572             fixing this error.
2573              
2574             Also added additional checks to read_sflap_packet so that if the
2575             other end of the connection dies we don't go into an infinite
2576             loop. Thanks to Chris Nelson for pointing this out.
2577              
2578             B<1.5>
2579              
2580             Added a very simple t/use.t test script that just makes sure
2581             the module loads properly.
2582              
2583             B<1.6>
2584              
2585             Patched around yet another undocumented "feature" of the TOC
2586             protocol---namely, in order to successfully sign on, you must have
2587             at least one buddy in your buddy list. At sign-on, in the absence
2588             of a real buddy list, Net::AOLIM inserts the current user as a
2589             buddy in group "Me." Don't bother removing this buddy, as it
2590             doesn't really exist---as soon as you add any real buddies, this
2591             one will go away. Thanks to Galen Johnson and Jay Luker for
2592             emailing with the symptoms.
2593              
2594             B<1.61>
2595              
2596             Made a small change to the example.pl script to keep it from
2597             dumping deref warnings. Thanks to an anonymous person who sent
2598             this suggestion through the CPAN bug tracking system.
2599              
2600             =cut