File Coverage

blib/lib/Net/Hotline/Client.pm
Criterion Covered Total %
statement 48 595 8.0
branch 0 442 0.0
condition 0 64 0.0
subroutine 16 101 15.8
pod 71 76 93.4
total 135 1278 10.5


line stmt bran cond sub pod time code
1             package Net::Hotline::Client;
2              
3             ## Copyright(c) 1998-2002 by John C. Siracusa. All rights reserved. This
4             ## program is free software; you can redistribute it and/or modify it under
5             ## the same terms as Perl itself.
6              
7 1     1   6 use strict;
  1         2  
  1         37  
8              
9 1     1   4 use vars qw(@ISA $VERSION $DEBUG);
  1         2  
  1         53  
10              
11 1     1   9 use Carp;
  1         1  
  1         48  
12 1     1   913 use IO::File;
  1         14098  
  1         170  
13 1     1   1881 use IO::Socket;
  1         28295  
  1         4  
14 1     1   1155 use Net::Hotline::User;
  1         2  
  1         22  
15 1     1   452 use Net::Hotline::Task;
  1         2  
  1         22  
16 1     1   410 use Net::Hotline::PrivateChat;
  1         3  
  1         33  
17 1     1   622 use Net::Hotline::FileListItem;
  1         2  
  1         28  
18 1     1   516 use Net::Hotline::FileInfoItem;
  1         3  
  1         26  
19 1     1   540 use Net::Hotline::TrackerListItem;
  1         2  
  1         23  
20 1     1   542 use Net::Hotline::Protocol::Packet;
  1         2  
  1         30  
21 1     1   7 use Net::Hotline::Protocol::Header;
  1         2  
  1         19  
22 1     1   4 use Net::Hotline::Shared qw(:all);
  1         2  
  1         158  
23 1     1   5 use Net::Hotline::Constants qw(:all);
  1         2  
  1         749  
24              
25             if($^O eq 'MacOS') # "#ifdef", where have you gone...
26             {
27             require Mac::MoreFiles;
28             require Mac::Files;
29             }
30              
31 1     1   1060 use AutoLoader 'AUTOLOAD';
  1         1689  
  1         7  
