/*
 * $Id: HttpUtils.java,v 1.14 2005/09/14 00:55:21 marbobo Exp $
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * $Log: HttpUtils.java,v $
 * Revision 1.14  2005/09/14 00:55:21  marbobo
 * + url2filename() korrigiert
 *
 * Revision 1.13  2005/09/07 23:21:28  marbobo
 * + Bugfix
 *
 * Revision 1.12  2005/07/19 12:14:46  marbobo
 * + Bugfix
 *
 * Revision 1.11  2004/11/10 15:02:46  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.10  2004/08/27 16:29:55  koppy
 * url2filename geändert
 *
 * Revision 1.9  2004/07/20 08:46:28  jonyp
 * added copyright info + cvs log
 *
 * Revision 1.8  2004/06/27 17:33:37  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.7  2004/03/18 14:22:43  koppy
 * PersistentObjects transaktionsfähig gemacht
 *
 * Revision 1.6  2004/02/26 13:59:21  koppy
 * + url2filename
 *
 * Revision 1.5  2004/02/18 14:49:17  marbobo
 * printRequestParameters() Anzeige korrigiert
 *
 * Revision 1.4  2004/01/31 19:16:49  bigmac
 * + printRequestParameters()
 *
 * Revision 1.3  2003/12/24 10:11:58  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.2  2002/12/16 13:17:38  lawern
 * Retriever ueberarbeitet
 *
 * Revision 1.1  2002/10/21 14:36:26  bigmac
 * *** empty log message ***
 *
 * Revision 1.1.1.3  2002/09/17 08:18:23  jonyp
 * no message
 *
 * Revision 1.1.1.2  2002/09/11 13:20:22  jonyp
 * no message
 *
 * Revision 1.1.1.1  2002/04/02 10:55:46  lawern
 * initiale Version
 *
 * Revision 1.4  2002/03/06 13:33:43  bigmac
 * Umgehung des getHeaderField()-Bugs in jdk1.4 eingebaut
 *
 * Revision 1.3  2002/02/01 15:19:24  bigmac
 * + getResponseCode()
 *
 * Revision 1.2  2002/01/17 18:23:08  koppy
 * connection.getResponseCode() abgeändert
 *
 * Revision 1.1  2001/12/12 15:24:16  bigmac
 * initial version
 *
 */
package vkc.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import vkc.exception.HttpResponseException;
import vkc.misc.VKCLogManager;


/**
 * Sammelklasse für den Zugriff auf URLs
 * <p>
 * @author bigmac
 */
public final class HttpUtils {
    private static boolean using_JDK_14 = false;

    static {
        String jvmVersion = System.getProperty("java.vm.version");

        if (jvmVersion != null && jvmVersion.indexOf("1.4") >= 0) {
            using_JDK_14 = true;
        }
    }

    private HttpUtils() {
    }

    /**
     * Utility method that prints out all request parameter names with their
     * corresponding value.
     * <p>
     * @param request
     */
    public static final void printRequestParameters(HttpServletRequest request) {
        final ArrayList   list = new ArrayList();
        Enumeration e = request.getParameterNames();
        int maxLen = 0;

        while (e.hasMoreElements()) {
            final String s = e.nextElement().toString();
            list.add(s);

            if (s.length() > maxLen) {
                maxLen = s.length();
            }
        }

        Collections.sort(list,
            new Comparator() {
                public int compare(Object o1, Object o2) {
                    return o1.toString().compareToIgnoreCase(o2.toString());
                }
            });

        System.out.println( "REQUEST PARAMETERS:" );
        System.out.println( "===================" );

        for (int i = 0; i < list.size(); i++) {
            final String s = list.get(i).toString();

            String[] values = request.getParameterValues(s);

            System.out.print(s + " -> ");

            for (int j = 0; values != null && j < values.length; j++) {
                System.out.print(values[j] + " ");
            }

            System.out.println();
        }
    }

