Beispiel #1
0
 /**
  * Read session handler.
  *
  * {@see http://php.net/manual/en/function.session-set-save-handler.php}
  *
  * @param string $sid
  * @return string
  */
 public function handler_read($sid)
 {
     try {
         if (!($record = $this->database->get_record('sessions', array('sid' => $sid), 'id'))) {
             // Let's cheat and skip locking if this is the first access,
             // do not create the record here, let the manager do it after session init.
             $this->failed = false;
             $this->recordid = null;
             $this->lasthash = sha1('');
             return '';
         }
         if ($this->recordid and $this->recordid != $record->id) {
             error_log('Second session read with different record id detected, cannot read session');
             $this->failed = true;
             $this->recordid = null;
             return '';
         }
         if (!$this->recordid) {
             // Lock session if exists and not already locked.
             $this->database->get_session_lock($record->id, $this->acquiretimeout);
             $this->recordid = $record->id;
         }
     } catch (\dml_sessionwait_exception $ex) {
         // This is a fatal error, better inform users.
         // It should not happen very often - all pages that need long time to execute
         // should close session immediately after access control checks.
         error_log('Cannot obtain session lock for sid: ' . $sid);
         $this->failed = true;
         throw $ex;
     } catch (\Exception $ex) {
         // Do not rethrow exceptions here, this should not happen.
         error_log('Unknown exception when starting database session : ' . $sid . ' - ' . $ex->getMessage());
         $this->failed = true;
         $this->recordid = null;
         return '';
     }
     // Finally read the full session data because we know we have the lock now.
     if (!($record = $this->database->get_record('sessions', array('id' => $record->id), 'id, sessdata'))) {
         // Ignore - something else just deleted the session record.
         $this->failed = true;
         $this->recordid = null;
         return '';
     }
     $this->failed = false;
     if (is_null($record->sessdata)) {
         $data = '';
         $this->lasthash = sha1('');
     } else {
         $data = base64_decode($record->sessdata);
         $this->lasthash = sha1($record->sessdata);
     }
     return $data;
 }
 /**
  * Obtain session lock
  * @param int $rowid id of the row with session record
  * @param int $timeout max allowed time to wait for the lock in seconds
  * @return void
  */
 public function get_session_lock($rowid, $timeout)
 {
     parent::get_session_lock($rowid, $timeout);
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     $sql = "SELECT GET_LOCK('{$fullname}', {$timeout})";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = $this->mysqli->query($sql);
     $this->query_end($result);
     if ($result) {
         $arr = $result->fetch_assoc();
         $result->close();
         if (reset($arr) == 1) {
             return;
         } else {
             throw new dml_sessionwait_exception();
         }
     }
 }
 /**
  * Obtain session lock
  * @param int $rowid id of the row with session record
  * @param int $timeout max allowed time to wait for the lock in seconds
  * @return bool success
  */
 public function get_session_lock($rowid, $timeout)
 {
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid, $timeout);
     $timeoutmilli = $timeout * 1000;
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     // There is one bug in PHP/freetds (both reproducible with mssql_query()
     // and its mssql_init()/mssql_bind()/mssql_execute() alternative) for
     // stored procedures, causing scalar results of the execution
     // to be cast to boolean (true/fals). Here there is one
     // workaround that forces the return of one recordset resource.
     // $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
     $sql = "BEGIN\n                    DECLARE @result INT\n                    EXECUTE @result = sp_getapplock @Resource='{$fullname}',\n                                                    @LockMode='Exclusive',\n                                                    @LockOwner='Session',\n                                                    @LockTimeout='{$timeoutmilli}'\n                    SELECT @result\n                END";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = mssql_query($sql, $this->mssql);
     $this->query_end($result);
     if ($result) {
         $row = mssql_fetch_row($result);
         if ($row[0] < 0) {
             throw new dml_sessionwait_exception();
         }
     }
     $this->free_result($result);
 }
