com.funambol.exchange.httptransport.HTTP_FBA_Authentication.java Source code

Java tutorial

Introduction

Here is the source code for com.funambol.exchange.httptransport.HTTP_FBA_Authentication.java

Source

/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2008 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

package com.funambol.exchange.httptransport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

import com.funambol.exchange.util.Def;
import com.funambol.framework.logging.FunambolLogger;
import com.funambol.framework.logging.FunambolLoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;

class HTTP_FBA_Authentication {

    private static final int SIZE_INPUT_BUFFER = 4096;

    private static final int MAX_REDIRECTS = 6;

    ExchangeHTTP exchangePacket;

    protected FunambolLogger log = FunambolLoggerFactory.getLogger(Def.LOGGER_NAME);

    public HTTP_FBA_Authentication() {
        exchangePacket = new ExchangeHTTP();
    }

    void processSetCookies(HashMap<String, String> cookieJar, String cookies) {
        int start = 0;
        while (start < cookies.length()) {

            String acookie;
            if (cookies.indexOf(";", start) != -1) {
                acookie = cookies.substring(start, cookies.indexOf(";", start));
            } else {
                acookie = cookies.substring(start);
            }

            if (acookie.indexOf("=") != -1) {
                String cookiename = acookie.substring(0, acookie.indexOf("="));
                cookiename = cookiename.trim();
                String cookieval = acookie.substring(acookie.indexOf("=") + 1, acookie.length());

                if (!("path".equals(cookiename) || "expires".equals(cookiename))) {

                    if ("".equals(cookieval)) {
                        cookieJar.remove(cookiename);
                    } else {
                        cookieJar.put(cookiename, cookieval);
                    }

                }
            }
            start += acookie.length() + 1;
        }
    }

    String createCookieString(HashMap<String, String> cookieJar) {
        ArrayList<String> pairs = new ArrayList<String>();

        for (Entry<String, String> entry : cookieJar.entrySet()) {
            pairs.add(entry.getKey() + "=" + entry.getValue());
        }

        String result = StringUtils.join(pairs.toArray(), "; ");

        return result;
    }

    /**
     * this method implements Form-Based Authentication
     * 
     * @param hostname
     * @param username
     * @param password
     * @param transport
     * @return
     * @throws IOException
     */
    public HTTP_Response doAuth(String hostname, String username, String password, WebDavHttpTransport transport,
            boolean isSSLEnabled) throws IOException {

        int numTries = 1;
        String location = "";
        String cookie = "";
        HashMap<String, String> cookies = new HashMap<String, String>();

        exchangePacket.setSocket(transport.getASocket());
        // For Exchange 07, 'owa' is a better folder to hit, can we put the Exc
        // version in a config somewhere
        // exchangePacket.setCommand("POST /owa/auth/owaauth.dll");
        exchangePacket.setCommand("POST /exchweb/bin/auth/owaauth.dll");
        exchangePacket.setHeader_content_type("application/x-www-form-urlencoded");
        // A URL encoded as "https://<hostname>/exchange/<username>")
        String prefix = "http://";

        if (isSSLEnabled) {
            prefix = "https://";
        }

        String codedURL = URLEncoder.encode(prefix + hostname + "/exchange/" + username, "UTF-8");

        // a packet data
        exchangePacket.setData("destination=" + codedURL + "&username=" + URLEncoder.encode(username, "UTF-8")
                + "&password=" + URLEncoder.encode(password, "UTF-8") + "&SubmitCreds=Log+On&trusted=4");
        exchangePacket.header_host = hostname;

        HTTP_Response response = bz_ExchangeSend(exchangePacket);

        cookie = response.getCookies_Returned();
        processSetCookies(cookies, cookie);
        cookie = createCookieString(cookies);

        while ((response.status_code == 302) && (numTries < MAX_REDIRECTS)) {

            exchangePacket.setSocket(transport.getNewSocket());

            if (log.isWarningEnabled()) {
                log.warn("user " + username + " not authenticated after attempt " + numTries);
            }
            if (log.isTraceEnabled()) {
                log.trace("Return Statuscode: " + response.status_code);
            }
            if (log.isDebugEnabled()) {
                log.debug("Received Location: " + response.location);
            }
            location = response.location;
            location = location.substring(8, location.length());
            location = location.substring(location.indexOf('/'), location.length());
            location = location.trim();
            if (log.isDebugEnabled()) {
                log.debug("Location Path: " + location);
            }
            // Check if login failed
            int reasonIndex = location.indexOf("reason=");
            if (reasonIndex != -1) {
                // Reason 0 is ok, anything else means failure
                int beginIndex = reasonIndex + "reason=".length();
                if (!"0".equals(location.substring(beginIndex, beginIndex + 1))) {
                    log.error("Exchange rejected user credentials");
                    cookies.clear();
                    break;
                }
            }

            exchangePacket.setCommand("GET " + location);

            exchangePacket.setHeader_content_type("");

            exchangePacket.setCookies_set(cookie);
            exchangePacket.setData("");
            exchangePacket.setHeader_referer("");
            exchangePacket.header_content_type = "";

            response = bz_ExchangeSend(exchangePacket);

            cookie = response.getCookies_Returned();
            processSetCookies(cookies, cookie);
            cookie = createCookieString(cookies);

            numTries++;

        }

        response.cookies_returned = createCookieString(cookies);

        return response;

    }