    /**
     * Stellt eine Verbindung zu einer URL her und gibt einen InputStream
     * bei erfolgreichem Verbindungsaufbau zurück
     * <p>
     * @param urlString - gesuchte URL
     * @param proxy_authorization - BASE64-kodierte Proxyautorisierungsdaten
     * @param webserver_authorization - BASE64-kodierte
     * Webserverautorisierungsdaten
     * @return InputStream bei erfolgreicher Verbindung
     * @author bigmac
     */
    public static final InputStream getInputStream(String urlString,
                                                   String webserver_authorization,
                                                   String proxy_authorization)
    throws UnknownHostException,
           ConnectException,
           ProtocolException,
           MalformedURLException,
           IOException,
           HttpResponseException,
           IllegalArgumentException {

    	if (urlString == null) {
            throw new IllegalArgumentException("url not specified");
        }

        if (urlString.length() == 0) {
            throw new IllegalArgumentException("url not specified");
        }
        
        URL url = new URL(urlString);
        URLConnection uc = url.openConnection();
        HttpURLConnection connection = (HttpURLConnection)uc;
        connection.setRequestProperty("User-Agent", "C-LAB Retriever/1.0 (+http://www.c-lab.de/vkc)");
        connection.setRequestMethod("GET");
        connection.setDoOutput(false);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setAllowUserInteraction(false);

        if (proxy_authorization != null) {
            connection.setRequestProperty("Proxy-Authorization", proxy_authorization);
        }
        
        if (webserver_authorization != null) {
            connection.setRequestProperty("Authorization", webserver_authorization);
        }
        
        /**
	     * Anmerkung: HttpUrlConnection.getResponseCode ist verbugt in
	     * JDKs 1.3.x ( s. Suns Bug-Database ). Zusätzlich liefert das
	     * JDK 1.4 bei "getHeaderField( 0 )" einen null-String zurück
	     * ... toll
	     */
        int responseCode = using_JDK_14
                               ? connection.getResponseCode()
                               : getResponseCode(uc.getHeaderField(0));

        if (responseCode != 200) {
            throw new HttpResponseException(responseCode, url.toExternalForm());
        }
        
        return connection.getInputStream();
    }

    /**
     * Stellt eine Verbindung zu einer URL her und gibt einen InputStream
     * bei erfolgreichem Verbindungsaufbau zurück
     * <p>
     * @param urlString - gesuchte URL
     * @param proxy_authorization - BASE64-kodierte Proxyautorisierungsdaten
     * @param webserver_authorization - BASE64-kodierte
     * Webserverautorisierungsdaten
     * @return InputStream bei erfolgreicher Verbindung
     * @author bigmac
     */
    public static final byte[] doPost(String urlString,
                                      String webserver_authorization,
                                      String proxy_authorization, byte[] data)
    throws UnknownHostException, ConnectException,
           ProtocolException, MalformedURLException,
           IOException, HttpResponseException,
           IllegalArgumentException {
        
    	if (urlString == null) {
            throw new IllegalArgumentException("url not specified");
        }
        
        if (urlString.length() == 0) {
            throw new IllegalArgumentException("url not specified");
        }
        
        if (data == null || data.length == 0) {
            throw new IllegalArgumentException("no data to post");
        }
        
        URL url = new URL(urlString);
        URLConnection uc = url.openConnection();
        HttpURLConnection connection = (HttpURLConnection)uc;
        connection.setRequestProperty("User-Agent", "C-LAB Retriever/1.0 (+http://www.c-lab.de/vkc)");
        connection.setRequestMethod("POST");
        connection.setDoOutput(false);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setAllowUserInteraction(false);

        if (proxy_authorization != null) {
            connection.setRequestProperty("Proxy-Authorization", proxy_authorization);
        }
        
        if (webserver_authorization != null) {
            connection.setRequestProperty("Authorization", webserver_authorization);
        }
        
        OutputStream out = null;
        InputStream in = null;
        byte[] answer = null;

        try {
            // POST-Daten wegschreiben
            out = new BufferedOutputStream(connection.getOutputStream());
            out.write(data, 0, data.length);
            out.flush();
            out.close();
            out = null;

            try {
                int responseCode = using_JDK_14
                                       ? connection.getResponseCode()
                                       : getResponseCode(uc.getHeaderField(0));

                if (responseCode != 200) {
                    throw new HttpResponseException(responseCode, url.toExternalForm());
                }
            } catch (java.io.FileNotFoundException fex) {
                throw new HttpResponseException(404, url.toExternalForm());
            }

            int contentLength = connection.getContentLength();

            if (contentLength <= 0) {
                throw new HttpResponseException(HttpURLConnection.HTTP_NO_CONTENT, url.toExternalForm());
            }
            
            in = new BufferedInputStream(connection.getInputStream());

            int readBytes = 0;
            int alreadyRead = 0;
            answer = new byte[contentLength];

            while (true) {
                readBytes = in.read(answer, alreadyRead, contentLength);

                if (readBytes <= 0) {
                    break;
                }

                alreadyRead += readBytes;
                contentLength -= readBytes;
            }
        } finally {
            if (out != null) {
                FileUtils.closeOutputStream(out);
            }
            
            if (in != null) {
                FileUtils.closeInputStream(in);
            }
        }

        return answer;
    }

