X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=session.php;h=87cbfd5e62f4e986d9accf118cdaf7cc5769e3f2;hb=8887166f7f16b778d9c1570e858f42afd282c427;hp=1ac709c58233e5a8d171de0bd064bb4bd6aa3840;hpb=85bb9b0d8edc94ba469ee4c225407b5d1a1ef993;p=wfpl.git diff --git a/session.php b/session.php index 1ac709c..87cbfd5 100644 --- a/session.php +++ b/session.php @@ -2,88 +2,123 @@ # Copyright (C) 2006 Jason Woofenden # -# This file is part of wfpl. +# 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 3 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, see . + + +# The functions in this file assume that you have this database table: +# drop table if exists wfpl_sessions; +# create table wfpl_sessions ( +# id int unique auto_increment, +# session_key varchar(16), +# idle_timeout int, +# expires int, +# expires_max int, +# value text +# ) CHARSET=utf8; + +# You'll want to use these: # -# wfpl is free software; you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation; either version 2.1 of the License, or (at your option) -# any later version. +# session_exists() +# session_new('timeout', 'max_len') +# session_set('key', 'value') +# session_sets(['key': 'value', 'key2': 'val2']) +# session_get('key') +# session_clear() # removes all set() values +# session_clear('key') +# session_kill() # -# wfpl 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 Lesser General Public License for -# more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with wfpl; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -# you'll need this file that calls db_connect() -if(!isset($GLOBALS['wfpl_db_handle'])) { - if(file_exists('db_connect.php')) { - require_once('db_connect.php'); - } elseif(file_exists('code/db_connect.php')) { - require_once('code/db_connect.php'); - } else { - die("session.php requires a file db_connect.php or that you call db_connect() first. See code/wfpl/db.php for more information."); - } -} +# All session data is cached in globals, so: +# 1. don't set large amonuts of data +# 2. session_get() is very fast (no db access) -# and these database tables: -# create table wfpl_sessions (id int unique auto_increment, session_key varchar(16), length int, expires int); -# create table wfpl_session_data (id int unique auto_increment, session_id int, name varchar(100), value text); -# run this command to install/clear the tables: -# mysql DATABASE_NAME < code/wfpl/examples/session.sql -# note: you may need these parameters for mysql: -u USERNAME -p - -# GLOSSARY -# -# session_key 16 digit string identifying the session -# session_id integer id of the record in the "sessions" table of the database -# UNTIL_CLOSE a constant passed as session length to indicate "until browser window closes" - - -# session_id is kept in $GLOBALS -# session_key is sent as a cookie, and thus appears in $_REQUEST. The clean version is in $GLOBALS # generate a new random 16-character string function session_generate_key() { - $character_set = "abcdefghijklmnopqrstuvwqyzABCDEFGHIJKLMNOPQRSTUVWQYZ0123456789"; - $id = " "; + $character_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + $id = " "; # PHP 4.2.0 and up seed the random number generator for you. # Lets hope that it seeds with something harder to guess than the clock. - for($i = 0; $i < 16; ++$i) { - $id{$i} = $character_set{mt_rand(0, 61)}; - } + for($i = 0; $i < 16; ++$i) { + $id{$i} = $character_set{mt_rand(0, 61)}; + } - return $id; + return $id; } # track this user with a session cookie (ie a cookie that goes away when the # user closes the browser). The timestamp is how long to track the session in # the database. Defaults to one day. -function session_new($length = 86400) { +function session_new($idle_timeout = 86400, $max_timeout = 'same_as_idle') { + if ($max_timeout === 'same_as_idle') { + $max_timeout = $idle_timeout; + } + kill_session(); + $session_key = session_generate_key(); - db_insert('wfpl_sessions', 'session_key,length', $session_key, $length); - $GLOBALS['session_id'] = db_auto_id(); - $GLOBALS['session_key'] = $session_key; - $_REQUEST['session_key'] = $session_key; #just in case someone calls session_exists() after session_new() - session_touch($length); + $now = time(); + $row = array( + 'session_key' => $session_key, + 'idle_timeout' => $idle_timeout, + 'expires' => $now + $idle_timeout, + 'expires_max' => $now + $max_timeout, + 'value' => '{}' + ); + + db_insert_assoc('wfpl_sessions', $row); + $session_id = db_auto_id(); + $GLOBALS['wfpl_session'] = array( + 'exists' => true, + 'id' => $session_id, + 'key' => $session_key, + 'idle_timeout' => $row['idle_timeout'], + 'expires' => $row['expires'], + 'expires_max' => $row['expires_max'], + 'value' => array() + ); + session_set_cookie(); + return $session_key; } -# call to renew the timeout for the session. -# assumes there's a session. call init_session() if you'd like one auto-create one if not found. -function session_touch($length = false) { - if(!$length) { - $length = db_get_value('wfpl_sessions', 'length', 'where id=%i', $GLOBALS['session_id']); +function session_set_cookie() { + if (session_exists()) { + if (!isset($GLOBALS['wfpl_session']['cookie_set'])) { + $GLOBALS['wfpl_session']['cookie_set'] = true; + header('Set-Cookie: session_key=' . $GLOBALS['wfpl_session']['key'] . '; Path=/'); + } } - $expires = time() + $length; - - header('Set-Cookie: session_key=' . $GLOBALS['session_key']); +} - db_update('wfpl_sessions', 'expires', $expires, 'where id=%i', $GLOBALS['session_id']); +# update the idle_timeout +function session_touch() { + if(!session_exists()) { + return; + } + # is the session extendable? + if ($GLOBALS['wfpl_session']['expires'] < $GLOBALS['wfpl_session']['expires_max']) { + # would this extend the session by at least 10%? + $now = time(); + $session_start = $GLOBALS['wfpl_session']['expires'] - $GLOBALS['wfpl_session']['idle_timeout']; + if ($now > $session_start + ceil(0.1 * $GLOBALS['wfpl_session']['idle_timeout'])) { + $expires = max( + $GLOBALS['wfpl_session']['expires_max'], + $now + $GLOBALS['wfpl_session']['idle_timeout'] + ); + db_update('wfpl_sessions', 'expires', $expires, 'where id=%i', $GLOBALS['wfpl_session']['id']); + } + } } # delete the current session @@ -91,54 +126,77 @@ function kill_session() { if(!session_exists()) { return; } - _kill_session($GLOBALS['session_id']); -} - -# for internal use. use kill_session() above -function _kill_session($id) { - db_delete('wfpl_session_data', 'where session_id=%i', $id); - db_delete('wfpl_sessions', 'where id=%i', $id); + db_delete('wfpl_sessions', 'where id=%i', $GLOBALS['wfpl_session']['id']); + $GLOBALS['wfpl_session'] = array('exists' => false); } # delete expired sessions from database function session_purge_old() { - $now = time(); - $expired_sessions = db_get_column('wfpl_sessions', 'id', 'where expires < %i', $now); - if($expired_sessions) foreach($expired_sessions as $expired_session) { - _kill_session($expired_session); - } + db_delete('wfpl_sessions', 'where expires < %i', time()); } # return true if a session exists function session_exists() { - if(!isset($_REQUEST['session_key'])) { - return false; + if (isset($GLOBALS['wfpl_session'])) { + return $GLOBALS['wfpl_session']['exists']; } - if(isset($GLOBALS['session_id'])) { - return true; + $GLOBALS['wfpl_session'] = array('exists' => false); + + if(!isset($_COOKIE['session_key'])) { + return false; } - $session_key = ereg_replace('[^a-zA-Z0-9]', '', $_REQUEST['session_key']); + $session_key = ereg_replace('[^a-zA-Z0-9]', '', $_COOKIE['session_key']); if(!strlen($session_key) == 16) { return false; } - $GLOBALS['session_key'] = $session_key; - session_purge_old(); - $id = db_get_value('wfpl_sessions', 'id', 'where session_key=%"', $session_key); - if($id === false) { + $row = db_get_assoc('wfpl_sessions', 'id,idle_timeout,expires,expires_max,value', 'where session_key=%"', $session_key); + if($row === false) { return false; } - $GLOBALS['session_id'] = $id; + $GLOBALS['wfpl_session']['exists'] = true; + $GLOBALS['wfpl_session']['id'] = $row['id']; + $GLOBALS['wfpl_session']['idle_timeout'] = $row['idle_timeout']; + $GLOBALS['wfpl_session']['expires'] = $row['expires']; + $GLOBALS['wfpl_session']['expires_max'] = $row['expires_max']; + $GLOBALS['wfpl_session']['key'] = $session_key; + + if ($row['value'] && is_array($parsed = json_decode($row['value'], true))) { + $GLOBALS['wfpl_session']['value'] = $parsed; + } else { + $GLOBALS['wfpl_session']['value'] = array(); + } + + # this session is not idle (extend if it's extendable) + session_touch(); + return true; } + +# generate a random password using only letters and numbers that look +# particularly unique +function new_readable_password($length = 8) { + $character_set = "ABCDEFHJKLMNPQRTUWXY34789"; + $code = ""; + + # PHP 4.2.0 and up seed the random number generator for you. + # Lets hope that it seeds with something harder to guess than the clock. + while($length--) { + $code .= $character_set{mt_rand(0, 24)}; # inclusive + } + + return $code; +} + +# depricated # return username if a session exists and is authenticated -function session_exists_and_authed() { +function logged_in() { if(!session_exists()) { return false; } @@ -147,27 +205,98 @@ function session_exists_and_authed() { } -# find existing session, or make one +# depricated +function session_exists_and_authed() { + return logged_in(); +} + + +# depricated +# return true if a session exists and is authenticated +function logged_in_as_admin() { + if(!session_exists()) { + return false; + } + + if(session_get('auth_admin')) { + return true; + } + return false; +} + + +# find existing session, or make one (name "session_init" was taken) function init_session() { if(!session_exists()) { session_new(); } } -# save a variable into the session +# internal use only (write session cache to db) +function _sync_session() { + db_update('wfpl_sessions', + 'value', json_encode($GLOBALS['wfpl_session']['value']), + 'where id=%i', $GLOBALS['wfpl_session']['id'] + ); +} + +# save data into the session +# $value can be anything json_encode()able function session_set($name, $value) { - session_clear($name); - db_insert('wfpl_session_data', 'session_id,name,value', $GLOBALS['session_id'], $name, $value); + init_session(); + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + if ($GLOBALS['wfpl_session']['value'][$name] === $value) { + return; + } + } + $GLOBALS['wfpl_session']['value'][$name] = $value; + _sync_session(); +} + +# save data into the session +# values can be anything json_encode()able +function session_sets($assoc) { + init_session(); + $dirty = false; + foreach ($assoc as $name => &$value) { + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + if ($GLOBALS['wfpl_session']['value'][$name] === $value) { + continue; + } + } + $GLOBALS['wfpl_session']['value'][$name] = $value; + $dirty = true; + } + if ($dirty) { + _sync_session(); + } } # remove variable from the session -function session_clear($name) { - db_delete('wfpl_session_data', 'where session_id=%i && name=%"', $GLOBALS['session_id'], $name); +# with no args: clear all +function session_clear($name = -1) { + if(!session_exists()) { + return; + } + if ($name === -1) { + if (count($GLOBALS['wfpl_session']['value']) > 0) { + $GLOBALS['wfpl_session']['value'] = array(); + _sync_session(); + } + } elseif (isset($GLOBALS['wfpl_session']['value'][$name])) { + unset($GLOBALS['wfpl_session']['value'][$name]); + _sync_session(); + } } # get a variable into the session function session_get($name) { - return db_get_value('wfpl_session_data', 'value', 'where session_id=%i && name=%"', $GLOBALS['session_id'], $name); + if(!session_exists()) { + return false; + } + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + return $GLOBALS['wfpl_session']['value'][$name]; + } else { + return false; + } } - -?>