<?php
require_once(LIB.'BlockTable.class.php');
class Action {
	var $ip;
	var $id;
	var $user_id;
	var $db;
	var $data;
	var $reason;

	function __construct($user_id=NULL, $id=NULL, $ip=NULL) {
		$this->id = empty($id) ? @$_SESSION['uid'] : $id;
		$this->ip = empty($ip) ? @$_SERVER['REMOTE_ADDR'] : $ip;
		$this->user_id = $user_id;
		if (ENABLE_BLOCK_TABLE) {
			require_once(LIB.'BlockTable.class.php');
			$this->block_table = new BlockTable();
		} else {
			$this->block_table = NULL;
		}

		include 'db.inc.php';
		$this->db = $db;
	}

	function prepare($target, $action, $options=array()) {
		$this->data = array_merge(array(
			'created' => '^CURRENT_TIMESTAMP',
			'ip' => $this->ip,
			'id' => $this->id,
			'user_id' => $this->user_id,
			'target' => $target,
			'action' => $action
		), $options);
		return $this->check();
	}

	function prepareOnly($target, $action, $options=array()) {
		$this->data = array_merge(array(
			'created' => '^CURRENT_TIMESTAMP',
			'ip' => $this->ip,
			'id' => $this->id,
			'user_id' => $this->user_id,
			'target' => $target,
			'action' => $action
		), $options);
	}

	function check() {
		if ($this->block_table) {
			$block = $this->block_table->match($this);
			//現状のブロックルールはすべて"block"しかありえないので、戻り値があった時点で強制ブロック
			if ($block)
				return FALSE;
		}
		return TRUE;
	}

	function write($options=array()) {
		$ret = $this->db->insert('action_logs', array_merge($this->data, $options));
		$this->data = NULL;
		return $ret;
	}

	function login($success=FALSE, $session_id_or_uid=NULL) {
		$ret = $this->prepare('user', 'login', array('detail' => $session_id_or_uid, 'success' => $success));
		if ($ret === FALSE)
			return FALSE;
		$this->write();

		if ($success) {
			$ret = $this->db->insert(
				'user_sessions',
				array(
					'session_id' => $session_id_or_uid,
					'user_id' => $this->user_id,
					'created' => '^CURRENT_TIMESTAMP',
					'last_access' => '^CURRENT_TIMESTAMP'
				)
			);
			if ($ret === FALSE) {
				$this->db->delete(
					'user_sessions',
					array('session_id' => $session_id_or_uid)
				);
				session_regenerate_id(TRUE);
				return FALSE;
			}
		}

		return TRUE;
	}

	function refreshSession() {
		$this->db->delete(
			'user_sessions',
			array('session_id' => session_id())
		);

		$old_id = session_id();
		session_regenerate_id(TRUE);
		$sid = session_id();

		$ret = $this->prepare('user', 'refresh', array('detail' => $sid, 'target_key' => $old_id));
		if ($ret === FALSE)
			return FALSE;
		$this->write();

		$ret = $this->db->insert(
			'user_sessions',
			array(
				'session_id' => $sid,
				'user_id' => $this->user_id,
				'created' => '^CURRENT_TIMESTAMP',
				'last_access' => '^CURRENT_TIMESTAMP'
			)
		);
		if ($ret === FALSE) {
			$this->db->delete(
				'user_sessions',
				array('session_id' => $sid)
			);
			session_regenerate_id(TRUE);
			return FALSE;
		}

		return TRUE;
	}

	function logout($session_id) {
		$ret = $this->prepare('user', 'logout', array('detail' => $session_id));
		if ($ret === FALSE)
			return FALSE;
		$this->write();

		$exists_session_ids = $this->db->selectList(
			'user_sessions',
			'session_id',
			'user_id=?',
			$this->user_id
		);

		foreach ($exists_session_ids as $exists_session_id) {
			@session_write_close();
			session_id($exists_session_id);
			if (@session_start()) {
				unset($_SESSION['user']);
				@session_destroy();
			}
		}

		return $this->db->pureDelete('user_sessions', 'user_id=?', $this->user_id);
	}