    /**
     * Stellt eine Verbindung zu einer URL her und gibt den Responseheader
     * bei erfolgreichem Verbindungsaufbau zurück
     * <p>
     * @param urlString - gesuchte URL
     * @param proxy_authorization - BASE64-kodierte Proxyautorisierungsdaten
     * @param webserver_authorization - BASE64-kodierte
     * Webserverautorisierungsdaten
     * @return HashMap mit Header-Daten
     * @author bigmac
     */
    public static final HashMap getHeaderFields(String urlString,
                                                String webserver_authorization,
                                                String proxy_authorization)
    throws UnknownHostException,
           ConnectException,
           ProtocolException,
           MalformedURLException,
           IOException,
           HttpResponseException {
        
    	URL url = new URL(urlString);
        URLConnection uc = url.openConnection();
        HttpURLConnection connection = (HttpURLConnection)uc;
        connection.setRequestProperty("User-Agent", "C-LAB Retriever/1.0 (+http://www.c-lab.de/vkc)");
        connection.setRequestMethod("GET");
        connection.setDoOutput(false);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setAllowUserInteraction(false);

        if (proxy_authorization != null) {
            connection.setRequestProperty("Proxy-Authorization", proxy_authorization);
        }
        
        if (webserver_authorization != null) {
            connection.setRequestProperty("Authorization", webserver_authorization);
        }
        
        try {
            int responseCode = using_JDK_14
                                   ? connection.getResponseCode()
                                   : getResponseCode(uc.getHeaderField(0));

            if (responseCode != 200) {
                throw new HttpResponseException(responseCode, url.toExternalForm());
            }
        } catch (java.io.FileNotFoundException fex) {
            throw new HttpResponseException(404, url.toExternalForm());
        }

        HashMap properties = new HashMap();
        int i = 1;
        String key = null;
        String value = null;

        while (true) {
            key = connection.getHeaderFieldKey(i);

            if (key == null) {
                break;
            }
            
            value = connection.getHeaderField(key);
            properties.put(key, value);
            i++;
        }

        return properties;
    }

