boxlet 0.0.1 → 0.0.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d5950f183d1274de5cd16237709c40ad58c65cf
4
+ data.tar.gz: 7ebc60f5a772512343f7d6778eb25ca227b2998f
5
+ SHA512:
6
+ metadata.gz: 94c93357b399e5855256009dc8bd9edcd3c18d4537cca5fdb2927d773021570148292b2d081b4b0db34d02a261093fab7f43827d6b9387ec0d353dfdf9171b72
7
+ data.tar.gz: 0667cdd101c8ebaf9c0a34d2975d3ec573a3b0eff0a08fb24ebdff0714f765108927efbd233eeb3da861bb6093831161e94ada72394f63ae5f47c39758753857
data/bin/boxlet CHANGED
@@ -1,12 +1,15 @@
1
- #! /usr/bin/env ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
- require "boxlet"
3
+ require 'boxlet'
4
4
 
5
- # Check to make sure we aren't running in Windows
6
- if RUBY_PLATFORM =~ /(win|w)32$/ then
7
- puts "Sorry, Boxlet is currently only available for Unix/Linux."
8
- exit!
5
+ argv = *ARGV
6
+ command = argv.count % 2 != 0 ? argv.shift : 'run'
7
+ argv_hash = Hash[*argv]
8
+
9
+ config = argv_hash["-c"] || argv_hash["--config"]
10
+ if !config
11
+ puts "WARNING: config file not specified, looking in current directory..."
12
+ config = 'config.yml'
9
13
  end
10
14
 
11
- server = Boxlet::Server.new
12
- server.run
15
+ Boxlet.run! argv_hash, command, config
data/boxlet.gemspec CHANGED
@@ -6,22 +6,30 @@ require "boxlet/version"
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "boxlet"
8
8
  spec.version = Boxlet::VERSION
9
- spec.authors = ["Clayton Liggitt", "arktisklada"]
9
+ spec.authors = ["arktisklada"]
10
10
  spec.email = ["mail@enorganik.com"]
11
- spec.description = %q{A server for Boxlet, a DIY, self-hosted file storage system}
12
- spec.summary = spec.description
13
- spec.homepage = "http://github.com/arktisklada/boxlet"
11
+ spec.summary = %q{Upload pics from your phone}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/arktisklada/boxlet"
14
14
  spec.license = "MIT"
15
15
 
16
- # spec.files = `git ls-files`.split($/)
17
16
  spec.files = %w[config.yml LICENSE.txt README.md Rakefile boxlet.gemspec]
18
17
  spec.files += Dir.glob('lib/**/*.rb')
18
+ spec.files += Dir.glob('lib/boxlet/app/*.rb')
19
19
 
20
- # spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
20
  spec.executables = 'boxlet'
22
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
- spec.require_paths = %w[lib]
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency "rack", "~> 1.5"
25
+ spec.add_dependency "rack-contrib", "~> 1.1"
26
+ spec.add_dependency "thin", "~> 1.6"
27
+
28
+ spec.add_runtime_dependency 'multi_json', '~> 1.10', '>= 1.10.1'
29
+ spec.add_runtime_dependency 'mongo', '~> 1.10', '>= 1.10.2'
30
+ spec.add_runtime_dependency 'bson_ext', '~> 1.10', '>= 1.10.2'
31
+ spec.add_runtime_dependency 'sys-filesystem', '~> 1.1', '>= 1.1.2'
24
32
 
25
33
  spec.add_development_dependency "bundler", "~> 1.3"
26
- spec.add_development_dependency "rake"
27
- end
34
+ spec.add_development_dependency "rake", "~> 10.3"
35
+ end
data/config.yml CHANGED
@@ -1,8 +1,32 @@
1
1
  # Boxlet configuration file
2
2
 
3
- # TCP Debug mode changes logging level between WARN (false) and DEBUG (true)
4
- tcp_debug: true
3
+ # Environment
4
+ environment: development
5
+ debug: true
5
6
 
6
- # Server configuration
7
- tcp_listen_ip: 127.0.0.1
8
- tcp_listen_port: 8077
7
+ # File system parameters
8
+ # path: ./
9
+ # upload_dir: ./uploads
10
+ # tmp_dir: /tmp
11
+ # file_system_root: /
12
+
13
+ # Capacity is either a percentage of available space on the drive or number in MB
14
+ capacity: 90%
15
+
16
+ # Server type and listen parameters
17
+ port: 8077
18
+ host: localhost
19
+ server_type: thin
20
+ daemonize: false
21
+
22
+ # Database config
23
+ db:
24
+ development:
25
+ host: localhost
26
+ db: boxlet_dev
27
+ production:
28
+ host: localhost
29
+ # port:
30
+ # user:
31
+ # pass:
32
+ db: boxlet
data/lib/boxlet.rb CHANGED
@@ -1,33 +1,73 @@
1
- require "fileutils"
2
1
  require "yaml"
