File Coverage

blib/lib/Flash/FLAP/IO/Deserializer.pm
Criterion Covered Total %
statement 6 103 5.8
branch 0 36 0.0
condition n/a
subroutine 2 18 11.1
pod 2 16 12.5
total 10 173 5.7


line stmt bran cond sub pod time code
1             package Flash::FLAP::IO::Deserializer;
2             # Copyright (c) 2003 by Vsevolod (Simon) Ilyushchenko. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5             # The code is based on the -PHP project (http://amfphp.sourceforge.net/)
6              
7             =head1 NAME
8              
9             Flash::FLAP::IO::Deserializer
10              
11             =head1 DESCRIPTION
12              
13             Package used to turn the binary data into physical perl objects.
14              
15             =head1 CHANGES
16              
17             =head2 Sat Mar 13 16:31:31 EST 2004
18              
19             =item Patch from Kostas Chatzikokolakis handling encoding.
20              
21             =head2 Sun Mar 9 18:17:31 EST 2003
22              
23             =item The return value of readArray should be \@ret, not @ret.
24              
25             =head2 Tue Mar 11 21:55:41 EST 2003
26              
27             =item Fixed reading keys of objects.
28              
29             =item Added floor(), as Perl lacks it.
30              
31             =head2 Sun Apr 6 14:24:00 2003
32              
33             =item Added code to read objects of type 8. Useful for decoding real AMF server packages, but hardly anywhere else.
34              
35             =cut
36              
37 1     1   10 use strict;
  1         2  
  1         43  
38              
39 1     1   1111 use Encode qw/from_to/;
  1         13396  
  1         1303  
