| Class | Gem::Package::TarInput |
| In: |
lib/rubygems/package/tar_input.rb
|
| Parent: | Object |
++
Copyright (C) 2004 Mauricio Julio Fernández Pradier See LICENSE.txt for additional licensing information.
| metadata | [R] |
# File lib/rubygems/package/tar_input.rb, line 23
23: def initialize(io, security_policy = nil)
24: @io = io
25: @tarreader = Gem::Package::TarReader.new @io
26: has_meta = false
27:
28: data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
29: dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
30:
31: @tarreader.each do |entry|
32: case entry.full_name
33: when "metadata"
34: @metadata = load_gemspec entry.read
35: has_meta = true
36: when "metadata.gz"
37: begin
38: # if we have a security_policy, then pre-read the metadata file
39: # and calculate it's digest
40: sio = nil
41: if security_policy
42: Gem.ensure_ssl_available
43: sio = StringIO.new(entry.read)
44: meta_dgst = dgst_algo.digest(sio.string)
45: sio.rewind
46: end
47:
48: gzis = Zlib::GzipReader.new(sio || entry)
49: # YAML wants an instance of IO
50: @metadata = load_gemspec(gzis)
51: has_meta = true
52: ensure
53: gzis.close unless gzis.nil?
54: end
55: when 'metadata.gz.sig'
56: meta_sig = entry.read
57: when 'data.tar.gz.sig'
58: data_sig = entry.read
59: when 'data.tar.gz'
60: if security_policy
61: Gem.ensure_ssl_available
62: data_dgst = dgst_algo.digest(entry.read)
63: end
64: end
65: end
66:
67: if security_policy then
68: Gem.ensure_ssl_available
69:
70: # map trust policy from string to actual class (or a serialized YAML
71: # file, if that exists)
72: if String === security_policy then
73: if Gem::Security::Policies.key? security_policy then
74: # load one of the pre-defined security policies
75: security_policy = Gem::Security::Policies[security_policy]
76: elsif File.exist? security_policy then
77: # FIXME: this doesn't work yet
78: security_policy = YAML.load File.read(security_policy)
79: else
80: raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
81: end
82: end
83:
84: if data_sig && data_dgst && meta_sig && meta_dgst then
85: # the user has a trust policy, and we have a signed gem
86: # file, so use the trust policy to verify the gem signature
87:
88: begin
89: security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
90: rescue Exception => e
91: raise "Couldn't verify data signature: #{e}"
92: end
93:
94: begin
95: security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
96: rescue Exception => e
97: raise "Couldn't verify metadata signature: #{e}"
98: end
99: elsif security_policy.only_signed
100: raise Gem::Exception, "Unsigned gem"
101: else
102: # FIXME: should display warning here (trust policy, but
103: # either unsigned or badly signed gem file)
104: end
105: end
106:
107: @tarreader.rewind
108: @fileops = Gem::FileOperations.new
109:
110: raise Gem::Package::FormatError, "No metadata found!" unless has_meta
111: end
# File lib/rubygems/package/tar_input.rb, line 15
15: def self.open(io, security_policy = nil, &block)
16: is = new io, security_policy
17:
18: yield is
19: ensure
20: is.close if is
21: end
# File lib/rubygems/package/tar_input.rb, line 113
113: def close
114: @io.close
115: @tarreader.close
116: end
# File lib/rubygems/package/tar_input.rb, line 118
118: def each(&block)
119: @tarreader.each do |entry|
120: next unless entry.full_name == "data.tar.gz"
121: is = zipped_stream entry
122:
123: begin
124: Gem::Package::TarReader.new is do |inner|
125: inner.each(&block)
126: end
127: ensure
128: is.close if is
129: end
130: end
131:
132: @tarreader.rewind
133: end
# File lib/rubygems/package/tar_input.rb, line 135
135: def extract_entry(destdir, entry, expected_md5sum = nil)
136: if entry.directory? then
137: dest = File.join destdir, entry.full_name
138:
139: if File.directory? dest then
140: @fileops.chmod entry.header.mode, dest, :verbose => false
141: else
142: @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
143: end
144:
145: fsync_dir dest
146: fsync_dir File.join(dest, "..")
147:
148: return
149: end
150:
151: # it's a file
152: md5 = Digest::MD5.new if expected_md5sum
153: destdir = File.join destdir, File.dirname(entry.full_name)
154: @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
155: destfile = File.join destdir, File.basename(entry.full_name)
156: @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
157:
158: open destfile, "wb", entry.header.mode do |os|
159: loop do
160: data = entry.read 4096
161: break unless data
162: # HACK shouldn't we check the MD5 before writing to disk?
163: md5 << data if expected_md5sum
164: os.write(data)
165: end
166:
167: os.fsync
168: end
169:
170: @fileops.chmod entry.header.mode, destfile, :verbose => false
171: fsync_dir File.dirname(destfile)
172: fsync_dir File.join(File.dirname(destfile), "..")
173:
174: if expected_md5sum && expected_md5sum != md5.hexdigest then
175: raise Gem::Package::BadCheckSum
176: end
177: end
Attempt to YAML-load a gemspec from the given io parameter. Return nil if it fails.
# File lib/rubygems/package/tar_input.rb, line 181
181: def load_gemspec(io)
182: Gem::Specification.from_yaml io
183: rescue Gem::Exception
184: nil
185: end
Return an IO stream for the zipped entry.
NOTE: Originally this method used two approaches, Return a GZipReader directly, or read the GZipReader into a string and return a StringIO on the string. The string IO approach was used for versions of ZLib before 1.2.1 to avoid buffer errors on windows machines. Then we found that errors happened with 1.2.1 as well, so we changed the condition. Then we discovered errors occurred with versions as late as 1.2.3. At this point (after some benchmarking to show we weren‘t seriously crippling the unpacking speed) we threw our hands in the air and declared that this method would use the String IO approach on all platforms at all times. And that‘s the way it is.
# File lib/rubygems/package/tar_input.rb, line 201
201: def zipped_stream(entry)
202: if defined? Rubinius then
203: zis = Zlib::GzipReader.new entry
204: dis = zis.read
205: is = StringIO.new(dis)
206: else
207: # This is Jamis Buck's Zlib workaround for some unknown issue
208: entry.read(10) # skip the gzip header
209: zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
210: is = StringIO.new(zis.inflate(entry.read))
211: end
212: ensure
213: zis.finish if zis
214: end