sinatra-s3 0.98

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.
Files changed (75) hide show
  1. data/README +23 -0
  2. data/Rakefile +51 -0
  3. data/bin/sinatra-s3 +30 -0
  4. data/db/migrate/001_create_bits.rb +28 -0
  5. data/db/migrate/002_create_users.rb +24 -0
  6. data/db/migrate/003_create_bits_users.rb +16 -0
  7. data/db/migrate/004_create_torrents.rb +22 -0
  8. data/db/migrate/005_create_torrent_peers.rb +26 -0
  9. data/examples/README +9 -0
  10. data/examples/wiki.rb +199 -0
  11. data/examples/wiki.ru +5 -0
  12. data/examples/wikicloth/MIT-LICENSE +20 -0
  13. data/examples/wikicloth/README +81 -0
  14. data/examples/wikicloth/Rakefile +23 -0
  15. data/examples/wikicloth/init.rb +1 -0
  16. data/examples/wikicloth/install.rb +0 -0
  17. data/examples/wikicloth/lib/core_ext.rb +43 -0
  18. data/examples/wikicloth/lib/wiki_buffer/html_element.rb +237 -0
  19. data/examples/wikicloth/lib/wiki_buffer/link.rb +70 -0
  20. data/examples/wikicloth/lib/wiki_buffer/table.rb +159 -0
  21. data/examples/wikicloth/lib/wiki_buffer/var.rb +77 -0
  22. data/examples/wikicloth/lib/wiki_buffer.rb +279 -0
  23. data/examples/wikicloth/lib/wiki_cloth.rb +61 -0
  24. data/examples/wikicloth/lib/wiki_link_handler.rb +138 -0
  25. data/examples/wikicloth/lib/wikicloth.rb +5 -0
  26. data/examples/wikicloth/run_tests.rb +48 -0
  27. data/examples/wikicloth/sample_documents/air_force_one.wiki +170 -0
  28. data/examples/wikicloth/sample_documents/cheatsheet.wiki +205 -0
  29. data/examples/wikicloth/sample_documents/default.css +34 -0
  30. data/examples/wikicloth/sample_documents/elements.wiki +7 -0
  31. data/examples/wikicloth/sample_documents/george_washington.wiki +526 -0
  32. data/examples/wikicloth/sample_documents/images.wiki +15 -0
  33. data/examples/wikicloth/sample_documents/lists.wiki +421 -0
  34. data/examples/wikicloth/sample_documents/pipe_trick.wiki +68 -0
  35. data/examples/wikicloth/sample_documents/random.wiki +55 -0
  36. data/examples/wikicloth/sample_documents/tv.wiki +312 -0
  37. data/examples/wikicloth/sample_documents/wiki.png +0 -0
  38. data/examples/wikicloth/sample_documents/wiki_tables.wiki +410 -0
  39. data/examples/wikicloth/tasks/wikicloth_tasks.rake +0 -0
  40. data/examples/wikicloth/test/test_helper.rb +3 -0
  41. data/examples/wikicloth/test/wiki_cloth_test.rb +8 -0
  42. data/examples/wikicloth/uninstall.rb +0 -0
  43. data/examples/wikicloth/wikicloth-0.1.3.gem +0 -0
  44. data/examples/wikicloth/wikicloth.gemspec +69 -0
  45. data/lib/sinatra-s3/admin.rb +626 -0
  46. data/lib/sinatra-s3/base.rb +526 -0
  47. data/lib/sinatra-s3/errors.rb +51 -0
  48. data/lib/sinatra-s3/ext.rb +20 -0
  49. data/lib/sinatra-s3/helpers/acp.rb +100 -0
  50. data/lib/sinatra-s3/helpers/admin.rb +41 -0
  51. data/lib/sinatra-s3/helpers/tracker.rb +42 -0
  52. data/lib/sinatra-s3/helpers/versioning.rb +27 -0
  53. data/lib/sinatra-s3/helpers.rb +79 -0
  54. data/lib/sinatra-s3/models/bit.rb +180 -0
  55. data/lib/sinatra-s3/models/bucket.rb +81 -0
  56. data/lib/sinatra-s3/models/file_info.rb +3 -0
  57. data/lib/sinatra-s3/models/git_bucket.rb +3 -0
  58. data/lib/sinatra-s3/models/slot.rb +47 -0
  59. data/lib/sinatra-s3/models/torrent.rb +6 -0
  60. data/lib/sinatra-s3/models/torrent_peer.rb +5 -0
  61. data/lib/sinatra-s3/models/user.rb +35 -0
  62. data/lib/sinatra-s3/s3.rb +57 -0
  63. data/lib/sinatra-s3/tasks.rb +62 -0
  64. data/lib/sinatra-s3/tracker.rb +134 -0
  65. data/lib/sinatra-s3.rb +1 -0
  66. data/public/css/control.css +225 -0
  67. data/public/css/wiki.css +47 -0
  68. data/public/images/external-link.gif +0 -0
  69. data/public/js/prototype.js +2539 -0
  70. data/public/js/upload_status.js +117 -0
  71. data/public/test.html +8 -0
  72. data/s3.yml.example +17 -0
  73. data/test/s3api_test.rb +121 -0
  74. data/test/test_helper.rb +25 -0
  75. metadata +156 -0
