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