/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	OfflineLo　リソースダウンロードクラス
//			変更履歴
//				2004.01.08	新規作成	鈴木　敏子
//
package jp.co.ntt.lms.lo.Offline;

// XMS2 API
import jp.co.ntt.lms.lo.LoTopClient;
import jp.co.ntt.lms.lo.LOException;
import jp.co.ntt.lms.lo.Offline.OfflineLoEnv;
import jp.co.ntt.lms.lo.Offline.OfflineLoData;
import jp.co.ntt.lms.lo.LoComData;
import jp.co.ntt.lms.xmo.Environment;
import jp.co.ntt.lms.xmo.MessageData;
import jp.co.ntt.lms.xmo.util.DebugLog;
import jp.co.ntt.lms.xmo.XMOPropertyDef;
import jp.co.ntt.lms.xmo.Base.XMSBaseClient;
import jp.co.ntt.lms.xmo.Base.XMSBaseTable;
import jp.co.ntt.lms.xmo.Lo.LoData;
import jp.co.ntt.lms.xmo.LoRelation.LoRelationClient;
import jp.co.ntt.lms.xmo.Lo.LoClient;
import jp.co.ntt.lms.xmo.util.FileControl;

// JAVA API
import java.io.File;
import java.util.*;
import java.io.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.RequestDispatcher;

/**
 * OfflineLo　リソースダウンロードクラス<BR>
 * ログインチェック、権限チェックを行い、権限があるユーザのみOffline教材をダウンロードする
 */
