OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class NetUtils {

    public static int findFreePort(final int preferred) {
        return findFreePort(null, preferred);
    }

    /**
     * Returns a free port number on localhost.
     * 
     * @param addr the bind address, <code>null</code> meaning an address of the loopback interface.
     * @param preferred if this port is free, then it's returned.
     * @return preferred if free otherwise any free port.
     */
    public static int findFreePort(final String addr, final int preferred) {
        if (isPortFree(addr, preferred))
            return preferred;
        else
            return findFreePort(addr);
    }

    /**
     * Returns a free port number on localhost.
     * 
     * @return a free port number on localhost, or -1 if unable to find a free port
     */
    public static int findFreePort() {
        return findFreePort(null);
    }

    public static int findFreePort(final String addr) {
        return checkPort(addr, 0);
    }

    public static boolean isPortFree(final String addr, final int port) {
        if (port <= 0)
            throw new IllegalArgumentException(port + " is negative");
        return checkPort(addr, port) == port;
    }

    // with code from from org/eclipse/jdt/launching/SocketUtil.java
    private static int checkPort(final String addr, final int port) {
        ServerSocket socket = null;
        try {
            // ATTN InetAddress.getByName(null) means an address of the loopback interface, while
            // passing null means any/all local addresses. The problem is that the constructor will
            // succeed for a given port even if it is already bound with the other address.
            socket = new ServerSocket(port, 0, InetAddress.getByName(addr));
            return socket.getLocalPort();
        } catch (IOException e) {
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                }
            }
        }
        return -1;
    }

    /**
     * Whether the passed address refers to this computer.
     * 
     * @param addr an ip or dns address, eg "192.168.28.52".
     * @return <code>true</code> if <code>addr</code> is bound to an interface of this computer.
     */
    static public final boolean isSelfAddr(String addr) {
        if (addr == null)
            return false;
        if (addr.startsWith("127.") || addr.startsWith("localhost"))
            return true;

        try {
            final Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
            while (en.hasMoreElements()) {
                final NetworkInterface ni = en.nextElement();
                final Enumeration<InetAddress> addresses = ni.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    final InetAddress inetAddress = addresses.nextElement();
                    if (addr.startsWith(inetAddress.getHostAddress()))
                        return true;
                }
            }
            return false;
        } catch (SocketException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static final HostnameVerifier HostnameNonVerifier = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    public static final String getHTTPContent(String address) {
        return getHTTPContent(address, false);
    }

    public static final String getHTTPContent(String address, final boolean dontVerify) {
        String content = "";
        OutputStream out = null;
        HttpsURLConnection conn = null;
        InputStream in = null;
        try {
            URL url = new URL(address);
            out = new ByteArrayOutputStream();
            conn = (HttpsURLConnection) url.openConnection();
            // Sur la connexion
            if (dontVerify) {
                conn.setHostnameVerifier(HostnameNonVerifier);
                // ou globalement
                // HttpsURLConnection.setDefaultHostnameVerifier(new CustomizedHostnameVerifier());
            }

            in = conn.getInputStream();
            final byte[] buffer = new byte[1024];
            int numRead;

            while ((numRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, numRead);

            }
            content = out.toString();

        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException ioe) {
            }
        }

        return content;
    }

    static public final String urlEncode(final String... kv) {
        return urlEncode(false, kv);
    }

    static public final String urlEncode(final boolean spaceAsPlus, final String... kv) {
        final int size = kv.length;
        if (size % 2 != 0)
            throw new IllegalArgumentException("Odd number of items : " + size);
        final LinkedHashMap<String, Object> map = new LinkedHashMap<>(size / 2, 1);
        for (int i = 0; i < size; i += 2) {
            map.put(kv[i], kv[i + 1]);
        }
        return urlEncode(map, spaceAsPlus);
    }

    /**
     * Encode pairs in application/x-www-form-urlencoded format. Although both the
     * <a href="https://url.spec.whatwg.org/#urlencoded-serializing">URL standard</a> and the
     * <a href="https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">HTML standard</a>
     * specifies <code>spaceAsPlus</code> some applications expect the opposite (e.g. POST with
     * jetty).
     * 
     * @param map the values to encode.
     * @param spaceAsPlus <code>true</code> to use '+' for a space, <code>false</code> to use '%20'.
     * @return the encoded string.
     * @see #percentEncode(String, String, boolean)
     */
    static public final String urlEncode(final Map<String, ?> map, final boolean spaceAsPlus) {
        if (map.isEmpty())
            return "";
        final String charset = StandardCharsets.UTF_8.name();
        final StringBuilder sb = new StringBuilder(256);
        for (final Entry<String, ?> e : map.entrySet()) {
            final Object value = e.getValue();
            // Avoid null and "null" confusion.
            if (value != null) {
                try {
                    sb.append(percentEncode(e.getKey(), charset, spaceAsPlus));
                    sb.append('=');
                    sb.append(percentEncode(String.valueOf(value), charset, spaceAsPlus));
                    sb.append('&');
                } catch (UnsupportedEncodingException exn) {
                    throw new IllegalStateException("UTF-8 should be standard", exn);
                }
            }
        }
        // remove last '&'
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    static private final Pattern SPACE_PATTERN = Pattern.compile("+", Pattern.LITERAL);

    /**
     * Percent encode a string.
     * 
     * @param s the string to encode.
     * @param charset the charset to use.
     * @param spaceAsPlus <code>true</code> to use '+' for a space (as per
     *        <a href="https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">HTML</a> and
     *        <a href=
     *        "https://url.spec.whatwg.org/#urlencoded-serializing">application/x-www-form-urlencoded</a>
     *        of URL Standard), <code>false</code> to use '%20' (as per
     *        <a href="https://url.spec.whatwg.org/#path-state">Path part</a> of URL Standard).
     * @return the encoded string.
     * @throws UnsupportedEncodingException if charset is unknown.
     * @see <a href="https://url.spec.whatwg.org/#string-percent-encode-after-encoding">URL
     *      standard</a>
     */
    static public final String percentEncode(final String s, final String charset, final boolean spaceAsPlus) throws UnsupportedEncodingException {
        final String withPlus = URLEncoder.encode(s, charset);
        if (spaceAsPlus)
            return withPlus;
        return SPACE_PATTERN.matcher(withPlus).replaceAll("%20");
    }

    // Create a trust manager that does not validate certificate chains
    static private final TrustManager[] TRUSTALL_MANAGERS = new TrustManager[] { new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    } };

    static public final SSLContext createTrustAllContext() throws KeyManagementException, NoSuchAlgorithmException {
        final SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, TRUSTALL_MANAGERS, new java.security.SecureRandom());
        return sc;
    }
}