File Coverage

blib/lib/Data/Radius/DictionaryParser.pm
Criterion Covered Total %
statement 90 105 85.7
branch 37 48 77.0
condition 6 8 75.0
subroutine 10 10 100.0
pod 0 3 0.0
total 143 174 82.1


line stmt bran cond sub pod time code
1             package Data::Radius::DictionaryParser;
2              
3 4     4   55 use v5.10;
  4         13  
4 4     4   20 use strict;
  4         8  
  4         94  
5 4     4   53 use warnings;
  4         9  
  4         168  
6 4     4   55 use Carp;
  4         11  
  4         305  
7 4     4   1498 use IO::File ();
  4         26338  
  4         103  
8 4     4   28 use File::Spec ();
  4         9  
  4         3754  
9              
10             # parser state
11             my $begin_vendor = undef;
12             my $begin_tlv = undef;
13             # map id to name, {vendor => {id => name}}
14             my %dict_id = ();
15             # map name to id
16             my %dict_attr = ();
17             my %dict_const_name = ();
18             my %dict_const_value = ();
19             my %dict_vendor_name = ();
20             my %dict_vendor_id = ();
21              
22             my %inc = ();
23              
24             sub new {
25 4     4 0 11 my $class = shift;
26              
27 4         19 cleanup();
28              
29 4         21 bless {}, $class;
30             }
31              
32             sub parse_file {
33 4     4 0 14 my ($self, $file) = @_;
34              
35 4         18 $self->_load_file($file);
36              
37             # copy values
38 4         5289 my $d = Data::Radius::Dictionary->new(
39             attr_id => { %dict_id },
40             attr_name => { %dict_attr },
41             const_name => { %dict_const_name },
42             const_value => { %dict_const_value },
43             vnd_name => { %dict_vendor_name },
44             vnd_id => { %dict_vendor_id },
45             );
46 4         312 return $d;
47             }
48              
49             sub cleanup {
50              
51 4     4 0 10 $begin_vendor = undef;
52 4         10 $begin_tlv = undef;
53              
54 4         10 %dict_id = ();
55 4         9 %dict_attr = ();
56 4         9 %dict_const_name = ();
57 4         8 %dict_const_value = ();
58 4         13 %dict_vendor_name = ();
59 4         8 %dict_vendor_id = ();
60 4         8 %inc = ();
61             }
62              
63             sub _load_file {
64 96     96   210 my ($self, $file) = @_;
65              
66 96 50       297 return if($inc{ $file });
67              
68 96   33     457 my $fh = IO::File->new($file) || carp 'Failed to open file: '.$!;
69             #printf "Loading file %s\n", $file;
70              
71 96         8356 $inc{$file} = 1;
72              
73 96         207 my($cmd, $name, $id, $type, $vendor, $has_tag, $has_options, $encrypt);
74              
75 96         2303 while(my $line = $fh->getline) {
76 13896         316987 $line =~ s/#.*$//;
77 13896 100       78452 next if($line =~ /^\s*$/);
78 10928         17009 chomp $line;
79              
80 10928         49604 ($cmd, $name, $id, $type, $vendor) = split(/\s+/, $line);
81 10928         21600 $cmd = lc($cmd);
82 10928         14706 $has_options = 0;
83 10928         13823 $has_tag = 0;
84 10928         15554 $encrypt = undef;
85              
86 10928 100       22359 if($cmd eq 'attribute') {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
87             # 'vendor' part can be an options - in FreeRADIUS dictionary format
88 5136 100       8995 if ($vendor) {
89             # there could be combination of both options:
90 1400 100       2831 if ($vendor =~ /has_tag/) {
91 156         217 $has_tag = 1;
92 156         215 $has_options = 1;
93             }
94 1400 100       2519 if ($vendor =~ /encrypt=(\d)/) {
95             #TODO encryption methods not supported now
96 80         164 $encrypt = $1;
97 80         112 $has_options = 1;
98             }
99              
100 1400 100       2401 if ($has_options) {
101 232         339 $vendor = undef;
102             }
103             }
104              
105 5136   100     16233 $vendor ||= $begin_vendor;
106              
107 5136 50       10732 if (exists $dict_attr{ $name }) {
108 0         0 warn "Duplicated attribute name $name";
109             }
110              
111 5136         21575 my $a_info = {
112             id => $id,
113             name => $name,
114             type => $type,
115             vendor => $vendor,
116             has_tag => $has_tag,
117             encrypt => $encrypt,
118             };
119              
120 5136         15042 $dict_attr{ $name } = $a_info;
121              
122 5136 100       8560 if ($begin_tlv) {
123 212         397 $a_info->{parent} = $begin_tlv;
124              
125 212         337 my $parent = $dict_attr{ $begin_tlv };
126 212         417 $parent->{tlv_attr_name}{ $name } = $a_info;
127 212         4115 $parent->{tlv_attr_id}{ $id } = $a_info;
128             }
129             else {
130 4924   100     98546 $dict_id{ $vendor // '' }{ $id } = $a_info;
131             }
132             }
133             elsif($cmd eq 'value') {
134             # VALUE NAS-Port-Type Ethernet 15
135 5512         9996 my ($v_name, $v_val) = ($id, $type);
136              
137 5512 50       11488 if (! exists $dict_attr{ $name }) {
138 0         0 warn "Value for unknown attribute $name";
139 0         0 next;
140             }
141              
142 5512         12873 $dict_const_name{$name}{$v_val} = $v_name;
143 5512         103884 $dict_const_value{$name}{$v_name} = $v_val;
144             }
145             elsif($cmd eq 'vendor') {
146             # VENDOR Mikrotik 14988
147 68         279 $dict_vendor_name{ $name } = $id;
148 68         1222 $dict_vendor_id{ $id } = $name;
149             }
150             elsif($cmd eq 'begin-vendor') {
151             # BEGIN-VENDOR Huawei
152 36 50       101 if (! exists $dict_vendor_name{ $name }) {
153 0         0 warn "BEGINE-VENDOR $name - vendor id is unknown";
154             }
155             # set default vendor for all attributes below
156 36         596 $begin_vendor = $name;
157             }
158             elsif($cmd eq 'end-vendor') {
159             # END-VENDOR Laurel
160 36 50       101 if (! $begin_vendor) {
161 0         0 warn "END-VENDOR found without BEGIN-VENDOR";
162 0         0 next;
163             }
164 36         591 $begin_vendor = undef;
165             }
166             elsif($cmd eq 'begin-tlv') {
167 24 50       51 if ($begin_tlv) {
168             # no support for 2nd level
169 0         0 warn "Nested BEGIN-TLV found";
170             }
171              
172             # BEGIN-TLV WiMAX-PPAC
173             # must be defined attribute with type 'tlv' first
174 24 50       60 if (! exists $dict_attr{ $name }) {
175 0         0 warn "Begin-tlv for unknown attribute $name";
176 0         0 next;
177             }
178 24 50       60 if ($dict_attr{ $name }{type} ne 'tlv') {
179 0         0 warn "Begin-tlv for attribute $name of non-tlv type";
180 0         0 next;
181             }
182 24         404 $begin_tlv = $name;
183             }
184             elsif($cmd eq 'end-tlv') {
185             # END-TLV WiMAX-PPAC
186 24 50       59 if (! $begin_tlv) {
187 0         0 warn "END-TLV found without BEGIN-TLV";
188 0         0 next;
189             }
190 24         447 $begin_tlv = undef;
191             }
192             elsif($cmd eq '$include') {
193             # $INCLUDE mikrotik
194              
195             # clear modifiers
196 92         167 ($begin_vendor, $begin_tlv) = ();
197              
198 92 50       662 if (File::Spec->file_name_is_absolute($name)) {
199 0         0 $self->_load_file($name);
200             }
201             else {
202             # relative to current file
203 92         953 my (undef, $path, undef) = File::Spec->splitpath($file);
204 92         965 $path = File::Spec->catfile($path, $name);
205 92         339 $self->_load_file($path);
206             }
207             }
208             else {
209 0         0 warn "Unknown command: $cmd";
210             }
211             }
212              
213 96         3354 $fh->close;
214              
215 96         3797 return 1;
216             }
217              
218             1;