<?php

/**
 * @file
 * Nifty OAuth2 Library Memcache Implementation.
 */

include "OAuth2.inc";

/**
 * OAuth2 Library Memcache Implementation.
 */
class MemcacheOAuth2 extends OAuth2 {

	private $memcache;

	/**
	 * Overrides OAuth2::__construct().
	 */
	public function __construct($config = array()) {
		parent::__construct($config);
		date_default_timezone_set('Asia/Tokyo');

		try {
			$this->memcache = new Memcache;
			// single memcache host
			//$this->memcache->connect('localhost', 11211);

			// multiple memcache hosts
			$memcache_hosts = explode(" ", $this->conf['memcache_host']);
			for ($i = 0; $i < count($memcache_hosts); $i++) {
				$host_port = explode(":", $memcache_hosts[$i]);
				$this->memcache->addServer($host_port[0], $host_port[1]);
			}

			$serverStatus = $this->memcache->getStats();
			if ($serverStatus == FALSE) {
				throw new Exception('system_error02');
			}
		}
		catch (Exception $e) {
			$this->log->err(logformat("OAuth exception occured: Can't connect memcache"));
			if ($this->conf['endpoint'] == 'authorize' || $this->conf['endpoint'] == 'applist') {
				$this->showError('system_error02');
				exit();
			}
			$this->handleException("500 internal server error", 'system_error02');
		}
	}

	/**
	 * Release memcache connection during destruct.
	 */
	function __destruct() {
		$this->memcache->close(); // Release memcache connection
	}

	/**
	 * Handle Memcache exceptional cases.
	 */
	public function handleException($http_status_code, $error, $error_description = NULL, $error_uri = NULL, $e = NULL) {
		if ($e) {
			$this->log->err(logformat("OAuth exception occured: ".$e->getMessage()));
		}
		else {
			$this->log->err(logformat("OAuth exception occured: ".$error));
		}

		$result['error'] = $error;

		if ($this->getVariable('display_error') && $error_description)
			$result["error_description"] = $error_description;

		if ($this->getVariable('display_error') && $error_uri)
			$result["error_uri"] = $error_uri;

		header("HTTP/1.1 ".$http_status_code);

		// add by shuu on 2012-07-13
		if ($http_status_code == OAUTH2_HTTP_UNAUTHORIZED) {
			header("WWW-Authenticate: OAuth");
		}

		header("Content-Type: application/json");
		header("Cache-Control: no-store");
		echo json_encode($result);

		exit();
	}

	/**
	 * Get client info from LDAP server
	 * @param $client_id Client identifier which is identical to service id
	 */
	public function getClient($client_id) {
		// searching client id without additional parameter
		if (preg_match('/^([^_]+)_/', $client_id, $matches)) {
			$client_id = $matches[1];
		}

		$errcnt = 0;
		$result = NULL;
		$arr_uri = explode(" ", $this->conf['uri']);
		for ($i = 0; $i < count($arr_uri); $i++) {
			$ldap = @ldap_connect($arr_uri[$i]);
			if (!@ldap_bind($ldap, $this->conf['binddn'], $this->conf['bindpw'])) {
				$errcnt++;
				continue;
			}
			$res = @ldap_search($ldap, 'ou=Services,'.$this->conf['basedn'], '(&(objectClass=seciossApplicationConfig)(cn='.$client_id.'))');
			if ($res == false) {
				continue;
			}
			$num = ldap_count_entries($ldap, $res);
			if ($num) {
				$entries = ldap_get_entries($ldap, $res);
				$result = unserialize($entries[0]['seciossconfigserializeddata'][0]);
			}
			else {
				continue;
			}
			ldap_unbind($ldap);
			break;
		}
		if ($errcnt == count($arr_uri)) {
			throw new Exception('system_error01');
		}
		return $result;
	}

