Package net.java.sip.communicator.impl.protocol.sip.xcap

Source Code of net.java.sip.communicator.impl.protocol.sip.xcap.BaseHttpXCapClient$HostNameResolverImpl

/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.sip.xcap;

import net.java.sip.communicator.impl.protocol.sip.*;
import net.java.sip.communicator.impl.protocol.sip.xcap.utils.*;
import net.java.sip.communicator.impl.protocol.sip.xcap.model.*;
import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror.*;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.util.*;
import org.apache.http.*;
import org.apache.http.auth.*;
import org.apache.http.client.methods.*;
import org.apache.http.conn.*;
import org.apache.http.conn.scheme.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.*;
import org.osgi.framework.*;

import javax.net.ssl.*;
import javax.sip.address.*;
import java.io.*;
import java.net.URI;
import java.net.*;

/**
* Base HTTP XCAP client implementation.
* <p/>
* Compliant with rfc4825
*
* @author Grigorii Balutsel
*/
public abstract class BaseHttpXCapClient implements HttpXCapClient
{
    /**
     * Class logger.
     */
    private static final Logger logger =
            Logger.getLogger(BaseHttpXCapClient.class);

    /**
     * HTTP Content-Type header.
     */
    public static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**
     * HTTP ETag header.
     */
    public static final String HEADER_ETAG = "ETag";

    /**
     * HTTP If-None-Match header.
     */
    public static final String HEADER_IF_NONE_MATCH = "If-None-Match";

    /**
     * XCap-error content type.
     */
    public static final String XCAP_ERROR_CONTENT_TYPE
            = "application/xcap-error+xml";

    /**
     * The default timeout (10 seconds)
     */
    private static int DEFAULT_TIMEOUT = 10 * 1000;

    /**
     * Current server uri.
     */
    protected URI uri;

    /**
     * Current user.
     */
    protected Address userAddress;

    /**
     * Current user loginname.
     */
    private String username;

    /**
     * Current user password.
     */
    private String password;

    /**
     * Indicates whether or not client is connected.
     */
    private boolean connected;

    /**
     * How many seconds should the client wait for HTTP response.
     */
    private int timeout;

    /**
     * The service we use to interact with user regarding certificates.
     */
    private CertificateVerificationService certificateVerification;

    /**
     * Creates an instance of this XCAP client.
     */
    public BaseHttpXCapClient()
    {
        timeout = DEFAULT_TIMEOUT;

        ServiceReference guiVerifyReference
            = SipActivator.getBundleContext().getServiceReference(
                CertificateVerificationService.class.getName());

        if(guiVerifyReference != null)
            certificateVerification
                = (CertificateVerificationService)SipActivator.getBundleContext()
                    .getService(guiVerifyReference);
    }

    /**
     * Connects user to XCap server.
     *
     * @param uri         the server location.
     * @param userAddress the URI of the user used for requests
     * @param username the user name.
     * @param password    the user password.
     * @throws XCapException if there is some error during operation.
     */
    public void connect(URI uri, Address userAddress, String username, String password)
            throws XCapException
    {
        if (!userAddress.getURI().isSipURI())
        {
            throw new IllegalArgumentException("Address must contains SipUri");
        }
        this.uri = uri;
        this.userAddress = (Address) userAddress.clone();
        this.username = username;
        this.password = password == null ? "" : password;
        connected = true;
    }

    /**
     * Checks if user is connected to the XCAP server.
     *
     * @return true if user is connected.
     */
    public boolean isConnected()
    {
        return connected;
    }

    /**
     * Disconnects user from the XCAP server.
     */
    public void disconnect()
    {
        this.uri = null;
        this.userAddress = null;
        this.password = null;
        connected = false;
    }

    /**
     * Gets the resource from the server.
     *
     * @param resourceId resource identifier.
     * @return the server response.
     * @throws IllegalStateException if the user has not been connected.
     * @throws XCapException         if there is some error during operation.
     */
    public XCapHttpResponse get(XCapResourceId resourceId)
            throws XCapException
    {
        return get(getResourceURI(resourceId));
    }

    /**
     * Gets resource from the server.
     *
     * @param uri the resource uri.
     * @return the server response.
     * @throws XCapException if there is error during reading the resource's
     *                       content.
     */
    protected XCapHttpResponse get(URI uri)
            throws XCapException
    {
        DefaultHttpClient httpClient = createHttpClient();
        try
        {
            HttpGet getMethod = new HttpGet(uri);
            getMethod.setHeader("Connection", "close");
            Credentials credentials =
                    new UsernamePasswordCredentials(getUserName(), password);
            httpClient.getCredentialsProvider().
                    setCredentials(AuthScope.ANY, credentials);

            HttpResponse response = httpClient.execute(getMethod);
            XCapHttpResponse result = createResponse(response);
            if (logger.isDebugEnabled())
            {
                byte[] contentBytes = result.getContent();
                String contenString;
                // for debug purposes print only xmls
                // skip the icon queries
                if(contentBytes != null && result.getContentType() != null
                        && !result.getContentType()
                                .startsWith(PresContentClient.CONTENT_TYPE))
                    contenString = new String(contentBytes);
                else
                    contenString = "";

                String logMessage = String.format(
                        "Getting resource %1s from the server content:%2s",
                        uri.toString(),
                        contenString
                );
                logger.debug(logMessage);
            }
            return result;
        }
        catch (IOException e)
        {
            String errorMessage = String.format(
                    "%1s resource cannot be read",
                    uri.toString());
            throw new XCapException(errorMessage, e);
        }
        finally
        {
            httpClient.getConnectionManager().shutdown();
        }
    }