    private HTTP_Response bz_ExchangeSend(ExchangeHTTP transaction) throws IOException {
        // START FORMING HTTP REQUEST:
        String headers_out = exchangeFBAPacket(transaction);

        InputStream in = transaction.socket.getInputStream();
        OutputStream out = transaction.socket.getOutputStream();

        out.write(headers_out.getBytes());
        out.flush();

        if (log.isTraceEnabled()) {
            log.trace("[HEADERS SENT]:\r\n" + headers_out.trim() + "\r\n\r\n");
        }

        String cookies = "";
        String headers_in = "";
        HTTP_Response response_status = new HTTP_Response();

        /**
         * reading the answer...
         */

        byte[] bResponse = new byte[SIZE_INPUT_BUFFER];
        int t = in.read(bResponse, 0, SIZE_INPUT_BUFFER);

        String str = new String(bResponse);

        /*
         * if (log.isTraceEnabled()) { log.trace("Exchange received: " + str); }
         */

        if (str.length() > 0) {

            // START SPECIAL HEADER PROCESSING:
            cookies = parseExchangeResponse(headers_out, cookies, response_status, str);
            // END SPECIAL HEADER PROCESSING.

            if (log.isInfoEnabled()) {
                log.info("received final cookies: " + cookies);
            }
            headers_in += str + "\r\n";
        }

        transaction.cookies_returned = cookies;
        response_status.setCookies_Returned(cookies);

        if (log.isTraceEnabled()) {
            log.trace("[HEADERS RECEIVED]:\r\n" + headers_in.trim() + "\r\n\r\n");
        }

        return response_status;
    }

