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