File Coverage

blib/lib/Net/Hotline/Protocol/Packet.pm
Criterion Covered Total %
statement 27 167 16.1
branch 0 68 0.0
condition 0 36 0.0
subroutine 9 12 75.0
pod 0 3 0.0
total 36 286 12.5


line stmt bran cond sub pod time code
1             package Net::Hotline::Protocol::Packet;
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   4 use strict;
  1         1  
  1         29  
8              
9 1     1   4 use vars qw($VERSION);
  1         2  
  1         31  
10              
11 1     1   4 use Carp;
  1         1  
  1         60  
12 1     1   1044 use POSIX qw(:errno_h);
  1         9045  
  1         6  
13 1     1   1422 use Net::Hotline::User;
  1         2  
  1         22  
14 1     1   4 use Net::Hotline::FileListItem;
  1         1  
  1         15  
15 1     1   566 use Net::Hotline::Protocol::Header;
  1         2  
  1         25  
16 1     1   538 use Net::Hotline::Shared qw(:all);
  1         2  
  1         211  
17             use Net::Hotline::Constants
18 1         2929 qw(HTLC_DATA_PCHAT_SUBJECT HTLC_DATA_RFLT HTLC_EWOULDBLOCK HTLC_NEWLINE
19             HTLS_DATA_AGREEMENT HTLS_DATA_CHAT HTLS_DATA_COLOR
20             HTLS_DATA_FILE_COMMENT HTLS_DATA_FILE_CREATOR HTLS_DATA_FILE_CTIME
21             HTLS_DATA_FILE_ICON HTLS_DATA_FILE_LIST HTLS_DATA_FILE_MTIME
22             HTLS_DATA_FILE_NAME HTLS_DATA_FILE_SIZE HTLS_DATA_FILE_TYPE
23             HTLS_DATA_HTXF_REF HTLS_DATA_HTXF_SIZE HTLS_DATA_ICON HTLS_DATA_MSG
24             HTLS_DATA_NEWS HTLS_DATA_NEWS_POST HTLS_DATA_NICKNAME
25             HTLS_DATA_PCHAT_REF HTLS_DATA_SERVER_MSG HTLS_DATA_SOCKET
26             HTLS_DATA_TASK_ERROR HTLS_DATA_USER_INFO HTLS_DATA_USER_LIST
27 1     1   688 HTLS_HDR_TASK SIZEOF_HL_PROTO_HDR HTLS_DATA_REPLY HTLS_DATA_IS_REPLY);
  1         2  