public class OfflineDownloadServlet extends HttpServlet
	implements OfflineLoPropertyDef
{
	/** バッファサイズ */
	private static final int BUF_SIZE = 8 * 1024;


	/** パラメータエラー */
	private static final String PARAM_ERR = "パラメータが不正です。";
	/** 処理開始メッセージ */
	private static final String START_LOG = "処理を開始します。";
	/** 処理終了メッセージ */
	private static final String END_LOG = "処理が終了しました。";
	/** ログインチェックエラー */
	private static final String NO_LOGIN = "ログインしていません。";
	/** LOが存在しないエラー */
	private static final String NO_EXIST_LO = "指定されたオフライン学習LOが存在しません。";
	/** LOが存在しないエラー(ログ用) */
	private static final String NO_EXIST_LO_FOR_LOG = "LOが存在しません。";
	/** ファイルが存在しないエラー */
	private static final String NO_EXIST_FILE = "指定された教材が存在しません。";
	/** ファイルが存在しないエラー(ログ用) */
	private static final String NO_EXIST_FILE_FOR_LOG = "教材が存在しません。";
	/** 権限がない場合のエラー */
	private static final String NO_AUTH_ERR = "権限がありません。";
	/** 学習アサインされていない場合のエラー */
	private static final String NO_LEARN_AUTH_ERR = "学習アサインされていません。";
	/** LOID有効性チェックエラー */
	private static final String NO_LOID_DATA_ERR = "LOIDが無効です。";
	/** RealLOID有効性チェックエラー */
	private static final String NO_REALLOID_DATA_ERR = "指定されたLOはオフライン学習LOではありません。";
	/** DBエラー */
	private static final String DB_ERROR = "データベースエラーが発生しました。";
	/** すべてのログの先頭につける */
	private static final String PREFIX = "OfflineLO：ダウンロード処理：";
	/** 権限チェックログの先頭につける */
	private static final String AUTH_PREFIX = "OfflineLO：ダウンロード処理：権限チェック：";

	/**
	 * コンストラクタ
	 */
	public OfflineDownloadServlet() {}

	/**
	 * ＧＥＴ要求を処理するメソッド<BR>
	 * @param HttpServletRequest	ＨＴＴＰリクエスト
	 * @param HttpServletResponse	ＨＴＴＰレスポンス
	 * @return void
	 * @exception javax.servlet.ServletException	サーブレット例外
	 * @exception java.io.IOException				入出力例外
	 */
	public void doGet(
		HttpServletRequest request,HttpServletResponse response)
			throws ServletException, IOException
	{
		// リクエスト処理を呼び出します
		this.doRequest(request, response);
	}

	/**
	 * ＰＯＳＴ要求を処理するメソッド<BR>
	 * @param HttpServletRequest	ＨＴＴＰリクエスト
	 * @param HttpServletResponse	ＨＴＴＰレスポンス
	 * @return void
	 * @exception javax.servlet.ServletException	サーブレット例外
	 * @exception java.io.IOException				入出力例外
	 */
	public void doPost(
		HttpServletRequest request,HttpServletResponse response)
			throws ServletException, IOException
	{
		// リクエスト処理を呼び出します
		this.doRequest(request, response);
	}

	/**
	 * 要求を処理するメソッド<BR>
	 * @param HttpServletRequest	ＨＴＴＰリクエスト
	 * @param HttpServletResponse	ＨＴＴＰレスポンス
	 * @return void
	 */
	private void doRequest(
		HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException
	{
		String strLoID			= request.getParameter( "LOID" );		// LoID
		String strMaterialID	= request.getParameter( "MaterialID" );	// 教材ID
		String strLoginUserID	= null;									// ログインユーザID
		String strPassword		= null;									// パスワード
		String strRealLOID		= null;									// RealLOID

		// セッションからログインユーザIDとパスワードを取得する
		HttpSession session = request.getSession();
		strLoginUserID = (String)session.getAttribute("userId");
		strPassword = (String)session.getAttribute("password");
		
DebugLog.write( this.getClass(), PREFIX + START_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.ROW);
		// パラメータチェック
		if( strLoID == null ||
			strLoID.equals("") ||
			strMaterialID == null ||
			strMaterialID.equals("") )
		{
			// パラメータが不正の場合は権限エラー画面に遷移する。
DebugLog.write( this.getClass(), PREFIX + PARAM_ERR + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
			goWarningpage( request, response, PARAM_ERR );
			return;
		}

		try {
			//-------------
			// 権限チェック
			//-------------
			// ログインチェック
			if( strLoginUserID == null )
			{
				// ログインしていない場合は権限エラー画面に遷移する。
DebugLog.write( this.getClass(), AUTH_PREFIX + NO_LOGIN, DebugLog.HIGHT);
				goWarningpage( request, response, NO_AUTH_ERR );
				return;
			}
			// LOIDの有効性チェック
			// LO情報をLOXMOより取得します。
			LoClient objLo = new LoClient();
			// 検索条件を設定
			objLo.setWhereLoID(strLoID);
			if( !objLo.select() ) {
				// 検索がうまくいかなかった場合
				String strMessage =  objLo.getMessage();
DebugLog.write( this.getClass(), PREFIX + strMessage + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
				goWarningpage( request, response, strMessage );
				return;
			}

			// LOテーブルからLOIDのレコードを取得できた場合
			if( objLo.next() ) {
				strRealLOID = objLo.getRealLOID();
			}
			// LOテーブルからLOIDのレコードを取得でなかった場合
			else{
				// LOID有効性チェックエラーの場合は権限エラー画面に遷移する。
DebugLog.write( this.getClass(), PREFIX + NO_EXIST_LO_FOR_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
DebugLog.write( this.getClass(), AUTH_PREFIX + NO_LOID_DATA_ERR + ":" + strLoID, DebugLog.HIGHT);
				goWarningpage( request, response, NO_EXIST_LO );
				return;
			}

			// RealLOIDの有効性チェック
			// OfflineLoEnv読込
			OfflineLoEnv objOfflineEnv = new OfflineLoEnv();
			String strMatPath = objOfflineEnv.getMaterialPath();	// 教材パス

			// Loサーバ名を取得する
			Environment objEnv = new Environment();
			String strLoServerName = objEnv.getLoServerName();

			LoTopClient objLoTop = new LoTopClient( strLoServerName, LO_TYPE_OFFLINE );
			// 検索処理
			if( !objLoTop.getData(strRealLOID) ) {
				// 検索がうまくいかなかった場合
				MessageData objMessage = objLoTop.getMessageData();
				String strMessage =  objMessage.getMessage();
DebugLog.write( this.getClass(), PREFIX + strMessage + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
				goWarningpage( request, response, strMessage );
				return;
			}

			// 検索件数をチェックし、0件名ならエラーとする
			// (すでに削除されている)
			if( objLoTop.getDataLength() == 0 ) {
				// すでにLOが存在しない場合はLO未存在エラー画面
DebugLog.write( this.getClass(), NO_LOID_DATA_ERR, DebugLog.HIGHT);
DebugLog.write( this.getClass(), AUTH_PREFIX + NO_REALLOID_DATA_ERR + ":" + strLoID, DebugLog.HIGHT);
				goWarningpage( request, response, NO_EXIST_LO );
				return;
			}


			// 権限チェック
DebugLog.write( this.getClass(), PREFIX + START_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.ROW);
			if( !checkAuth( strLoginUserID, strLoID ) ) {
				// 権限がない場合は権限エラー画面に遷移する。
DebugLog.write( this.getClass(), PREFIX + NO_AUTH_ERR + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
DebugLog.write( this.getClass(), AUTH_PREFIX + NO_LEARN_AUTH_ERR + ":" + strLoginUserID + ":" + strLoID, DebugLog.HIGHT);
				goWarningpage( request, response, NO_AUTH_ERR );
				return;
			}
DebugLog.write( this.getClass(), PREFIX + END_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.ROW);


			// LoTopクライアントの学習開始メソッドを実行します。
			// 　入力パラメータ：
			//      LoID
			//      ユーザID
			//      パスワード
			//      戻り先URL
			// 　返却値：
			// 　   正否（true：正常、false：異常）
			boolean bLearn = objLoTop.startLearn(	strLoginUserID,
													strPassword,
													strLoID,
													null,
													strRealLOID,
													null,
													null
													);
			if ( bLearn == false )
			{
DebugLog.write( this.getClass(), PREFIX + DB_ERROR + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
				goWarningpage( request, response, DB_ERROR );
				return;
			}
			
			
			// 教材データファイル読込処理
			// ファイルの存在するディレクトリに存在するファイルのリストを取得します。
			String strFilePath = strMatPath + "/" + strMaterialID + "/";
			String strMatXML = strMaterialID + ".xml";
			FileControl objFileControl = new FileControl();
			String[] strFileList = objFileControl.getFileList(strFilePath, jp.co.ntt.lms.xmo.util.FileControl.FILE_LIST);
			// ファイルリストが存在しない場合は画面にその旨を表示する
			if(strFileList.length == 0){
DebugLog.write( this.getClass(), PREFIX + NO_EXIST_FILE_FOR_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
				goWarningpage( request, response, NO_EXIST_FILE );
				return;
			}
			
			// strMaterialID.xml以外の最初に取得できたファイル名でファイルパスを生成する。
			String strFileName = null;	// 教材ファイル名
			for(int i=0; i<strFileList.length;i++){
				if(strFileList[i] != null && !strFileList[i].equals(strMatXML)){
					strFileName = strFileList[i];
					break;
				}
			}
			// ファイル名が取得できた場合はファイルパスを生成します。
			if(strFileName != null){
				strFilePath += strFileName;
System.out.println("strFilePath = " + strFilePath);
			}
			// ファイルが存在しない場合は画面にその旨を表示する
			else{
DebugLog.write( this.getClass(), PREFIX + NO_EXIST_FILE_FOR_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
				goWarningpage( request, response, NO_EXIST_FILE );
				return;
			}

			byte[] buff = new byte[BUF_SIZE];
			int intSize = -1;

			// ファイルの読み込み
			FileInputStream input = new FileInputStream( strFilePath );
			OutputStream out = response.getOutputStream();

			// ヘッダ設定
			String strAttachment = "attachment; filename=" + strMaterialID + ".yyw";
			response.setHeader( "Pragma", "cache");
			response.setHeader( "cache-control", "cache" );
			response.setContentType( "application/octet-stream" );
			response.setHeader( "Content-Disposition", strAttachment );

			while( ( intSize = input.read( buff ) ) >= 0 ) {
				out.write( buff ,0 , intSize );
			}

			input.close();
DebugLog.write( this.getClass(), PREFIX + END_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.ROW);
		}
		catch( FileNotFoundException fe ) {
			// ファイルが存在しない場合は画面にその旨を表示する
DebugLog.write( this.getClass(), PREFIX + NO_EXIST_FILE_FOR_LOG + ":" + strLoID + ":" + strLoginUserID, DebugLog.HIGHT);
DebugLog.write( this.getClass(), fe, DebugLog.HIGHT);
			goWarningpage( request, response, NO_EXIST_FILE );
			return;
		}
		catch( Exception e ) {
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			goErrorPage( request, response, e.getMessage() );
		}
	}

	/**
	 * 警告ページ画面へフォワードするメソッド<BR>
	 * @param HttpServletRequest	ＨＴＴＰリクエスト
	 * @param HttpServletResponse	ＨＴＴＰレスポンス
	 * @param HttpServletResponse	エラーメッセージ
	 * @return void
	 */
	private void goErrorPage(
		HttpServletRequest request, HttpServletResponse response,
		String strErrorMessage)
			throws ServletException, IOException
	{
		request.setAttribute("ErrorMessage", strErrorMessage);
		ServletContext objSc    = getServletContext();
		RequestDispatcher objRd = objSc.getRequestDispatcher("/jsp/lo/Com/ComErr.jsp");
		objRd.forward(request, response);
	}

	/**
	 * エラーページ画面へフォワードするメソッド<BR>
	 * @param HttpServletRequest	ＨＴＴＰリクエスト
	 * @param HttpServletResponse	ＨＴＴＰレスポンス
	 * @param HttpServletResponse	エラーメッセージ
	 * @return void
	 */
	private void goWarningpage(
		HttpServletRequest request, HttpServletResponse response,
		String strErrorMessage)
			throws ServletException, IOException
	{
		response.setContentType( "text/html" );
		request.setAttribute("WarningMessage", strErrorMessage);
		ServletContext objSc    = getServletContext();
		RequestDispatcher objRd = objSc.getRequestDispatcher("/jsp/lo/Offline/OfflineWarning.jsp");
		objRd.forward(request, response);
	}

	/**
	 * 権限チェック
	 *  以下のチェックをする
	 *    LOのアサインチェック
	 *    指定されたLOがアサインされていない場合はエラーとする
	 * @return boolean true: 権限有り false: 権限なし
	 */
	private boolean checkAuth( String strUserID, String strLoID )
		throws LOException, Exception
	{
DebugLog.write( this.getClass(), AUTH_PREFIX + START_LOG, DebugLog.ROW);
		// ユーザIDがスーパユーザの場合は無条件にOK
		if( strUserID.equals(XMOPropertyDef.ROOT_USER) ) {
			return true;
		}

		// 権限チェックフラグ
		boolean isAuth = false;

		// 権限対象LOのリストを取得します。
		// XMSBaseを使用して、ログインユーザが使用できるLOリストを取得します。
		XMSBaseClient objBase = new XMSBaseClient();
		if( !objBase.searchRootLoList( strUserID, jp.co.ntt.lms.xmo.Base.XMSBaseTable.LEARN_TYPE ) ) {
			throw new LOException( objBase.getMessage() );
		}

		LoData[] objRootLo = objBase.getRootLoList();
		// 権限フィルタ用のIDリストを作成する。
		Vector vecAuthList = new Vector();
		for( int i = 0; i < objRootLo.length; i++ ) {
			vecAuthList.add( objRootLo[i].getLoID() );
		}

		// 指定LOIDのパスを取得します。
		// 参照LOIDのパスを取得する
		LoRelationClient objLoRelation = new LoRelationClient();
		if( !objLoRelation.searchPath(strLoID) ) {
			// パス検索がうまくいかなかった場合
			throw new LOException( objLoRelation.getMessage() );
		}

		// パス毎に権限チェックを行う。
		while( objLoRelation.next() ) {
			String[] strPath = objLoRelation.getPath();
			for( int i = 0; i < strPath.length; i++ ) {

				// 権限フィルターが設定されていた場合は権限によるチェックをします。
				if( vecAuthList.contains( strPath[i] ) ) {
					// 権限フィルターにIDが含まれていたら権限フラグをtrueにする
					isAuth = true;
					break;
				}

			}

			if( isAuth ) {
				break;
			}
		}
DebugLog.write( this.getClass(), AUTH_PREFIX + END_LOG + ":" + isAuth, DebugLog.ROW);

		return isAuth;
	}

}
