rupert 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 41a2074b3a0e08e088990219f5659323705498e3
4
- data.tar.gz: 810e02415872bb758efa3b109f70e5d264f8455f
3
+ metadata.gz: 8a0baa0b545ca0c693abd38c0c23e6631d441982
4
+ data.tar.gz: f786fc3a79ef710b65d0c8c63bfc652368b21c58
5
5
  SHA512:
6
- metadata.gz: 3b351ae5740f728a267a41afc8a474921aaf072d3141209cc86d743e4011ebe223adb5c6fb45db7c3bb29d4b67d5cbae99d6748c564264f97b2160508db7f626
7
- data.tar.gz: 1585d64649df3e91617ddb3b4b88464d8a4f3aac419e18c862ee1009de1915554502be5c793257d79d4a0f39164008d98170bb79bbe59787f3a9ff75e2eba24f
6
+ metadata.gz: c8757b87031ea4a3744a80bb0b2664f606c7e1f5b49aa77ae77f9a1fb32b6dd8fc806dc8016b3ef3da125aac8dad3bcf294df4825f98d6b8cbea1e2128371006
7
+ data.tar.gz: 09a9460c7382da48467712467a059e6c9518b65c1693073715f1181d3f845be5e2dab03166b12102ce5fd215f1e20e05a53143ed83810d8e53396dbb54511579
checksums.yaml.gz.sig ADDED
Binary file
data.tar.gz.sig ADDED
Binary file
data/Changelog.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 0.0.2
4
+
5
+ * First pieces of metadata fetched from RPM header structure:
6
+ * package name
7
+ * list of installed files
8
+ * installed package size
9
+ * Major design improvements
10
+
11
+ ## 0.0.1
12
+
13
+ * Read information from RPM lead section
14
+ * Read MD5 checksum information from RPM signature structure
15
+ * Perform basic (non-crypto) package integrity verification
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Gem Version](https://badge.fury.io/rb/rupert.png)](http://badge.fury.io/rb/rupert)
1
2
  [![Build Status](https://travis-ci.org/stefanozanella/rupert.png?branch=master)](https://travis-ci.org/stefanozanella/rupert)
2
3
  [![Code Climate](https://codeclimate.com/github/stefanozanella/rupert.png)](https://codeclimate.com/github/stefanozanella/rupert)
3
4
  [![Coverage Status](https://coveralls.io/repos/stefanozanella/rupert/badge.png?branch=master)](https://coveralls.io/r/stefanozanella/rupert?branch=master)
@@ -23,6 +24,8 @@ Or install it yourself as:
23
24
 
24
25
  ## Usage
25
26
 
27
+ ### Parsing an RPM
28
+
26
29
  You can read an RPM simply with:
27
30
 
28
31
  rpm = Rupert::RPM.load('rpm-4.8.0-32.el6.x86_64.rpm')
@@ -33,6 +36,23 @@ or just check if a specific file is an RPM with:
33
36
 
34
37
  (note that loading a file that is not an RPM generates an exception)
35
38
 
39
+ ### Verifying RPM for corruption
40
+
41
+ You can verify if an RPM is corrupted after loading it with:
42
+
43
+ rpm.intact?
44
+
45
+ Note that this only verifies if the MD5 stored in RPM metadata corresponds to
46
+ the MD5 calculated over the content and metadata itself. It doesn't provide any
47
+ warranty that the packages has been _maliciously_ altered. For this, you need
48
+ to check package _signature_.
49
+
50
+ ### List of installed files
51
+
52
+ The list of installed files is returned as an array of absolute filenames with:
53
+
54
+ rpm.filenames
55
+
36
56
  ## Contributing
37
57
 
38
58
  1. Fork it
@@ -40,3 +60,7 @@ or just check if a specific file is an RPM with:
40
60
  3. Commit your changes (`git commit -am 'Add some feature'`)
41
61
  4. Push to the branch (`git push origin my-new-feature`)
42
62
  5. Create new Pull Request
63
+
64
+ ## Changelog
65
+
66
+ See [Changelog.md](Changelog.md)
data/TODO.md CHANGED
@@ -26,14 +26,20 @@
26
26
  * Remember that signature is not mandatory in RPM!
27
27
  * It looks like size is also a form of signature, in the sense that RPM uses
28
28
  the stored value to check actual size of header + payload (or did I
29
- understood wrong?).
29
+ understood wrong?) -> make integrity + auth verification check everything at
30
+ once?.
30
31
  * I18N ???
32
+ * Improve Index robustness against null values of store and entries, and for
33
+ missing tags -> decide what to return. Also improve robustness for invalid
34
+ types (cryptic metaprogramming errors are returned as of now).
31
35
 
32
36
  # Roadmap
33
37
 
34
- * Pick the entry type table. For each type, pick a meaningful header tag (like
35
- name, file list, etc.) to derive a feature to be implemented. At the end, all
36
- types should be correctly handled
38
+ * Pick the entry type table. For each type, pick a meaningful (mandatory may be
39
+ * easy - see below) header tag (like
40
+ name, file list, etc.) to derive a feature to be implemented. At the end,
41
+ (almost) all types should be correctly handled
37
42
  * Decide a sensible behaviour for missing mandatory tags. Then, pick all
38
43
  mandatory header tags and build a feature for them if not already covered
39
44
  with previous method.
45
+ * Decide how to handle optional tags (return nil?)
data/lib/rupert/parser.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rupert/rpm/lead'
2
- require 'rupert/rpm/signature'
2
+ require 'rupert/rpm/index'
3
+ require 'rupert/rpm/entry'
3
4
 
4
5
  module Rupert
5
6
  class Parser
@@ -11,49 +12,65 @@ module Rupert
11
12
  # TODO Fit to current design (i.e. no parsing in Lead c'tor?)
12
13
  lead = RPM::Lead.new(@raw_io)
13
14
 
14
- entry_count, store_size = parse_header
15
- entries = parse_entries(entry_count)
15
+ signature = signature_from(parse_index(@raw_io))
16
16
 
17
- store = parse_store(store_size)
18
- content = parse_content
17
+ # TODO I'd like to get rid of this duplication, but still don't know how.
18
+ # Ideally, raw signed content should be available from both archive and
19
+ # header, and concatenated to calculate checksum.
20
+ content = parse_content @raw_io
21
+ @raw_io.seek(-content.length, IO::SEEK_CUR)
19
22
 
20
- signature = RPM::Signature.new(RPM::Signature::Index.new(entries, store))
23
+ header = header_from(parse_index(@raw_io))
21
24
 
22
- RPM.new(lead, signature, content)
25
+ RPM.new(lead, signature, content, header)
23
26
  end
24
27
 
25
28
  private
26
29
 
27
- def parse_header
30
+ def header_from(index)
31
+ RPM::Header.new index
32
+ end
33
+
34
+ def signature_from(index)
35
+ RPM::Signature.new index
36
+ end
37
+
38
+ def parse_header(raw_io)
28
39
  header_size = 16
29
40
  header_format = "@8NN"
30
41
 
31
- @raw_io.read(header_size).unpack(header_format)
42
+ raw_io.read(header_size).unpack(header_format)
32
43
  end
33
44
 
34
- def parse_entries(count)
35
- entry_size = 16
36
- entry_format = "NNNN"
45
+ def parse_store(size, raw_io)
46
+ StringIO.new(raw_io.read(nearest_multiple(8, size)))
47
+ end
37
48
 
38
- entries = Hash.new
39
- count.times do
40
- tag, type, offset, count = @raw_io.read(entry_size).unpack(entry_format)
41
- entry = RPM::Signature::Entry.new tag, type, offset, count
42
- entries[entry.tag] = entry
49
+ def parse_content(raw_io)
50
+ raw_io.read.force_encoding(Encoding::ASCII_8BIT)
51
+ end
52
+
53
+ def parse_index(raw_io)
54
+ index = RPM::Index.new
55
+
56
+ entry_count, store_size = parse_header(raw_io)
57
+ entry_count.times do
58
+ index.add parse_entry(raw_io)
43
59
  end
44
60
 
45
- entries
46
- end
61
+ index.store = parse_store(store_size, raw_io)
47
62
 
48
- def parse_store(size)
49
- RPM::Signature::Store.new(StringIO.new(@raw_io.read(nearest_multiple(size, 8))))
63
+ index
50
64
  end
51
65
 
52
- def parse_content
53
- @raw_io.read
66
+ def parse_entry(raw_io)
67
+ entry_size = 16
68
+ entry_format = "NNNN"
69
+
70
+ RPM::Entry.new(*raw_io.read(entry_size).unpack(entry_format))
54
71
  end
55
72
 
56
- def nearest_multiple(size, modulo)
73
+ def nearest_multiple(modulo, size)
57
74
  (size / modulo.to_f).ceil * modulo
58
75
  end
59
76
  end
data/lib/rupert/rpm.rb CHANGED
@@ -2,6 +2,7 @@ require 'rupert/errors'
2
2
  require 'rupert/parser'
3
3
  require 'rupert/rpm/lead'
4
4
  require 'rupert/rpm/signature'
5
+ require 'rupert/rpm/header'
5
6
 
6
7
  require 'base64'
7
8
 
@@ -26,7 +27,7 @@ module Rupert
26
27
  # Tells whether given filename points to a valid RPM or not.
27
28
  #
28
29
  # @param filename [String] filename to inspect
29
- # @return `true` if file starts with the correct magic header
30
+ # @return +true+ if file starts with the correct magic header
30
31
  def rpm?(filename)
31
32
  Lead.new(File.open(filename, 'r')).rpm?
32
33
  end
@@ -35,37 +36,39 @@ module Rupert
35
36
  # Initialize the RPM object, given its components.
36
37
  #
37
38
  # This method is not intended to be used to instantiate RPM objects
38
- # directly. Instead, use Rupert::RPM::load for a more straightforward
39
+ # directly. Instead, use {Rupert::RPM.load} for a more straightforward
39
40
  # alternative.
40
41
  #
41
42
  # @param lead [Rupert::RPM::Lead] RPM lead section
42
- # @param signature [Rupert::RPM::Signature] RPM signature section
43
+ # @param signature [Rupert::RPM::Signature] RPM signature information
43
44
  # @param content [String] Raw content found after the signature structure
44
- def initialize(lead, signature, content)
45
+ # @param header [Rupert::RPM::Header] RPM header holding package metadata
46
+ def initialize(lead, signature, content, header)
45
47
  @lead = lead
46
48
  @signature = signature
47
49
  @content = content
50
+ @header = header
48
51
  end
49
52
 
50
53
  # RPM version used to encode the package.
51
54
  #
52
- # @return [String] the RPM version in `<major>.<minor>` format
55
+ # @return [String] the RPM version in +<major>.<minor>+ format
53
56
  def rpm_version
54
57
  @lead.rpm_version
55
58
  end
56
59
 
57
- # @return `true` if the RPM is of type binary, `false` otherwise
60
+ # @return +true+ if the RPM is of type binary, +false+ otherwise
58
61
  def binary?
59
62
  @lead.binary_type?
60
63
  end
61
64
 
62
- # @return `true` if the RPM is of type source, `false` otherwise
65
+ # @return +true+ if the RPM is of type source, +false+ otherwise
63
66
  def source?
64
67
  @lead.source_type?
65
68
  end
66
69
 
67
- # Which architecture the package was built for, e.g. `i386/x86_64` or
68
- # `arm`
70
+ # Which architecture the package was built for, e.g. +i386/x86_64+ or
71
+ # +arm+
69
72
  #
70
73
  # @return [String] package architecture name
71
74
  def rpm_arch
@@ -74,20 +77,20 @@ module Rupert
74
77
 
75
78
  # Full package name
76
79
  #
77
- # @return [String] package name in the form <name>-<version>-<rev>.<suffix>
80
+ # @return [String] package name in the form +<name>-<version>-<rev>.<suffix>+
78
81
  def name
79
- @lead.name
82
+ @header.name
80
83
  end
81
84
 
82
85
  # OS for which the package was built
83
86
  #
84
- # @return [String] as defined in /usr/lib/rpm/rpmrc under the canonical OS
87
+ # @return [String] as defined in _/usr/lib/rpm/rpmrc_ under the canonical OS
85
88
  # names section
86
89
  def os
87
90
  @lead.os
88
91
  end
89
92
 
90
- # @return `true` if the package is signed, `false` otherwise
93
+ # @return +true+ if the package is signed, +false+ otherwise
91
94
  def signed?
92
95
  @lead.signed?
93
96
  end
@@ -95,10 +98,10 @@ module Rupert
95
98
  # MD5 checksum stored in the package (base64 encoded). To be used to check
96
99
  # package integrity.
97
100
  #
98
- # NOTE: This is not the MD5 of the whole package; rather, the digest is
101
+ # *NOTE*: This is not the MD5 of the whole package; rather, the digest is
99
102
  # calculated over the header and payload, leaving out the lead and the
100
- # signature header. I.e., running `md5sum <myrpm>` won't held the same
101
- # result as `Rupert::RPM.load('<myrpm>').md5`.
103
+ # signature header. I.e., running +md5sum <myrpm>+ won't held the same
104
+ # result as +Rupert::RPM.load('<myrpm>').md5+.
102
105
  #
103
106
  # @return [String] Base64-encoded MD5 checksum of package's header and
104
107
  # payload, stored in the RPM itself
@@ -109,10 +112,36 @@ module Rupert
109
112
  # Verifies package integrity. Compares MD5 checksum stored in the package
110
113
  # with checksum calculated over header(s) and payload (archive).
111
114
  #
112
- # @return `true` if package is intact, `false` if package (either stored MD5 or
115
+ # @return +true+ if package is intact, +false+ if package (either stored MD5 or
113
116
  # payload) is corrupted
114
117
  def intact?
115
- @signature.verify_checksum(@content)
118
+ @signature.md5 == Digest::MD5.digest(@content)
119
+ end
120
+
121
+ # Package uncompressed size.
122
+ #
123
+ # This is the size (in bytes) of the uncompressed archive, or if you
124
+ # prefer, package's installed size.
125
+ #
126
+ # *NOTE*: if reading a package built with native +rpmbuild+, this number
127
+ # (which is stored in the RPM itself) might not be precise, as
128
+ # this[http://rpm5.org/community/rpm-devel/2689.html] thread explains.
129
+ #
130
+ # @return [Fixnum] package uncompressed size (bytes)
131
+ def uncompressed_size
132
+ @header.uncompressed_size
133
+ end
134
+
135
+ # List of installed files (full paths).
136
+ #
137
+ # @return [Array] array of +String+, with entries corresponding to
138
+ # absolute filenames
139
+ def filenames
140
+ @header.dirindexes.map { |idx|
141
+ @header.dirnames[idx]
142
+ }.zip(@header.basenames).map { |dir, name|
143
+ File.join(dir, name)
144
+ }
116
145
  end
117
146
  end
118
147
  end
@@ -0,0 +1,127 @@
1
+ module Rupert
2
+ class RPM
3
+ class Entry
4
+ # Initializes a new index entry.
5
+ #
6
+ # @param tag [String] 4 byte entry tag (semantic data type)
7
+ # @param type [String] 4 byte entry data format
8
+ # @param offset [String] 4 byte pointer to data in the index store
9
+ # @param count [String] 4 byte number of data items held by the entry
10
+ def initialize(tag, type, offset, count)
11
+ @tag, @type, @offset, @count = tag, type, offset, count
12
+ end
13
+
14
+ attr_accessor :tag
15
+
16
+ # Fetches referenced data from a store.
17
+ #
18
+ # An entry contains only information about a piece of data, but not the
19
+ # actual data. In essence, it behaves more or less like a pointer,
20
+ # which contains the address at which data is available.
21
+ #
22
+ # This method behaves exactly like pointer dereference, i.e. it returns
23
+ # the actual data at the address held by the entry itself. In addition,
24
+ # data is not returned in raw form; instead, it is returned in the
25
+ # format declared in the entry itself. The available RPM formats are the
26
+ # following:
27
+ #
28
+ # * NULL
29
+ # * CHAR
30
+ # * INT8
31
+ # * INT16
32
+ # * INT32
33
+ # * INT64 (not supported yet even in rpmlib?)
34
+ # * STRING
35
+ # * BIN
36
+ # * STRING_ARRAY
37
+ # * I18NSTRING
38
+ #
39
+ # which are in turn mapped in Ruby with:
40
+ #
41
+ # * +nil+
42
+ # * (+Array+ of) +String+ of length 1
43
+ # * (+Array+ of) +Fixnum+
44
+ # * (+Array+ of) +Fixnum+
45
+ # * (+Array+ of) +Fixnum+
46
+ # * (+Array+ of) +Fixnum+/+Bignum+
47
+ # * +String+ of arbitrary length
48
+ # * +String+ of arbitrary length, 8-bit ASCII encoded
49
+ # * +Array+ of +String+
50
+ # * +Array+ of +String+
51
+ #
52
+ # *NOTE*: The store is sought to retrieve data. Do not make any
53
+ # assumptions on IO's pointer state after method call. If you need to
54
+ # perform subsequent operations on the IO that require a particular
55
+ # cursor position, seek the IO to wanted position before performing
56
+ # the operation.
57
+ #
58
+ # @param store [IO] raw data store, represented by an IO object
59
+ # @return [Object] data referenced by this entry, in whatever format
60
+ # the entry prescribe
61
+ def resolve(store)
62
+ store.seek(@offset, IO::SEEK_SET)
63
+ read_and_convert(@type, store)
64
+ end
65
+
66
+ private
67
+
68
+ # :nodoc: Null byte used to indicate string termination
69
+ NULL_CHAR = "\x00".force_encoding(Encoding::ASCII_8BIT)
70
+
71
+ # :nodoc: Map of numerical entry types to parsing functions.
72
+ #
73
+ # NOTE: It turns out that distinguishing between strings and string
74
+ # arrays is irrelevant (at least with this implementation), since for
75
+ # every data type, if the count is 1 data is not wrapped in an array.
76
+ TYPE_MAP = {
77
+ 4 => :int32,
78
+ 6 => :string,
79
+ 7 => :binary,
80
+ 8 => :string
81
+ }
82
+
83
+ # :nodoc: Reads given type of data from given store
84
+ def read_and_convert(type, store)
85
+ method(TYPE_MAP[type]).call(store)
86
+ end
87
+
88
+ # :nodoc: Returns a list of integers, or a single element if count is 1
89
+ def int32(store)
90
+ first_or(array_of(lambda { one_int_32(store) }))
91
+ end
92
+
93
+ # :nodoc: Returns an array with given number of null-terminated strings,
94
+ # or a single string if count is 1
95
+ def string(store)
96
+ first_or(array_of(lambda { one_string(store) }))
97
+ end
98
+
99
+ def binary(store)
100
+ store.read(@count)
101
+ end
102
+
103
+ # :nodoc: Parses a single int32 from the store
104
+ def one_int_32(store)
105
+ store.read(4).unpack("N").first
106
+ end
107
+
108
+ # :nodoc: Parses a null-terminated string, without the trailing null
109
+ # character.
110
+ def one_string(store)
111
+ store.gets(NULL_CHAR).chomp(NULL_CHAR)
112
+ end
113
+
114
+ # :nodoc: Strips off array if it contains only one element
115
+ def first_or(ary)
116
+ ary.length == 1 ? ary.first : ary
117
+ end
118
+
119
+ # :nodoc: Builds an array of count elements parsed used given function
120
+ def array_of(parse_fun)
121
+ (1..@count).inject([]) { |ary|
122
+ ary << parse_fun.call
123
+ }
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,55 @@
1
+ module Rupert
2
+ class RPM
3
+ class Header
4
+ NAME_TAG = 1000.freeze
5
+ SIZE_TAG = 1009.freeze
6
+ DIRINDEXES_TAG = 1116.freeze
7
+ BASENAMES_TAG = 1117.freeze
8
+ DIRNAMES_TAG = 1118.freeze
9
+
10
+ # Creates a new header.
11
+ #
12
+ # @param index [Rupert::RPM::Index] index structure holding actual
13
+ # information
14
+ def initialize(index)
15
+ @index = index
16
+ end
17
+
18
+ # Package name.
19
+ #
20
+ # @return [String]
21
+ def name
22
+ @index.get(NAME_TAG)
23
+ end
24
+
25
+ # Package uncompressed size (bytes).
26
+ #
27
+ # @return [Fixnum]
28
+ def uncompressed_size
29
+ @index.get(SIZE_TAG)
30
+ end
31
+
32
+ # Package files basename list.
33
+ #
34
+ # @return [Array] of +String+
35
+ def basenames
36
+ @index.get(BASENAMES_TAG)
37
+ end
38
+
39
+ # Installed directory list.
40
+ #
41
+ # @return [Array] of +String+
42
+ def dirnames
43
+ @index.get(DIRNAMES_TAG)
44
+ end
45
+
46
+ # Map between basenames and relative directories.
47
+ #
48
+ # @return [Array] of +Fixnum+, where each number represents an index in
49
+ # the +dirnames+ array
50
+ def dirindexes
51
+ @index.get(DIRINDEXES_TAG)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ require 'rupert/rpm/entry'
2
+
3
+ module Rupert
4
+ class RPM
5
+ class Index
6
+ attr_writer :store
7
+
8
+ # Initializes a new signature index, given the header's entries and the
9
+ # store containing actual data.
10
+ #
11
+ # @param entries [Array] a list of
12
+ # {Rupert::RPM::Entry}, or optionally a
13
+ # single {Rupert::RPM::Entry} not included in
14
+ # any array
15
+ # @param store [IO] raw store containing data pointed by entries
16
+ def initialize(entries=[], store=nil)
17
+ @entries = index list_of entries
18
+ @store = store
19
+ end
20
+
21
+ # Retrieves data pointed by given tag.
22
+ #
23
+ # @param tag [Fixnum] data type
24
+ # @return [Object] data associated to given tag, with variable format
25
+ # depending on how it's stored (see
26
+ # {Rupert::RPM::Entry#resolve})
27
+ def get(tag)
28
+ @entries[tag].resolve(@store)
29
+ end
30
+
31
+ # Adds an entry to the index.
32
+ #
33
+ # @param entry [Rupert::RPM::Entry] new entry to add to the index
34
+ def add(entry)
35
+ @entries[entry.tag] = entry
36
+ end
37
+
38
+ private
39
+
40
+ # :nodoc: Force returned object to be an array
41
+ def list_of(maybe_an_array)
42
+ [maybe_an_array].flatten
43
+ end
44
+
45
+ # :nodoc: Given an array of entries, index them by tag
46
+ def index(entries)
47
+ Hash[entries.collect { |entry| [entry.tag, entry] }]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -5,7 +5,7 @@ module Rupert
5
5
  LEAD_LENGTH = 96.freeze # byte
6
6
 
7
7
  # The magic header that identifies an RPM beyond a shadow of a doubt, as
8
- # every good RPM starts with hex `ed ab ee db`.
8
+ # every good RPM starts with hex +ed ab ee db+.
9
9
  MAGIC = "\xed\xab\xee\xdb".force_encoding(Encoding::ASCII_8BIT).freeze
10
10
 
11
11
  # RPM of type binary
@@ -25,12 +25,12 @@ module Rupert
25
25
  # Only valid and recognized signature type
26
26
  SIGNATURE_TYPE_HEADER = 5.freeze
27
27
 
28
- # Chomps given IO, producing a `Lead` object and returning the remaining
28
+ # Chomps given IO, producing a {Lead} object and returning the remaining
29
29
  # part for subsequent processing.
30
30
  #
31
31
  # Lead data is expected to begin at IO start, so returned scrap is
32
32
  # basically the input IO without its first
33
- # `Rupert::RPM::Lead::LEAD_LENGTH` bytes.
33
+ # {Rupert::RPM::Lead::LEAD_LENGTH} bytes.
34
34
  #
35
35
  # @param io [IO] IO object containing lead data at its start, possibly
36
36
  # with additional bytes at the end
@@ -73,24 +73,24 @@ module Rupert
73
73
 
74
74
  # Tells if the file is recognized as an RPM or not
75
75
  #
76
- # @return `true` if magic number is found at lead's start, `false`
76
+ # @return +true+ if magic number is found at lead's start, +false+
77
77
  # otherwise
78
78
  def rpm?
79
79
  @magic == MAGIC
80
80
  end
81
81
 
82
- # @return `true` if lead reports RPM as of binary type
82
+ # @return +true+ if lead reports RPM as of binary type
83
83
  def binary_type?
84
84
  @type == TYPE_BINARY
85
85
  end
86
86
 
87
- # @return `true` if lead reports RPM as of source type
87
+ # @return +true+ if lead reports RPM as of source type
88
88
  def source_type?
89
89
  @type == TYPE_SOURCE
90
90
  end
91
91
 
92
92
  # The architecture the package was built for. A list of supported
93
- # architectures can be found in `/usr/lib/rpm/rpmrc` on RedHat based
93
+ # architectures can be found in _/usr/lib/rpm/rpmrc_ on RedHat based
94
94
  # systems.
95
95
  #
96
96
  # @return [String] a string representing the architecture name(s)
@@ -107,12 +107,12 @@ module Rupert
107
107
 
108
108
  # OS for which the package was built
109
109
  #
110
- # @return [String] OS canonical name as defined in `/usr/lib/rpm/rpmrc`
110
+ # @return [String] OS canonical name as defined in _/usr/lib/rpm/rpmrc_
111
111
  def os
112
112
  @@os_map[@osnum]
113
113
  end
114
114
 
115
- # @return `true` if the RPM is recognized as being signed, `false` otherwise
115
+ # @return +true+ if the RPM is recognized as being signed, +false+ otherwise
116
116
  def signed?
117
117
  @signature_type == SIGNATURE_TYPE_HEADER
118
118
  end
@@ -129,7 +129,7 @@ module Rupert
129
129
  private
130
130
 
131
131
  # :nodoc
132
- # The format string passed to `unpack` to parse the lead
132
+ # The format string passed to +unpack+ to parse the lead
133
133
  LEAD_FORMAT = "A4CCnnZ66nna16".freeze
134
134
 
135
135
  # :nodoc
@@ -1,5 +1,3 @@
1
- require 'rupert/rpm/signature/index'
2
-
3
1
  module Rupert
4
2
  class RPM
5
3
  class Signature
@@ -21,23 +19,6 @@ module Rupert
21
19
  def md5
22
20
  @index.get MD5_TAG
23
21
  end
24
-
25
- # Verifies if stored MD5 checksum corresponds to digest calculated over
26
- # given content.
27
- #
28
- # @return `true` if stored MD5 checksum corresponds to MD5 calculated
29
- # over given content, `false` otherwise
30
- def verify_checksum(content)
31
- md5 == md5_checksum(content)
32
- end
33
-
34
- private
35
-
36
- # :nodoc
37
- # MD5 checksum of given string
38
- def md5_checksum(str)
39
- Digest::MD5.digest(str)
40
- end
41
22
  end
42
23
  end
43
24
  end
@@ -1,3 +1,3 @@
1
1
  module Rupert
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,20 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDQDCCAiigAwIBAgIBADANBgkqhkiG9w0BAQUFADBGMRgwFgYDVQQDDA96YW5l
3
+ bGxhLnN0ZWZhbm8xFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk
4
+ ARkWA2NvbTAeFw0xMzA4MDcxOTA2MDZaFw0xNDA4MDcxOTA2MDZaMEYxGDAWBgNV
5
+ BAMMD3phbmVsbGEuc3RlZmFubzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYK
6
+ CZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
7
+ 2JCnJCnjjs62MR/Tw/4WgSG42ruiCEqXV1ECMsXymHPE8xyHkYAwLXvBRzOkZ/IA
8
+ puJ1XhScduMRcUuE0ZPA5N2HBZI0WsmyyNTYBjOob8m0SNInoRZfIMloj3D8QzB7
9
+ /6G5HLMWNx60JEpIDgfXvIuSRKNKQ0/0+/G/H4COgj72pd3F4dYltvx+mSwPRq7Q
10
+ MdZsK3T5Q3d4eLBY1VSlJpq0wkwdEWTXAhR0Mfmbn1D8m9IhJfubgXuXVBY4OPO8
11
+ KAF/wWqTkzA6guVQlSKdZR4vwms7OpeFkotnivBKa6JwUQSXO8AZEyy53V8cSYDu
12
+ dbaFi53YbEwOWSMQnW8/kQIDAQABozkwNzAdBgNVHQ4EFgQUcBKkmJAvSTKfDf7z
13
+ LEu1wE+Rk+swCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQAD
14
+ ggEBAMeqfk1l4Y0iZ8jNiu0afcQ60DVqBkRtrT/rsZEqGsdOw11bntOE4yWpo4Kd
15
+ Y0C/kYrVQ/mIN7IGKbCSjES3aYdQftV9SRW77FA25m2KXRbnEYtJDUX35gAqSdRY
16
+ 9IiYivsMq2dr70eKPNFrFOwWvmwhcGyEG8EDvYoXWllke7RGz1Dn/AZx6jPnShO+
17
+ 0ru4OXsM9++md3sGXIugEFNygvo2/1yQoTe6+XiBocS+pWsJd6JZBfkxPRT4Dz4H
18
+ RigBD0E3/t/ABjCXkmqwp5gnAZmP8JiVUkn8rp5E0FXvC8T7nsPs2TW/TAmUV6rN
19
+ hK25FX8YWgT9fD9y3PpWjiYcrCo=
20
+ -----END CERTIFICATE-----
data/rupert.gemspec CHANGED
@@ -31,4 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.description = %s{
32
32
  Rupert allows to manipulate RPM files independently from availability of rpmlib.
33
33
  }
34
+
35
+ spec.signing_key = File.expand_path "~/.ssh/rubygems-stefanozanella.key"
36
+ spec.cert_chain = ["rubygems-stefanozanella.crt"]
34
37
  end
@@ -20,6 +20,22 @@ describe Rupert::RPM do
20
20
  }.must_raise Rupert::NotAnRPM
21
21
  end
22
22
 
23
+ it "tells the package's name" do
24
+ rpm.name.must_equal "rpm"
25
+ end
26
+
27
+ it "tells the package uncompressed size (in bytes)" do
28
+ rpm.uncompressed_size.must_equal 2031240
29
+ end
30
+
31
+ it "tells the full name of the files contained in the package" do
32
+ rpm.filenames.length.must_equal 0x8c
33
+
34
+ rpm.filenames.must_include "/bin/rpm"
35
+ rpm.filenames.must_include "/usr/share/doc/rpm-4.8.0/ChangeLog.bz2"
36
+ rpm.filenames.must_include "/var/lib/rpm/__db.009"
37
+ end
38
+
23
39
  it "knows which version of RPM the file is" do
24
40
  rpm.rpm_version.must_equal "3.0"
25
41
  end
@@ -29,15 +45,11 @@ describe Rupert::RPM do
29
45
  end
30
46
 
31
47
  it "tells if the file is a binary or source RPM" do
32
- assert binary_rpm.binary?, "failed to recognize RPM as binary"
33
- refute binary_rpm.source?, "RPM misrecognized as of source type"
48
+ binary_rpm.must_be :binary?
49
+ binary_rpm.wont_be :source?
34
50
 
35
- assert source_rpm.source?, "failed to recognize RPM as source"
36
- refute source_rpm.binary?, "RPM misrecognized as of binary type"
37
- end
38
-
39
- it "tells the package's name" do
40
- rpm.name.must_equal "rpm-4.8.0-32.el6"
51
+ source_rpm.must_be :source?
52
+ source_rpm.wont_be :binary?
41
53
  end
42
54
 
43
55
  it "tells the operating system for which the package has been built" do
@@ -45,6 +57,6 @@ describe Rupert::RPM do
45
57
  end
46
58
 
47
59
  it "tells if package is signed" do
48
- assert rpm.signed?, "expected the package to be signed, but it was not"
60
+ rpm.must_be :signed?
49
61
  end
50
62
  end
data/test/test_helper.rb CHANGED
@@ -24,6 +24,19 @@ def ascii(string)
24
24
  string.force_encoding(Encoding::ASCII_8BIT)
25
25
  end
26
26
 
27
+ # Returns a random binary string (8-bit ASCII encoding) of given length
28
+ def random_ascii(size)
29
+ require 'securerandom'
30
+ ascii(SecureRandom.random_bytes(size))
31
+ end
32
+
33
+ # Returns the (strict) base64 representation of given string
34
+ def base64(string)
35
+ require 'base64'
36
+
37
+ Base64.strict_encode64(string)
38
+ end
39
+
27
40
  # Pads a string with nulls to fill given length
28
41
  def pad(string, length)
29
42
  ("%-#{length}.#{length}s" % string).gsub(" ", "\x00")
@@ -0,0 +1,48 @@
1
+ require 'test_helper'
2
+
3
+ describe Rupert::RPM::Entry do
4
+ describe "fetching binary data" do
5
+ let(:store) { io(ascii("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")) }
6
+ let(:entry) { Rupert::RPM::Entry.new(nil, 7, 4, 5) }
7
+
8
+ it "fetches binary data which length and position is given by the entry" do
9
+ entry.resolve(store).must_equal ascii("\x04\x05\x06\x07\x08")
10
+ end
11
+ end
12
+
13
+ describe "fetching a single string" do
14
+ let(:store) { io(ascii("\x00\x01Null-terminated string.\x00LOLTHISCRAP")) }
15
+ let(:entry) { Rupert::RPM::Entry.new(nil, 6, 2, 1) }
16
+
17
+ it "retrieves all chars starting from entry offset until it encounters `\x00`" do
18
+ entry.resolve(store).must_equal "Null-terminated string."
19
+ end
20
+ end
21
+
22
+ describe "fetching an array of strings" do
23
+ let(:store) { io(ascii("One\x00Two\x00Three")) }
24
+ let(:entry) { Rupert::RPM::Entry.new(nil, 8, 0, 3) }
25
+
26
+ it "retrieves all chars starting from entry offset until it encounters `\x00`" do
27
+ entry.resolve(store).must_equal [ "One", "Two", "Three" ]
28
+ end
29
+ end
30
+
31
+ describe "fetching a single 32-bit integer" do
32
+ let(:store) { io(ascii("Somecrap\x00\x12\x34\x56\x78Tail content")) }
33
+ let(:entry) { Rupert::RPM::Entry.new(nil, 4, 9, 1) }
34
+
35
+ it "successfully retrieves a single 32-bit integer" do
36
+ entry.resolve(store).must_equal 0x12345678
37
+ end
38
+ end
39
+
40
+ describe "fetching an array of 32-bit integer" do
41
+ let(:store) { io(ascii("\x11\x11\x11\x11\x22\x22\x22\x22\x33\x33\x33\x33")) }
42
+ let(:entry) { Rupert::RPM::Entry.new(nil, 4, 0, 3) }
43
+
44
+ it "successfully retrieves the given number of 32-bit integers" do
45
+ entry.resolve(store).must_equal [ 0x11111111, 0x22222222, 0x33333333 ]
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ describe Rupert::RPM::Header do
4
+ let(:name_tag) { Rupert::RPM::Header::NAME_TAG }
5
+ let(:size_tag) { Rupert::RPM::Header::SIZE_TAG }
6
+ let(:basenames_tag) { Rupert::RPM::Header::BASENAMES_TAG }
7
+ let(:dirnames_tag) { Rupert::RPM::Header::DIRNAMES_TAG }
8
+ let(:dirindexes_tag) { Rupert::RPM::Header::DIRINDEXES_TAG }
9
+
10
+ let(:index) { mock }
11
+ let(:header) { Rupert::RPM::Header.new index }
12
+
13
+ it "maps RPM name stored in the header" do
14
+ index.expects(:get).once.with(name_tag)
15
+
16
+ header.name
17
+ end
18
+
19
+ it "maps RPM uncompressed size stored in the header" do
20
+ index.expects(:get).once.with(size_tag)
21
+
22
+ header.uncompressed_size
23
+ end
24
+
25
+ it "maps RPM basenames stored in the header" do
26
+ index.expects(:get).once.with(basenames_tag)
27
+
28
+ header.basenames
29
+ end
30
+
31
+ it "maps RPM dirnames stored in the header" do
32
+ index.expects(:get).once.with(dirnames_tag)
33
+
34
+ header.dirnames
35
+ end
36
+
37
+ it "maps RPM dirindexes stored in the header" do
38
+ index.expects(:get).once.with(dirindexes_tag)
39
+
40
+ header.dirindexes
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ describe Rupert::RPM::Index do
4
+ let(:entry) { Rupert::RPM::Entry.new(3145, nil, nil, nil) }
5
+ let(:store) { mock }
6
+ let(:index) { Rupert::RPM::Index.new(entry, store) }
7
+
8
+ it "retrieves data associated to a specific tag" do
9
+ entry.expects(:resolve).once.with(store)
10
+
11
+ index.get(3145)
12
+ end
13
+ end
@@ -4,22 +4,10 @@ describe Rupert::RPM::Signature do
4
4
  let(:md5_signature_tag) { Rupert::RPM::Signature::MD5_TAG }
5
5
  let(:index) { mock }
6
6
  let(:signature) { Rupert::RPM::Signature.new(index) }
7
- let(:pristine_content) { ascii("\x01\x02\x03\x04") }
8
- let(:corrupted_content) { ascii("\xf4\x04\x57\x1e") }
9
7
 
10
- it "fetches the MD5 from its index" do
11
- index.expects(:get).once.with(md5_signature_tag)
8
+ it "maps the MD5 stored in the index" do
9
+ index.expects(:get).once.with(md5_signature_tag)
12
10
 
13
11
  signature.md5
14
12
  end
15
-
16
- it "correctly verifies integrity of pristine and corrupted packages" do
17
- index.stubs(:get).returns(md5(pristine_content))
18
-
19
- assert signature.verify_checksum(pristine_content),
20
- "expected pristine content to be verified correctly, but it was not"
21
-
22
- refute signature.verify_checksum(corrupted_content),
23
- "expected corrupted content not to be verified correctly, but it was"
24
- end
25
13
  end
@@ -1,19 +1,47 @@
1
1
  require 'test_helper'
2
2
 
3
3
  describe Rupert::RPM do
4
- let(:signature) { mock }
5
- let(:rpm) { Rupert::RPM.new(nil, signature, signed_content) }
6
- let(:signed_content) { ascii("\x01\x02\x03\x04") }
4
+ let(:signature) { mock }
5
+ let(:header) { mock }
6
+ let(:rpm) { Rupert::RPM.new(nil, signature, signed_content, header) }
7
+ let(:corrupted_rpm) { Rupert::RPM.new(nil, signature, corrupted_content, header) }
8
+ let(:signed_content) { ascii("\x01\x02\x03\x04") }
9
+ let(:corrupted_content) { ascii("\xf4\x04\x57\x1e") }
7
10
 
8
- it "exposes the MD5 digest held by the signature" do
9
- signature.expects(:md5).once.returns("abc")
11
+ it "exposes MD5 checksum in base64 encoding" do
12
+ random_md5 = random_ascii(128)
13
+ signature.stubs(:md5).returns(random_md5)
10
14
 
11
- rpm.md5
15
+ rpm.md5.must_equal base64(random_md5)
12
16
  end
13
17
 
14
- it "asks the signature to verify content integrity" do
15
- signature.expects(:verify_checksum).once.with(signed_content)
18
+ it "correctly verifies integrity of pristine and corrupted packages" do
19
+ signature.stubs(:md5).returns(md5(signed_content))
16
20
 
17
- rpm.intact?
21
+ assert rpm.intact?,
22
+ "expected RPM to be intact, but it wasn't"
23
+
24
+ refute corrupted_rpm.intact?,
25
+ "expected RPM not to be intact, but it was"
26
+ end
27
+
28
+ it "exposes RPM name stored in the header" do
29
+ header.stubs(:name).returns("package-name")
30
+
31
+ rpm.name.must_equal("package-name")
32
+ end
33
+
34
+ it "exposes RPM uncompressed size stored in the header" do
35
+ header.stubs(:uncompressed_size).returns(1234)
36
+
37
+ rpm.uncompressed_size.must_equal 1234
38
+ end
39
+
40
+ it "exposes RPM basenames stored in the header" do
41
+ header.stubs(:basenames).returns(["file1", "file1", "file2"])
42
+ header.stubs(:dirnames).returns(["/dir1", "/dir2"])
43
+ header.stubs(:dirindexes).returns([0, 1, 0])
44
+
45
+ rpm.filenames.must_equal [ "/dir1/file1", "/dir2/file1", "/dir1/file2" ]
18
46
  end
19
47
  end
metadata CHANGED
@@ -1,14 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rupert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefano Zanella
8
8
  autorequire:
9
9
  bindir: bin
10
- cert_chain: []
11
- date: 2013-08-01 00:00:00.000000000 Z
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDQDCCAiigAwIBAgIBADANBgkqhkiG9w0BAQUFADBGMRgwFgYDVQQDDA96YW5l
14
+ bGxhLnN0ZWZhbm8xFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk
15
+ ARkWA2NvbTAeFw0xMzA4MDcxOTA2MDZaFw0xNDA4MDcxOTA2MDZaMEYxGDAWBgNV
16
+ BAMMD3phbmVsbGEuc3RlZmFubzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYK
17
+ CZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
18
+ 2JCnJCnjjs62MR/Tw/4WgSG42ruiCEqXV1ECMsXymHPE8xyHkYAwLXvBRzOkZ/IA
19
+ puJ1XhScduMRcUuE0ZPA5N2HBZI0WsmyyNTYBjOob8m0SNInoRZfIMloj3D8QzB7
20
+ /6G5HLMWNx60JEpIDgfXvIuSRKNKQ0/0+/G/H4COgj72pd3F4dYltvx+mSwPRq7Q
21
+ MdZsK3T5Q3d4eLBY1VSlJpq0wkwdEWTXAhR0Mfmbn1D8m9IhJfubgXuXVBY4OPO8
22
+ KAF/wWqTkzA6guVQlSKdZR4vwms7OpeFkotnivBKa6JwUQSXO8AZEyy53V8cSYDu
23
+ dbaFi53YbEwOWSMQnW8/kQIDAQABozkwNzAdBgNVHQ4EFgQUcBKkmJAvSTKfDf7z
24
+ LEu1wE+Rk+swCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQAD
25
+ ggEBAMeqfk1l4Y0iZ8jNiu0afcQ60DVqBkRtrT/rsZEqGsdOw11bntOE4yWpo4Kd
26
+ Y0C/kYrVQ/mIN7IGKbCSjES3aYdQftV9SRW77FA25m2KXRbnEYtJDUX35gAqSdRY
27
+ 9IiYivsMq2dr70eKPNFrFOwWvmwhcGyEG8EDvYoXWllke7RGz1Dn/AZx6jPnShO+
28
+ 0ru4OXsM9++md3sGXIugEFNygvo2/1yQoTe6+XiBocS+pWsJd6JZBfkxPRT4Dz4H
29
+ RigBD0E3/t/ABjCXkmqwp5gnAZmP8JiVUkn8rp5E0FXvC8T7nsPs2TW/TAmUV6rN
30
+ hK25FX8YWgT9fD9y3PpWjiYcrCo=
31
+ -----END CERTIFICATE-----
32
+ date: 2013-08-07 00:00:00.000000000 Z
12
33
  dependencies:
13
34
  - !ruby/object:Gem::Dependency
14
35
  name: bundler
@@ -134,6 +155,7 @@ files:
134
155
  - .gitignore
135
156
  - .ruby-version
136
157
  - .travis.yml
158
+ - Changelog.md
137
159
  - Gemfile
138
160
  - LICENSE.txt
139
161
  - README.md
@@ -143,12 +165,13 @@ files:
143
165
  - lib/rupert/errors.rb
144
166
  - lib/rupert/parser.rb
145
167
  - lib/rupert/rpm.rb
168
+ - lib/rupert/rpm/entry.rb
169
+ - lib/rupert/rpm/header.rb
170
+ - lib/rupert/rpm/index.rb
146
171
  - lib/rupert/rpm/lead.rb
147
172
  - lib/rupert/rpm/signature.rb
148
- - lib/rupert/rpm/signature/entry.rb
149
- - lib/rupert/rpm/signature/index.rb
150
- - lib/rupert/rpm/signature/store.rb
151
173
  - lib/rupert/version.rb
174
+ - rubygems-stefanozanella.crt
152
175
  - rupert.gemspec
153
176
  - test/end_to_end/rpm_signature_test.rb
154
177
  - test/end_to_end/rpm_test.rb
@@ -160,9 +183,10 @@ files:
160
183
  - test/fixtures/rpm-libs-4.8.0-32.el6.x86_64.rpm
161
184
  - test/fixtures/rpmdevtools-7.5-2.el6.noarch.rpm
162
185
  - test/test_helper.rb
186
+ - test/unit/rpm/entry_test.rb
187
+ - test/unit/rpm/header_test.rb
188
+ - test/unit/rpm/index_test.rb
163
189
  - test/unit/rpm/lead_test.rb
164
- - test/unit/rpm/signature/index_test.rb
165
- - test/unit/rpm/signature/store_test.rb
166
190
  - test/unit/rpm/signature_test.rb
167
191
  - test/unit/rpm_test.rb
168
192
  homepage: ''
@@ -201,8 +225,9 @@ test_files:
201
225
  - test/fixtures/rpm-libs-4.8.0-32.el6.x86_64.rpm
202
226
  - test/fixtures/rpmdevtools-7.5-2.el6.noarch.rpm
203
227
  - test/test_helper.rb
228
+ - test/unit/rpm/entry_test.rb
229
+ - test/unit/rpm/header_test.rb
230
+ - test/unit/rpm/index_test.rb
204
231
  - test/unit/rpm/lead_test.rb
205
- - test/unit/rpm/signature/index_test.rb
206
- - test/unit/rpm/signature/store_test.rb
207
232
  - test/unit/rpm/signature_test.rb
208
233
  - test/unit/rpm_test.rb
metadata.gz.sig ADDED
Binary file
@@ -1,19 +0,0 @@
1
- module Rupert
2
- class RPM
3
- class Signature
4
- class Entry
5
- # Initializes a new index entry.
6
- #
7
- # @param tag [String] 4 byte entry tag (semantic data type)
8
- # @param type [String] 4 byte entry data format
9
- # @param offset [String] 4 byte pointer to data in the index store
10
- # @param count [String] 4 byte number of data items held by the entry
11
- def initialize(tag, type, offset, count)
12
- @tag, @type, @offset, @count = tag, type, offset, count
13
- end
14
-
15
- attr_accessor :tag, :type, :offset, :count
16
- end
17
- end
18
- end
19
- end
@@ -1,32 +0,0 @@
1
- require 'rupert/rpm/signature/entry'
2
- require 'rupert/rpm/signature/store'
3
-
4
- module Rupert
5
- class RPM
6
- class Signature
7
- class Index
8
- # Initializes a new signature index, given the header's entries and the
9
- # store containing actual data.
10
- #
11
- # @param entries [Hash] a map of
12
- # Rupert::RPM::Signature::Entry, indexed by tag
13
- # @param store [Rupert::RPM::Signature::Store] store containing
14
- # data pointed by entries
15
- def initialize(entries, store)
16
- @entries = entries
17
- @store = store
18
- end
19
-
20
- # Retrieves data pointed by given tag.
21
- #
22
- # @param tag [Fixnum] data type
23
- # @return [Object] data associated to given tag, with variable format
24
- # depending on how it's stored
25
- def get(tag)
26
- entry = @entries[tag]
27
- @store.fetch(entry)
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,35 +0,0 @@
1
- module Rupert
2
- class RPM
3
- class Signature
4
- # Package information is mostly contained in headers. Headers are composed
5
- # of an index and a store.
6
- # The (raw) store holds semantic RPM information in an undefined
7
- # order and without any structure (i.e. by concatenating all pieces
8
- # together). Addressing of resources in the store is handled by header
9
- # entries, which define data format, position and size. Responsibility
10
- # of the store is to take care of extracting these pieces given proper
11
- # addressing information.
12
- class Store
13
- # Creates a new store by wrapping given chunck of raw data into a Store
14
- # object.
15
- #
16
- # @param io [IO] raw binary data carved from RPM header. Must be an IO
17
- # containing only store's data (i.e. entry addresses are considered
18
- # relative to 0)
19
- def initialize(io)
20
- @io = io
21
- end
22
-
23
- # Fetches data pointed by given entry.
24
- #
25
- # @param entry [Rupert::RPM::Signature::Entry] entry containing address
26
- # and type of needed information
27
- # @return [String] binary string containing asked data
28
- def fetch(entry)
29
- @io.seek(entry.offset, IO::SEEK_SET)
30
- @io.read(entry.count)
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,14 +0,0 @@
1
- require 'test_helper'
2
-
3
- describe Rupert::RPM::Signature::Index do
4
- let(:entry) { Rupert::RPM::Signature::Entry.new(1234, 7, 567, 890) }
5
- let(:entries) { { entry.tag => entry } }
6
- let(:store) { mock }
7
- let(:index) { Rupert::RPM::Signature::Index.new(entries, store) }
8
-
9
- it "retrieves binary data marked with a specific tag from the store" do
10
- store.expects(:fetch).once.with(entry)
11
-
12
- index.get(entry.tag)
13
- end
14
- end
@@ -1,12 +0,0 @@
1
- require 'test_helper'
2
-
3
- describe Rupert::RPM::Signature::Store do
4
- let(:raw_io) { io(ascii("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")) }
5
- let(:entry) { Rupert::RPM::Signature::Entry.new(nil, 7, 4, 5) }
6
- let(:store) { Rupert::RPM::Signature::Store.new(raw_io) }
7
-
8
- it "fetches a raw chunck of data given the entry that points to it" do
9
- data = store.fetch(entry)
10
- data.must_equal ascii("\x04\x05\x06\x07\x08")
11
- end
12
- end