<?php
/**
 * Session
 *
 * $Id$
 *
 * Copyright (c) 2007, DM Solutions Group Inc.
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*****************************************************************************
 * Purpose: Directory-based PHP session capability
 *****************************************************************************
 *
 * This code was taken from the php_utils cvs module maintained on 
 * cvs.maptools.org/cvs/maptools/cvsroot. Version 1.1 of this file was an 
 * exact copy of 1.29 except for the comment block at the top.
 *
 * Developer's notes:
 *
 * Include this file and call installSessionDirectoryHandler() before starting
 * a session.
 *
 * Call initializeSession() to start or restart a session.
 *
 * Session-based resources can be saved in the directory returned from
 * getSessionSavePath().
 *
 * Setting $GLOBALS['bDebug'] = true will enable outputting some debug
 * messages into the session.log file in the debug directory defined
 * by $GLOBALS['szDebugDir']
 *
 */

global $gszSessSavePath;
global $gszSessName;

$GLOBALS['bDebug'] = false;
$GLOBALS['szDebugPage'] = isset( $_SERVER['ORIG_PATH_TRANSLATED'] ) ? basename( $_SERVER['ORIG_PATH_TRANSLATED'] ) : 'not set';
$GLOBALS['szDebugDir'] = "/tmp/";

/* file-based session locking based on code contributed by
 * Andreas Hocevar ahocevar@hotmail.com
 */
if (!isset($GLOBALS['bLockSession'])) {
    $GLOBALS['bLockSession'] = false;
}

/**
 * _open Called by PHP session manager when a session is opened.
 * We just set the save path and session name into global variable.
 *
 * @param szSavePath String containing the absolute path where to
 *        save session info.
 * @param szSessionName String contqaining the session name.
 */
function _open($szSavePath, $szSessionName)
{
    if ($GLOBALS["bDebug"])
    {
      debug_msg("Opened() $szSavePath, $szSessionName");
      debug_msg("session_id() says ".session_id());
    }

    $GLOBALS['gszSessSavePath'] = $szSavePath;
    $GLOBALS['gszSessName'] = $szSessionName;
    $GLOBALS['gszSessId'] = session_id();

    $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS['gszSessId'];
    $szSessionFile = $szSessionDir."/session_file";
    $szLockFile = $szSessionDir."/lock";

    clearstatcache();
    if (!file_exists($szSessionDir))
    {
        mkdir($szSessionDir);
    }

    if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
    {
        $i=0;
        while ($i < ini_get("max_execution_time") - 2 ) //wait at most 2 seconds less than the execution time for the lock
        {
            clearstatcache();
            if (!file_exists($szLockFile))
            {
                break;
            }
            if ($GLOBALS["bDebug"])
            {
                debug_msg("pausing in open()");
            }
            $i++;
            sleep(1);
        }
        $fh = fopen( $szLockFile, "w+" );
        if ($fh !== false)
        {
            fwrite( $fh, "1" );
            fclose($fh);
            if ($GLOBALS["bDebug"])
            {
                debug_msg("created lock file in open()");
            }
        }
    }

    return(true);
}

/**
 * _close Called by PHP session manager when a session is closed,
 * not destroyed. In this case we do nothing.
 */
function _close()
{
    if ($GLOBALS["bDebug"])
    {
      debug_msg("Closed()");
    }
    if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
    {
        $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";
        if ($GLOBALS["bDebug"])
        {
            debug_msg("checking lock file $szLockFile");
        }
        clearstatcache();
        if ( @is_file( $szLockFile )  )
        {
            @unlink( $szLockFile );
            if ($GLOBALS["bDebug"])
            {
                debug_msg("removed lock file $szLockFile");
            }
        }
        else
        {
            if ($GLOBALS["bDebug"])
            {
                debug_msg("lock file $szLockFile is missing.");
            }

        }
    }
    return(true);
}

/**
 * _read Called by PHP session manager when the session file
 * is read. In this case we just return the file content of
 * session_file file.
 */
function _read($szId)
{
    $GLOBALS["gszSessId"] = $szId;

    if ($GLOBALS["bDebug"])
    {
      debug_msg("Read() $szId");
    }

    $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$szId;
    $szSessionFile = $szSessionDir."/session_file";

    clearstatcache();
    if (!file_exists($szSessionDir))
    {
        mkdir($szSessionDir);
    }

    if ($fp = @fopen($szSessionFile, "r"))
    {
        $szSessionData = fread($fp, filesize($szSessionFile));
        fclose( $fp );
        return($szSessionData);
    }
    else
    {
        return(""); // Must return "" here.
    }
}

