<?php

define('GCBOS_LIMIT_FROM_START',		0);
define('GCBOS_LIMIT_FROM_END',			1);

define('COMMAND_PARAM_TYPE_NUMBER',		1);
define('COMMAND_PARAM_TYPE_STRING',		2);

define('GCBOS_MESSAGE_TYPE_NORMAL',		0);
define('GCBOS_MESSAGE_TYPE_SYSTEM',		1);
define('GCBOS_MESSAGE_TYPE_PRIVATE',	2);
define('GCBOS_MESSAGE_TYPE_MUTED',		3);
define('GCBOS_MESSAGE_TYPE_UNMUTED',	4);
define('GCBOS_MESSAGE_TYPE_ACTION',		5);
define('GCBOS_MESSAGE_TYPE_EDIT',		6);
define('GCBOS_MESSAGE_TYPE_DELETE',		7);

define('GCBOS_EFFECT_MUTE', 1);
define('GCBOS_EFFECT_TRUESIGHT', 2);

require_once(DIR . '/includes/class_bbcode.php');
require_once(DIR . '/includes/functions_newpost.php');
require_once(DIR . '/includes/functions_user.php');

class GCBOS
{
	var $vbulletin;
	var $db;
	var $commands;
	
	function GCBOS()
	{
		global $vbulletin;
		
		$this->vbulletin =& $vbulletin;
		$this->db =& $this->vbulletin->db;
		$this->commands = $this->fetch_commands();
	}
	
	function fetch_commands()
	{
		global $vbphrase;
		
		$commands = array();
		$permissions = convert_bits_to_array($this->vbulletin->userinfo['permissions']['gcbos_permissions'], $this->vbulletin->bf_ugp_gcbos_permissions);
						
		($hook = vBulletinHook::fetch_hook('gcbos_fetch_commands')) ? eval($hook) : false;
		
		return $commands;
	}
	
	function fetch_command_by_alias($alias)
	{
		foreach ($this->commands as $command)
		{
			if (is_array($command))
			{
				foreach ($command['alias'] as $calias)
				{
					if (strcasecmp($alias, $calias) == 0) return $command;
				}
			}
		}
		return false;
	}
	
