/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.filter;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

import java.io.File;												// 5.7.3.2 (2014/02/28) Tomcat8 対応
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.CharacterCodingException;					// 6.3.1.0 (2015/06/28)

import org.opengion.fukurou.system.OgRuntimeException ;				// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.OgCharacterException ;			// 6.5.0.1 (2016/10/21)
import org.opengion.fukurou.system.DateSet;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.Closer;
import org.opengion.fukurou.util.FileUtil;							// 6.2.0.0 (2015/02/27)
import org.opengion.fukurou.util.StringUtil;						// 6.3.8.0 (2015/09/11)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.CR;				// 6.3.1.0 (2015/06/28)
import org.opengion.hayabusa.common.HybsSystem;						// 6.2.4.1 (2015/05/22)

/**
 * AccessStopFilter は、Filter インターフェースを継承した アクセス制御クラスです。
 * web.xml で filter 設定することにより、Webアプリケーションへのアクセスを制御できます。
 * また、SYSTEM ユーザーは、このフィルターを常に通過します。
 *
 * フィルターに対してweb.xml でパラメータを設定します。
 *   ・startTime:停止開始時刻 (初期値:230000)
 *   ・stopTime :停止終了時刻 (初期値:070000)
 *   ・filename :停止時メッセージ表示ファイル名 (初期値:jsp/custom/stopFile.html)
 *   ・passUsers:停止中でもアクセス可能なユーザーID (初期値:SYSTEM,ADMIN)
 *   ・addUsers :停止中でもアクセス可能な追加ユーザーID (初期値:null)
 *
 * 【WEB-INF/web.xml】
 *     &lt;filter&gt;
 *         &lt;filter-name&gt;AccessStopFilter&lt;/filter-name&gt;
 *         &lt;filter-class&gt;org.opengion.hayabusa.filter.AccessStopFilter&lt;/filter-class&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;startTime&lt;/param-name&gt;
 *             &lt;param-value&gt;070000&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;stopTime&lt;/param-name&gt;
 *             &lt;param-value&gt;070000&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;filename&lt;/param-name&gt;
 *             &lt;param-value&gt;jsp/custom/stopFile.html&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *     &lt;/filter&gt;
 *
 *     &lt;filter-mapping&gt;
 *         &lt;filter-name&gt;AccessStopFilter&lt;/filter-name&gt;
 *         &lt;url-pattern&gt;/jsp/*&lt;/url-pattern&gt;
 *     &lt;/filter-mapping&gt;
 *
 * @og.group フィルター処理
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class AccessStopFilter implements Filter {

	private static boolean useFilter = true ;		// 6.3.8.3 (2015/10/03)

	private String startTime	= "230000";		// 停止開始時刻
	private String stopTime		= "070000";		// 停止終了時刻
	private String filename		= "jsp/custom/stopFile.html";	// 6.3.8.0 (2015/09/11) 停止時メッセージ表示ファイル名
	private String passUsers	= "SYSTEM,admin";				// 6.3.8.0 (2015/09/11) 停止中でもアクセス可能なユーザーID
	private int    startStop	;

	private static final String  USERID_HEADER = HybsSystem.sys( "USERID_HEADER_NAME" ); // 5.10.18.0 (2019/11/29)

	/**
	 * フィルター処理本体のメソッドです。
	 *
	 * @og.rev 3.1.3.0 (2003/04/10) UTF-8 決め打ちで、stopFile.html を返送する。
	 * @og.rev 3.1.8.0 (2003/05/16) 文字エンコードが、UTF-8 になっていないのを修正。
	 * @og.rev 6.2.0.0 (2015/02/27) new BufferedReader … を、FileUtil.getBufferedReader … に変更。
	 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。
	 * @og.rev 6.3.8.3 (2015/10/03) フィルターの停止処理。
	 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。
	 *
	 * @param	request		ServletRequestオブジェクト
	 * @param	response	ServletResponseオブジェクト
	 * @param	chain		FilterChainオブジェクト
	 * @throws IOException 入出力エラーが発生した場合、throw されます。
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 */
	public void doFilter( final ServletRequest request,
							final ServletResponse response,
							final FilterChain chain) throws IOException, ServletException {

		if( useFilter && isStop( request ) ) {				// 6.3.8.3 (2015/10/03) フィルターの停止処理
			BufferedReader in = null ;
			try {
				response.setContentType( "text/html; charset=UTF-8" );
				final PrintWriter out = response.getWriter();
				in = FileUtil.getBufferedReader( new File( filename ), "UTF-8" );		// 6.2.0.0 (2015/02/27)
				String str ;
				while( (str = in.readLine()) != null ) {
					out.println( str );
				}
				out.flush();
			}
			catch( final UnsupportedEncodingException ex ) {
				final String errMsg = "指定されたエンコーディングがサポートされていません。[UTF-8]" ;
				throw new OgRuntimeException( errMsg,ex );
			}
			// 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。
			catch( final CharacterCodingException ex ) {
				final String errMsg = "文字のエンコード・エラーが発生しました。" + CR
									+	"  ファイルのエンコードが指定のエンコードと異なります。" + CR
									+	" [" + filename + "] , Encode=[UTF-8]" ;
				throw new OgCharacterException( errMsg,ex );	// 6.5.0.1 (2016/10/21)
			}
			catch( final IOException ex ) {
				final String errMsg = "ストリームがオープン出来ませんでした。[" + filename + "]" ;
				throw new OgRuntimeException( errMsg,ex );
			}
			finally {
				Closer.ioClose( in );
			}
			return;
		}

		chain.doFilter(request, response);
	}

	/**
	 * フィルターの初期処理メソッドです。
	 *
	 * フィルターに対してweb.xml で初期パラメータを設定します。
	 *   ・startTime:停止開始時刻 (初期値:230000)
	 *   ・stopTime :停止終了時刻 (初期値:070000)
	 *   ・filename :停止時メッセージ表示ファイル名 (初期値:jsp/custom/stopFile.html)
	 *   ・passUsers:停止中でもアクセス可能なユーザーID (初期値:SYSTEM,admin)
	 *   ・addUsers :停止中でもアクセス可能な追加ユーザーID (初期値:null)
	 *
	 * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
	 * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、HybsSystem経由で、取得する。
	 * @og.rev 6.3.8.0 (2015/09/11) パラメータの初期値設定と、passUsers、addUsers 属性追加
	 *
	 * @param	filterConfig	FilterConfigオブジェクト
	 */
	public void init( final FilterConfig filterConfig ) {
		// 6.3.8.0 (2015/09/11) パラメータの初期値設定と、passUsers、addUsers 属性追加
		startTime = StringUtil.nval( filterConfig.getInitParameter( "startTime" ) , startTime );
		stopTime  = StringUtil.nval( filterConfig.getInitParameter( "stopTime"  ) , stopTime  );
		filename  = HybsSystem.getRealPath()
						+ StringUtil.nval( filterConfig.getInitParameter( "filename" ) , filename );
		passUsers = StringUtil.nval( filterConfig.getInitParameter( "passUsers"  ) , passUsers  )	// 6.3.8.0 (2015/09/11) 新規
						+ ',' + filterConfig.getInitParameter( "addUsers" ) ;

		if( startTime == null || stopTime == null ) {
			startStop = 0;
		}
		else {
			startStop = startTime.compareTo( stopTime );
		}
	}

	/**
	 * フィルターの終了処理メソッドです。
	 *
	 */
	public void destroy() {
		// ここでは処理を行いません。
	}

	/**
	 * フィルターの内部状態をチェックするメソッドです。
	 * 内部のフラグをもとに、停止/許可を求めます。
	 *
	 * @og.rev 3.1.8.0 (2003/05/16) 開始時刻と終了時刻を同一にしていると、画面からの制御が効かないバグを修正。
	 * @og.rev 5.5.3.2 (2012/06/08) 通過させるユーザーに、admin を追加します。
	 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
	 * @og.rev 6.3.8.0 (2015/09/11) パラメータの初期値設定と、passUsers、addUsers 属性追加
	 * @og.rev 6.3.8.3 (2015/10/03) フィルターの停止処理。判定をdoFilterメソッドに移動。
	 * @og.rev 5.10.18.0 (2019/11/29) ヘッダ認証
	 *
	 * @param request ServletRequestオブジェクト
	 *
	 * @return	(true:停止  false:実行許可)
	 */
	private boolean isStop( final ServletRequest request ) {
//		final String userID = ((HttpServletRequest)request).getRemoteUser() ;
		String userID = ((HttpServletRequest)request).getRemoteUser() ;

		// 5.10.18.0 (2019/11/29) ヘッダ認証
		if( USERID_HEADER != null && USERID_HEADER.length() > 0 && ( userID == null || userID.length() == 0) ) {
			userID = ((HttpServletRequest)request).getHeader( USERID_HEADER );
		}

		// 5.5.3.2 (2012/06/08) 通過させるユーザーに、admin を追加
		// 6.3.8.0 (2015/09/11) パラメータの初期値設定と、passUsers、addUsers 属性追加
		if( passUsers.contains( userID ) ) { return false; }

		// 4.0.0 (2005/01/31)
		final String time = DateSet.getDate( "HHmmss" );		// 5.5.7.2 (2012/10/09) HybsDateUtil を利用

		return	startStop < 0 && startTime.compareTo( time ) < 0 && time.compareTo( stopTime ) < 0 ||
				startStop > 0 && ( startTime.compareTo( time ) < 0 || time.compareTo( stopTime ) < 0 ) ;

 //		boolean rtnFlag = stopFilter;
 //		if( startStop < 0 ) {
 //			if( startTime.compareTo( time ) < 0 &&
 //				time.compareTo( stopTime )  < 0 ) {
 //					rtnFlag = true;
 //			}
 //		}
 //		else if( startStop > 0 ) {
 //			if( startTime.compareTo( time ) < 0 ||
 //				time.compareTo( stopTime )  < 0 ) {
 //					rtnFlag = true;
 //				}
 //		}
 //		return rtnFlag;
	}

	/**
	 * フィルターの実行/停止を設定するメソッドです。
	 *
	 * 初期値は、true:実行 です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) synchronized の廃止
	 * @og.rev 6.3.8.3 (2015/10/03) フィルターの停止処理。メソッド名変更、引数の意味反転。
	 *
	 * @param flag (true:実行  false:停止)
	 */
	public static void setUseFilter( final boolean flag ) {
		useFilter = flag;
	}

	/**
	 * フィルターの内部状態(強制停止/解除)を取得するメソッドです。
	 * これは、現在、アクセス制限がどうなっているかという状態ではなく、
	 * 強制停止されているかどうかの確認メソッドです。
	 *
	 * @og.rev 4.0.0.0 (2007/11/29) getStopFilter() ⇒ isStopFilter() に変更
	 * @og.rev 6.3.8.3 (2015/10/03) フィルターの停止処理。メソッド名変更、戻り値の意味反転。
	 *
	 * @return	(true:実行  false:停止)
	 */
	public static boolean isUseFilter() {
		return useFilter;
	}

	/**
	 * 内部状態を文字列で返します。
	 *
	 * @return	このクラスの文字列表示
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder( BUFFER_MIDDLE )
			.append( "AccessStopFilter" )
			.append( '[' ).append( startTime ).append( "],")		// 6.0.2.5 (2014/10/31) char を append する。
			.append( '[' ).append( stopTime  ).append( "],")
			.append( '[' ).append( filename  ).append( "],");
		return sb.toString();
	}
}
