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