3
-
2
+ require "rack/contrib"
4
3
  require "boxlet/version"
5
- require "boxlet/log"
6
- require "boxlet/tcp_server"
4
+ require "boxlet/app"
5
+ require "boxlet/config"
6
+ require "boxlet/runner"
7
+ require "pp" #unless ENV['RACK_ENV'] == 'production'
8
+
9
+
10
+ APP_ROOT = Dir.pwd
7
11
 
8
12
 
9
13
  module Boxlet
10
-
11
- class Server
12
- def initialize()
13
- # Load the config
14
- @@config = YAML.load_file("config.yml")
15
-
16
- if @@config['git_debug'] then
17
- Grit.debug = true
18
- end
19
-
20
- @server = TcpServer.new
21
- end
22
-
23
- public
24
-
25
- def self.config
26
- return @@config
27
- end
14
+
15
+ extend self
16
+ extend Boxlet::Config
17
+
18
+ attr_accessor :runner, :config, :raw_params, :raw_config
19
+
20
+ def run!(argv, command='run', config_file='config.yml', &blk)
21
+ populate_params!(argv, config_file)
28
22
 
29
- def run
30
- @server.start
23
+ app = Boxlet::App.new
24
+
25
+ case command
26
+ when 'run'
27
+ @runner = Boxlet::Runner.new
28
+ @runner.start(app.bind, &blk)
29
+ else
30
+ app.send(command, Boxlet.config)
31
31
  end
32
+
33
+ app
34
+ end
35
+
36
+ def stop!
37
+ @runner.stop
38
+ app
39
+ end
40
+
41
+
42
+ def debug?
43
+ @config[:debug] == true ? true : false
44
+ end
45
+
46
+ def config
47
+ @config
48
+ end
49
+
50
+ def params
51
+ @params
32
52
  end
53
+
33
54
  end
