# Copyright (C) 2003, 2004 Gregoire Lejeune <gregoire.lejeune@free.fr>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

module Asp
  class Session
    require 'asp/common'
    require 'pstore'
    require 'ftools'
    require 'digest/md5'
    
    public
    def [](key)
      rcod = nil
    
      if @sessionDB.nil? == true
        @sessionID = newSessionID
        openSessionDB

        if @oAspHandler.public_methods.member?( "session_OnStart" ) == true
          @oAspHandler.session_OnStart
        end
      end
    
      @sessionDB.transaction {
        begin
          rcod = @sessionDB[key]
        rescue PStore::Error
          rcod = nil
        end
      }

      return rcod
    end
    
    def []=(key, val)
      rcod = nil
    
      if @sessionDB.nil? == true
        @sessionID = newSessionID
        openSessionDB

        if @oAspHandler.public_methods.member?( "session_OnStart" ) == true
          @oAspHandler.session_OnStart
        end
      end
                        
      @sessionDB.transaction {
        @sessionDB[key] = val
        @sessionDB.commit
        begin
          rcod = @sessionDB[key]
        rescue PStore::Error
          rcod = nil
        end
      }
    
      return rcod
    end

    def timeout( min=nil )
      if min.nil? == false
        @bSendCookie = true
        @timeout = Time::now+(60*min)
      end

      return @timeout
    end

    def sessionID
      return @sessionID
    end

    def abandon
      if @sessionID.nil? == false and @sessionDB.nil? == false
        @sessionID = nil
        @sessionDB = nil
        File::rm_f( @sessionDBFile )
        @bSendCookie = true
        
        if @oAspHandler.public_methods.member?( "session_OnEnd" ) == true
          @oAspHandler.session_OnEnd
        end
      end
    end
    
    ## -- Reserved for Ruby/ASP internal use --
    def addSessionCookieHeader
      
      #if @sessionID.nil? == false and @bSendCookie == true
      if @bSendCookie == true
        if @sessionID.nil? == false
          expires = "; expires="+String( @timeout )
          cookie = Asp::Common::sessionID+"=id="+@sessionID+"&exp="+Asp::Common::url_encode( String( @timeout ) )+expires+"; path=/"
        else
          @timeout = Time::at( 0 )
          expires = "; expires="+String( @timeout )
          cookie = Asp::Common::sessionID+"=id=00&exp="+Asp::Common::url_encode( String( @timeout ) )+expires+"; path=/"
        end
        return cookie
      end

      return nil
    end

    private 
    
    def initialize( r, oAspError, aspHandler, id=nil )
      @oApacheRequest = r
      @oAspError = oAspError
      @oAspHandler = aspHandler
      @sessionID = nil
      @sessionDBFile = nil
      @sessionDB = nil
      @timeout = Time::now+(60*Asp::Common::sessionTimeout)
      @bSendCookie = true

      if id.nil? == false
        @sessionID = id
      else
        getSessionIDFromCookie
      end

      openSessionDB
    end

    def openSessionDB
      if @sessionDB.nil? == true and @sessionID.nil? == false
        @sessionDBFile = Asp::Common::stateDir+"/"+@sessionID+".00"
#        secure = @sessionDBFile.taint
#        $SAFE=1
#        secure.untaint
        @sessionDB = PStore::new( @sessionDBFile.untaint ) 
      end
    end

    def getSessionIDFromCookie
      cookiesStr = @oApacheRequest.headers_in['Cookie']
     
      if cookiesStr.nil? == false
        parts = cookiesStr.split( /;\s*/ )
        parts.each { |v|
          name, value = v.split( /\=/, 2 )
          name = Asp::Common::url_decode( name )
          next if name != Asp::Common::sessionID

          cookieData = Asp::Common::parseCookie( value )
          @sessionID = cookieData['id']
          @timeout = cookieData['exp']
          @bSendCookie = false
          break
        }
      end
    end

    def newSessionID
      @bSendCookie = true

      md5 = Digest::MD5::new
      md5.update(String(Time::now))
      md5.update(String(rand(0)))
      md5.update(String($$))
      md5.update('foobar')
      md5.hexdigest[0,16]
    end

  end
end
