r5_geminabox 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+
2
+ module Geminabox
3
+ module Proxy
4
+ class FileHandler
5
+
6
+ attr_reader :file_name
7
+
8
+ def initialize(file_name)
9
+ @file_name = file_name
10
+ ensure_destination_exists
11
+ end
12
+
13
+ def local_path
14
+ File.expand_path(file_name, root_path)
15
+ end
16
+
17
+ def root_path
18
+ Geminabox.data
19
+ end
20
+
21
+ def local_file_exists?
22
+ file_exists? local_path
23
+ end
24
+
25
+ def proxy_file_exists?
26
+ file_exists? proxy_path
27
+ end
28
+
29
+ def proxy_path
30
+ File.expand_path(file_name, proxy_folder_path)
31
+ end
32
+
33
+ def file_exists?(path)
34
+ File.exists? path
35
+ end
36
+
37
+ def proxy_folder_path
38
+ File.join(root_path, proxy_folder_name)
39
+ end
40
+
41
+ def proxy_folder_name
42
+ 'proxy'
43
+ end
44
+
45
+ def remote_content
46
+ Geminabox.http_adapter.get_content(remote_url).force_encoding(encoding)
47
+ rescue
48
+ raise GemStoreError.new(500, "Unable to get content from #{remote_url}")
49
+ end
50
+
51
+ def remote_url
52
+ "http://rubygems.org/#{file_name}"
53
+ end
54
+
55
+ def local_content
56
+ File.read(local_path).force_encoding(encoding)
57
+ end
58
+
59
+ private
60
+ def encoding
61
+ "UTF-8"
62
+ end
63
+
64
+ def ensure_destination_exists
65
+ create_local_folder unless local_folder_exists?
66
+ create_proxy_folder unless proxy_folder_exists?
67
+ end
68
+
69
+ def proxy_file_folder
70
+ File.dirname proxy_path
71
+ end
72
+
73
+ def proxy_folder_exists?
74
+ Dir.exists?(proxy_file_folder)
75
+ end
76
+
77
+ def create_proxy_folder
78
+ FileUtils.mkdir_p(proxy_file_folder)
79
+ end
80
+
81
+ def local_file_folder
82
+ File.dirname local_path
83
+ end
84
+
85
+ def local_folder_exists?
86
+ Dir.exists?(local_file_folder)
87
+ end
88
+
89
+ def create_local_folder
90
+ FileUtils.mkdir_p(local_file_folder)
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,81 @@
1
+ require 'sinatra/base'
2
+ require 'net/http'
3
+
4
+ module Geminabox
5
+ module Proxy
6
+ class Hostess < Sinatra::Base
7
+ attr_accessor :file_handler
8
+
9
+ def serve
10
+ if file_handler
11
+ send_file file_handler.proxy_path
12
+ else
13
+ send_file(File.expand_path(File.join(Server.data, *request.path_info)), :type => response['Content-Type'])
14
+ end
15
+ end
16
+
17
+ %w[specs.4.8.gz
18
+ latest_specs.4.8.gz
19
+ prerelease_specs.4.8.gz
20
+ ].each do |index|
21
+ get "/#{index}" do
22
+ splice_file index
23
+ content_type 'application/x-gzip'
24
+ serve
25
+ end
26
+ end
27
+
28
+ %w[quick/Marshal.4.8/*.gemspec.rz
29
+ yaml.Z
30
+ Marshal.4.8.Z
31
+ ].each do |deflated_index|
32
+ get "/#{deflated_index}" do
33
+ copy_file request.path_info[1..-1]
34
+ content_type('application/x-deflate')
35
+ serve
36
+ end
37
+ end
38
+
39
+ %w[yaml
40
+ Marshal.4.8
41
+ specs.4.8
42
+ latest_specs.4.8
43
+ prerelease_specs.4.8
44
+ ].each do |old_index|
45
+ get "/#{old_index}" do
46
+ splice_file old_index
47
+ serve
48
+ end
49
+ end
50
+
51
+ get "/gems/*.gem" do
52
+ get_from_rubygems_if_not_local
53
+ serve
54
+ end
55
+
56
+ private
57
+ def get_from_rubygems_if_not_local
58
+
59
+ file = File.expand_path(File.join(Server.data, *request.path_info))
60
+
61
+ unless File.exists?(file)
62
+ ruby_gems_url = 'http://production.cf.rubygems.org'
63
+ path = File.join(ruby_gems_url, *request.path_info)
64
+ content = Geminabox.http_adapter.get_content(path)
65
+ GemStore.create(IncomingGem.new(StringIO.new(content)))
66
+ end
67
+
68
+ end
69
+
70
+ def splice_file(file_name)
71
+ self.file_handler = Splicer.make(file_name)
72
+ end
73
+
74
+ def copy_file(file_name)
75
+ self.file_handler = Copier.copy(file_name)
76
+ end
77
+
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,67 @@
1
+
2
+ module Geminabox
3
+ module Proxy
4
+ class Splicer < FileHandler
5
+
6
+ def self.make(file_name)
7
+ splicer = new(file_name)
8
+ splicer.create
9
+ splicer
10
+ end
11
+
12
+ def create
13
+ File.open(splice_path, 'w'){|f| f.write(new_content)}
14
+ end
15
+
16
+ def new_content
17
+ if local_file_exists?
18
+ merge_content
19
+ else
20
+ remote_content
21
+ end
22
+ end
23
+
24
+ def splice_path
25
+ proxy_path
26
+ end
27
+
28
+ def splice_folder_path
29
+ proxy_folder_path
30
+ end
31
+
32
+ def splice_file_exists?
33
+ file_exists? splice_path
34
+ end
35
+
36
+ def merge_content
37
+ if gzip?
38
+ merge_gziped_content
39
+ else
40
+ merge_text_content
41
+ end
42
+ end
43
+
44
+ def gzip?
45
+ /\.gz$/ =~ file_name
46
+ end
47
+
48
+ private
49
+ def merge_gziped_content
50
+ package(unpackage(local_content) | unpackage(remote_content))
51
+ end
52
+
53
+ def unpackage(content)
54
+ Marshal.load(Gem.gunzip(content))
55
+ end
56
+
57
+ def package(content)
58
+ Gem.gzip(Marshal.dump(content))
59
+ end
60
+
61
+ def merge_text_content
62
+ local_content.to_s + remote_content.to_s
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+
3
+ module Geminabox
4
+ module RubygemsDependency
5
+
6
+ class << self
7
+
8
+ def for(*gems)
9
+
10
+ url = [
11
+ rubygems_uri,
12
+ '?gems=',
13
+ gems.map(&:to_s).join(',')
14
+ ].join
15
+ body = Geminabox.http_adapter.get_content(url)
16
+ JSON.parse(body)
17
+ rescue Exception => e
18
+ return [] if Geminabox.allow_remote_failure
19
+ raise e
20
+ end
21
+
22
+ def rubygems_uri
23
+ "https://bundler.rubygems.org/api/v1/dependencies.json"
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,290 @@
1
+ module Geminabox
2
+
3
+ class Server < Sinatra::Base
4
+ enable :static, :methodoverride
5
+
6
+ def self.delegate_to_geminabox(*delegate_methods)
7
+ delegate_methods.each{|m| set m, Geminabox.send(m)}
8
+ end
9
+
10
+ delegate_to_geminabox(
11
+ :public_folder,
12
+ :data,
13
+ :build_legacy,
14
+ :incremental_updates,
15
+ :views,
16
+ :allow_replace,
17
+ :gem_permissions,
18
+ :allow_delete,
19
+ :rubygems_proxy
20
+ )
21
+
22
+ if Server.rubygems_proxy
23
+ use Proxy::Hostess
24
+ else
25
+ use Hostess
26
+ end
27
+
28
+ class << self
29
+ def disallow_replace?
30
+ ! allow_replace
31
+ end
32
+
33
+ def allow_delete?
34
+ allow_delete
35
+ end
36
+
37
+ def fixup_bundler_rubygems!
38
+ return if @post_reset_hook_applied
39
+ Gem.post_reset{ Gem::Specification.all = nil } if defined? Bundler and Gem.respond_to? :post_reset
40
+ @post_reset_hook_applied = true
41
+ end
42
+
43
+ def reindex(force_rebuild = false)
44
+ fixup_bundler_rubygems!
45
+ force_rebuild = true unless incremental_updates
46
+ if force_rebuild
47
+ indexer.generate_index
48
+ dependency_cache.flush
49
+ else
50
+ begin
51
+ require 'geminabox/indexer'
52
+ updated_gemspecs = Geminabox::Indexer.updated_gemspecs(indexer)
53
+ Geminabox::Indexer.patch_rubygems_update_index_pre_1_8_25(indexer)
54
+ indexer.update_index
55
+ updated_gemspecs.each { |gem| dependency_cache.flush_key(gem.name) }
56
+ rescue => e
57
+ puts "#{e.class}:#{e.message}"
58
+ puts e.backtrace.join("\n")
59
+ reindex(:force_rebuild)
60
+ end
61
+ end
62
+ end
63
+
64
+ def indexer
65
+ Gem::Indexer.new(data, :build_legacy => build_legacy)
66
+ end
67
+
68
+ def dependency_cache
69
+ @dependency_cache ||= Geminabox::DiskCache.new(File.join(data, "_cache"))
70
+ end
71
+ end
72
+
73
+
74
+
75
+ before do
76
+ headers 'X-Powered-By' => "geminabox #{Geminabox::VERSION}"
77
+ end
78
+
79
+ get '/' do
80
+ @gems = load_gems
81
+ @index_gems = index_gems(@gems)
82
+ erb :index
83
+ end
84
+
85
+ get '/atom.xml' do
86
+ @gems = load_gems
87
+ erb :atom, :layout => false
88
+ end
89
+
90
+ get '/api/v1/dependencies' do
91
+ query_gems.any? ? Marshal.dump(gem_list) : 200
92
+ end
93
+
94
+ get '/api/v1/dependencies.json' do
95
+ query_gems.any? ? gem_list.to_json : {}
96
+ end
97
+
98
+ get '/upload' do
99
+ erb :upload
100
+ end
101
+
102
+ get '/reindex' do
103
+ self.class.reindex(:force_rebuild)
104
+ redirect url("/")
105
+ end
106
+
107
+ get '/gems/:gemname' do
108
+ gems = Hash[load_gems.by_name]
109
+ @gem = gems[params[:gemname]]
110
+ halt 404 unless @gem
111
+ erb :gem
112
+ end
113
+
114
+ delete '/gems/*.gem' do
115
+ unless self.class.allow_delete?
116
+ error_response(403, 'Gem deletion is disabled - see https://github.com/cwninja/geminabox/issues/115')
117
+ end
118
+ File.delete file_path if File.exists? file_path
119
+ self.class.reindex(:force_rebuild)
120
+ redirect url("/")
121
+ end
122
+
123
+ post '/upload' do
124
+ unless params[:file] && params[:file][:filename] && (tmpfile = params[:file][:tempfile])
125
+ @error = "No file selected"
126
+ halt [400, erb(:upload)]
127
+ end
128
+ handle_incoming_gem(Geminabox::IncomingGem.new(tmpfile))
129
+ end
130
+
131
+ post '/api/v1/gems' do
132
+ begin
133
+ handle_incoming_gem(Geminabox::IncomingGem.new(request.body))
134
+ rescue Object => o
135
+ File.open "/tmp/debug.txt", "a" do |io|
136
+ io.puts o, o.backtrace
137
+ end
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def handle_incoming_gem(gem)
144
+ begin
145
+ GemStore.create(gem, params[:overwrite])
146
+ rescue GemStoreError => error
147
+ error_response error.code, error.reason
148
+ end
149
+
150
+ if api_request?
151
+ "Gem #{gem.name} received and indexed."
152
+ else
153
+ redirect url("/")
154
+ end
155
+ sleep 10
156
+ redirect url("/")
157
+ end
158
+
159
+ def api_request?
160
+ request.accept.first.to_s != "text/html"
161
+ end
162
+
163
+ def error_response(code, message)
164
+ halt [code, message] if api_request?
165
+ html = <<HTML
166
+ <html>
167
+ <head><title>Error - #{code}</title></head>
168
+ <body>
169
+ <h1>Error - #{code}</h1>
170
+ <p>#{message}</p>
171
+ </body>
172
+ </html>
173
+ HTML
174
+ halt [code, html]
175
+ end
176
+
177
+ def file_path
178
+ File.expand_path(File.join(settings.data, *request.path_info))
179
+ end
180
+
181
+ def dependency_cache
182
+ self.class.dependency_cache
183
+ end
184
+
185
+ def all_gems
186
+ all_gems_with_duplicates.inject(:|)
187
+ end
188
+
189
+ def all_gems_with_duplicates
190
+ specs_files_paths.map do |specs_file_path|
191
+ if File.exists?(specs_file_path)
192
+ Marshal.load(Gem.gunzip(Gem.read_binary(specs_file_path)))
193
+ else
194
+ []
195
+ end
196
+ end
197
+ end
198
+
199
+ def specs_file_types
200
+ [:specs, :prerelease_specs]
201
+ end
202
+
203
+ def specs_files_paths
204
+ specs_file_types.map do |specs_file_type|
205
+ File.join(settings.data, spec_file_name(specs_file_type))
206
+ end
207
+ end
208
+
209
+ def spec_file_name(specs_file_type)
210
+ [specs_file_type, Gem.marshal_version, 'gz'].join('.')
211
+ end
212
+
213
+ def load_gems
214
+ @loaded_gems ||= Geminabox::GemVersionCollection.new(all_gems)
215
+ end
216
+
217
+ def index_gems(gems)
218
+ Set.new(gems.map{|gem| gem.name[0..0].downcase})
219
+ end
220
+
221
+ def gem_list
222
+ settings.rubygems_proxy ? combined_gem_list : local_gem_list
223
+ end
224
+
225
+ def query_gems
226
+ params[:gems].to_s.split(',')
227
+ end
228
+
229
+ def local_gem_list
230
+ query_gems.map{|query_gem| gem_dependencies(query_gem) }.flatten(1)
231
+ end
232
+
233
+ def remote_gem_list
234
+ RubygemsDependency.for(*query_gems)
235
+ end
236
+
237
+ def combined_gem_list
238
+ GemListMerge.from(local_gem_list, remote_gem_list)
239
+ end
240
+
241
+ helpers do
242
+ def spec_for(gem_name, version, platform = default_platform)
243
+ filename = [gem_name, version]
244
+ filename.push(platform) if platform != default_platform
245
+ spec_file = File.join(settings.data, "quick", "Marshal.#{Gem.marshal_version}", "#{filename.join("-")}.gemspec.rz")
246
+ File::open(spec_file, 'r') do |unzipped_spec_file|
247
+ unzipped_spec_file.binmode
248
+ Marshal.load(Gem.inflate(unzipped_spec_file.read))
249
+ end if File.exists? spec_file
250
+ end
251
+
252
+ def default_platform
253
+ 'ruby'
254
+ end
255
+
256
+ # Return a list of versions of gem 'gem_name' with the dependencies of each version.
257
+ def gem_dependencies(gem_name)
258
+ dependency_cache.marshal_cache(gem_name) do
259
+ load_gems.
260
+ select { |gem| gem_name == gem.name }.
261
+ map { |gem| [gem, spec_for(gem.name, gem.number, gem.platform)] }.
262
+ reject { |(_, spec)| spec.nil? }.
263
+ map do |(gem, spec)|
264
+ {
265
+ :name => gem.name,
266
+ :number => gem.number.version,
267
+ :platform => gem.platform,
268
+ :dependencies => runtime_dependencies(spec)
269
+ }
270
+ end
271
+ end
272
+ end
273
+
274
+
275
+
276
+ def runtime_dependencies(spec)
277
+ spec.
278
+ dependencies.
279
+ select { |dep| dep.type == :runtime }.
280
+ map { |dep| name_and_requirements_for(dep) }
281
+ end
282
+
283
+ def name_and_requirements_for(dep)
284
+ name = dep.name.kind_of?(Array) ? dep.name.first : dep.name
285
+ [name, dep.requirement.to_s]
286
+ end
287
+ end
288
+ end
289
+
290
+ end