<?php
// GIJOE's Ticket Class (based on Marijuana's Oreteki XOOPS)
// nobunobu's suggestions are applied

if( ! class_exists( 'XoopsGTicket' ) ) {

class XoopsGTicket {

	var $_errors = array() ;
	var $_latest_token = '' ;
	var $messages = array() ;

	function XoopsGTicket()
	{
		global $xoopsConfig ;

		// language file
		if( defined( 'XOOPS_ROOT_PATH' ) && ! empty( $xoopsConfig['language'] ) && ! strstr( $xoopsConfig['language'] , '/' ) ) {
			if( file_exists( dirname( dirname( __FILE__ ) ) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml' ) ) {
				include dirname( dirname( __FILE__ ) ) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml' ;
			}
		}

		// default messages
		if( empty( $this->messages ) ) $this->messages = array(
			'err_general' => 'GTicket Error' ,
			'err_nostubs' => 'No stubs found' ,
			'err_noticket' => 'No ticket found' ,
			'err_nopair' => 'No valid ticket-stub pair found' ,
			'err_timeout' => 'Time out' ,
			'err_areaorref' => 'Invalid area or referer' ,
			'fmt_prompt4repost' => 'error(s) found:<br /><span style="background-color:red;font-weight:bold;color:white;">%s</span><br />Confirm it.<br />And do you want to post again?' ,
			'btn_repost' => 'repost' ,
		) ;
	}

	// render form as plain html
	function getTicketHtml( $salt = '' , $timeout = 1800 , $area = '' )
	{
		return '<input type="hidden" name="XOOPS_G_TICKET" value="'.$this->issue( $salt , $timeout , $area ).'" />' ;
	}

	// returns an object of XoopsFormHidden including theh ticket
	function getTicketXoopsForm( $salt = '' , $timeout = 1800 , $area = '' )
	{
		return new XoopsFormHidden( 'XOOPS_G_TICKET' , $this->issue( $salt , $timeout , $area ) ) ;
	}

	// add a ticket as Hidden Element into XoopsForm
	function addTicketXoopsFormElement( &$form , $salt = '' , $timeout = 1800 , $area = '' )
	{
		$form->addElement( new XoopsFormHidden( 'XOOPS_G_TICKET' , $this->issue( $salt , $timeout , $area ) ) ) ;
	}

	// returns an array for xoops_confirm() ;
	function getTicketArray( $salt = '' , $timeout = 1800 , $area = '' )
	{
		return array( 'XOOPS_G_TICKET' => $this->issue( $salt , $timeout , $area ) ) ;
	}

	// return GET parameter string.
	function getTicketParamString( $salt = '' , $noamp = false , $timeout=1800 , $area = '' )
	{
	    return ( $noamp ? '' : '&amp;' ) . 'XOOPS_G_TICKET=' . $this->issue( $salt, $timeout , $area ) ;
	}

	// issue a ticket
	function issue( $salt = '' , $timeout = 1800 , $area = '' )
	{
		global $xoopsModule ;
	
		// create a token
		list( $usec , $sec ) = explode( " " , microtime() ) ;
		$appendix_salt = empty( $_SERVER['PATH'] ) ? XOOPS_DB_NAME : $_SERVER['PATH'] ;
		$token = crypt( $salt . $usec . $appendix_salt . $sec ) ;
		$this->_latest_token = $token ;

		if( empty( $_SESSION['XOOPS_G_STUBS'] ) ) $_SESSION['XOOPS_G_STUBS'] = array() ;

		// limit max stubs 10
		if( sizeof( $_SESSION['XOOPS_G_STUBS'] ) > 10 ) {
			$_SESSION['XOOPS_G_STUBS'] = array_slice( $_SESSION['XOOPS_G_STUBS'] , -10 ) ;
		}

		// record referer if browser send it
		$referer = empty( $_SERVER['HTTP_REFERER'] ) ? '' : $_SERVER['REQUEST_URI'] ;

		// area as module's dirname
		if( ! $area && is_object( @$xoopsModule ) ) {
			$area = $xoopsModule->getVar('dirname') ;
		}

		// store stub
		$_SESSION['XOOPS_G_STUBS'][] = array(
			'expire' => time() + $timeout ,
			'referer' => $referer ,
			'area' => $area ,
			'token' => $token
		) ;

		// paid md5ed token as a ticket
		return md5( $token . XOOPS_DB_PREFIX ) ;
	}

	// check a ticket
	function check( $post = true , $area = '' , $allow_repost = true )
	{
		global $xoopsModule ;

		$this->_errors = array() ;

		// CHECK: stubs are not stored in session
		if( ! is_array(@$_SESSION['XOOPS_G_STUBS'])) {
			$this->_errors[] = $this->messages['err_nostubs'] ;
			$_SESSION['XOOPS_G_STUBS'] = array() ;
		}

		// get key&val of the ticket from a user's query
		$ticket = $post ? @$_POST['XOOPS_G_TICKET'] : @$_GET['XOOPS_G_TICKET'] ;

		// CHECK: no tickets found
		if( empty( $ticket ) ) {
			$this->_errors[] = $this->messages['err_noticket'] ;
		}

		// gargage collection & find a right stub
		$stubs_tmp = $_SESSION['XOOPS_G_STUBS'] ;
		$_SESSION['XOOPS_G_STUBS'] = array() ;
		foreach( $stubs_tmp as $stub ) {
			// default lifetime 30min
			if( $stub['expire'] >= time() ) {
				if( md5( $stub['token'] . XOOPS_DB_PREFIX ) === $ticket ) {
					$found_stub = $stub ;
				} else {
					// store the other valid stubs into session
					$_SESSION['XOOPS_G_STUBS'][] = $stub ;
				}
			} else {
				if( md5( $stub['token'] . XOOPS_DB_PREFIX ) === $ticket ) {
					// not CSRF but Time-Out
					$timeout_flag = true ;
				}
			}
		}

		// CHECK: the right stub found or not
		if( empty( $found_stub ) ) {
			if( empty( $timeout_flag ) ) $this->_errors[] = $this->messages['err_nopair'] ;
			else $this->_errors[] = $this->messages['err_timeout'] ;
		} else {

			// set area if necessary
			// area as module's dirname
			if( ! $area && is_object( @$xoopsModule ) ) {
				$area = $xoopsModule->getVar('dirname') ;
			}

			// check area or referer
			if( @$found_stub['area'] == $area ) $area_check = true ;
			if( ! empty( $found_stub['referer'] ) && strstr( @$_SERVER['HTTP_REFERER'] , $found_stub['referer'] ) ) $referer_check = true ;

			if( empty( $area_check ) && empty( $referer_check ) ) { // loose
				$this->_errors[] = $this->messages['err_areaorref'] ;
			}
		}

		if( ! empty( $this->_errors ) ) {
			if( $allow_repost ) {
				// repost form
				$this->draw_repost_form( $area ) ;
				exit ;
			} else {
				// failed
				$this->clear() ;
				return false ;
			}
		} else {
			// all green
			return true;
		}
	}

	// draw form for repost
	function draw_repost_form( $area = '' )
	{
		// Notify which file is broken
		if( headers_sent() ) {
			restore_error_handler() ;
			set_error_handler( 'GTicket_ErrorHandler4FindOutput' ) ;
			header( 'Dummy: for warning' ) ;
			restore_error_handler() ;
			exit ;
		}
	
		error_reporting( 0 ) ;
		while( ob_get_level() ) ob_end_clean() ;

		$table = '<table>' ;
		$form = '<form action="?'.htmlspecialchars(@$_SERVER['QUERY_STRING'],ENT_QUOTES).'" method="post" >' ;
		foreach( $_POST as $key => $val ) {
			if( $key == 'XOOPS_G_TICKET' ) continue ;
			if( get_magic_quotes_gpc() ) {
				$key = stripslashes( $key ) ;
			}
			if( is_array( $val ) ) {
				list( $tmp_table , $tmp_form ) = $this->extract_post_recursive( htmlspecialchars($key,ENT_QUOTES) , $val ) ;
				$table .= $tmp_table ;
				$form .= $tmp_form ;
			} else {
				if( get_magic_quotes_gpc() ) {
					$val = stripslashes( $val ) ;
				}
				$table .= '<tr><th>'.htmlspecialchars($key,ENT_QUOTES).'</th><td>'.htmlspecialchars($val,ENT_QUOTES).'</td></tr>'."\n" ;
				$form .= '<input type="hidden" name="'.htmlspecialchars($key,ENT_QUOTES).'" value="'.htmlspecialchars($val,ENT_QUOTES).'" />'."\n" ;
			}
		}
		$table .= '</table>' ;
		$form .= $this->getTicketHtml(__LINE__,300,$area).'<input type="submit" value="'.$this->messages['btn_repost'].'" /></form>' ;

		echo '<html><head><title>'.$this->messages['err_general'].'</title><style>table,td,th {border:solid black 1px; border-collapse:collapse;}</style></head><body>' . sprintf( $this->messages['fmt_prompt4repost'] , $this->getErrors() ) . $table . $form . '</body></html>' ;
	}

	function extract_post_recursive( $key_name , $tmp_array ) {
		$table = '' ;
		$form = '' ;
		foreach( $tmp_array as $key => $val ) {
			if( get_magic_quotes_gpc() ) {
				$key = stripslashes( $key ) ;
			}
			if( is_array( $val ) ) {
				list( $tmp_table , $tmp_form ) = $this->extract_post_recursive( $key_name.'['.htmlspecialchars($key,ENT_QUOTES).']' , $val ) ;
				$table .= $tmp_table ;
				$form .= $tmp_form ;
			} else {
				if( get_magic_quotes_gpc() ) {
					$val = stripslashes( $val ) ;
				}
				$table .= '<tr><th>'.$key_name.'['.htmlspecialchars($key,ENT_QUOTES).']</th><td>'.htmlspecialchars($val,ENT_QUOTES).'</td></tr>'."\n" ;
				$form .= '<input type="hidden" name="'.$key_name.'['.htmlspecialchars($key,ENT_QUOTES).']" value="'.htmlspecialchars($val,ENT_QUOTES).'" />'."\n" ;
			}
		}
		return array( $table , $form ) ;
	}


	// clear all stubs
	function clear()
	{
		$_SESSION['XOOPS_G_STUBS'] = array() ;
	}


	// Ticket Using
	function using()
	{
		if( ! empty( $_SESSION['XOOPS_G_STUBS'] ) ) {
			return true;
		} else {
			return false;
		}
	}


	// return errors
	function getErrors( $ashtml = true )
	{
		if( $ashtml ) {
			$ret = '' ;
			foreach( $this->_errors as $msg ) {
				$ret .= "$msg<br />\n" ;
			}
		} else {
			$ret = $this->_errors ;
		}
		return $ret ;
	}

// end of class
}

function GTicket_ErrorHandler4FindOutput($errNo, $errStr, $errFile, $errLine)
{
	if( preg_match( '?'.preg_quote(XOOPS_ROOT_PATH).'([^:]+)\:(\d+)?' , $errStr , $regs ) ) {
		echo "Irregular output! check the file ".htmlspecialchars($regs[1])." line ".htmlspecialchars($regs[2]) ;
	} else {
		echo "Irregular output! check language files etc." ;
	}
	return ;
}

// create a instance in global scope
$GLOBALS['xoopsGTicket'] = new XoopsGTicket() ;

}

if( ! function_exists( 'admin_refcheck' ) ) {

//Admin Referer Check By Marijuana(Rev.011)
function admin_refcheck($chkref = "") {
	if( empty( $_SERVER['HTTP_REFERER'] ) ) {
		return true ;
	} else {
		$ref = $_SERVER['HTTP_REFERER'];
	}
	$cr = XOOPS_URL;
	if ( $chkref != "" ) { $cr .= $chkref; }
	if ( strpos($ref, $cr) !== 0 ) { return false; }
	return true;
}

}


?>