1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
require 'cgi'
require 'final'
class CGI
class Session
attr_reader :session_id
def Session::callback(dbman)
lambda{
dbman.close
}
end
def create_new_id
require 'md5'
md5 = MD5::new
md5.update(String(Time::now))
md5.update(String(rand(0)))
md5.update(String($$))
md5.update('foobar')
@session_id = md5.hexdigest[0,16]
end
private :create_new_id
def initialize(request, option={})
session_key = option['session_key'] || '_session_id'
id, = option['session_id']
unless id
if option['new_session']
id = create_new_id
end
end
unless id
id, = request[session_key]
unless id
id, = request.cookies[session_key]
end
unless id
if option.key?('new_session') and not option['new_session']
raise ArgumentError, "session_key `%s' should be supplied"%session_key
end
id = create_new_id
end
end
@session_id = id
dbman = option['database_manager'] || FileStore
@dbman = dbman::new(self, option)
request.instance_eval do
@output_hidden = {session_key => id}
@output_cookies = [Cookie::new(session_key,id)]
end
ObjectSpace::define_finalizer(self, Session::callback(@dbman))
end
def [](key)
unless @data
@data = @dbman.restore
end
@data[key]
end
def []=(key, val)
unless @write_lock
@write_lock = true
end
unless @data
@data = @dbman.restore
end
@data[key] = String(val)
end
def update
@dbman.update
end
def delete
@dbman.delete
end
class FileStore
def initialize(session, option={})
dir = option['tmpdir'] || ENV['TMP'] || '/tmp'
prefix = option['prefix'] || ''
path = dir+"/"+prefix+session.session_id
path.untaint
unless File::exist? path
@hash = {}
end
begin
@f = open(path, "r+")
rescue Errno::ENOENT
@f = open(path, "w+")
end
end
def restore
unless @hash
@hash = {}
@f.flock File::LOCK_EX
@f.rewind
for line in @f
line.chomp!
k, v = line.split('=',2)
@hash[CGI::unescape(k)] = CGI::unescape(v)
end
end
@hash
end
def update
@f.rewind
for k,v in @hash
@f.printf "%s=%s\n", CGI::escape(k), CGI::escape(v)
end
@f.truncate @f.tell
end
def close
update
@f.close
end
def delete
path = @f.path
@f.close
File::unlink path
end
end
class MemoryStore
GLOBAL_HASH_TABLE = {}
def initialize(session, option={})
@session_id = session.session_id
GLOBAL_HASH_TABLE[@session_id] = {}
end
def restore
GLOBAL_HASH_TABLE[@session_id]
end
def update
# don't need to update; hash is shared
end
def close
# don't need to close
end
def delete
GLOBAL_HASH_TABLE[@session_id] = nil
end
end
end
end
|