package org.apache.soap.util.net;

/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "SOAP" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2000, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.net.*;
import java.io.*;
import java.util.*;
import javax.net.ssl.*;
import java.security.*;

/**
 * A bunch of utility stuff for doing SSL things.
 *
 * @author Chris Nelson (cnelson@synchrony.net)
 * @author Jan-Sebastian Winckelmann ( jan.winckelmann@hermes-kredit.com )
 */
public class SSLUtils {
/** This method builds an SSL socket, after auto-starting SSL */
public static Socket buildSSLSocket(String host, int port)
	throws IOException, UnknownHostException {

	SSLSocket socket;

	/*
	* Let's setup the SSLContext first, as there's a lot of
	* computations to be done.  If the socket were created
	* before the SSLContext, the server/proxy might timeout
	* waiting for the client to actually send something.
	*/
	SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();

	/*
	 * Set up a socket to do tunneling through the proxy.
	 * Start it off as a regular socket, then layer SSL
	 * over the top of it.
	 */
	String tunnelHost = System.getProperty("https.proxyHost");
	String tunnelPort = System.getProperty("https.proxyPort");

	if (tunnelHost != null && tunnelPort != null)
	{
		int tunnelPortInt = new Integer(tunnelPort).intValue();

		Socket tunnel = new Socket(tunnelHost, tunnelPortInt);
		doTunnelHandshake(tunnel, host, port, tunnelHost, tunnelPortInt);

		/*
		 * Ok, let's overlay the tunnel socket with SSL.
		 */
		socket = (SSLSocket) factory.createSocket(tunnel, host, port, true);

	}
	else
	{
		socket = (SSLSocket) factory.createSocket(host, port);
	}

	socket.startHandshake();

	return socket;
}
/*
 * Tell our tunnel where we want to CONNECT, and look for the
 * right reply.  Throw IOException if anything goes wrong.
 */
private static void doTunnelHandshake(Socket tunnel, String host, int port, String tunnelHost, int tunnelPort)
	throws IOException {
	OutputStream out = tunnel.getOutputStream();
	String msg =
		"CONNECT "
			+ host
			+ ":"
			+ port
			+ " HTTP/1.0\n"
			+ "User-Agent: "
			+ sun.net.www.protocol.http.HttpURLConnection.userAgent
			+ "\r\n\r\n";
	byte b[];
	try
	{
		/*
		 * We really do want ASCII7 -- the http protocol doesn't change
		 * with locale.
		 */
		b = msg.getBytes("ASCII7");
	}
	catch (UnsupportedEncodingException ignored)
	{
		/*
		 * If ASCII7 isn't there, something serious is wrong, but
		 * Paranoia Is Good (tm)
		 */
		b = msg.getBytes();
	}
	out.write(b);
	out.flush();

	/*
	 * We need to store the reply so we can create a detailed
	 * error message to the user.
	 */
	byte reply[] = new byte[200];
	int replyLen = 0;
	int newlinesSeen = 0;
	boolean headerDone = false; /* Done on first newline */

	InputStream in = tunnel.getInputStream();
	boolean error = false;

	while (newlinesSeen < 2)
	{
		int i = in.read();
		if (i < 0)
		{
			throw new IOException("Unexpected EOF from proxy");
		}
		if (i == '\n')
		{
			headerDone = true;
			++newlinesSeen;
		}
		else
			if (i != '\r')
			{
				newlinesSeen = 0;
				if (!headerDone && replyLen < reply.length)
				{
					reply[replyLen++] = (byte) i;
				}
			}
	}

	/*
	 * Converting the byte array to a string is slightly wasteful
	 * in the case where the connection was successful, but it's
	 * insignificant compared to the network overhead.
	 */
	String replyStr;
	try
	{
		replyStr = new String(reply, 0, replyLen, "ASCII7");
	}
	catch (UnsupportedEncodingException ignored)
	{
		replyStr = new String(reply, 0, replyLen);
	}

	/* We asked for HTTP/1.0, so we should get that back */
	if (!replyStr.startsWith("HTTP/1.0 200"))
	{
		throw new IOException(
			"Unable to tunnel through "
				+ tunnelHost
				+ ":"
				+ tunnelPort
				+ ".  Proxy returns \""
				+ replyStr
				+ "\"");
	}

	/* tunneling Handshake was successful! */
}
}
