rubygems-update 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rubygems-update might be problematic. Click here for more details.

Files changed (96) hide show
  1. data/ChangeLog +2335 -0
  2. data/README +54 -0
  3. data/Rakefile +293 -0
  4. data/Releases +98 -0
  5. data/TODO +7 -0
  6. data/bin/gem +11 -0
  7. data/bin/gem_server +111 -0
  8. data/bin/generate_yaml_index.rb +58 -0
  9. data/bin/update_rubygems +18 -0
  10. data/doc/doc.css +73 -0
  11. data/doc/makedoc.rb +4 -0
  12. data/examples/application/an-app.gemspec +26 -0
  13. data/examples/application/bin/myapp +3 -0
  14. data/examples/application/lib/somefunctionality.rb +3 -0
  15. data/gemspecs/README +4 -0
  16. data/gemspecs/cgikit-1.1.0.gemspec +18 -0
  17. data/gemspecs/jabber4r.gemspec +26 -0
  18. data/gemspecs/linguistics.gemspec +22 -0
  19. data/gemspecs/ook.gemspec +21 -0
  20. data/gemspecs/progressbar.gemspec +22 -0
  21. data/gemspecs/redcloth.gemspec +22 -0
  22. data/gemspecs/rublog.gemspec +23 -0
  23. data/gemspecs/ruby-doom.gemspec +21 -0
  24. data/gemspecs/rubyjdwp.gemspec +21 -0
  25. data/gemspecs/statistics.gemspec +21 -0
  26. data/lib/rubygems.rb +353 -0
  27. data/lib/rubygems/builder.rb +54 -0
  28. data/lib/rubygems/cmd_manager.rb +127 -0
  29. data/lib/rubygems/command.rb +191 -0
  30. data/lib/rubygems/config_file.rb +57 -0
  31. data/lib/rubygems/doc_manager.rb +94 -0
  32. data/lib/rubygems/format.rb +65 -0
  33. data/lib/rubygems/gem_commands.rb +925 -0
  34. data/lib/rubygems/gem_runner.rb +23 -0
  35. data/lib/rubygems/installer.rb +621 -0
  36. data/lib/rubygems/loadpath_manager.rb +108 -0
  37. data/lib/rubygems/old_format.rb +150 -0
  38. data/lib/rubygems/open-uri.rb +604 -0
  39. data/lib/rubygems/package.rb +740 -0
  40. data/lib/rubygems/remote_installer.rb +499 -0
  41. data/lib/rubygems/rubygems_version.rb +6 -0
  42. data/lib/rubygems/source_index.rb +130 -0
  43. data/lib/rubygems/specification.rb +613 -0
  44. data/lib/rubygems/user_interaction.rb +176 -0
  45. data/lib/rubygems/validator.rb +148 -0
  46. data/lib/rubygems/version.rb +279 -0
  47. data/lib/ubygems.rb +4 -0
  48. data/pkgs/sources/lib/sources.rb +6 -0
  49. data/pkgs/sources/sources.gemspec +14 -0
  50. data/post-install.rb +75 -0
  51. data/redist/session.gem +433 -0
  52. data/scripts/buildtests.rb +25 -0
  53. data/scripts/gemdoc.rb +62 -0
  54. data/scripts/runtest.rb +17 -0
  55. data/scripts/specdoc.rb +164 -0
  56. data/setup.rb +1360 -0
  57. data/test/bogussources.rb +5 -0
  58. data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
  59. data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
  60. data/test/data/lib/code.rb +1 -0
  61. data/test/data/one/README.one +1 -0
  62. data/test/data/one/lib/one.rb +3 -0
  63. data/test/data/one/one.gemspec +17 -0
  64. data/test/data/one/one.yaml +40 -0
  65. data/test/functional.rb +145 -0
  66. data/test/gemenvironment.rb +45 -0
  67. data/test/gemutilities.rb +18 -0
  68. data/test/insure_session.rb +46 -0
  69. data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
  70. data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
  71. data/test/mockgemui.rb +45 -0
  72. data/test/onegem.rb +23 -0
  73. data/test/simple_gem.rb +66 -0
  74. data/test/test_builder.rb +13 -0
  75. data/test/test_cached_fetcher.rb +60 -0
  76. data/test/test_check_command.rb +28 -0
  77. data/test/test_command.rb +130 -0
  78. data/test/test_configfile.rb +36 -0
  79. data/test/test_format.rb +70 -0
  80. data/test/test_gemloadpaths.rb +45 -0
  81. data/test/test_gempaths.rb +115 -0
  82. data/test/test_loadmanager.rb +40 -0
  83. data/test/test_local_cache.rb +157 -0
  84. data/test/test_package.rb +600 -0
  85. data/test/test_parse_commands.rb +179 -0
  86. data/test/test_process_commands.rb +21 -0
  87. data/test/test_remote_fetcher.rb +162 -0
  88. data/test/test_remote_installer.rb +154 -0
  89. data/test/test_source_index.rb +58 -0
  90. data/test/test_specification.rb +286 -0
  91. data/test/test_validator.rb +53 -0
  92. data/test/test_version_comparison.rb +204 -0
  93. data/test/testgem.rc +6 -0
  94. data/test/user_capture.rb +1 -0
  95. data/test/yaml_data.rb +59 -0
  96. metadata +151 -0
