session.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. /*
  3. Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.
  4. This file is part of the Fat-Free Framework (http://fatfreeframework.com).
  5. This is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or later.
  8. Fat-Free Framework is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License along
  13. with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. namespace DB\SQL;
  16. //! SQL-managed session handler
  17. class Session extends Mapper {
  18. protected
  19. //! Session ID
  20. $sid,
  21. //! Anti-CSRF token
  22. $_csrf,
  23. //! User agent
  24. $_agent,
  25. //! IP,
  26. $_ip,
  27. //! Suspect callback
  28. $onsuspect;
  29. /**
  30. * Open session
  31. * @return TRUE
  32. * @param $path string
  33. * @param $name string
  34. **/
  35. function open($path,$name) {
  36. return TRUE;
  37. }
  38. /**
  39. * Close session
  40. * @return TRUE
  41. **/
  42. function close() {
  43. $this->reset();
  44. $this->sid=NULL;
  45. return TRUE;
  46. }
  47. /**
  48. * Return session data in serialized format
  49. * @return string
  50. * @param $id string
  51. **/
  52. function read($id) {
  53. $this->load(['session_id=?',$this->sid=$id]);
  54. if ($this->dry())
  55. return '';
  56. if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
  57. $fw=\Base::instance();
  58. if (!isset($this->onsuspect) ||
  59. $fw->call($this->onsuspect,[$this,$id])===FALSE) {
  60. //NB: `session_destroy` can't be called at that stage (`session_start` not completed)
  61. $this->destroy($id);
  62. $this->close();
  63. unset($fw->{'COOKIE.'.session_name()});
  64. $fw->error(403);
  65. }
  66. }
  67. return $this->get('data');
  68. }
  69. /**
  70. * Write session data
  71. * @return TRUE
  72. * @param $id string
  73. * @param $data string
  74. **/
  75. function write($id,$data) {
  76. $this->set('session_id',$id);
  77. $this->set('data',$data);
  78. $this->set('ip',$this->_ip);
  79. $this->set('agent',$this->_agent);
  80. $this->set('stamp',time());
  81. $this->save();
  82. return TRUE;
  83. }
  84. /**
  85. * Destroy session
  86. * @return TRUE
  87. * @param $id string
  88. **/
  89. function destroy($id) {
  90. $this->erase(['session_id=?',$id]);
  91. return TRUE;
  92. }
  93. /**
  94. * Garbage collector
  95. * @return TRUE
  96. * @param $max int
  97. **/
  98. function cleanup($max) {
  99. $this->erase(['stamp+?<?',$max,time()]);
  100. return TRUE;
  101. }
  102. /**
  103. * Return session id (if session has started)
  104. * @return string|NULL
  105. **/
  106. function sid() {
  107. return $this->sid;
  108. }
  109. /**
  110. * Return anti-CSRF token
  111. * @return string
  112. **/
  113. function csrf() {
  114. return $this->_csrf;
  115. }
  116. /**
  117. * Return IP address
  118. * @return string
  119. **/
  120. function ip() {
  121. return $this->_ip;
  122. }
  123. /**
  124. * Return Unix timestamp
  125. * @return string|FALSE
  126. **/
  127. function stamp() {
  128. if (!$this->sid)
  129. session_start();
  130. return $this->dry()?FALSE:$this->get('stamp');
  131. }
  132. /**
  133. * Return HTTP user agent
  134. * @return string
  135. **/
  136. function agent() {
  137. return $this->_agent;
  138. }
  139. /**
  140. * Instantiate class
  141. * @param $db \DB\SQL
  142. * @param $table string
  143. * @param $force bool
  144. * @param $onsuspect callback
  145. * @param $key string
  146. **/
  147. function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL,$key=NULL) {
  148. if ($force) {
  149. $eol="\n";
  150. $tab="\t";
  151. $sqlsrv=preg_match('/mssql|sqlsrv|sybase/',$db->driver());
  152. $db->exec(
  153. ($sqlsrv?
  154. ('IF NOT EXISTS (SELECT * FROM sysobjects WHERE '.
  155. 'name='.$db->quote($table).' AND xtype=\'U\') '.
  156. 'CREATE TABLE dbo.'):
  157. ('CREATE TABLE IF NOT EXISTS '.
  158. ((($name=$db->name())&&$db->driver()!='pgsql')?
  159. ($db->quotekey($name,FALSE).'.'):''))).
  160. $db->quotekey($table,FALSE).' ('.$eol.
  161. ($sqlsrv?$tab.$db->quotekey('id').' INT IDENTITY,'.$eol:'').
  162. $tab.$db->quotekey('session_id').' VARCHAR(255),'.$eol.
  163. $tab.$db->quotekey('data').' TEXT,'.$eol.
  164. $tab.$db->quotekey('ip').' VARCHAR(45),'.$eol.
  165. $tab.$db->quotekey('agent').' VARCHAR(300),'.$eol.
  166. $tab.$db->quotekey('stamp').' INTEGER,'.$eol.
  167. $tab.'PRIMARY KEY ('.$db->quotekey($sqlsrv?'id':'session_id').')'.$eol.
  168. ($sqlsrv?',CONSTRAINT [UK_session_id] UNIQUE(session_id)':'').
  169. ');'
  170. );
  171. }
  172. parent::__construct($db,$table);
  173. $this->onsuspect=$onsuspect;
  174. session_set_save_handler(
  175. [$this,'open'],
  176. [$this,'close'],
  177. [$this,'read'],
  178. [$this,'write'],
  179. [$this,'destroy'],
  180. [$this,'cleanup']
  181. );
  182. register_shutdown_function('session_commit');
  183. $fw=\Base::instance();
  184. $headers=$fw->HEADERS;
  185. $this->_csrf=$fw->hash($fw->SEED.
  186. extension_loaded('openssl')?
  187. implode(unpack('L',openssl_random_pseudo_bytes(4))):
  188. mt_rand()
  189. );
  190. if ($key)
  191. $fw->$key=$this->_csrf;
  192. $this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
  193. if (strlen($this->_agent) > 300) {
  194. $this->_agent = substr($this->_agent, 0, 300);
  195. }
  196. $this->_ip=$fw->IP;
  197. }
  198. }