File Coverage

blib/lib/RPM/Header/PurePerl.pm
Criterion Covered Total %
statement 81 136 59.5
branch 32 56 57.1
condition 13 27 48.1
subroutine 5 12 41.6
pod n/a
total 131 231 56.7


line stmt bran cond sub pod time code
1             # Copyright (C) 2001,2002,2006 Troels Liebe Bentsen
2             # This library is free software; you can redistribute it and/or modify
3             # it under the same terms as Perl itself.
4              
5             package RPM::Header::PurePerl;
6 1     1   692 use vars '$VERSION';
  1         1  
  1         88  
7             $VERSION = q{1.0.2};
8              
9 1     1   5 use strict;
  1         1  
  1         24  
10 1     1   652 use RPM::Header::PurePerl::Tagtable;
  1         7  
  1         2197  
11              
12             sub TIEHASH # during tie()
13             {
14 1     1   15 my $RPM_HEADER_MAGIC = chr(0x8e).chr(0xad).chr(0xe8);
15 1         3 my $RPM_FILE_MAGIC = chr(0xed).chr(0xab).chr(0xee).chr(0xdb);
16 1         2 my $buff;
17            
18 1         2 my ($class_name, $filename, $readtype) = @_;
19 1         6 my $self = bless { hash => {}, }, $class_name;
20            
21 1 50 33     53 if (!defined($filename) or !open(RPMFILE, "<$filename")) { return undef; }
  0         0  
22            
23 1         4 binmode(RPMFILE);
24            
25             # Read rpm lead
26 1         30 read(RPMFILE, $buff, 96);
27 1         23 ( $self->{'hash'}->{'LEAD_MAGIC'}, # unsigned char[4], í«îÛ == rpm
28             $self->{'hash'}->{'LEAD_MAJOR'}, # unsigned char, 3 == rpm version 3.x
29             $self->{'hash'}->{'LEAD_MINOR'}, # unsigned char, 0 == rpm version x.0
30             $self->{'hash'}->{'LEAD_TYPE'}, # short(int16), 0 == binary, 1 == source
31             $self->{'hash'}->{'LEAD_ARCHNUM'}, # short(int16), 1 == i386
32             $self->{'hash'}->{'LEAD_NAME'}, # char[66], rpm name
33             $self->{'hash'}->{'LEAD_OSNUM'}, # short(int16), 1 == Linux
34             $self->{'hash'}->{'LEAD_SIGNATURETYPE'}, # short(int16), 1280 == rpm 4.0
35             $self->{'hash'}->{'LEAD_RESERVED'} # char[16] future expansion
36             ) = unpack("a4CCssA66ssA16", $buff);
37             # DEBUG:
38             # foreach my $var (keys %{$self->{'hash'}}) { print "$self->{'hash'}->{$var}\n"; } exit;
39            
40 1 50       6 if (!$self->{'hash'}->{'LEAD_MAGIC'} eq $RPM_FILE_MAGIC) { return 0; }
  0         0  
41            
42             # Quick read option.
43 1 50 33     5 if (defined($readtype) and ($readtype eq 'onlylead')) { return $self; }
  0         0  
44            
45 1         6 for (my $header_num=1; $header_num < 3; $header_num++) {
46             # DEBUG:
47             # print "hlead:".tell(RPMFILE)."\n";
48            
49             # Read lead of the headers
50 2         5 read(RPMFILE, $buff, 16);
51            
52             # DEBUG:
53             # print "hlead:".tell(RPMFILE)."\n";
54            
55 2         8 my ($header_magic, $header_version, $header_reserved, $header_entries,
56             $header_size) = unpack("a3CNNN", $buff);
57            
58             # DEBUG:
59             #print "$header_magic, $header_version, $header_reserved, $header_entries, $header_size\n"; next;
60             #read(RPMFILE, $buff, 2200, 0); print "header magic:".index($buff, $RPM_HEADER_MAGIC, 256)."\n"; exit;
61            
62 2 50       7 if ($header_magic eq $RPM_HEADER_MAGIC) { # RPM_HEADER_MAGIC
63             # Read the record structure.
64 2         3 my $record;
65 2         5 read(RPMFILE, $record, 16*$header_entries);
66            
67             # Read the tag structure, pad to a multiplyer of 8 if it's the first header.
68 2 100       6 if ($header_num == 1) {
69             # DEBUG:
70             #print "Offset 1: $header_size, ".tell(RPMFILE)."\n";
71 1 50       4 if (($header_size % 8) == 0) {
72 1         3 read(RPMFILE, $buff, $header_size);
73             }
74             else {
75 0         0 read(RPMFILE, $buff, $header_size+(8-($header_size % 8)));
76             }
77             }
78             else {
79             # DEBUG:
80             #print "Offset 2:".tell(RPMFILE)."\n";
81 1         3 read(RPMFILE, $buff, $header_size);
82             }
83            
84 2         8 for (my $record_num=0; $record_num < $header_entries;
85             $record_num++) { # RECORD LOOP
86 51         131 my ($tag, $type, $offset, $count) =
87             unpack("NNNN", substr($record, $record_num*16, 16));
88            
89 51         56 my @value;
90            
91             # 10x if signature header.
92 51 100       96 if ($header_num == 1) { $tag = $tag*10; }
  4         6  
93            
94             # Unknown tag
95 51 50       253 if (!defined($hdr_tags{$tag})) {
    50          
    50          
    50          
    100          
    100          
    50          
96 0         0 print "Unknown $tag, $type\n"; next;
  0         0  
97             }
98             # Null type
99             elsif ($type == 0) {
100 0         0 @value = ('');
101             }
102             # Char type
103             elsif ($type == 1) {
104 0         0 print "Char $count $hdr_tags{$tag}{'TAGNAME'}\n";
105             #for (my $i=0; $i < $count; $i++) {
106             #push(@value, substr($buff, $offset, $count));
107             # $header_info{$record}{'offset'} += $count;
108             #}
109             }
110             # int8
111             elsif ($type == 2) {
112 0         0 @value = unpack("C*", substr($buff, $offset, 1*$count));
113 0         0 $offset = 1*$count;
114             }
115             # int16
116             elsif ($type == 3) {
117 2         7 @value = unpack("n*", substr($buff, $offset, 2*$count));
118 2         3 $offset = 2*$count;
119             }
120             # int32
121             elsif ($type == 4) {
122 14         34 @value = unpack("N*", substr($buff, $offset, 4*$count));
123 14         18 $offset = 4*$count;
124             }
125             # int64
126             elsif ($type == 5) {
127 0         0 print "Int64(Not supported): ".
128             "$count $hdr_tags{$tag}{'TAGNAME'}\n";
129             #@value = unpack("N*", substr($buff, $offset, 4*$count));
130             #$offset = 4*$count;
131             }
132             # String, String array, I18N string array
133 51 100 100     228 if ($type == 6 or $type == 8 or $type == 9) {
    100 100        
134 32         65 for(my $i=0;$i<$count;$i++) {
135 34         50 my $length = index($buff, "\0", $offset)-$offset;
136             # unpack istedet for substr.
137 34         70 push(@value, substr($buff, $offset, $length));
138 34         84 $offset += $length+1;
139             }
140             }
141             # bin
142             elsif ($type == 7) {
143             #print "Bin $count $tags{$tag}{'TAGNAME'}\n";
144 3         7 $value[0] = substr($buff, $offset, $count);
145             }
146             # Find out if it's an array type or not.
147 51 100 66     214 if (defined($hdr_tags{$tag}{'TYPE'})
148             and $hdr_tags{$tag}{'TYPE'} == 1) {
149 36         39 @{$self->{'hash'}->{$hdr_tags{$tag}{'TAGNAME'}}} = @value;
  36         238  
150             }
151             else {
152 15         85 $self->{'hash'}->{$hdr_tags{$tag}{'TAGNAME'}} = $value[0];
153             }
154             } # RECORD LOOP
155             } # HEADER LOOP
156             }
157            
158             # Save package(cpio.gz) location.
159 1         3 $self->{'hash'}->{'PACKAGE_OFFSET'} = tell(RPMFILE);
160 1         17 close(RPMFILE);
161              
162             # Make old packages look like new ones.
163 1 50       5 if (defined($self->{'hash'}->{'FILENAMES'})) {
164 0         0 my $count = 0;
165 0         0 my %quick_dirnames;
166 0         0 foreach my $filename (@{$self->{'hash'}->{'FILENAMES'}}) {
  0         0  
167 0         0 my $file = ''; my $dir = '/';
  0         0  
168            
169 0 0       0 if($filename =~ /(.*\/)(.*$)/) {
170 0         0 $file = $1; $dir = $2;
  0         0  
171             } else {
172 0         0 $file = $filename;
173             }
174            
175 0 0       0 if (!defined($quick_dirnames{$dir})) {
176 0         0 push(@{$self->{'hash'}->{'DIRNAMES'}}, $dir);
  0         0  
177 0         0 $quick_dirnames{$dir} = $count++;
178             }
179 0         0 push(@{$self->{'hash'}->{'BASENAMES'}}, $file);
  0         0  
180 0         0 push(@{$self->{'hash'}->{'DIRINDEXES'}}, $quick_dirnames{$dir});
  0         0  
181             }
182 0         0 delete($self->{'hash'}->{'FILENAMES'});
183             }
184              
185             # Wait I can beat it, a package sould also provide is's own name, sish (and only once).
186 1         2 my %quick_provides = map {$_ => 1} @{$self->{'hash'}->{'PROVIDENAME'}};
  1         6  
  1         4  
187 1         2 my %quick_provideflags = map {$_ => 1} @{$self->{'hash'}->{'PROVIDEFLAGS'}};
  1         18  
  1         3  
188 1         6 my %quick_provideversion
189 1         2 = map {$_ => 1} @{$self->{'hash'}->{'PROVIDEVERSION'}};
  1         3  
190            
191 1 0 33     5 if (!defined($quick_provides{$self->{'hash'}->{'NAME'}}) and
      0        
192             !defined($quick_provideflags{8}) and
193             !defined($quick_provideversion{$self->{'hash'}->{'VERSION'}})) {
194 0         0 push(@{$self->{'hash'}->{'PROVIDENAME'}}, $self->{'hash'}->{'NAME'});
  0         0  
195 0         0 push(@{$self->{'hash'}->{'PROVIDEFLAGS'}}, 8);
  0         0  
196 0         0 push(@{$self->{'hash'}->{'PROVIDEVERSION'}},
  0         0  
197             $self->{'hash'}->{'VERSION'}.'-'.$self->{'hash'}->{'RELEASE'});
198             }
199            
200             # FILEVERIFYFLAGS is signed
201 1 50       5 if ($self->{'hash'}->{'FILEVERIFYFLAGS'}) {
202 1         2 for(my $i=0;$i{'hash'}->{'FILEVERIFYFLAGS'}}); $i++) {
  2         8  
203 1         2 my $val = @{$self->{'hash'}->{'FILEVERIFYFLAGS'}}[$i];
  1         2  
204 1 50 33     12 if (int($val) == $val && $val >= 2147483648 &&
      33        
205             $val <= 4294967295) {
206 1         1 @{$self->{'hash'}->{'FILEVERIFYFLAGS'}}[$i] -= 4294967296;
  1         4  
207             }
208             }
209             }
210            
211             # Lets handel the SIGNATURE, this does not work, fix it please.
212 1 50       5 if (defined($self->{'hash'}->{'SIGNATURE_MD5'})) {
213 0         0 $self->{'hash'}->{'SIGNATURE_MD5'} =
214             unpack("H*", $self->{'hash'}->{'SIGNATURE_MD5'});
215             }
216              
217             # Old stuff, so it can be a drop in replacement for RPM::HEADERS.
218 1 50       7 if (defined($self->{'hash'}->{'EPOCH'})) {
219 0         0 $self->{'hash'}->{'SERIAL'} = $self->{'hash'}->{'EPOCH'};
220             }
221              
222 1 50       5 if (defined($self->{'hash'}->{'LICENSE'})) {
223 1         4 $self->{'hash'}->{'COPYRIGHT'} = $self->{'hash'}->{'LICENSE'};
224             }
225            
226 1 50       4 if (defined($self->{'hash'}->{'PROVIDENAME'})) {
227 1         4 $self->{'hash'}->{'PROVIDES'} = $self->{'hash'}->{'PROVIDENAME'};
228             }
229            
230 1 50       4 if (defined($self->{'hash'}->{'OBSOLETENAME'})) {
231 0         0 $self->{'hash'}->{'OBSOLETES'} = $self->{'hash'}->{'OBSOLETENAME'};
232             }
233            
234 1         6 return $self;
235             }
236              
237             sub FETCH # during $a = $ht{something};
238             {
239 2     2   32 my ($self, $key) = @_;
240 2         10 return $self->{hash}->{$key};
241             }
242              
243             sub STORE # during $ht{something} = $a;
244             {
245 0     0     my ($self, $key, $val) = @_;
246 0           $self->{hash}->{$key} = $val;
247             }
248              
249             sub DELETE # during delete $ht{something}
250             {
251 0     0     my ($self, $key) = @_;
252 0           delete $self->{hash}->{$key};
253             }
254              
255             sub CLEAR # during %h = ();
256             {
257 0     0     my ($self) = @_;
258 0           $self->{hash} = {};
259 0           ();
260             }
261              
262             sub EXISTS # during if (exists $h{something}) { ... }
263             {
264 0     0     my ($self, $key) = @_;
265 0           return exists $self->{hash}->{$key};
266             }
267              
268             sub FIRSTKEY # at the beginning of foreach (keys %h) { ... }
269             {
270 0     0     my ($self) = @_;
271 0           each %{$self->{hash}};
  0            
272             }
273              
274             sub NEXTKEY # during foreach()
275             {
276 0     0     my ($self) = @_;
277 0           each %{$self->{hash}};
  0            
278             }
279              
280             sub DESTROY # well, when the hash gets destroyed
281 0     0     {
282             # do nothing here
283             }
284              
285             =head1 NAME
286              
287             RPM::Header::PurePerl - a perl only implementation of a RPM header reader.
288              
289             =head1 VERSION
290              
291             Version 1.0.2
292              
293             =head1 SYNOPSIS
294              
295             use RPM::Header::PurePerl;
296             tie my %rpm, "RPM::Header::PurePerl", "rpm-4.0-1-i586.rpm"
297             or die "Problem, could not open rpm";
298             print $rpm{'NAME'};
299              
300             =head1 DESCRIPTION
301              
302             RPM::Header::PurePerl is a clone of RPM::Header written in only Perl, so it
303             provides a way to read a rpm package on systems where rpm is not installed.
304             RPM::Header::PurePerl can used as a drop in replacement for RPM::Header, if
305             needed also the other way round.
306              
307             =head1 NOTES
308              
309             The former name of this package was RPM::PerlOnly.
310              
311             =head1 AUTHOR
312              
313             Troels Liebe Bentsen
314              
315             =head1 COPYRIGHT AND LICENCE
316              
317             Copyright (C) 2001,2002,2006 Troels Liebe Bentsen
318              
319             This library is free software; you can redistribute it and/or modify
320             it under the same terms as Perl itself.
321              
322             =cut
323              
324             1;
325             __END__