/* Copyright (c) 2000, 2001 Oracle Corporation. All Rights Reserved. */
package oracle.portal.provider.v2.http;

import java.util.Calendar;
import java.io.UnsupportedEncodingException;

import javax.naming.NamingException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;

import oracle.portal.provider.v2.ProviderUser;
import oracle.portal.provider.v2.ProviderSubscription;
import oracle.portal.provider.v2.PortalRealm;
import oracle.portal.provider.v2.UserLocation;
import oracle.portal.provider.v2.ProviderSession;
import oracle.portal.provider.v2.ProviderException;
import oracle.portal.provider.v2.oid.OidManager;
import oracle.portal.provider.v2.http.HttpCommonConstants;

import oracle.portal.log.LogManager;

import oracle.security.jazn.realm.Realm;

import oracle.webdb.provider.v2.adapter.soapV1.ProviderAdapter;
import oracle.webdb.provider.v2.adapter.HttpConstants;
import oracle.webdb.utils.HTTPUtils;
import oracle.webdb.utils.SimpleStringBuffer;

public class ServletProviderUser extends ProviderUser
{
    private String mName = null;
    private String mName308 = null;
    private String mName309 = null;
    private String mMappedName = null;
    private ProviderSubscription mProviderSubscription = null;
    private PortalRealm mPortalRealm;
    private Calendar mLoginTime = null;
    private String mPortalSessionId = null;
    private UserLocation mLocation = null;
    private int mAuthenticationLevel = AUTH_LEVEL_NOT_DETERMINED;

    private HttpServletRequest mRequest;

    private OidManager mOidManager = null;
    private String mUserDN = null;
    private String mSubscriberDN = null;
    private String mUserGUID = null;
    private String mSubscriberGUID = null;

    /**
     * Constructs this <code>ServletProviderUser</code> object. For
     * performance, parameters will only be read from the given
     * <code>HttpServletRequest</code> object when required.
     *
     * @param request a <code>HttpServletRequest</code>.
     */
    public ServletProviderUser(HttpServletRequest request)
    {
        mRequest = request;
    }

    /**
     * Constructs this <code>ServletProviderUser</code> object. For
     * performance, parameters will only be read from the given
     * <code>HttpServletRequest</code> object when required.
     * Used in version 9.0.2.6
     *
     * @param request a <code>HttpServletRequest</code>.
     */
    public ServletProviderUser(HttpServletRequest request, OidManager oidManager)
    {
        this(request);
        mOidManager = oidManager;
    }

    /**
     * Constructs this <code>ServletProviderUser</code> object using the
     * information supplied.
     */
    public ServletProviderUser(HttpServletRequest request,
                               String name308,
                               String name309,
                               String subscriberId,
                               String companyName,
                               int authLevel,
                               String mappedName,
                               Calendar loginTime,
                               String portalSessionId,
                               UserLocation location)
    {
        this(request);
        mName308 = name308;
        mName309 = name309;
        mMappedName = mappedName;
        mPortalSessionId = portalSessionId;
        mLoginTime = loginTime;
        mLocation = location;
        mAuthenticationLevel = authLevel;
        mProviderSubscription = createProviderSubscription(subscriberId);
        mPortalRealm = createPortalRealm(companyName);
    }

    /**
     * Constructs this <code>ServletProviderUser</code> object using the
     * information supplied.
     * Used in version 9.0.2.6
     */
    public ServletProviderUser(HttpServletRequest request,
                               String name308,
                               String name309,
                               String subscriberId,
                               String companyName,
                               int authLevel,
                               String mappedName,
                               Calendar loginTime,
                               String portalSessionId,
                               UserLocation location,
                               OidManager oidManager,
                               String userDN,
                               String userGUID,
                               String subscriberDN,
                               String subscriberGUID)
    {
        this(request, name308, name309, subscriberId, companyName, authLevel,
             mappedName, loginTime, portalSessionId, location);
        mOidManager = oidManager;
        mUserDN = userDN;
        mUserGUID = userGUID;
        mSubscriberDN = subscriberDN;
        mSubscriberGUID = subscriberGUID;
 
    }