    /**
     * Puts the resource to the server.
     *
     * @param resource the resource  to be saved on the server.
     * @return the server response.
     * @throws IllegalStateException if the user has not been connected.
     * @throws XCapException         if there is some error during operation.
     */
    public XCapHttpResponse put(XCapResource resource)
            throws XCapException
    {
        DefaultHttpClient httpClient = createHttpClient();
        try
        {
            URI resourceUri = getResourceURI(resource.getId());
            HttpPut putMethod = new HttpPut(resourceUri);
            putMethod.setHeader("Connection", "close");
            StringEntity stringEntity = new StringEntity(resource.getContent());
            stringEntity.setContentType(resource.getContentType());
            stringEntity.setContentEncoding("UTF-8");
            putMethod.setEntity(stringEntity);
            Credentials credentials =
                    new UsernamePasswordCredentials(getUserName(), password);
            httpClient.getCredentialsProvider().
                    setCredentials(AuthScope.ANY, credentials);
            if (logger.isDebugEnabled())
            {
                String logMessage = String.format(
                        "Puting resource %1s to the server %2s",
                        resource.getId().toString(),
                        resource.getContent()
                );
                logger.debug(logMessage);
            }
            HttpResponse response = httpClient.execute(putMethod);
            return createResponse(response);
        }
        catch (IOException e)
        {
            String errorMessage = String.format(
                    "%1s resource cannot be put",
                    resource.getId().toString());
            throw new XCapException(errorMessage, e);
        }
        finally
        {
            httpClient.getConnectionManager().shutdown();
        }
    }

    /**
     * Deletes the resource from the server.
     *
     * @param resourceId resource identifier.
     * @return the server response.
     * @throws IllegalStateException if the user has not been connected.
     * @throws XCapException         if there is some error during operation.
     */
    public XCapHttpResponse delete(XCapResourceId resourceId)
            throws XCapException
    {
        assertConnected();
        DefaultHttpClient httpClient = createHttpClient();
        try
        {
            URI resourceUri = getResourceURI(resourceId);
            HttpDelete deleteMethod = new HttpDelete(resourceUri);
            deleteMethod.setHeader("Connection", "close");
            Credentials credentials =
                    new UsernamePasswordCredentials(getUserName(), password);
            httpClient.getCredentialsProvider().
                    setCredentials(AuthScope.ANY, credentials);
            if (logger.isDebugEnabled())
            {
                String logMessage = String.format(
                        "Deleting resource %1s from the server",
                        resourceId.toString()
                );
                logger.debug(logMessage);
            }
            HttpResponse response = httpClient.execute(deleteMethod);
            return createResponse(response);
        }
        catch (IOException e)
        {
            String errorMessage = String.format(
                    "%1s resource cannot be deleted",
                    resourceId.toString());
            throw new XCapException(errorMessage, e);
        }
        finally
        {
            httpClient.getConnectionManager().shutdown();
        }
    }

    /**
     * Gets user name.
     *
     * @return the user name.
     */
    public String getUserName()
    {
        return username;
    }

    /**
     * Gets server uri.
     *
     * @return the server uri.
     */
    public URI getUri()
    {
        return uri;
    }

    /**
     * Gets operation timeout.The deffault value is 10 seconds.
     *
     * @return operation timeout.
     */
    public int getTimeout()
    {
        return timeout;
    }

    /**
     * Sets operation timeout. The deffault value is 10 seconds.
     *
     * @param timeout operation timeout.
     */
    public void setTimeout(int timeout)
    {
        this.timeout = timeout;
    }

    /**
     * Utility method throwing an exception if the user is not connected.
     *
     * @throws IllegalStateException if the user is not connected.
     */
    protected void assertConnected()
    {
        if (!connected)
        {
            throw new IllegalStateException(
                    "User is not connected to the server");
        }
    }

    /**
     * Gets resource uri from XCAP resource identifier.
     *
     * @param resourceId the resource identifier.
     * @return the resource uri.
     */
    protected URI getResourceURI(XCapResourceId resourceId)
    {
        try
        {
            return new URI(uri.toString() + "/" + resourceId);
        }
        catch (URISyntaxException e)
        {
            throw new IllegalArgumentException(
                    "Invalid XCAP resource identifier", e);
        }
    }

