sinatra-s3 0.98

Sign up to get free protection for your applications and to get access to all the features.
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