@@ -0,0 +1,108 @@
1
+ module Kernel
2
+ alias require__ require
3
+ def require(file)
4
+ Gem::LoadPathManager.search_loadpath(file) || Gem::LoadPathManager.search_gempath(file)
5
+ require__(file)
6
+ end
7
+ end
8
+
9
+ module Gem
10
+ module LoadPathManager
11
+ @paths = nil
12
+
13
+ # These local versions of Gem::Version and Gem::Specification are
14
+ # used by the load path manager because they are faster than the
15
+ # fully functional ones. Full functionality is not required
16
+ # during the load phase.
17
+ module Gem
18
+ class Version
19
+ class Requirement
20
+ def initialize(string)
21
+ end
22
+ end
23
+ end
24
+ class Specification
25
+ def initialize(&block)
26
+ @require_paths = ['lib']
27
+ @platform = nil
28
+ yield self
29
+ end
30
+ attr_reader :version
31
+ attr_accessor :files, :require_paths, :name
32
+ def platform=(platform)
33
+ @platform = platform unless platform == "ruby"
34
+ end
35
+ def requirements; []; end
36
+ def version=(version)
37
+ @version = ::Gem::Version.create(version)
38
+ end
39
+ def full_name
40
+ @full_name ||=
41
+ if @platform.nil? || @platform == "ruby" || @platform == ""
42
+ "#{@name}-#{@version}"
43
+ else
44
+ "#{@name}-#{@version}-#{@platform}"
45
+ end
46
+ end
47
+ def method_missing(method, *args)
48
+ end
49
+ def <=>(other)
50
+ r = @name<=>other.name
51
+ r = other.version<=>@version if r == 0
52
+ r
53
+ end
54
+ def to_s
55
+ "#<Gem::Specification name=#{@name} version=#{@version} quick=true>"
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.paths
61
+ @paths
62
+ end
63
+
64
+ # Prep the list of potential paths for require file resolution.
65
+ def self.build_paths
66
+ @specs ||= []
67
+ @paths = []
68
+ ::Gem.path.each do |gempath|
69
+ newspecs = Dir.glob("#{gempath}/specifications/*.gemspec").collect { |specfile|
70
+ eval(File.read(specfile))
71
+ }.sort
72
+ @specs.concat(newspecs)
73
+ newspecs.each do |spec|
74
+ spec.require_paths.each {|path| @paths << "#{gempath}/gems/#{spec.full_name}/#{path}"}
75
+ end
76
+ end
77
+ end
78
+
79
+ # True if the file can be resolved with the existing load path.
80
+ def self.search_loadpath(file)
81
+ glob_over($LOAD_PATH, file).size > 0
82
+ end
83
+
84
+ def self.search_gempath(file)
85
+ build_paths unless @paths
86
+ fullname = glob_over(@paths, file).first
87
+ return false unless fullname
88
+ @specs.each do |spec|
89
+ if fullname.include?("/#{spec.full_name}/")
90
+ ::Gem.activate(spec.name, false, spec.version.to_s)
91
+ return true
92
+ end
93
+ end
94
+ false
95
+ end
96
+
97
+ private
98
+
99
+ SUFFIX_PATTERN = "{,.rb,.so,.bundle,.dll,.sl}"
100
+
101
+ def self.glob_over(list, file)
102
+ files = Dir.glob("{#{(list).join(',')}}/#{file}#{SUFFIX_PATTERN}").map{|x| Marshal.load(Marshal.dump(x))}
103
+ files.delete_if { |f| File.directory?(f) }
104
+ end
105
+
106
+ end
107
+ end
108
+
@@ -0,0 +1,150 @@
1
+ module Gem
2
+
3
+ ##
4
+ # Used to raise parsing and loading errors
5
+ #
6
+ class FormatException < Gem::Exception
7
+ attr_accessor :file_path
8
+ #I go back and forth on whether or not to create custom exception classes
9
+ end
10
+
11
+ ##
12
+ # The format class knows the guts of the RubyGem .gem file format
13
+ # and provides the capability to read gem files
14
+ #
15
+ class OldFormat
16
+ attr_accessor :spec, :file_entries, :gem_path
17
+
18
+ ##
19
+ # Constructs an instance of a Format object, representing the gem's
20
+ # data structure.
21
+ #
22
+ # gem:: [String] The file name of the gem
23
+ #
24
+ def initialize(gem_path)
25
+ @gem_path = gem_path
26
+ end
27
+
28
+ ##
29
+ # Reads the named gem file and returns a Format object, representing
30
+ # the data from the gem file
31
+ #
32
+ # file_path:: [String] Path to the gem file
33
+ #
34
+ def self.from_file_by_path(file_path)
35
+ unless File.exist?(file_path)
36
+ raise Gem::Exception, "Cannot load gem file [#{file_path}]"
37
+ end
38
+ require 'fileutils'
39
+ File.open(file_path, 'r') do |file|
40
+ from_io(file, file_path)
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Reads a gem from an io stream and returns a Format object, representing
46
+ # the data from the gem file
47
+ #
48
+ # io:: [IO] Stream from which to read the gem
49
+ #
50
+ def self.from_io(io, gem_path="(io)")
51
+ format = self.new(gem_path)
52
+ skip_ruby(io)
53
+ format.spec = read_spec(io)
54
+ format.file_entries = []
55
+ read_files_from_gem(io) do |entry, file_data|
56
+ format.file_entries << [entry, file_data]
57
+ end
58
+ format
59
+ end
60
+
61
+ private
62
+ ##
63
+ # Skips the Ruby self-install header. After calling this method, the
64
+ # IO index will be set after the Ruby code.
65
+ #
66
+ # file:: [IO] The IO to process (skip the Ruby code)
67
+ #
68
+ def self.skip_ruby(file)
69
+ end_seen = false
70
+ loop {
71
+ line = file.gets
72
+ if(line == nil || line.chomp == "__END__") then
73
+ end_seen = true
74
+ break
75
+ end
76
+ }
77
+ if(end_seen == false) then
78
+ raise Gem::Exception.new("Failed to find end of ruby script while reading gem")
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Reads the specification YAML from the supplied IO and constructs
84
+ # a Gem::Specification from it. After calling this method, the
85
+ # IO index will be set after the specification header.
86
+ #
87
+ # file:: [IO] The IO to process
88
+ #
89
+ def self.read_spec(file)
90
+ require 'yaml'
91
+ yaml = ''
92
+ begin
93
+ read_until_dashes(file) do |line|
94
+ yaml << line
95
+ end
96
+ Specification.from_yaml(yaml)
97
+ rescue YAML::Error => e
98
+ raise Gem::Exception.new("Failed to parse gem specification out of gem file")
99
+ rescue ArgumentError => e
100
+ raise Gem::Exception.new("Failed to parse gem specification out of gem file")
101
+ end
102
+ end
103
+
104
+ ##
105
+ # Reads lines from the supplied IO until a end-of-yaml (---) is
106
+ # reached
107
+ #
108
+ # file:: [IO] The IO to process
109
+ # block:: [String] The read line
110
+ #
111
+ def self.read_until_dashes(file)
112
+ while((line = file.gets) && line.chomp.strip != "---") do
113
+ yield line
114
+ end
115
+ end
116
+
117
+
118
+ ##
119
+ # Reads the embedded file data from a gem file, yielding an entry
120
+ # containing metadata about the file and the file contents themselves
121
+ # for each file that's archived in the gem.
122
+ # NOTE: Many of these methods should be extracted into some kind of
123
+ # Gem file read/writer
124
+ #
125
+ # gem_file:: [IO] The IO to process
126
+ #
127
+ def self.read_files_from_gem(gem_file)
128
+ require 'zlib'
129
+ require 'yaml'
130
+ errstr = "Error reading files from gem"
131
+ header_yaml = ''
132
+ begin
133
+ self.read_until_dashes(gem_file) do |line|
134
+ header_yaml << line
135
+ end
136
+ header = YAML.load(header_yaml)
137
+ raise Gem::Exception.new(errstr) unless header
138
+ header.each do |entry|
139
+ file_data = ''
140
+ self.read_until_dashes(gem_file) do |line|
141
+ file_data << line
142
+ end
143
+ yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
144
+ end
145
+ rescue Exception,Zlib::DataError => e
146
+ raise Gem::Exception.new(errstr)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,604 @@
1
+ #= open-uri.rb
2
+ #
3
+ #open-uri.rb is easy-to-use wrapper for net/http and net/ftp.
4
+ #
5
+ #== Example
6
+ #
7
+ #It is possible to open http/ftp URL as usual a file:
8
+ #
9
+ # open("http://www.ruby-lang.org/") {|f|
10
+ # f.each_line {|line| p line}
11
+ # }
12
+ #
13
+ #The opened file has several methods for meta information as follows since
14
+ #it is extended by OpenURI::Meta.
15
+ #
16
+ # open("http://www.ruby-lang.org/en") {|f|
17
+ # f.each_line {|line| p line}
18
+ # p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
19
+ # p f.content_type # "text/html"
20
+ # p f.charset # "iso-8859-1"
21
+ # p f.content_encoding # []
22
+ # p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
23
+ # }
24
+ #
25
+ #Additional header fields can be specified by an optional hash argument.
26
+ #
27
+ # open("http://www.ruby-lang.org/en/",
28
+ # "User-Agent" => "Ruby/#{RUBY_VERSION}",
29
+ # "From" => "foo@bar.invalid",
30
+ # "Referer" => "http://www.ruby-lang.org/") {|f|
31
+ # ...
32
+ # }
33
+ #
34
+ #The environment variables such as http_proxy and ftp_proxy are in effect by
35
+ #default. :proxy => nil disables proxy.
36
+ #
37
+ # open("http://www.ruby-lang.org/en/raa.html",
38
+ # :proxy => nil) {|f|
39
+ # ...
40
+ # }
41
+ #
42
+ #URI objects can be opened in similar way.
43
+ #
44
+ # uri = URI.parse("http://www.ruby-lang.org/en/")
45
+ # uri.open {|f|
46
+ # ...
47
+ # }
48
+ #
49
+ #URI objects can be read directly.
50
+ #The returned string is also extended by OpenURI::Meta.
51
+ #
52
+ # str = uri.read
53
+ # p str.base_uri
54
+ #
55
+ #Author:: Tanaka Akira <akr@m17n.org>
56
+
57
+ require 'uri'
58
+ require 'stringio'
59
+ require 'time'
60
+
61
+ module Kernel
62
+ private
63
+ alias open_uri_original_open open # :nodoc:
64
+
65
+ # makes possible to open various resources including URIs.
66
+ # If the first argument respond to `open' method,
67
+ # the method is called with the rest arguments.
68
+ #
69
+ # If the first argument is a string which begins with xxx://,
70
+ # it is parsed by URI.parse. If the parsed object respond to `open' method,
71
+ # the method is called with the rest arguments.
72
+ #
73
+ # Otherwise original open is called.
74
+ #
75
+ # Since open-uri.rb provides URI::HTTP#open and URI::FTP#open,
76
+ # Kernel[#.]open can accepts such URIs and strings which begins with
77
+ # http:// and ftp://. In this http and ftp case, the opened file object
78
+ # is extended by OpenURI::Meta.
79
+ def open(name, *rest, &block) # :doc:
80
+ if name.respond_to?(:open)
81
+ name.open(*rest, &block)
82
+ elsif name.respond_to?(:to_str) &&
83
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
84
+ (uri = URI.parse(name)).respond_to?(:open)
85
+ uri.open(*rest, &block)
86
+ else
87
+ open_uri_original_open(name, *rest, &block)
88
+ end
89
+ end
90
+ module_function :open
91
+ end
92
+
93
+ module OpenURI
94
+ Options = {
95
+ :proxy => true,
96
+ :progress_proc => true,
97
+ :content_length_proc => true,
98
+ }
99
+
100
+
101
+ def OpenURI.check_options(options) # :nodoc:
102
+ options.each {|k, v|
103
+ next unless Symbol === k
104
+ unless Options.include? k
105
+ raise ArgumentError, "unrecognized option: #{k}"
106
+ end
107
+ }
108
+ end
109
+
110
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
111
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
112
+ mode = rest.shift
113
+ if !rest.empty? && Integer === rest.first
114
+ perm = rest.shift
115
+ end
116
+ end
117
+ return mode, perm, rest
118
+ end
119
+
120
+ def OpenURI.open_uri(name, *rest) # :nodoc:
121
+ uri = URI::Generic === name ? name : URI.parse(name)
122
+ mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
123
+ options = rest.shift if !rest.empty? && Hash === rest.first
124
+ raise ArgumentError.new("extra arguments") if !rest.empty?
125
+ options ||= {}
126
+ OpenURI.check_options(options)
127
+
128
+ unless mode == nil ||
129
+ mode == 'r' || mode == 'rb' ||
130
+ mode == File::RDONLY
131
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
132
+ end
133
+
134
+ io = open_loop(uri, options)
135
+ if block_given?
136
+ begin
137
+ yield io
138
+ ensure
139
+ io.close
140
+ end
141
+ else
142
+ io
143
+ end
144
+ end
145
+
146
+ def OpenURI.open_loop(uri, options) # :nodoc:
147
+ case opt_proxy = options.fetch(:proxy, true)
148
+ when true
149
+ find_proxy = lambda {|u| u.find_proxy}
150
+ when nil, false
151
+ find_proxy = lambda {|u| nil}
152
+ when String
153
+ opt_proxy = URI.parse(opt_proxy)
154
+ find_proxy = lambda {|u| opt_proxy}
155
+ when URI::Generic
156
+ find_proxy = lambda {|u| opt_proxy}
157
+ else
158
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
159
+ end
160
+
161
+ uri_set = {}
162
+ buf = nil
163
+ while true
164
+ redirect = catch(:open_uri_redirect) {
165
+ buf = Buffer.new
166
+ if proxy_uri = find_proxy.call(uri)
167
+ proxy_uri.proxy_open(buf, uri, options)
168
+ else
169
+ uri.direct_open(buf, options)
170
+ end
171
+ nil
172
+ }
173
+ if redirect
174
+ if redirect.relative?
175
+ # Although it violates RFC2616, Location: field may have relative
176
+ # URI. It is converted to absolute URI using uri as a base URI.
177
+ redirect = uri + redirect
178
+ end
179
+ unless OpenURI.redirectable?(uri, redirect)
180
+ raise "redirection forbidden: #{uri} -> #{redirect}"
181
+ end
182
+ uri = redirect
183
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
184
+ uri_set[uri.to_s] = true
185
+ else
186
+ break
187
+ end
188
+ end
189
+ io = buf.io
190
+ io.base_uri = uri
191
+ io
192
+ end
193
+
194
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
195
+ # This test is intended to forbid a redirection from http://... to
196
+ # file:///etc/passwd.
197
+ # However this is ad hoc. It should be extensible/configurable.
198
+ uri1.scheme.downcase == uri2.scheme.downcase ||
199
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
200
+ end
201
+
202
+ class HTTPError < StandardError
203
+ def initialize(message, io)
204
+ super(message)
205
+ @io = io
206
+ end
207
+ attr_reader :io
208
+ end
209
+
210
+ class Buffer # :nodoc:
211
+ def initialize
212
+ @io = StringIO.new
213
+ @size = 0
214
+ end
215
+ attr_reader :size
216
+
217
+ StringMax = 10240
218
+ def <<(str)
219
+ @io << str
220
+ @size += str.length
221
+ if StringIO === @io && StringMax < @size
222
+ require 'tempfile'
223
+ io = Tempfile.new('open-uri')
224
+ io.binmode
225
+ Meta.init io, @io if Meta === @io
226
+ io << @io.string
227
+ @io = io
228
+ end
229
+ end
230
+
231
+ def io
232
+ Meta.init @io unless Meta === @io
233
+ @io
234
+ end
235
+ end
236
+
237
+ # Mixin for holding meta-information.
238
+ module Meta
239
+ def Meta.init(obj, src=nil) # :nodoc:
240
+ obj.extend Meta
241
+ obj.instance_eval {
242
+ @base_uri = nil
243
+ @meta = {}
244
+ }
245
+ if src
246
+ obj.status = src.status
247
+ obj.base_uri = src.base_uri
248
+ src.meta.each {|name, value|
249
+ obj.meta_add_field(name, value)
250
+ }
251
+ end
252
+ end
253
+
254
+ # returns an Array which consists status code and message.
255
+ attr_accessor :status
256
+
257
+ # returns a URI which is base of relative URIs in the data.
258
+ # It may differ from the URI supplied by a user because redirection.
259
+ attr_accessor :base_uri
260
+
261
+ # returns a Hash which represents header fields.
262
+ # The Hash keys are downcased for canonicalization.
263
+ attr_reader :meta
264
+
265
+ def meta_add_field(name, value) # :nodoc:
266
+ @meta[name.downcase] = value
267
+ end
268
+
269
+ # returns a Time which represents Last-Modified field.
270
+ def last_modified
271
+ if v = @meta['last-modified']
272
+ Time.httpdate(v)
273
+ else
274
+ nil
275
+ end
276
+ end
277
+
278
+ RE_LWS = /[\r\n\t ]+/n
279
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
280
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])"}n
281
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
282
+
283
+ def content_type_parse # :nodoc:
284
+ v = @meta['content-type']
285
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
286
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
287
+ type = $1.downcase
288
+ subtype = $2.downcase
289
+ parameters = []
290
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
291
+ val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval
292
+ parameters << [att.downcase, val]
293
+ }
294
+ ["#{type}/#{subtype}", *parameters]
295
+ else
296
+ nil
297
+ end
298
+ end
299
+
300
+ # returns "type/subtype" which is MIME Content-Type.
301
+ # It is downcased for canonicalization.
302
+ # Content-Type parameters are stripped.
303
+ def content_type
304
+ type, *parameters = content_type_parse
305
+ type || 'application/octet-stream'
306
+ end
307
+
308
+ # returns a charset parameter in Content-Type field.
309
+ # It is downcased for canonicalization.
310
+ #
311
+ # If charset parameter is not given but a block is given,
312
+ # the block is called and its result is returned.
313
+ # It can be used to guess charset.
314
+ #
315
+ # If charset parameter and block is not given,
316
+ # nil is returned except text type in HTTP.
317
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
318
+ def charset
319
+ type, *parameters = content_type_parse
320
+ if pair = parameters.assoc('charset')
321
+ pair.last.downcase
322
+ elsif block_given?
323
+ yield
324
+ elsif type && %r{\Atext/} =~ type &&
325
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
326
+ "iso-8859-1" # RFC2616 3.7.1
327
+ else
328
+ nil
329
+ end
330
+ end
331
+
332
+ # returns a list of encodings in Content-Encoding field
333
+ # as an Array of String.
334
+ # The encodings are downcased for canonicalization.
335
+ def content_encoding
336
+ v = @meta['content-encoding']
337
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
338
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
339
+ else
340
+ []
341
+ end
342
+ end
343
+ end
344
+
345
+ # Mixin for HTTP and FTP URIs.
346
+ module OpenRead
347
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
348
+ #
349
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
350
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
351
+ #
352
+ # `mode', `perm' is same as Kernel#open.
353
+ #
354
+ # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
355
+ # support write mode (yet).
356
+ # Also `perm' is just ignored because it is meaningful only for file
357
+ # creation.
358
+ #
359
+ # `options' must be a hash.
360
+ #
361
+ # Each pairs which key is a string in the hash specify a extra header
362
+ # field for HTTP.
363
+ # I.e. it is ignored for FTP without HTTP proxy.
364
+ #
365
+ # The hash may include other option which key is a symbol:
366
+ #
367
+ # :proxy => "http://proxy.foo.com:8000/"
368
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
369
+ # :proxy => true
370
+ # :proxy => false
371
+ # :proxy => nil
372
+ #
373
+ # If :proxy option is specified, the value should be String, URI,
374
+ # boolean or nil.
375
+ # When String or URI is given, it is treated as proxy URI.
376
+ # When true is given or the option itself is not specified,
377
+ # environment variable `scheme_proxy'(or `SCHEME_PROXY') is examined.
378
+ # `scheme' is replaced by `http' or `ftp'.
379
+ # When false or nil is given, the environment variables are ignored and
380
+ # connection will be made to a server directly.
381
+ #
382
+ # :content_length_proc => lambda {|content_length| ... }
383
+ #
384
+ # If :content_length_proc option is specified, the option value procedure
385
+ # is called before actual transfer is started.
386
+ # It takes one argument which is expected content length in bytes.
387
+ #
388
+ # If two or more transfer is done by HTTP redirection, the procedure
389
+ # is called only one for a last transfer.
390
+ #
391
+ # When expected content length is unknown, the procedure is called with
392
+ # nil.
393
+ # It is happen when HTTP response has no Content-Length header.
394
+ #
395
+ # :progress_proc => lambda {|size| ...}
396
+ #
397
+ # If :progress_proc option is specified, the proc is called with one
398
+ # argument each time when `open' gets content fragment from network.
399
+ # The argument `size' `size' is a accumulated transfered size in bytes.
400
+ #
401
+ # If two or more transfer is done by HTTP redirection, the procedure
402
+ # is called only one for a last transfer.
403
+ #
404
+ # :progress_proc and :content_length_proc are intended to be used for
405
+ # progress bar.
406
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
407
+ #
408
+ # pbar = nil
409
+ # open("http://...",
410
+ # :content_length_proc => lambda {|t|
411
+ # if t && 0 < t
412
+ # pbar = ProgressBar.new("...", t)
413
+ # pbar.file_transfer_mode
414
+ # end
415
+ # },
416
+ # :progress_proc => lambda {|s|
417
+ # pbar.set s if pbar
418
+ # }) {|f| ... }
419
+ #
420
+ # OpenURI::OpenRead#open returns an IO like object if block is not given.
421
+ # Otherwise it yields the IO object and return the value of the block.
422
+ # The IO object is extended with OpenURI::Meta.
423
+ def open(*rest, &block)
424
+ OpenURI.open_uri(self, *rest, &block)
425
+ end
426
+
427
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
428
+ # returns the content as string.
429
+ # The string is extended with OpenURI::Meta.
430
+ # The argument `options' is same as OpenURI::OpenRead#open.
431
+ def read(options={})
432
+ self.open(options) {|f|
433
+ str = f.read
434
+ Meta.init str, f
435
+ str
436
+ }
437
+ end
438
+ end
439
+ end
440
+
441
+ module URI
442
+ class Generic
443
+ # returns a proxy URI.
444
+ # The proxy URI is obtained from environment variables such as http_proxy,
445
+ # ftp_proxy, no_proxy, etc.
446
+ # If there is no proper proxy, nil is returned.
447
+ #
448
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
449
+ # are examined too.
450
+ #
451
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
452
+ # It's because HTTP_PROXY may be set by Proxy: header.
453
+ # So HTTP_PROXY is not used.
454
+ # http_proxy is not used too if the variable is case insensitive.
455
+ # CGI_HTTP_PROXY can be used instead.
456
+ def find_proxy
457
+ name = self.scheme.downcase + '_proxy'
458
+ proxy_uri = nil
459
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
460
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
461
+ # HTTP_* for header information in CGI.
462
+ # So it should be careful to use it.
463
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
464
+ case pairs.length
465
+ when 0 # no proxy setting anyway.
466
+ proxy_uri = nil
467
+ when 1
468
+ k, v = pairs.shift
469
+ if k == 'http_proxy' && ENV[k.upcase] == nil
470
+ # http_proxy is safe to use because ENV is case sensitive.
471
+ proxy_uri = ENV[name]
472
+ else
473
+ proxy_uri = nil
474
+ end
475
+ else # http_proxy is safe to use because ENV is case sensitive.
476
+ proxy_uri = ENV[name]
477
+ end
478
+ if !proxy_uri
479
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
480
+ proxy_uri = ENV["CGI_#{name.upcase}"]
481
+ end
482
+ elsif name == 'http_proxy'
483
+ unless proxy_uri = ENV[name]
484
+ if proxy_uri = ENV[name.upcase]
485
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
486
+ end
487
+ end
488
+ else
489
+ proxy_uri = ENV[name] || ENV[name.upcase]
490
+ end
491
+
492
+ if proxy_uri && self.host
493
+ require 'socket'
494
+ begin
495
+ addr = IPSocket.getaddress(self.host)
496
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
497
+ rescue SocketError
498
+ end
499
+ end
500
+
501
+ if proxy_uri
502
+ proxy_uri = URI.parse(proxy_uri)
503
+ unless URI::HTTP === proxy_uri
504
+ raise "Non-HTTP proxy URI: #{proxy_uri}"
505
+ end
506
+ name = 'no_proxy'
507
+ if no_proxy = ENV[name] || ENV[name.upcase]
508
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
509
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
510
+ (!port || self.port == port.to_i)
511
+ proxy_uri = nil
512
+ break
513
+ end
514
+ }
515
+ end
516
+ proxy_uri
517
+ else
518
+ nil
519
+ end
520
+ end
521
+ end
522
+
523
+ class HTTP
524
+ def direct_open(buf, options) # :nodoc:
525
+ proxy_open(buf, request_uri, options)
526
+ end
527
+
528
+ def proxy_open(buf, uri, options) # :nodoc:
529
+ header = {}
530
+ options.each {|k, v| header[k] = v if String === k }
531
+
532
+ if uri.respond_to? :host
533
+ # According to RFC2616 14.23, Host: request-header field should be set
534
+ # an origin server.
535
+ # But net/http wrongly set a proxy server if an absolute URI is
536
+ # specified as a request URI.
537
+ # So open-uri override it here explicitly.
538
+ header['host'] = uri.host
539
+ header['host'] += ":#{uri.port}" if uri.port
540
+ end
541
+
542
+ require 'net/http'
543
+ resp = nil
544
+ Net::HTTP.start(self.host, self.port) {|http|
545
+ http.request_get(uri.to_s, header) {|response|
546
+ resp = response
547
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
548
+ if resp.key?('Content-Length')
549
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
550
+ else
551
+ options[:content_length_proc].call(nil)
552
+ end
553
+ end
554
+ resp.read_body {|str|
555
+ buf << str
556
+ if options[:progress_proc] && Net::HTTPSuccess === resp
557
+ options[:progress_proc].call(buf.size)
558
+ end
559
+ }
560
+ }
561
+ }
562
+ io = buf.io
563
+ io.rewind
564
+ io.status = [resp.code, resp.message]
565
+ resp.each {|name,value| buf.io.meta_add_field name, value }
566
+ case resp
567
+ when Net::HTTPSuccess
568
+ when Net::HTTPMovedPermanently, # 301
569
+ Net::HTTPFound, # 302
570
+ Net::HTTPSeeOther, # 303
571
+ Net::HTTPTemporaryRedirect # 307
572
+ throw :open_uri_redirect, URI.parse(resp['location'])
573
+ else
574
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
575
+ end
576
+ end
577
+
578
+ include OpenURI::OpenRead
579
+ end
580
+
581
+ class FTP
582
+ def direct_open(buf, options) # :nodoc:
583
+ require 'net/ftp'
584
+ # todo: extract user/passwd from .netrc.
585
+ user = 'anonymous'
586
+ passwd = nil
587
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
588
+
589
+ ftp = Net::FTP.open(self.host)
590
+ ftp.login(user, passwd)
591
+ if options[:content_length_proc]
592
+ options[:content_length_proc].call(ftp.size(self.path))
593
+ end
594
+ ftp.getbinaryfile(self.path, '/dev/null', Net::FTP::DEFAULT_BLOCKSIZE) {|str|
595
+ buf << str
596
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
597
+ }
598
+ ftp.close
599
+ buf.io.rewind
600
+ end
601
+
602
+ include OpenURI::OpenRead
603
+ end
604
+ end