| 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; |