    /**
     * Gets the name of the portal user represented by this object.
     *
     * @return the portal user name.
     */
    public String getName()
    {
        // name308 is the username as sent by Portal release 3.0.8 and earlier
        // This variable communicates 2 pieces of information, the logged-in
        // state of the user and the username. In release 3.0.9, the logged in
        // state is communicated via the authentication level and the name309
        // communicates the username.  Two variables are necessary because in
        // release 3.0.9 and later, the username should always represent the
        // actual username of the user - it will never be "portal_public".
        // This means that a second variable was necessary to maintain
        // backwards compatibility when a provider written using JPDK 3.0.9 or
        // later is registered with portal 3.0.8 or earlier
        if (mName == null)
        {
            // use name309 if it is given to us, otherwise use name308
            mName = (getName309() == null ? getName308() : getName309());
        }
        return mName;
    }

    /**
     * Gets the external application username that is mapped to this portal
     * user.
     *
     * @return the mapped application user. <b>null</b> if there is no mapped
     * user.
     */
    public String getMappedName() 
    {
        if (mMappedName == null)
        {
            mMappedName =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_MAPPED_USER);
        }
        return mMappedName;
    }

    /**
     * Gets subscription information related to this user/request Note, the
     * subscription information is specific to the ProviderInstance the current
     * request was received for.
     *
     * @return ProviderSubscription object representing the subscription info
     * or <b>null</b> if there is no subscription info.
     */
    public ProviderSubscription getProviderSubscription()
    {
        if (mProviderSubscription == null)
        {
            String key =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_SUBSCRIBER_ID);
            mProviderSubscription = createProviderSubscription(key);
        }
        return mProviderSubscription;
    }

    /**
     * Get the portal user's realm. This method returns the generic Realm
     * defined by {@link oracle.security.jazn.realm.Realm}.
     *
     * @return the portal realm.
     */
    public Realm getRealm() 
    {
        if (mPortalRealm == null)
        {
            String sub =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_COMPANY_NAME);
            mPortalRealm = createPortalRealm(sub);
        }
        return mPortalRealm;
    }

    /**
     * Get the portal user' realm. This method returns the Provider framework
     * specific {@link PortalRealm} implementation.
     */
    public PortalRealm getPortalRealm() 
    {
        return (PortalRealm)getRealm();
    }

    /**
     * Gets the time when this user logged into the Portal.
     *
     * @return the login time.
     */
    public Calendar getLoginTime()
    {
        if (mLoginTime == null)
        {
            String time = 
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_LOGIN_TIME);
            mLoginTime = ProviderAdapter.extractLoginTime(time);
        }
        return mLoginTime;
    }

    /**
     * Gets the user's location.  Value is allowed to be <code>null</code>,
     * which means the information is not available.
     *
     * @return a {@link UserLocation} object representing the user's location.
     */
    public UserLocation getLocation()
    {
        if (mLocation == null)
        {
            mLocation = extractLocationFromHeaders();
        }
        return mLocation;
    }

    /**
     * Gets the Portal session ID for this user.  Portlet developers can use
     * this ID to map to their in memory "session" state vs. using a cookie
     * based mechanism.  The benefit is performance.  If the provider doesn't
     * use cookies the portal can communicate more efficiently with it.
     *
     * @return the Portal session ID for this user.
     */
    public String getPortalSessionId()
    {
        if (mPortalSessionId == null)
        {
            mPortalSessionId =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_SESSION_ID);
        }
        return mPortalSessionId;
    }

    /**
     * Gets the authentication level for the user.
     *
     * @return the authentication level.
     */
    public int getAuthenticationLevel()
    {
        if (mAuthenticationLevel == AUTH_LEVEL_NOT_DETERMINED)
        {
            String s =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_AUTH_LEVEL);
            // initialize the authentication level
            if (s == null) 
            {
                // authLevel is not sent by portal, therefore it must be portal
                // 3.0.8 or earlier.  Derive the authentication level from
                // name308

                if (getName308() == null)
                {
                    // should never happen - but saves checking for null in
                    // subsequent IF statements
                    mAuthenticationLevel = NOT_AUTHENTICATED;
                }
                else if (getName308().equalsIgnoreCase(PORTAL_PUBLIC))
                {
                    mAuthenticationLevel = NOT_AUTHENTICATED;
                }
                else if (getName308().equalsIgnoreCase(PUBLIC_USERNAME))
                {
                    mAuthenticationLevel = PUBLIC_STRONG_AUTH;
                }
                else
                {
                    mAuthenticationLevel = USER_STRONG_AUTH;
                }
            }
            else
            {
                mAuthenticationLevel = Integer.parseInt(s);
            }
        }
        return mAuthenticationLevel;
    }

    public ProviderSession getSession(boolean create)
    {
        return ServletProviderSession.getAttachedSession(mRequest, create);
    }

    public ProviderSession getSession()
    {
        return ServletProviderSession.getAttachedSession(mRequest);
    }

    /**
     *  Gets an array of all the cookies established between this user and the
     *  provider, or a <code>null</code> value if the user currently has no
     *  cookies. Cookies must be established in the Provider's initSession
     *  method.
     *
     *  @return an array of all this user's cookies or <code>null</code> if the
     *  user currently has no cookies.
     */
    public Cookie[] getCookies()
    {
        return mRequest.getCookies();
    }

    private ProviderSubscription createProviderSubscription(String key)
    {
        ProviderSubscription sub = null;
        if (key != null && !key.equals(""))
        {
            sub = new ProviderSubscription(key);
        }
        return sub;
    }

    private PortalRealm createPortalRealm(String id)
    {
        PortalRealm sub = null;
        // To satisfy portal's concept of a user, we must ALWAYS have a
        // non-null realm
        if (id != null && !id.equals(""))
        {
            sub = new PortalRealm(id);
        }
        else
        {
            sub = new PortalRealm(DEFAULT_SUBSCRIBER_NAME);
        }
        return sub;
    }

    private UserLocation extractLocationFromHeaders()
    {
        String addressLine1 = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_ADDRESSLINE1);

        // if the first header isn't there we assume all headers aren't there
        // hence no address
        if (addressLine1 == null) return null;

        // now get the rest of the data
        String addressLine2 = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_ADDRESSLINE2);
        String addressLineLast = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_ADDRESSLASTLINE);
        String block = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_BLOCK);
        String city = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_CITY);
        String county = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_COUNTY);
        String state = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_STATE);
        String country = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_COUNTRY);
        String postalCode = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_POSTALCODE);
        String postalCodeExt = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_POSTALCODEEXT);
        String type = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_TYPE);
        String companyName = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_COMPANY);
        String positionX = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_X);
        String positionY = mRequest.getHeader(
            HttpCommonConstants.HEADER_IASWIRELESS_USER_LOCATION_Y);

        // Determine how these values are encoded
        String encoding = mRequest.getHeader(HttpCommonConstants.HTTP_PORTAL_CONTENT_CHARSET);
        if (encoding == null) encoding = HttpCommonConstants.HTTP_DEFAULT_CHARSET;

        // create the address object from above info
        // first decode the values as they are encoded like URL parameters
        UserLocation.Address address = null;
        try
        {
            address = new UserLocation.Address(
            HTTPUtils.decode(companyName, encoding),
            HTTPUtils.decode(addressLine1, encoding),
            HTTPUtils.decode(addressLine2, encoding),
            HTTPUtils.decode(addressLineLast, encoding),
            HTTPUtils.decode(block, encoding),
            HTTPUtils.decode(city, encoding),
            HTTPUtils.decode(county, encoding),
            HTTPUtils.decode(state, encoding),
            HTTPUtils.decode(country, encoding),
            HTTPUtils.decode(postalCode, encoding),
            HTTPUtils.decode(postalCodeExt, encoding));
        }
        catch (UnsupportedEncodingException uee)
        {
            return null;
        }
        // convert the values that need converting
        UserLocation.LocationType lType =
            UserLocation.LocationType.getLocationType(type);
        float x = 0, y = 0;
        try
        {
            if (positionX != null)
            {
                x = Float.valueOf(positionX).floatValue();
            }
            if (positionY != null)
            {
                y = Float.valueOf(positionY).floatValue();
            }
        } catch (NumberFormatException nfe)
        {
            // ignore as already set to default.
        }
        return new UserLocation(lType, x, y, address);
    }

    private String getName308()
    {
        if (mName308 == null)
        {
            mName308 =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_USER);
        }
        return mName308;
    }

    private String getName309()
    {
        if (mName309 == null)
        {
            mName309 =
                mRequest.getParameter(HttpConstants.REQUEST_PARAM_USERNAME);
        }
        return mName309;
    }

    /**
     * Get the portal user's distinguished name.
     *
     * This method returns the
     * users distinguished name as stored in OID.
     *
     * @return the portal user's distinguished name.
     */
    public String getUserDN()
    {
        if(mUserDN == null)
        {
            try
            {
                mUserDN = 
                    HTTPUtils.decode(mRequest.getHeader("X-ORACLE-PORTAL-USER-DN"),
                                     mRequest.getHeader(HttpCommonConstants.HTTP_PORTAL_CONTENT_CHARSET));    
            }
            catch(UnsupportedEncodingException uee)
            {
                LogManager.warning("Could not decode the header representing " +
                                   "the portal user's distinguished name.", 
                                   uee);
            }
        }

        return mUserDN;
    }

    /**
     * Get the portal user's subscriber name. 
     *
     * This method returns the user's subscriber name as stored in OID
     *
     * @return the portal user's subscriber name.
     */
    public String getSubscriberName()
    {
        String name = null;
        Realm realm = getRealm();
        if(realm != null)
        {
            name = realm.getName();
        }

        return name;
    }

    /**
     * Get the portal user's Subscription distinguished name.
     *
     * This method returns the user's subscriber distinguished
     * name as stored in OID.
     *
     * @return the portal user's subscriber distinguished name.
     */
    public String getSubscriberDN()
    {
        if(mSubscriberDN == null)
        {   
            try
            {
                mSubscriberDN = 
                    HTTPUtils.decode(mRequest.getHeader("X-ORACLE-PORTAL-SUBSCRIBER-DN"),
                                     mRequest.getHeader(HttpCommonConstants.HTTP_PORTAL_CONTENT_CHARSET));
            }
            catch (UnsupportedEncodingException uee)
            {
                LogManager.warning("Could not decode the header representing " +
                                   "the portal user's subscriber " +
                                   "distinguished name.", uee);
            }
        }

        return mSubscriberDN;
    }

   /**
    * Get the portal user's GUID.
    *
    * This method returns the user's GUID as stored in OID.
    *
    * @return the portal user's GUID.
    */
    public String getUserGUID()
    {
        if(mUserGUID == null)
        {
            mUserGUID = mRequest.getHeader("X-ORACLE-PORTAL-USER-GUID");
        }
        return mUserGUID;
    }

    /**
     * Get the portal user's subscriber GUID.
     *
     * This method returns the user's Subscriber GUID as stored in OID.
     *
     * @return the portal user's subscriber GUID.
     */
    public String getSubscriberGUID()
    {
        if(mSubscriberGUID == null)
        {
            mSubscriberGUID = mRequest.getHeader("X-ORACLE-PORTAL-SUBSCRIBER-GUID");
        }
        return mSubscriberGUID;
    }

   /**
    * Get the portal user's first name.
    *
    * This method returns the user's first name as stored in OID.
    *
    * @return the portal user's first name.
    */
    public String getFirstName()
    {
        return getPropertyValue(OidManager.OID_USER_GIVEN_NAME);
    }

   /**
    * Get the portal user's last name.
    *
    * This method returns the user's last name as stored in OID.
    *
    * @return the portal user's last name.
    */
    public String getLastName()
    {
        return getPropertyValue(OidManager.OID_USER_FAMILY_NAME);
    }

   /**
    * Get the portal user's email address.
    *
    * This method returns the user's email address as stored in OID.
    *
    * @return the portal user's email address.
    */
    public String getEmail()
    {
        return getPropertyValue(OidManager.OID_USER_MAIL);
    }

   /**
    * Get a named property value of the user as stored in OID.
    *
    * Any property associated with the user can be queried that
    * is stored in the OID user repository. Use the Oracle Directory
    * Manager tool to find out all the properties available.
    * Some of the commonly used property names are defined in OidManager class
    *
    * @param name of a user property
    * @return the portal user's named property.
    */
    public String getPropertyValue(String propertyName)
    {
        String value = null;

        if(mOidManager!=null && propertyName!=null)
        {
            try
            {
                value = mOidManager.getUserProperty(
                                        getUserDN(), getSubscriberDN(), propertyName);
            }
            catch (NamingException ne)
            {
                 LogManager.warning(new SimpleStringBuffer(100)
                     .append("ServletProviderUser.getPropertyValue(")
                     .append(propertyName)
                     .append(") failed").toString()
                     , ne);
            }
            catch (ProviderException pe)
            {
                 LogManager.warning(new SimpleStringBuffer(100)
                     .append("ServletProviderUser.getPropertyValue(")
                     .append(propertyName)
                     .append(") failed").toString()
                     , pe);
            }
        }

        return value;
    }

    /**
     * check if this user is a member of a group.
     *
     * The users, groups and group membership are stored in OID
     *
     * @param the name of the group
     * @return the whether or not this user is a member of the named group
     */
    public boolean isUserInGroup(String groupName)
    {
        boolean isInGroup = false;

        if(mOidManager!=null && groupName != null)
        {
            try
            {
                isInGroup = mOidManager.isUserInGroup(
                                          getUserDN(), getSubscriberDN(), groupName);
            }
            catch (NamingException ne)
            {
                 LogManager.warning(new SimpleStringBuffer(100)
                     .append("ServletProviderUser.isUserInGroup(")
                     .append(groupName)
                     .append(") failed").toString()
                     , ne);
            }
            catch (ProviderException pe)
            {
                 LogManager.warning(new SimpleStringBuffer(100)
                     .append("ServletProviderUser.isUserInGroup(")
                     .append(groupName)
                     .append(") failed").toString()
                     , pe);
            }
        }

        return isInGroup;
    }

   /**
    * Determine if the OidManager is configured.
    *
    * This is needed because
    * the methods  return null if there is no Oid Manager or there is no data
    * this method enables the caller to determine the reason why null was
    * returned
    *
    * @return whether the oid manager is configured.
    */
    public boolean isLDAPEnabled()
    {
        return (mOidManager != null);
    }

   /**
    * Get the users full name.
    *
    * Get the users full name eg First name, Last name
    *
    * @return the users full name
    */
    public String getFullName()
    {
        String first = getFirstName();
        String last = getLastName();

        if (first != null)
        {
            return (last != null) ? first + " " + last : first;
        }
        else 
        {
            return (last != null) ? last : null;
        }
    }

    /**
     * Get the users display name.
     *
     * @return the users display name
     */
    public String getDisplayName()
    {
        return getPropertyValue(OidManager.OID_USER_DISPLAY_NAME);
    }

    /**
     * Get the users description
     *
     * @return the users description
     */
    public String getDescription()
    {
        return getPropertyValue(OidManager.OID_USER_DESCRIPTION);
    }
}