@@ -0,0 +1,41 @@
1
+ require 'aws/s3'
2
+
3
+ module S3
4
+ module AdminHelpers
5
+
6
+ def login_required
7
+ @user = User.find(session[:user_id]) unless session[:user_id].nil?
8
+ redirect '/control/login' if @user.nil?
9
+ end
10
+
11
+ def number_to_human_size(size)
12
+ case
13
+ when size < 1.kilobyte: '%d Bytes' % size
14
+ when size < 1.megabyte: '%.1f KB' % (size / 1.0.kilobyte)
15
+ when size < 1.gigabyte: '%.1f MB' % (size / 1.0.megabyte)
16
+ when size < 1.terabyte: '%.1f GB' % (size / 1.0.gigabyte)
17
+ else '%.1f TB' % (size / 1.0.terabyte)
18
+ end.sub('.0', '')
19
+ rescue
20
+ nil
21
+ end
22
+
23
+ def signed_url(path)
24
+ url = "#{path}?"
25
+ url + AWS::S3::Authentication::QueryString.new(Net::HTTP::Get.new(path), @user.key, @user.secret)
26
+ end
27
+
28
+ def errors_for(model)
29
+ ret = ""
30
+ if model.errors.size > 0
31
+ ret += "<ul class=\"errors\">"
32
+ model.errors.each_full do |error|
33
+ ret += "<li>#{error}</li>"
34
+ end
35
+ ret += "</ul>"
36
+ end
37
+ ret
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,42 @@
1
+ require 'aws/s3'
2
+
3
+ module S3
4
+ module TrackerHelper
5
+
6
+ def torrent(bit)
7
+ mi = bit.metainfo
8
+ mi.announce = URI("http://#{env['HTTP_HOST']}/tracker/announce")
9
+ mi.url_list = URI("http://#{env['HTTP_HOST']}/#{bit.parent.name}/#{bit.name}")
10
+ mi.created_by = "Served by Sinatra-S3/0.1a"
11
+ mi.creation_date = Time.now
12
+ t = Torrent.find_by_bit_id bit.id
13
+ info_hash = Digest::SHA1.digest(mi.info.to_bencoding).to_hex_s
14
+ unless t and t.info_hash == info_hash
15
+ t ||= Torrent.new
16
+ t.update_attributes(:info_hash => info_hash, :bit_id => bit.id, :metainfo => mi.to_bencoding)
17
+ end
18
+ status 200
19
+ headers 'Content-Disposition' => "attachment; filename=#{bit.name}.torrent;", 'Content-Type' => 'application/x-bittorrent'
20
+ body mi.to_bencoding
21
+ end
22
+
23
+ def torrent_list(info_hash)
24
+ params = {:order => 'seeders DESC, leechers DESC', :include => :bit}
25
+ params[:conditions] = ['info_hash = ?', info_hash.to_hex_s] if info_hash
26
+ Torrent.find :all, params
27
+ end
28
+
29
+ def tracker_reply(params)
30
+ status 200
31
+ headers 'Content-Type' => 'text/plain'
32
+ body params.merge('interval' => TRACKER_INTERVAL).to_bencoding
33
+ end
34
+
35
+ def tracker_error(msg)
36
+ status 200
37
+ headers 'Content-Type' => 'text/plain'
38
+ body ({'failure reason' => msg}.to_bencoding)
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module S3
2
+ module Helpers
3
+ module Versioning
4
+
5
+ def versioning_response_for(bit)
6
+ xml do |x|
7
+ x.VersioningConfiguration :xmlns => "http://s3.amazonaws.com/doc/2006-03-01/" do
8
+ x.Versioning bit.versioning_enabled? ? 'Enabled' : 'Suspended' if File.exists?(File.join(bit.fullpath, '.git'))
9
+ end
10
+ end
11
+ end
12
+
13
+ def manage_versioning(bucket)
14
+ raise NotImplemented unless defined?(Git)
15
+ only_can_write_acp bucket
16
+
17
+ env['rack.input'].rewind
18
+ data = env['rack.input'].read
19
+ xml_request = REXML::Document.new(data).root
20
+
21
+ bucket.git_init() if !bucket.versioning_enabled? && xml_request.elements['Status'].text == 'Enabled'
22
+ bucket.git_destroy() if bucket.versioning_enabled? && xml_request.elements['Status'].text == 'Suspended'
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,79 @@
1
+ Dir["#{File.dirname(__FILE__)}/helpers/*.rb"].each {|r| require r }
2
+
3
+ module S3
4
+ module Helpers
5
+
6
+ include ACP
7
+ include Versioning
8
+
9
+ # Kick out anonymous users.
10
+ def only_authorized; raise S3::AccessDenied unless @user end
11
+ # Kick out any users which do not have read access to a certain resource.
12
+ def only_can_read bit; raise S3::AccessDenied unless bit.readable_by? @user end
13
+ # Kick out any users which do not have write access to a certain resource.
14
+ def only_can_write bit; raise S3::AccessDenied unless bit.writable_by? @user end
15
+ # Kick out any users which do not own a certain resource.
16
+ def only_owner_of bit; raise S3::AccessDenied unless bit.owned_by? @user end
17
+ # Kick out any non-superusers
18
+ def only_superusers; raise S3::AccessDenied unless @user.superuser? end
19
+
20
+ protected
21
+ def load_buckets
22
+ @buckets = Bucket.find_by_sql [%{
23
+ SELECT b.*, COUNT(c.id) AS total_children
24
+ FROM bits b LEFT JOIN bits c ON c.parent_id = b.id AND c.deleted = 0
25
+ WHERE b.deleted = 0 AND b.parent_id IS NULL AND b.owner_id = ?
26
+ GROUP BY b.id ORDER BY b.name}, @user.id]
27
+ @bucket = Bucket.new(:owner_id => @user.id, :access => CANNED_ACLS['private'])
28
+ end
29
+
30
+ def xml
31
+ xml = Builder::XmlMarkup.new
32
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
33
+ yield xml
34
+ content_type 'text/xml'
35
+ body xml.target!
36
+ end
37
+
38
+ # Convenient method for generating a SHA1 digest.
39
+ def hmac_sha1(key, s)
40
+ ipad = [].fill(0x36, 0, 64)
41
+ opad = [].fill(0x5C, 0, 64)
42
+ key = key.unpack("C*")
43
+ if key.length < 64 then
44
+ key += [].fill(0, 0, 64-key.length)
45
+ end
46
+
47
+ inner = []
48
+ 64.times { |i| inner.push(key[i] ^ ipad[i]) }
49
+ inner += s.unpack("C*")
50
+
51
+ outer = []
52
+ 64.times { |i| outer.push(key[i] ^ opad[i]) }
53
+ outer = outer.pack("c*")
54
+ outer += Digest::SHA1.digest(inner.pack("c*"))
55
+
56
+ return Base64::encode64(Digest::SHA1.digest(outer)).chomp
57
+ end
58
+
59
+ def generate_secret
60
+ abc = %{ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz}
61
+ (1..40).map { abc[rand(abc.size),1] }.join
62
+ end
63
+
64
+ def generate_key
65
+ abc = %{ABCDEF0123456789}
66
+ (1..20).map { abc[rand(abc.size),1] }.join
67
+ end
68
+
69
+ def get_prefix(c)
70
+ c.name.sub(params['prefix'], '').split(params['delimiter'])[0] + params['delimiter']
71
+ end
72
+
73
+ def r(name, title, layout = :layout)
74
+ @title = title
75
+ haml name, :layout => layout
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,180 @@
1
+ class Bit < ActiveRecord::Base
2
+
3
+ has_many :bits_users
4
+
5
+ serialize :meta
6
+ serialize :obj
7
+
8
+ belongs_to :parent, :class_name => 'Bit', :foreign_key => 'parent_id'
9
+ belongs_to :owner, :class_name => 'User', :foreign_key => 'owner_id'
10
+
11
+ validates_length_of :name, :within => 3..255
12
+
13
+ has_one :torrent
14
+
15
+ def git_repository
16
+ versioning_enabled? ? Git.open(git_repository_path) : nil
17
+ end
18
+
19
+ def fullpath; File.join(S3::STORAGE_PATH, name) end
20
+
21
+ def git_repository_path
22
+ self.obj ? File.join(File.dirname(self.fullpath)) : self.fullpath
23
+ end
24
+
25
+ def versioning_enabled?
26
+ return false if !defined?(Git) || !File.exists?(File.join(git_repository_path,'.git'))
27
+ return false if File.exists?(File.join(git_repository_path,'.git', 'disable_versioning'))
28
+ return true
29
+ end
30
+
31
+ def git_object
32
+ git_repository.log.path(File.basename(self.obj.path)).first if versioning_enabled? && self.obj
33
+ end
34
+
35
+ def objectish
36
+ git_repository.gcommit(version).gtree.blobs[File.basename(fullpath)].objectish
37
+ end
38
+
39
+ def diff(to)
40
+ to = Bit.find_by_version(to) if to.instance_of?(String)
41
+ git_repository.diff(objectish, to.objectish)
42
+ end
43
+
44
+ def self.diff(from,to)
45
+ from = Bit.find_by_version(from)
46
+ to = Bit.find_by_version(to)
47
+ from.git_repository.diff(from.objectish, to.objectish)
48
+ end
49
+
50
+ def acl_list
51
+ bit_perms = self.access.nil? ? "000" : self.access.to_s(8)
52
+ acls = { :owner => { :id => self.owner.key, :accessnum => 7, :type => "CanonicalUser", :name => self.owner.login, :access => "FULL_ACCESS" },
53
+ :anonymous => { :id => nil, :accessnum => bit_perms[2,1], :access => acl_label(bit_perms[2,1]),
54
+ :type => "Group", :uri => "http://acs.amazonaws.com/groups/global/AllUsers" },
55
+ :authenticated => { :id => nil, :accessnum => bit_perms[1,1], :access => acl_label(bit_perms[1,1]),
56
+ :type => "Group", :uri => "http://acs.amazonaws.com/groups/global/AuthenticatedUsers" }
57
+ }.merge(get_acls_for_bin)
58
+ acls.delete_if { |key,value| value[:access] == "NONE" || (key == :authenticated && (!acls[:anonymous].nil? && value[:accessnum] <= acls[:anonymous][:accessnum])) }
59
+ end
60
+
61
+ def get_acls_for_bin
62
+ ret = {}
63
+ for a in self.bits_users
64
+ ret[a.user.key] = { :type => "CanonicalUser", :id => a.user.key, :name => a.user.login, :access => acl_label(a.access.to_s(8)[0,1]),
65
+ :accessnum => a.access.to_s(8)[0,1] }
66
+ end
67
+ ret
68
+ end
69
+
70
+ def self.acl_text
71
+ { 0 => "NONE", 1 => "NONE", 2 => "NONE", 3 => "NONE", 4 => "READ", 5 => "READ_ACP", 6 => "WRITE", 7 => "WRITE_ACP" }
72
+ end
73
+
74
+ def acl_label(num)
75
+ Bit.acl_text[num.to_i]
76
+ end
77
+
78
+ def grant hsh
79
+ if hsh[:access]
80
+ self.access = hsh[:access]
81
+ self.save
82
+ end
83
+ end
84
+
85
+ def access_readable
86
+ name, _ = S3::CANNED_ACLS.find { |k, v| v == self.access }
87
+ if name
88
+ name
89
+ else
90
+ [0100, 0010, 0001].map do |i|
91
+ [[4, 'r'], [2, 'w'], [1, 'x']].map do |k, v|
92
+ (self.access & (i * k) == 0 ? '-' : v )
93
+ end
94
+ end.join
95
+ end
96
+ end
97
+
98
+ def acp_readable_by? user
99
+ # if owner
100
+ return true if user && user == owner
101
+ # if can write or better
102
+ return true if user && acl_list[user.key] && acl_list[user.key][:accessnum].to_i >= 5
103
+ # if authenticated
104
+ return true if user && acl_list[:authenticated] && acl_list[:authenticated][:accessnum].to_i >= 5
105
+ # if anonymous
106
+ return true if acl_list[:anonymous] && acl_list[:anonymous][:accessnum].to_i >= 5
107
+ end
108
+
109
+ def acp_writable_by? user
110
+ # if owner
111
+ return true if user && user == owner
112
+ # if can write or better
113
+ return true if user && acl_list[user.key] && acl_list[user.key][:accessnum].to_i == 7
114
+ # if authenticated
115
+ return true if user && acl_list[:authenticated] && acl_list[:authenticated][:accessnum].to_i == 7
116
+ # if anonymous
117
+ return true if acl_list[:anonymous] && acl_list[:anonymous][:accessnum].to_i == 7
118
+ end
119
+
120
+ def readable_by? user
121
+ return true if user && acl_list[user.key] && acl_list[user.key][:accessnum].to_i >= 4
122
+ check_access(user, S3::READABLE_BY_AUTH, S3::READABLE)
123
+ end
124
+
125
+ def writable_by? user
126
+ return true if user && acl_list[user.key] && acl_list[user.key][:accessnum].to_i >= 6
127
+ check_access(user, S3::WRITABLE_BY_AUTH, S3::WRITABLE)
128
+ end
129
+
130
+ def check_access user, group_perm, user_perm
131
+ !!( if owned_by?(user) or (user and access & group_perm > 0) or (access & user_perm > 0)
132
+ true
133
+ elsif user
134
+ acl = users.find(user.id) rescue nil
135
+ acl and acl.access.to_i & user_perm
136
+ end )
137
+ end
138
+
139
+ def owned_by? user
140
+ user and owner_id == user.id
141
+ end
142
+
143
+ def git_update
144
+ # update git info so we can serve it over http
145
+ base_dir = File.join(self.git_repository_path,'.git')
146
+ if File.exists?(base_dir)
147
+ File.open(File.join(base_dir,'HEAD'),'w') { |f| f.write("ref: refs/heads/master") }
148
+ if File.exists?(File.join(base_dir,'refs/heads/master'))
149
+ File.open(File.join(base_dir,'info/refs'),'w') { |f|
150
+ ref = File.open(File.join(base_dir,'refs/heads/master')) { |re| re.read }
151
+ f.write("#{ref.chomp}\trefs/heads/master\n")
152
+ }
153
+ end
154
+ end
155
+ end
156
+
157
+ def each_piece(files, length)
158
+ buf = ""
159
+ files.each do |f|
160
+ File.open(f) do |fh|
161
+ begin
162
+ read = fh.read(length - buf.length)
163
+ if (buf.length + read.length) == length
164
+ yield(buf + read)
165
+ buf = ""
166
+ else
167
+ buf += read
168
+ end
169
+ end until fh.eof?
170
+ end
171
+ end
172
+ yield buf
173
+ end
174
+
175
+ end
176
+
177
+ class BitsUser < ActiveRecord::Base
178
+ belongs_to :bit
179
+ belongs_to :user
180
+ end
@@ -0,0 +1,81 @@
1
+ class Bucket < Bit
2
+
3
+ named_scope :user_buckets, lambda { |uid| { :conditions => ['parent_id IS NULL AND owner_id = ?', uid ], :order => "name" } }
4
+ named_scope :root, lambda { |name| { :conditions => ['deleted = 0 AND parent_id IS NULL AND name = ?', name] } }
5
+
6
+ def items(marker,prefix)
7
+ Slot.bucket(self).items(marker,prefix)
8
+ end
9
+
10
+ def self.find_root(bucket_name)
11
+ root(bucket_name).find(:first) or raise S3::NoSuchBucket
12
+ end
13
+
14
+ def find_slot(oid)
15
+ Slot.find(:first, :conditions => ['deleted = 0 AND parent_id = ? AND name = ?', self.id, oid]) or raise S3::NoSuchKey
16
+ end
17
+
18
+ def remove_from_filesystem
19
+ bucket_dir = File.join(S3::STORAGE_PATH, self.name)
20
+ FileUtils.rm_rf bucket_dir if File.directory?(bucket_dir) && Dir.empty?(bucket_dir)
21
+ end
22
+
23
+ def git_disable
24
+ FileUtils.touch(File.join(self.fullpath, '.git', 'disable_versioning'))
25
+ end
26
+
27
+ def add_child(bit)
28
+ bit.update_attributes(:parent_id => self.id)
29
+ end
30
+
31
+ def git_init
32
+ disable_file = File.join(self.fullpath, '.git', 'disable_versioning')
33
+ FileUtils.rm_f(disable_file) and return if File.exists?(disable_file)
34
+
35
+ begin
36
+ FileUtils.mkdir_p(self.fullpath) unless File.exists?(self.fullpath)
37
+ dir_empty = !Dir.foreach(self.fullpath) {|n| break true unless /\A\.\.?\z/ =~ n}
38
+ g = Git.init(self.fullpath)
39
+ g.config('user.name', self.owner.login)
40
+ g.config('user.email', self.owner.email)
41
+ # if directory is not empty we need to add the files
42
+ # into version control
43
+ unless dir_empty
44
+ g.add('.')
45
+ g.commit_all("Enabling versioning for bucket #{self.name}.")
46
+ self.git_update
47
+ end
48
+ self.type = "GitBucket"
49
+ self.save()
50
+ self.git_update
51
+ rescue Git::GitExecuteError => error_message
52
+ puts "[#{Time.now}] GIT: #{error_message}"
53
+ end
54
+ end
55
+
56
+ def metainfo
57
+ children = self.all_children
58
+ mii = RubyTorrent::MetaInfoInfo.new
59
+ mii.name = self.name
60
+ mii.piece_length = 512.kilobytes
61
+ mii.files, files = [], []
62
+ mii.pieces = ""
63
+ i = 0
64
+ Slot.find(:all, :conditions => ['parent_id = ?', self.id]).each do |slot|
65
+ miif = RubyTorrent::MetaInfoInfoFile.new
66
+ miif.length = slot.obj.size
67
+ miif.md5sum = slot.obj.md5
68
+ miif.path = File.split(slot.name)
69
+ mii.files << miif
70
+ files << slot.fullpath
71
+ end
72
+ each_piece(files, mii.piece_length) do |piece|
73
+ mii.pieces += Digest::SHA1.digest(piece)
74
+ i += 1
75
+ end
76
+ mi = RubyTorrent::MetaInfo.new
77
+ mi.info = mii
78
+ mi
79
+ end
80
+
81
+ end
@@ -0,0 +1,3 @@
1
+ class FileInfo
2
+ attr_accessor :path, :mime_type, :disposition, :size, :md5, :etag
3
+ end
@@ -0,0 +1,3 @@
1
+ class GitBucket < Bucket
2
+
3
+ end
@@ -0,0 +1,47 @@
1
+ class Slot < Bit
2
+
3
+ named_scope :bucket, lambda { |bucket| { :conditions => [ 'bits.deleted = 0 AND parent_id = ?', bucket.id ], :order => "name" } }
4
+ named_scope :items, lambda { |marker,prefix| { :conditions => condition_string(marker,prefix) } }
5
+
6
+ def fullpath; File.join(S3::STORAGE_PATH, obj.path) end
7
+
8
+ def etag
9
+ if self.obj.respond_to? :etag
10
+ self.obj.etag
11
+ elsif self.obj.respond_to? :md5
12
+ self.obj.md5
13
+ else
14
+ %{"#{MD5.md5(self.obj)}"}
15
+ end
16
+ end
17
+
18
+ def remove_from_filesystem
19
+ FileUtils.rm_f fullpath
20
+ end
21
+
22
+ def metainfo
23
+ mii = RubyTorrent::MetaInfoInfo.new
24
+ mii.name = self.name
25
+ mii.length = self.obj.size
26
+ mii.md5sum = self.obj.md5
27
+ mii.piece_length = 512.kilobytes
28
+ mii.pieces = ""
29
+ i = 0
30
+ each_piece([self.fullpath], mii.piece_length) do |piece|
31
+ mii.pieces += Digest::SHA1.digest(piece)
32
+ i += 1
33
+ end
34
+ mi = RubyTorrent::MetaInfo.new
35
+ mi.info = mii
36
+ mi
37
+ end
38
+
39
+ protected
40
+ def self.condition_string(marker,prefix)
41
+ conditions = []
42
+ conditions << "name LIKE '#{prefix.gsub(/\\/, '\&\&').gsub(/'/, "''")}%'" unless prefix.blank?
43
+ conditions << "name > '#{marker.gsub(/\\/, '\&\&').gsub(/'/, "''")}'" unless marker.blank?
44
+ conditions.empty? ? nil : conditions.join(" AND ")
45
+ end
46
+
47
+ end
@@ -0,0 +1,6 @@
1
+ class Torrent < ActiveRecord::Base
2
+
3
+ belongs_to :bit
4
+ has_many :torrent_peers
5
+
6
+ end
@@ -0,0 +1,5 @@
1
+ class TorrentPeer < ActiveRecord::Base
2
+
3
+ belongs_to :torrent
4
+
5
+ end
@@ -0,0 +1,35 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'helpers.rb')
2
+
3
+ class User < ActiveRecord::Base
4
+
5
+ include S3::Helpers
6
+
7
+ has_many :bits, :foreign_key => 'owner_id'
8
+ has_many :bits_users
9
+
10
+ validates_length_of :login, :within => 3..40
11
+ validates_uniqueness_of :login
12
+ validates_uniqueness_of :key
13
+ validates_presence_of :password
14
+ validates_confirmation_of :password
15
+
16
+ def destroy
17
+ self.deleted = 1
18
+ self.save
19
+ end
20
+
21
+ attr_accessor :skip_before_save
22
+
23
+ protected
24
+ def before_save
25
+ unless self.skip_before_save
26
+ @password_clean = self.password
27
+ self.password = hmac_sha1(self.password, self.secret)
28
+ end
29
+ end
30
+
31
+ def after_save
32
+ self.password = @password_clean
33
+ end
34
+
35
+ end
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+
4
+ require 'sinatra/base'
5
+ require 'openssl'
6
+ require 'digest/sha1'
7
+ require 'md5'
8
+ require 'haml'
9
+
10
+ begin
11
+ require "git"
12
+ puts "-- Git support found, versioning support enabled." if $VERBOSE
13
+ rescue LoadError
14
+ puts "-- Git support not found, versioning support disabled."
15
+ end
16
+
17
+ begin
18
+ require 'exifr'
19
+ puts "-- EXIFR found, JPEG metadata enabled." if $VERBOSE
20
+ rescue LoadError
21
+ puts "-- EXIFR not found, JPEG metadata disabled."
22
+ end
23
+
24
+ begin
25
+ require 'rubytorrent'
26
+ puts "-- RubyTorrent support found, bittorrent support enabled." if $VERBOSE
27
+ rescue LoadError
28
+ puts "-- RubyTorrent support not found, bittorrent support disabled."
29
+ end
30
+
31
+ module S3
32
+ VERSION = "0.98"
33
+ DEFAULT_PASSWORD = 'pass@word1'
34
+
35
+ BUFSIZE = (4 * 1024)
36
+ ROOT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
37
+ PUBLIC_PATH = File.join(ROOT_DIR, 'public')
38
+ STORAGE_PATH = File.expand_path('storage') unless defined?(STORAGE_PATH)
39
+ RESOURCE_TYPES = %w[acl versioning torrent]
40
+ CANNED_ACLS = {
41
+ 'private' => 0600,
42
+ 'public-read' => 0644,
43
+ 'public-read-write' => 0666,
44
+ 'authenticated-read' => 0640,
45
+ 'authenticated-read-write' => 0660
46
+ }
47
+ READABLE = 0004
48
+ WRITABLE = 0002
49
+ READABLE_BY_AUTH = 0040
50
+ WRITABLE_BY_AUTH = 0020
51
+
52
+ POST = %{if(!this.title||confirm(this.title+'?')){var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit();}return false;}
53
+ POPUP = %{window.open(this.href,'changelog','height=600,width=500,scrollbars=1');return false;}
54
+ end
55
+
56
+ %w(bit bucket git_bucket slot user file_info torrent torrent_peer).each {|r| require "#{File.dirname(__FILE__)}/models/#{r}" }
57
+ %w(ext helpers errors admin base tracker).each {|r| require "#{File.dirname(__FILE__)}/#{r}"}
@@ -0,0 +1,62 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require File.join(File.dirname(__FILE__), 's3')
5
+
6
+ namespace :db do
7
+ task :environment do
8
+ ActiveRecord::Base.establish_connection(S3.config[:db])
9
+ end
10
+
11
+ desc "Migrate the database"
12
+ task(:migrate => :environment) do
13
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
14
+ ActiveRecord::Migration.verbose = true
15
+
16
+ out_dir = File.dirname(S3.config[:db][:database])
17
+ FileUtils.mkdir_p(out_dir) unless File.exists?(out_dir)
18
+
19
+ ActiveRecord::Migrator.migrate(File.join(S3::ROOT_DIR, 'db', 'migrate'))
20
+ num_users = User.count || 0
21
+ if num_users == 0
22
+ puts "** No users found, creating the `admin' user."
23
+ class S3KeyGen
24
+ include S3::Helpers
25
+ def secret() generate_secret(); end;
26
+ def key() generate_key(); end;
27
+ end
28
+ User.create :login => "admin", :password => S3::DEFAULT_PASSWORD,
29
+ :email => "admin@parkplace.net", :key => S3KeyGen.new.key(), :secret => S3KeyGen.new.secret(),
30
+ :activated_at => Time.now, :superuser => 1
31
+ end
32
+ end
33
+ end
34
+
35
+ namespace :setup do
36
+ task :wiki do
37
+ begin
38
+ Bucket.find_root('wiki')
39
+ rescue S3::NoSuchBucket
40
+ wiki_owner = User.find_by_login('wiki')
41
+ if wiki_owner.nil?
42
+ class S3KeyGen
43
+ include S3::Helpers
44
+ def secret() generate_secret(); end;
45
+ def key() generate_key(); end;
46
+ end
47
+ puts "** No wiki user found, creating the `wiki' user."
48
+ wiki_owner = User.create :login => "wiki", :password => S3::DEFAULT_PASSWORD,
49
+ :email => "wiki@parkplace.net", :key => S3KeyGen.new.key(), :secret => S3KeyGen.new.secret(),
50
+ :activated_at => Time.now
51
+ end
52
+ wiki_bucket = Bucket.create(:name => 'wiki', :owner_id => wiki_owner.id, :access => 438)
53
+ templates_bucket = Bucket.create(:name => 'templates', :owner_id => wiki_owner.id, :access => 438)
54
+ if defined?(Git)
55
+ wiki_bucket.git_init
56
+ templates_bucket.git_init
57
+ else
58
+ puts "Git support not found therefore Wiki history is disabled."
59
+ end
60
+ end
61
+ end
62
+ end