28              
29             $VERSION = '0.80';
30              
31             sub new
32             {
33 0     0 0   my($class) = shift;
34 0           my($self);
35              
36 0           $self =
37             {
38             'PROTO_HEADER' => undef,
39              
40             'USER_LIST' => undef,
41             'FILE_LIST' => undef,
42             'USER_INFO' => undef,
43             'NEWS' => undef,
44              
45             'SOCKET' => undef,
46             'ICON' => undef,
47             'COLOR' => undef,
48             'NICK' => undef,
49             'TASK_ERROR' => undef,
50             'DATA' => undef,
51              
52             'FILE_ICON' => undef,
53             'FILE_TYPE' => undef,
54             'FILE_CREATOR' => undef,
55             'FILE_SIZE' => undef,
56             'FILE_NAME' => undef,
57             'FILE_COMMENT' => undef,
58             'FILE_CTIME' => undef,
59             'FILE_MTIME' => undef,
60              
61             'HTXF_SIZE' => undef,
62             'HTXF_REF' => undef,
63             'HTXF_RFLT' => undef,
64              
65             'PCHAT_REF' => undef,
66              
67             'IS_REPLY' => undef,
68             'REPLY_TO' => undef,
69              
70             'TYPE' => undef
71             };
72              
73 0           bless $self, $class;
74 0           return $self;
75             }
76              
77             sub clear
78             {
79 0     0 0   my($self) = shift;
80              
81 0           $self->{'PROTO_HEADER'} =
82              
83             $self->{'USER_LIST'} =
84             $self->{'FILE_LIST'} =
85             $self->{'USER_INFO'} =
86             $self->{'NEWS'} =
87              
88             $self->{'SOCKET'} =
89             $self->{'ICON'} =
90             $self->{'COLOR'} =
91             $self->{'NICK'} =
92             $self->{'TASK_ERROR'} =
93             $self->{'DATA'} =
94              
95             $self->{'FILE_ICON'} =
96             $self->{'FILE_TYPE'} =
97             $self->{'FILE_CREATOR'} =
98             $self->{'FILE_SIZE'} =
99             $self->{'FILE_NAME'} =
100             $self->{'FILE_COMMENT'} =
101             $self->{'FILE_CTIME'} =
102             $self->{'FILE_MTIME'} =
103              
104             $self->{'HTXF_SIZE'} =
105             $self->{'HTXF_REF'} =
106             $self->{'HTXF_RFLT'} =
107              
108             $self->{'PCHAT_REF'} =
109              
110             $self->{'IS_REPLY'} =
111             $self->{'REPLY_TO'} =
112              
113             $self->{'TYPE'} = undef;
114             }
115              
116             sub read_parse
117             {
118 0     0 0   my($self, $fh, $blocking) = @_;
119              
120 0           my($data, $length, $atom_count, $atom_type, $atom_len, $read_err,
121             $nick, $socket, $icon, $user_type, $name, $color, $read);
122              
123 0           $self->clear();
124              
125 0 0         unless($fh->opened())
126             {
127 0           $self->{'TYPE'} = 'DISCONNECTED';
128 0           return(1);
129             }
130              
131 0           $read = _read($fh, \$data, SIZEOF_HL_PROTO_HDR, $blocking);
132 0           $read_err = 0 + $!; # Get the numerical value of the magical $!
133              
134 0 0 0       unless(defined($read) && $read > 0)
135             {
136 0 0 0       if($read_err == EWOULDBLOCK || $read_err == EAGAIN)
    0 0        
      0        
137             {
138             #_debug("WOULDBLOCK\n");
139 0           return(HTLC_EWOULDBLOCK);
140             }
141             elsif($read_err == ECONNRESET || $read_err == ECONNABORTED ||
142             $read_err == ENOTCONN)
143             {
144             #_debug("DISCONNECTED\n");
145 0           $self->clear();
146 0           $self->{'TYPE'} = 'DISCONNECTED';
147 0           return(1);
148             }
149             else
150             {
151             # I'm assuming this is a MacPerl bug: sysread() sometimes returns
152             # undefined without setting $!. I use the "shrug and continue"
153             # method here and just treat it as an idle event.
154 0 0         return(HTLC_EWOULDBLOCK) if($^O eq 'MacOS');
155              
156             # It's fatal on non-Mac OS systems, however.
157 0           die "sysread() error($read_err): $!\n";
158              
159             # I'm also getting:
160             #
161             # sysread() error(145): Connection timed out
162             #
163             # On Solaris. Hmmmm...
164             }
165             }
166              
167 0           _debug("Packet data:\n", _hexdump($data));
168              
169 0           $self->{'PROTO_HEADER'} = new Net::Hotline::Protocol::Header($data);
170              
171 0           $length = unpack("N", $self->{'PROTO_HEADER'}->len());
172 0           $self->{'TYPE'} = unpack("N", $self->{'PROTO_HEADER'}->type());
173              
174 0 0         if($self->{'TYPE'} == HTLS_HDR_TASK)
175             {
176 0           $self->{'TASK_NUM'} = unpack("N", $self->{'PROTO_HEADER'}->seq());
177             }
178              
179 0           $length -= _read($fh, \$atom_count, 2);
180 0           $atom_count = unpack("n", $atom_count);
181              
182 0           _debug("Atom count: $atom_count\n");
183              
184 0           for(; $atom_count != 0; $atom_count--)
185             {
186             # This probably doesn't need to be here anymore, but just to be safe...
187 0 0         if($length < 4)
188             {
189 0           $length -= _read($fh, \$data, $length);
190 0           _debug("Slurped up < 4 bytes, length = $length\n");
191 0           return(1);
192             }
193              
194 0           $length -= _read($fh, \$atom_type, 2);
195 0           $length -= _read($fh, \$atom_len, 2);
196              
197 0           _debug("Atom type:\n", _hexdump($atom_type));
198 0           _debug("Atom length:\n", _hexdump($atom_len));
199              
200 0           $atom_type = unpack("n", $atom_type);
201 0           $atom_len = unpack("n", $atom_len);
202              
203 0 0 0       if($atom_type == HTLS_DATA_USER_LIST)
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
204             {
205 0           my($user_data, $user);
206              
207 0           $length -= _read($fh, \$user_data, $atom_len);
208              
209 0           $user = new Net::Hotline::User($user_data);
210              
211 0           _debug(" Nick: ", $user->nick(), "\n",
212             " Icon: ", $user->icon(), "\n",
213             "Socket: ", $user->socket(), "\n",
214             " Color: ", $user->color(), "\n");
215              
216 0           $self->{'USER_LIST'}->{$user->socket()} = $user;
217             }
218             elsif($atom_type == HTLS_DATA_FILE_LIST)
219             {
220 0           my($file_data, $file);
221              
222 0           $length -= _read($fh, \$file_data, $atom_len);
223              
224 0           $file = new Net::Hotline::FileListItem($file_data);
225              
226 0           _debug(" Type: ", $file->type(), "\n",
227             "Creator: ", $file->creator(), "\n",
228             " Size: ", $file->size(), "\n",
229             " Name: ", $file->name(), "\n");
230              
231 0           push(@{$self->{'FILE_LIST'}}, $file);
  0            
232             }
233             elsif($atom_type == HTLS_DATA_SOCKET)
234             {
235 0           $length -= _read($fh, \$socket, $atom_len);
236              
237 0           _debug("Socket: ", _hexdump($socket));
238              
239             # Older versions of the Hotline server sent socket numbers
240             # in 4 bytes. Newer versions send it in 2. Nice.
241 0 0         if($atom_len == 4)
242             {
243 0           $self->{'SOCKET'} = unpack("N", $socket);
244             }
245             else
246             {
247 0           $self->{'SOCKET'} = unpack("n", $socket);
248             }
249             }
250             elsif($atom_type == HTLS_DATA_ICON)
251             {
252 0           $length -= _read($fh, \$icon, $atom_len);
253              
254 0           _debug("Icon: ", _hexdump($icon));
255              
256 0           $self->{'ICON'} = unpack("n", $icon);
257             }
258             elsif($atom_type == HTLS_DATA_COLOR)
259             {
260 0           $length -= _read($fh, \$color, $atom_len);
261              
262 0           _debug("Color: ", _hexdump($color));
263              
264 0           $self->{'COLOR'} = unpack("n", $color);
265             }
266             elsif($atom_type == HTLS_DATA_NICKNAME)
267             {
268 0           $length -= _read($fh, \$nick, $atom_len);
269              
270 0           _debug("Nick: ", _hexdump($nick));
271              
272 0           $self->{'NICK'} = $nick;
273             }
274             elsif($atom_type == HTLS_DATA_TASK_ERROR)
275             {
276 0           $length -= _read($fh, \$data, $atom_len);
277              
278 0           _debug("Task error:\n", _hexdump($data));
279              
280 0           $data =~ s/@{[HTLC_NEWLINE]}/\n/osg;
  0            
281 0           $self->{'TASK_ERROR'} = $data;
282             }
283             elsif($atom_type == HTLS_DATA_FILE_ICON)
284             {
285 0           $length -= _read($fh, \$data, $atom_len);
286              
287 0           _debug("File icon:\n", _hexdump($data));
288              
289 0           $self->{'FILE_ICON'} = unpack("n", $data);
290             }
291             elsif($atom_type == HTLS_DATA_FILE_TYPE)
292             {
293 0           $length -= _read($fh, \$data, $atom_len);
294              
295 0           _debug("File type:\n", _hexdump($data));
296              
297 0           $self->{'FILE_TYPE'} = $data;
298             }
299             elsif($atom_type == HTLS_DATA_FILE_CREATOR)
300             {
301 0           $length -= _read($fh, \$data, $atom_len);
302              
303 0           _debug("File creator:\n", _hexdump($data));
304              
305 0           $self->{'FILE_CREATOR'} = $data;
306             }
307             elsif($atom_type == HTLS_DATA_FILE_SIZE)
308             {
309 0           $length -= _read($fh, \$data, $atom_len);
310              
311 0           _debug("File size:\n", _hexdump($data));
312              
313 0 0         if($atom_len == 2) # Grrrrrrr...
314             {
315 0           $self->{'FILE_SIZE'} = unpack("n", $data);
316             }
317             else
318             {
319 0           $self->{'FILE_SIZE'} = unpack("N", $data);
320             }
321             }
322             elsif($atom_type == HTLS_DATA_FILE_NAME)
323             {
324 0           $length -= _read($fh, \$data, $atom_len);
325              
326 0           _debug("File name:\n", _hexdump($data));
327              
328 0           $self->{'FILE_NAME'} = $data;
329             }
330             elsif($atom_type == HTLS_DATA_FILE_COMMENT)
331             {
332 0           $length -= _read($fh, \$data, $atom_len);
333              
334 0           _debug("File comment:\n", _hexdump($data));
335              
336 0           $self->{'FILE_COMMENT'} = $data;
337             }
338             elsif($atom_type == HTLS_DATA_FILE_CTIME)
339             {
340 0           $length -= _read($fh, \$data, $atom_len);
341              
342 0           $data =~ s/^....//;
343 0           _debug("File ctime:\n", _hexdump($data));
344              
345 0           $self->{'FILE_CTIME'} = unpack("N", $data);
346             }
347             elsif($atom_type == HTLS_DATA_FILE_MTIME)
348             {
349 0           $length -= _read($fh, \$data, $atom_len);
350              
351 0           $data =~ s/^....//;
352 0           _debug("File mtime:\n", _hexdump($data));
353              
354 0           $self->{'FILE_MTIME'} = unpack("N", $data);
355             }
356             elsif($atom_type == HTLS_DATA_PCHAT_REF)
357             {
358 0           $length -= _read($fh, \$data, $atom_len);
359              
360 0           _debug("Private chat ref: ", _hexdump($data));
361              
362             # Server 1.2.1 gives chat refs in 2 bytes. Annoying!
363 0 0         if($atom_len == 2)
364             {
365 0           $self->{'PCHAT_REF'} = unpack("n", $data);
366             }
367             else
368             {
369 0           $self->{'PCHAT_REF'} = unpack("N", $data);
370             }
371             }
372             elsif($atom_type == HTLS_DATA_IS_REPLY)
373             {
374 0           $length -= _read($fh, \$data, $atom_len);
375              
376 0           _debug("Is reply:\n", _hexdump($data));
377            
378 0           $self->{'IS_REPLY'} = unpack("n", $data);
379             }
380             elsif($atom_type == HTLS_DATA_REPLY)
381             {
382 0           $length -= _read($fh, \$data, $atom_len);
383              
384 0           _debug("In reply to:\n", _hexdump($data));
385              
386 0           $data =~ s/@{[HTLC_NEWLINE]}/\n/osg;
  0            
387 0           $self->{'REPLY_TO'} = $data;
388             }
389             elsif($atom_type == HTLS_DATA_MSG ||
390             $atom_type == HTLS_DATA_NEWS ||
391             $atom_type == HTLS_DATA_AGREEMENT ||
392             $atom_type == HTLS_DATA_USER_INFO ||
393             $atom_type == HTLS_DATA_CHAT ||
394             $atom_type == HTLC_DATA_PCHAT_SUBJECT ||
395             $atom_type == HTLS_DATA_MSG ||
396             $atom_type == HTLS_DATA_SERVER_MSG ||
397             $atom_type == HTLS_DATA_NEWS_POST)
398             {
399 0           $length -= _read($fh, \$data, $atom_len);
400              
401 0           _debug("Data:\n", _hexdump($data));
402              
403 0           $data =~ s/@{[HTLC_NEWLINE]}/\n/osg;
  0            
404 0           $self->{'DATA'} = $data;
405             }
406             elsif($atom_type == HTLS_DATA_HTXF_SIZE)
407             {
408 0           $length -= _read($fh, \$data, $atom_len);
409              
410 0           _debug("HTXF size:\n", _hexdump($data));
411              
412 0 0         if($atom_len == 2)
413             {
414 0           $self->{'HTXF_SIZE'} = unpack("n", $data);
415             }
416             else
417             {
418 0           $self->{'HTXF_SIZE'} = unpack("N", $data);
419             }
420             }
421             elsif($atom_type == HTLS_DATA_HTXF_REF)
422             {
423 0           $length -= _read($fh, \$data, $atom_len);
424              
425 0           _debug("HTXF ref:\n", _hexdump($data));
426              
427 0           $self->{'HTXF_REF'} = unpack("N", $data);
428             }
429             elsif($atom_type == HTLC_DATA_RFLT)
430             {
431 0           $length -= _read($fh, \$data, $atom_len);
432              
433 0           _debug("HTXF RFLT:\n", _hexdump($data));
434              
435 0           $self->{'HTXF_RFLT'} = $data;
436             }
437             else
438             {
439 0           $length -= _read($fh, \$data, $atom_len);
440              
441 0           _debug("Default data:\n", _hexdump($data));
442 0           $self->{'DATA'} = $data;
443             }
444             }
445              
446 0 0         if($length > 0) # Should not be reached...
447             {
448 0           _debug("Left-over length!\n");
449              
450 0           while($length > 0)
451             {
452 0           $length -= _read($fh, \$data, $length);
453 0           _debug("Left over data:\n", _hexdump($data));
454             }
455             }
456              
457 0           return(1);
458             }
459              
460             1;