	function perform_command($message, &$output)
	{
		global $vbphrase;
		
		if (!preg_match('#^/(\?|\w+)([\s\t]+(.*)$)?#', $message, $match)) return false;
		
		$alias = strtolower($match[1]);
		$rawparams = trim($match[3]);
		$params = array();
		$done = false;
		$permissions = convert_bits_to_array($this->vbulletin->userinfo['permissions']['gcbos_permissions'], $this->vbulletin->bf_ugp_gcbos_permissions);
		$command = $this->fetch_command_by_alias($alias);
		
		if (!$command) return;
		
		for ($i = 0; $i < count($command['params']); $i++) // for instead of foreach for ease of checking for last
		{
			$param = $command['params'][$i];
			$last = ($i == count($command['params']) - 1);
			
			if ($param == COMMAND_PARAM_TYPE_NUMBER)
			{
				if (preg_match('/^("|\'|)(\d+)\1/', $rawparams, $pmatch))
				{
					$params[] = $pmatch[2];
					$rawparams = trim(substr($rawparams, strlen($pmatch[0])));
				}
				else
				{
					return false;
				}
			}
			elseif ($param == COMMAND_PARAM_TYPE_STRING)
			{
				if ($last)
				{
					$params[] = trim($rawparams);
					$rawparams = '';
				}
				elseif (preg_match('/^("|\'|)((?(?<=.).+?|[^\s]+))(?<!\\\)\1/', $rawparams, $pmatch))
				{
					if (strlen($pmatch[1]) > 0) $pmatch[2] = str_replace('\\' . $pmatch[1], $pmatch[1], $pmatch[2]);
					$params[] = $pmatch[2];
					$rawparams = trim(substr($rawparams, strlen($pmatch[0])));
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
		
		$rawparams = trim($match[3]);
		
		$done = call_user_func($command['function'], $this, $command, $params, $message, &$output);
		
		($hook = vBulletinHook::fetch_hook('gcbos_process_command')) ? eval($hook) : false;
		
		return $done;
	}
	
	function clear_messages()
	{
		$this->db->query_write("DELETE FROM `" . TABLE_PREFIX . "gcbos`");
		$this->create_message('clear', 0, TIMENOW, 0, GCBOS_MESSAGE_TYPE_SYSTEM, $this->vbulletin->userinfo['userid']);
		return true;
	}
	
	function add_effect($userid, $effect)
	{
		if (is_array($userid)) $userid = $userid['userid'];
		
		$this->db->query_write("
			UPDATE
				`" . TABLE_PREFIX . "user`
					AS
						`user`
			SET
				`user`.`gcbos_effects` = `user`.`gcbos_effects` | '" . $this->db->escape_string($effect) . "'
			WHERE
				`user`.`userid` = '" . $this->db->escape_string($userid) . "'
			LIMIT
				1
		");
	}
	
	function remove_effect($userid, $effect)
	{
		if (is_array($userid)) $userid = $userid['userid'];
		
		$this->db->query_write("
			UPDATE
				`" . TABLE_PREFIX . "user`
					AS
						`user`
			SET
				`user`.`gcbos_effects` = `user`.`gcbos_effects` & ~'" . $this->db->escape_string($effect) . "'
			WHERE
				`user`.`userid` = '" . $this->db->escape_string($userid) . "'
			LIMIT
				1
		");
	}
	
	function fetch_users_with_effect($effect)
	{
		$users = array();
		$result = $this->db->query("
			SELECT
				`user`.*
			FROM
				`" . TABLE_PREFIX . "user`
					AS
						`user`
			WHERE
				(`user`.`gcbos_effects` & '" . $this->db->escape_string($effect) . "') = '" . $this->db->escape_string($effect) . "'
		");
		
		while ($user = $this->db->fetch_array($result))
		{
			$users[] = $user;
		}
		
		return $users;
	}
	
	function has_effect($userid, $effect = -1)	// Despite appearance, $userid is the optional one.
	{
		if ($effect < 0)
		{
			$effect = $userid;					// Told you.
			$userid = $this->vbulletin->userinfo['userid'];
		}
		
		$user = array();
		if (is_array($userid)) $user = $userid;
		if (is_numeric($userid)) $user = fetch_userinfo($userid);
		
		return ($user['gcbos_effects'] & $effect) == $effect;
	}
	
	function fetch_userinfo_by_username($username)
	{
		return $this->db->query_first("
			SELECT
				`user`.*
			FROM
				`" . TABLE_PREFIX . "user`
					AS
						`user`
			WHERE
				`user`.`username` = '" . $this->db->escape_string($username) . "'
			LIMIT
				1
		");
	}
	
	function edit_message($message, $text)
	{
		if (is_numeric($message)) $message = $this->fetch_message($message);
		
		$this->db->query_write("
			UPDATE
				`" . TABLE_PREFIX . "gcbos`
					AS
						`gcbos`
			SET
				`gcbos`.`message` = '" . $this->db->escape_string($text) . "'
			WHERE
				`gcbos`.`gcbosid` = '" . $this->db->escape_string($message['gcbosid']) . "'
			LIMIT 1
		");
		
		$this->create_message("$message[gcbosid] $text", $message['displayuserid'], $message['dateline'], $message['recipientid'], GCBOS_MESSAGE_TYPE_EDIT, $message['userid']);
	}
	
	function delete_message($message)
	{
		if (is_numeric($message)) $message = $this->fetch_message($message);
		
		$this->db->query_write("
			DELETE FROM
				`" . TABLE_PREFIX . "gcbos`
			WHERE
				`gcbosid` = '" . $this->db->escape_string($message['gcbosid']) . "'
			LIMIT 1
		");
		$this->create_message($message['gcbosid'], 0, $message['dateline'], $message['recipientid'], GCBOS_MESSAGE_TYPE_DELETE, $message['userid']);
	}
	
	function fetch_message($gcbosid)
	{
		return $this->db->query_first("
			SELECT
				`gcbos`.*
			FROM
				`" . TABLE_PREFIX . "gcbos`
					AS
						`gcbos`
			WHERE
				`gcbos`.`gcbosid` = '" . $this->db->escape_string($gcbosid) . "'
			LIMIT 1
		");
	}
	
	function build_message($gcbosid, $message = '', $displayuserid = 0, $timestamp = TIMENOW, $recipientid = 0, $userid = 0, $edit = false, $filter = true, $showmeta = true, $permanent = false, $prefixChar = '#')
	{
		global $vbphrase;
		
		if ($message == '')
		{
			$msg = $this->db->query_first("SELECT * FROM `" . TABLE_PREFIX . "gcbos` WHERE `gcbosid` = '" . $this->db->escape_string($gcbosid) . "' LIMIT 1");
			$message = $msg['message'];
			$displayuserid = $msg['displayuserid'];
			$timestamp = $msg['dateline'];
			$recipientid = $msg['recipientid'];
			$userid = $msg['userid'];
		}
		
		$result = '';
		$name = '';
		$originalMessage = $this->filter_message($message, null, false);
		$time = vbdate('H:i', $timestamp, false, false, true);
		$userinfo = fetch_userinfo($userid);
		$recipientinfo = fetch_userinfo($recipientid);
		$permissions = convert_bits_to_array($this->vbulletin->userinfo['permissions']['gcbos_permissions'], $this->vbulletin->bf_ugp_gcbos_permissions);
		$editable = ((($userid == $this->vbulletin->userinfo['userid']) && ($permissions['can_edit_own'])) || ($permissions['can_edit_others']));
		$tagStart = '';
		$tagEnd = '';
		$prefix = $prefixChar;
		
		($hook = vBulletinHook::fetch_hook('gcbos_build_message_start')) ? eval($hook) : false;
		
		if (($this->has_effect(GCBOS_EFFECT_TRUESIGHT)) && ($userinfo['userid'] > 0))
		{
			$message .= ' (' . $userinfo['username'] . ')';
		}
		
		if ($gcbosid < 1) $editable = false;
		if ($permanent) $editable = false;

		$message = utf8_decode($message);

		if ($displayuserid > 0)
		{
			$displayuserinfo = fetch_userinfo($displayuserid);
			$name = sprintf('[pm="%s"]%s[/pm]', $displayuserinfo['username'], fetch_musername($displayuserinfo));
		}
		elseif (($userid > 0) && ($displayuserid < 0))
		{
			$name = sprintf('[pm="%s"]%s[/pm]', $userinfo['username'], fetch_musername($userinfo));
		}
		else
		{
			$name = $vbphrase['GCBOS'];
		}
		
		if ($filter) $message = $this->filter_message($message);
		
		if ($editable)
		{
			$message = sprintf('[edit="%s"]%s[/edit]', $this->escape_param($gcbosid, '"'), $message);
			$prefix = sprintf("[delmessage='%s']%s[/delmessage]", $this->escape_param($gcbosid), $prefixChar);
		}
		
		if ($edit)
		{
			$tagStart = sprintf("[modify='%s|%s|%s']", $this->escape_param($gcbosid), intval(false), $this->escape_param($originalMessage));
			$tagEnd = '[/modify]';
		}
		else
		{
			$tagStart = sprintf("[message='%s|%s']", $this->escape_param($gcbosid), $this->escape_param($originalMessage));
			$tagEnd = '[/message]';
		}
		
		if (!$showmeta)
		{
			$result = sprintf("%s*[instant]%s %s[/instant]*%s", $tagStart, $name, $message, $tagEnd);
		}
		elseif ($recipientid > 0)
		{
			$result = sprintf("%s%s%s [instant]%s/[pm='%s']%s[/pm]> %s[/instant]%s", $tagStart, $prefix, $time, $name, $this->escape_param($recipientinfo['username']), fetch_musername($recipientinfo), $message, $tagEnd);
		}
		else
		{
			$result = sprintf("%s%s%s [instant]%s> %s[/instant]%s", $tagStart, $prefix, $time, $name, $message, $tagEnd);
		}
		
		($hook = vBulletinHook::fetch_hook('gcbos_build_message_complete')) ? eval($hook) : false;
		
		return $result;
	}
	
	// A taste of eunomia...
	function test_for_spam(&$message, $userid)
	{
		if (!$this->vbulletin->options['gcbos_spam_enable']) return false;
		
		$types = array(GCBOS_MESSAGE_TYPE_NORMAL, GCBOS_MESSAGE_TYPE_ACTION, GCBOS_MESSAGE_TYPE_EDIT);
		$time = TIMENOW - $this->vbulletin->options['gcbos_spam_time'];
		$num = 0;
		$spamscore = 0;
		$last = 0;
		
		($hook = vBulletinHook::fetch_hook('gcbos_spam_test_start')) ? eval($hook) : false;
		
		$types = join(', ', array_map('intval', $types));
		if (count($types) < 1) $types = GCBOS_MESSAGE_TYPE_NORMAL;
		
		$result = $this->db->query("
			SELECT
				`gcbos`.*
			FROM
				`" . TABLE_PREFIX . "gcbos` AS `gcbos`
			WHERE
				`gcbos`.`userid` = " . intval($userid) . "
				AND `gcbos`.`type` IN ($types)
				AND `gcbos`.`dateline` > " . intval($time) . "
			ORDER BY
				`gcbos`.`gcbosid` DESC
		");
		$count = $this->db->num_rows($result);
		
		while ($row = $this->db->fetch_array($result))
		{
			$num++;
			
			($hook = vBulletinHook::fetch_hook('gcbos_spam_test_bit')) ? eval($hook) : false;
			
			if (strcasecmp($row['message'], $message) == 0)
			{
				$spamscore += $this->vbulletin->options['gcbos_spam_score_identical'];
			}
			
			if ($last > 0) $spamscore += ($this->vbulletin->options['gcbos_spam_time'] - ($last - $row['dateline'])) * $this->vbulletin->options['gcbos_spam_score_speed'];
			
			$last = $row['dateline'];
		}
		
		if ($spamscore > 0) $spamscore = ($this->vbulletin->options['gcbos_spam_time'] * $spamscore) / $count;
		
		($hook = vBulletinHook::fetch_hook('gcbos_spam_test_complete')) ? eval($hook) : false;
		
		return ($spamscore >= $this->vbulletin->options['gcbos_spam_limit']);
	}
	
	function create_message($message, $displayuserid = -1, $time = TIMENOW, $recipientid = 0, $type = GCBOS_MESSAGE_TYPE_NORMAL, $userid = -1)
	{
		if (intval($userid) < 0) $userid = $this->vbulletin->userinfo['userid'];
		if (intval($displayuserid) < 0) $displayuserid = $userid;
		
		if ((!$this->has_effect($userid, GCBOS_EFFECT_MUTE)) && ($type != GCBOS_MESSAGE_TYPE_MUTE) && ($this->test_for_spam($message, $userid)))
		{
			$userinfo = fetch_userinfo($userid);
			$this->add_effect($userinfo['userid'], GCBOS_EFFECT_MUTE);
			$this->create_message($userinfo['username'], 0, TIMENOW, 0, GCBOS_MESSAGE_TYPE_MUTED);
			return;
		}
		
		$cancel = false;
		
		($hook = vBulletinHook::fetch_hook('gcbos_create_message')) ? eval($hook) : false;
		
		if ($cancel || $this->has_effect($userid, 1) && ($type != GCBOS_MESSAGE_TYPE_UNMUTED)) return;
		
		$this->db->query_write("
			INSERT INTO `" . TABLE_PREFIX . "gcbos`
			(
				displayuserid,
				userid,
				dateline,
				message,
				recipientid,
				type
			)
			VALUES
			(
				'" . $this->db->escape_string($displayuserid) . "',
				'" . $this->db->escape_string($userid) . "',
				'" . $this->db->escape_string($time) . "',
				'" . $this->db->escape_string($message) . "',
				'" . $this->db->escape_string($recipientid) . "',
				'" . $this->db->escape_string($type) . "'
			)
		");
	}
	
	function filter_message($message, $tags = null, $bbcode = true)
	{	
		if ($tags == null) $tags = $this->fetch_forbidden_tags();
		$allowedTags = fetch_tag_list();
		
		if (is_array($tags)) $tags = implode('|', array_map('preg_quote', array_keys($tags, true)));
		
		($hook = vBulletinHook::fetch_hook('gcbos_filter_message_start')) ? eval($hook) : false;
		
		foreach (explode('|', $tags) as $tag)
		{
			$allowedTags['option'][$tag] = null;
			$allowedTags['no_option'][$tag] = null;
		}
		
		if ($bbcode)
		{
			$bbp =& new vB_BbCodeParser($this->vbulletin, $allowedTags);
			
			$message = convert_url_to_bbcode($message, $allowedTags);
			$message = $bbp->do_parse($message, false, true, true, false, true);
		}
		
		$message = preg_replace('#\[(\/?(?:' . $tags . '))#', '[!$1', $message);
		
		($hook = vBulletinHook::fetch_hook('gcbos_filter_message_complete')) ? eval($hook) : false;
		
		return $message;
	}
	
	function fetch_forbidden_tags()
	{
		$tags = array();
		$cptags = explode(',', $this->vbulletin->options['gcbos_forbidden_bbcode']);
		
		$tags['message'] = true;
		$tags['notice'] = true;
		$tags['clear'] = true;
		$tags['modify'] = true;
		$tags['edit'] = true;
		$tags['delmessage'] = true;
		
		foreach ($cptags as $tag)
		{
			$tags[$tag] = true;
		}
		
		($hook = vBulletinHook::fetch_hook('gcbos_fetch_forbidden_tags')) ? eval($hook) : false;
		
		return $tags;
	}
	
	function set_notice($notice)
	{
		($hook = vBulletinHook::fetch_hook('gcbos_set_notice')) ? eval($hook) : false;
		
		$this->db->query_write("
			UPDATE
				`" . TABLE_PREFIX . "setting`
			SET
				`value` = '" . $this->db->escape_string($notice) . "'
			WHERE
				`varname` = 'gcbos_notice'
			LIMIT
				1
		");
		
		$this->create_message("notice $notice", -1, TIMENOW, 0, GCBOS_MESSAGE_TYPE_SYSTEM, $this->vbulletin->userinfo['userid']);
	}
	
	function fetch_notice()
	{
		$result = $this->db->query_first("
			SELECT
				`setting`.`value`
			FROM
				`" . TABLE_PREFIX . "setting`
					AS
						`setting`
			WHERE
				`setting`.`varname` = 'gcbos_notice'
			LIMIT 1
		");
		
		$notice = '';
		
		if (is_array($result)) $notice = $result['value'];
		
		$notice = trim($notice);
		
		($hook = vBulletinHook::fetch_hook('gcbos_fetch_notice')) ? eval($hook) : false;
		
		return $notice;
	}
	
	function escape_param($input, $quote = "'", $delim = '|')
	{
		return preg_replace('/([\[\]\\\\' . preg_quote($quote, '/') . preg_quote($delim, '/') . '])/', '\\\\$1', $input);
	}
	
	function fetch_messages($startid = 0, $limit = -1, $limitFrom = GCBOS_LIMIT_FROM_START, $includeSystem = true)
	{
		if ((intval($limit) <= 0) || intval($limit) > $this->vbulletin->options['gcbos_message_limit']) $limit = $this->vbulletin->options['gcbos_message_limit'];
		
		$messages = array();
		
		$result = $this->db->query("
			" . (($limitFrom == GCBOS_LIMIT_FROM_END) ? "SELECT
				*
			FROM
			(" : "") . "
				SELECT
					`gcbos`.`gcbosid`,
					`gcbos`.`message`,
					`gcbos`.`dateline`,
					`gcbos`.`recipientid`,
					`gcbos`.`displayuserid`,
					`gcbos`.`userid`,
					`gcbos`.`type`,
					`recipient`.`username` AS `recipientname`,
					`display`.`username` AS `username`,
					`user`.`username` AS `realname`
				FROM `" . TABLE_PREFIX . "gcbos` AS `gcbos`
					LEFT JOIN `" . TABLE_PREFIX . "user` AS `user`
						ON `user`.`userid` = `gcbos`.`userid`
					LEFT JOIN `" . TABLE_PREFIX . "user` AS `display`
						ON `display`.`userid` = `gcbos`.`displayuserid`
					LEFT JOIN `" . TABLE_PREFIX . "user` AS `recipient`
						ON `recipient`.`userid` = `gcbos`.`recipientid`
				WHERE
					`gcbos`.`gcbosid` > '" . $this->db->escape_string($startid) . "'
					AND
					(
						`gcbos`.`recipientid` = '" . $this->db->escape_string($this->vbulletin->userinfo['userid']) . "'
						OR `gcbos`.`userid` = '" . $this->db->escape_string($this->vbulletin->userinfo['userid']) . "'
						OR `gcbos`.`recipientid` = 0
					)
					" . ((!$includeSystem)?"AND `gcbos`.`type` != '" . $this->db->escape_string(GCBOS_MESSAGE_TYPE_SYSTEM) . "'":'') . "
				ORDER BY
					`gcbos`.`gcbosid` " . (($limitFrom == GCBOS_LIMIT_FROM_END) ? "DESC LIMIT " . intval($limit) . ") `tmp`
			ORDER BY
				`gcbosid` ASC" : "ASC") . "
			LIMIT
				" . intval($limit) . "
		");
		
		while ($message = $this->db->fetch_array($result))
		{
			$show = true;
			
			($hook = vBulletinHook::fetch_hook('gcbos_fetch_messages_bit')) ? eval($hook) : false;
			
			if (fetch_user_relationship($this->vbulletin->userinfo['userid'], $message['userid']) == 0)
			{
				$show = false;
			}
			
			if ($show) $messages[] = $message;
		}
		
		($hook = vBulletinHook::fetch_hook('gcbos_fetch_messages_complete')) ? eval($hook) : false;
		
		return $messages;
	}
	
	function ignore_user($userid, $victimid = -1)
	{
		if (is_array($userid)) $userid = $userid['userid'];
		if (is_array($victimid)) $victimid = $victimid['userid'];
		
		if (intval($victimid) < 1)
		{
			$victimid = $userid;
			$userid = $this->vbulletin->userinfo['userid'];
		}
		
		if ((intval($userid) < 1) || (intval($victimid) < 1)) return false;
		if ($userid == $victimid) return false;
		
		$userinfo = fetch_userinfo($userid);
		$victim = fetch_userinfo($victimid);
		
		cache_permissions($userinfo);
		
		$uglist = $victim['usergroupid'];
		if (trim($victim['membergroupids']) != '') $uglist .= ',' . $victim[membergroupids];
		
		if (!$this->vbulletin->options['ignoremods'] && can_moderate(0, '', $victim['userid'], $uglist) AND !($userinfo['permissions']['adminpermissions'] & $this->vbulletin->bf_ugp_adminpermissions['cancontrolpanel']))
		{
			return false;
		}
		
		$this->db->query_write("
			INSERT IGNORE INTO " . TABLE_PREFIX . "userlist
				(userid, relationid, type, friend)
			VALUES
				(" . $userinfo['userid'] . ", " . intval($victim['userid']) . ", 'ignore', 'no')
		");
		
		return true;
	}
	
	function unignore_user($userid, $victimid = -1)
	{
		if (is_array($userid)) $userid = $userid['userid'];
		if (is_array($victimid)) $victimid = $victimid['userid'];
		
		if (intval($victimid) < 1)
		{
			$victimid = $userid;
			$userid = $this->vbulletin->userinfo['userid'];
		}
		
		if ((intval($userid) < 1) || (intval($victimid) < 1)) return false;
		if ($userid == $victimid) return false;
		
		$this->db->query_write("
			DELETE FROM " . TABLE_PREFIX . "userlist
			WHERE userid = " . intval($userid) . "
				AND type = 'ignore'
				AND relationid = " . intval($victimid) . "
		");
		
		return true;
	}
	
	function fetch_ignores($userid = -1)
	{
		$result = array();
		
		if (is_array($userid)) $userid = $userid['userid'];
		if (intval($userid) < 1) $userid = $this->vbulletin->userinfo['userid'];
		
		$rows = $this->db->query("
			SELECT
				`user`.*
			FROM
				`" . TABLE_PREFIX . "userlist`
					AS
						`ul`
				LEFT JOIN
					`" . TABLE_PREFIX . "user`
					AS
						`user`
							ON
								`user`.`userid` = `ul`.`relationid`
			WHERE
				`ul`.`type` = 'ignore'
				AND `ul`.`userid` = '" . intval($userid) . "'
		");
		
		while ($row = $this->db->fetch_array($rows))
		{
			$result[] = $row;
		}
		
		return $result;
	}
}

?>