1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
require 'open-uri'
begin
require 'net/https'
$rubygems_schema = 'https'
# open-uri of ruby 2.2.0 accept an array of PEMs as ssl_ca_cert, but old
# versions are not. so, patching OpenSSL::X509::Store#add_file instead.
class OpenSSL::X509::Store
alias orig_add_file add_file
def add_file(pems)
Array(pems).each do |pem|
if File.directory?(pem)
add_path pem
else
orig_add_file pem
end
end
end
end
# since open-uri internally checks ssl_ca_cert by File.directory?, to allow
# accept an array.
class <<File
alias orig_directory? directory?
def File.directory? files
files.is_a?(Array) ? false : orig_directory?(files)
end
end
rescue LoadError
$rubygems_schema = 'http'
end
class Downloader
class GNU < self
def self.download(name, *rest)
super("http://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=#{name};hb=master", name, *rest)
end
end
class RubyGems < self
def self.download(name, dir = nil, ims = true, options = {})
require 'rubygems'
require 'rubygems/package'
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/*.pem", File.dirname(__FILE__)))
if $rubygems_schema != 'https'
warn "*** using http instead of https ***"
end
file = under(dir, name)
super("#{$rubygems_schema}://rubygems.org/downloads/#{name}", file, nil, ims, options) or
return false
policy = Gem::Security::LowSecurity
(policy = policy.dup).ui = Gem::SilentUI.new if policy.respond_to?(:'ui=')
pkg = Gem::Package.new(file)
pkg.security_policy = policy
begin
pkg.verify
rescue Gem::Security::Exception => e
$stderr.puts e.message
File.unlink(file)
false
else
true
end
end
def self.verify(pkg)
end
end
Gems = RubyGems
class Unicode < self
def self.download(name, *rest)
super("http://www.unicode.org/Public/#{name}", name, *rest)
end
end
def self.mode_for(data)
/\A#!/ =~ data ? 0755 : 0644
end
def self.http_options(file, since)
options = {}
if since
case since
when true
since = (File.mtime(file).httpdate rescue nil)
when Time
since = since.httpdate
end
if since
options['If-Modified-Since'] = since
end
end
options['Accept-Encoding'] = '*' # to deisable Net::HTTP::GenericRequest#decode_content
options
end
# Downloader.download(url, name, [dir, [ims]])
#
# Update a file from url if newer version is available.
# Creates the file if the file doesn't yet exist; however, the
# directory where the file is being created has to exist already.
# If +ims+ is false, always download url regardless of its last
# modified time.
#
# Example usage:
# download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
# 'UnicodeData.txt', 'enc/unicode/data'
def self.download(url, name, dir = nil, ims = true, options = {})
file = under(dir, name)
if ims.nil? and File.exist?(file)
if $VERBOSE
$stdout.puts "#{name} already exists"
$stdout.flush
end
return true
end
url = URI(url)
if $VERBOSE
$stdout.print "downloading #{name} ... "
$stdout.flush
end
begin
data = url.read(options.merge(http_options(file, ims.nil? ? true : ims)))
rescue OpenURI::HTTPError => http_error
if http_error.message =~ /^304 / # 304 Not Modified
if $VERBOSE
$stdout.puts "not modified"
$stdout.flush
end
return true
end
raise
rescue Timeout::Error
if ims.nil? and File.exist?(file)
puts "Request for #{url} timed out, using old version."
return true
end
raise
rescue SocketError
if ims.nil? and File.exist?(file)
puts "No network connection, unable to download #{url}, using old version."
return true
end
raise
end
mtime = nil
open(file, "wb", 0600) do |f|
f.write(data)
f.chmod(mode_for(data))
mtime = data.meta["last-modified"]
end
if mtime
mtime = Time.httpdate(mtime)
File.utime(mtime, mtime, file)
end
if $VERBOSE
$stdout.puts "done"
$stdout.flush
end
true
rescue => e
raise "failed to download #{name}\n#{e.message}: #{url}"
end
def self.under(dir, name)
dir ? File.join(dir, File.basename(name)) : name
end
end
if $0 == __FILE__
ims = true
until ARGV.empty?
case ARGV[0]
when '-d'
destdir = ARGV[1]
ARGV.shift
when '-e'
ims = nil
when '-a'
ims = true
when /\A-/
abort "#{$0}: unknown option #{ARGV[0]}"
else
break
end
ARGV.shift
end
dl = Downloader.constants.find do |name|
ARGV[0].casecmp(name.to_s) == 0
end unless ARGV.empty?
$VERBOSE = true
if dl
dl = Downloader.const_get(dl)
ARGV.shift
ARGV.each do |name|
dl.download(name, destdir, ims)
end
else
abort "usage: #{$0} url name" unless ARGV.size == 2
Downloader.download(ARGV[0], ARGV[1], destdir, ims)
end
end
|