line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# These objects, initialized with an "OSCAR protocol template" from Net::OSCAR::XML::protoparse, |
2
|
|
|
|
|
|
|
# pack and unpack data according to the specification of that template. |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Net::OSCAR::XML::Template; |
5
|
|
|
|
|
|
|
BEGIN { |
6
|
5
|
|
|
5
|
|
120
|
$Net::OSCAR::XML::Template::VERSION = '1.928'; |
7
|
|
|
|
|
|
|
} |
8
|
|
|
|
|
|
|
|
9
|
5
|
|
|
5
|
|
31
|
use strict; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
165
|
|
10
|
5
|
|
|
5
|
|
27
|
use warnings; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
228
|
|
11
|
|
|
|
|
|
|
|
12
|
5
|
|
|
5
|
|
27
|
use Net::OSCAR::XML; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
154
|
|
13
|
5
|
|
|
5
|
|
464
|
use Net::OSCAR::Common qw(:loglevels); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
1114
|
|
14
|
5
|
|
|
5
|
|
451
|
use Net::OSCAR::Utility qw(hexdump); |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
361
|
|
15
|
5
|
|
|
5
|
|
27
|
use Net::OSCAR::TLV; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
227
|
|
16
|
5
|
|
|
5
|
|
29
|
use Data::Dumper; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
219
|
|
17
|
5
|
|
|
5
|
|
28
|
use Carp; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
18826
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new($@) { |
20
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
21
|
0
|
|
0
|
|
|
|
my $package = ref($class) || $class || "Net::OSCAR::XML::Template"; |
22
|
0
|
|
|
|
|
|
my $self = {template => $_[0]}; |
23
|
0
|
0
|
0
|
|
|
|
$self->{oscar} = $class->{oscar} if ref($class) and $class->{oscar}; |
24
|
0
|
|
|
|
|
|
bless $self, $package; |
25
|
0
|
|
|
|
|
|
return $self; |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# Net::OSCAR::XML caches Template objects that don't have an associated OSCAR, |
29
|
|
|
|
|
|
|
# so that the same Template can be reused with multiple OSCAR objects. |
30
|
|
|
|
|
|
|
# Before returning a Template to the user, it calls set_oscar, so here we clone |
31
|
|
|
|
|
|
|
# ourself with the new OSCAR. |
32
|
|
|
|
|
|
|
# |
33
|
|
|
|
|
|
|
sub set_oscar($$) { |
34
|
0
|
|
|
0
|
0
|
|
my($self, $oscar) = @_; |
35
|
0
|
|
|
|
|
|
my $clone = $self->new($self->{template}); |
36
|
0
|
|
|
|
|
|
$clone->{oscar} = $oscar; |
37
|
0
|
|
|
|
|
|
return $clone; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# If given a scalar ref instead of a scalar as the second argument, |
42
|
|
|
|
|
|
|
# we will modify the packet in-place. |
43
|
|
|
|
|
|
|
sub unpack($$) { |
44
|
0
|
|
|
0
|
0
|
|
my ($self, $x_packet) = @_; |
45
|
0
|
|
|
|
|
|
my $oscar = $self->{oscar}; |
46
|
0
|
|
|
|
|
|
my $template = $self->{template}; |
47
|
0
|
0
|
|
|
|
|
my $packet = ref($x_packet) ? $$x_packet : $x_packet; |
48
|
|
|
|
|
|
|
|
49
|
0
|
|
|
|
|
|
my %data = (); |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
0
|
|
|
$oscar->log_print_cond(OSCAR_DBG_XML, sub { "Decoding:\n", hexdump($packet), "\n according to: ", Data::Dumper::Dumper($template) }); |
|
0
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
53
|
0
|
|
|
|
|
|
assert(ref($template) eq "ARRAY"); |
54
|
0
|
|
|
|
|
|
foreach my $datum (@$template) { |
55
|
|
|
|
|
|
|
# In TLV chains, count refers to number of TLVs, not number of repetitions of the datum, so it defaults to infinite. |
56
|
0
|
|
0
|
|
|
|
my $count = $datum->{count} || ($datum->{type} eq "tlvchain" ? -1 : 1); |
57
|
0
|
|
|
|
|
|
my @results; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
## Figure out how much input data this datum is dealing with |
61
|
|
|
|
|
|
|
|
62
|
0
|
0
|
0
|
|
|
|
if($datum->{prefix} and $datum->{prefix} eq "count") { |
63
|
0
|
|
0
|
|
|
|
($count) = unpack($datum->{prefix_packlet}, substr($packet, 0, $datum->{prefix_len}, "")) || 0; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
my $size = undef; |
67
|
0
|
0
|
|
|
|
|
if($datum->{type} eq "num") { |
68
|
0
|
0
|
|
|
|
|
if($count != -1) { |
69
|
0
|
|
|
|
|
|
$size = $datum->{len} * $count; |
70
|
|
|
|
|
|
|
} else { |
71
|
0
|
|
|
|
|
|
$size = length($packet); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} else { |
74
|
0
|
0
|
0
|
|
|
|
if($datum->{prefix} and $datum->{prefix} eq "length") { |
|
|
0
|
|
|
|
|
|
75
|
0
|
|
|
|
|
|
($size) = unpack($datum->{prefix_packlet}, substr($packet, 0, $datum->{prefix_len}, "")); |
76
|
|
|
|
|
|
|
} elsif(exists($datum->{len})) { |
77
|
|
|
|
|
|
|
# In TLV chains, count is the number of TLVs, not a repeat |
78
|
|
|
|
|
|
|
# count for the datum. |
79
|
0
|
0
|
|
|
|
|
if($datum->{type} eq "tlvchain") { |
80
|
0
|
|
|
|
|
|
$size = $datum->{len}; |
81
|
|
|
|
|
|
|
} else { |
82
|
0
|
0
|
|
|
|
|
if($count == -1) { |
83
|
0
|
|
|
|
|
|
$size = length($packet); |
84
|
|
|
|
|
|
|
} else { |
85
|
0
|
|
|
|
|
|
$size = $datum->{len} * $count; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
my $input; |
92
|
0
|
0
|
|
|
|
|
if(defined($size)) { |
93
|
0
|
|
|
|
|
|
$input = substr($packet, 0, $size, ""); |
94
|
|
|
|
|
|
|
} else { |
95
|
0
|
|
|
|
|
|
$input = $packet; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
## Okay, we have our input data -- act on it |
100
|
|
|
|
|
|
|
|
101
|
0
|
0
|
0
|
|
|
|
if($datum->{type} eq "num") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
102
|
0
|
|
0
|
|
|
|
for(my $i = 0; ($input ne "") and ($count == -1 or $i < $count); $i++) { |
|
|
|
0
|
|
|
|
|
103
|
0
|
|
|
|
|
|
push @results, unpack($datum->{packlet}, substr($input, 0, $datum->{len}, "")); |
104
|
|
|
|
|
|
|
|
105
|
0
|
0
|
0
|
|
|
|
if(exists($datum->{enum_byval}) and exists($datum->{enum_byval}->{$results[-1]})) { |
106
|
0
|
|
|
|
|
|
$results[-1] = $datum->{enum_byval}->{$results[-1]}; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
} elsif($datum->{type} eq "data" or $datum->{type} eq "ref") { |
110
|
|
|
|
|
|
|
# If we just have simple, no preset length, no subitems, raw data, it can't have a repeat count, since the first repetition will gobble up everything |
111
|
0
|
|
0
|
|
|
|
assert($datum->{type} ne "data" or ($datum->{items} and @{$datum->{items}}) or defined($size) or $count == 1 or $datum->{null_terminated}); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# We want: |
114
|
|
|
|
|
|
|
# |
115
|
|
|
|
|
|
|
# to be empty string, not undefined, when length==0. |
116
|
0
|
0
|
0
|
|
|
|
if(!$input and $count == 1 and defined($size)) { |
|
|
|
0
|
|
|
|
|
117
|
0
|
|
|
|
|
|
push @results, ""; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
0
|
|
|
|
for(my $i = 0; $input and ($count == -1 or $i < $count); $i++) { |
|
|
|
0
|
|
|
|
|
121
|
|
|
|
|
|
|
# So, consider the structure: |
122
|
|
|
|
|
|
|
# |
123
|
|
|
|
|
|
|
# |
124
|
|
|
|
|
|
|
# |
125
|
|
|
|
|
|
|
# |
126
|
|
|
|
|
|
|
# We don't know the size of 'foo' in advance. |
127
|
|
|
|
|
|
|
# Thus, we pass a reference to the actual packet into protopack. |
128
|
|
|
|
|
|
|
# subpacket will be modified to be the packet minus the bits that the contents of the data consumed. |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
my %tmp; |
131
|
0
|
0
|
|
|
|
|
if($datum->{type} eq "data") { |
|
|
0
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
my $subinput; |
133
|
0
|
0
|
|
|
|
|
if($datum->{len}) { |
|
|
0
|
|
|
|
|
|
134
|
0
|
|
|
|
|
|
$subinput = substr($input, 0, $datum->{len}, ""); |
135
|
|
|
|
|
|
|
} elsif($datum->{null_terminated}) { |
136
|
0
|
|
|
|
|
|
$input =~ s/^(.*?)\0//; |
137
|
0
|
|
|
|
|
|
$subinput = $1; |
138
|
|
|
|
|
|
|
} else { |
139
|
0
|
|
|
|
|
|
$subinput = $input; |
140
|
0
|
|
|
|
|
|
$input = ""; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
0
|
0
|
|
|
|
|
if(exists($datum->{pad})) { |
144
|
0
|
|
|
|
|
|
my $pad = chr($datum->{pad}); |
145
|
0
|
|
|
|
|
|
$subinput =~ s/$pad*$//; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
0
|
0
|
|
|
|
if($datum->{items} and @{$datum->{items}}) { |
|
0
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
assert(!$datum->{null_terminated}); |
150
|
0
|
|
|
|
|
|
(%tmp) = $self->new($datum->{items})->unpack(\$subinput); |
151
|
0
|
0
|
|
|
|
|
$input = $subinput unless $datum->{len}; |
152
|
|
|
|
|
|
|
} else { |
153
|
0
|
0
|
|
|
|
|
$subinput =~ s/\0$// if $datum->{null_terminated}; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# The simple case -- raw |
156
|
0
|
0
|
|
|
|
|
push @results, $subinput if $datum->{name}; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} elsif($datum->{type} eq "ref") { |
159
|
0
|
|
|
|
|
|
(%tmp) = protoparse($oscar, $datum->{name})->unpack(\$input); |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
|
push @results, \%tmp if %tmp; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} elsif($datum->{type} eq "tlvchain") { |
165
|
0
|
|
|
|
|
|
my @unknown; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
## First set up a hash to store the data for each TLV, grouped by (sub)type |
168
|
|
|
|
|
|
|
## |
169
|
0
|
|
|
|
|
|
my $tlvmap = tlv(); |
170
|
0
|
0
|
|
|
|
|
if($datum->{subtyped}) { |
171
|
0
|
|
|
|
|
|
foreach (@{$datum->{items}}) { |
|
0
|
|
|
|
|
|
|
172
|
0
|
|
0
|
|
|
|
$tlvmap->{$_->{num}} ||= tlv(); |
173
|
0
|
|
0
|
|
|
|
$tlvmap->{$_->{num}}->{$_->{subtype} || -1} = {%$_}; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} else { |
176
|
0
|
|
|
|
|
|
$tlvmap->{$_->{num}} = {%$_} foreach (@{$datum->{items}}); |
|
0
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
## Now, go through the chain and split the data into TLVs. |
180
|
|
|
|
|
|
|
## |
181
|
0
|
|
0
|
|
|
|
for(my $i = 0; $input and ($count == -1 or $i < $count); $i++) { |
|
|
|
0
|
|
|
|
|
182
|
0
|
|
|
|
|
|
my %tlv; |
183
|
0
|
0
|
|
|
|
|
if($datum->{subtyped}) { |
184
|
0
|
|
|
|
|
|
(%tlv) = protoparse($oscar, "subtyped_TLV")->unpack(\$input); |
185
|
|
|
|
|
|
|
} else { |
186
|
0
|
|
|
|
|
|
(%tlv) = protoparse($oscar, "TLV")->unpack(\$input); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
|
my $unknown = 0; |
190
|
0
|
0
|
|
|
|
|
if(!exists($tlvmap->{$tlv{type}})) { |
191
|
0
|
0
|
|
|
|
|
$tlvmap->{$tlv{type}} = $datum->{subtyped} ? tlv() : {}; |
192
|
0
|
|
|
|
|
|
$unknown = 1; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
0
|
0
|
|
|
|
|
assert(!exists($tlv{name})) if exists($tlv{count}); |
196
|
0
|
0
|
|
|
|
|
if($datum->{subtyped}) { |
197
|
0
|
|
|
|
|
|
assert(exists($tlv{subtype})); |
198
|
|
|
|
|
|
|
|
199
|
0
|
0
|
|
|
|
|
if(!exists($tlvmap->{$tlv{type}}->{$tlv{subtype}})) { |
200
|
0
|
0
|
|
|
|
|
if(exists($tlvmap->{$tlv{type}}->{-1})) { |
201
|
0
|
|
|
|
|
|
$tlv{subtype} = -1; |
202
|
|
|
|
|
|
|
} else { |
203
|
0
|
|
|
|
|
|
$tlvmap->{$tlv{type}}->{$tlv{subtype}} = {}; |
204
|
0
|
|
|
|
|
|
$unknown = 1; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
0
|
0
|
|
|
|
|
if(!$unknown) { |
209
|
0
|
|
|
|
|
|
my $type = $tlv{type}; |
210
|
0
|
|
|
|
|
|
my $subtype = $tlv{subtype}; |
211
|
0
|
|
0
|
|
|
|
$tlvmap->{$type}->{$subtype}->{data} ||= []; |
212
|
0
|
|
0
|
|
|
|
$tlvmap->{$type}->{$subtype}->{outdata} ||= []; |
213
|
|
|
|
|
|
|
|
214
|
0
|
0
|
|
|
|
|
$tlv{data} = "" if !defined($tlv{data}); |
215
|
0
|
|
|
|
|
|
push @{$tlvmap->{$type}->{$subtype}->{data}}, $tlv{data}; |
|
0
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
} else { |
217
|
0
|
|
|
|
|
|
push @unknown, { |
218
|
|
|
|
|
|
|
type => $tlv{type}, |
219
|
|
|
|
|
|
|
subtype => $tlv{subtype}, |
220
|
|
|
|
|
|
|
data => $tlv{data} |
221
|
|
|
|
|
|
|
}; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
} else { |
224
|
0
|
0
|
|
|
|
|
if(!$unknown) { |
225
|
0
|
|
|
|
|
|
my $type = $tlv{type}; |
226
|
0
|
|
0
|
|
|
|
$tlvmap->{$type}->{data} ||= []; |
227
|
0
|
|
0
|
|
|
|
$tlvmap->{$type}->{outdata} ||= []; |
228
|
|
|
|
|
|
|
|
229
|
0
|
0
|
|
|
|
|
$tlv{data} = "" if !defined($tlv{data}); |
230
|
0
|
|
|
|
|
|
push @{$tlvmap->{$tlv{type}}->{data}}, $tlv{data}; |
|
0
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
} else { |
232
|
0
|
|
|
|
|
|
push @unknown, { |
233
|
|
|
|
|
|
|
type => $tlv{type}, |
234
|
|
|
|
|
|
|
data => $tlv{data} |
235
|
|
|
|
|
|
|
}; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
## Almost done! Go back through the hash we made earlier, which now has the |
241
|
|
|
|
|
|
|
## data in it, and figure out which TLVs we want to emit. |
242
|
|
|
|
|
|
|
## |
243
|
0
|
|
|
|
|
|
my @outvals; |
244
|
0
|
|
|
|
|
|
while(my($num, $val) = each %$tlvmap) { |
245
|
0
|
0
|
|
|
|
|
if($datum->{subtyped}) { |
246
|
0
|
|
|
|
|
|
while(my($subtype, $subval) = each %$val) { |
247
|
0
|
0
|
|
|
|
|
push @outvals, $subval if exists($subval->{data}); |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} else { |
250
|
0
|
0
|
|
|
|
|
push @outvals, $val if exists($val->{data}); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
## Okay, now take the TLVs to emit, and structure the output correctly |
256
|
|
|
|
|
|
|
## for each thing-to-emit. We'll need to do one last phase of postprocessing |
257
|
|
|
|
|
|
|
## so that we can group counted TLVs correctly. |
258
|
|
|
|
|
|
|
## |
259
|
0
|
|
|
|
|
|
foreach my $val (@outvals) { |
260
|
0
|
|
|
|
|
|
foreach (@{$val->{data}}) { |
|
0
|
|
|
|
|
|
|
261
|
0
|
0
|
|
|
|
|
next unless exists($val->{items}); |
262
|
0
|
|
|
|
|
|
my(%tmp) = $self->new($val->{items})->unpack($_); |
263
|
|
|
|
|
|
|
# We want: |
264
|
|
|
|
|
|
|
# |
265
|
|
|
|
|
|
|
# to give x => "" when TLV 1 is present but empty, |
266
|
|
|
|
|
|
|
# not x => undef. |
267
|
0
|
0
|
0
|
|
|
|
if(@{$val->{items}} == 1 and $val->{items}->[0]->{name}) { |
|
0
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
my $name = $val->{items}->[0]->{name}; |
269
|
0
|
0
|
|
|
|
|
$tmp{$name} = "" if !defined($tmp{$name}); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
0
|
0
|
|
|
|
|
if(@{$val->{items}}) { |
|
0
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
push @{$val->{outdata}}, \%tmp; |
|
0
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
} else { |
275
|
0
|
|
|
|
|
|
push @{$val->{outdata}}, ""; |
|
0
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
## Okay, we've stashed the output (formatted data structures) for each TLV. |
282
|
|
|
|
|
|
|
## Now we need to merge these into results. |
283
|
|
|
|
|
|
|
## This is normally just pushing everything out to results, as a hashref |
284
|
|
|
|
|
|
|
## under the TLVs name for named TLVs, but counted TLVs also need to |
285
|
|
|
|
|
|
|
## be layered into an array. |
286
|
|
|
|
|
|
|
## |
287
|
0
|
|
|
|
|
|
foreach my $val (@outvals) { |
288
|
0
|
0
|
|
|
|
|
if(exists($val->{count})) { |
289
|
0
|
0
|
|
|
|
|
if(exists($val->{name})) { |
290
|
0
|
|
|
|
|
|
push @results, { |
291
|
|
|
|
|
|
|
$val->{name} => $val->{outdata} |
292
|
|
|
|
|
|
|
}; |
293
|
|
|
|
|
|
|
} else { |
294
|
0
|
|
|
|
|
|
push @results, $val->{outdata}->[0]; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
} else { |
297
|
0
|
0
|
|
|
|
|
if(exists($val->{name})) { |
298
|
0
|
|
|
|
|
|
push @results, { |
299
|
|
|
|
|
|
|
$val->{name} => $val->{outdata}->[0] |
300
|
|
|
|
|
|
|
}; |
301
|
|
|
|
|
|
|
} else { |
302
|
0
|
|
|
|
|
|
push @results, $val->{outdata}->[0]; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
0
|
0
|
|
|
|
|
push @results, {__UNKNOWN => [@unknown]} if @unknown; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# If we didn't know the length of the datum in advance, |
312
|
|
|
|
|
|
|
# we've been modifying the entire packet in-place. |
313
|
0
|
0
|
|
|
|
|
$packet = $input if !defined($size); |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
## Okay, we have the results from this datum, store them away. |
317
|
|
|
|
|
|
|
|
318
|
0
|
0
|
|
|
|
|
if($datum->{name}) { |
|
|
0
|
|
|
|
|
|
319
|
0
|
0
|
0
|
|
|
|
if($datum->{count} or ($datum->{prefix} and $datum->{prefix} eq "count")) { |
|
0
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
320
|
0
|
|
|
|
|
|
$data{$datum->{name}} = \@results; |
321
|
|
|
|
|
|
|
} elsif( |
322
|
|
|
|
|
|
|
$datum->{type} eq "ref" or |
323
|
|
|
|
|
|
|
(ref($datum->{items}) and @{$datum->{items}}) |
324
|
|
|
|
|
|
|
) { |
325
|
0
|
|
|
|
|
|
$data{$_} = $results[0]->{$_} foreach keys %{$results[0]}; |
|
0
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
} else { |
327
|
0
|
|
|
|
|
|
$data{$datum->{name}} = $results[0]; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
} elsif(@results) { |
330
|
0
|
|
|
|
|
|
foreach my $result(@results) { |
331
|
0
|
0
|
|
|
|
|
next unless ref($result); |
332
|
0
|
|
|
|
|
|
$data{$_} = $result->{$_} foreach keys %$result; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
0
|
0
|
|
0
|
|
|
$oscar->log_print_cond(OSCAR_DBG_XML, sub { "Decoded:\n", join("\n", map { "\t$_ => ".(defined($data{$_}) ? hexdump($data{$_}) : 'undef') } keys %data) }); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# Remember, passing in a ref to packet in place of actual packet data == in-place editing... |
340
|
0
|
0
|
|
|
|
|
$$x_packet = $packet if ref($x_packet); |
341
|
|
|
|
|
|
|
|
342
|
0
|
|
|
|
|
|
return %data; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub pack($%) { |
347
|
0
|
|
|
0
|
0
|
|
my($self, %data) = @_; |
348
|
0
|
|
|
|
|
|
my $packet = ""; |
349
|
0
|
|
|
|
|
|
my $oscar = $self->{oscar}; |
350
|
0
|
|
|
|
|
|
my $template = $self->{template}; |
351
|
|
|
|
|
|
|
|
352
|
0
|
0
|
|
0
|
|
|
$oscar->log_print_cond(OSCAR_DBG_XML, sub { "Encoding:\n", join("\n", map { "\t$_ => ".(defined($data{$_}) ? hexdump($data{$_}) : 'undef') } keys %data), "\n according to: ", Data::Dumper::Dumper($template) }); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
|
354
|
0
|
|
|
|
|
|
assert(ref($template) eq "ARRAY"); |
355
|
0
|
|
|
|
|
|
foreach my $datum (@$template) { |
356
|
0
|
|
|
|
|
|
my $output = undef; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
## Figure out what we're packing |
359
|
0
|
|
|
|
|
|
my $value = undef; |
360
|
0
|
0
|
|
|
|
|
$value = $data{$datum->{name}} if $datum->{name}; |
361
|
0
|
0
|
|
|
|
|
$value = $datum->{value} if !defined($value); |
362
|
0
|
0
|
|
|
|
|
my @valarray = ref($value) eq "ARRAY" ? @$value : ($value); # Don't modify $value in-place! |
363
|
|
|
|
|
|
|
|
364
|
0
|
0
|
0
|
|
|
|
$datum->{count} = @valarray if $datum->{prefix} and $datum->{prefix} eq "count"; |
365
|
0
|
0
|
|
|
|
|
my $max_count = exists($datum->{count}) ? $datum->{count} : 1; |
366
|
0
|
|
|
|
|
|
my $count = 0; |
367
|
|
|
|
|
|
|
|
368
|
0
|
|
0
|
|
|
|
assert($max_count == -1 or @valarray <= $max_count); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
## Pack it |
372
|
0
|
0
|
0
|
|
|
|
if($datum->{type} eq "num") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
373
|
0
|
0
|
|
|
|
|
next unless defined($value); |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
0
|
|
|
|
for($count = 0; ($max_count == -1 or $count < $max_count) and @valarray; $count++) { |
|
|
|
0
|
|
|
|
|
376
|
0
|
|
|
|
|
|
my $val = shift @valarray; |
377
|
0
|
0
|
0
|
|
|
|
if(exists($datum->{enum_byname}) and exists($datum->{enum_byname}->{$val})) { |
378
|
0
|
|
|
|
|
|
$val = $datum->{enum_byname}->{$val}; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
|
$output .= pack($datum->{packlet}, $val); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
} elsif($datum->{type} eq "data" or $datum->{type} eq "ref") { |
384
|
0
|
|
0
|
|
|
|
for($count = 0; ($max_count == -1 or $count < $max_count) and @valarray; $count++) { |
|
|
|
0
|
|
|
|
|
385
|
0
|
|
|
|
|
|
my $val = shift @valarray; |
386
|
|
|
|
|
|
|
|
387
|
0
|
0
|
0
|
|
|
|
if($datum->{items} and @{$datum->{items}}) { |
|
0
|
0
|
|
|
|
|
|
388
|
0
|
0
|
|
|
|
|
$output .= $self->new($datum->{items})->pack(ref($val) ? %$val : %data); |
389
|
|
|
|
|
|
|
} elsif($datum->{type} eq "ref") { |
390
|
0
|
|
0
|
|
|
|
assert($max_count == 1 or (ref($val) and ref($val) eq "HASH")); |
391
|
0
|
0
|
|
|
|
|
$output .= protoparse($oscar, $datum->{name})->pack(ref($val) ? %$val : %data); |
392
|
|
|
|
|
|
|
} else { |
393
|
0
|
0
|
|
|
|
|
$output .= $val if defined($val); |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
0
|
0
|
|
|
|
|
$output .= chr(0) if $datum->{null_terminated}; |
397
|
0
|
0
|
|
|
|
|
if(exists($datum->{pad})) { |
398
|
0
|
|
0
|
|
|
|
assert(exists($datum->{len}) and exists($datum->{pad})); |
399
|
|
|
|
|
|
|
|
400
|
0
|
0
|
|
|
|
|
my $outlen = defined($output) ? length($output) : 0; |
401
|
0
|
|
|
|
|
|
my $pad_needed = $datum->{len} - $outlen; |
402
|
0
|
0
|
|
|
|
|
$output .= chr($datum->{pad}) x $pad_needed if $pad_needed; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
} elsif($datum->{type} eq "tlvchain") { |
406
|
0
|
|
|
|
|
|
foreach my $tlv (@{$datum->{items}}) { |
|
0
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
my $tlvdata = undef; |
408
|
|
|
|
|
|
|
|
409
|
0
|
0
|
|
|
|
|
if(exists($tlv->{name})) { |
410
|
0
|
0
|
0
|
|
|
|
if(exists($data{$tlv->{name}})) { |
|
0
|
0
|
|
|
|
|
|
411
|
0
|
0
|
|
|
|
|
if(@{$tlv->{items}}) { |
|
0
|
|
|
|
|
|
|
412
|
0
|
|
0
|
|
|
|
assert(ref($data{$tlv->{name}}) eq "HASH" or ref($data{$tlv->{name}}) eq "ARRAY"); |
413
|
0
|
0
|
|
|
|
|
if(ref($data{$tlv->{name}}) eq "ARRAY") { |
414
|
0
|
|
|
|
|
|
$tlvdata = []; |
415
|
0
|
|
|
|
|
|
push @$tlvdata, $self->new($tlv->{items})->pack(%$_) foreach @{$data{$tlv->{name}}}; |
|
0
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
} else { |
417
|
0
|
|
|
|
|
|
$tlvdata = [$self->new($tlv->{items})->pack(%{$data{$tlv->{name}}})]; |
|
0
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
} else { |
420
|
0
|
0
|
|
|
|
|
$tlvdata = [""] if defined($data{$tlv->{name}}); |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} elsif(exists($tlv->{value}) and !@{$tlv->{items}}) { |
423
|
0
|
|
|
|
|
|
$tlvdata = [$tlv->{value}]; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
} else { |
426
|
0
|
|
|
|
|
|
my $tmp = $self->new($tlv->{items})->pack(%data); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# If TLV has no name and only one element, do special handling for "present but empty" value. |
429
|
0
|
0
|
0
|
|
|
|
if($tmp ne "") { |
|
0
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
430
|
0
|
|
|
|
|
|
$tlvdata = [$tmp]; |
431
|
0
|
|
|
|
|
|
} elsif(@{$tlv->{items}} == 1 and $tlv->{items}->[0]->{name} and exists($data{$tlv->{items}->[0]->{name}})) { |
432
|
0
|
|
|
|
|
|
$tlvdata = [""]; |
433
|
|
|
|
|
|
|
} elsif(!@{$tlv->{items}} and exists($tlv->{value})) { |
434
|
0
|
|
|
|
|
|
$tlvdata = [$tlv->{value}]; |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
|
assert($tlv->{num}); |
439
|
0
|
0
|
|
|
|
|
next unless defined($tlvdata); |
440
|
|
|
|
|
|
|
|
441
|
0
|
|
|
|
|
|
$count++; |
442
|
0
|
0
|
|
|
|
|
if($datum->{subtyped}) { |
443
|
0
|
|
|
|
|
|
my $subtype = 0; |
444
|
0
|
|
|
|
|
|
assert(exists($tlv->{subtype})); |
445
|
0
|
0
|
|
|
|
|
$subtype = $tlv->{subtype} if $tlv->{subtype} != -1; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
$output .= protoparse($oscar, "subtyped_TLV")->pack( |
448
|
|
|
|
|
|
|
type => $tlv->{num}, |
449
|
|
|
|
|
|
|
subtype => $subtype, |
450
|
|
|
|
|
|
|
data => $_ |
451
|
0
|
|
|
|
|
|
) foreach @$tlvdata; |
452
|
|
|
|
|
|
|
} else { |
453
|
|
|
|
|
|
|
$output .= protoparse($oscar, "TLV")->pack( |
454
|
|
|
|
|
|
|
type => $tlv->{num}, |
455
|
|
|
|
|
|
|
data => $_ |
456
|
0
|
|
|
|
|
|
) foreach @$tlvdata; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
## Handle any prefixes |
463
|
0
|
0
|
0
|
|
|
|
if($datum->{prefix} and defined($output)) { |
464
|
0
|
0
|
|
|
|
|
if($datum->{prefix} eq "count") { |
465
|
0
|
|
|
|
|
|
$packet .= pack($datum->{prefix_packlet}, $count); |
466
|
|
|
|
|
|
|
} else { |
467
|
0
|
|
|
|
|
|
$packet .= pack($datum->{prefix_packlet}, length($output)); |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
0
|
0
|
|
|
|
|
$packet .= $output if defined($output); |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
0
|
|
|
0
|
|
|
$oscar->log_print_cond(OSCAR_DBG_XML, sub { "Encoded:\n", hexdump($packet) }); |
|
0
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
|
return $packet; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub assert($) { |
480
|
0
|
|
|
0
|
0
|
|
my $test = shift; |
481
|
0
|
0
|
|
|
|
|
return if $test; |
482
|
0
|
|
|
|
|
|
confess("Net::OSCAR internal error"); |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# Why isn't this imported properly?? |
486
|
0
|
|
|
0
|
0
|
|
sub protoparse { Net::OSCAR::XML::protoparse(@_); } |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
1; |