package org.maachang.proxy.engine.net.http;

import java.util.ArrayList;
import java.util.HashMap;

import org.maachang.proxy.engine.net.RequestInfo;
import org.maachang.proxy.engine.net.server.ProxyInfo;
import org.maachang.util.AnalysisUtil;
import org.maachang.util.StringUtil;

/**
 * HTTP上のCookieを扱う.
 * 
 * @version 2007/08/26
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class Cookie {
    
    /**
     * Cookie取得ヘッダ.
     */
    public static final String GET_COOKIE = "Cookie" ;
    
    /**
     * Cookie設定ヘッダ.
     */
    public static final String SET_COOKIE = "Set-Cookie" ;
    
    /**
     * セッションを保持する名前.
     */
    public static final String SESSION_NAME = "maachang-session" ;
    
    /**
     * セッションパラメータ名.
     */
    public static final String SESSION_PARAM = "_maachang-session" ;
    
    /**
     * セッションパラメータ名(Binary).
     */
    public static final byte[] SESSION_PARAM_BIN = SESSION_PARAM.getBytes() ;
    
    /**
     * Cookieを発行.
     * <BR><BR>
     * 新しいCookieを発行します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     * @param path 指定Cookieが有効となるPathを設定します.
     * @return String Set-Cookieヘッダ情報が返されます.
     * @exception Exception 例外.
     */
    public static final String createCookie( String key,String value,String path )
        throws Exception {
        return createCookie( key,value,path,Long.MIN_VALUE ) ;
    }
    
    /**
     * Cookieを発行.
     * <BR><BR>
     * 新しいCookieを発行します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     * @param path 指定Cookieが有効となるPathを設定します.
     * @param expire このCookieを削除する時間を設定します.
     * @return String Set-Cookieヘッダ情報が返されます.
     * @exception Exception 例外.
     */
    public static final String createCookie( String key,String value,String path,long expire )
        throws Exception {
        if( key == null || ( key = key.trim() ).length() <= 0 ||
            value == null || ( value = value.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "key及びvalueは必須条件です" ) ;
        }
        else if( key.indexOf( "=" ) != -1 ) {
            throw new IllegalArgumentException( "key名[" + key + "]には不正な文字が格納されています" ) ;
        }
        StringBuilder buf = new StringBuilder() ;
        buf.append( key ).append( "=" ).append( value ).append( ";" ) ;
        if( path != null && ( path = path.trim() ).length() > 0 ) {
            buf.append( " path=" ).append( path ).append( ";" ) ;
        }
        if( expire != Long.MIN_VALUE ) {
            buf.append( " expires=" ).append( HttpdTimestamp.getTimestamp( true,expire ) ).append( ";" ) ;
        }
        return buf.toString() ;
    }
    
    /**
     * 指定Cookie情報を解析.
     * <BR><BR>
     * 指定されたCookie情報を解析します.
     * <BR>
     * @param cookie 対象のCookieヘッダ内容を設定します.
     * @return HashMap<String,String> 解析された内容が返されます.
     * @exception Exception 例外.
     */
    public static final HashMap<String,String> analysisCookie( String cookie )
        throws Exception {
        if( cookie == null || ( cookie = cookie.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "Cookie情報は不正です" ) ;
        }
        ArrayList<String> lst = StringUtil.cutString( true,false,cookie,":; =" ) ;
        if( lst == null || lst.size() <= 0 ) {
            return null ;
        }
        HashMap<String,String> ret = new HashMap<String,String>() ;
        int len = lst.size() ;
        for( int i = 0 ; i < len ; i += 2 ) {
            String key = lst.get( i ).trim() ;
            String value = lst.get( i+1 ) ;
            ret.put( key,value ) ;
        }
        return ret ;
    }
    
    /**
     * リクエストヘッダから、Cookie情報を取得.
     * <BR><BR>
     * リクエストヘッダから、Cookie情報を取得します.
     * <BR>
     * @param info 接続情報を設定します.
     * @param request 対象のHttpdRequest情報を設定します.
     * @return String 対象のCookie情報が返されます.
     * @exception Exception 例外.
     */
    public static final String getSessionId( ProxyInfo info,RequestInfo request )
        throws Exception {
        HttpParams params = null ;
        HashMap<String,String> ana = null ;
        String sessionKey = info.getSessionName() ;
        String id = null ;
        String cookie = request.getHeader( GET_COOKIE,0 ) ;
        // クッキーから、セッションIDを取得.
        if( cookie != null ) {
            if( ana == null ) {
                ana = Cookie.analysisCookie( cookie ) ;
            }
            if( sessionKey == null || sessionKey.length() <= 0 ) {
                id = ana.get( SESSION_NAME ) ;
            }
            else {
                id = ana.get( sessionKey ) ;
            }
            if( id != null ) {
                request.setSessionId( id ) ;
                return id ;
            }
        }
        // パラメータから、セッションIDを取得.
        if( id == null ) {
            // Urlクエリに存在するかチェック.
            if( request.getUrl().indexOf( "?" ) > 0 ) {
                if( params == null ) {
                    params = AnalysisUtil.convertAnalysisQuery( request ) ;
                }
                if( sessionKey == null || sessionKey.length() <= 0 ) {
                    id = ( String )params.get( SESSION_PARAM ) ;
                }
                else {
                    id = ( String )params.get( sessionKey ) ;
                }
                if( id != null ) {
                    request.setSessionId( id ) ;
                    return id ;
                }
            }
            // bodyパラメータに存在するかチェック.
            byte[] body = request.getBody() ;
            if( body != null ) {
                int bodyLen = body.length ;
                byte[] sessionKeyBin = null ;
                if( sessionKey == null || sessionKey.length() <= 0 ) {
                    sessionKeyBin = SESSION_PARAM_BIN ;
                }
                else {
                    sessionKeyBin = sessionKey.getBytes( "ISO-8859-1" ) ;
                }
                int p = AnalysisUtil.binaryIndexOf( body,sessionKeyBin,0,bodyLen ) ;
                if( p > -1 ) {
                    int r = AnalysisUtil.binaryIndexOf( body,"&",p,bodyLen ) ;
                    if( r <= -1 ) {
                        r = bodyLen ;
                    }
                    bodyLen = r - ( p + sessionKeyBin.length + 1 ) ;
                    byte[] idBin = new byte[ bodyLen ] ;
                    System.arraycopy(body,p + sessionKeyBin.length + 1,idBin,0,bodyLen ) ;
                    id = new String( idBin,"ISO-8859-1" ) ;
                    idBin = null ;
                    if( id != null ) {
                        request.setSessionId( id ) ;
                        return id ;
                    }
                }
            }
        }
        return null ;
    }
    
    
}
