<?php
/*
 * framework-spider
 * spider/HttpRequest.class.php
 * 
 * グローバル関数定義ファイル
 * spiderで利用するグローバル関数を定義するファイルです
 * 
 * CopyRight(C)Framework-Spider Developer Team. 2010. All Right Reserved. 
 * URL         : http://sourceforge.jp/projects/frameworkspider/
 * Mail        : frameworkspider-dev@lists.sourceforge.jp
 * Auther      : Masanori Nakashima
 * Modifier    : Masanori Nakashima
 * 
 */
require_once(dirname(dirname(__FILE__))
.DIRECTORY_SEPARATOR.'util'
.DIRECTORY_SEPARATOR.'CharUtility.class.php');
require_once(dirname(__FILE__)
.DIRECTORY_SEPARATOR.'HttpSession.class.php');
/**
 * クライアントからの要求を提供するHTTPリクエスト、
 * クライアントへの応答を送信する際の処理を行うHTTPレスポンス
 * それぞれで使用するデータを保持するクラスです。
 * 
 * @package spider
 * @version 1.2.00
 * @copyright Copyright(c)2010, Masanori Nakashima <m_nakashima@users.sourceforge.jp>
 * @author Masanori Nakashima <m_nakashima@users.sourceforge.jp>
 * @access public
 */
class spider_HttpRequest {
	/** Attributes			*/
	var $attribute_array	= array();
	/** Errors				*/
	var $errors				= array();
	/** Global Errors		*/
	var $global_errors		= array();
	/** Response Headers	*/
	var $headers			= array();
	/** Response Body		*/
	var $response_body		= null;
	/** Response File Path	*/
	var $response_file_path	= null;
	/** Redirect To URL		*/
	var $redirect_url		= null;
	/** Attribute Prefix	*/
	var $attribute_prefix	= null;
	/** Access User Agent Class	*/
	var $agentClass			= null;
	/** locale	*/
	var $locale	= 'ja';
	/** for debug	*/
	var $startTime;
	var $lastTime;