/**
 * _write Called by PHP session manager when session should be
 * saved.
 *
 * @param szId String containing the unique identifier of current
 *             session.
 * @param szSessionData String containig the session file content
 *                      to be saved.
 */
function _write($szId, $szSessionData)
{
    $result = false;
    if ($GLOBALS["bDebug"])
    {
      debug_msg("Write() $szId $szSessionData");
    }

    $szSessionFile = $GLOBALS['gszSessSavePath']."/sess_".$szId.
                     "/session_file";

    // touch command don't works under windows for directory.
    // since it is only needed for unix (for now) I'll test
    // the platform and bypass this call is windows platform.
    if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
        @touch($GLOBALS['gszSessSavePath']."/sess_".$szId);

    if ($fp = @fopen($szSessionFile, "w"))
    {
        $result = fwrite($fp, $szSessionData);
        fclose($fp);

    }
    return($result);
}

/**
 * _destroy Called by PHP session manager when it should be explicitly
 * destroyed now.
 */
function _destroy($szId)
{
    if ($GLOBALS["bDebug"])
    {
      debug_msg("Destroy $szId");
    }
    if ( (PHP_OS == "WINNT" || PHP_OS == "WIN32"|| $GLOBALS['bLockSession']) )
    {
        $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";

        if ( @is_file( $szLockFile )  )
        {
            @unlink( $szLockFile );
        }
    }
  /*
    if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
    {
        if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
            eval($GLOBALS['gszGarbageColectionCallBackFunction']);
    }

    $bReturn = true;

    if (!$szDir = @opendir($GLOBALS['gszSessSavePath']))
    {
        return false;
    }

    while($szFile = readdir($szDir))
    {
        if (!strstr($szFile,'sess_'.$szId))
            continue;

        $bReturn = (deleteDirectory($GLOBALS['gszSessSavePath']."/".
                                    $szFile)) ? $bReturn : false;
    }
    closedir($szDir);

    return $bReturn;
  */
  return true;
}

/**
 * _gc Called by PHP session manager when a session is started or
 * register (not all the time) depending og session.gc_probability
 */
function _gc($nMaxLifeTime)
{
   if ($GLOBALS["bDebug"])
    {
      debug_msg("_gc called");
    }
    if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
    {
        if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
            eval($GLOBALS['gszGarbageColectionCallBackFunction']);
    }

    if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
    {
      @touch($GLOBALS['gszSessSavePath']."/deleteme", time()-$nMaxLifeTime*60);
      system("find ".$GLOBALS['gszSessSavePath']." -name sess_* ! -newer ".
             $GLOBALS['gszSessSavePath']."/deleteme -exec rm -rf {} \; ");

      return true;
    }

    $bReturn = true;

    if (!$hDir = @opendir($GLOBALS['gszSessSavePath']))
    {
        return false;
    }

    while($szFile = readdir($hDir))
    {
        if (!strstr($szFile,'sess_'))
            continue;

        if (strpos($szFile,'sess_') != 0)
            continue;

        $szSessionDir = $GLOBALS['gszSessSavePath']."/".$szFile;
        $szSessionFile = $szSessionDir."/session_file";

        if (!($mtime = @filemtime($szSessionFile)))
        {
            $bReturn=false;
            continue;
        }

        if (time() > $mtime + $nMaxLifeTime)
        {
            $bReturn = (deleteDirectory($szSessionDir)) ? $bReturn : false;
        }
        closedir($hDir);

        return $bReturn;
    }
}

////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////

function deleteDirectory($szFile)
{
    if (PHP_OS != "WINNT" && PHP_OS != "WIN32")
        chmod($szFile,0777);

    if (is_dir($szFile))
    {
        $handle = opendir($szFile);
        while($szFileName = readdir($handle))
        {
            if ($szFileName != "." && $szFileName != "..")
            {
                deleteDirectory($szFile."/".$szFileName);
            }
        }
        closedir($handle);
        rmdir($szFile);
    }
    else
    {
        unlink($szFile);
    }
}

function installSessionDirectoryHandler($szGCCallBack="")
{
    $GLOBALS['gszGarbageColectionCallBackFunction'] = $szGCCallBack;

    // Set handler functions
    session_set_save_handler("_open",
                             "_close",
                             "_read",
                             "_write",
                             "_destroy",
                             "_gc");
}