	function read($log_id) {
		return $this->db->selectRow(
			'^'.$this->db->getTableName('action_logs').' l left join '.$this->db->getTableName('users').' u on l.user_id=u.id',
			array(
				'l.log_id',
				'l.created',
				'l.ip',
				'l.id',
				'l.user_id',
				'u.login_id',
				'u.display_name',
				'l.target',
				'l.target_id',
				'l.target_key',
				'l.action',
				'l.success',
				'l.detail'
			),
			array('log_id' => $log_id)
		);
	}

	function search($options, $limit=50, $order='log_id desc') {
		$s = $this->_getSearchConditions($options);
		return $this->db->select(
			'^'.$this->db->getTableName('action_logs').' l left join '.$this->db->getTableName('users').' u on l.user_id=u.id',
			array(
				'l.log_id',
				'l.created',
				'l.ip',
				'l.id',
				'l.user_id',
				'u.login_id',
				'u.display_name',
				'l.target',
				'l.target_id',
				'l.target_key',
				'l.action',
				'l.success',
				'l.detail'
			),
			$s['where'],
			$s['params'],
			array('limit' => $this->db->safeLimit($limit), 'order' => $order)
		);
	}

	function count($options) {
		$s = $this->_getSearchConditions($options);
		return $this->db->count(
						'^'.$this->db->getTableName('action_logs').' l left join '.$this->db->getTableName('users').' u on l.user_id=u.id',
			$s['where'],
			$s['params']
		);
	}

	function _getSearchConditions($options) {
		$w = array();
		$p = array();
		if (! empty($options['log_id'])) {
			$this->_con1('l.log_id', $options['log_id'], $w, $p);
		}
		if (! empty($options['created'])) {
			$this->_con1('l.created', $options['created'], $w, $p);
		}
		if (! empty($options['ip'])) {
			$this->_con2('l.ip', $options['ip'], $w, $p);
		}
		if (! empty($options['id'])) {
			$this->_con2('l.id', $options['id'], $w, $p);
		}
		if (! empty($options['user'])) {
			$w[] = 'u.login_id like ? or u.display_name like ?';
			$p[] = str_replace('%', '\\%', $options['user']).'%';
			$p[] = str_replace('%', '\\%', $options['user']).'%';
		}
		if (! empty($options['target'])) {
			$this->_con2('l.target', $options['target'], $w, $p);
		}
		if (! empty($options['target_id'])) {
			$this->_con1('l.target_id', $options['target_id'], $w, $p);
		}
		if (! empty($options['target_key'])) {
			$this->_con2('l.target_key', $options['target_key'], $w, $p);
		}
		if (! empty($options['action'])) {
			$this->_con2('l.action', $options['action'], $w, $p);
		}
		if (isset($options['success']) && ($options['success'] === '0' || $options['success'] === '1')) {
			$w[] = 'l.success=?';
			$p[] = $options['success'];
		}
		if (! empty($options['detail'])) {
			$w[] = 'l.detail like ?';
			$p[] = '%'.str_replace('%', '\\%', $options['detail']).'%';
		}
		if (empty($w))
			return array('where' => NULL, 'params' => array());
		return array('where' => '('.implode(') and (', $w).')', 'params' => $p);
	}

	function _con1($f, $v, &$w, &$p) {
		if (strpos($v, '>') ===  0) {
			$w[] = $f.' > ?';
			$p[] = substr($v, 1);
		} else if (strpos($v, '<') ===  0) {
			$w[] = $f.' < ?';
			$p[] = substr($v, 1);
		} else if (strpos($v, '!=') ===  0) {
			$w[] = $f.' != ?';
			$p[] = substr($v, 2);
		} else {
			$w[] = $f.' = ?';
			$p[] = $v;
		}
	}

	function _con2($f, $v, &$w, &$p) {
		$w[] = $f.' like ?';
		$p[] = str_replace('%', '\\%', $v).'%';
	}
}