	/**
	 * Implements OAuth2::checkClientCredentials().
	 */
	protected function checkClientCredentials($client_id, $client_secret = NULL) {
		try {
			if ($client_secret === NULL) {
				//return $result !== FALSE;
				return $result = FALSE;
			}

			$result = $this->getClient($client_id);

			if ($result == NULL) {
				return FALSE;
			}

			return $result["client_secret"] == $client_secret;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error01');
		}
	}

	/**
	 * Implements OAuth2::getRedirectUri().
	 */
	protected function getRedirectUri($client_id) {
		try {
			$result = $this->getClient($client_id);

			if ($result == NULL) {
				return FALSE;
			}

			return isset($result["redirect_uri"]) && $result["redirect_uri"] ? $result["redirect_uri"] : NULL;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error01');
		}
	}

	/**
	 * get username by a valid access token.
	 */
	public function getAccessTokenUsername($oauth_token) {
		try {
			$token = $this->getAccessToken($oauth_token);

			if ($token === NULL)
				return FALSE;

			return isset($token["username"]) && $token["username"] ? $token["username"] : NULL;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02');
		}
	}

	/**
	 * Implements OAuth2::getAccessToken().
	 */
	protected function getAccessToken($oauth_token) {
		try {
			$prefix = $this->conf['auth_level'];

			$result = $this->memcache->get($prefix.'_access_tokens_'.$oauth_token);

			return $result !== FALSE ? $result : NULL;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02');
		}
	}

	/**
	 * Implements OAuth2::setAccessToken().
	 */
	protected function setAccessToken($oauth_token, $client_id, $expires, $scope = NULL, $username = NULL, $additional_data = NULL) {
		try {
			$prefix = $this->conf['auth_level'];
			$lifetime = $this->conf['access_token_lifetime'];
			if (!$lifetime) {
			$lifetime = $expires;
			}
			else {
			$lifetime += time();
			}

			// searching client id without additional parameter
			if (preg_match('/^([^_]+)_/', $client_id, $matches)) {
			$client_id = $matches[1];
			}

			$tokenArray = array();
			$tokenArray['access_token'] = $oauth_token;
			$tokenArray['client_id'] = $client_id;
			$tokenArray['expires'] = $lifetime;
			$tokenArray['scope'] = $scope;
			$tokenArray['username'] = $username;
			$tokenArray['additional_data'] = $additional_data;

			$this->memcache->set($prefix.'_access_tokens_'.$oauth_token, $tokenArray, false, $this->conf['memcache_duration']);

		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to save $prefix Access Token($oauth_token) to memcache");
		}
	}

	/**
	 * OAuth2.0 draft 26
	 * delete access token from memcache after used/renewed
	 */
	protected function unsetAccessToken($oauth_token) {
		try {
			$prefix = $this->conf['auth_level'];

			$this->memcache->delete($prefix.'_access_tokens_'.$oauth_token);
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to unset $prefix Access Token($oauth_token) from memcache");
		}
	}


	/**
	 * Implements OAuth2::getRefreshToken().
	 */
	protected function getRefreshToken($refresh_token) {
		try {
			$prefix = $this->conf['auth_level'];

			$result = $this->memcache->get($prefix.'_refresh_tokens_'.$refresh_token);

			return $result !== FALSE ? $result : NULL;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02');
		}
	}


	/**
	 * Implements OAuth2::setRefreshToken().
	 */
	protected function setRefreshToken($refresh_token, $client_id, $expires, $scope = NULL, $username = NULL, $access_token = NULL, $additional_data = NULL) {
		try {
			$prefix = $this->conf['auth_level'];
			$lifetime = $this->conf['refresh_token_lifetime'];
			if (!$lifetime) {
			$lifetime = $expires;
			}
			else {
			$lifetime += time();
			}

			// searching client id without additional parameter
			if (preg_match('/^([^_]+)_/', $client_id, $matches)) {
			$client_id = $matches[1];
			}

			$tokenArray = array();
			$tokenArray['refresh_token'] = $refresh_token;
			$tokenArray['client_id'] = $client_id;
			$tokenArray['expires'] = $lifetime;
			$tokenArray['scope'] = $scope;
			$tokenArray['username'] = $username;
			$tokenArray['access_token'] = $access_token;//新しいトークン発行したら、使わないトークンを無効にする
			$tokenArray['additional_data'] = $additional_data;

			$this->memcache->set($prefix.'_refresh_tokens_'.$refresh_token, $tokenArray, false, $this->conf['memcache_duration']);

		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to save $prefix Refresh Token($refresh_token) to memcache");
		}
	}