40              
41             # the number of headers in the packet
42             my $header_count;
43             # the content of the headers
44             my $headers;
45             # the number of body elements
46             my $body_count;
47             # the content of the body
48             my $body;
49              
50             sub floor
51             {
52 0     0 1   my $n = shift;
53              
54 0 0         return int($n) - ($n < 0 ? 1: 0) * ($n != int($n) ? 1 : 0);
    0          
55             }
56              
57              
58             #******************** PUBLIC METHODS ****************************/
59              
60             # constructor that also dserializes the raw data
61             sub new
62             {
63 0     0 0   my ($proto, $is, $encoding)=@_;
64 0           my $self = {};
65 0           bless $self, $proto;
66             # the object to store the deserialized data
67 0           $self->{amfdata} = new Flash::FLAP::Util::Object();
68             # save the input stream in this object
69 0           $self->{inputStream} = $is;
70             # save the encoding in this object
71 0           $self->{encoding} = $encoding;
72             # read the binary header
73 0           $self->readHeader();
74             # read the binary body
75 0           $self->readBody();
76 0           return $self;
77             }
78              
79             # returns the instance of the Object package
80             sub getObject
81             {
82 0     0 0   my ($self)=@_;
83 0           return $self->{amfdata};
84             }
85              
86             #******************** PRIVATE METHODS ****************************/
87              
88             sub readHeader
89             {
90 0     0 0   my ($self)=@_;
91             # ignore the first two bytes -- version or something
92 0           $self->{inputStream}->readInt();
93             # find the total number of header elements
94 0           $self->{header_count} = $self->{inputStream}->readInt();
95             # loop over all of the header elements
96 0           while($self->{header_count}--)
97             {
98 0           my $name = $self->{inputStream}->readUTF();
99             # find the must understand flag
100 0           my $required = $self->readBoolean();
101             # grab the length of the header element
102 0           my $length = $self->{inputStream}->readLong();
103             # grab the type of the element
104 0           my $type = $self->{inputStream}->readByte();
105             # turn the element into real data
106 0           my $content = $self->readData($type);
107             # save the name/value into the headers array
108 0           $self->{amfdata}->addHeader($name, $required, $content);
109             }
110             }
111              
112             sub readBody
113             {
114 0     0 0   my ($self)=@_;
115             # find the total number of body elements
116 0           $self->{body_count} = $self->{inputStream}->readInt();
117             # loop over all of the body elements
118 0           while($self->{body_count}--)
119             {
120 0           my $method = $self->readString();
121             # the target that the client understands
122 0           my $target = $self->readString();
123             # grab the length of the body element
124 0           my $length = $self->{inputStream}->readLong();
125            
126             # grab the type of the element
127 0           my $type = $self->{inputStream}->readByte();
128             # turn the argument elements into real data
129 0           my $data = $self->readData($type);
130             # add the body element to the body object
131 0           $self->{amfdata}->addBody($method, $target, $data);
132             }
133             }
134              
135              
136             # reads an object and converts the binary data into a Perl object
137             sub readObject
138             {
139 0     0 0   my ($self)=@_;
140             # init the array
141 0           my %ret;
142            
143             # grab the key
144 0           my $key = $self->{inputStream}->readUTF();
145            
146 0           for (my $type = $self->{inputStream}->readByte(); $type != 9; $type = $self->{inputStream}->readByte())
147             {
148             # grab the value
149 0           my $val = $self->readData($type);
150             # save the name/value pair in the array
151 0           $ret{$key} = $val;
152             # get the next name
153 0           $key = $self->{inputStream}->readUTF();
154             }
155             # return the array
156 0           return \%ret;
157             }
158              
159             # reads and array object and converts the binary data into a Perl array
160             sub readArray
161             {
162 0     0 1   my ($self)=@_;
163             # init the array object
164 0           my @ret;
165             # get the length of the array
166 0           my $length = $self->{inputStream}->readLong();
167             # loop over all of the elements in the data
168 0           for (my $i=0; $i<$length; $i++)
169             {
170             # grab the type for each element
171 0           my $type = $self->{inputStream}->readByte();
172             # grab each element
173 0           push @ret, $self->readData($type);
174             }
175             # return the data
176 0           return \@ret;
177             }
178              
179             sub readCustomClass
180             {
181 0     0 0   my ($self)=@_;
182             # grab the explicit type -- I'm not really convinced on this one but it works,
183             # the only example i've seen is the NetDebugConfig object
184 0           my $typeIdentifier = $self->{inputStream}->readUTF();
185             # the rest of the bytes are an object without the 0x03 header
186 0           my $value = $self->readObject();
187             # save that type because we may need it if we can find a way to add debugging features
188 0           $value->{"_explicitType"} = $typeIdentifier;
189             # return the object
190 0           return $value;
191             }
192              
193             sub readNumber
194             {
195 0     0 0   my ($self)=@_;
196             # grab the binary representation of the number
197 0           return $self->{inputStream}->readDouble();
198             }
199              
200             # read the next byte and return it's boolean value
201             sub readBoolean
202             {
203 0     0 0   my ($self)=@_;
204             # grab the int value of the next byte
205 0           my $int = $self->{inputStream}->readByte();
206             # if it's a 0x01 return true else return false
207 0           return ($int == 1);
208             }
209              
210             sub readString
211             {
212 0     0 0   my ($self)=@_;
213 0           my $s = $self->{inputStream}->readUTF();
214 0 0         from_to($s, "utf8", $self->{encoding}) if $self->{encoding};
215 0           return $s;
216             }
217              
218             sub readDate
219             {
220 0     0 0   my ($self)=@_;
221 0           my $ms = $self->{inputStream}->readDouble(); # date in milliseconds from 01/01/1970
222              
223             # nasty way to get timezone
224 0           my $int = $self->{inputStream}->readInt();
225 0 0         if($int > 720)
226             {
227 0           $int = -(65536 - $int);
228             }
229 0           my $hr = floor($int / 60);
230 0           my $min = $int % 60;
231 0           my $timezone = "GMT " . -$hr . ":" . abs($min);
232             # end nastiness
233              
234             # is there a nice way to return entire date(milliseconds and timezone) in PHP???
235 0           return $ms;
236             }
237              
238             # XML comes in as a plain string except it has a long displaying the length instead of a short?
239             sub readXML
240             {
241 0     0 0   my ($self)=@_;
242             # reads XML
243 0           my $rawXML = $self->{inputStream}->readLongUTF();
244 0 0         from_to($rawXML, "utf8", $self->{encoding}) if $self->{encoding};
245            
246             # maybe parse the XML into a PHP XML structure??? or leave it to the developer
247            
248             # return the xml
249 0           return $rawXML;
250             }
251             sub readFlushedSO
252             {
253 0     0 0   my ($self)=@_;
254             # receives [type(07) 00 00] if SO is flushed and contains 'public' properties
255             # see debugger readout ???
256 0           return $self->{inputStream}->readInt();
257             }
258              
259             sub readASObject
260             {
261 0     0 0   my ($self)=@_;
262              
263             #object Button, object Textformat, object Sound, object Number, object Boolean, object String,
264             #SharedObject unflushed, XMLNode, used XMLSocket??, NetConnection,
265             #SharedObject.data, SharedObject containing 'private' properties
266              
267             #the final byte seems to be the dataType -> 0D
268 0           return undef;
269             }
270              
271             # main switch function to process all of the data types
272             sub readData
273             {
274 0     0 0   my ($self, $type) = @_;
275 0           my $data;
276             #print STDERR "Reading data of type $type\n";
277 0 0         if ($type == 0) # number
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
278             {
279 0           $data = $self->readNumber();
280             }
281             elsif ($type == 1) # boolean
282             {
283 0           $data = $self->readBoolean();
284             }
285             elsif ($type == 2) # string
286             {
287 0           $data = $self->readString();
288             }
289             elsif ($type == 3) # object Object
290             {
291 0           $data = $self->readObject();
292             }
293             elsif ($type == 5) # null
294             {
295 0           $data = undef;
296             }
297             elsif ($type == 6) # undefined
298             {
299 0           $data = undef;
300             }
301             elsif ($type == 7) # flushed SharedObject containing 'public' properties
302             {
303 0           $data = $self->readFlushedSO();
304             }
305             elsif ($type == 8) # array
306             {
307             # shared object format only (*.sol)
308             # only time I saw it was the serverinfo value in a ColdFusion RecordSet
309             # It was just four zeroes - skip them.
310 0           for (my $i=0; $i<4; $i++)
311             {
312 0           $self->{inputStream}->readByte();
313             }
314             }
315             elsif ($type == 10) # array
316             {
317 0           $data = $self->readArray();
318             }
319             elsif ($type == 11) # date
320             {
321 0           $data = $self->readDate();
322             }
323             elsif ($type == 13) # mainly internal AS objects
324             {
325 0           $data = $self->readASObject();
326             }
327             elsif ($type == 15) # XML
328             {
329 0           $data = $self->readXML();
330             }
331             elsif ($type == 16) # Custom Class
332             {
333 0           $data = $self->readCustomClass();
334             }
335             else # unknown case
336             {
337 0           print "xxx $type ";
338             }
339              
340 0           return $data;
341             }
342            
343             1;