	/**
	 * コンストラクタ
	 */
	function spider_HttpRequest() {
		$this->attribute_array	= array();
		$this->errors			= array();
		$this->startTime		= time();
		$this->lastTime			= time();
		$isLangInclude	= false;
		$msgBundlePath	= dirname(__FILE__).DIRECTORY_SEPARATOR.'message';
		if( isset($_SERVER)
		&& isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])
		&& strlen($_SERVER['HTTP_ACCEPT_LANGUAGE'])>0 ){
			$langArray = explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
			foreach($langArray as $langStr){
				if( strpos($langStr,';') !== false ){
					$langStr = substr($langStr,0,strpos($langStr,';'));
				}
				$path	= $msgBundlePath.'.'.$langStr.'.inc.php';
				if( file_exists($path) ){
					include($path);
					$isLangInclude	= true;
					break;
				}
			}
		}
		if( !$isLangInclude && file_exists($msgBundlePath.'.inc.php') ){
			include($msgBundlePath.'.inc.php');
		}
	}
	/**
	 * ロケールの設定
	 */
	function setLocale( $locale ){
		$this->locale	= $locale;
		$fileNameArray	= scandir(DIR_PATH_LIB);
		foreach($fileNameArray as $fileName){
			if(strpos($fileName,'.')===0){
			} else if( is_dir(DIR_PATH_LIB.DIRECTORY_SEPARATOR.$fileName) ){
				$msgOrgPath	= DIR_PATH_LIB.DIRECTORY_SEPARATOR.$fileName.DIRECTORY_SEPARATOR.'messages';
				$msgPath		= $msgOrgPath.'.'.$locale.'.inc.php';
				if( file_exists($msgPath) ){
					include($msgPath);
				} else if( strpos($locale,'_') > 0 ){
					$loc	= substr($locale,0,strpos($locale,'_'));
					$msgPath		= $msgOrgPath.'.'.$loc.'.inc.php';
					if( file_exists($msgPath) ){
						include($msgPath);
					}
				}
			}
		}
	}
	/**
	 * 属性を設定します。
	 * @param $key 属性名
	 * @param $value 属性値
	 */
	function setAttribute( $key, & $value ) {
		$this->attribute_array[$key]		= $value;
		$GLOBALS[$key]						= $value;
		if( !is_null( $this->attribute_prefix ) && strlen($this->attribute_prefix) > 0 ) {
			$reg_key	= $this->attribute_prefix.'.'.$key;
			$this->attribute_array[$reg_key]	= $value;
			$GLOBALS[$reg_key]								= $value;
		}
	}
	/**
	 * 指定キーの属性を取得します。
	 * @param $key 属性名
	 */
	function getAttribute( $key ) {
		$ref	= & $this->attribute_array[$key];
		return $ref;
	}
	/**
	 * 指定キーの性がセットされたか確認します
	 */
	function existAttribute( $key ) {
		return array_key_exists( $key, $this->attribute_array );
	}
	/**
	 * パラメータ取得
	 * @param $paramName パラメータ名
	 * @param $target get/post
	 * @param $getType array/
	 */
	function getParam( $paramName, $target="post", $getType=null, $orderNum=0 ) {
		$targetEnv	= $_POST;
		if( preg_match('/^[gG][eE][tT]$/',$target) > 0 ) {
			$targetEnv	= $_GET;
		}
		$value	= null;
		if( isset($targetEnv[$paramName]) ) {
			$value	= $targetEnv[$paramName];
		}
		if( is_array($value) ) {
			if( preg_match('/^[aA][rR][rR][aA][yY]$/',$getType) > 0 ) {
				$array	= array();
				foreach( $value as $key=>$val ) {
					if( strlen($val) > 0 ){
						$val	= str_replace("\r\n","\n",$val);
						$val	= str_replace("\r","\n",$val);
						$array[$key]	= mb_convert_kana(stripslashes($val),"KVa");
					}
				}
				return $value;
			} else {
				if( isset($value[$orderNum]) ) {
					$val	= mb_convert_kana(stripslashes($value[$orderNum]),"KVa");;
					$val	= str_replace("\r\n","\n",$val);
					$val	= str_replace("\r","\n",$val);
					return $val;
				} else {
					return null;
				}
			}
		} else {
			$value	= mb_convert_kana(stripslashes($value),"KVa");
			$value	= str_replace("\r\n","\n",$value);
			$value	= str_replace("\r","\n",$value);
			if( preg_match('/^[aA][rR][rR][aA][yY]$/',$getType) > 0 ) {
				if( strlen($value) > 0 ){
					return array($value);
				} else {
					return array();
				}
			} else {
				return $value;
			}
		}
	}
	function addLocaledError( $message, $log_level=false, $replaceHash=array() ) {
		$this->addError( $message, $log_level, $replaceHash );
	}
	/**
	 * エラーメッセージを追加します。
	 * @param $message エラーメッセージ
	 * @param $log_level ログ出力を同時に行う場合に指定します。falseを指定すると記録しません。デフォルトはfalseです。
	 */
	function addError( $message, $log_level=false, $replaceHash=array() ) {
		if(isset($GLOBALS['spider.messages'])
		&& is_array($GLOBALS['spider.messages'])
		&& isset($GLOBALS['spider.messages'][$message]) ){
			$message	= $GLOBALS['spider.messages'][$message];
			if(is_array($replaceHash)){
				foreach($replaceHash as $key => $val ){
					$message	= str_replace('{'.$key.'}',$val,$message);
				}
			}
		}
		array_push( $this->errors, $message );
		if( false !== $log_level && is_numeric($log_level) ) {
			$this->writeLog( $message, $log_level );
		}
	}
	/**
	 * エラーがあるか確認します。
	 * @return true:エラーあり、false:それ以外
	 */
	function isError() {
		if( count( $this->errors ) > 0 ) {
			return true;
		} else {
			return false;
		}
	}
	/**
	 * グローバルエラーを追加します
	 */
	function addGlobalError( $message ) {
		if(isset($GLOBALS['spider.messages'])
		&& is_array($GLOBALS['spider.messages'])
		&& isset($GLOBALS['spider.messages'][$message]) ){
			$message	= $GLOBALS['spider.messages'][$message];
		}
		if( isset($_SESSION) ) {
			if( isset($_SESSION['spider.global_errors']) ) {
				$globalErrors	= unserialize( $_SESSION['spider.global_errors'] );
			}
			if( !is_array( $globalErrors ) ) {
				$globalErrors	= array();
			}
			array_push($globalErrors,$message);
			$_SESSION['spider.global_errors']	= serialize($globalErrors);
		}
		array_push( $this->global_errors, $message );
	}
	/**
	 * レスポンスヘッダを設定します
	 * @param string $key このリクエストに対する追加レスポンスヘッダのキー文字列
	 * @param string $value このリクエストに対する追加レスポンスヘッダの値文字列
	 * @return void
	 */
	function setResponseHeader( $key, $value ) {
		$this->headers[$key]	= $value;
	}
	/**
	 * レスポンスボディを設定します
	 * @param string $body このリクエストに対するレスポンスボディ文字列
	 * @return void
	 */
	function setResponseBody( $body ) {
		$this->response_body	= $body;
	}
	/**
	 * レスポンスボディをファイルパスで設定します。
	 * @param string $file_path このリクエストに対するレスポンスボディが記述されたファイルの絶対パス
	 * @return void
	 */
	function setResponseFile( $file_path ) {
		$this->response_file_path	= $file_path;
	}
	/**
	 * リダイレクト先を設定します
	 * @param string $url URIで指定する場合はspider.inc.phpの設置フォルダを起点としたURIでリダイレクト先を指定します。httpから始まる完全URLも指定できます。
	 * @return void
	 */
	function redirectTo($url) {
		$isSiteInternal	= false;
		if( preg_match('/^\\//',$url) > 0 ) {
			// リダイレクト先がURI指定だった場合
			$isSiteInternal	= true;
			if( SPIDER_ORG_URI_REQUEST != SPIDER_URI_REQUEST ) {
				// リクエストと実リクエストに差分がある場合
				if( preg_match('/^'.util_CharUtility::escapeRegxStr(SPIDER_URI_BASE).'/',$url) == 0 ) {
					$prefix	= preg_replace('/'.util_CharUtility::escapeRegxStr(SPIDER_ORG_URI_BASE).'$/','',SPIDER_URI_BASE);
					$this->redirect_url	= $prefix.$url;
				} else {
					$this->redirect_url	= $url;
				}
			} else {
				// リクエストと実リクエストに差分がない場合
				if( preg_match('/^'.util_CharUtility::escapeRegxStr(SPIDER_URI_BASE).'/',$url) == 0 ) {
					$this->redirect_url	= preg_replace('/\\/$/','',SPIDER_URI_BASE).$url;
				} else {
					// 指定URIにリダイレクト
					$this->redirect_url	= $url;
				}
			}
			// 絶対URLに書き換える
			$this->redirect_url	= SPIDER_URL_DOC.preg_replace('/^\\//','',$this->redirect_url);
		} else if( preg_match('/^\\./',$url) > 0 ){
			// 相対パスの場合
			$isSiteInternal	= true;
			// 現在のアクセスフォルダ
			$accessPath	= dirname(SPIDER_URL_REQUEST).'/';
			// 絶対URLに書き換える
			if( strpos($url,'../') !== false ) {
				// 上位階層へのリンクがあるなら階層を上にたどる
				$upCount	= substr_count($url,'../');
				for( $i=0; $i<$upCount; $i++ ) {
					$accessPath	= dirname($accessPath).'/';
				}
				$this->redirect_url	= $accessPath.basename($url);
			} else {
				// ./から始まる場合は同一階層以下
				$this->redirect_url	= $accessPath.preg_replace('/^\\.\\//','',$url);
			}
		} else if( preg_match('/^http/',$url) > 0 ){
			// 相対パスかプロトコルからの完全URLの場合
			$this->redirect_url	= $url;
			// リダイレクト対象が自サイト内であるか確認
			if( preg_match('/^'.util_CharUtility::escapeRegxStr(SPIDER_URL_BASE).'/',$url) > 0
				||preg_match('/^'.util_CharUtility::escapeRegxStr(SPIDER_ORG_URL_BASE).'/',$url) > 0 ) {
				// ベースURLを含む
				$isSiteInternal	= true;
			} else {
				// 含まない場合は登録されたリバースプロキシも確認
				foreach( $GLOBALS['REV_PROXY_HASH'] as $hostName => $infoHash ) {
					$compare	= $hostName.$infoHash['uri'];
					if( preg_match('/^'.util_CharUtility::escapeRegxStr($compare).'/',$url) > 0 ) {
						$isSiteInternal	= true;
						break;
					}
				}
			}
		} else {
			// ファイルまたはフォルダ名から始まる場合
			$isSiteInternal	= true;
			$accessPath	= dirname(SPIDER_URL_REQUEST).'/'.$url;
		}
		// サイト内外に限らずセッションIDの指定がGETパラメータにあるなら一度削除する
		$this->redirect_url	= preg_replace('/'.util_CharUtility::escapeRegxStr(session_name().'=').'[0-9a-zA-Z]{0,32}(\\&|$)/','',$this->redirect_url);
		// サイト内だったら必要に応じてセッションIDを付与
		$sessionParam	= session_name().'='.session_id();
		if( $isSiteInternal ) {
			$sessionPermitHostArray	= array();
			if( isset($GLOBALS['SPIDER_USER_AGENT_REMOTE_SESSION_PERMIT_HOSTS'][$this->agentClass]) ) {
				$sessionPermitHostArray	= $GLOBALS['SPIDER_USER_AGENT_REMOTE_SESSION_PERMIT_HOSTS'][$this->agentClass];
			}
			if( is_array($sessionPermitHostArray) && count($sessionPermitHostArray) > 0 ) {
				// アクセス元ホスト指定環境なら現在のセッションIDをGETパラメータに付与
				// セッションIDをGETパラメータに付加
				if( strpos($this->redirect_url,'?') !== false ) {
					// 既存のGETパラメータがあるなら&で付加
					$this->redirect_url	.= '&'.$sessionParam;
				} else {
					$this->redirect_url	.= '?'.$sessionParam;
				}
				setcookie( SPIDER_USE_SPIDER_SESSION_ID, '', time()-42000, SPIDER_URI_BASE );
			} else {
				// クッキーが送信されているならリダイレクト前に明示的にクッキー書き換え
				setcookie( session_name(), session_id(), time()+(60*60), SPIDER_URI_BASE );
			}
		}
		// SSLリダイレクトの自動調整
		if( defined('SPIDER_ACCESS_URL_HTTP') && strlen(SPIDER_ACCESS_URL_HTTP) > 0
			&& defined('SPIDER_ACCESS_URL_SSL') && strlen(SPIDER_ACCESS_URL_SSL) > 0 ) {
			$targetSpiderUri	= preg_replace('/^'.util_CharUtility::escapeRegxStr(SPIDER_URL_BASE).'/','/',$this->redirect_url);
			$requreSSLUri		= false;
			foreach( $GLOBALS['SPIDER_ACCESS_SSL_URI_ARRAY'] as $ssUri ) {
				$uPos	= strpos($targetSpiderUri,$ssUri);
				if($uPos !== false && $uPos === 0) {
					$requreSSLUri		= true;
					break;
				}
			}

			$needSesParam	= false;
			if( $requreSSLUri == true && SPIDER_IS_SSL == false ) {
				$this->redirect_url	= str_replace(SPIDER_ACCESS_URL_HTTP,SPIDER_ACCESS_URL_SSL,$this->redirect_url);
				$needSesParam	= true;
			} else if( $requreSSLUri == false && SPIDER_IS_SSL == true ){
				$this->redirect_url	= str_replace(SPIDER_ACCESS_URL_SSL,SPIDER_ACCESS_URL_HTTP,$this->redirect_url);
				$needSesParam	= true;
			}

			if( $needSesParam && strpos($this->redirect_url,$sessionParam) === false ) {
				// セッションIDを含まないなら付加
				if( strpos($this->redirect_url,'?') !== false ) {
					// 既存のGETパラメータがあるなら&で付加
					$this->redirect_url	.= '&'.$sessionParam;
				} else {
					$this->redirect_url	.= '?'.$sessionParam;
				}
			}
		}
	}
	/**
	 * Rangeヘッダ付きアクセスの情報ハッシュを取得します。
	 * Rangeアクセスでない場合はfalseを返します
	 */
	function getRequestRangeInfo() {
		$ifUnmodifiedSince	= null;
		$downloadRange		= null;
		foreach( $_SERVER as $key => $val ) {
			if( preg_match('/^[iI][fF]\\-[uU][nN][mM][oO][dD][iI][fF][iI][eE][dD]\\-[sS][iI][nN][cC][eE]$/',trim($key)) > 0 ) {
				$ifUnmodifiedSince	= trim($val);
			} else if( preg_match('/^[rR][aA][nN][gG][eE]$/',trim($key)) > 0 ) {
				$downloadRange	= trim($val);
			}
		}
		if( !is_null($ifUnmodifiedSince) && strlen($ifUnmodifiedSince) > 0
			&& !is_null($downloadRange) && strlen($downloadRange) > 0 ) {
			list( $range_prefix, $range ) = explode('=',$downloadRange);
			list( $range, $length ) = explode('/',$range);
			list( $start, $end ) = explode('-',$range);
			$retHash	= array(
				'since'	=> $ifUnmodifiedSince,
				'start'	=> $start,
				'end'		=> $end,
				'length'	=> $length
			);
			return $retHash;
		}
		return false;
	}
	/**
	 * ページ出力しないで出力する優先出力があるか確認します
	 */
	function hasPreferredResponse() {
		if( !is_null( $this->redirect_url ) && strlen($this->redirect_url)> 0 ) {
			return true;
		} else if( !is_null( $this->response_file_path )
			&& strlen( $this->response_file_path ) > 0
			&& file_exists( $this->response_file_path ) ){
			return true;
		} else if( !is_null( $this->response_body )
			&& strlen( $this->response_body ) > 0 ) {
			return true;
		}
		return false;
	}
	/**
	 * 優先レスポンスを出力します
	 */
	function ouptutPreferredResponse() {
		if( !is_null( $this->redirect_url ) && strlen($this->redirect_url)> 0 ) {
			// リダイレクト
			header('Location: '.$this->redirect_url);
		} else if( !is_null( $this->response_file_path )
			&& strlen( $this->response_file_path ) > 0
			&& file_exists( $this->response_file_path ) ){
			// リクエストヘッダの確認
			$ifUnmodifiedSince	= null;
			$downloadRange		= null;
			foreach( $_SERVER as $key => $val ) {
				if( preg_match('/^[iI][fF]\\-[uU][nN][mM][oO][dD][iI][fF][iI][eE][dD]\\-[sS][iI][nN][cC][eE]$/',trim($key)) > 0 ) {
					$ifUnmodifiedSince	= trim($val);
				} else if( preg_match('/^[rR][aA][nN][gG][eE]$/',trim($key)) > 0 ) {
					$downloadRange	= trim($val);
				}
			}
			if( $rangeInfo = $this->getRequestRangeInfo() ) {
				$ifUnmodifiedSinceTime	= strtotime($rangeInfo['since']);
				if( $ifUnmodifiedSinceTime === false ) {
					$this->addLocaledError('spider.request.invalidmodifieredsince',SPIDER_LOG_LEVEL_FATAL);
				} else if( $ifUnmodifiedSinceTime < filemtime($this->response_file_path) ) {
					$this->addLocaledError('spider.request.modifiedsincelastaccess',SPIDER_LOG_LEVEL_FATAL);
				} else {
					$fileHashVal	= md5_file($this->response_file_path);
					header('HTTP/1.1 206 Partial content');
					header('Date: ' . date("D M j G:i:s T Y"));
					header('Last-Modified: ' . date("D M j G:i:s T Y",filemtime($this->response_file_path)));
					header('ETag: "'.$fileHashVal.'"');
					header('Accept-Ranges: bytes');
					header('Content-Range: bytes '. $rangeInfo['start'].'-'.$rangeInfo['end'].'/'.(string)(filesize($this->response_file_path)) );
					header('Content-Length: ' .(string)($rangeInfo['end']-$rangeInfo['start']+1) );
					header('Connection: close');
					header('Content-Transfer-Encoding: Binary');
					// 指定されたバイト範囲を読みだす
					$fp	= @fopen( $this->response_file_path, "rb" );
					if( $fp ) {
						fseek( $fp, $rangeInfo['start'] );
						echo fread( $fp, ($rangeInfo['end']-$rangeInfo['start']+1) );
						@fclose( $fp );
					}
				}
			} else {
				// Rangeでない場合はそのまま全て出力
				header('Content-Length: '.(string)(filesize($this->response_file_path)) );
				readfile($this->response_file_path);
			}
		} else if( !is_null( $this->response_body )
			&& strlen( $this->response_body ) > 0 ) {
			// バイト出力
			header('Content-Length: '.(string)(strlen($this->response_body)) );
			echo $this->response_body;
		}
		if( $this->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * セッション変数を設定します
	 * @param $key
	 * @param $value
	 * @param $scopse GLOBALまたは有効フォルダURI
	 */
	function setSession( $key, $value, $scope=SPIDER_SESSION_SCOPE_AUTO ) {
		$this->optimizeSession();
		if( $scope == SPIDER_SESSION_SCOPE_AUTO ) {
			$scope	= dirname(SPIDER_PHP_SELF);
		}
		if( SPIDER_SESSION_SCOPE_GLOBAL == $scope ) {
			$key	= $this->_getGlobalSessionKey( $key );
		} else {
			$key	= $scope.'/'.$key;
		}
		$spiderSession	= new spider_HttpSession();
		if( isset($_SESSION) && isset($_SESSION['spider_session_object']) ) {
			$spiderSession	= unserialize($_SESSION['spider_session_object']);
		}
		$spiderSession->setValue($key,$value);
		$_SESSION['spider_session_object']	= serialize($spiderSession);
	}
	/**
	 * セッション変数を取得します
	 * @param $key
	 */
	function getSession( $key ) {
		$this->optimizeSession();
		$spiderSession	= new spider_HttpSession();
		if( isset($_SESSION) && isset($_SESSION['spider_session_object']) ) {
			$spiderSession	= unserialize($_SESSION['spider_session_object']);
		}
		$value			= null;
		$current_scope	= dirname(SPIDER_PHP_SELF);
		while( strlen($current_scope) > 1 ) {
			$target_key		= $current_scope.'/'.$key;
			if( false !== $spiderSession->getValue($target_key) ) {
				return $spiderSession->getValue($target_key);
			}
			$current_scope	= dirname( $current_scope );
		}
		$target_key	= $this->_getGlobalSessionKey( $key );
		if( false !== $spiderSession->getValue($target_key) ) {
			return $spiderSession->getValue($target_key);
		}
		return null;
	}
	/**
	 * セッション変数が登録されているか確認します
	 * @param $key
	 */
	function existsSession( $key ) {
		$this->optimizeSession();
		$spiderSession	= new spider_HttpSession();
		if( isset($_SESSION) && isset($_SESSION['spider_session_object']) ) {
			$spiderSession	= unserialize($_SESSION['spider_session_object']);
		}
		$value			= null;
		$current_scope	= dirname(SPIDER_PHP_SELF);
		while( strlen($current_scope) > 1 ) {
			$target_key		= $current_scope.'/'.$key;
			if( false !== $spiderSession->getValue($target_key) ) {
				return true;
			}
			$current_scope	= dirname( $current_scope );
		}
		$target_key	= $this->_getGlobalSessionKey( $key );
		if( false !== $spiderSession->getValue($target_key) ) {
			return true;
		}
		return false;
	}
	/**
	 * セッション登録済みの変数を削除します
	 */
	function removeSession( $key, $scope=SPIDER_SESSION_SCOPE_AUTO ) {
		$this->optimizeSession();
		$spiderSession	= new spider_HttpSession();
		if( isset($_SESSION) && isset($_SESSION['spider_session_object']) ) {
			$spiderSession	= unserialize($_SESSION['spider_session_object']);
		}
		if( $scope == SPIDER_SESSION_SCOPE_AUTO ) {
			$scope	= dirname(SPIDER_PHP_SELF);
		}
		if( SPIDER_SESSION_SCOPE_GLOBAL == $scope ) {
			$key	= $this->_getGlobalSessionKey( $key );
		} else {
			$key	= $scope.'/'.$key;
		}
		$spiderSession->removeValue($key);
		$_SESSION['spider_session_object']	= serialize($spiderSession);
	}
	/**
	 * セッション登録済み情報を最適化します
	 */
	function optimizeSession() {
		if( isset( $_SESSION ) && is_array( $_SESSION ) ) {
			$spiderSession	= new spider_HttpSession();
			if( isset($_SESSION) && isset($_SESSION['spider_session_object']) ) {
				$spiderSession	= unserialize($_SESSION['spider_session_object']);
			}
			if( count($spiderSession->classNameHash) > 0 ) {
				$defArray	= array_unique($spiderSession->classNameHash);
				foreach( $defArray as $classDef ) {
					spider_Controller::loadClassDefinition($classDef);
				}
				$spiderSession	= unserialize($_SESSION['spider_session_object']);
			}
			$current_scope	= dirname(SPIDER_PHP_SELF);
			foreach( $_SESSION as $key => $value ) {
				if( strlen($key) > 0 && preg_match('/^spider\\_GLOBAL\\./',$key) == 0
					&& preg_match('/^'.util_CharUtility::escape_regx_str($current_scope).'/',$key) == 0 ) {
					$spiderSession->removeValue($key);
				}
			}
			$_SESSION['spider_session_object']	= serialize($spiderSession);
		}
	}
	/**
	 * セッションIDを作成しなおします
	 */
	function renewSessionId() {
		spider_HttpSession::regenerateId( $this );
	}
	/**
	 * グローバルセッションキー
	 */
	function _getGlobalSessionKey( $key ) {
		return 'spider_GLOBAL.'.$key;
	}
	/**
	 * ログを出力します
	 * @param $message ログメッセージ
	 */
	function writeLog( $message, $log_level=SPIDER_LOG_LEVEL_INFO, $logFileUri=null ) {
		if(isset($GLOBALS['spider.messages'])
		&& is_array($GLOBALS['spider.messages'])
		&& isset($GLOBALS['spider.messages'][$message]) ){
			$message	= $GLOBALS['spider.messages'][$message];
		}
		$system_log_level	= SPIDER_LOG_LEVEL_INFO;
		if( defined('SYSTEM_LOG_LEVEL') && is_numeric(SYSTEM_LOG_LEVEL)
			&& preg_match('/^[0-4]$/',SYSTEM_LOG_LEVEL) > 0 ) {
			$system_log_level	= SYSTEM_LOG_LEVEL;
		}
		// システム設定のログレベルより大きいレベルのログは出力しない
		if( $log_level > $system_log_level ) {
			return true;
		}

		$message	= str_replace( "\r\n", "\n", $message );
		$message	= str_replace( "\r", "\n", $message );
		$message	= str_replace( "\n", " ", $message );

		$callstack		= debug_backtrace();
		$callerFile	= str_replace(DIRECTORY_SEPARATOR,'/',$callstack[0]['file']);
		$caller_line	= $callstack[0]['line'];
		unset($callstack);

		$log_message	= date('Y-m-d H:i:s')
			. "\t".$message
			. "\t"."[".$callerFile."] "
			. "\t"."[".$caller_line."] "
			. "\t" . $_SERVER['REMOTE_ADDR']
			. "\t" . (isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : '' )
			. "\t" . $_SERVER['HTTP_USER_AGENT'];
		
		$log_type	= SPIDER_LOG_TYPE_FILE;
		if( defined('SYSTEM_LOG_TYPE') && is_numeric(SYSTEM_LOG_TYPE) ) {
			if( SPIDER_LOG_TYPE_SYSLOG == SYSTEM_LOG_TYPE ) {
				$log_type	= SPIDER_LOG_TYPE_SYSLOG;
			}
		}
		if( SPIDER_LOG_TYPE_SYSLOG == $log_type ) {
			// syslogdに出力する場合
			openlog('spiderLog', LOG_PID | LOG_NDELAY, LOG_SYSLOG);
			$log_level	= LOG_INFO;
			switch ($log_level) {
				case SPIDER_LOG_LEVEL_DEBUG:
				$log_level	= LOG_DEBUG;
				break;
				case SPIDER_LOG_LEVEL_INFO:
				$log_level	= LOG_INFO;
				break;
				case SPIDER_LOG_LEVEL_NOTICE:
				$log_level	= LOG_NOTICE;
				break;
				case SPIDER_LOG_LEVEL_WARNING:
				$log_level	= LOG_WARNING;
				break;
				case SPIDER_LOG_LEVEL_ERROR:
				$log_level	= LOG_ERR;
				break;
				case SPIDER_LOG_LEVEL_FATAL:
				$log_level	= LOG_CRIT;
				break;
				default:
				break;
			}
			syslog($log_level, $log_message);
			closelog();
		} else {
			// ファイルに出力する
			$logFilePath	= null;
			// ログファイル名の決定
			if( is_null($logFileUri) || strlen($logFileUri) == 0 ) {
				// ファイルURIが指定されていない場合は自動生成
				$logFilePath	= DIR_PATH_LOG.'/spider';
				if( !file_exists( $logFilePath ) ) {
					if( @mkdir( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER ) ) {
						@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER );
					}
				}
				$logFilePath	= $logFilePath.'/'.date('Y');
				if( !file_exists( $logFilePath ) ) {
					if( @mkdir( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER ) ) {
						@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER );
					}
				}
				$logFilePath	= $logFilePath.'/'.date('m');
				if( !file_exists( $logFilePath ) ) {
					if( @mkdir( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER ) ) {
						@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER );
					}
				}
				$logFilePath	= $logFilePath.'/'.'system_'.date('d').'.log';
				if( !file_exists( $logFilePath ) ) {
					if(@touch( $logFilePath, SPIDER_PERMITTION_DATA_FILE )){
						@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FILE );
					}
				}
			} else {
				// ログファイルURIが指定されている場合は指定ログファイルへ出力
				$logFilePath	= DIR_PATH_LOG;
				$logFileUri	= str_replace('/','/',$logFileUri);
				$dirNames	= explode('/',$logFileUri);
				$fileName	= array_pop($dirNames);
				foreach( $dirNames as $dirName ) {
					if( strlen(trim($dirName)) > 0 ) {
						$logFilePath	.= '/'.$dirName;
						if( !file_exists( $logFilePath ) ) {
							if( @mkdir( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER ) ) {
								@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FOLDER );
							}
						}
					}
				}
				$logFilePath	.= '/'.$fileName;
				if( !file_exists( $logFilePath ) ) {
					if(@touch( $logFilePath, SPIDER_PERMITTION_DATA_FILE )){
						@chmod( $logFilePath, SPIDER_PERMITTION_DATA_FILE );
					}
				}
			}
			// ログを出力
			error_log($log_message."\n" , 3, $logFilePath );
		}
		return true;
	}
	/**
	 * Fatalレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function fatal($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_FATAL, $logFileUri );
	}
	/**
	 * Errorレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function error($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_ERROR, $logFileUri );
	}
	/**
	 * Warningレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function warn($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_WARNING, $logFileUri );
	}
	/**
	 * Noticeレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function notice($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_NOTICE, $logFileUri );
	}
	/**
	 * Infoレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function info($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_INFO, $logFileUri );
	}
	/**
	 * Debugレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function debug($message, $logFileUri=null){
		$this->writeLog( $message, SPIDER_LOG_LEVEL_DEBUG, $logFileUri );
	}
	/**
	 * システム設定された値でメール送信オブジェクトを作成して取得します。
	 * @return mixed util_Mailクラスの実装オブジェクト・インスタンスを作成できなかった場合はfalse
	 */
	function getSystemMailer() {
		if( defined('SYSTEM_MAIL_SEND_METHOD') ) {
			require_once( dirname(dirname(__FILE__))
				. DIRECTORY_SEPARATOR.'util'
				. DIRECTORY_SEPARATOR.'Mail.class.php');
			// 送信オプション
			$params		= array();
			// 送信方法の決定
			$class_name	= 'PHP';
			if( preg_match('/^[sS][mM][tT][pP]$/',SYSTEM_MAIL_SEND_METHOD) > 0 ) {
				$class_name	= 'SMTP';
			} else if( preg_match('/^[sS][eE][nN][dD][mM][aA][iI][lL]$/',SYSTEM_MAIL_SEND_METHOD) > 0 ) {
				$class_name	= 'SendMail';
			}
			$params		= $GLOBALS['SYSTEM_MAIL_SEND_METHOD_OPTIONS'];
			// 送信オブジェクト作成
			$send_object	= util_Mail::get_instance( $class_name, $params );
			if( $send_object === false ) {
				return false;
			} else {
				return $send_object;
			}
		} else {
			return false;
		}
	}
	/**
	 * メールを送信します。
	 * 宛先、配信元、返信先、戻り先のメールアドレスを指定しなかった場合、下記に定義された定数もしはグローバル配列を探して
	 * 定義済みの変数を元に送信を実行します。定義がなかった場合はfalseを返します。
	 * 
	 * 宛て先メールアドレス $GLOBALS['SYSTEM_MAIL_REPORT_TO_ADDRESSES'] 配列で定義してください。
	 * 配信元メールアドレス SYSTEM_MAIL_FROM_ADDRESS 文字列で定義してください。
	 * 配信先メールアドレス SYSTEM_MAIL_REPLY_ADDRESS 指定がない場合はSYSTEM_MAIL_FROM_ADDRESSを利用します。
	 * 戻り先メールアドレス SYSTEM_MAIL_RETURN_ADDRESS 指定がない場合はSYSTEM_MAIL_FROM_ADDRESSを利用します。
	 * 
	 * @param $subject メール件名
	 * @param $message メール本文
	 * @param $mailto 宛て先メールアドレス
	 * @param $from 配信元メールアドレス指定
	 * @param $reply 返信先メールアドレス指定
	 * @param $return エラー戻り先メールアドレス指定
	 * @return boolean
	 */
	function mailTo($subject,$message,$mailto=null,$from=null,$reply=null,$return=null) {
		$mailto_addresses	= array();
		if( isset($GLOBALS['SYSTEM_MAIL_REPORT_TO_ADDRESSES'])
		&& is_array($GLOBALS['SYSTEM_MAIL_REPORT_TO_ADDRESSES']) ) {
			$mailto_addresses	= $GLOBALS['SYSTEM_MAIL_REPORT_TO_ADDRESSES'];
		}
		$mailto	= trim($mailto);
		if(!is_null($mailto) && strlen($mailto) > 0 ) {
			array_push($mailto_addresses, $mailto);
		}
		if(count($mailto_addresses)==0){
			$this->warn('HttpRequest->MailTo: derivery mail address is required!: '.$subject.':'.$message);
			return false;
		}
		if(defined('SYSTEM_MAIL_SUBJECT_PREFIX')){
			$subject	= SYSTEM_MAIL_SUBJECT_PREFIX.$subject;
		}
		if( is_null($from) || strlen($from) == 0 ) {
			if(defined('SYSTEM_MAIL_FROM_ADDRESS') && strlen(SYSTEM_MAIL_FROM_ADDRESS)>0 ) {
				if(defined('SYSTEM_MAIL_FROM_NAME') && strlen(SYSTEM_MAIL_FROM_NAME)>0 ) {
					$from	= SYSTEM_MAIL_FROM_NAME.' <'.SYSTEM_MAIL_FROM_ADDRESS.'>';
				} else {
					$from	= SYSTEM_MAIL_FROM_ADDRESS;
				}
			} else {
				$this->warn('HttpRequest->MailTo: from address is required!: '.$subject.':'.$message);
				return false;
			}
		}
		if( is_null($reply) || strlen($reply) == 0 ) {
			if(defined('SYSTEM_MAIL_REPLY_ADDRESS') && strlen(SYSTEM_MAIL_REPLY_ADDRESS)>0 ) {
				$reply	= SYSTEM_MAIL_REPLY_ADDRESS;
			} else {
				$reply	= $from;
			}
		}
		if( is_null($return) || strlen($return) == 0 ) {
			if(defined('SYSTEM_MAIL_RETURN_ADDRESS') && strlen(SYSTEM_MAIL_RETURN_ADDRESS)>0 ) {
				$return	= SYSTEM_MAIL_RETURN_ADDRESS;
			} else {
				$return	= $from;
			}
		}
		
		if( $send_object = $this->getSystemMailer() ) {
			foreach( $mailto_addresses as $address ) {
				$result	= $send_object->send( $address, $subject, $message, $from, $reply, $return );
				if( $result ) {
					$this->info( 'HttpRequest->MailTo: '.$address.':'.$subject.':'.$message );
					return true;
				} else {
					$this->warn('HttpRequest->MailTo: delivey error: '.$subject.':'.$message);
					return false;
				}
			}
		} else {
			$this->warning( 'HttpRequest->MailTo: can\'t create mailer instance!: '.$mailto.':'.$subject.':'.$message );
			return false;
		}
	}
	/**
	 * 対象URIがSSL強制URI内か確認します
	 * @param $uri 対象URI
	 * @return boolean 強制SSL対象ならtrue
	 */
	function isRequireSSL( $uri ) {
		foreach($GLOBALS['SPIDER_ACCESS_SSL_URI_ARRAY'] as $sslFolderUri) {
			$ps	= strpos($uri,$sslFolderUri);
			if( $ps !== false && $ps == 0 ) {
				return true;
			}
		}
		return false;
	}
	/**
	 * リモートホスト名を取得します。
	 * ApacheのHostnameLookupが無効な場合でも取得できます。
	 * @return string リモートホスト名文字列
	 * @access public
	 */
	function getRemoteHostString() {
		if( isset($_SERVER['HTTP_X_FORWARDED_FOR']) && strlen($_SERVER['HTTP_X_FORWARDED_FOR']) > 0 ) {
			if( function_exists('gethostbyaddr') ) {
				return gethostbyaddr($_SERVER['HTTP_X_FORWARDED_FOR']);
			} else {
				return $_SERVER['HTTP_X_FORWARDED_FOR'];
			}
		} else if( isset($_SERVER['REMOTE_HOST']) && strlen($_SERVER['REMOTE_HOST']) > 0 ) {
			return $_SERVER['REMOTE_HOST'];
		} else if( isset($_SERVER['REMOTE_ADDR']) && strlen($_SERVER['REMOTE_ADDR']) > 0 ) {
			if( function_exists('gethostbyaddr') ) {
				return gethostbyaddr($_SERVER['REMOTE_ADDR']);
			} else {
				return $_SERVER['REMOTE_ADDR'];
			}
		}
		return null;
	}
	/**
	 * ユーザーエージェント名文字列を取得します
	 * @return string アクセス元ユーザーエージェント名
	 */
	function getUserAgentString() {
		$userAgent		= '';
		if( isset($_SERVER['HTTP_USER_AGENT']) ) {
			$userAgent	= $_SERVER['HTTP_USER_AGENT'];
			$agentClass	= $this->agentClass;
			if( strlen($agentClass) > 0
				&& isset($GLOBALS['SPIDER_USER_AGENT_SESSION_SAVE_DELETE_REGX'][$agentClass])
				&& strlen($GLOBALS['SPIDER_USER_AGENT_SESSION_SAVE_DELETE_REGX'][$agentClass]) > 0 ) {
				$userAgent	= preg_replace($GLOBALS['SPIDER_USER_AGENT_SESSION_SAVE_DELETE_REGX'][$agentClass],'',$userAgent);
			}
		}
		return $userAgent;
	}
	/**
	 * for debug echo time
	 */
	function debugExecTime($caption='') {
		if( in_array($_SERVER['REMOTE_ADDR'],$GLOBALS['SPIDER_DEBUG_COMMAND_PERMIT_ADDRESSES']) ){
			$time	= time() - $this->lastTime;
			echo $caption.'='.$time.' : ';
			$this->lastTime	= time();
			if( function_exists('memory_get_usage') ) {
				$usedMemory	= memory_get_usage();
				$usedMemoryString	= $usedMemory.'Byte';
				if( $usedMemory >= 1024*1024 ) {
					$usedMemoryString	= round($usedMemory/(1024*1024),1).'MB';
				} else if( $usedMemory >= 1024*1024 ) {
					$usedMemoryString	= round($usedMemory/1024,1).'KB';
				}
				echo 'Used Memory='.$usedMemoryString;
			}
			echo "<br />\n";
		}
	}
}
?>