File Coverage

blib/lib/Net/MSN.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Net::MSN - Construct for connecting to the MSN network.
2             # Originally written by:
3             # Adam Swann - http://www.adamswann.com/library/2002/msn-perl/
4             # Modified by:
5             # David Radunz - http://www.boxen.net/
6             #
7             # $Id: MSN.pm,v 1.22 2003/10/29 22:21:48 david Exp $
8            
9             package Net::MSN;
10            
11 1     1   1141 use strict;
  1         3  
  1         52  
12 1     1   8 use warnings;
  1         2  
  1         54  
13            
14             BEGIN {
15             # Modules
16             # CPAN
17 1     1   6 use Digest::MD5 qw(md5_hex);
  1         15  
  1         83  
18            
19             # Local
20 1     1   772 use Net::MSN::PassPort;
  1         2  
  1         31  
21 1     1   720 use Net::MSN::SB;
  0            
  0            
22            
23             # Inherit Base Class
24             use base 'Net::MSN::Base';
25            
26             use constant TRUE => 1;
27             use constant FALSE => 0;
28             use constant MSN_PROTOCOL => 'MSNP9 MSNP8 CVRO';
29             use constant MSN_VERSION => '6.0.0602';
30             use constant OPERATING_SYSTEM => 'winnt 5.1 i386';
31            
32             use vars qw($VERSION);
33            
34             $VERSION = do { my @r=(q$Revision: 1.22 $=~/\d+/g); sprintf "%d."."%03d"x$#r,@r };
35            
36             use vars qw(%errlist %statuscodes %PendingMsgs);
37            
38             %errlist = (
39             200 => 'ERR_SYNTAX_ERROR',
40             201 => 'ERR_INVALID_PARAMETER',
41             205 => 'ERR_INVALID_USER',
42             206 => 'ERR_FQDN_MISSING',
43             207 => 'ERR_ALREADY_LOGIN',
44             208 => 'ERR_INVALID_USERNAME',
45             209 => 'ERR_INVALID_FRIENDLY_NAME',
46             210 => 'ERR_LIST_FULL',
47             215 => 'ERR_ALREADY_THERE',
48             216 => 'ERR_NOT_ON_LIST',
49             218 => 'ERR_ALREADY_IN_THE_MODE',
50             219 => 'ERR_ALREADY_IN_OPPOSITE_LIST',
51             280 => 'ERR_SWITCHBOARD_FAILED',
52             281 => 'ERR_NOTIFY_XFR_FAILED',
53             300 => 'ERR_REQUIRED_FIELDS_MISSING',
54             302 => 'ERR_NOT_LOGGED_IN',
55             500 => 'ERR_INTERNAL_SERVER',
56             501 => 'ERR_DB_SERVER',
57             510 => 'ERR_FILE_OPERATION',
58             520 => 'ERR_MEMORY_ALLOC',
59             600 => 'ERR_SERVER_BUSY',
60             601 => 'ERR_SERVER_UNAVAILABLE',
61             602 => 'ERR_PEER_NS_DOWN',
62             603 => 'ERR_DB_CONNECT',
63             604 => 'ERR_SERVER_GOING_DOWN',
64             707 => 'ERR_CREATE_CONNECTION',
65             711 => 'ERR_BLOCKING_WRITE',
66             712 => 'ERR_SESSION_OVERLOAD',
67             713 => 'ERR_USER_TOO_ACTIVE',
68             714 => 'ERR_TOO_MANY_SESSIONS',
69             715 => 'ERR_NOT_EXPECTED',
70             717 => 'ERR_BAD_FRIEND_FILE',
71             911 => 'ERR_AUTHENTICATION_FAILED',
72             913 => 'ERR_NOT_ALLOWED_WHEN_OFFLINE',
73             920 => 'ERR_NOT_ACCEPTING_NEW_USERS',
74             );
75            
76             %statuscodes = (
77             NLN => 'Online',
78             FLN => 'Offline',
79             HDN => 'Hidden',
80             BSY => 'Busy',
81             IDL => 'Idle',
82             BRB => 'Be Right Back',
83             AWY => 'Away',
84             PHN => 'On the Phone',
85             LUN => 'Out to Lunch'
86             );
87             }
88            
89             sub new {
90             my ($class, %args) = @_;
91            
92             my %defaults = (
93             ScreenName => '',
94             Handle => '',
95             Password => '',
96             Host => 'messenger.hotmail.com',
97             Port => 1863,
98             AutoReconnect => 1,
99             AutoReconnectDelay => 10,
100             _Type => 'NS'
101             );
102             my $self = __PACKAGE__->SUPER::new(
103             __PACKAGE__->SUPER::merge_opts(\%defaults, \%args)
104             );
105            
106             $self->{_args} = \%args;
107             $self->{Callback} = {};
108             $self->{Requests} = {};
109             $self->{Sessions} = {};
110             $self->{Buddies} = {};
111             $self->{PendingSB} = {};
112            
113             $self->{_PassPort} = new Net::MSN::PassPort(%args);
114            
115             return $self;
116             }
117            
118             sub _construct_args {
119             my ($self, %newargs) = @_;
120            
121             if (defined $self->{_args} && ref $self->{_args} eq 'HASH') {
122             my %args = %{$self->{_args}};
123            
124             foreach my $arg (keys %newargs) {
125             $args{$arg} = $newargs{$arg};
126             }
127            
128             return %args;
129             } else {
130             return %newargs;
131             }
132             }
133            
134             sub _connect_SB {
135             my ($self, $chandle, $host, $port, $key, $type, $sid, $pc) = @_;
136            
137             $port = $port || $self->{Port};
138             $type = $type || 'USR';
139            
140             if ($self->if_session_exists($chandle)) {
141             $self->{_Log}('## HAVE EXISTING SESSION, CLOSING!! ##', 1);
142             $self->_disconnect_SB($self->{Sessions}->{$chandle});
143             }
144            
145             my $sb = new Net::MSN::SB(
146             $self->_construct_args(
147             _Host => $host,
148             _Port => $port,
149             Handle => $chandle
150             )
151             );
152            
153             $sb->construct_socket();
154            
155             if (defined $pc && $pc == 1) {
156             $sb->{PendingCall} = 1;
157             $sb->{PendingMsgs} = 1 if ($self->have_pending_msgs($chandle));
158             }
159            
160             $self->remove_pending_SB($chandle);
161             $self->{Sessions}->{$chandle} = $sb;
162            
163             my $send_msg = $self->{Handle}. ' ' . $key;
164             $send_msg .= ' '. $sid if ($type eq 'ANS');
165            
166             $sb->send($type, $send_msg);
167             }
168            
169             sub _disconnect_SB {
170             my ($self, $sb) = @_;
171            
172             return unless (defined $sb && $sb);
173            
174             my $chandle = $sb->{Handle};
175            
176             $sb->remove_socket();
177             $self->remove_session($chandle);
178             }
179            
180             sub get_SB {
181             my ($self, $chandle) = @_;
182            
183             if ($self->if_session_exists($chandle)) {
184             return $self->{Sessions}->{$chandle};
185             }
186             }
187            
188             sub if_pending_SB {
189             my ($self, $chandler) = @_;
190            
191             return (defined $chandler && defined $self->{PendingSB} &&
192             exists $self->{PendingSB}->{$chandler} &&
193             $self->{PendingSB}->{$chandler} == 1);
194             }
195            
196             sub remove_pending_SB {
197             my ($self, $chandler) = @_;
198            
199             if ($self->if_pending_SB($chandler)) {
200             delete($self->{PendingSB}->{$chandler});
201             }
202             }
203            
204             sub if_request_exists {
205             my ($self, $trid) = @_;
206            
207             return (defined $trid && defined $self->{Requests} &&
208             exists $self->{Requests}->{$trid});
209             }
210            
211             sub if_request_type_exists {
212             my ($self, $trid, $type) = @_;
213            
214             return ($self->if_request_exists($trid) &&
215             defined $type &&
216             exists $self->{Requests}->{$trid}->{Type} &&
217             $self->{Requests}->{$trid}->{Type} eq $type);
218             }
219            
220             sub remove_request {
221             my ($self, $trid) = @_;
222            
223             if ($self->if_request_exists($trid)) {
224             delete($self->{Requests}->{$trid});
225             }
226             }
227            
228             sub if_session_exists {
229             my ($self, $chandle) = @_;
230            
231             return (defined $self->{Sessions} && defined $chandle &&
232             exists $self->{Sessions}->{$chandle});
233             }
234            
235             sub remove_session {
236             my ($self, $chandle) = @_;
237            
238             if ($self->if_session_exists($chandle)) {
239             delete($self->{Sessions}->{$chandle});
240             }
241             }
242            
243             sub sendmsg {
244             my ($self, $chandle, $message) = @_;
245            
246             return unless (defined $chandle && defined $message);
247            
248             my $sb = $self->get_SB($chandle);
249             if (defined $sb && $sb) {
250             if (defined $sb->{PendingMsgs} && $sb->{PendingMsgs} == 1) {
251             push(@{$PendingMsgs{$chandle}}, $message);
252             return 1;
253             }
254             unless (defined $sb->{Connected} && $sb->{Connected} == 1) {
255             push(@{$PendingMsgs{$chandle}}, $message);
256             $sb->{PendingMsgs} = 1;
257             } else {
258             $sb->sendmsg($message);
259             }
260             return 1;
261             } else {
262             if ($self->if_pending_SB($chandle)) {
263             push(@{$PendingMsgs{$chandle}}, $message);
264             } else {
265             push(@{$PendingMsgs{$chandle}}, $message);
266             $self->{PendingSB}->{$chandle} = 1;
267             return $self->call($chandle);
268             }
269             }
270            
271             return;
272             }
273            
274             sub have_pending_msgs {
275             my ($self, $chandle) = @_;
276            
277             return unless (defined $chandle && $chandle &&
278             %PendingMsgs && exists $PendingMsgs{$chandle} &&
279             ref $PendingMsgs{$chandle} eq 'ARRAY' &&
280             @{$PendingMsgs{$chandle}} >= 1);
281            
282             return 1;
283             }
284            
285             sub call {
286             my ($self, $handle) = @_;
287            
288             if ($self->is_buddy_online($handle)) {
289             $self->send('XFR', 'SB');
290            
291             $self->{Requests}->{$__PACKAGE__::TrID}->{Type} = 'XFR';
292             $self->{Requests}->{$__PACKAGE__::TrID}->{Call} = 1;
293             $self->{Requests}->{$__PACKAGE__::TrID}->{Handle} = $handle;
294            
295             return 1;
296             }
297            
298             return;
299             }
300            
301             sub buddyaddfl {
302             my ($self, $username, $fname) = @_;
303            
304             $self->send('ADD', 'FL '. $username. ' '. $fname);
305             }
306            
307             sub buddyaddal {
308             my ($self, $username, $fname) = @_;
309            
310             $self->send('ADD', 'AL '. $username. ' '. $fname);
311             }
312            
313             sub buddyadd {
314             my ($self, $username, $fname) = @_;
315            
316             return unless (defined $username);
317             return if (defined $self->{Buddies}->{$username});
318            
319             $self->{Buddies}->{$username}->{Seen} = 0;
320             $self->{Buddies}->{$username}->{FName} = $fname;
321             $self->{Buddies}->{$username}->{DisplayName} = $self->normalize($fname);
322             $self->{Buddies}->{$username}->{DisplayName} =~ s/0$//;
323            
324             unless (defined($self->{Buddies}->{$username}->{Status})) {
325             $self->{Buddies}->{$username}->{Status} = $statuscodes{'FLN'};
326             $self->{Buddies}->{$username}->{StatusCode} = 'FLN';
327             $self->{Buddies}->{$username}->{NLNCode} = '';
328             $self->{Buddies}->{$username}->{LastChange} = time;
329             }
330            
331             return 1;
332             }
333            
334             sub buddyupdate {
335             my ($self, $username, $fname, $status) = @_;
336            
337             return unless (defined $username);
338            
339             $self->{Buddies}->{$username}->{Seen} = 1;
340            
341             if (defined $fname && $fname) {
342             $self->{Buddies}->{$username}->{FName} = $fname;
343             $self->{Buddies}->{$username}->{DisplayName} =
344             $self->normalize($fname);
345             }
346             if (defined $status && $status) {
347             $self->{Buddies}->{$username}->{Status} = $statuscodes{$status};
348             if ($status ne 'FLN' && $status ne 'NLN' &&
349             $status ne 'HDN') {
350             $self->{Buddies}->{$username}->{StatusCode} = 'NLN';
351             $self->{Buddies}->{$username}->{NLNCode} = $status;
352             } else {
353             $self->{Buddies}->{$username}->{StatusCode} = $status;
354             $self->{Buddies}->{$username}->{NLNCode} = '';
355             }
356             $self->{Buddies}->{$username}->{LastChange} = time;
357             }
358             }
359            
360             sub buddyname {
361             my ($self, $username) = @_;
362            
363             return unless (defined $username);
364             return $self->{Buddies}->{$username}->{DisplayName};
365             }
366            
367             sub buddystatus {
368             my ($self, $username, $status) = @_;
369            
370             return unless (defined $username);
371             return $self->{Buddies}->{$username}->{Status};
372             }
373            
374             sub is_buddy_offline {
375             my ($self, $username) = @_;
376            
377             if ($self->if_buddy_exists($username)) {
378             if (defined($self->{Buddies}->{$username}->{StatusCode})) {
379             return 1 if ($self->{Buddies}->{$username}->{StatusCode} eq 'FLN');
380             }
381             }
382            
383             return;
384             }
385            
386             sub is_buddy_online {
387             my ($self, $username) = @_;
388            
389             if ($self->if_buddy_exists($username)) {
390             if (defined $self->{Buddies}->{$username}->{StatusCode}) {
391             return 1 if ($self->{Buddies}->{$username}->{StatusCode} eq 'NLN');
392             }
393             }
394            
395             return;
396             }
397            
398             sub if_buddy_exists {
399             my ($self, $username) = @_;
400            
401             return (defined $username && defined $self->{Buddies}->{$username});
402             }
403            
404             sub remove_buddy {
405             my ($self, $username) = @_;
406            
407             if ($self->if_buddy_exists($username)) {
408             delete($self->{Buddies}->{$username});
409             }
410             }
411            
412             sub connect {
413             my ($self, $handle, $password, $args) = @_;
414            
415             $self->{'Handle'} = $handle if (defined $handle);
416             $self->{'Password'} = $password if (defined $password);
417            
418             $self->set_options($args) if (defined $args && ref $args eq 'HASH');
419            
420             die "MSN->connect(Username,Password, [{ args }])\n"
421             unless (defined $self->{'Handle'} && defined $self->{'Password'});
422            
423             die "MSN->connect(Username,Password, [{ Host => 'messenger.hotmail.com'".
424             ", Port => 1863 }]\n"
425             unless (defined $self->{Host} && defined $self->{Port});
426            
427             ($self->{_Host}, $self->{_Port}) = ($self->{Host}, $self->{Port});
428            
429             # Create the socket and add to the Select object.
430             $self->construct_socket();
431            
432             $self->send('VER', MSN_PROTOCOL);
433            
434             return 1;
435             }
436            
437             sub disconnect {
438             my ($self) = @_;
439            
440             $self->sendnotrid('OUT');
441             $self->disconnect_socket();
442             }
443            
444             sub if_callback_exists {
445             my ($self, $callback) = @_;
446            
447             return (defined $callback && defined $self->{Callback} &&
448             defined $self->{Callback}->{$callback} &&
449             ref $self->{Callback}->{$callback} eq 'CODE');
450             }
451            
452             sub set_event {
453             my ($self, %events) = @_;
454            
455             return unless (%events);
456            
457             foreach my $event (keys %events) {
458             $self->{Callback}->{$event} = $events{$event};
459             }
460             }
461            
462             sub check_event {
463             my ($self) = @_;
464            
465             if (my @ready = $__PACKAGE__::Select->can_read(0.1)) {
466             foreach my $fh (@ready) {
467             my $fn = $fh->fileno();
468             my $this_self = ${$__PACKAGE__::Socks->{$fn}};
469            
470             if (my $line = $fh->getline()) {
471             $line =~ s/[\r\n]//g;
472             $self->{_Log}('('. $fn. ')RX: '. $line, 3);
473             $self->process_event($this_self, $line, $fh);
474             } else {
475             $self->cleanup_closed_socket($this_self);
476             next;
477             }
478             }
479             }
480            
481             return 1;
482             }
483            
484             sub cleanup_closed_socket {
485             my ($self, $this_self) = @_;
486            
487             if ($this_self->{_Type} eq 'SB') {
488             $self->{_Log}("Switch Board closed the connection", 1);
489             $self->_disconnect_SB($this_self);
490             } else {
491             $self->{_Log}("Notification Server closed the connection", 1);
492             $this_self->remove_socket();
493            
494             # AutoReconnect
495             if (defined $self->{AutoReconnect} &&
496             $self->{AutoReconnect} == 1 &&
497             defined $self->{AutoReconnectDelay} &&
498             $self->{AutoReconnectDelay} >= 0) {
499             &{$self->{Callback}->{on_disconnect}}
500             if ($self->if_callback_exists('on_disconnect'));
501             $self->{_Log}("Auto Reconnecting .. in ".
502             $self->{AutoReconnectDelay}. " seconds", 1);
503             sleep $self->{AutoReconnectDelay};
504             $self->connect();
505             } else {
506             if ($self->if_callback_exists('on_disconnect')) {
507             &{$self->{Callback}->{on_disconnect}};
508             } else {
509             die "Notification Server closed the connection, ".
510             "and no Auto Reconnect specified!\n";
511             }
512             }
513             }
514             }
515            
516             sub process_event {
517             my ($self, $this_self, $line, $fh) = @_;
518            
519             my ($cmd, @data) = split(/ /, $line);
520            
521             return unless (defined $cmd && $cmd);
522            
523             if ($cmd eq 'VER') {
524             $this_self->send('CVR', '0x0409 '. OPERATING_SYSTEM. ' MSNMSGR '. MSN_VERSION. ' MSMSGS '. $self->{'Handle'});
525             } elsif ($cmd eq 'CVR') {
526             $this_self->send('USR', 'TWN I '. $self->{'Handle'});
527             # } elsif ($cmd eq 'INF') {
528             # my $secpkg = $data[1];
529             # if ($secpkg eq 'MD5') {
530             # $this_self->send('USR', 'MD5 I '. $self->{'Handle'});
531             # } else {
532             # $self->{_Log}('Unknown security package: '. $secpkg.
533             # ' requested by the server', 1);
534             # }
535             } elsif ($cmd eq 'USR') {
536             if ($data[1] eq 'TWN' && $data[2] eq 'S') {
537             my $key = $self->{_PassPort}->login(
538             $self->{'Handle'}, $self->{'Password'}, $data[3]
539             );
540             die "Couldnt retrieve session key!" unless (defined $key);
541             $this_self->send('USR', 'TWN S '. $key);
542             } elsif ($data[1] eq 'OK') {
543             if ($this_self->{_Type} eq 'SB') {
544             $this_self->{Connected} = 1;
545             if (defined $this_self->{PendingCall} &&
546             $this_self->{PendingCall} == 1) {
547             $this_self->send('CAL', $this_self->{Handle});
548             }
549             } else {
550             $self->{'Handle'} = $data[2];
551             $self->{'ScreenName'} = $self->normalize($data[3]);
552             &{$self->{Callback}->{on_connect}}
553             if ($self->if_callback_exists('on_connect'));
554             $this_self->send('CHG', 'NLN');
555             $this_self->send('SYN', '0');
556             }
557             } else {
558             die "Unsupported authentication method: \"",
559             join(" ", @data), "\"\n";
560             }
561             } elsif ($cmd eq 'XFR') {
562             if ($data[1] eq 'NS') {
563             $self->cycle_socket(split(/:/, $data[2]));
564             $self->send('VER', MSN_PROTOCOL);
565             } elsif ($data[1] eq 'SB') {
566             if ($self->if_request_type_exists($data[0], 'XFR') &&
567             exists $self->{Requests}->{$data[0]}->{Call} &&
568             $self->{Requests}->{$data[0]}->{Call} == 1 &&
569             exists $self->{Requests}->{$data[0]}->{Handle}) {
570             my ($h, undef) = split(/:/, $data[2]);
571             $self->_connect_SB($self->{Requests}->{$data[0]}->{Handle},
572             $h, undef, $data[4], 'USR', undef, 1);
573             $self->remove_request($data[0]);
574             } else {
575             $self->{_Log}("Huh? Recieved XFR SB request, ".
576             "but there are no pending calls!", 1);
577             }
578             }
579             } elsif ($cmd eq 'CHL') {
580             my ($TrID, $key) = @data;
581             my $md5 = md5_hex($key, 'Q1P7W2E4J9R8U3S5');
582             $this_self->sendraw('QRY', 'msmsgs@msnmsgr.com '. length($md5).
583             "\r\n". $md5);
584             } elsif ($cmd eq 'QRY') {
585             # we passed the challenge, lets send a ping
586             $this_self->sendnotrid('PNG');
587             } elsif ($cmd eq 'PNG') {
588             # our ping was recieved.
589            
590             } elsif ($cmd eq 'CHG') {
591             # FIXME: Sends a client state change to the server. Echos the
592             # success of the client's state change request.
593             #
594             # MSN is saying our CHG is OK
595             return;
596             } elsif ($cmd eq 'SYN') {
597             # FIXME: Initiates client-server property synchronization.
598             #
599             # MSN is saying our SYN is OK
600             return;
601             } elsif ($cmd eq 'JOI') {
602             my ($chandle, $friendly) = @data;
603             if ($self->if_callback_exists('on_join')) {
604             if ($self->if_session_exists($chandle)) {
605             &{$self->{Callback}->{on_join}}($this_self, $chandle, $friendly);
606             } else {
607             $self->{_Log}('#### WHY AM I HERE?! JOI W/OUT session ####', 1);
608             }
609             }
610             if (defined $this_self->{PendingMsgs} &&
611             $this_self->{PendingMsgs} == 1 && $self->have_pending_msgs($chandle)) {
612             while (my $message = shift @{$PendingMsgs{$chandle}}) {
613             $this_self->sendmsg($message);
614             }
615             $this_self->{PendingMsgs} = 0;
616             }
617             } elsif ($cmd eq 'BYE') {
618             my ($chandle) = @data;
619             $self->_disconnect_SB($this_self);
620            
621             if ($self->if_callback_exists('on_bye')) {
622             &{$self->{Callback}->{on_bye}}($chandle);
623             }
624             } elsif ($cmd eq 'CAL') {
625             if (defined $this_self->{PendingCall} &&
626             $this_self->{PendingCall} == 1) {
627             $this_self->{PendingCall} = 0;
628             }
629             } elsif ($cmd eq 'RNG') {
630             my ($sid, $addr, undef, $key, $chandle, $cname) = @data;
631             my ($h, undef) = split(/:/, $addr);
632             $self->_connect_SB($chandle, $h, '', $key, 'ANS', $sid);
633             } elsif ($cmd eq 'ANS') {
634             my ($response) = @data;
635            
636             $this_self->{Connected} = 1;
637            
638             if ($self->if_callback_exists('on_answer')) {
639             &{$self->{Callback}->{on_answer}}($this_self, @data);
640             }
641             } elsif ($cmd eq 'MSG') {
642             my ($chandle, $friendly, $length) = @data;
643             my ($msg, $response) = ();
644             $fh->read($msg, $length);
645             unless ($msg =~ m{Content-Type: text/x-msmsgscontrol}s) {
646             $msg = $self->normalize($self->stripheader($msg));
647             $friendly = $self->normalize($friendly);
648             if ($this_self->{_Type} eq 'SB') {
649             if ($self->if_session_exists($chandle)) {
650             if ($self->if_callback_exists('on_message')) {
651             &{$self->{Callback}->{on_message}}(
652             $this_self, $chandle, $friendly, $msg
653             );
654             }
655             } else {
656             $self->{_Log}('#### WHY AM I HERE?! MSG W/out session ####', 1);
657             }
658             }
659             } else {
660             #print STDERR "msg sent: ". $msg. "\n";
661             }
662             } elsif ($cmd eq 'LST') {
663             # FIXME : huh??
664             return unless ($data[1] eq 'FL');
665             $self->buddyadd($data[5], $data[6]);
666             } elsif ($cmd eq 'ILN') {
667             my (undef, $status, $username, $fname) = @data;
668             $self->buddyupdate($username, $fname, $status);
669             } elsif ($cmd eq 'NLN') {
670             my ($status, $username, $fname) = @data;
671             $self->buddyupdate($username, $fname, $status);
672             } elsif ($cmd eq 'FLN') {
673             my ($username) = @data;
674             $self->buddyupdate($username, undef, $cmd);
675             } elsif ($cmd =~ /^[0-9]+$/) {
676             if (defined $this_self->{PendingCall} &&
677             $this_self->{PendingCall} == 1) {
678             $self->_disconnect_SB($this_self);
679             }
680             $self->{_Log}('ERROR: '. $self->converterror($cmd), 1);
681             } elsif ($cmd eq 'ADD') {
682             my (undef, $type, undef, $chandle, $friendly) = @data;
683             if (defined $type && $type eq 'RL' && !$self->if_buddy_exists($chandle)) {
684             if ($self->if_callback_exists('auth_add')) {
685             if (&{$self->{Callback}->{auth_add}}($chandle, $friendly)) {
686             $self->buddyaddfl($chandle, $chandle);
687             $self->buddyaddal($chandle, $chandle);
688             }
689             } else {
690             $self->buddyaddfl($chandle, $chandle);
691             $self->buddyaddal($chandle, $chandle);
692             }
693             }
694             } elsif ($cmd eq 'REM') {
695             my (undef, $type, undef, $chandle, $friendly) = @data;
696             if (defined $type && $type eq 'RL') {
697             $self->{_Log}($chandle. ' has removed us from their contact list',
698             3);
699             } elsif (defined $type && $type eq 'FL') {
700             # removed user from our contact list, lets removethe buddy
701             $self->{_Log}('removing '. $chandle. ' from our contact list', 3);
702             $self->remove_buddy($chandle);
703             } elsif (defined $type && $type eq 'AL') {
704             # FIXME
705             }
706             } else {
707             $self->{_Log}('RECIEVED UNKNOWN: '. $cmd. ' '. @data, 2);
708             }
709            
710             return 1;
711             }
712            
713             sub converterror {
714             my ($self, $err) = @_;
715            
716             return (defined $errlist{$err}) ?
717             $err. ': '. $errlist{$err} : $err;
718             }
719            
720             sub normalize {
721             my ($self, $in) = @_;
722            
723             $in =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
724            
725             return $in;
726             }
727            
728             sub stripheader {
729             my ($self, $msg) = @_;
730            
731             $msg =~ s/\r//gs;
732             $msg =~ s/^.*?\n\n//s;
733            
734             return $msg;
735             }
736            
737             return 1;