|
1 # Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 # All rights reserved. |
|
3 # This component and the accompanying materials are made available |
|
4 # under the terms of the License "Eclipse Public License v1.0" |
|
5 # which accompanies this distribution, and is available |
|
6 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 # |
|
8 # Initial Contributors: |
|
9 # Nokia Corporation - initial contribution. |
|
10 # |
|
11 # Contributors: |
|
12 # |
|
13 # Description: |
|
14 # |
|
15 # |
|
16 |
|
17 package ExportData; |
|
18 use strict; |
|
19 use Utils; |
|
20 use IniData; |
|
21 |
|
22 use constant BIN_KEYS => 0; |
|
23 use constant RELDATA_KEYS => 1; |
|
24 use constant EXP_KEYS => 2; |
|
25 use constant SRC_KEYS => 3; |
|
26 use constant SRC_EXP_KEYS => 4; |
|
27 |
|
28 # |
|
29 # Constructor |
|
30 # |
|
31 sub New { |
|
32 my $invocant = shift; |
|
33 my $class = ref($invocant) || $invocant; |
|
34 my %args = @_; |
|
35 my $self = { |
|
36 exportsFile => $args{exports_file}, |
|
37 verbose => $args{verbose}, |
|
38 iniData => IniData->New(), |
|
39 }; |
|
40 bless $self, $class; |
|
41 |
|
42 $self->ParseExportData(); |
|
43 return $self; |
|
44 } |
|
45 |
|
46 # |
|
47 # Public |
|
48 # |
|
49 |
|
50 sub PgpKeysForSource { |
|
51 my $self = shift; |
|
52 my $component = lc(shift); |
|
53 my $category = lc(shift); |
|
54 |
|
55 return $self->ReconstructData($component, SRC_KEYS, $category); |
|
56 } |
|
57 |
|
58 sub PgpKeysForBinaries { |
|
59 my $self = shift; |
|
60 my $component = lc(shift); |
|
61 |
|
62 return $self->ReconstructData($component, BIN_KEYS); |
|
63 } |
|
64 |
|
65 sub PgpKeysForExports { |
|
66 my $self = shift; |
|
67 my $component = lc(shift); |
|
68 my $category = lc(shift); |
|
69 |
|
70 return $self->ReconstructData($component, EXP_KEYS, $category); |
|
71 } |
|
72 |
|
73 sub PgpKeysForRelData { |
|
74 my $self = shift; |
|
75 my $component = lc(shift); |
|
76 |
|
77 return $self->ReconstructData($component, RELDATA_KEYS); |
|
78 } |
|
79 |
|
80 sub AllPgpKeys { |
|
81 my $self = shift; |
|
82 if (exists $self->{pgpKeys}) { |
|
83 return $self->{pgpKeys}; |
|
84 } |
|
85 return []; |
|
86 } |
|
87 |
|
88 sub ExportableComponents { |
|
89 my $self = shift; |
|
90 |
|
91 if (exists $self->{components}) { |
|
92 return $self->{components}; |
|
93 } |
|
94 return []; |
|
95 } |
|
96 |
|
97 sub ComponentIsExportable { |
|
98 my $self = shift; |
|
99 my $comp = lc(shift); |
|
100 |
|
101 foreach my $exportableComp (@{$self->ExportableComponents()}) { |
|
102 if ($comp eq lc($exportableComp)) { |
|
103 return 1; |
|
104 } |
|
105 } |
|
106 return 0; |
|
107 } |
|
108 |
|
109 |
|
110 # |
|
111 # Private |
|
112 # |
|
113 |
|
114 sub ParseExportData { |
|
115 my $self = shift; |
|
116 |
|
117 unless ($self->{exportsFile}) { |
|
118 die "Error: Export data filename not defined\n"; |
|
119 } |
|
120 |
|
121 open EXPORTS, "$self->{exportsFile}" or die "Error: Unable to open $self->{exportsFile} for reading\n"; |
|
122 |
|
123 if ($self->{verbose}) { |
|
124 print "Parsing export data file $self->{exportsFile} ...\n"; |
|
125 } |
|
126 |
|
127 my $separator = $self->{iniData}->CsvSeparator(); |
|
128 |
|
129 my $firstRow = 1; |
|
130 while (my $row = <EXPORTS>) { |
|
131 chomp $row; |
|
132 if ($row =~ /^\s*$/ or $row =~ /^[$separator]*#/) {next;} #ignore empty rows in table |
|
133 #handle first non empty row |
|
134 if ($firstRow) { |
|
135 $self->HandleFirstRow($row); |
|
136 $firstRow = 0; |
|
137 } |
|
138 #handle subsequent non empty rows |
|
139 else { |
|
140 $self->HandleRow($row); |
|
141 } |
|
142 } |
|
143 close EXPORTS; |
|
144 } |
|
145 |
|
146 sub HandleFirstRow { |
|
147 my $self = shift; |
|
148 my $row = shift; |
|
149 |
|
150 #parse row of delimiter-separated values |
|
151 my @cols = $self->ParseCSV($row); |
|
152 |
|
153 for (my $i = 1; $i <= $#cols; ++$i) { |
|
154 my $cell = $cols[$i]; |
|
155 if (defined $cell) { |
|
156 Utils::StripWhiteSpace(\$cell); |
|
157 my ($pgpKeyid) = ($cell =~ /\b(0X[0-9a-fA-F]{8})\b/i); |
|
158 unless ($pgpKeyid) { |
|
159 die "Error: PGP key ID of the correct format not defined in column header \"$cell\"\n"; |
|
160 } |
|
161 push @{$self->{pgpKeys}}, $pgpKeyid; |
|
162 push @{$self->{nonemptyColumns}} ,$i; |
|
163 } |
|
164 else { |
|
165 die "Error: Undefined PGP key in ".$self->{exportsFile}." file.\n"; |
|
166 } |
|
167 } |
|
168 } |
|
169 |
|
170 sub HandleRow { |
|
171 my $self = shift; |
|
172 my $row = shift; |
|
173 |
|
174 $row = lc($row); |
|
175 |
|
176 #parse row of delimiter-separated values |
|
177 my @cols = $self->ParseCSV($row); |
|
178 |
|
179 my $component = $cols[0]; |
|
180 Utils::StripWhiteSpace(\$component); |
|
181 if ($component =~ /^\s*$/) { |
|
182 die "Error: Export table has wrong format. Must have component name in first column.\n"; |
|
183 } |
|
184 push @{$self->{components}}, $component; |
|
185 |
|
186 #iterate over columns which have a nonempty recipient heading and store cell data |
|
187 my @cells = @cols[@{$self->{nonemptyColumns}}]; |
|
188 for (my $j = 0; $j < @cells; ++$j) { |
|
189 $self->HandleCell($component, $j, $cells[$j]); #$j is the PGP array index |
|
190 } |
|
191 } |
|
192 |
|
193 sub HandleCell { |
|
194 my $self = shift; |
|
195 my $component = shift; |
|
196 my $pgpKeyIndex = shift; |
|
197 my $cell = shift; |
|
198 |
|
199 my $pgpKey = $self->{pgpKeys}->[$pgpKeyIndex]; |
|
200 |
|
201 # cell must not be undef but may be blank |
|
202 if (!defined $cell) { |
|
203 $cell = ''; |
|
204 } |
|
205 |
|
206 if ($cell =~ /exclude(?!_)/i) { |
|
207 # Cells containing 'exclude' must not have _any_ release files of this |
|
208 # component exported to this recipient. However if only you want to stop |
|
209 # binaries, use exclude_bin |
|
210 return; |
|
211 } |
|
212 |
|
213 # Other cells must have the recipient's key added to 'relDataPgpKeys' and |
|
214 # possibly also 'srcPgpKeys', 'expPgpKeys or 'binPgpKeys' for this component. |
|
215 # Concatenating the string save memory, over using an array |
|
216 $self->{keys}->{$component}->[RELDATA_KEYS] .= "$pgpKeyIndex,"; |
|
217 |
|
218 # Include binaries unless 'exclude_bin' |
|
219 if ( $cell !~ s/exclude_bin//i ) { |
|
220 $self->{keys}->{$component}->[BIN_KEYS] .= "$pgpKeyIndex,"; |
|
221 } |
|
222 |
|
223 # Identify any S() or E() blocks |
|
224 my %blocks; |
|
225 while ($cell =~ s/([a-z])\((.*?)\)//i) { |
|
226 if (!defined $blocks{$1}) { |
|
227 $blocks{$1} = [$1, $2]; |
|
228 } else { |
|
229 die "Error: Export table has wrong format. Multiple $1() blocks found in cell for component '$component', PGP key '$pgpKey'\n"; |
|
230 } |
|
231 } |
|
232 |
|
233 foreach my $block (keys(%blocks)) { |
|
234 my ($origblock, $cats) = @{$blocks{$block}}; |
|
235 my $type; |
|
236 if ($block eq "s") { |
|
237 $type = SRC_KEYS; |
|
238 } elsif ($block eq "e") { |
|
239 $type = EXP_KEYS; |
|
240 } |
|
241 if (defined $type) { |
|
242 while ($cats =~ s/([a-z]-[a-z]|\S)//i) { # a letter range (e.g. A-Z) or any non whitespace character |
|
243 my $cat = $1; |
|
244 |
|
245 if ($cat =~ /(.)-(.)/) { |
|
246 my ($from, $to) = ($1, $2); |
|
247 |
|
248 foreach my $cat (ord($from)..ord($to)) { # convert the characters to numbers so that we can do a foreach on the range |
|
249 $cat -= 96; |
|
250 $self->{keys}->{$component}->[$type]->[$cat] .= "$pgpKeyIndex,"; |
|
251 } |
|
252 } |
|
253 elsif ($cat =~ /^[a-z]$/i) { |
|
254 $cat = ord($cat) - 96; |
|
255 $self->{keys}->{$component}->[$type]->[$cat] .= "$pgpKeyIndex,"; |
|
256 } else { |
|
257 die "Error: Export table has wrong format. '$cat' is not a valid IPR category in cell for component '$component', PGP key '$pgpKey'\n"; |
|
258 } |
|
259 } |
|
260 } else { |
|
261 die "Error: Export table has wrong format. '$origblock()' is not a valid construct in cell for component '$component', PGP key '$pgpKey'\n"; |
|
262 } |
|
263 } |
|
264 |
|
265 # Handle any 'old format' IPR categories not in blocks |
|
266 while ($cell =~ s/([a-z]-[a-z]|\S)//i) { # a letter range (e.g. A-Z) or any non whitespace character |
|
267 my $cat = $1; |
|
268 |
|
269 if ($cat =~ /(.)-(.)/) { |
|
270 my ($from, $to) = ($1, $2); |
|
271 |
|
272 foreach my $cat (ord($from)..ord($to)) { # convert the characters to numbers so that we can do a foreach on the range |
|
273 $cat -= 96; |
|
274 $self->{keys}->{$component}->[SRC_EXP_KEYS]->[$cat] .= "$pgpKeyIndex,"; |
|
275 } |
|
276 } |
|
277 elsif ($cat !~ /^[a-z]$/i) { |
|
278 die "Error: Export table has wrong format. '$cat' is not a valid IPR category in cell for component '$component', PGP key '$pgpKey'\n"; |
|
279 } |
|
280 else { |
|
281 $cat = ord($cat) - 96; |
|
282 $self->{keys}->{$component}->[SRC_EXP_KEYS]->[$cat] .= "$pgpKeyIndex,"; |
|
283 } |
|
284 } |
|
285 } |
|
286 |
|
287 sub ParseCSV { |
|
288 my $self = shift; |
|
289 my $text = shift; # record containing delimited-separated values |
|
290 my @new ; |
|
291 |
|
292 my $separator = $self->{iniData}->CsvSeparator(); |
|
293 |
|
294 while ($text =~ m{"([^\"\\]*(?:\\.[^\"\\]*)*)"$separator?|([^$separator]+)$separator?|$separator}gx) { |
|
295 push(@new, $+); |
|
296 } |
|
297 |
|
298 push(@new, undef) if substr($text, -1,1) eq $separator; |
|
299 |
|
300 return @new; # list of values that were delimited-separated |
|
301 } |
|
302 |
|
303 sub ReconstructData { |
|
304 my $self = shift; |
|
305 my $component = shift; |
|
306 my $type = shift; |
|
307 my $category = shift; |
|
308 |
|
309 if ($category) { |
|
310 $category = ord($category) - 96; |
|
311 } |
|
312 |
|
313 if (defined $self->{keys}->{$component}) { |
|
314 my @results; |
|
315 my @pgpKeysIndex; |
|
316 |
|
317 if ($type == EXP_KEYS || $type == SRC_KEYS) { |
|
318 # Gets a list of the src or export keys, as well as the list of keys in both source and exports. |
|
319 # Splits the key indexes on , |
|
320 if (defined $self->{keys}->{$component}->[$type]->[$category]) { |
|
321 @pgpKeysIndex = split /,/, $self->{keys}->{$component}->[$type]->[$category]; |
|
322 } |
|
323 if (defined $self->{keys}->{$component}->[SRC_EXP_KEYS]->[$category]) { |
|
324 push @pgpKeysIndex, split /,/, $self->{keys}->{$component}->[SRC_EXP_KEYS]->[$category]; |
|
325 } |
|
326 } |
|
327 else { # BIN or RELDATA |
|
328 @pgpKeysIndex = split /,/, $self->{keys}->{$component}->[$type] |
|
329 } |
|
330 |
|
331 @results = map $self->{pgpKeys}->[$_], @pgpKeysIndex; |
|
332 return \@results; |
|
333 } |
|
334 |
|
335 return []; |
|
336 } |
|
337 |
|
338 1; |
|
339 |
|
340 __END__ |
|
341 |
|
342 =head1 NAME |
|
343 |
|
344 ExportData.pm - Provides an interface to the contents of the project's export data file. |
|
345 |
|
346 =head1 DESCRIPTION |
|
347 |
|
348 A module used for accessing export restriction information for a component release. |
|
349 |
|
350 =head1 INTERFACE |
|
351 |
|
352 =head2 New |
|
353 |
|
354 Passed a named parameter list in the form of hash key value pairs: |
|
355 |
|
356 exportsFile => $export_data_filename |
|
357 verbose => $integer_verbosity_value |
|
358 |
|
359 Opens and parses the export data file which should contain lines of delimiter separated values representing a table of component name rows and recipient columns, as in the example below: |
|
360 |
|
361 | pgpkeyid_1 (recipient) | pgpkeyid_2 (recipient) | pgpkeyid_3 (recipient) | |
|
362 ------------+------------------------+------------------------+------------------------+-- |
|
363 component_1 | DE | E | CDE | |
|
364 ------------+------------------------+------------------------+------------------------+-- |
|
365 component_2 | S(CDE) E(DE) | | DE | |
|
366 ------------+------------------------+------------------------+------------------------+-- |
|
367 component_3 | D-G T | A-F | exclude | |
|
368 ------------+------------------------+------------------------+------------------------+-- |
|
369 component_4 | exclude_bin DEFG | DEFG | DEFG | |
|
370 |
|
371 |
|
372 |
|
373 The column headers must contain the recipients PGP key ID - an eight digit hexadecimal number preceeded by C<0x> (e.g C<0xD9A2CE15>). This public PGP key will be used to encrypt all files sent to the recipient. The name of the recipient may also be included in the column header although this is not mandatory. |
|
374 |
|
375 A cell contains a list of IPR categories available to the recipient of the component. |
|
376 Each category must be a single letter or digit or a range (e.g. A-Z). Empty cells imply that the recipient |
|
377 does not have access to any source for the corresponding component but can still receive |
|
378 binaries. |
|
379 |
|
380 Alternatively, different categories may be specified for source files and export files, using the S(...) and E(...) notations respectively, with '...' being a list of IPR categories. |
|
381 |
|
382 To prevent a recipient from receiving both source and binaries for the corresponding component, use the keyword C<exclude>. This can be useful when certain recipients may receive releases of some but not all components. |
|
383 |
|
384 To prevent a recipient from receiving binaries for the corresponding component, use the keyword C<exclude_bin>. Unlike C<exclude>, this does not break any environment. |
|
385 |
|
386 Components which are not listed in the table but exist on the local site will not be exported to any recipients. However, a warning will be issued to alert the exporter of this situation. |
|
387 |
|
388 If a licensee or third party does not use C<DISTRIBUTION.POLICY> files to categorize source then all source will have the category X. In this case, putting X in a cell implies that all source for that component will be sent to the recipient, otherwise none will be sent. |
|
389 |
|
390 Lines starting with a C<#> are treated as comments and ignored. |
|
391 |
|
392 [NOTE: It is recommended that this file is created and maintained using a spreadsheet |
|
393 application (saving as a CSV file) rather than editing it directly.] |
|
394 |
|
395 If your CSV file does not use a comma ',' as the separator you will need to specify the required |
|
396 separator in your reltools.ini, using the syntax F<csv_separator <separator>>, e.g. F<csv_separator ;>. |
|
397 |
|
398 =head2 PgpKeysForRelData |
|
399 |
|
400 Expects a component name. Returns a reference to an array of public PGP key ids (corresponding to different |
|
401 recipients) to be used to encrypt the component's reldata. |
|
402 |
|
403 =head2 PgpKeysForSource |
|
404 |
|
405 Expects a component name and a source category. Returns a reference to an array of public PGP |
|
406 key ids (corresponding to different recipients) to be used to encrypt the component's source of |
|
407 this category. |
|
408 |
|
409 =head2 PgpKeysForBinaries |
|
410 |
|
411 Expects a component name. Returns a reference to an array of public PGP key ids (corresponding to different |
|
412 recipients) to be used to encrypt the component's binaries. |
|
413 |
|
414 =head2 PgpKeysForExports |
|
415 |
|
416 Expects a component name and an IPR category. Returns a reference to an array of public PGP |
|
417 key ids (corresponding to different recipients) to be used to encrypt the component's exports of |
|
418 this category. |
|
419 |
|
420 =head2 AllPgpKeys |
|
421 |
|
422 Returns a reference to an array of all PGP key IDs listed in the export table. |
|
423 |
|
424 =head2 ExportableComponents |
|
425 |
|
426 Returns a reference to an array of all the components listed in the export table |
|
427 |
|
428 =head2 ComponentIsExportable |
|
429 |
|
430 Expects to be passed a component name. Returns true if the component is listed in the |
|
431 export table. |
|
432 |
|
433 =head1 KNOWN BUGS |
|
434 |
|
435 None. |
|
436 |
|
437 =head1 COPYRIGHT |
|
438 |
|
439 Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
440 All rights reserved. |
|
441 This component and the accompanying materials are made available |
|
442 under the terms of the License "Eclipse Public License v1.0" |
|
443 which accompanies this distribution, and is available |
|
444 at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
445 |
|
446 Initial Contributors: |
|
447 Nokia Corporation - initial contribution. |
|
448 |
|
449 Contributors: |
|
450 |
|
451 Description: |
|
452 |
|
453 |
|
454 =cut |