32              
33             #
34             # Class attributes
35             #
36              
37             $VERSION = '0.83';
38             $DEBUG = 0;
39              
40             # CRC perl code lifted from Convert::BinHex by Eryq (eryq@enteract.com)
41             # An array useful for CRC calculations that use 0x1021 as the "seed":
42             my(@CRC_MAGIC) = (
43             0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
44             0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
45             0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
46             0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
47             0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
48             0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
49             0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
50             0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
51             0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
52             0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
53             0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
54             0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
55             0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
56             0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
57             0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
58             0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
59             0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
60             0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
61             0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
62             0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
63             0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
64             0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
65             0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
66             0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
67             0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
68             0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
69             0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
70             0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
71             0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
72             0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
73             0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
74             0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
75             );
76              
77             1;
78              
79             #
80             # Non-autoloaded object methods
81             #
82              
83             sub new
84             {
85 0     0 0   my($class) = shift;
86              
87 0 0         my($self) =
88             {
89             'NICK' => undef,
90             'LOGIN' => undef,
91             'COLOR' => undef,
92             'SERVER_PORT' => undef,
93             'SERVER_ADDR' => undef,
94             'TRACKER_ADDR' => undef,
95              
96             'SOCKET' => undef,
97             'BLOCKING' => 1,
98             'SERVER' => undef,
99             'SEQNUM' => 1,
100              
101             'USER_LIST' => undef,
102             'NEWS' => undef,
103             'FILES' => undef,
104             'AGREEMENT' => undef,
105             'PCHATS' => undef,
106             'TASKS' => undef,
107              
108             'FILE_INFO' => undef,
109              
110             'HANDLERS' =>
111             {
112             'AGREEMENT' => undef,
113             'BAN' => undef,
114             'CHAT' => undef,
115             'CHAT_ACTION' => undef,
116             'COLOR' => undef,
117             'EVENT' => undef,
118             'FILE_DELETE' => undef,
119             'FILE_GET' => undef,
120             'FILE_GET_INFO' => undef,
121             'FILE_LIST' => undef,
122             'FILE_MKDIR' => undef,
123             'FILE_MOVE' => undef,
124             'FILE_SET_INFO' => undef,
125             'ICON' => undef,
126             'JOIN' => undef,
127             'KICK' => undef,
128             'LEAVE' => undef,
129             'LOGIN' => undef,
130             'MSG' => undef,
131             'NEWS' => undef,
132             'NEWS_POST' => undef,
133             'NEWS_POSTED' => undef,
134             'NICK' => undef,
135             'PCHAT_ACCEPT' => undef,
136             'PCHAT_CREATE' => undef,
137             'PCHAT_INVITE' => undef,
138             'PCHAT_JOIN' => undef,
139             'PCHAT_LEAVE' => undef,
140             'PCHAT_SUBJECT' => undef,
141             'QUIT' => undef,
142             'SEND_MSG' => undef,
143             'SERVER_MSG' => undef,
144             'TASK_ERROR' => undef,
145             'USER_GETINFO' => undef,
146             'USER_LIST' => undef
147             },
148              
149             'BLOCKING_TASKS' => undef,
150             'DEFAULT_HANDLERS' => undef,
151             'HANDLERS_WHEN_BLOCKING' => undef,
152              
153             'LOGGED_IN' => undef,
154              
155             'EVENT_TIMING' => 1,
156             'CONNECT_TIMEOUT' => 15,
157             'PATH_SEPARATOR' => HTLC_PATH_SEPARATOR,
158             'HTXF_BUFSIZE' => HTXF_BUFSIZE,
159              
160             'DOWNLOADS_DIR' => undef,
161             'DATA_FORK_EXT' => '.data',
162             'RSRC_FORK_EXT' => '.rsrc',
163              
164             'LAST_ACTIVITY' => time(),
165             'LAST_ERROR' => undef,
166             'MACOS' => ($^O eq 'MacOS') ? 1 : 0
167             };
168              
169 0           bless $self, $class;
170 0           return $self;
171             }
172              
173 0     0 1   sub agreement { $_[0]->{'AGREEMENT'} }
174              
175             sub blocking
176             {
177 0     0 1   my($self, $blocking) = @_;
178              
179 0 0         return $self->{'BLOCKING'} unless(@_ == 2);
180              
181 0 0 0       if(ref($self->{'SERVER'}) && $self->{'SERVER'}->opened())
182             {
183 0           _set_blocking($self->{'SERVER'}, $blocking);
184             }
185              
186 0 0         $self->{'BLOCKING'} = (($blocking) ? 1 : 0);
187 0           return $self->{'BLOCKING'};
188             }
189              
190             sub blocking_tasks
191             {
192 0     0 1   my($self, $arg) = @_;
193 0 0         $self->{'BLOCKING_TASKS'} = ($arg) ? 1 : 0 if(@_ == 2);
    0          
194 0           return $self->{'BLOCKING_TASKS'};
195             }
196              
197             sub connect_timeout
198             {
199 0     0 1   my($self, $secs) = @_;
200 0 0         $self->{'CONNECT_TIMEOUT'} = $secs if($secs =~ /^\d+$/);
201 0           return $self->{'CONNECT_TIMEOUT'};
202             }
203              
204             sub default_handlers
205             {
206 0     0 1   my($self, $arg) = @_;
207 0 0         $self->{'DEFAULT_HANDLERS'} = ($arg) ? 1 : 0 if(@_ == 2);
    0          
208 0           return $self->{'DEFAULT_HANDLERS'};
209             }
210              
211             sub downloads_dir
212             {
213 0     0 1   my($self, $dir) = @_;
214 0 0         $self->{'DOWNLOADS_DIR'} = $dir if(-d $dir);
215 0           return $self->{'DOWNLOADS_DIR'};
216             }
217              
218             sub data_fork_extension
219             {
220 0     0 1   my($self, $ext) = @_;
221 0 0         croak("The data fork extension may not be the same as the resource fork extension!")
222             if($ext eq $self->{'DATA_FORK_EXT'});
223 0 0         $self->{'DATA_FORK_EXT'} = $ext if(defined($ext));
224 0           return $self->{'DATA_FORK_EXT'};
225             }
226              
227             sub event_timing
228             {
229 0     0 1   my($self, $secs) = @_;
230              
231 0 0         if(defined($secs))
232             {
233 0 0         croak qw(Bad argument to event_timing() - "$secs") if($secs =~ /[^0-9.]/);
234 0           $self->{'EVENT_TIMING'} = $secs;
235             }
236              
237 0           return $self->{'EVENT_TIMING'};
238             }
239              
240 0     0 1   sub files { $_[0]->{'FILES'} }
241 0     0 1   sub handlers { $_[0]->{'HANDLERS'} }
242              
243             sub handlers_during_blocking_tasks
244             {
245 0     0 1   my($self, $arg) = @_;
246 0 0         $self->{'HANDLERS_WHEN_BLOCKING'} = ($arg) ? 1 : 0 if(@_ == 2);
    0          
247 0           return $self->{'HANDLERS_WHEN_BLOCKING'};
248             }
249              
250 0     0 1   sub last_error { $_[0]->{'LAST_ERROR'} }
251 0     0 1   sub clear_error { $_[0]->{'LAST_ERROR'} = undef }
252              
253             sub xfer_bufsize
254             {
255 0     0 1   my($self, $size) = @_;
256 0 0         $self->{'HTXF_BUFSIZE'} = $size if($size =~ /^\d+$/);
257 0           return $self->{'HTXF_BUFSIZE'};
258             }
259              
260             sub last_activity
261             {
262 0     0 1   my($self) = shift;
263 0           return $self->{'LAST_ACTIVITY'};
264             }
265              
266 0     0 1   sub news { $_[0]->{'NEWS'} }
267              
268             sub path_separator
269             {
270 0     0 1   my($self, $separator) = @_;
271 0 0         $self->{'PATH_SEPARATOR'} = $separator if($separator =~ /^.$/);
272 0           return $self->{'PATH_SEPARATOR'};
273             }
274              
275             sub rsrc_fork_extension
276             {
277 0     0 1   my($self, $ext) = @_;
278 0 0         croak("The resource fork extension may not be the same as the data fork extension!")
279             if($ext eq $self->{'RSRC_FORK_EXT'});
280 0 0         $self->{'RSRC_FORK_EXT'} = $ext if(defined($ext));
281 0           return $self->{'RSRC_FORK_EXT'};
282             }
283              
284 0     0 1   sub pchats { $_[0]->{'PCHATS'} }
285 0     0 1   sub userlist { $_[0]->{'USER_LIST'} }
286              
287             sub server
288             {
289 0 0   0 1   $_[0]->{'SERVER_ADDR'} .
290             ($_[0]->{'SERVER_PORT'} ne HTLS_TCPPORT) ?
291             ":$_[0]->{'SERVER_PORT'}" : '';
292             }
293              
294             sub connect
295             {
296 0     0 1   my($self, $server) = @_;
297              
298 0           my($address, $port);
299              
300 0 0         if(($address = $server) =~ s/^([^ :]+)(?:[: ](\d+))?$/$1/)
301             {
302 0   0       $port = $2 || HTLS_TCPPORT;
303             }
304             else
305             {
306 0           croak("Bad server address: $server");
307             }
308              
309             eval
310 0           {
311 0     0     $SIG{'ALRM'} = sub { die "timeout" };
  0            
312 0           alarm($self->{'CONNECT_TIMEOUT'});
313              
314 0           $self->{'SERVER'} =
315             IO::Socket::INET->new(PeerAddr =>$address,
316             PeerPort =>$port,
317             Proto =>'tcp');
318              
319 0           alarm(0);
320 0           $SIG{'ALRM'} = 'DEFAULT';
321             };
322              
323 0 0         if($@ =~ /timeout/)
324             {
325 0           $self->{'LAST_ERROR'} = "Timed out after $self->{'CONNECT_TIMEOUT'} seconds";
326 0           return;
327             }
328              
329 0 0 0       if(!$self->{'SERVER'} || $@)
330             {
331 0   0       $self->{'LAST_ERROR'} = $@ || $! || 'Connection failed';
332 0           return;
333             }
334              
335 0           $self->{'SERVER'}->autoflush(1);
336              
337 0           $self->{'SERVER_ADDR'} = $address;
338 0           $self->{'SERVER_PORT'} = $port;
339              
340 0           return(1);
341             }
342              
343             sub disconnect
344             {
345 0     0 1   my($self) = shift;
346              
347 0 0 0       if(ref($self->{'SERVER'}) && $self->{'SERVER'}->opened())
348             {
349 0           $self->{'SERVER'}->close();
350 0           $self->{'LOGGED_IN'} = undef;
351 0           $self->{'SERVER_ADDR'} = undef;
352 0           return(1);
353             }
354              
355 0           $self->{'LAST_ERROR'} = 'Not connected.';
356 0           return;
357             }
358              
359             sub login
360             {
361 0     0 1   my($self, %args) = @_;
362              
363 0 0         if($self->{'BLOCKING_TASKS'})
364             {
365 0           return $self->_login_now(%args);
366             }
367             else
368             {
369 0           return $self->_login(%args);
370             }
371             }
372              
373             sub _login_now
374             {
375 0     0     my($self, %args) = @_;
376              
377 0           my($no_news, $no_userlist, $task_num, $task, $packet);
378              
379 0           $no_news = $args{'NoNews'};
380 0           $no_userlist = $args{'NoUserList'};
381              
382 0           $args{'NoNews'} = $args{'NoUserList'} = undef;
383              
384 0           $task_num = $self->_login(%args);
385 0           $task = $self->{'TASKS'}->{$task_num};
386              
387 0 0         return unless($task_num);
388              
389 0           $packet = _blocking_task($self, $task_num);
390              
391 0 0         if($task->error())
392             {
393 0           $self->{'LAST_ERROR'} = $task->error_text();
394 0           $self->disconnect();
395 0           return;
396             }
397              
398 0 0         unless($no_news)
399             {
400 0 0         unless($self->get_news())
401             {
402 0           $self->{'LAST_ERROR'} = "Login succeeded, but could not get news.";
403 0           return("0E-0");
404             }
405             }
406              
407 0 0         unless($no_userlist)
408             {
409 0 0         unless($self->get_userlist())
410             {
411 0           $self->{'LAST_ERROR'} = "Login succeeded, but could not get userlist";
412 0           return("0E-0");
413             }
414             }
415              
416 0           return(1);
417             }
418              
419             sub _login
420             {
421 0     0     my($self, %args) = @_;
422              
423 0           my($nick, $login, $password, $icon, $enc_login, $enc_password,
424             $proto_header, $data, $response, $task_num, $server);
425              
426 0 0         $server = $self->{'SERVER'} or croak "Not connected to a server";
427              
428 0 0         unless($server->opened())
429             {
430 0           $self->{'LAST_ERROR'} = "login() called before connect()";
431 0           return;
432             }
433              
434 0   0       $nick = $args{'Nickname'} || HTLC_DEFAULT_NICK;
435 0   0       $login = $args{'Login'} || HTLC_DEFAULT_LOGIN;
436 0   0       $icon = $args{'Icon'} || HTLC_DEFAULT_ICON;
437 0           $password = $args{'Password'};
438              
439 0           $self->{'NICK'} = $nick;
440 0           $self->{'LOGIN'} = $login;
441 0           $self->{'ICON'} = $icon;
442              
443 0 0         _hlc_write($self, $server, \HTLC_MAGIC, HTLC_MAGIC_LEN) || return;
444 0 0         _hlc_read($self, $server, \$response, HTLS_MAGIC_LEN) || return;
445              
446 0 0         if($response ne HTLS_MAGIC)
447             {
448 0           $self->{'LAST_ERROR'} = "Handshake failed. Not a hotline server?";
449 0           $self->disconnect();
450 0           return;
451             }
452              
453 0           $enc_login = _encode($login);
454 0           $enc_password = _encode($password);
455              
456 0           $proto_header = new Net::Hotline::Protocol::Header;
457              
458 0           $proto_header->type(HTLC_HDR_LOGIN);
459 0           $proto_header->seq($self->_next_seqnum());
460 0           $proto_header->task(0x00000000);
461 0           $proto_header->len(SIZEOF_HL_PROTO_HDR +
462             length($enc_login) +
463             length($enc_password) +
464             length($nick));
465 0           $proto_header->len2($proto_header->len);
466              
467 0           my($fmt) = 'nnna*nna*nna*nnn';
468              
469 0           $data = $proto_header->header() .
470             pack($fmt, 0x0004, # Num atoms
471              
472             HTLC_DATA_LOGIN, # Atom type
473             length($enc_login), # Atom length
474             $enc_login, # Atom data
475              
476             HTLC_DATA_PASSWORD, # Atom type
477             length($enc_password), # Atom length
478             $enc_password, # Atom data
479              
480             HTLC_DATA_NICKNAME, # Atom type
481             length($nick), # Atom length
482             $nick, # Atom data
483              
484             HTLC_DATA_ICON, # Atom type
485             0x0002, # Atom length
486             $icon); # Atom data
487              
488 0           _debug(_hexdump($data));
489              
490 0           $task_num = $proto_header->seq();
491              
492 0 0         if(_hlc_write($self, $server, \$data, length($data)))
493             {
494 0           _debug("NEW TASK: LOGIN - $task_num\n");
495 0           $self->{'TASKS'}->{$task_num} =
496             new Net::Hotline::Task($task_num, HTLC_TASK_LOGIN, time());
497             }
498 0           else { return }
499              
500 0 0         unless($args{'NoUserList'})
501             {
502 0           $self->req_userlist();
503             }
504              
505 0 0         unless($args{'NoNews'})
506             {
507 0           $self->req_news();
508             }
509              
510 0           _set_blocking($server, $self->{'BLOCKING'});
511              
512 0           return($task_num);
513             }
514              
515             sub run
516             {
517 0     0 1   my($self) = shift;
518              
519 0 0         my($server) = $self->{'SERVER'} or croak "Not connected to a server";
520 0 0         return unless($server->opened());
521              
522 0           my($ret, $packet);
523              
524 0           $packet = new Net::Hotline::Protocol::Packet;
525              
526 0           while($ret = $packet->read_parse($server, $self->{'BLOCKING'}))
527             {
528 0 0         _process_packet($self, $packet, $ret) || return(1);
529             }
530              
531 0           return(1);
532             }
533              
534             sub _process_packet
535             {
536 0     0     my($self, $packet, $ret, $blocking_task) = @_;
537              
538 0           my($data_ref, $type, $use_handlers);
539              
540 0   0       $use_handlers = !($blocking_task && !$self->{'HANDLERS_WHEN_BLOCKING'});
541              
542 0           $type = $packet->{'TYPE'};
543              
544 0 0         if($ret == HTLC_EWOULDBLOCK) # Idle event
545             {
546 0 0         if(defined($self->{'HANDLERS'}->{'EVENT'}))
547             {
548 0           &{$self->{'HANDLERS'}->{'EVENT'}}($self, 1);
  0            
549             }
550              
551 0           select(undef, undef, undef, $self->{'EVENT_TIMING'});
552 0           return(1);
553             }
554              
555 0           $self->{'LAST_ACTIVITY'} = time();
556              
557 0 0         if(defined($self->{'HANDLERS'}->{'EVENT'})) # Non-idle event
558             {
559 0           &{$self->{'HANDLERS'}->{'EVENT'}}($self, 0);
  0            
560             }
561              
562 0           _debug("Packet type = $type\n");
563              
564 0 0 0       if($type == HTLS_HDR_USER_LEAVE)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
565             {
566             # Hotline server *BUG* - you may get a "disconnect" packet for a
567             # socket _before_ you get the "connect" packet for that socket!
568             # In fact, the "connect" packet will never arrive in this case.
569              
570 0 0 0       if(defined($packet->{'SOCKET'}) &&
571             defined($self->{'USER_LIST'}->{$packet->{'SOCKET'}}))
572             {
573 0           my($user) = $self->{'USER_LIST'}->{$packet->{'SOCKET'}};
574              
575 0           delete $self->{'USER_LIST'}->{$packet->{'SOCKET'}};
576              
577 0 0         if($use_handlers)
578             {
579 0 0         if(defined($self->{'HANDLERS'}->{'LEAVE'}))
    0          
580             {
581 0           &{$self->{'HANDLERS'}->{'LEAVE'}}($self, $user);
  0            
582             }
583             elsif($self->{'DEFAULT_HANDLERS'})
584             {
585 0           print "USER LEFT: ", $user->nick(), "\n";
586             }
587             }
588             }
589             }
590             elsif($type == HTLS_HDR_TASK)
591             {
592 0           my($task) = $self->{'TASKS'}->{$packet->{'TASK_NUM'}};
593              
594 0           my($task_type) = $task->type();
595              
596 0           $task->finish(time());
597              
598 0 0         if(defined($packet->{'TASK_ERROR'}))
599             {
600 0           $task->error(1);
601 0           $task->error_text($packet->{'TASK_ERROR'});
602              
603 0 0         if($use_handlers)
604             {
605 0 0         if(defined($self->{'HANDLERS'}->{'TASK_ERROR'}))
606             {
607 0           &{$self->{'HANDLERS'}->{'TASK_ERROR'}}($self, $task);
  0            
608             }
609             else
610             {
611 0           print "TASK ERROR(", $task->num(), ':', $task->type(), ") ",
612             $task->error_text(), "\n";
613             }
614             }
615             }
616             else
617             {
618 0           $task->error(0);
619              
620 0 0 0       if($task_type == HTLC_TASK_USER_LIST && defined($packet->{'USER_LIST'}))
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
621             {
622 0           $self->{'USER_LIST'} = $packet->{'USER_LIST'};
623              
624 0 0         if($use_handlers)
625             {
626 0 0         if(defined($self->{'HANDLERS'}->{'USER_LIST'}))
    0          
627             {
628 0           &{$self->{'HANDLERS'}->{'USER_LIST'}}($self, $task);
  0            
629             }
630             elsif($self->{'DEFAULT_HANDLERS'})
631             {
632 0           print "GET USER LIST: Task complete.\n";
633             }
634             }
635             }
636             elsif($task_type == HTLC_TASK_FILE_LIST)
637             {
638 0           my($path);
639              
640 0 0         $task->path("") unless(length($task->path()));
641 0           $path = $task->path();
642              
643 0 0         if($packet->{'FILE_LIST'})
644             {
645 0           $self->{'FILES'}->{$path} = $packet->{'FILE_LIST'};
646             }
647             else
648             {
649 0           $self->{'FILES'}->{$path} = [];
650             }
651              
652 0 0         if($use_handlers)
653             {
654 0 0         if(defined($self->{'HANDLERS'}->{'FILE_LIST'}))
    0          
655             {
656 0           &{$self->{'HANDLERS'}->{'FILE_LIST'}}($self, $task);
  0            
657             }
658             elsif($self->{'DEFAULT_HANDLERS'})
659             {
660 0           print "GET FILE LIST: Task complete.\n";
661             }
662             }
663             }
664             elsif($task_type == HTLC_TASK_NEWS && defined($packet->{'DATA'}))
665             {
666 0           my(@news) = split(/_{58}/, $packet->{'DATA'});
667              
668 0           $self->{'NEWS'} = \@news;
669              
670 0 0         if($use_handlers)
671             {
672 0 0         if(defined($self->{'HANDLERS'}->{'NEWS'}))
    0          
673             {
674 0           &{$self->{'HANDLERS'}->{'NEWS'}}($self, $task);
  0            
675             }
676             elsif($self->{'DEFAULT_HANDLERS'})
677             {
678 0           print "GET NEWS: Task complete.\n";
679             }
680             }
681             }
682             elsif($task_type == HTLC_TASK_USER_INFO && defined($packet->{'DATA'}))
683             {
684 0           my($user) = $self->{'USER_LIST'}->{$task->socket()};
685              
686 0           $user->info($packet->{'DATA'});
687              
688 0 0         if($use_handlers)
689             {
690 0 0         if(defined($self->{'HANDLERS'}->{'USER_GETINFO'}))
    0          
691             {
692 0           &{$self->{'HANDLERS'}->{'USER_GETINFO'}}($self, $task);
  0            
693             }
694             elsif($self->{'DEFAULT_HANDLERS'})
695             {
696 0           print "GET USER INFO: Task complete.\n";
697             }
698             }
699              
700 0           _debug("USER_GETINFO for: $packet->{'NICK'} (", $task->socket(), ")\n",
701             $packet->{'DATA'}, "\n");
702             }
703             elsif($task_type == HTLC_TASK_FILE_INFO)
704             {
705 0           my($path, $file_info);
706              
707 0 0         $task->path("") unless(length($task->path));
708 0           $path = $task->path();
709              
710 0           $file_info = $self->{'FILE_INFO'} = new Net::Hotline::FileInfoItem();
711              
712 0           $file_info->icon($packet->{'FILE_ICON'});
713 0           $file_info->type($packet->{'FILE_TYPE'});
714 0           $file_info->creator($packet->{'FILE_CREATOR'});
715 0           $file_info->size($packet->{'FILE_SIZE'});
716 0           $file_info->name($packet->{'FILE_NAME'});
717 0           $file_info->comment($packet->{'FILE_COMMENT'});
718 0           $file_info->ctime($packet->{'FILE_CTIME'});
719 0           $file_info->mtime($packet->{'FILE_MTIME'});
720              
721 0 0         if($use_handlers)
722             {
723 0 0         if(defined($self->{'HANDLERS'}->{'FILE_GET_INFO'}))
    0          
724             {
725 0           &{$self->{'HANDLERS'}->{'FILE_GET_INFO'}}($self, $task, $file_info);
  0            
726             }
727             elsif($self->{'DEFAULT_HANDLERS'})
728             {
729 0           print "FILE_GET_INFO: Task complete.\n";
730             }
731             }
732             }
733             elsif($task_type == HTLC_TASK_LOGIN)
734             {
735 0           $self->{'LOGGED_IN'} = 1;
736              
737 0 0         if($use_handlers)
738             {
739 0 0         if(defined($self->{'HANDLERS'}->{'LOGIN'}))
    0          
740             {
741 0           &{$self->{'HANDLERS'}->{'LOGIN'}}($self);
  0            
742             }
743             elsif($self->{'DEFAULT_HANDLERS'})
744             {
745 0           print "LOGIN: Task complete.\n";
746             }
747             }
748             }
749             elsif($task_type == HTLC_TASK_NEWS_POST)
750             {
751 0 0         if($use_handlers)
752             {
753 0 0         if(defined($self->{'HANDLERS'}->{'NEWS_POST'}))
    0          
754             {
755 0           &{$self->{'HANDLERS'}->{'NEWS_POST'}}($self, $task);
  0            
756             }
757             elsif($self->{'DEFAULT_HANDLERS'})
758             {
759 0           print "POST NEWS: Task complete.\n";
760             }
761             }
762             }
763             elsif($task_type == HTLC_TASK_SEND_MSG)
764             {
765 0 0         if($use_handlers)
766             {
767 0 0         if(defined($self->{'HANDLERS'}->{'SEND_MSG'}))
    0          
768             {
769 0           &{$self->{'HANDLERS'}->{'SEND_MSG'}}($self, $task);
  0            
770             }
771             elsif($self->{'DEFAULT_HANDLERS'})
772             {
773 0           print "SEND MSG: Task complete.\n";
774             }
775             }
776             }
777             elsif($task_type == HTLC_TASK_KICK)
778             {
779 0 0         if($use_handlers)
780             {
781 0 0         if(defined($self->{'HANDLERS'}->{'KICK'}))
    0          
782             {
783 0           &{$self->{'HANDLERS'}->{'KICK'}}($self, $task);
  0            
784             }
785             elsif($self->{'DEFAULT_HANDLERS'})
786             {
787 0           print "KICK: Task complete.\n";
788             }
789             }
790             }
791             elsif($task_type == HTLC_TASK_BAN)
792             {
793 0 0         if($use_handlers)
794             {
795 0 0         if(defined($self->{'HANDLERS'}->{'BAN'}))
    0          
796             {
797 0           &{$self->{'HANDLERS'}->{'BAN'}}($self, $task);
  0            
798             }
799             elsif($self->{'DEFAULT_HANDLERS'})
800             {
801 0           print "BAN: Task complete.\n";
802             }
803             }
804             }
805             elsif($task_type == HTLC_TASK_SET_INFO)
806             {
807 0 0         if($use_handlers)
808             {
809 0 0         if(defined($self->{'HANDLERS'}->{'FILE_SET_INFO'}))
    0          
810             {
811 0           &{$self->{'HANDLERS'}->{'FILE_SET_INFO'}}($self, $task);
  0            
812             }
813             elsif($self->{'DEFAULT_HANDLERS'})
814             {
815 0           print "SET INFO: Task complete.\n";
816             }
817             }
818             }
819             elsif($task_type == HTLC_TASK_FILE_DELETE)
820             {
821 0 0         if($use_handlers)
822             {
823 0 0         if(defined($self->{'HANDLERS'}->{'FILE_DELETE'}))
    0          
824             {
825 0           &{$self->{'HANDLERS'}->{'FILE_DELETE'}}($self, $task);
  0            
826             }
827             elsif($self->{'DEFAULT_HANDLERS'})
828             {
829 0           print "DELETE FILE: Task complete.\n";
830             }
831             }
832             }
833             elsif($task_type == HTLC_TASK_FILE_MKDIR)
834             {
835 0 0         if($use_handlers)
836             {
837 0 0         if(defined($self->{'HANDLERS'}->{'FILE_MKDIR'}))
    0          
838             {
839 0           &{$self->{'HANDLERS'}->{'FILE_MKDIR'}}($self, $task);
  0            
840             }
841             elsif($self->{'DEFAULT_HANDLERS'})
842             {
843 0           print "CREATE FOLDER: Task complete.\n";
844             }
845             }
846             }
847             elsif($task_type == HTLC_TASK_FILE_MOVE)
848             {
849 0 0         if($use_handlers)
850             {
851 0 0         if(defined($self->{'HANDLERS'}->{'FILE_MOVE'}))
    0          
852             {
853 0           &{$self->{'HANDLERS'}->{'FILE_MOVE'}}($self, $task);
  0            
854             }
855             elsif($self->{'DEFAULT_HANDLERS'})
856             {
857 0           print "MOVE FILE: Task complete.\n";
858             }
859             }
860             }
861             elsif($task_type == HTLC_TASK_FILE_GET)
862             {
863 0           my($size) = $packet->{'HTXF_SIZE'};
864 0           my($ref) = $packet->{'HTXF_REF'};
865              
866 0 0         if($use_handlers)
867             {
868 0 0         if(defined($self->{'HANDLERS'}->{'FILE_GET'}))
869             {
870 0           &{$self->{'HANDLERS'}->{'FILE_GET'}}($self, $task, $ref, $size);
  0            
871             }
872             else
873             {
874 0 0         print "GET FILE: Starting download (ref = $ref, size = $size)\n"
875             if($self->{'DEFAULT_HANDLERS'});
876              
877 0           $self->recv_file($task, $ref, $size);
878             }
879             }
880             }
881             elsif($task_type == HTLC_TASK_FILE_PUT)
882             {
883 0           my($ref) = $packet->{'HTXF_REF'};
884 0           my($resume) = $packet->{'HTXF_RFLT'};
885 0           my($size) = ${$task->misc()}[0] + ${$task->misc()}[1];
  0            
  0            
886              
887 0 0         if($use_handlers)
888             {
889 0 0         if(defined($self->{'HANDLERS'}->{'FILE_PUT'}))
890             {
891 0           &{$self->{'HANDLERS'}->{'FILE_PUT'}}($self, $task, $ref, $size, $resume);
  0            
892             }
893             else
894             {
895 0 0         print "GET PUT: Starting upload (ref = $ref)\n"
896             if($self->{'DEFAULT_HANDLERS'});
897              
898 0           $self->send_file($task, $ref, $size, $resume);
899             }
900             }
901             }
902             elsif($task_type == HTLC_TASK_PCHAT_CREATE)
903             {
904 0           my($ref) = $packet->{'PCHAT_REF'};
905 0           my($user) = $self->{'USER_LIST'}->{$packet->{'SOCKET'}};
906 0           my($pchat) = $self->{'PCHATS'}->{$ref} = new Net::Hotline::PrivateChat;
907              
908 0           $pchat->reference($ref);
909 0           $pchat->userlist({ $packet->{'SOCKET'} => $user });
910              
911 0 0         if($use_handlers)
912             {
913 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_CREATE'}))
    0          
914             {
915 0           &{$self->{'HANDLERS'}->{'PCHAT_CREATE'}}($self, $task, $pchat);
  0            
916             }
917             elsif($self->{'DEFAULT_HANDLERS'})
918             {
919 0           print "CREATE PCHAT($ref): Task complete.\n";
920             }
921             }
922             }
923             elsif($task_type == HTLC_TASK_PCHAT_ACCEPT)
924             {
925 0           my($ref) = $task->misc();
926              
927 0           my($userlist);
928            
929             # Create userlist of references to the main userlist rather
930             # than new user objects (as returned in the packet)
931 0           foreach my $socket (keys(%{$packet->{'USER_LIST'}}))
  0            
932             {
933 0           $userlist->{$socket} = $self->{'USER_LIST'}->{$socket};
934             }
935              
936 0           my($pchat) = $self->{'PCHATS'}->{$ref} =
937             new Net::Hotline::PrivateChat($ref, $userlist);
938            
939 0 0         if($use_handlers)
940             {
941 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_ACCEPT'}))
    0          
942             {
943 0           &{$self->{'HANDLERS'}->{'PCHAT_ACCEPT'}}($self, $task, $pchat);
  0            
944             }
945             elsif($self->{'DEFAULT_HANDLERS'})
946             {
947 0           print "ACCEPT PCHAT INVITE($ref): Task complete.\n";
948             }
949             }
950             }
951             }
952             # Reclaim memory
953 0           delete $self->{'TASKS'}->{$packet->{'TASK_NUM'}};
954             }
955             elsif($type == HTLS_HDR_AGREEMENT)
956             {
957 0           $self->{'AGREEMENT'} = $packet->{'DATA'};
958              
959 0 0         if(defined($packet->{'DATA'}))
960             {
961 0 0         if($use_handlers)
962             {
963 0 0         if(defined($self->{'HANDLERS'}->{'AGREEMENT'}))
    0          
964             {
965 0           &{$self->{'HANDLERS'}->{'AGREEMENT'}}($self, \$packet->{'DATA'});
  0            
966             }
967             elsif($self->{'DEFAULT_HANDLERS'})
968             {
969 0           print "AGREEMENT:\n", $packet->{'DATA'}, "\n";
970             }
971             }
972             }
973             }
974             elsif($type == HTLS_HDR_MSG)
975             {
976 0           my($user) = $self->{'USER_LIST'}->{$packet->{'SOCKET'}};
977              
978             # User-to-user message
979 0 0 0       if(defined($user) && defined($packet->{'DATA'}))
    0          
980             {
981 0 0         if($use_handlers)
982             {
983 0 0         if(defined($self->{'HANDLERS'}->{'MSG'}))
    0          
984             {
985 0           &{$self->{'HANDLERS'}->{'MSG'}}($self, $user, \$packet->{'DATA'}, \$packet->{'REPLY_TO'});
  0            
986             }
987             elsif($self->{'DEFAULT_HANDLERS'})
988             {
989 0           print "MSG: ", $user->nick(), "(",
990             $packet->{'SOCKET'}, ") ",
991             $packet->{'DATA'};
992              
993 0 0         if($packet->{'IS_REPLY'})
994             {
995 0           print " (In reply to: $packet->{'REPLY_TO'}])";
996             }
997              
998 0           print "\n";
999             }
1000             }
1001             }
1002             elsif(defined($packet->{'DATA'})) # Server message
1003             {
1004 0 0         if($use_handlers)
1005             {
1006 0 0         if(defined($self->{'HANDLERS'}->{'SERVER_MSG'}))
    0          
1007             {
1008 0           &{$self->{'HANDLERS'}->{'SERVER_MSG'}}($self, \$packet->{'DATA'});
  0            
1009             }
1010             elsif($self->{'DEFAULT_HANDLERS'})
1011             {
1012 0           print "SERVER MSG: ", $packet->{'DATA'}, "\n";
1013             }
1014             }
1015             }
1016             }
1017             elsif($type == HTLS_HDR_USER_CHANGE)
1018             {
1019 0 0 0       if(defined($packet->{'NICK'}) && defined($packet->{'SOCKET'}) &&
      0        
      0        
1020             defined($packet->{'ICON'}) && defined($packet->{'COLOR'}))
1021             {
1022 0 0         if(defined($self->{'USER_LIST'}->{$packet->{'SOCKET'}}))
1023             {
1024 0           my($user) = $self->{'USER_LIST'}->{$packet->{'SOCKET'}};
1025              
1026 0 0         if($user->nick() ne $packet->{'NICK'})
    0          
    0          
1027             {
1028 0           my($old_nick) = $user->nick();
1029              
1030 0           $user->nick($packet->{'NICK'});
1031              
1032 0 0         if($use_handlers)
1033             {
1034 0 0         if(defined($self->{'HANDLERS'}->{'NICK'}))
    0          
1035             {
1036 0           &{$self->{'HANDLERS'}->{'NICK'}}($self, $user, $old_nick, $user->nick());
  0            
1037             }
1038             elsif($self->{'DEFAULT_HANDLERS'})
1039             {
1040 0           print "USER CHANGE: $old_nick is now known as ", $user->nick(), "\n";
1041             }
1042             }
1043             }
1044             elsif($user->icon() ne $packet->{'ICON'})
1045             {
1046 0           my($old_icon) = $user->icon();
1047              
1048 0           $user->icon($packet->{'ICON'});
1049              
1050 0 0         if($use_handlers)
1051             {
1052 0 0         if(defined($self->{'HANDLERS'}->{'ICON'}))
    0          
1053             {
1054 0           &{$self->{'HANDLERS'}->{'ICON'}}($self, $user, $old_icon, $user->icon());
  0            
1055             }
1056             elsif($self->{'DEFAULT_HANDLERS'})
1057             {
1058 0           print "USER CHANGE: ", $user->nick(),
1059             " icon changed from $old_icon to ",
1060             $user->icon(), "\n";
1061             }
1062             }
1063             }
1064             elsif($user->color() ne $packet->{'COLOR'})
1065             {
1066 0           my($old_color) = $user->color();
1067              
1068 0           $user->color($packet->{'COLOR'});
1069              
1070 0 0         if($use_handlers)
1071             {
1072 0 0         if(defined($self->{'HANDLERS'}->{'COLOR'}))
    0          
1073             {
1074 0           &{$self->{'HANDLERS'}->{'COLOR'}}($self, $user, $old_color, $user->color());
  0            
1075             }
1076             elsif($self->{'DEFAULT_HANDLERS'})
1077             {
1078 0           print "USER CHANGE: ", $user->nick(),
1079             " color changed from $old_color to ",
1080             $user->color(), "\n";
1081             }
1082             }
1083             }
1084             }
1085             else
1086             {
1087 0           $self->{'USER_LIST'}->{$packet->{'SOCKET'}} =
1088             new Net::Hotline::User($packet->{'SOCKET'},
1089             $packet->{'NICK'},
1090             undef,
1091             $packet->{'ICON'},
1092             $packet->{'COLOR'});
1093              
1094 0 0         if($use_handlers)
1095             {
1096 0 0         if(defined($self->{'HANDLERS'}->{'JOIN'}))
    0          
1097             {
1098 0           &{$self->{'HANDLERS'}->{'JOIN'}}($self, $self->{'USER_LIST'}->{$packet->{'SOCKET'}});
  0            
1099             }
1100             elsif($self->{'DEFAULT_HANDLERS'})
1101             {
1102 0           print "JOINED:\n",
1103             " Nick: $packet->{'NICK'}\n",
1104             " Icon: $packet->{'ICON'}\n",
1105             "Socket: $packet->{'SOCKET'}\n",
1106             " Color: $packet->{'COLOR'}\n";
1107             }
1108             }
1109             }
1110             }
1111             }
1112             elsif($type == HTLS_HDR_CHAT)
1113             {
1114 0 0         if(defined($packet->{'DATA'}))
1115             {
1116 0           $packet->{'DATA'} =~ s/^\n//s;
1117              
1118 0           my($ref) = $packet->{'PCHAT_REF'};
1119              
1120 0 0         if($ref) # Priate chat
1121             {
1122             # Private chat "action"
1123 0 0         if($packet->{'DATA'} =~ /^ \*\*\* /)
1124             {
1125 0 0         if($use_handlers)
1126             {
1127 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_ACTION'}))
    0          
1128             {
1129 0           &{$self->{'HANDLERS'}->{'PCHAT_ACTION'}}($self, $ref, \$packet->{'DATA'});
  0            
1130             }
1131             elsif($self->{'DEFAULT_HANDLERS'})
1132             {
1133 0           print "PCHAT($ref) ACTION: ", $packet->{'DATA'}, "\n";
1134             }
1135             }
1136             }
1137             else # Regular private chat
1138             {
1139 0 0         if($use_handlers)
1140             {
1141 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_CHAT'}))
    0          
1142             {
1143 0           &{$self->{'HANDLERS'}->{'PCHAT_CHAT'}}($self, $ref, \$packet->{'DATA'});
  0            
1144             }
1145             elsif($self->{'DEFAULT_HANDLERS'})
1146             {
1147 0           print "PCHAT($ref): ", $packet->{'DATA'}, "\n";
1148             }
1149             }
1150             }
1151             }
1152             else # Regular chat
1153             {
1154             # Chat "action"
1155 0 0         if($packet->{'DATA'} =~ /^ \*\*\* /)
1156             {
1157 0 0         if($use_handlers)
1158             {
1159 0 0         if(defined($self->{'HANDLERS'}->{'CHAT_ACTION'}))
    0          
1160             {
1161 0           &{$self->{'HANDLERS'}->{'CHAT_ACTION'}}($self, \$packet->{'DATA'});
  0            
1162             }
1163             elsif($self->{'DEFAULT_HANDLERS'})
1164             {
1165 0           print "CHAT ACTION: ", $packet->{'DATA'}, "\n";
1166             }
1167             }
1168             }
1169             else # Regular chat
1170             {
1171 0 0         if($use_handlers)
1172             {
1173 0 0         if(defined($self->{'HANDLERS'}->{'CHAT'}))
    0          
1174             {
1175 0           &{$self->{'HANDLERS'}->{'CHAT'}}($self, \$packet->{'DATA'});
  0            
1176             }
1177             elsif($self->{'DEFAULT_HANDLERS'})
1178             {
1179 0           print "CHAT: ", $packet->{'DATA'}, "\n";
1180             }
1181             }
1182             }
1183             }
1184             }
1185             }
1186             elsif($type == HTLS_HDR_NEWS_POST)
1187             {
1188 0           my($post) = $packet->{'DATA'};
1189              
1190 0 0         if(defined($post))
1191             {
1192 0           $post =~ s/@{[HTLC_NEWLINE]}/\n/osg;
  0            
1193 0           $post =~ s/_{58}//sg;
1194              
1195 0 0         if($use_handlers)
1196             {
1197 0 0         if(defined($self->{'HANDLERS'}->{'NEWS_POSTED'}))
    0          
1198             {
1199 0           &{$self->{'HANDLERS'}->{'NEWS_POSTED'}}($self, \$post);
  0            
1200             }
1201             elsif($self->{'DEFAULT_HANDLERS'})
1202             {
1203 0           print "NEWS: New post made.\n";
1204             }
1205             }
1206             }
1207             }
1208             elsif($type == HTLS_HDR_POLITE_QUIT ||
1209             $type eq 'DISCONNECTED')
1210             {
1211 0 0         if(defined($packet->{'DATA'}))
    0          
1212             {
1213 0 0         if($use_handlers)
1214             {
1215 0 0         if(defined($self->{'HANDLERS'}->{'QUIT'}))
    0          
1216             {
1217 0           &{$self->{'HANDLERS'}->{'QUIT'}}($self, \$packet->{'DATA'});
  0            
1218             }
1219             elsif($self->{'DEFAULT_HANDLERS'})
1220             {
1221 0           print "CONNECTION CLOSED: ", $packet->{'DATA'}, "\n";
1222             }
1223             }
1224             }
1225             elsif($self->{'DEFAULT_HANDLERS'})
1226             {
1227 0 0         if($use_handlers)
1228             {
1229 0           print "CONNECTION CLOSED\n";
1230             }
1231             }
1232              
1233 0           $self->disconnect();
1234 0           return(0);
1235             }
1236             elsif($type == HTLS_HDR_PCHAT_INVITE)
1237             {
1238 0 0         if($use_handlers)
1239             {
1240 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_INVITE'}))
    0          
1241             {
1242 0           &{$self->{'HANDLERS'}->{'PCHAT_INVITE'}}($self, $packet->{'PCHAT_REF'},
  0            
1243             $packet->{'SOCKET'},
1244             $packet->{'NICK'});
1245             }
1246             elsif($self->{'DEFAULT_HANDLERS'})
1247             {
1248 0           print "PCHAT INVITE($packet->{'PCHAT_REF'}) from $packet->{'NICK'}($packet->{'SOCKET'})",
1249             "($packet->{'SOCKET)'})\n";
1250             }
1251             }
1252             }
1253             elsif($type == HTLS_HDR_PCHAT_USER_JOIN)
1254             {
1255 0           my($ref) = $packet->{'PCHAT_REF'};
1256 0           my($socket) = $packet->{'SOCKET'};
1257 0           my($pchat) = $self->{'PCHATS'}->{$ref};
1258              
1259 0           $pchat->userlist()->{$socket} = $self->{'USER_LIST'}->{$socket};
1260              
1261 0 0         if($use_handlers)
1262             {
1263 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_JOIN'}))
    0          
1264             {
1265 0           &{$self->{'HANDLERS'}->{'PCHAT_JOIN'}}($self, $pchat, $socket);
  0            
1266             }
1267             elsif($self->{'DEFAULT_HANDLERS'})
1268             {
1269 0           print "PCHAT($ref) JOIN($socket)\n";
1270             }
1271             }
1272             }
1273             elsif($type == HTLS_HDR_PCHAT_USER_LEAVE)
1274             {
1275 0           my($ref) = $packet->{'PCHAT_REF'};
1276 0           my($socket) = $packet->{'SOCKET'};
1277 0           my($pchat) = $self->{'PCHATS'}->{$ref};
1278              
1279 0           delete $pchat->userlist()->{$socket};
1280              
1281 0 0         if($use_handlers)
1282             {
1283 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_LEAVE'}))
    0          
1284             {
1285 0           &{$self->{'HANDLERS'}->{'PCHAT_LEAVE'}}($self, $pchat, $socket);
  0            
1286             }
1287             elsif($self->{'DEFAULT_HANDLERS'})
1288             {
1289 0           print "PCHAT($ref) LEAVE($socket)\n";
1290             }
1291             }
1292             }
1293             elsif($type == HTLS_HDR_PCHAT_SUBJECT)
1294             {
1295 0           my($pchat) = $self->{'PCHATS'}->{$packet->{'PCHAT_REF'}};
1296            
1297 0           $pchat->subject($packet->{'DATA'});
1298              
1299 0 0         if($use_handlers)
1300             {
1301 0 0         if(defined($self->{'HANDLERS'}->{'PCHAT_SUBJECT'}))
    0          
1302             {
1303 0           &{$self->{'HANDLERS'}->{'PCHAT_SUBJECT'}}($self, $pchat, \$packet->{'DATA'});
  0            
1304             }
1305             elsif($self->{'DEFAULT_HANDLERS'})
1306             {
1307 0           print "PCHAT(", $pchat->reference(), ") Subject set to: $packet->{'DATA'}\n";
1308             }
1309             }
1310             }
1311              
1312 0           return(1);
1313             }
1314              
1315             sub _handler
1316             {
1317 0     0     my($self, $code_ref, $type) = @_;
1318              
1319 0 0         if(defined($code_ref))
1320             {
1321 0 0         if(ref($code_ref) eq 'CODE')
1322             {
1323 0           $self->{'HANDLERS'}->{$type} = $code_ref;
1324             }
1325             }
1326              
1327 0           return $self->{'HANDLERS'}->{$type};
1328             }
1329              
1330             sub _next_seqnum
1331             {
1332 0     0     my($self) = shift;
1333              
1334 0           return $self->{'SEQNUM'}++;
1335             }
1336              
1337 0     0 1   sub agreement_handler { return _handler($_[0], $_[1], 'AGREEMENT') }
1338 0     0 1   sub ban_handler { return _handler($_[0], $_[1], 'BAN') }
1339 0     0 1   sub chat_handler { return _handler($_[0], $_[1], 'CHAT') }
1340 0     0 1   sub chat_action_handler { return _handler($_[0], $_[1], 'CHAT_ACTION') }
1341 0     0 1   sub color_handler { return _handler($_[0], $_[1], 'COLOR') }
1342 0     0 1   sub event_loop_handler { return _handler($_[0], $_[1], 'EVENT') }
1343 0     0 1   sub delete_file_handler { return _handler($_[0], $_[1], 'FILE_DELETE') }
1344 0     0 1   sub get_file_handler { return _handler($_[0], $_[1], 'FILE_GET') }
1345 0     0 1   sub put_file_handler { return _handler($_[0], $_[1], 'FILE_PUT') }
1346 0     0 1   sub file_info_handler { return _handler($_[0], $_[1], 'FILE_GET_INFO') }
1347 0     0 1   sub file_list_handler { return _handler($_[0], $_[1], 'FILE_LIST') }
1348 0     0 0   sub new_folder_handler { return _handler($_[0], $_[1], 'FILE_MKDIR') }
1349 0     0 0   sub move_file_handler { return _handler($_[0], $_[1], 'FILE_MOVE') }
1350 0     0 1   sub set_file_info_handler { return _handler($_[0], $_[1], 'FILE_SET_INFO') }
1351 0     0 1   sub icon_handler { return _handler($_[0], $_[1], 'ICON') }
1352 0     0 1   sub join_handler { return _handler($_[0], $_[1], 'JOIN') }
1353 0     0 1   sub kick_handler { return _handler($_[0], $_[1], 'KICK') }
1354 0     0 1   sub leave_handler { return _handler($_[0], $_[1], 'LEAVE') }
1355 0     0 1   sub login_handler { return _handler($_[0], $_[1], 'LOGIN') }
1356 0     0 1   sub msg_handler { return _handler($_[0], $_[1], 'MSG') }
1357 0     0 1   sub news_handler { return _handler($_[0], $_[1], 'NEWS') }
1358 0     0 1   sub post_news_handler { return _handler($_[0], $_[1], 'NEWS_POST') }
1359 0     0 1   sub news_posted_handler { return _handler($_[0], $_[1], 'NEWS_POSTED') }
1360 0     0 1   sub nick_handler { return _handler($_[0], $_[1], 'NICK') }
1361 0     0 1   sub pchat_accept_handler { return _handler($_[0], $_[1], 'PCHAT_ACCEPT') }
1362 0     0 1   sub pchat_action_handler { return _handler($_[0], $_[1], 'PCHAT_ACTION') }
1363 0     0 1   sub pchat_chat_handler { return _handler($_[0], $_[1], 'PCHAT_CHAT') }
1364 0     0 0   sub pchat_create_handler { return _handler($_[0], $_[1], 'PCHAT_CREATE') }
1365 0     0 1   sub pchat_invite_handler { return _handler($_[0], $_[1], 'PCHAT_INVITE') }
1366 0     0 1   sub pchat_join_handler { return _handler($_[0], $_[1], 'PCHAT_JOIN') }
1367 0     0 1   sub pchat_leave_handler { return _handler($_[0], $_[1], 'PCHAT_LEAVE') }
1368 0     0 1   sub pchat_subject_handler { return _handler($_[0], $_[1], 'PCHAT_SUBJECT') }
1369 0     0 1   sub quit_handler { return _handler($_[0], $_[1], 'QUIT') }
1370 0     0 1   sub send_msg_handler { return _handler($_[0], $_[1], 'SEND_MSG') }
1371 0     0 1   sub server_msg_handler { return _handler($_[0], $_[1], 'SERVER_MSG') }
1372 0     0 1   sub task_error_handler { return _handler($_[0], $_[1], 'TASK_ERROR') }
1373 0     0 1   sub user_info_handler { return _handler($_[0], $_[1], 'USER_GETINFO') }
1374 0     0 1   sub user_list_handler { return _handler($_[0], $_[1], 'USER_LIST') }
1375              
1376             #
1377             # Package subroutines
1378             #
1379              
1380 0     0 1   sub version { $Net::Hotline::Client::VERSION }
1381              
1382             sub debug
1383             {
1384 0 0 0 0 1   if(@_ == 1 && !ref($_[0]))
    0 0        
1385             {
1386 0 0         $Net::Hotline::Client::DEBUG = ($_[0]) ? 1 : 0;
1387             }
1388             elsif(@_ == 2 && ref($_[0]) eq 'Net::Hotline::Client')
1389             {
1390 0 0         $Net::Hotline::Client::DEBUG = ($_[1]) ? 1 : 0;
1391             }
1392              
1393 0           return $Net::Hotline::Client::DEBUG;
1394             }
1395              
1396             sub _hlc_write
1397             {
1398 0     0     my($self, $fh, $data_ref, $len) = @_;
1399              
1400 0 0 0       return("0-E0") if($len == 0 || !defined($len));
1401            
1402 0 0         unless(_write($fh, $data_ref, $len) == $len)
1403             {
1404 0           $self->{'LAST_ERROR'} = "Write error: $!";
1405 0           return;
1406             }
1407              
1408 0           return($len);
1409             }
1410              
1411             sub _hlc_read
1412             {
1413 0     0     my($self, $fh, $data_ref, $len) = @_;
1414              
1415 0 0 0       return("0-E0") if($len == 0 || !defined($len));
1416              
1417 0 0         unless(_read($fh, $data_ref, $len) == $len)
1418             {
1419 0           $self->{'LAST_ERROR'} = "Read error: $!";
1420 0           return;
1421             }
1422              
1423 0           return($len);
1424             }
1425              
1426             sub _hlc_buffered_read
1427             {
1428 0     0     my($self, $fh, $data_ref, $len) = @_;
1429              
1430 0 0 0       return("0-E0") if($len == 0 || !defined($len));
1431              
1432 0 0         unless(read($fh, $$data_ref, $len) == $len)
1433             {
1434 0           $self->{'LAST_ERROR'} = "Read error: $!";
1435 0           return;
1436             }
1437              
1438 0           return($len);
1439             }
1440              
1441             # Macbinary CRC perl code from Convert::BinHex by Eryq (eryq@enteract.com)
1442             # (It needs access to the lexical @CRC_MAGIC, so it can't be auto-loaded)
1443             sub macbin_crc
1444             {
1445 0 0   0 0   shift if(ref($_[0]));
1446              
1447 0           my($len) = length($_[0]);
1448 0           my($crc) = $_[1];
1449              
1450 0           for(my $i = 0; $i < $len; $i++)
1451             {
1452 0           ($crc ^= (vec($_[0], $i, 8) << 8)) &= 0xFFFF;
1453 0           $crc = ($crc << 8) ^ $CRC_MAGIC[$crc >> 8];
1454             }
1455 0           return $crc;
1456             }
1457              
1458             #
1459             # Satisfy autoloader's ridiculous *8-character* unique name limit :-/
1460             #
1461              
1462 0     0 1   sub get_filelist { al01_get_filelist(@_) }
1463 0     0 1   sub get_fileinfo { al02_get_fileinfo(@_) }
1464 0     0 1   sub get_userinfo { al03_get_userinfo(@_) }
1465 0     0 1   sub user_by_nick { al04_user_by_nick(@_) }
1466 0     0 1   sub req_userlist { al05_req_userlist(@_) }
1467 0     0 1   sub req_filelist { al06_req_filelist(@_) }
1468 0     0 1   sub pchat_action { al07_pchat_action(@_) }
1469 0     0 1   sub get_file { al08_get_file(@_) }
1470 0     0 1   sub put_file { al09_put_file(@_) }
1471              
1472             # Internal functions that were also munged up:
1473              
1474             # _al01_put_file_resume_now
1475             # _al02_get_file_resume_now
1476             # _al03_delete_file_now
1477             # _al04_new_folder_now
1478             # _al05_put_file_now
1479             # _al06_put_file_resume
1480             # _al07_get_file_now
1481             # _al08_get_file_resume
1482             # _al09_file_action_stub
1483             # _al10_post_news_now
1484             # _al11_pchat_invite_now
1485             # _al12_pchat_accept_now
1486             # _al13_comment_now
1487              
1488             __END__