function initializeSession( $szSessName="sid", $szSessSavePath="", $szSessionID="" )
{
    
    if ($GLOBALS["bDebug"])
    {
      debug_msg("initializeSession( $szSessName, $szSessSavePath, $szSessionID )");
    }

    
    //if session was run already don't execute again
    if (isset($GLOBALS['session_started']))
    {
        return true;   
    }
    if ($szSessName == "")
    {
        echo "<FONT color=#FF0000>FATAL ERROR: Sessionname not specified</FONT>";
        exit;
    }
    else
        ini_set("session.name", $szSessName);

    if ($szSessSavePath != "")
    {
        ini_set("session.save_path", $szSessSavePath);
    }

    clearstatcache();
    // Check if save path is writable
    if (!(file_exists(ini_get("session.save_path")) &&
          is_writable(ini_get("session.save_path"))))
    {
        echo "<FONT COLOR=#DD0000>FATAL ERROR: Session save path (".ini_get("session.save_path").") doesn't exist or is not writable</FONT>";
        exit;
    }

    //turn off cookies for propagating session ids
    ini_set( "session.use_cookies", "0" );

    // turn off tranparent SID (becuase of buffer problem)
    ini_set( "session.use_trans_sid", "0" );

    // intialize tmp id
    $szTmpID = "";

    // check both get and post variables
    if ( isset($GLOBALS['_GET'][ini_get('session.name')]) )
        $szTmpID = $GLOBALS['_GET'][ini_get('session.name')];
    elseif (isset($GLOBALS['_POST'][ini_get('session.name')]))
        $szTmpID = $GLOBALS['_POST'][ini_get('session.name')];

    // create new if necessary
    if ( strlen( $szTmpID ) <= 0 )
    {
        if ($GLOBALS["bDebug"])
        {
            debug_msg("creating a new session because .$szTmpID. has zero characters ");
        }
        // create new and set IP flag
        if ( strlen( $szSessionID ) > 0 )
        {
            $szTmpID = $szSessionID;
        }
        else            
        {
            $szTmpID = uniqid("");
        }
        $bNewSession = true;
        if ($GLOBALS["bDebug"])
        {
            debug_msg("creating a new session with id ");
        }
   }
    else
        $bNewSession = false;

    // initialize flag variable
    $bSessionOK = true;

    // set the session ID
    session_id( $szTmpID );

    // Check if session is expired
    if (!$bNewSession)
    {
        $szSavePath = getSessionSavePath();
        $szSessionFile = $szSavePath."/session_file";

        if (file_exists($szSessionFile))
            if ($atime=@filemtime($szSessionFile))
                if (time() > $atime + ini_get("session.gc_maxlifetime"))
                {
                    $szTmpID = uniqid("");

                    // reset the session ID
                    session_id( $szTmpID );

                    $bNewSession = true;
                    $bSessionOK = false;
                }
    }

    //start the session
    session_start();
    register_shutdown_function( "session_write_close" );

  
    // set IP if a new session
    if ( $bNewSession ) $_SESSION["gszRemoteAdd"] = $_SERVER["REMOTE_ADDR"];

/* ============================================================================
 * Check IP to see if it is the same
 * ========================================================================= */

    // check if the IP has been set and validate
    if ( isset( $_SESSION["gszRemoteAdd"] ) &&
                                   strlen(trim($_SESSION["gszRemoteAdd"])) > 0 )
    {
        // check if IP matches current client
        if ( trim( $_SESSION["gszRemoteAdd"] ) !=
                                               trim( $_SERVER["REMOTE_ADDR"] ) )
        {
            // possible security breach void session
            /* if the session address is the loopback interface then it is
             * likely that the application was configured to use an external
             * address but someone is trying to test locally using localhost
             */
            if ($_SESSION['gszRemoteAdd'] != '127.0.0.1')
            {
                $bSessionOK = false;
            }
        }
    }
    else
    {
        // possible security breach void session
        $bSessionOK = false;
    }
    
    // return success or failure and set global so we
    // know session has been inited.
    if ($bSessionOK)
    {
        $GLOBALS['session_started'] = true;
    }
    
        
    return $bSessionOK;

// end intializeSession() function
}

function getSessionSavePath()
{
    $szReturn  = ini_get("session.save_path")."/sess_".session_id()."/";
    $szReturn = str_replace( "\\", "/", $szReturn );
    return $szReturn;
}

function debug_msg( $szMsg )
{
    list($usec, $sec) = explode(" ",microtime()); 
    $ts = sprintf( "%s.%4d", date( "H:s", $sec), round( 10000 * $usec )); 
    $fh = fopen($GLOBALS['szDebugDir']."session.log", "a+");
    fwrite($fh, "$ts : ".$GLOBALS['szDebugPage']." : $szMsg\n");
    fclose($fh);
}
?>