    /**
     * Liesst den Inhalt einer URL ein und gibt ihn als ByteArray zurück
     * <p>
     * @param urlString - gesuchte URL
     * @param proxy_authorization - BASE64-kodierte Proxyautorisierungsdaten
     * @param webserver_authorization - BASE64-kodierte
     * Webserverautorisierungsdaten
     * @return Inhalt der URL als Bytearray bei erfolgreicher Verbindung
     * @author bigmac
     */
    public static byte[] doGet(String urlString,
                               String webserver_authorization,
                               String proxy_authorization)
    throws UnknownHostException, ConnectException,
           ProtocolException, MalformedURLException,
           IOException, HttpResponseException,
           IllegalArgumentException {

        if (urlString == null) {
            throw new IllegalArgumentException("url not specified");
        }
        
        if (urlString.length() == 0) {
            throw new IllegalArgumentException("url not specified");
        }
        
        URL url = new URL(urlString);
        URLConnection uc = url.openConnection();
        HttpURLConnection connection = (HttpURLConnection)uc;
        connection.setRequestProperty("User-Agent", "C-LAB Retriever/1.0 (+http://www.c-lab.de/vkc)");
        connection.setRequestMethod("GET");
        connection.setDoOutput(false);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setAllowUserInteraction(false);

        if (proxy_authorization != null) {
            connection.setRequestProperty("Proxy-Authorization", proxy_authorization);
        }
        
        if (webserver_authorization != null) {
            connection.setRequestProperty("Authorization", webserver_authorization);
        }
        
        try {
            int responseCode = using_JDK_14
                                   ? connection.getResponseCode()
                                   : getResponseCode(uc.getHeaderField(0));

            if (responseCode != 200) {
                throw new HttpResponseException(responseCode, url.toExternalForm());
            }
        } catch (java.io.FileNotFoundException fex) {
            throw new HttpResponseException(404, url.toExternalForm());
        }

        int contentLength = connection.getContentLength();

        if (contentLength <= 0) {
            throw new HttpResponseException(HttpURLConnection.HTTP_NO_CONTENT, url.toExternalForm());
        }
        
        BufferedInputStream bis = null;
        InputStream is = null;

        try {
            is = connection.getInputStream();
            bis = new BufferedInputStream(is);

            byte[] content = new byte[contentLength];
            int readBytes = 0;
            int alreadyRead = 0;

            while (true) {
                readBytes = bis.read(content, alreadyRead, contentLength);

                if (readBytes <= 0) {
                    break;
                }
                
                alreadyRead += readBytes;
                contentLength -= readBytes;
            }

            if (contentLength != 0) {
                throw new IOException("unexpected end of inputstream: " +
                                      readBytes + "bytes received but " +
                                      contentLength + " were expected");
            }
            
            return content;
        } finally {
            if (bis != null) {
                FileUtils.closeInputStream(bis);
            }
            
            if (is != null) {
                FileUtils.closeInputStream(is);
            }
        }
    }

    /* bestimmt aus einem Vector von Request-Properties den Wert
     * einer gesuchten Property (z.B. Content-length)
     * <p>
     * @param prop - gesuchte Property
     * @param header - Vector mit http-Properties
     * @return String mit Propertywert, leeren String falls nicht vorhanden
     */
    public static String getHeaderProperty(String prop, java.util.Vector header) {
        if (header == null || prop == null) {
            return "";
        }
        
        prop = prop.toLowerCase();

        for (int i = 0; i < header.size(); i++) {
            String s = (String)header.elementAt(i);
            String sLower = s.toLowerCase();

            if (sLower.startsWith(prop)) {
                int index = s.indexOf(":");

                if (index < 0) {
                    return "";
                }
                
                s = s.substring(index + 1, s.length());

                return s.trim(); // CRLF abschneiden
            }
        }

        return "";
    }

    /**
     * Liesst den Header eines HTTP-Request ein und liefert alle
     * Request-Properties in einem Vector aus Strings zurück.
     * <p>
     * @param in - Inputstream aus dem der HTTP-Request gelesen
     * werden soll
     * @return Vector mit Strings falls das Lesen geklappt hat.
     * null, sonst.
     * @author bigmac
     *
     */
    public static final java.util.Vector getHttpHeader(InputStream in) {
        java.util.Vector v = new java.util.Vector();
        String line = null;

        try {
            while (true) {
                line = VKCUtils.read_CRLF_line(in);

                if (line == null) {
                    break;
                }
                
                line = line.trim();
                v.add(line);

                if (line.length() == 0) {
                    break;
                }
            }

            return (v.size() == 0) ? null : v;
        } catch (Exception e) {
            VKCLogManager.errorLog(HttpUtils.class, "", e.getMessage(), e);

            return null;
        }
    }

    /**
     * Holt den HTTP-Response Code aus einem HTTP HeaderField
     * @author bigmac
     * @param resp - String der ein HTTP-Header Field enthaelt
     * @return HTTP Response Code
     */
    public static final int getResponseCode(String resp) {
        try {
            int ind = resp.indexOf(' ');

            while (resp.charAt(ind) == ' ') {
                ind++;
            }
            
            return Integer.parseInt(resp.substring(ind, ind + 3));
        } catch (Exception e) {
            VKCLogManager.errorLog(HttpUtils.class, "", e.getMessage(), e);

            return HttpURLConnection.HTTP_BAD_REQUEST;
        }
    }
    
    public static final String url2filename(URL url){
    	String path = url.getPath();
    	
    	if (path == null || path.trim().length() < 2) {
    		return "";
    	}

    	return path.substring(1);	
    }
}