	/**
	 * OAuth2.0 draft 26
	 * delete refresh token from memcache after used
	 */
	protected function unsetRefreshToken($refresh_token) {
		try {
			$prefix = $this->conf['auth_level'];

			$this->memcache->delete($prefix.'_refresh_tokens_'.$refresh_token);
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to unset $prefix Refresh Token($refresh_token) from memcache");
		}
	}

	/**
	 * Implements OAuth2::getAuthCode().
	 */
	protected function getAuthCode($code) {
		try {
			$prefix = $this->conf['auth_level'];

			$result = $this->memcache->get($prefix.'_auth_codes_'.$code);

			return $result !== FALSE ? $result : NULL;
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02');
		}
	}

	/**
	 * Implements OAuth2::setAuthCode().
	 */
	protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL, $username = NULL, $additional_data = NULL) {
		try {
			$prefix = $this->conf['auth_level'];
			$lifetime = $this->conf['auth_code_lifetime'];
			if (!$lifetime) {
			$lifetime = $expires;
			}
			else {
			$lifetime += time();
			}

			// searching client id without additional parameter
			if (preg_match('/^([^_]+)_/', $client_id, $matches)) {
			$client_id = $matches[1];
			}

			$authCodeArray = array();
			$authCodeArray['code'] = $code;
			$authCodeArray['client_id'] = $client_id;
			$authCodeArray['redirect_uri'] = $redirect_uri;
			$authCodeArray['expires'] = $lifetime;
			$authCodeArray['scope'] = $scope;
			$authCodeArray['username'] = $username;
			$authCodeArray['additional_data'] = $additional_data;

			$this->memcache->set($prefix.'_auth_codes_'.$code, $authCodeArray, false, $this->conf['memcache_duration']);

		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to save $prefix Auth Code($code) to memcache");
		}
	}

	/**
	 * OAuth2.0 draft 26
	 * delete authorization code from memcache after used
	 */
	protected function unsetAuthCode($code) {
		try {
			$prefix = $this->conf['auth_level'];

			$this->memcache->delete($prefix.'_auth_codes_'.$code);
		} catch (Exception $e) {
			$this->handleException("500 internal server error", 'system_error02', "Failed to unset $prefix Auth Code($code) from memcache");
		}
	}

	/**
	 * Overrides OAuth2::getSupportedGrantTypes().
	 */
	protected function getSupportedGrantTypes() {
		return array(
			OAUTH2_GRANT_TYPE_AUTH_CODE,
			OAUTH2_GRANT_TYPE_REFRESH_TOKEN,
		);
	}

	/**
	 * Overrides OAuth2::getSupportedAuthResponseTypes().
	 */
	protected function getSupportedAuthResponseTypes() {
		return array(
			OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE,
			//OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN,
			//OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN
		);
	}

	/**
	 * Overrides OAuth2::getSupportedScopes().
	 */
	protected function getSupportedScopes() {
		$scopes = array();
		if (isset($this->conf['supported_scopes']) && $this->conf['supported_scopes']) {
			$scopes = explode(" ", $this->conf['supported_scopes']);
		}
		return $scopes;
	}

	/**
	 * display error page when error occured during finishing Client Authorization
	 */
	public function showError($error, $client_id = NULL, $client = NULL, $state = NULL) {
		$smarty = new Smarty();
		$smarty->template_dir = 'templates';
		$smarty->compile_dir = 'templates_c';

		$smarty->assign('error', $error);

		if ($client_id) {
			$smarty->assign('client_id', $client_id);
		}

		if ($client) {
			$smarty->assign('crmode1', $client["crmode1"]);
			$smarty->assign('crmode2', $client["crmode2"]);
			$smarty->assign('crmode3', $client["crmode3"]);
			$smarty->assign('samode1', $client["samode1"]);
			$smarty->assign('samode2', $client["samode2"]);
			$smarty->assign('samode3', $client["samode3"]);
			$smarty->assign('name', $client["name"]);
			$smarty->assign('serverurl', $client["serverurl"]);
			$smarty->assign('logouturl', $client["logouturl"]);
			$smarty->assign('pattern', $client["pattern"]);
			$smarty->assign('logoutlink', $client["logoutlink"]);
			$smarty->assign('ticket_name', $client["ticket_name"]);
			$smarty->assign('skip_post_url_decode', $client["skip_post_url_decode"]);
			//$smarty->assign('client_secret', $client["client_secret"]);
			$smarty->assign('redirect_uri', $client["redirect_uri"]);
			$smarty->assign('scope_flag', $client["scope_flag"]);
			$smarty->assign('id_hash_flag', $client["id_hash_flag"]);
			$smarty->assign('salt', $client["salt"]);
			$smarty->assign('provider', $client["provider"]);
			$smarty->assign('icon_url', $client["icon_url"]);
			$smarty->assign('email', $client["email"]);
		}

		if ($state) {
			$smarty->assign('state', $state);
		}

		$smarty->display('fatal.tpl');
	}


	/**
	 * アクセス回数をカウント
	 * @param ipaddr アクセス元のIP
	 * @param prefix プレフィックス
	 */
	public function getAccessCount($ipaddr, $prefix, $interval) {
		if ($ipaddr === NULL) {
			return 0;
		}
		try {
			$now = time();
			$stored = array();
			$stored = $this->memcache->get("$prefix_$ipaddr");
			if ($stored) {
				$timestampsArray = array();
				foreach (explode("#", $stored['timestamps']) as $timestamp) {
					if ($now - $timestamp < $interval) {
						array_push($timestampsArray, $timestamp);
					}
				}
				// prevent to run out of memory
				if (count($timestampsArray) < 100) {
					array_push($timestampsArray, $now);
				}

				$stored['timestamps'] = implode("#", $timestampsArray);
				$this->memcache->set("$prefix_$ipaddr", $stored, false, $interval);

				return count($timestampsArray);
			}
			else {
				$timestampsArray = array();
				array_push($timestampsArray, $now);
				$stored['timestamps'] = implode("#", $timestampsArray);
				$this->memcache->set("$prefix_$ipaddr", $stored, false, $interval);

				return 1;
			}
		}
		catch (Exception $e) {
			$this->log->err(logformat("Get access count error: memcache error"));
			return 0;
		}
	}

	/**
	 * アクセス回数をリセット
	 * @param ipaddr アクセス元のIP
	 * @param prefix プレフィックス
	 */
	public function resetAccessCount($ipaddr, $prefix) {
		try {
			return $this->memcache->delete("$prefix_$ipaddr");
		}
		catch (Exception $e) {
			$this->log->err(logformat("Reset access count error: memcache error"));
			return FALSE;
		}
	}

	/**
	 * scopeによってSAPSの返す情報によりユーザ情報を作ります
	 * @param saps 
	 * @param scope 
	 */
	public function createUserInfoFromSAPSByScope($saps, $scope='profile') {
		$userInfo = array();

		$scope = explode(" ", $scope);
		foreach ($scope as $value) {
			// 基本情報
			if ($value == 'profile') {
				// user identifier
				$userInfo['user_id'] = $saps['id'];
				// 性別
				if ($saps['sex'] == '0') {
					$userInfo['gender'] = 'male';
				}
				else if ($saps['sex'] == '1') {
					$userInfo['gender'] = 'female';
				}
				// 生年月日
				if (isset($saps['birthday']) && $saps['birthday']) {
					$birthday = substr($saps['birthday'], 4, 2).'/'.substr($saps['birthday'] ,6, 2).'/'.substr($saps['birthday'], 0, 4);
					$userInfo['birthday'] = $birthday;
				}
				// 更新日
				if (isset($saps['last_update']) && $saps['last_update']) {
					$updated_time = date("Y-m-d\TH:i:sO", strtotime($saps['last_update']));
					$userInfo['updated_time'] = $updated_time;
				}
			}
			// メールアドレス
			if ($value == 'email') {
				if (isset($saps['mail_address']) && $saps['mail_address']) {
					$userInfo['email'] = $saps['mail_address'];
					if ($saps['type'] == '1') {
						$userInfo['email_verified'] = true;
					}
					else if ($saps['mail_flg'] == '0') {
						$userInfo['email_verified'] = false;
					}
					else if ($saps['mail_flg'] == '1') {
						$userInfo['email_verified'] = true;
					}
				}
			}
			// 住所
			if ($value == 'address') {
				// 郵便番号
				if (preg_match('/^A/', $saps['area_code'])) {
					$userAddress = array();
					if (isset($saps['post_code']) && $saps['post_code']) {
						$userAddress['postal_code'] = $saps['post_code'];
					}
					$userAddress['country'] = 'JA';
					$userInfo['address'] = $userAddress;
				}
			}
		}

		return $userInfo;
	}


	/**
	 * 連携履歴更新
	 * @param op	更新オプション
	 * 				0: 連携状態確認、
	 * 				1: 連携状態新規、
	 *				2: 「最終利用日」と「ユーザIDハッシュ」更新
	 * 				3: 連携状態削除、
	 * @param user_id			ユーザID
	 * @param service_id		クライアントSPのID
	 * @param deleted			削除フラグ
	 * @param scope				認可scope
	 * @param additional_data	入力ID
	 * @param contents_id		ハッシュ化されたユーザID
	 * @return 更新結果
	 * 				array/NULL: クエリ結果
	 * 				0:	更新成功
	 * 				-1: コネクションエラー
	 * 				-2: SQLエラー
	 */
	private function queryOAuthHistory($op, $user_id, $service_id, $deleted = NULL, $scope = NULL, $additional_data = NULL, $contents_id = NULL) {
		$query_result = 0;
		$current_timestamp = "'".date("Y-m-d H:i:s")."'";
		if (preg_match('/^([^_]+)_/', $service_id, $matches)) {
			$service_id = $matches[1];
		}

		//==============================================================================

		// データベース情報取得
		$reg_dsn = '/^DBI:mysql:database=(.*);host=(.*);port=(.*);mysql_connect_timeout=(.*)$/';
		if ($this->conf['endpoint'] == 'authorize' || $this->conf['endpoint'] == 'resource' || $this->conf['endpoint'] == 'applist_w') {
			// Database insert/update
			$arr_dsn = explode(" ", $this->conf['dsn']);
		}
		else {
			// $this->conf['endpoint'] == 'token' || $this->conf['endpoint'] == 'applist'
			// Database select
			$arr_dsn = explode(" ", $this->conf['readdsn']);
		}
		$cnt_dsn = count($arr_dsn);
		$cnt = 0;

		foreach($arr_dsn as $dsn) {
			$cnt++;
			if (preg_match($reg_dsn, $dsn, $matches)) {
				$db_info_dbname = isset($matches[1]) ? $matches[1] : '';
				$db_info_host = isset($matches[2]) ? $matches[2] : '';
				$db_info_port = isset($matches[3]) ? $matches[3] : '';
				$db_info_timeout = isset($matches[4]) ? $matches[4] : 10;
			}
			if (!$db_info_dbname || !$db_info_host || !$db_info_port) {
				$this->log->crit(logformat("Get database information failed ($cnt/$cnt_dsn)"));
				if ($cnt_dsn == $cnt) {
					return -1;
				}
				else {
					continue;
				}
			}

			// データベースコネクション作成
			ini_set('mysql.connect_timeout', $db_info_timeout);
			$db_conn = @mysql_connect("$db_info_host:$db_info_port", $this->conf['dbuser'], $this->conf['dbpasswd']);
			if (!$db_conn) {
				$this->log->crit(logformat("Connect to database failed ($cnt/$cnt_dsn): ".mysql_error()));
				if ($cnt_dsn == $cnt) {
					return -1;
				}
				else {
					continue;
				}
			}
			else if (!mysql_select_db($db_info_dbname)) {
				$this->log->crit(logformat("Select database $db_info_dbname failed ($cnt/$cnt_dsn): ".mysql_error()));
				@mysql_close($db_conn);
				if ($cnt_dsn == $cnt) {
					return -1;
				}
				else {
					continue;
				}
			}
		//==============================================================================

			// 連携状態確認
			//$row = array();
			$applist = array();
			if ($op == '0' || $op == '1') {
				$str_sql = "select * from ".$this->conf['oauth_table']." where 1 = 1 ";
				if ($user_id) {
					$str_sql .= "and user_id = '$user_id' ";
				}
				if ($service_id) {
					$str_sql .= "and oauth_service_id = '$service_id' ";
				}
				if ($deleted !== NULL) {
					$str_sql .= "and deleted = '$deleted' ";
				}

				// 検索SQLを実行
				$result = mysql_query($str_sql);
				//$this->log->debug(logformat("DEBUG - OAuth SQL query executed: [$str_sql]"));
				if (!$result) {
					$this->log->crit(logformat("Get OAuth authorization status failed: ".mysql_error()));
					$query_result = -2;
				}
				else {
					$this->log->debug(logformat("DEBUG - Select OAuth authorization status succeeded"));
					while ($row = @mysql_fetch_assoc($result)) {
						$applist[] = $row;
					}
					$query_result = $applist;
				}
			}

			// 連携状態新規
			if ($op == '1') {
				if (empty($applist)) {
					$str_sql = "INSERT INTO ".$this->conf['oauth_table']." (user_id, oauth_service_id, scope, additional_data, contents_id, create_time, update_time, access_time, deleted)".
								"VALUES('$user_id', '$service_id', '$scope', '$additional_data', '$contents_id', $current_timestamp, $current_timestamp, NULL, '0')";
				}
				else {
					$str_sql = "UPDATE ".$this->conf['oauth_table']." SET deleted = 0, update_time = $current_timestamp ";
					//if ($scope) {
						$str_sql .= ", scope = '$scope' ";
					//}
					if ($additional_data) {
						$str_sql .= ", additional_data = '$additional_data' ";
					}
					if ($contents_id) {
						$str_sql .= ", contents_id = '$contents_id'";
					}
					$str_sql .= "WHERE user_id = '$user_id' and oauth_service_id = '$service_id'";
				}

				$result = mysql_query($str_sql);
				//$this->log->debug(logformat("DEBUG - OAuth SQL query executed: [$str_sql]"));
				if (!$result) {
					$this->log->crit(logformat("Create OAuth authorization status failed: ".mysql_error()));
					$query_result = -2;
				}
				else {
					$this->log->debug(logformat("DEBUG - Create OAuth authorization status succeeded"));
					$query_result = 0;
				}
			}

			// 「最終利用日」と「ユーザIDハッシュ」更新
			else if ($op == '2') {
				$str_sql = "UPDATE ".$this->conf['oauth_table']." SET access_time = $current_timestamp ";
				if ($contents_id) {
					$str_sql .= ", contents_id = '$contents_id'";
				}
				$str_sql .= "WHERE user_id = '$user_id' and oauth_service_id = '$service_id'";

				$result = mysql_query($str_sql);
				//$this->log->debug(logformat("DEBUG - OAuth SQL query executed: [$str_sql]"));
				if (!$result) {
					$this->log->crit(logformat("Update OAuth authorization status failed: ".mysql_error()));
					$query_result = -2;
				}
				else {
					$this->log->debug(logformat("DEBUG - Update OAuth authorization status succeeded"));
					$query_result = 0;
				}
			}

			// 連携状態削除
			else if ($op == '3') {
				$str_sql = "UPDATE ".$this->conf['oauth_table']." SET deleted = $deleted, update_time = $current_timestamp WHERE user_id = '$user_id' and oauth_service_id = '$service_id'";

				$result = mysql_query($str_sql);
				//$this->log->debug(logformat("DEBUG - OAuth SQL query executed: [$str_sql]"));
				if (!$result) {
					$this->log->crit(logformat("Delete OAuth authorization status failed: ".mysql_error()));
					$query_result = -2;
				}
				else {
					$this->log->debug(logformat("DEBUG - Delete OAuth authorization status succeeded"));
					$query_result = 0;
				}
			}

			@mysql_close($db_conn);
			break;
		}
		return $query_result;
	}


	public function getAuthorizedApps($user_id, $service_id, $deleted = NULL) {
		$query_result = $this->queryOAuthHistory('0', $user_id, $service_id, $deleted);
		if ($query_result == -1 || $query_result == -2) {
			return FALSE;
		}
		return $query_result;
	}

	public function createAuthorizedApps($user_id, $service_id, $scope, $additional_data) {
		$query_result = $this->queryOAuthHistory('1', $user_id, $service_id, NULL, $scope, $additional_data);
		return $query_result == 0;
	}

	public function updateAuthorizedApps($user_id, $service_id, $scope, $contents_id) {
		$query_result = $this->queryOAuthHistory('2', $user_id, $service_id, NULL, $scope, NULL, $contents_id);
		return $query_result == 0;
	}

	public function removeAuthorizedApps($user_id, $service_id) {
		$query_result = $this->queryOAuthHistory('3', $user_id, $service_id, '1');
		return $query_result == 0;
	}


	/**
	 * scopeを日本語で表示
	 * @param scope
	 * @japanese_scopes 日本語変換パタン
	 *                  定義形式："profile=ポロフィール email=メールアドレス address=住所"
	 */
	public function displayScopeJP($scope, $japanese_scopes) {
		if (!$scope) {
			return '';
		}
		// default japanese scopes
		if (!$japanese_scopes) {
			$japanese_scopes = "profile=ポロフィール email=メールアドレス address=住所";
		}

		$patterns = explode(" ", $japanese_scopes);
		foreach ($patterns as $value) {
			if (preg_match('/([^=]*)=(.*)$/', $value, $matches)) {
				$scope = preg_replace("/$matches[1]/", $matches[2], $scope);
			}
		}
		$scope = preg_replace('/ /', '、', $scope);

		return $scope;
	}

	/**
	 * 指定URLからコンテンツ取得
	 * @param url
	 * @param timeout
	 */
	public function getContentsByUrl($url, $timeout) {
		$curlObj = curl_init();
		curl_setopt($curlObj, CURLOPT_URL, $url);
		if (preg_match('/^https/', $url)) {
			curl_setopt($curlObj, CURLOPT_PORT , 443);
			curl_setopt($curlObj, CURLOPT_SSL_VERIFYPEER, 0);
			curl_setopt($curlObj, CURLOPT_SSL_VERIFYHOST, 0);
		}
		curl_setopt($curlObj, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curlObj, CURLOPT_FAILONERROR, 1);
		curl_setopt($curlObj, CURLOPT_TIMEOUT, $timeout);
		$response = curl_exec($curlObj);
		if ($response === FALSE) {
			$this->log->debug(logformat("DEBUG - Get contents by url failed: ".curl_error($curlObj)));
		}
		curl_close($curlObj);
		return $response;
	}

	/**
	 * Check both side
	 */
	public function checkScope2($required_scope, $available_scope) {
		// The required scope should match or be a subset of the available scope
		if (!is_array($required_scope))
		$required_scope = explode(" ", $required_scope);

		if (!is_array($available_scope))
		$available_scope = explode(" ", $available_scope);

		return (count(array_diff($required_scope, $available_scope)) == 0 && count(array_diff($available_scope, $required_scope)) == 0);
	}

}
