1#-- 2# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. 3# All rights reserved. 4# See LICENSE.txt for permissions. 5#++ 6 7## 8# The format class knows the guts of the ancient .gem file format and provides 9# the capability to read such ancient gems. 10# 11# Please pretend this doesn't exist. 12 13class Gem::Package::Old < Gem::Package 14 15 undef_method :spec= 16 17 ## 18 # Creates a new old-format package reader for +gem+. Old-format packages 19 # cannot be written. 20 21 def initialize gem 22 require 'fileutils' 23 require 'zlib' 24 Gem.load_yaml 25 26 @contents = nil 27 @gem = gem 28 @security_policy = nil 29 @spec = nil 30 end 31 32 ## 33 # A list of file names contained in this gem 34 35 def contents 36 verify 37 38 return @contents if @contents 39 40 open @gem, 'rb' do |io| 41 read_until_dashes io # spec 42 header = file_list io 43 44 @contents = header.map { |file| file['path'] } 45 end 46 end 47 48 ## 49 # Extracts the files in this package into +destination_dir+ 50 51 def extract_files destination_dir 52 verify 53 54 errstr = "Error reading files from gem" 55 56 open @gem, 'rb' do |io| 57 read_until_dashes io # spec 58 header = file_list io 59 raise Gem::Exception, errstr unless header 60 61 header.each do |entry| 62 full_name = entry['path'] 63 64 destination = install_location full_name, destination_dir 65 66 file_data = '' 67 68 read_until_dashes io do |line| 69 file_data << line 70 end 71 72 file_data = file_data.strip.unpack("m")[0] 73 file_data = Zlib::Inflate.inflate file_data 74 75 raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if 76 file_data.length != entry['size'].to_i 77 78 FileUtils.rm_rf destination 79 80 FileUtils.mkdir_p File.dirname destination 81 82 open destination, 'wb', entry['mode'] do |out| 83 out.write file_data 84 end 85 86 say destination if Gem.configuration.really_verbose 87 end 88 end 89 rescue Zlib::DataError 90 raise Gem::Exception, errstr 91 end 92 93 ## 94 # Reads the file list section from the old-format gem +io+ 95 96 def file_list io # :nodoc: 97 header = '' 98 99 read_until_dashes io do |line| 100 header << line 101 end 102 103 YAML.load header 104 end 105 106 ## 107 # Reads lines until a "---" separator is found 108 109 def read_until_dashes io # :nodoc: 110 while (line = io.gets) && line.chomp.strip != "---" do 111 yield line if block_given? 112 end 113 end 114 115 ## 116 # Skips the Ruby self-install header in +io+. 117 118 def skip_ruby io # :nodoc: 119 loop do 120 line = io.gets 121 122 return if line.chomp == '__END__' 123 break unless line 124 end 125 126 raise Gem::Exception, "Failed to find end of ruby script while reading gem" 127 end 128 129 ## 130 # The specification for this gem 131 132 def spec 133 verify 134 135 return @spec if @spec 136 137 yaml = '' 138 139 open @gem, 'rb' do |io| 140 skip_ruby io 141 read_until_dashes io do |line| 142 yaml << line 143 end 144 end 145 146 yaml_error = if RUBY_VERSION < '1.9' then 147 YAML::ParseError 148 elsif YAML::ENGINE.yamler == 'syck' then 149 YAML::ParseError 150 else 151 YAML::SyntaxError 152 end 153 154 begin 155 @spec = Gem::Specification.from_yaml yaml 156 rescue yaml_error => e 157 raise Gem::Exception, "Failed to parse gem specification out of gem file" 158 end 159 rescue ArgumentError => e 160 raise Gem::Exception, "Failed to parse gem specification out of gem file" 161 end 162 163 ## 164 # Raises an exception if a security policy that verifies data is active. 165 # Old format gems cannot be verified as signed. 166 167 def verify 168 return true unless @security_policy 169 170 raise Gem::Security::Exception, 171 'old format gems do not contain signatures and cannot be verified' if 172 @security_policy.verify_data 173 174 true 175 end 176 177end 178 179