xfiles 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +15 -0
  2. data/bin/xfiles +406 -0
  3. metadata +58 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Yzk5YzAzY2ViNWU5MzgyYzBlNTVhODAwN2VhMzgxMWVkNWIxMDEwNg==
5
+ data.tar.gz: !binary |-
6
+ MTY5MGQzODU3NWQxNmJmNzRhNmQxODhhMWFmNTVhMjM0MjJiOTU5YQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Y2FiMWNkOWEwZWU4NDc5OTMwZTQ4MTQxM2E1MzY5MDVlNmQ1MTk5ZDVmZmI2
10
+ MTdjZmRkNjE3ZmFiNDkyYjUxNTlmOTZjMzU5Njc0YmIyYWY4MTNkZGE0N2Ux
11
+ NmQwNTc0ZGUwZDI4ODUyYmM3YjJkZWY0MjZiMjNjY2Y3Njg5MmQ=
12
+ data.tar.gz: !binary |-
13
+ YTc1NzNiZTk4N2RjY2ZlNWY5OGU5MDBiN2VjYzA3NzM0Y2QxOWIxMmI2M2U1
14
+ NGRjZTc2YmQyM2VkNjJhNGYyYTJkNGRhZDg3Mzc0MjRiZmJlY2VjOGJmZDQ4
15
+ ZDFkNmE2MmJlMmM2NmJhZTcwZGZjMTNkMWRlZDBmNDYxNTY0N2U=
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env ruby
2
+ VERSION="1.6"
3
+ require 'socket'
4
+ require 'ostruct'
5
+ require 'rubygems'
6
+ require 'optparse'
7
+
8
+ #colorization
9
+ class String
10
+ def colorize(color_code) "\e[#{color_code}m#{self}\e[0m" end
11
+ def red() colorize(31) end
12
+ def lred() colorize(91) end
13
+ def green() colorize(32) end
14
+ def lgreen() colorize(92) end
15
+ def orange() colorize('38;5;208') end
16
+ def yellow() colorize(93) end
17
+ def blue() colorize(34) end
18
+ def pink() colorize(35) end
19
+ end
20
+
21
+ class XFilesConfig < OpenStruct
22
+ def timeout?
23
+ !(self.timeout_state == false)
24
+ end
25
+ def timeout
26
+ return self.timeout_override if self.timeout_override
27
+ self.secure? ? self.default_secure_timeout : self.default_insecure_timeout
28
+ end
29
+ def disable_timeout
30
+ self.timeout_state = false
31
+ end
32
+ def timeout=(seconds)
33
+ self.timeout_override=seconds
34
+ end
35
+ def secure?
36
+ !!(self.secure && self.secure.to_sym != :none)
37
+ end
38
+ def overwrite?
39
+ !!self.overwrite
40
+ end
41
+ def versioned?
42
+ !!self.versioned
43
+ end
44
+ end
45
+
46
+ config = XFilesConfig.new(
47
+ :secure=>:access_code,
48
+ :default_insecure_timeout=>2*60,
49
+ :default_secure_timeout=>30*60,
50
+ :overwrite=>false,
51
+ :versioned=>false,
52
+ :name=>'xFiles Server'
53
+ )
54
+
55
+ optparse = OptionParser.new do |opts|
56
+ opts.banner="Usage: #{File.basename($0)} [ACCESS-CODE] [OPTIONS]"
57
+ opts.program_name=config.name
58
+ opts.release=VERSION
59
+ opts.separator ''
60
+ opts.separator " Access options:"
61
+ opts.on('--no-auth', 'Do not require authorization') do
62
+ config.secure = nil
63
+ end
64
+ opts.on('--timeout=SECONDS', Integer, "Overrides default timeout") do |seconds|
65
+ config.timeout=seconds.to_i
66
+ end
67
+ opts.on('--no-timeout', "Do not use timeout") do
68
+ config.disable_timeout
69
+ end
70
+ opts.separator ''
71
+ opts.separator " Upload options:"
72
+ opts.on('--version-uploads', "Add version number to pre-existing files") do
73
+ config.versioned = true
74
+ end
75
+ opts.on('--overwrite', "Overwrite pre-existing files") do
76
+ config.overwrite=true
77
+ end
78
+ opts.separator ''
79
+ opts.on('--debug') do
80
+ require 'debugger'
81
+ Debugger.settings[:autoeval]=true
82
+ Debugger.settings[:autolist]=1
83
+ Debugger.settings[:listsize]=20
84
+ end
85
+
86
+ opts.on('-v', '--version', 'Show version') do
87
+ puts VERSION
88
+ exit
89
+ end
90
+ opts.separator ''
91
+ end
92
+
93
+ optparse.parse!
94
+ if config.secure? && (ARGV[0].nil? || ARGV[0] !~ /[_0-9a-zA-Z]+/)
95
+ puts "First argument must be an access code".orange
96
+ puts
97
+ exit
98
+ end
99
+
100
+ ACCESS_CODE||=ARGV[0]
101
+ start_message=["Starting #{config.name} with".green]
102
+ start_message << 'NO ACCESS CODE'.orange unless config.secure?
103
+ start_message << 'access code: '.green + ACCESS_CODE.orange if config.secure?
104
+ puts start_message.join(' ')
105
+ unless config.secure?
106
+ puts "WARNING: Anyone can download or upload files without an access code".orange
107
+ end
108
+
109
+ addresses = Socket.ip_address_list.map{|a| a.ip_address}.select do |ip|
110
+ ip =~ /^([0-9]{1,3}\.){3}[0-9]+$/ && ip != "127.0.0.1"
111
+ end.map{|ip| "http://#{ip}:2021" }
112
+ puts "#{config.name} available at: ".green + addresses.join(', ').yellow
113
+
114
+ def seconds_by_unit(seconds)
115
+ case
116
+ when seconds/60 == 0 then "#{seconds} seconds"
117
+ when seconds/3600 == 0 then "%0.1f minutes" % (seconds*1.0/60)
118
+ when seconds/86400 == 0 then "%0.1f hours" % (seconds*1.0/3600)
119
+ else "%0.1f days" % (seconds*1.0/86400)
120
+ end.sub(/\.0+\s/, ' ')
121
+ end
122
+
123
+ # -- START SINATRA
124
+ require 'sinatra'
125
+
126
+ if config.timeout?
127
+ t=Thread.new(config.timeout) do |seconds|
128
+ text = seconds_by_unit(seconds)
129
+ puts "Server will live for #{text}".green
130
+ sleep(seconds)
131
+ puts "Server has timed out after #{text}. Ending session now.".orange
132
+ Process.kill 'TERM', Process.pid
133
+ end
134
+ end
135
+
136
+ # -- Sinatra Controller code starts here
137
+
138
+ use Rack::Session::Cookie, :key => 'xfile_server',
139
+ :path => '/',
140
+ :expire_after => 3600, # In seconds
141
+ :secret => 'qridufkasnvudksursnfjsdl'
142
+
143
+ #set :environment, 'production'
144
+ set :port, 2021
145
+ set :bind, "0.0.0.0"
146
+ enable :sessions
147
+
148
+ FLASH={}
149
+ def clear_flash
150
+ FLASH.clear
151
+ end
152
+
153
+ def flash
154
+ FLASH
155
+ end
156
+
157
+ configure do
158
+ mime_type :css, 'text/css'
159
+ end
160
+
161
+ before do
162
+ expires 0, :public, :must_revalidate
163
+ end
164
+
165
+ get "/" do
166
+ @hostname=Socket.gethostname
167
+ @config = config
168
+ @pwd=Dir.pwd
169
+ @files=Dir.glob('*').select{|fn| File.file?(fn) }.sort
170
+ @authorized=session[:access_code] && session[:access_code] == ACCESS_CODE
171
+ #haml :upload_haml
172
+ output = if config.secure? && !@authorized
173
+ erb :access_erb
174
+ else
175
+ erb :upload_erb
176
+ end
177
+ clear_flash
178
+ output
179
+ end
180
+
181
+ get "/css/style.css" do
182
+ content_type :css
183
+ erb :style
184
+ end
185
+
186
+ get "/download/:filename" do |filename|
187
+ if File.exist?(filename)
188
+ puts "Sent file '#{filename}' to #{request.ip}".yellow
189
+ send_file filename, :filename=>filename, :type=>"application/octet-stream"
190
+ else
191
+ redirect "/"
192
+ end
193
+ end
194
+
195
+ post "/access" do
196
+ if params[:code] == ACCESS_CODE
197
+ session[:access_code] = ACCESS_CODE
198
+ puts "Access granted to #{request.ip}".green
199
+ else
200
+ flash[:error]='Incorrect access code!'
201
+ puts "Access denied to #{request.ip}".orange
202
+ end
203
+ redirect "/"
204
+ end
205
+
206
+ get "/access" do
207
+ redirect "/"
208
+ end
209
+
210
+ get "/upload" do
211
+ redirect "/"
212
+ end
213
+
214
+ def versioned_filename(filename, version)
215
+ parts = filename.split(/\./)
216
+ parts[0] = parts[0] + "(v#{version})"
217
+ parts.join('.')
218
+ end
219
+ def unversioned_filename(filename)
220
+ filename.sub(/(\w)\(v\d+\)(\.|$)/, '\1\2')
221
+ end
222
+
223
+ def find_free_version_number(filename)
224
+ version = 1
225
+ while File.exist?(versioned_filename(filename, version)) do
226
+ version += 1;
227
+ end
228
+ version
229
+ end
230
+
231
+ def advance_versions(filename, version)
232
+ moveto = versioned_filename(filename, version)
233
+ version.downto(2) do |v|
234
+ orig = versioned_filename(filename, v-1)
235
+ File.rename(orig, moveto) if File.exist?(orig)
236
+ moveto = orig
237
+ end
238
+ File.rename(filename, moveto) if File.exist?(filename)
239
+ end
240
+
241
+ def file_size(filename)
242
+ size = File.size(filename)*1.0
243
+ for index in 0..3
244
+ break if size < 1024
245
+ size /= 1024
246
+ end
247
+ "%.2f %s" % [size, %w(bytes KB MB GB)[index]]
248
+ end
249
+
250
+ post "/upload" do
251
+ if params['file'] && params['file'][:filename] && !params['file'][:filename].empty?
252
+ filename=params['file'][:filename]
253
+ unless config.overwrite?
254
+ if config.versioned?
255
+ filename = unversioned_filename(filename)
256
+ free_version = find_free_version_number(filename)
257
+ advance_versions(filename, free_version)
258
+ else
259
+ index=0
260
+ while File.exist?(filename) do
261
+ index+=1
262
+ filename=params['file'][:filename].sub(/\./, "(#{index}).")
263
+ end
264
+ end
265
+ end
266
+ File.open(filename, "w") {|f| f.write(params['file'][:tempfile].read) }
267
+ puts "Received file '#{filename}' (#{file_size(filename)}) from #{request.ip}".yellow
268
+ end
269
+ redirect "/"
270
+ end
271
+
272
+
273
+ __END__
274
+
275
+ @@ style
276
+
277
+ body {
278
+ margin: 0;
279
+ padding: 0;
280
+ }
281
+
282
+ #page {
283
+ margin: 0;
284
+ padding-bottom: 65px;
285
+ }
286
+ #header {
287
+ margin-bottom: 15px;
288
+ width: 100%;
289
+ background-color: lightblue;
290
+ }
291
+ #directory {
292
+ margin-top: 25px;
293
+ }
294
+ h3 {
295
+ margin-top: 0;
296
+ margin-bottom: 2px;
297
+ font-weight: normal;
298
+ }
299
+ hr {
300
+ margin-top: 0;
301
+ margin-bottom: 5px;
302
+ }
303
+ #files {
304
+ font-weight: bold;
305
+ }
306
+ form#upload {
307
+ margin-top: 20px;
308
+ }
309
+ #page>h1, #page>h3, #page>form, #page>ul, #page>p {
310
+ padding-left: 20px;
311
+ }
312
+ #page>ul {
313
+ list-style-type: none;
314
+ }
315
+
316
+ @@ access_erb
317
+
318
+ <html>
319
+ <head>
320
+ <title><%= @config.name %> -- Access</title>
321
+ <link rel='stylesheet' type='text/css' href='/css/style.css'/>
322
+ </head>
323
+ <body>
324
+ <div id='page'>
325
+ <h1 id='header'><%= @config.name %> on <%=@hostname %></h1>
326
+ <h3 id='directory'>Location: <%=@pwd.gsub(/[^\/]/, '*') %></h3><hr/>
327
+ <br/>
328
+ <form id='access' method="post" action='/access'>
329
+ <% if flash[:error] %>
330
+ <p style='color: red'><%=flash[:error] %></p>
331
+ <% end %>
332
+ Access Code: <input type='text' name='code' autofocus /><br/>
333
+ <input type='submit' value='Access'/>
334
+ </form>
335
+ <br/>
336
+ <hr/>
337
+ </div>
338
+ </body>
339
+ </html>
340
+
341
+ @@ upload_erb
342
+
343
+ <html>
344
+ <head>
345
+ <title><%= @config.name %></title>
346
+ <link rel='stylesheet' type='text/css' href='/css/style.css'/>
347
+ <meta http-equiv='refresh' content="60;url=/" />
348
+ <script type='text/javascript'>
349
+ function close_confirm(elem) {
350
+ elem.parentNode.style.visibility='hidden';
351
+ stop_propagation();
352
+ }
353
+ function hide_all_confirms() {
354
+ targets = document.getElementsByClassName('confirm');
355
+ for(i=0; i<targets.length; i++) {
356
+ targets[i].style.visibility='hidden';
357
+ }
358
+ }
359
+ function stop_propagation() {
360
+ if (window.event) {
361
+ if (window.event.stopPropagation) {
362
+ window.event.stopPropagation();
363
+ } else {
364
+ window.event.cancelBubble = true;
365
+ }
366
+ }
367
+ }
368
+ function show_sibling(elem) {
369
+ target = elem.parentNode.getElementsByTagName("div")[0];
370
+ if (target.style.visibility == 'visible') {
371
+ target.style.visibility = 'hidden';
372
+ } else {
373
+ hide_all_confirms();
374
+ target.style.visibility='visible';
375
+ }
376
+ stop_propagation();
377
+ }
378
+ </script>
379
+ </head>
380
+ <body onclick="hide_all_confirms();">
381
+ <div id='page'>
382
+ <h1 id='header'><%= @config.name %> on <%=@hostname %></h1>
383
+ <h3 id='directory'>Location: <%=@pwd %></h3><hr/>
384
+ <% if @files.empty? %>
385
+ <p>No files in directory</p>
386
+ <% else %>
387
+ <ul id='files'><% @files.each do |file| %>
388
+ <li>
389
+ <a href='' onclick="show_sibling(this); return false;"><%=file %></a>
390
+ <div class='confirm' style='visibility: hidden; display: inline-block;'>
391
+ &nbsp; <==&nbsp; download?&nbsp;
392
+ <a href="/download/<%=file %>" onclick="close_confirm(this); return true;">Yes</a>
393
+ <a href='' onclick="close_confirm(this); return false;">No</a>
394
+ </div>
395
+ </li>
396
+ <% end %></ul>
397
+ <% end %><hr/>
398
+ <form id='upload' method="post" action='/upload' enctype='multipart/form-data'>
399
+ <h3>Upload File</h3>
400
+ <input type='file' name='file'/><br/>
401
+ <input type='submit' value='Upload'/>
402
+ </form>
403
+ </div>
404
+ </body>
405
+ </html>
406
+
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xfiles
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.6.0
5
+ platform: ruby
6
+ authors:
7
+ - R2dR
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.3
27
+ description: Easy file transfer web interface built on Sinatra
28
+ email: github@r2dr.com
29
+ executables:
30
+ - xfiles
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/xfiles
35
+ homepage: http://github.com/r2dr/xfiles
36
+ licenses: []
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.0.6
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Sinatra file transfer web interface
58
+ test_files: []