Java tutorial
/* * 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; } }