package org.maachang.proxy.engine.conf;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;

import org.maachang.proxy.engine.admin.AdminManager;
import org.maachang.util.FileUtil;
import org.maachang.util.NetMask;
import org.maachang.util.StringUtil;
import org.maachang.util.conf.Config;
import org.maachang.util.conf.ReadIni;

/**
 * MaachangProxyコンフィグ.
 * 
 * @version 2008/03/26
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class MaachangProxyConfig {
    
    /**
     * ネットマスクコンフィグファイル名.
     */
    private static final String CONF_NAME = "conf/maachangProxy.conf" ;
    
    /**
     * 管理者用定義.
     */
    private static final String ADMIN_SECTION = "admin" ;
    
    /**
     * 管理者アクセス用セクション.
     */
    private static final String ADMIN_ADDRESS_SECTION = "admin-address" ;
    
    /**
     * 管理者認証モード.
     */
    private static final String AUTH_MODE = "auth" ;
    
    /**
     * 管理者用ホスト名.
     */
    private static final String ADMIN_HOST = "host" ;
    
    /**
     * 管理者認証ユーザパスワード.
     */
    private static final String AUTH_USER_PASSWD = "user" ;
    
    /**
     * 管理者アクセスアドレス用要素キー名.
     */
    private static final String KEY_NAME = "value" ;
    
    /**
     * 管理者用URLパッケージ名.
     */
    private static final String ADMIN_URL_PACKAGE = "org.maachang.proxy.engine.admin.page" ;
    
    /**
     * 更新チェック間隔.
     */
    private static final long CHECK_TIMING = 2500L ;
    
    /**
     * 管理者ホスト名.
     */
    private String adminHost = null ;
    
    /**
     * 管理者認証ユーザパスワード.
     */
    private HashMap<String,String> adminUserPasswd = null ;
    
    /**
     * 管理者用アドレス群管理.
     */
    private ArrayList<NetMask> adminList = null ;
    
    /**
     * コンフィグファイル最終更新時間.
     */
    private long fileTime = -1L ;
    
    /**
     * 前回コンフィグ更新チェックを行った時間.
     */
    private long beforeCheckTime = -1L ;
    
    /**
     * コンストラクタ.
     */
    public MaachangProxyConfig() {
        this.adminList = new ArrayList<NetMask>() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.adminList = null ;
    }
    
    /**
     * 管理者URLチェック.
     * @parma host 対象のアクセスホスト名を設定します.
     * @param url 対象のアクセスURLを設定します.
     * @return boolean [true]の場合、管理者URLです.
     * @exception Exception 例外.
     */
    public synchronized boolean isAdminUrl( String host,String url )
        throws Exception {
        if( host == null || ( host = host.trim() ).length() <= 0 ||
            url == null || ( url = url.trim() ).length() <= 0 ) {
            return false ;
        }
        updateCheck() ;
        if( adminHost == null || ( adminHost = adminHost.trim() ).length() <= 0 ) {
            return url.startsWith( AdminManager.ADMIN_URL ) ;
        }
        return ( adminHost.equals( host ) && url.startsWith( AdminManager.ADMIN_URL ) ) ;
    }
    
    /**
     * ユーザ名／パスワード認証を行うべきかチェック.
     * @return boolean [true]の場合、ユーザ名／パスワード認証を行います.
     * @exception Exception 例外.
     */
    public synchronized boolean isAuthAdmin()
        throws Exception {
        updateCheck() ;
        return adminUserPasswd != null ;
    }
    
    /**
     * 指定ユーザ名、パスワードを認証.
     * @param user 対象のユーザ名を設定します.
     * @param passwd 対象のパスワードを設定します.
     * @return boolean [true]の場合、認証成功です.
     * @exception Exception 例外.
     */
    public synchronized boolean isAuth( String user,String passwd )
        throws Exception {
        updateCheck() ;
        if( adminUserPasswd == null ) {
            return true ;
        }
        if( user == null || ( user = user.trim() ).length() <= 0 ) {
            return false ;
        }
        String chk = adminUserPasswd.get( user ) ;
        if( chk == null || ( chk = chk.trim() ).length() <= 0 ) {
            chk = "" ;
        }
        if( passwd == null || ( passwd = passwd.trim() ).length() <= 0 ) {
            passwd = "" ;
        }
        return chk.equals( passwd ) ;
    }
    
    /**
     * 管理者アドレスチェック.
     * @param addr 対象のアドレスを設定します.
     * @return boolean [true]の場合、対象アドレスは管理者画面アクセス対象です.
     * @exception Exception 例外.
     */
    public boolean isAdmin( InetAddress addr )
        throws Exception {
        if( addr == null ) {
            return false ;
        }
        boolean ret = isListMask( adminList,addr.getHostAddress() ) ;
        if( adminList.size() <= 0 ) {
            return true ;
        }
        return ret ;
    }
    
    /**
     * 管理者アドレスチェック.
     * @param addr 対象のアドレスを設定します.
     * @return boolean [true]の場合、対象アドレスは管理者画面アクセス対象です.
     * @exception Exception 例外.
     */
    public boolean isAdmin( String addr )
        throws Exception {
        if( addr == null || addr.length() <= 0 ) {
            return false ;
        }
        boolean ret = isListMask( adminList,addr ) ;
        if( adminList.size() <= 0 ) {
            return true ;
        }
        return ret ;
    }
    
    /**
     * 指定URLから、管理者用ページURLに変換.
     * @param url 対象のURLを設定します.
     * @return String 管理者用ページURLが返されます.
     */
    public String getAdminUrl( String url ) {
        if( url.startsWith( AdminManager.ADMIN_URL ) == false ) {
            return null ;
        }
        String ret = url.substring( AdminManager.ADMIN_URL.length() ) ;
        int paramPoint ;
        if( ( paramPoint = ret.indexOf( "?" ) ) >= 0 ) {
            ret = ret.substring( 0,paramPoint ) ;
        }
        if( ret == null || ( ret = ret.trim() ).length() <= 0 ) {
            ret = "" ;
        }
        else {
            ret = StringUtil.changeString( ret,"/","." ) ;
        }
        if( ret.length() <= 0 || ret.endsWith( "." ) ) {
            if( ret.endsWith( "." ) == false ) {
                ret += "." ;
            }
            ret += "AdminLogin" ;
        }
        if( ret.startsWith( "." ) == false ) {
            ret = "." + ret ;
        }
        return ADMIN_URL_PACKAGE + ret ;
    }
    
    /** 指定リストと、アドレス比較. */
    private synchronized boolean isListMask( ArrayList<NetMask> list,String addr )
        throws Exception {
        if( addr == null || addr.length() <= 0 ) {
            return false ;
        }
        updateCheck() ;
        int len = list.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            NetMask mask = list.get( i ) ;
            if( mask.isRange( addr ) == true ) {
                return true ;
            }
        }
        return false ;
    }
    
    /** 指定ファイルの更新確認. */
    private final void updateCheck() throws Exception {
        if( this.beforeCheckTime + CHECK_TIMING > System.currentTimeMillis() ) {
            return ;
        }
        this.beforeCheckTime = System.currentTimeMillis() ;
        long time = FileUtil.getLastTime( CONF_NAME ) ;
        if( time <= 0L ) {
            this.fileTime = -1L ;
            clearData() ;
        }
        else if( time != fileTime ) {
            read() ;
            this.fileTime = time ;
        }
    }
    
    /** ファイル内容を読み込む. */
    private final void read() throws Exception {
        Config conf = getConfig( CONF_NAME ) ;
        clearData() ;
        if( conf == null ) {
            return ;
        }
        readAdmin( conf ) ;
        readList( adminList,conf,ADMIN_ADDRESS_SECTION ) ;
    }
    
    /** 格納情報のクリア. */
    private final void clearData() {
        adminList.clear() ;
        adminHost = null ;
        adminUserPasswd = null ;
    }
    
    /** 管理者向け情報の取得. */
    private void readAdmin( Config conf )
        throws Exception {
        this.adminHost = conf.get( ADMIN_SECTION,ADMIN_HOST,0 ) ;
        this.adminUserPasswd = null ;
        if( conf.getBoolean( ADMIN_SECTION,AUTH_MODE,0 ) == true ) {
            String[] userList = conf.getAll( ADMIN_SECTION,AUTH_USER_PASSWD ) ;
            if( userList != null && userList.length > 0 ) {
                HashMap<String,String> lst = new HashMap<String,String>() ;
                int len = userList.length ;
                for( int i = 0 ; i < len ; i ++ ) {
                    String value = userList[ i ] ;
                    if( value == null || ( value = value.trim() ).length() <= 0 ) {
                        continue ;
                    }
                    int p = value.indexOf( "|" ) ;
                    if( p <= -1 ) {
                        lst.put( value,"" ) ;
                    }
                    else {
                        lst.put( value.substring( 0,p ).trim(),value.substring( p+1 ).trim() ) ;
                    }
                }
                if( lst.size() > 0 ) {
                    adminUserPasswd = lst ;
                }
            }
        }
    }
    
    /** 指定条件の内容を取得. */
    private static final void readList( ArrayList<NetMask> list,Config conf,String section )
        throws Exception {
        String[] values = conf.getAll( section,KEY_NAME ) ;
        if( values == null || values.length <= 0 ) {
            return ;
        }
        int len = values.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            String value = values[ i ] ;
            if( value == null || ( value = value.trim() ).length() <= 0 ) {
                continue ;
            }
            NetMask mask = new NetMask( value ) ;
            list.add( mask ) ;
        }
    }
    
    /** コンフィグファイルを取得. */
    private static final Config getConfig( String name ) {
        BufferedReader r = null ;
        try {
            Config ret = new Config() ;
            r = new BufferedReader( new InputStreamReader( new FileInputStream( name ),"UTF8" ) ) ;
            ReadIni.analisys( ret,r ) ;
            r.close() ;
            r = null ;
            return ret ;
        } catch( Exception e ) {
            return null ;
        } finally {
            if( r != null ) {
                try {
                    r.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
}