    /**
     * Creates HTTP client with special parameters.
     *
     * @return the HTTP client.
     */
    private DefaultHttpClient createHttpClient()
    {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        try
        {
            // make sure we use Certificate Verification Service if
            // for some reason the certificate needs to be shown to user
            // for approval
            ClientConnectionManager ccm = httpClient.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            SSLContext ctx = certificateVerification.getSSLContext(
                uri.getHost(), uri.getPort() == -1 ? 443 : uri.getPort());
            org.apache.http.conn.ssl.SSLSocketFactory ssf
                = new org.apache.http.conn.ssl.SSLSocketFactory(
                        ctx, new HostNameResolverImpl());
            ssf.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory
                .ALLOW_ALL_HOSTNAME_VERIFIER);
            sr.register(new Scheme("https", ssf, 443));
        }
        catch(Throwable e)
        {
            logger.error("Cannot add our trust manager to httpClient", e);
        }
        HttpParams httpParams = httpClient.getParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, timeout);
        HttpConnectionParams.setSoTimeout(httpParams, timeout);
        return httpClient;
    }

    /**
     * Creates XCAP response from HTTP response.
     * If HTTP code is 200, 201 or 409 the HTTP content would be readed.
     *
     * @param response the HTTP response.
     * @return the XCAP response.
     * @throws IOException if there is error during reading the HTTP content.
     */
    private XCapHttpResponse createResponse(HttpResponse response)
            throws IOException
    {
        XCapHttpResponse xcapHttpResponse = new XCapHttpResponse();
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == HttpStatus.SC_OK ||
                statusCode == HttpStatus.SC_CREATED ||
                statusCode == HttpStatus.SC_CONFLICT)
        {
            String contentType = getSingleHeaderValue(response,
                    HEADER_CONTENT_TYPE);
            byte[] content = StreamUtils.read(
                    response.getEntity().getContent());
            String eTag = getSingleHeaderValue(response, HEADER_ETAG);
            xcapHttpResponse.setContentType(contentType);
            xcapHttpResponse.setContent(content);
            xcapHttpResponse.setETag(eTag);
        }
        xcapHttpResponse.setHttpCode(statusCode);
        return xcapHttpResponse;
    }

    /**
     * Reads response from http.
     * @param response the response
     * @return the result String.
     * @throws IOException
     */
    private static String readResponse(HttpResponse response)
            throws IOException
    {
        HttpEntity responseEntity = response.getEntity();
        if (responseEntity.getContentLength() == 0)
        {
            return "";
        }
        byte[] content = StreamUtils.read(responseEntity.getContent());
        return new String(content, "UTF-8");
    }

    /**
     * Gets HTTP header value.
     *
     * @param response   the HTTP response.
     * @param headerName the header name.
     * @return the header value.
     */
    protected static String getSingleHeaderValue(
            HttpResponse response,
            String headerName)
    {
        Header[] headers = response.getHeaders(headerName);
        if (headers != null && headers.length > 0)
        {
            return headers[0].getValue();
        }
        return null;
    }

    /**
     * Analyzes the response and returns xcap error or null
     * if response doesn't have it.
     *
     * @param response the server response.
     * @return xcap error or null.
     */
    protected String getXCapErrorMessage(XCapHttpResponse response)
    {
        int httpCode = response.getHttpCode();
        String contentType = response.getContentType();
        try
        {
            if (httpCode != HttpStatus.SC_CONFLICT || contentType == null ||
                    !contentType.startsWith(XCAP_ERROR_CONTENT_TYPE))
            {
                return null;
            }
            String content = new String(response.getContent());
            XCapErrorType xCapError = XCapErrorParser.fromXml(content);
            XCapError error = xCapError.getError();
            if (error == null)
            {
                return null;
            }
            return error.getPhrase();
        }
        catch (ParsingException e)
        {
            logger.error("XCapError cannot be parsed.");
            return null;
        }
    }

    /**
     * Using deprecated HostNameResolver as quick fix,
     * Make apache http client lib to use our resolver when connecting to hosts.
     */
    private class HostNameResolverImpl
        implements HostNameResolver
    {
        /**
         * Resolves given hostname to its IP address
         *
         * @param hostname the hostname.
         * @return IP address.
         * @throws java.io.IOException
         */
        public InetAddress resolve(String hostname)
            throws IOException
        {
            try
            {
                InetSocketAddress addr = null;
                // use port 80 as we need only the address
                if(ProtocolProviderServiceSipImpl.checkPreferIPv6Addresses())
                    addr = NetworkUtils.getAAAARecord(hostname, 80);
                else
                    addr = NetworkUtils.getARecord(hostname, 80);

                if(addr != null)
                    return addr.getAddress();
                else
                    return null;
            }
            catch(java.text.ParseException e)
            {
                throw new IOException(e.getMessage());
            }
        }
    }
}
TOP

Related Classes of net.java.sip.communicator.impl.protocol.sip.xcap.BaseHttpXCapClient$HostNameResolverImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.