lib/cgi/session.rb


DEFINITIONS

This source file includes following functions.


   1  # Copyright (C) 2001  Yukihiro "Matz" Matsumoto
   2  # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
   3  # Copyright (C) 2000  Information-technology Promotion Agency, Japan
   4  
   5  require 'cgi'
   6  require 'final'
   7  
   8  class CGI
   9    class Session
  10  
  11      attr_reader :session_id
  12  
  13      def Session::callback(dbman)
  14        lambda{
  15          dbman[0].close unless dbman.empty?
  16        }
  17      end
  18  
  19      def Session::create_new_id
  20        require 'digest/md5'
  21        md5 = Digest::MD5::new
  22        md5.update(String(Time::now))
  23        md5.update(String(rand(0)))
  24        md5.update(String($$))
  25        md5.update('foobar')
  26        md5.hexdigest[0,16]
  27      end
  28  
  29      def initialize(request, option={})
  30        session_key = option['session_key'] || '_session_id'
  31        id, = option['session_id']
  32        unless id
  33          if option['new_session']
  34            id = Session::create_new_id
  35          end
  36        end
  37        unless id
  38          id, = request[session_key]
  39          id = id.read if id.respond_to?(:read)
  40          unless id
  41            id, = request.cookies[session_key]
  42          end
  43          unless id
  44            if option.key?('new_session') and not option['new_session']
  45              raise ArgumentError, "session_key `%s' should be supplied"%session_key
  46            end
  47            id = Session::create_new_id
  48          end
  49        end
  50        @session_id = id
  51        dbman = option['database_manager'] || FileStore
  52        @dbman = dbman::new(self, option)
  53        request.instance_eval do
  54          @output_hidden = {session_key => id}
  55          @output_cookies =  [
  56            Cookie::new("name" => session_key,
  57                        "value" => id,
  58                        "expires" => option['session_expires'],
  59                        "domain" => option['session_domain'],
  60                        "secure" => option['session_secure'],
  61                        "path" => if option['session_path'] then
  62                                    option['session_path']
  63                                  elsif ENV["SCRIPT_NAME"] then
  64                                    File::dirname(ENV["SCRIPT_NAME"])
  65                                  else
  66                                    ""
  67                                  end)
  68          ]
  69        end
  70        @dbprot = [@dbman]
  71        ObjectSpace::define_finalizer(self, Session::callback(@dbprot))
  72      end
  73  
  74      def [](key)
  75        unless @data
  76          @data = @dbman.restore
  77        end
  78        @data[key]
  79      end
  80  
  81      def []=(key, val)
  82        unless @write_lock
  83          @write_lock = true
  84        end
  85        unless @data
  86          @data = @dbman.restore
  87        end
  88        @data[key] = val
  89      end
  90  
  91      def update
  92        @dbman.update
  93      end
  94  
  95      def close
  96        @dbman.close
  97        @dbprot.clear
  98      end
  99  
 100      def delete
 101        @dbman.delete
 102        @dbprot.clear
 103      end
 104  
 105      class FileStore
 106        def check_id(id)
 107          /[^0-9a-zA-Z]/ =~ id.to_s ? false : true
 108        end
 109  
 110        def initialize(session, option={})
 111          dir = option['tmpdir'] || ENV['TMP'] || '/tmp'
 112          prefix = option['prefix'] || ''
 113          id = session.session_id
 114          unless check_id(id)
 115            raise ArgumentError, "session_id `%s' is invalid" % id
 116          end
 117          path = dir+"/"+prefix+id
 118          path.untaint
 119          unless File::exist? path
 120            @hash = {}
 121          end
 122          begin
 123            @f = open(path, "r+")
 124          rescue Errno::ENOENT
 125            @f = open(path, "w+")
 126          end
 127        end
 128  
 129        def restore
 130          unless @hash
 131            @hash = {}
 132            @f.flock File::LOCK_EX
 133            @f.rewind
 134            for line in @f
 135              line.chomp!
 136              k, v = line.split('=',2)
 137              @hash[CGI::unescape(k)] = CGI::unescape(v)
 138            end
 139          end
 140          @hash
 141        end
 142  
 143        def update
 144          return unless @hash
 145          @f.rewind
 146          for k,v in @hash
 147            @f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(v))
 148          end
 149          @f.truncate @f.tell
 150        end
 151  
 152        def close
 153          return if @f.closed?
 154          update
 155          @f.close
 156        end
 157  
 158        def delete
 159          path = @f.path
 160          @f.close
 161          File::unlink path
 162        end
 163      end
 164  
 165      class MemoryStore
 166        GLOBAL_HASH_TABLE = {}
 167  
 168        def initialize(session, option=nil)
 169          @session_id = session.session_id
 170          GLOBAL_HASH_TABLE[@session_id] ||= {}
 171        end
 172  
 173        def restore
 174          GLOBAL_HASH_TABLE[@session_id]
 175        end
 176  
 177        def update
 178          # don't need to update; hash is shared
 179        end
 180  
 181        def close
 182          # don't need to close
 183        end
 184  
 185        def delete
 186          GLOBAL_HASH_TABLE.delete(@session_id)
 187        end
 188      end
 189    end
 190  end