rupert 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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