55
+
56
+
57
+ # Configure our temporary folder
58
+ class Dir
59
+ def Dir::tmpdir
60
+ tmp = './tmp'
61
+ if $SAFE > 0
62
+ tmp = @@systmpdir
63
+ else
64
+ for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp']
65
+ if dir and stat = File.stat(dir) and stat.directory? and stat.writable?
66
+ tmp = dir
67
+ break
68
+ end rescue nil
69
+ end
70
+ File.expand_path(tmp)
71
+ end
72
+ end
73
+ end
data/lib/boxlet/app.rb ADDED
@@ -0,0 +1,133 @@
1
+ require "sys/filesystem"
2
+ #require "rack/boxlet_url_builder"
3
+ require "rack/request"
4
+ require "rack/response"
5
+ require "rack/file_upload"
6
+ require "boxlet/db"
7
+ require "boxlet/app/router"
8
+
9
+
10
+ module Boxlet
11
+ class App
12
+
13
+ include Sys
14
+
15
+ def self.routes
16
+ routes = {
17
+ ["/", :get] => :index,
18
+ # ["/auth"] => :auth,
19
+ # ["/register_device", :post] => :register_device,
20
+ # ["/notifications", :*] => :notifications,
21
+ ["/stats", :post] => :stats,
22
+ ["/push_files", :post] => :push_files,
23
+ ["/file_list"] => :file_list,
24
+ ["/file_info"] => :file_info
25
+ }
26
+ end
27
+
28
+ def initialize
29
+ usage = Boxlet::App.app_space_usage
30
+ capacity = Boxlet::App.app_space_capacity
31
+ puts "Space Utilization: #{usage}MB / #{capacity}MB (#{(usage.to_f / capacity).round(3)}%)"
32
+ end
33
+
34
+ def bind
35
+ Rack::Builder.new do
36
+ use Rack::Reloader
37
+ # use Rack::Static, :urls => ["/public"]
38
+ use Rack::FileUpload, :upload_dir => [Boxlet.config[:upload_dir] || APP_ROOT + '/uploads']
39
+
40
+ Boxlet::App.routes.each do |route, action|
41
+ map route[0] do
42
+ run Boxlet::Router.new(route[1] || :*, action)
43
+ end
44
+ end
45
+
46
+ end.to_app
47
+ end
48
+
49
+
50
+ def setup(config)
51
+ begin
52
+ # Check for free space
53
+ if Boxlet::App.free_space <= 50
54
+ raise "Not enough free space"
55
+ end
56
+ if Boxlet::App.app_space_usage / Boxlet::App.app_space_capacity >= 0.9
57
+ puts "App is over 90% full"
58
+ end
59
+
60
+ # Create upload and tmp directories
61
+ upload_dir = Boxlet.config[:upload_dir]
62
+ tmp_dir = Boxlet.config[:tmp_dir]
63
+ if !File.exists?(upload_dir) || !File.exists?(tmp_dir)
64
+ if !File.exists?(upload_dir)
65
+ puts "Upload directory (#{upload_dir}) does not exist. Create? [y/n]"
66
+ create = gets.chomp
67
+ if create =~ /y/i
68
+ Dir.mkdir(upload_dir)
69
+ puts "Upload directory created!"
70
+ end
71
+ end
72
+ if !File.exists?(tmp_dir)
73
+ puts "Temp directory (#{tmp_dir}) does not exist. Create? [y/n]"
74
+ create = gets.chomp
75
+ if create =~ /y/i
76
+ Dir.mkdir(tmp_dir)
77
+ puts "Temp directory created!"
78
+ end
79
+ end
80
+ if File.exists?(upload_dir) && File.exists?(tmp_dir)
81
+ puts "Done creating directories."
82
+ else
83
+ raise "Error creating directories. Please check your config and file permissions, and retry."
84
+ end
85
+ end
86
+
87
+ puts "\nBoxlet setup is done!"
88
+ rescue => e
89
+ puts "\nERROR: #{e}"
90
+ end
91
+ end
92
+
93
+
94
+ # App disk space functions
95
+
96
+ def self.free_space
97
+ Boxlet::App.app_space_capacity - Boxlet::App.app_space_usage
98
+ end
99
+
100
+ def self.app_space_capacity
101
+ drive_free_space = Boxlet::App.drive_free_space
102
+ if Boxlet.config[:capacity].is_a? String
103
+ Boxlet::App.drive_free_space * Boxlet.config[:capacity].to_i / 100
104
+ else
105
+ Boxlet.config[:capacity]
106
+ end
107
+ end
108
+
109
+ def self.app_space_usage
110
+ raise RuntimeError, "#{Boxlet.config[:upload_dir]} is not a directory" unless File.directory?(Boxlet.config[:upload_dir])
111
+
112
+ total_size = 0
113
+ Dir["#{Boxlet.config[:upload_dir]}/**/*"].each do |f|
114
+ total_size += File.size(f) if File.file?(f) && File.size?(f)
115
+ end
116
+ total_size / 1000000 # / 1048576 # to megabytes
117
+ end
118
+
119
+ # Drive disk space functions
120
+
121
+ def self.drive_free_space
122
+ stat = Filesystem.stat(Boxlet.config[:file_system_root])
123
+ (stat.block_size * stat.blocks_available).to_mb
124
+ end
125
+
126
+ def self.drive_capacity
127
+ stat = Filesystem.stat(Boxlet.config[:file_system_root])
128
+ (stat.block_size * stat.blocks).to_mb
129
+ end
130
+
131
+
132
+ end
133
+ end
@@ -0,0 +1,168 @@
1
+ require 'date'
2
+
3
+ # routes = {
4
+ # ["/", :get] => :index,
5
+ # ["/auth"] => :auth,
6
+ # ["/register_device", :post] => :register_device,
7
+ # ["/notifications", :post] => :notifications,
8
+ # ["/push_files", :post] => :push_files,
9
+ # ["/file_list"] => :file_list,
10
+ # ["/file_info"] => :file_info
11
+ # }
12
+
13
+
14
+ module Boxlet
15
+ class Controller
16
+ attr_accessor :request, :format, :params
17
+
18
+ def initialize(request)
19
+ @request = request
20
+ @params = Boxlet.symbolize_keys request.params
21
+ pp request.params
22
+
23
+ @format = :html
24
+ end
25
+
26
+ def action(action)
27
+ action_response = self.send(action)
28
+ set_user if action =~ /push_files|file_list|file_info/
29
+
30
+ {format: @format, content: action_response}
31
+ end
32
+
33
+
34
+ # actions
35
+
36
+ def index
37
+ '<html><body><form action="/push_files" method="post" enctype="multipart/form-data">UUID:<input type="text" name="uuid"><br><input type="file" name="file"><input type="submit"></form>'
38
+ end
39
+
40
+ # def auth
41
+ # @format = :json
42
+ # "auth"
43
+ # end
44
+
45
+ # def register_device
46
+ # @format = :json
47
+
48
+ # pp @params if Boxlet.config[:debug]
49
+
50
+ # uuid = @params[:uuid]
51
+ # # user = user_model.merge { uuid: uuid }
52
+ # if db.collection('users').insert(user)
53
+ # {response: true}
54
+ # else
55
+ # {response: false}
56
+ # end
57
+ # end
58
+
59
+ # def notifications
60
+ # @format = :json
61
+
62
+ # uuid = @params[:uuid]
63
+ # notifications = @params[:notifications]
64
+ # pp uuid
65
+ # # @user
66
+ # "notifications"
67
+ # end
68
+
69
+ def stats
70
+ @format = :json
71
+
72
+ {
73
+ capacity: Boxlet::App.app_space_capacity,
74
+ usage: Boxlet::App.app_space_usage,
75
+ free_space: free_space?
76
+ }
77
+ end
78
+
79
+ def push_files
80
+ @format = :json
81
+
82
+ upload_path = user_upload_dir || './uploads'
83
+ upload_file = @params[:file]
84
+ asset_path = @params[:asset_path]
85
+ asset_path_params = Rack::Utils.parse_nested_query(asset_path[asset_path.index('?') + 1..-1])
86
+
87
+ new_filename = "#{asset_path_params["id"]}.#{asset_path_params["ext"]}"
88
+ new_path = File.join(upload_path, new_filename)
89
+ FileUtils.mv(upload_file[:tempfile].path, new_path)
90
+
91
+ if File.exists? new_path
92
+ file = File.open(new_path, 'r')
93
+ # asset_date = Date.parse(@params[:asset_date])
94
+ asset = {
95
+ filename: upload_file[:filename],
96
+ size: file.size,
97
+ local_date: file.mtime.to_i,
98
+ asset_path: @params[:asset_path],
99
+ asset_date: @params[:asset_date],
100
+ uuid: @params[:uuid]
101
+ }
102
+ db.collection('assets').insert(asset)
103
+ {response: true}
104
+ else
105
+ {response: false}
106
+ end
107
+ end
108
+
109
+ def file_list
110
+ @format = :json
111
+
112
+ uuid = @params[:uuid]
113
+ db.collection('assets').find({uuid: uuid}).to_a
114
+ end
115
+
116
+ def file_info
117
+ @format = :json
118
+
119
+ uuid = @params[:uuid]
120
+ asset_path = @params[:asset_path]
121
+ file_model.merge db.collection('assets').find({asset_path: asset_path, uuid: uuid}).to_a.first || {}
122
+ end
123
+
124
+
125
+ private
126
+
127
+ def db
128
+ Boxlet::Db.connection
129
+ end
130
+
131
+ def set_user
132
+ user_model.merge db.collection('users').find({uuid: @params[:uuid]}).to_a.first || {}
133
+ end
134
+
135
+ def user_upload_dir
136
+ user_upload_dir_name = Boxlet.config[:upload_dir] + "/" + (@params[:uuid] || '')
137
+ Dir.mkdir(user_upload_dir_name) unless File.exists?(user_upload_dir_name)
138
+ user_upload_dir_name
139
+ end
140
+
141
+ def free_space?
142
+ Boxlet::App.free_space > 50
143
+ end
144
+
145
+
146
+ # Models
147
+
148
+ def file_model
149
+ {
150
+ filename: '',
151
+ size: 0,
152
+ local_date: 0,
153
+ asset_path: '',
154
+ asset_date: '',
155
+ uuid: []
156
+ }
157
+ end
158
+
159
+ def user_model
160
+ {
161
+ uuid: '',
162
+ notifications: 1,
163
+ last_activity: Time.now
164
+ }
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,45 @@
1
+ require "rack/request"
2
+ require "rack/response"
3
+ require "boxlet/app/controller"
4
+ require "json"
5
+
6
+
7
+ module Boxlet
8
+ class Router
9
+
10
+ attr_accessor :method, :action
11
+
12
+ def initialize(method, action)
13
+ @method = method
14
+ @action = action
15
+ end
16
+
17
+ def call(env)
18
+ request = Rack::Request.new(env)
19
+
20
+ if Boxlet.debug?
21
+ puts "\n#{env["REMOTE_ADDR"]} - [#{Time.now.to_s}] #{@method.upcase} #{env["SERVER_PROTOCOL"]} => #{env["REQUEST_PATH"]}"
22
+ end
23
+
24
+ response = Rack::Response.new
25
+ controller = Boxlet::Controller.new(request)
26
+ if (@method == :* || request.get? && @method == :get) || (request.post? && @method == :post)
27
+ puts "Responding: #{@method.upcase} => #{@action}" if Boxlet.debug?
28
+ action_response = controller.action(@action)
29
+ response.status = 200
30
+ else
31
+ response.status = 404
32
+ raise "404"
33
+ end
34
+
35
+ if action_response[:format] == :json
36
+ response.write action_response[:content].to_json
37
+ else
38
+ response.write action_response[:content]
39
+ end
40
+
41
+ response.finish
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,119 @@
1
+ module Boxlet
2
+ module Config
3
+
4
+ ARGS = {
5
+ :path => {
6
+ :short => 'f',
7
+ :default => Proc.new { Dir.pwd }
8
+ },
9
+ :port => {
10
+ :short => 'p',
11
+ :default => 8077,
12
+ :sanitizer => Proc.new { |p| p.to_i }
13
+ },
14
+ :host => {
15
+ :short => 'o',
16
+ :default => 'localhost'
17
+ },
18
+ :server_type => {
19
+ :short => 's',
20
+ :default => 'rack',
21
+ :sanitizer => Proc.new { |p| p.to_sym }
22
+ },
23
+ :environment => {
24
+ :short => 'E',
25
+ :default => 'development'
26
+ },
27
+ :daemonize => {
28
+ :short => 'D',
29
+ :default => 'false',
30
+ :sanitizer => Proc.new { |p| p == 'true' }
31
+ },
32
+ :debug => {
33
+ :short => 'd',
34
+ :default => 'true',
35
+ :sanitizer => Proc.new { |p| p == 'true' }
36
+ },
37
+ :upload_dir => {
38
+ :short => 'U',
39
+ :default => './uploads'
40
+ },
41
+ :tmp_dir => {
42
+ :short => 'T',
43
+ :default => './tmp'
44
+ },
45
+ :file_system_root => {
46
+ :short => 'r',
47
+ :default => '/'
48
+ },
49
+ :capacity => {
50
+ :short => 'C',
51
+ :default => '90%',
52
+ :sanitizer => Proc.new { |p| (p.to_i.to_s == p) ? p.to_i : p }
53
+ }
54
+ }
55
+
56
+
57
+ def populate_params!(argv, path_to_config)
58
+ @raw_config = load_config_file(path_to_config)
59
+ @raw_params = parse_arguments(argv)
60
+ # @config = @raw_params.merge(@raw_config)
61
+ @config = @raw_config.merge(@raw_params)
62
+
63
+ @config[:debug] = @raw_config[:debug] || @raw_params[:debug]
64
+ if @config[:debug]
65
+ pp @config
66
+ end
67
+ end
68
+
69
+
70
+ def symbolize_keys(hash)
71
+ hash.inject({}){|result, (key, value)|
72
+ new_key = key.instance_of?(String) ? key.to_sym : key
73
+ new_value = value.instance_of?(Hash) ? symbolize_keys(value) : value
74
+
75
+ result[new_key] = new_value
76
+ result
77
+ }
78
+ end
79
+
80
+ private
81
+
82
+ def parse_arguments(argv)
83
+ params = Hash.new
84
+
85
+ ARGS.each_pair do |param_name, param_attrs|
86
+ param_short_name = param_attrs[:short]
87
+ config_value = @raw_config[param_name.to_sym]
88
+ default = param_attrs[:default]
89
+ sanitizer = param_attrs[:sanitizer]
90
+
91
+ param_value = argv["--#{param_name}"] ||
92
+ (param_short_name.nil? ? nil : argv["-#{param_short_name}"]) ||
93
+ (config_value.nil? ? nil : config_value) ||
94
+ (default.is_a?(Proc) ? default.call : default)
95
+
96
+ param_value = sanitizer.call(param_value) if sanitizer.is_a?(Proc)
97
+
98
+ if !param_value.nil?
99
+ params[param_name] = param_value
100
+ end
101
+ end
102
+
103
+ params
104
+ end
105
+
106
+ def load_config_file(path_to_config)
107
+ begin
108
+ loaded_config = YAML.load_file(path_to_config)
109
+ symbolize_keys(loaded_config)
110
+ rescue
111
+ puts "Error loading config file! Using defaults..."
112
+ {}
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+
119
+ end
data/lib/boxlet/db.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'mongo'
2
+
3
+
4
+ module Boxlet
5
+ module Db
6
+
7
+ extend self
8
+ include Mongo
9
+
10
+ attr_accessor :db
11
+
12
+ def connection
13
+ @db ||= self.connect
14
+ end
15
+
16
+ def connect
17
+ config = Boxlet.config
18
+ db_config = config[:db][config[:environment].to_sym || :development]
19
+
20
+ host = db_config[:host] || 'localhost'
21
+ port = db_config[:port] || MongoClient::DEFAULT_PORT
22
+ puts "Connecting to #{host}:#{port}" if Boxlet.debug?
23
+ client = MongoClient.new(host, port)
24
+ db = client.db(db_config[:db] || 'boxlet_development')
25
+ return db
26
+ end
27
+
28
+ end
29
+ end
data/lib/boxlet/log.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "logger"
2
2
  require "fileutils"
