Class Gem::Package::TarInput
In: lib/rubygems/package/tar_input.rb
Parent: Object

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 27
 27:   def initialize(io, security_policy = nil)
 28:     @io = io
 29:     @tarreader = Gem::Package::TarReader.new @io
 30:     has_meta = false
 31: 
 32:     data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
 33:     dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
 34: 
 35:     @tarreader.each do |entry|
 36:       case entry.full_name
 37:       when "metadata"
 38:         @metadata = load_gemspec entry.read
 39:         has_meta = true
 40:       when "metadata.gz"
 41:         begin
 42:           # if we have a security_policy, then pre-read the metadata file
 43:           # and calculate it's digest
 44:           sio = nil
 45:           if security_policy
 46:             Gem.ensure_ssl_available
 47:             sio = StringIO.new(entry.read)
 48:             meta_dgst = dgst_algo.digest(sio.string)
 49:             sio.rewind
 50:           end
 51: 
 52:           # TODO use Gem.gunzip
 53:           gzis = Zlib::GzipReader.new(sio || entry)
 54:           # YAML wants an instance of IO
 55:           @metadata = load_gemspec(gzis)
 56:           has_meta = true
 57:         ensure
 58:           gzis.close unless gzis.nil?
 59:         end
 60:       when 'metadata.gz.sig'
 61:         meta_sig = entry.read
 62:       when 'data.tar.gz.sig'
 63:         data_sig = entry.read
 64:       when 'data.tar.gz'
 65:         if security_policy
 66:           Gem.ensure_ssl_available
 67:           data_dgst = dgst_algo.digest(entry.read)
 68:         end
 69:       end
 70:     end
 71: 
 72:     if security_policy then
 73:       Gem.ensure_ssl_available
 74: 
 75:       # map trust policy from string to actual class (or a serialized YAML
 76:       # file, if that exists)
 77:       if String === security_policy then
 78:         if Gem::Security::Policies.key? security_policy then
 79:           # load one of the pre-defined security policies
 80:           security_policy = Gem::Security::Policies[security_policy]
 81:         elsif File.exist? security_policy then
 82:           # FIXME: this doesn't work yet
 83:           security_policy = YAML.load File.read(security_policy)
 84:         else
 85:           raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
 86:         end
 87:       end
 88: 
 89:       if data_sig && data_dgst && meta_sig && meta_dgst then
 90:         # the user has a trust policy, and we have a signed gem
 91:         # file, so use the trust policy to verify the gem signature
 92: 
 93:         begin
 94:           security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
 95:         rescue Exception => e
 96:           raise "Couldn't verify data signature: #{e}"
 97:         end
 98: 
 99:         begin
100:           security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
101:         rescue Exception => e
102:           raise "Couldn't verify metadata signature: #{e}"
103:         end
104:       elsif security_policy.only_signed
105:         raise Gem::Exception, "Unsigned gem"
106:       else
107:         # FIXME: should display warning here (trust policy, but
108:         # either unsigned or badly signed gem file)
109:       end
110:     end
111: 
112:     @tarreader.rewind
113:     @fileops = Gem::FileOperations.new
114: 
115:     unless has_meta then
116:       path = io.path if io.respond_to? :path
117:       error = Gem::Package::FormatError.new 'no metadata found', path
118:       raise error
119:     end
120:   end

[Source]

    # File lib/rubygems/package/tar_input.rb, line 19
19:   def self.open(io, security_policy = nil,  &block)
20:     is = new io, security_policy
21: 
22:     yield is
23:   ensure
24:     is.close if is
25:   end

Public Instance methods

[Source]

     # File lib/rubygems/package/tar_input.rb, line 122
122:   def close
123:     @io.close
124:     @tarreader.close
125:   end

[Source]

     # File lib/rubygems/package/tar_input.rb, line 127
127:   def each(&block)
128:     @tarreader.each do |entry|
129:       next unless entry.full_name == "data.tar.gz"
130:       is = zipped_stream entry
131: 
132:       begin
133:         Gem::Package::TarReader.new is do |inner|
134:           inner.each(&block)
135:         end
136:       ensure
137:         is.close if is
138:       end
139:     end
140: 
141:     @tarreader.rewind
142:   end

[Source]

     # File lib/rubygems/package/tar_input.rb, line 144
144:   def extract_entry(destdir, entry, expected_md5sum = nil)
145:     if entry.directory? then
146:       dest = File.join destdir, entry.full_name
147: 
148:       if File.directory? dest then
149:         @fileops.chmod entry.header.mode, dest, :verbose => false
150:       else
151:         @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
152:       end
153: 
154:       fsync_dir dest
155:       fsync_dir File.join(dest, "..")
156: 
157:       return
158:     end
159: 
160:     # it's a file
161:     md5 = Digest::MD5.new if expected_md5sum
162:     destdir = File.join destdir, File.dirname(entry.full_name)
163:     @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
164:     destfile = File.join destdir, File.basename(entry.full_name)
165:     @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
166: 
167:     open destfile, "wb", entry.header.mode do |os|
168:       loop do
169:         data = entry.read 4096
170:         break unless data
171:         # HACK shouldn't we check the MD5 before writing to disk?
172:         md5 << data if expected_md5sum
173:         os.write(data)
174:       end
175: 
176:       os.fsync
177:     end
178: 
179:     @fileops.chmod entry.header.mode, destfile, :verbose => false
180:     fsync_dir File.dirname(destfile)
181:     fsync_dir File.join(File.dirname(destfile), "..")
182: 
183:     if expected_md5sum && expected_md5sum != md5.hexdigest then
184:       raise Gem::Package::BadCheckSum
185:     end
186:   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 190
190:   def load_gemspec(io)
191:     Gem::Specification.from_yaml io
192:   rescue Gem::Exception
193:     nil
194:   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 210
210:   def zipped_stream(entry)
211:     if defined? Rubinius or defined? Maglev then
212:       # these implementations have working Zlib
213:       zis = Zlib::GzipReader.new entry
214:       dis = zis.read
215:       is = StringIO.new(dis)
216:     else
217:       # This is Jamis Buck's Zlib workaround for some unknown issue
218:       entry.read(10) # skip the gzip header
219:       zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
220:       is = StringIO.new(zis.inflate(entry.read))
221:     end
222:   ensure
223:     zis.finish if zis
224:   end

[Validate]