X-Git-Url: https://jasonwoof.com/gitweb/?p=wfpl.git;a=blobdiff_plain;f=session.php;h=f31085fc326e6f20085ba78d79bb0ff8679b7c00;hp=4f1cb67285d25a6b171887bcd77b83dc937a2be5;hb=9a3136a5bee66e1055ffb566373952b6054dd7bf;hpb=b0b28a4587992b92f47abb0429df0e1eaf7f151f diff --git a/session.php b/session.php index 4f1cb67..f31085f 100644 --- a/session.php +++ b/session.php @@ -2,89 +2,125 @@ # 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."); - } -} - -# 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 +# 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) -# 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 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - $id = " "; + $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) { +# start a new session, tracked by a browser "session cookie". +# +# args: +# $idle_timeout (seconds) session ends after this much inactivity (or up to 10% less) +# $max_length (seconds) session ends after this long, regardless of activity +function session_new($idle_timeout = 129600 /* 36 hours */, $max_length = 604800 /* 1 week */) { + 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); - return $GLOBALS['session_key']; + $now = time(); + $row = array( + 'session_key' => $session_key, + 'idle_timeout' => $idle_timeout, + 'expires' => $now + $idle_timeout, + 'expires_max' => $now + $max_length, + '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' => $idle_timeout, + 'expires' => $now + $idle_timeout, + 'expires_max' => $now + $max_length, + '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']); +# this is a helper function. See session_new() +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(); + $last_activity = $GLOBALS['wfpl_session']['expires'] - $GLOBALS['wfpl_session']['idle_timeout']; + # don't db_update if only a tiny fraction of the idle timeout has passed + $db_threshold = ceil(0.1 * $GLOBALS['wfpl_session']['idle_timeout']); + if ($now > $last_activity + $db_threshold) { + $expires = min( + $GLOBALS['wfpl_session']['expires_max'], + $now + $GLOBALS['wfpl_session']['idle_timeout'] + ); + db_update('wfpl_sessions', 'expires', $expires, 'where id=%i', $GLOBALS['wfpl_session']['id']); + $GLOBALS['wfpl_session']['expires'] = $expires; + } + } } # delete the current session @@ -92,54 +128,81 @@ 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 = preg_replace('|[^a-z0-9]|i', '', $_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; + } + $now = time(); + if ($now >= (int) $row['expires']) { + session_purge_old(); return false; } - $GLOBALS['session_id'] = $id; + $GLOBALS['wfpl_session']['exists'] = true; + $GLOBALS['wfpl_session']['id'] = $row['id']; + $GLOBALS['wfpl_session']['idle_timeout'] = (int) $row['idle_timeout']; + $GLOBALS['wfpl_session']['expires'] = (int) $row['expires']; + $GLOBALS['wfpl_session']['expires_max'] = (int) $row['expires_max']; + $GLOBALS['wfpl_session']['key'] = $session_key; + + if (strlen($row['value']) && is_array($parsed = json_decode($row['value'], true))) { + $GLOBALS['wfpl_session']['value'] = $parsed; + } else { + $GLOBALS['wfpl_session']['value'] = array(); + } + + # mark session as not idle + 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; } @@ -148,27 +211,100 @@ 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() { + if (count($GLOBALS['wfpl_session']['value']) > 0) { + $value = json_encode($GLOBALS['wfpl_session']['value']); + } else { + $value = ''; + } + db_update('wfpl_sessions', 'value', $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; + } } - -?>