    /**
     * this method parses the cookie packet from Exchange, to extract the
     * session cookies required for FBA
     * 
     * We expect a packet in the form:
     * 
     * <code>
     * 
     * HTTP/1.1 302 Moved Temporarily
     * Server: Microsoft-IIS/5.0
     * Date: Thu, 24 Jul 2003 17:23:33 GMT
     * X-Powered-By: ASP.NET
     * Location: https://ex2k3.xcs.ximian.com/exchange/
     * Set-Cookie: sessionid=fbb50caf-381a-4f85-9582-a7a902b4561f,0x409; path=/
     * Set-Cookie: cadata="2,8JOrhvROIJykiSTShG6Ujrigo+a5XQgEbws7tq3//37QERyFwWDoV7xw6DG+Awlm"; HttpOnly; secure; path=/
     * Content-Length: 0
     * </code>
     * 
     * 
     * @param headers_out
     * @param cookies
     * @param response_status
     * @param str
     * @return
     */
    private String parseExchangeResponse(String headers_out, String cookies, HTTP_Response response_status,
            String str) {

        int content_length;

        String toSearch = "HTTP/";

        if (str.indexOf(toSearch) != -1) {
            // we want to take the return status
            int firstMarker = str.indexOf(toSearch) + toSearch.length() + 4;

            String tmpString = str.substring(firstMarker, firstMarker + 3);
            response_status.status_code = Integer.parseInt(tmpString);

            if (log.isTraceEnabled()) {
                log.debug("received status code: " + response_status.status_code);
            }
            response_status.status_description = str.substring("HTTP/1.1 200 ".length(), str.indexOf("\n"));
            if (log.isTraceEnabled()) {
                log.debug("received status descr: " + response_status.status_description);
            }

            // Parse and find location
            toSearch = "Location: ";
            firstMarker = str.indexOf(toSearch) + toSearch.length();
            String parsed = str.substring(firstMarker, str.lastIndexOf("\n"));
            response_status.location = parsed.substring(0, parsed.indexOf("\n"));
            log.info("Location found was: " + response_status.location);

            if (log.isTraceEnabled()) {
                // Comparing status code to expected range:
                if ((response_status.status_code != 200) && (response_status.status_code != 207)
                        && (response_status.status_code != 302)) {
                    // Unexpected status code encountered, enabling debug:
                    log.trace("[HEADERS SENT]:\r\n" + headers_out.trim() + "\r\n\r\n");

                }
            }
        }

        int tmpInt = str.indexOf("content-length: ");
        if (tmpInt != -1) {
            content_length = Integer.parseInt(str.substring(tmpInt, tmpInt + "content-length: ".length()));

            if (log.isTraceEnabled()) {
                log.trace("received content length: " + content_length);
            }
        }

        HashMap<String, String> cookiejar = new HashMap<String, String>();
        tmpInt = str.indexOf("Set-Cookie:");
        while (tmpInt != -1) {
            tmpInt += "Set-Cookie:".length();
            int end = str.indexOf("\n", tmpInt);
            if (end == -1) {
                end = str.length();
            } else {
                end = end - 1;
            }
            cookies += str.substring(tmpInt, end) + "; ";
            tmpInt = str.indexOf("Set-Cookie:", tmpInt + 1);
            if (tmpInt != -1) {
                tmpInt = str.indexOf("Set-Cookie:", tmpInt);
            }
        }

        processSetCookies(cookiejar, cookies);
        cookies = createCookieString(cookiejar);

        return cookies;
    }

    private String exchangeFBAPacket(ExchangeHTTP transaction) {
        // FORMING HTTP Request
        String headers_out = transaction.getCommand() + " HTTP/1.1\r\n";
        if (transaction.q234486) {
            headers_out += "cookies: Necessary according to Q234486, " + transaction.cookies_set + "\r\n";
        }
        if (transaction.header_accept.length() > 0) {
            headers_out += "Accept: " + transaction.header_accept + "\r\n";
        }
        if (transaction.header_accept_language.length() > 0) {
            headers_out += "Accept-Language: " + transaction.header_accept_language + "\r\n";
        }

        if (transaction.header_referer.length() > 0) {
            headers_out += "Referer: " + transaction.header_referer + "\r\n";
        }
        if (transaction.header_content_type.length() > 0) {
            headers_out += "Content-Type: " + transaction.header_content_type + "\r\n";
        }
        if (transaction.header_connection.length() > 0) {
            headers_out += "Connection: " + transaction.header_connection + "\r\n";
        }
        if (transaction.cookies_set.length() > 0) {
            headers_out += "Cookie: " + transaction.cookies_set + "\r\n";
        }
        if (transaction.header_user_agent.length() > 0) {
            headers_out += "User-Agent: " + transaction.header_user_agent + "\r\n";
        }
        if (transaction.header_host.length() > 0) {
            headers_out += "Host: " + transaction.header_host + "\r\n";
        }
        if (transaction.header_cache_control.length() > 0) {
            headers_out += "Cache-Control: " + transaction.header_cache_control + "\r\n";
        }
        if (transaction.data.length() > 0) {
            headers_out += "Content-Length: " + (transaction.data.length() + 1) + "\r\n" + "\r\n" + transaction.data
                    + "\r\n";
        }
        headers_out += "\r\n\r\n";

        // END FORMING HTTP REQUEST.
        return headers_out;
    }
}