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.

Methods

close   each   extract_entry   load_gemspec   new   open   zipped_stream  

Included Modules

Gem::Package::FSyncDir Enumerable

Attributes

metadata  [R] 

Public Class methods

[Source]

     # 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

[Source]

    # 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

Public Instance methods

[Source]

     # File lib/rubygems/package/tar_input.rb, line 113
113:   def close
114:     @io.close
115:     @tarreader.close
116:   end

[Source]

     # 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

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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

[Validate]