internalsessionhandler.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * GNU social's implementation of SessionHandler
  18. *
  19. * @package GNUsocial
  20. * @author Evan Prodromou
  21. * @author Brion Vibber
  22. * @author Mikael Nordfeldth
  23. * @author Sorokin Alexei
  24. * @author Diogo Cordeiro <diogo@fc.up.pt>
  25. * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
  26. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  27. */
  28. defined('GNUSOCIAL') || die();
  29. /**
  30. * Superclass representing the associated interfaces of session handling.
  31. *
  32. * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
  33. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  34. */
  35. class InternalSessionHandler implements SessionHandlerInterface
  36. {
  37. /**
  38. * A helper function to print a session-related message to the debug log if
  39. * the site session debug configuration option is enabled.
  40. * @param $msg
  41. * @return void
  42. */
  43. public static function logdeb($msg)
  44. {
  45. if (common_config('sessions', 'debug')) {
  46. common_debug("Session: " . $msg);
  47. }
  48. }
  49. /**
  50. * Dummy option for saving to file needed for full PHP adherence.
  51. *
  52. * @param $save_path
  53. * @param $session_name
  54. * @return bool true
  55. */
  56. public function open($save_path, $session_name)
  57. {
  58. return true;
  59. }
  60. /**
  61. * Dummy option for saving to file needed for full PHP adherence.
  62. *
  63. * @return bool true
  64. */
  65. public function close()
  66. {
  67. return true;
  68. }
  69. /**
  70. * Fetch the session data for the session with the given $id.
  71. *
  72. * @param $id
  73. * @return string Returns an encoded string of the read data. If nothing was read, it must return an empty string. Note this value is returned internally to PHP for processing.
  74. */
  75. public function read($id)
  76. {
  77. self::logdeb("Fetching session '$id'.");
  78. $session = Session::getKV('id', $id);
  79. if (empty($session)) {
  80. self::logdeb("Couldn't find '$id'.");
  81. return '';
  82. } else {
  83. self::logdeb("Found '$id', returning " .
  84. strlen($session->session_data) .
  85. " chars of data.");
  86. return (string)$session->session_data;
  87. }
  88. }
  89. /**
  90. * Write the session data for session with given $id as $session_data.
  91. *
  92. * @param $id
  93. * @param $session_data
  94. * @return bool Returns TRUE on success or FALSE on failure.
  95. */
  96. public function write($id, $session_data)
  97. {
  98. self::logdeb("Writing session '$id'.");
  99. $session = Session::getKV('id', $id);
  100. if (empty($session)) {
  101. self::logdeb("'$id' doesn't yet exist; inserting.");
  102. $session = new Session();
  103. $session->id = $id;
  104. $session->session_data = $session_data;
  105. $session->created = common_sql_now();
  106. $result = $session->insert();
  107. if (!$result) {
  108. common_log_db_error($session, 'INSERT', __FILE__);
  109. self::logdeb("Failed to insert '$id'.");
  110. } else {
  111. self::logdeb("Successfully inserted '$id' (result = $result).");
  112. }
  113. return (bool) $result;
  114. } else {
  115. self::logdeb("'$id' already exists; updating.");
  116. if (strcmp($session->session_data, $session_data) == 0) {
  117. self::logdeb("Not writing session '$id' - unchanged.");
  118. return true;
  119. } else {
  120. self::logdeb("Session '$id' data changed - updating.");
  121. // Only update the required field
  122. $orig = clone($session);
  123. $session->session_data = $session_data;
  124. $result = $session->update($orig);
  125. if (!$result) {
  126. common_log_db_error($session, 'UPDATE', __FILE__);
  127. self::logdeb("Failed to update '$id'.");
  128. } else {
  129. self::logdeb("Successfully updated '$id' (result = $result).");
  130. }
  131. return (bool) $result;
  132. }
  133. }
  134. }
  135. /**
  136. * Find sessions that have persisted beyond $maxlifetime and delete them.
  137. * This will be limited by config['sessions']['gc_limit'] - it won't delete
  138. * more than the number of sessions specified there at a single pass.
  139. *
  140. * @param $maxlifetime
  141. * @return bool Returns TRUE on success or FALSE on failure.
  142. */
  143. public function gc($maxlifetime)
  144. {
  145. self::logdeb("Garbage Collector has now started with maxlifetime = '$maxlifetime'.");
  146. $epoch = common_sql_date(time() - $maxlifetime);
  147. $ids = [];
  148. $session = new Session();
  149. $session->whereAdd('modified < "' . $epoch . '"');
  150. $session->selectAdd();
  151. $session->selectAdd('id');
  152. $limit = common_config('sessions', 'gc_limit');
  153. if ($limit > 0) {
  154. // On large sites, too many sessions to expire
  155. // at once will just result in failure.
  156. $session->limit($limit);
  157. }
  158. $session->find();
  159. while ($session->fetch()) {
  160. $ids[] = $session->id;
  161. }
  162. $session->free();
  163. self::logdeb("Garbage Collector found " . count($ids) . " ids to delete.");
  164. foreach ($ids as $id) {
  165. self::logdeb("Garbage Collector will now delete session '$id'.");
  166. self::destroy($id);
  167. }
  168. return true;
  169. }
  170. /**
  171. * Deletes session with given id $id.
  172. *
  173. * @param $id
  174. * @return bool Returns TRUE on success or FALSE on failure.
  175. */
  176. public function destroy($id)
  177. {
  178. self::logdeb("Destroying session '$id'.");
  179. $session = Session::getKV('id', $id);
  180. if (empty($session)) {
  181. self::logdeb("Can't find '$id' to destroy.");
  182. return false;
  183. } else {
  184. $result = $session->delete();
  185. if (!$result) {
  186. common_log_db_error($session, 'DELETE', __FILE__);
  187. self::logdeb("Failed to destroy '$id'.");
  188. } else {
  189. self::logdeb("Successfully destroyed '$id' (result = $result).");
  190. }
  191. return (bool) $result;
  192. }
  193. }
  194. }