3
3
 
4
+
4
5
  module Log
5
6
  def get_logger(name, shift_age, shift_size, level)
6
7
  # create the directory for the log if it doesn't exist
@@ -0,0 +1,37 @@
1
+ require "handlers/thin"
2
+
3
+
4
+ module Boxlet
5
+ class Runner
6
+ attr_accessor :server, :server_instance
7
+
8
+ def start(app, &block)
9
+ params = Boxlet.config
10
+ environment = ENV['RACK_ENV'] || params[:environment] rescue 'development'
11
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
12
+
13
+ params[:Host] = params.delete(:host) || default_host
14
+ params[:Port] = params.delete(:port) || 8077
15
+
16
+ server_type = params.delete(:server_type) || :thin
17
+ @server_instance = self.send server_type.to_sym, app, params
18
+ @server_instance.start do |server|
19
+ self.server = server
20
+ block.call(server) if block_given?
21
+ end
22
+ end
23
+
24
+ def stop
25
+ @server.stop!
26
+ end
27
+
28
+ def rack(app, params)
29
+ Rack::Server.new(params.merge({app: app}))
30
+ end
31
+
32
+ def thin(app, params)
33
+ Boxlet::Handlers::Thin.new(app, params)
34
+ end
35
+
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Boxlet
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,34 @@
1
+ require "thin"
2
+ require "rack/content_length"
3
+ require "rack/chunked"
4
+
5
+
6
+ module Boxlet
7
+ module Handlers
8
+ class Thin
9
+ attr_accessor :app, :params
10
+
11
+ def initialize(app, params={})
12
+ @app = app
13
+ @params = params
14
+ # super
15
+ end
16
+
17
+ # ported from https://github.com/rack/rack/tree/master/lib/rack/handler/thin.rb
18
+ def start
19
+ # environment = ENV['RACK_ENV'] || 'development'
20
+ # default_host = environment == 'development' ? : '0.0.0.0'
21
+ host = @params.delete(:Host) || 'localhost'
22
+ port = @params.delete(:Port) || 8077
23
+
24
+ args = [host, port, @app, @params]
25
+
26
+ # Thin versions below 0.8.0 do not support additional options
27
+ args.pop if ::Thin::VERSION::MAJOR < 1 && ::Thin::VERSION::MINOR < 8
28
+ server = ::Thin::Server.new(*args)
29
+ yield server if block_given?
30
+ server.start
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require "rack"
2
+ require "rack/boxlet_url_map"
3
+
4
+
5
+ module Rack
6
+ class BoxletUrlBuilder < Builder
7
+
8
+ private
9
+
10
+ def generate_map(default_app, mapping)
11
+ mapped = default_app ? {'/' => default_app} : {}
12
+ mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
13
+ BoxletUrlMap.new(mapped)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,66 @@
1
+ require "rack"
2
+
3
+
4
+ module Rack
5
+ class BoxletUrlMap < URLMap
6
+
7
+ def remap(map)
8
+ @mapping = map.map { |location, app|
9
+ if location.is_a? String
10
+ if location =~ %r{\Ahttps?://(.*?)(/.*)}
11
+ host, location = $1, $2
12
+ else
13
+ host = nil
14
+ end
15
+
16
+ unless location[0] == ?/
17
+ raise ArgumentError, "paths need to start with /"
18
+ end
19
+
20
+ location = location.chomp('/')
21
+ match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
22
+ elsif location.is_a? Regexp
23
+ match = location
24
+ else
25
+ raise ArgumentError, "location should be an instance of String or Regexp"
26
+ end
27
+
28
+ [host, location, match, app]
29
+ }.sort_by do |(host, location, _, _)|
30
+ [host ? -host.size : INFINITY, (location.is_a?(String) ? -location.size : INFINITY)]
31
+ end
32
+
33
+ @mapping
34
+ end
35
+
36
+ def call(env)
37
+ path = env["PATH_INFO"]
38
+ script_name = env['SCRIPT_NAME']
39
+ hHost = env['HTTP_HOST']
40
+ sName = env['SERVER_NAME']
41
+ sPort = env['SERVER_PORT']
42
+
43
+ @mapping.each do |host, location, match, app|
44
+ unless hHost == host \
45
+ || sName == host \
46
+ || (!host && (hHost == sName || hHost == sName+':'+sPort))
47
+ next
48
+ end
49
+
50
+ next unless m = match.match(path.to_s)
51
+
52
+ rest = m[1]
53
+ next unless !rest || rest.empty? || rest[0] == ?/
54
+
55
+ return app.call(env)
56
+ end
57
+
58
+ [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
59
+
60
+ ensure
61
+ env['PATH_INFO'] = path
62
+ env['SCRIPT_NAME'] = script_name
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,121 @@
1
+ # ported from https://github.com/New-Bamboo/rack-raw-upload
2
+ require 'multi_json'
3
+
4
+
5
+ module Rack
6
+ class FileUpload
7
+
8
+ def initialize(app, options={})
9
+ @app = app
10
+ @paths = [options[:upload_dir]]
11
+ @explicit = options[:explicit]
12
+ @tmpdir = options[:tmp_dir] || Dir::tmpdir
13
+ @paths = [@paths] if @paths.kind_of?(String)
14
+ end
15
+
16
+ def call(env)
17
+ kick_in?(env) ? convert_and_pass_on(env) : @app.call(env)
18
+ end
19
+
20
+ def upload_path?(request_path)
21
+ return true if @paths.nil?
22
+
23
+ @paths.any? do |candidate|
24
+ literal_path_match?(request_path, candidate) || wildcard_path_match?(request_path, candidate)
25
+ end
26
+ end
27
+
28
+
29
+ private
30
+
31
+ def convert_and_pass_on(env)
32
+ if env['rack.input'].kind_of?(Tempfile)
33
+ env['rack.input'].extend(EqlFix)
34
+ tempfile = env['rack.input']
35
+ else
36
+ tempfile = Tempfile.new('raw-upload.', @tmpdir)
37
+
38
+ env['rack.input'].each do |chunk|
39
+ if chunk.respond_to?(:force_encoding)
40
+ tempfile << chunk.force_encoding('UTF-8')
41
+ else
42
+ tempfile << chunk
43
+ end
44
+ end
45
+ env['rack.input'].rewind
46
+
47
+ tempfile.flush
48
+ tempfile.rewind
49
+ end
50
+ fake_file = {
51
+ :filename => env['HTTP_X_FILE_NAME'],
52
+ :type => env['CONTENT_TYPE'],
53
+ :tempfile => tempfile,
54
+ }
55
+ env['rack.request.form_input'] = env['rack.input']
56
+ env['rack.request.form_hash'] ||= {}
57
+ env['rack.request.query_hash'] ||= {}
58
+ env['rack.request.form_hash']['file'] = fake_file
59
+ env['rack.request.query_hash']['file'] = fake_file
60
+ if env['HTTP_X_QUERY_PARAMS']
61
+ env['rack.errors'].puts("Warning! The header X-Query-Params is deprecated. Please use X-JSON-Params instead.")
62
+ inject_json_params!(env, env['HTTP_X_QUERY_PARAMS'])
63
+ end
64
+ if env['HTTP_X_JSON_PARAMS']
65
+ inject_json_params!(env, env['HTTP_X_JSON_PARAMS'])
66
+ end
67
+ if env['HTTP_X_PARAMS']
68
+ inject_query_params!(env, env['HTTP_X_PARAMS'])
69
+ end
70
+ @app.call(env)
71
+ end
72
+
73
+ def kick_in?(env)
74
+ env['HTTP_X_FILE_UPLOAD'] == 'true' ||
75
+ ! @explicit && env['HTTP_X_FILE_UPLOAD'] != 'false' && raw_file_upload?(env) ||
76
+ env.has_key?('HTTP_X_FILE_UPLOAD') && env['HTTP_X_FILE_UPLOAD'] != 'false' && raw_file_upload?(env)
77
+ end
78
+
79
+ def raw_file_upload?(env)
80
+ upload_path?(env['PATH_INFO']) &&
81
+ %{POST PUT}.include?(env['REQUEST_METHOD']) &&
82
+ content_type_of_raw_file?(env['CONTENT_TYPE']) &&
83
+ env['CONTENT_LENGTH'].to_i > 0
84
+ end
85
+
86
+ def literal_path_match?(request_path, candidate)
87
+ candidate == request_path
88
+ end
89
+
90
+ def wildcard_path_match?(request_path, candidate)
91
+ return false unless candidate.include?('*')
92
+ regexp = '^' + candidate.gsub('.', '\.').gsub('*', '[^/]*') + '$'
93
+ !! (Regexp.new(regexp) =~ request_path)
94
+ end
95
+
96
+ def content_type_of_raw_file?(content_type)
97
+ case content_type
98
+ when %r{^application/x-www-form-urlencoded}, %r{^multipart/form-data}
99
+ false
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ def random_string
106
+ (0...8).map{65.+(rand(25)).chr}.join
107
+ end
108
+
109
+ def inject_json_params!(env, params)
110
+ json = MultiJson.load(params)
111
+ env['rack.request.form_hash'].merge!(json)
112
+ env['rack.request.query_hash'].merge!(json)
113
+ end
114
+
115
+ def inject_query_params!(env, params)
116
+ json = Rack::Utils.parse_query(params)
117
+ env['rack.request.form_hash'].merge!(json)
118
+ env['rack.request.query_hash'].merge!(json)
119
+ end
120
+ end
121
+ end
metadata CHANGED
@@ -1,50 +1,166 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boxlet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
6
5
  platform: ruby
7
6
  authors:
8
- - Clayton Liggitt
9
7
  - arktisklada
10
8
  autorequire:
11
9
  bindir: bin
12
10
  cert_chain: []
13
- date: 2014-06-02 00:00:00.000000000 Z
11
+ date: 2014-07-10 00:00:00.000000000 Z
14
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack-contrib
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thin
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.10'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 1.10.1
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '1.10'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 1.10.1
75
+ - !ruby/object:Gem::Dependency
76
+ name: mongo
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.10'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 1.10.2
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '1.10'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 1.10.2
95
+ - !ruby/object:Gem::Dependency
96
+ name: bson_ext
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.10'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 1.10.2
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '1.10'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 1.10.2
115
+ - !ruby/object:Gem::Dependency
116
+ name: sys-filesystem
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '1.1'
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 1.1.2
125
+ type: :runtime
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.1'
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.1.2
15
135
  - !ruby/object:Gem::Dependency
16
136
  name: bundler
17
137
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
138
  requirements:
20
- - - ~>
139
+ - - "~>"
21
140
  - !ruby/object:Gem::Version
22
141
  version: '1.3'
23
142
  type: :development
24
143
  prerelease: false
25
144
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
145
  requirements:
28
- - - ~>
146
+ - - "~>"
29
147
  - !ruby/object:Gem::Version
30
148
  version: '1.3'
31
149
  - !ruby/object:Gem::Dependency
32
150
  name: rake
33
151
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
152
  requirements:
36
- - - ! '>='
153
+ - - "~>"
37
154
  - !ruby/object:Gem::Version
38
- version: '0'
155
+ version: '10.3'
39
156
  type: :development
40
157
  prerelease: false
41
158
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
159
  requirements:
44
- - - ! '>='
160
+ - - "~>"
45
161
  - !ruby/object:Gem::Version
46
- version: '0'
47
- description: A server for Boxlet, a DIY, self-hosted file storage system
162
+ version: '10.3'
163
+ description: Upload pics from your phone
48
164
  email:
49
165
  - mail@enorganik.com
50
166
  executables:
@@ -52,39 +168,47 @@ executables:
52
168
  extensions: []
53
169
  extra_rdoc_files: []
54
170
  files:
55
- - config.yml
56
171
  - LICENSE.txt
57
172
  - README.md
58
173
  - Rakefile
174
+ - bin/boxlet
59
175
  - boxlet.gemspec
176
+ - config.yml
177
+ - lib/boxlet.rb
178
+ - lib/boxlet/app.rb
179
+ - lib/boxlet/app/controller.rb
180
+ - lib/boxlet/app/router.rb
181
+ - lib/boxlet/config.rb
182
+ - lib/boxlet/db.rb
60
183
  - lib/boxlet/log.rb
61
- - lib/boxlet/tcp_server.rb
184
+ - lib/boxlet/runner.rb
62
185
  - lib/boxlet/version.rb
63
- - lib/boxlet.rb
64
- - bin/boxlet
65
- homepage: http://github.com/arktisklada/boxlet
186
+ - lib/handlers/thin.rb
187
+ - lib/rack/boxlet_url_builder.rb
188
+ - lib/rack/boxlet_url_map.rb
189
+ - lib/rack/file_upload.rb
190
+ homepage: https://github.com/arktisklada/boxlet
66
191
  licenses:
67
192
  - MIT
193
+ metadata: {}
68
194
  post_install_message:
69
195
  rdoc_options: []
70
196
  require_paths:
71
197
  - lib
72
198
  required_ruby_version: !ruby/object:Gem::Requirement
73
- none: false
74
199
  requirements:
75
- - - ! '>='
200
+ - - ">="
76
201
  - !ruby/object:Gem::Version
77
202
  version: '0'
78
203
  required_rubygems_version: !ruby/object:Gem::Requirement
79
- none: false
80
204
  requirements:
81
- - - ! '>='
205
+ - - ">="
82
206
  - !ruby/object:Gem::Version
83
207
  version: '0'
84
208
  requirements: []
85
209
  rubyforge_project:
86
- rubygems_version: 1.8.23
210
+ rubygems_version: 2.2.2
87
211
  signing_key:
88
- specification_version: 3
89
- summary: A server for Boxlet, a DIY, self-hosted file storage system
212
+ specification_version: 4
213
+ summary: Upload pics from your phone
90
214
  test_files: []
@@ -1,62 +0,0 @@
1
- require "socket"
2
-
3
- class TcpServer
4
-
5
- include Log
6
-
7
- def initialize()
8
- @listen_ip = Boxlet::Server.config['tcp_listen_ip'] || '127.0.0.1'
9
- @listen_port = Boxlet::Server.config['tcp_listen_port'] || 11311
10
-
11
- @log = get_logger('log/tcp.log', 10, 1024000, Boxlet::Server.config['tcp_debug'] ? Logger::DEBUG : Logger::WARN);
12
- @log.info("Initializing TCP server on #{@listen_ip}:#{@listen_port}")
13
- @server = TCPServer.open(@listen_ip, @listen_port)
14
-
15
- @connections = Hash.new
16
- end
17
-
18
- def start
19
- @log.info("Starting TCP server")
20
-
21
- # Start a new thread for the TCP server so that it does not block and
22
- # hold up the rest of the program execution
23
- # Thread.start() do
24
- loop {
25
- # When a new connection comes in, create a new thread and begin handling
26
- # requests
27
- Thread.start(@server.accept) do |client|
28
- @log.info("Client connected!")
29
-
30
- # Output welcome messages
31
- client.puts Time.now.ctime
32
- client.puts "Welcome to Boxlet!"
33
-
34
- # Read input from TCP client
35
- while line = client.gets
36
- entry = line.split
37
- cmd = entry.shift
38
-
39
- case cmd
40
- # Retrieve config value
41
- when "config_get" then client.puts(Boxlet::Server.config[entry[0]] || "ERROR")
42
-
43
- # Halt the server
44
- when "stop" then
45
- client.puts "Daemon halting!"
46
- @log.info("Daemon halting!")
47
- exit(0)
48
-
49
- # Disconnect the client
50
- when "quit" then
51
- client.puts "Goodbye!"
52
- client.close
53
- else
54
- client.puts "INVALID"
55
- end
56
- end
57
- end
58
- }
59
- # end
60
- end
61
-
62
- end