Beispiel #4
0
 /**
  * Write session handler.
  *
  * {@see http://php.net/manual/en/function.session-set-save-handler.php}
  *
  * NOTE: Do not write to output or throw any exceptions!
  *       Hopefully the next page is going to display nice error or it recovers...
  *
  * @param string $sid
  * @param string $session_data
  * @return bool success
  */
 public function handler_write($sid, $session_data)
 {
     global $USER;
     // TODO: MDL-20625 we need to rollback all active transactions and log error if any open needed
     if ($this->failed) {
         // do not write anything back - we failed to start the session properly
         return false;
     }
     $userid = 0;
     if (!empty($USER->realuser)) {
         $userid = $USER->realuser;
     } else {
         if (!empty($USER->id)) {
             $userid = $USER->id;
         }
     }
     if (isset($this->record->id)) {
         $data = base64_encode($session_data);
         // There might be some binary mess :-(
         // Skip db update if nothing changed,
         // do not update the timemodified each second.
         $hash = sha1($data);
         if ($this->lasthash === $hash and $this->record->userid == $userid and time() - $this->record->timemodified < 20 and $this->record->lastip == getremoteaddr()) {
             // No need to update anything!
             return true;
         }
         $this->record->sessdata = $data;
         $this->record->userid = $userid;
         $this->record->timemodified = time();
         $this->record->lastip = getremoteaddr();
         try {
             $this->database->update_record_raw('sessions', $this->record);
             $this->lasthash = $hash;
         } catch (dml_exception $ex) {
             if ($this->database->get_dbfamily() === 'mysql') {
                 try {
                     $this->database->set_field('sessions', 'state', 9, array('id' => $this->record->id));
                 } catch (Exception $ignored) {
                 }
                 error_log('Can not write database session - please verify max_allowed_packet is at least 4M!');
             } else {
                 error_log('Can not write database session');
             }
             return false;
         } catch (Exception $ex) {
             error_log('Can not write database session');
             return false;
         }
     } else {
         // fresh new session
         try {
             $record = new stdClass();
             $record->state = 0;
             $record->sid = $sid;
             $record->sessdata = base64_encode($session_data);
             // there might be some binary mess :-(
             $record->userid = $userid;
             $record->timecreated = $record->timemodified = time();
             $record->firstip = $record->lastip = getremoteaddr();
             $record->id = $this->database->insert_record_raw('sessions', $record);
             $this->record = $this->database->get_record('sessions', array('id' => $record->id));
             $this->lasthash = sha1($record->sessdata);
             $this->database->get_session_lock($this->record->id, SESSION_ACQUIRE_LOCK_TIMEOUT);
         } catch (Exception $ex) {
             // this should not happen
             error_log('Can not write new database session or acquire session lock');
             $this->failed = true;
             return false;
         }
     }
     return true;
 }
 public function get_session_lock($rowid)
 {
     parent::get_session_lock($rowid);
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     $sql = "SELECT GET_LOCK('{$fullname}',120)";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = $this->mysqli->query($sql);
     $this->query_end($result);
     if ($result) {
         $arr = $result->fetch_assoc();
         $result->close();
         if (reset($arr) == 1) {
             return;
         } else {
             // try again!
             $this->get_session_lock($rowid);
         }
     }
 }
 /**
  * Obtain session lock
  * @param int $rowid id of the row with session record
  * @param int $timeout max allowed time to wait for the lock in seconds
  * @return bool success
  */
 public function get_session_lock($rowid, $timeout)
 {
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid, $timeout);
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     $sql = 'SELECT MOODLE_LOCKS.GET_LOCK(:lockname, :locktimeout) FROM DUAL';
     $params = array('lockname' => $fullname, 'locktimeout' => $timeout);
     $this->query_start($sql, $params, SQL_QUERY_AUX);
     $stmt = $this->parse_query($sql);
     $this->bind_params($stmt, $params);
     $result = oci_execute($stmt, $this->commit_status);
     if ($result === false) {
         // Any failure in get_lock() raises error, causing return of bool false
         throw new dml_sessionwait_exception();
     }
     $this->query_end($result, $stmt);
     oci_free_statement($stmt);
 }
 /**
  * Obtain session lock
  * @param int $rowid id of the row with session record
  * @param int $timeout max allowed time to wait for the lock in seconds
  * @return bool success
  */
 public function get_session_lock($rowid, $timeout)
 {
     // NOTE: there is a potential locking problem for database running
     //       multiple instances of moodle, we could try to use pg_advisory_lock(int, int),
     //       luckily there is not a big chance that they would collide
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid, $timeout);
     $timeoutmilli = $timeout * 1000;
     $sql = "SET statement_timeout TO {$timeoutmilli}";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = pg_query($this->pgsql, $sql);
     $this->query_end($result);
     if ($result) {
         pg_free_result($result);
     }
     $sql = "SELECT pg_advisory_lock({$rowid})";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $start = time();
     $result = pg_query($this->pgsql, $sql);
     $end = time();
     try {
         $this->query_end($result);
     } catch (dml_exception $ex) {
         if ($end - $start >= $timeout) {
             throw new dml_sessionwait_exception();
         } else {
             throw $ex;
         }
     }
     if ($result) {
         pg_free_result($result);
     }
     $sql = "SET statement_timeout TO DEFAULT";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = pg_query($this->pgsql, $sql);
     $this->query_end($result);
     if ($result) {
         pg_free_result($result);
     }
 }
 /**
  * Obtain session lock
  * @param int $rowid id of the row with session record
  * @param int $timeout max allowed time to wait for the lock in seconds
  * @return void
  */
 public function get_session_lock($rowid, $timeout)
 {
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid, $timeout);
     $timeoutmilli = $timeout * 1000;
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     // While this may work using proper {call sp_...} calls + binding +
     // executing + consuming recordsets, the solution used for the mssql
     // driver is working perfectly, so 100% mimic-ing that code.
     // $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
     $sql = "BEGIN\n                    DECLARE @result INT\n                    EXECUTE @result = sp_getapplock @Resource='{$fullname}',\n                                                    @LockMode='Exclusive',\n                                                    @LockOwner='Session',\n                                                    @LockTimeout='{$timeoutmilli}'\n                    SELECT @result\n                END";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = sqlsrv_query($this->sqlsrv, $sql);
     $this->query_end($result);
     if ($result) {
         $row = sqlsrv_fetch_array($result);
         if ($row[0] < 0) {
             throw new dml_sessionwait_exception();
         }
     }
     $this->free_result($result);
 }
 public function get_session_lock($rowid)
 {
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid);
     $fullname = $this->dbname . '-' . $this->prefix . '-session-' . $rowid;
     $sql = 'SELECT MOODLE_LOCKS.GET_LOCK(:lockname, :locktimeout) FROM DUAL';
     $params = array('lockname' => $fullname, 'locktimeout' => 120);
     $this->query_start($sql, $params, SQL_QUERY_AUX);
     $stmt = $this->parse_query($sql);
     $this->bind_params($stmt, $params);
     $result = oci_execute($stmt, $this->commit_status);
     $this->query_end($result, $stmt);
     oci_free_statement($stmt);
 }
    public function get_session_lock($rowid) {
        if (!$this->session_lock_supported()) {
            return;
        }
        parent::get_session_lock($rowid);

        $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
        $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  120000";
        $this->query_start($sql, null, SQL_QUERY_AUX);
        $result = mssql_query($sql, $this->mssql);
        $this->query_end($result);

        $this->free_result($result);
    }
 public function get_session_lock($rowid)
 {
     // NOTE: there is a potential locking problem for database running
     //       multiple instances of moodle, we could try to use pg_advisory_lock(int, int),
     //       luckily there is not a big chance that they would collide
     if (!$this->session_lock_supported()) {
         return;
     }
     parent::get_session_lock($rowid);
     $sql = "SELECT pg_advisory_lock({$rowid})";
     $this->query_start($sql, null, SQL_QUERY_AUX);
     $result = pg_query($this->pgsql, $sql);
     $this->query_end($result);
     if ($result) {
         pg_free_result($result);
     }
 }