/******************************************************************************* * Copyright (c) 2013 Cloud Bees, Inc. * All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cloud Bees, Inc. - initial API and implementation *******************************************************************************/ package com.cloudbees.eclipse.core.util; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URL; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.params.AuthPolicy; import org.apache.http.client.params.CookiePolicy; import org.apache.http.client.params.HttpClientParams; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import com.cloudbees.eclipse.core.CloudBeesCorePlugin; import com.cloudbees.eclipse.core.CloudBeesException; import com.cloudbees.eclipse.core.XMLReplace; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Utils { public static Gson createGson() { final GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.serializeSpecialFloatingPointValues(); gsonBuilder.serializeNulls(); // gsonBuilder.setPrettyPrinting(); // temporary // gsonBuilder.excludeFieldsWithoutExposeAnnotation(); Gson g = gsonBuilder.create(); return g; } /** * Converts string to US-ASCII base64 string. * * @param str * @return */ public static String toB64(final String str) { try { if (str == null || str.length() == 0) { return new String(new byte[0], "US-ASCII"); } return new String(Base64.encodeBase64(str.getBytes("UTF-8")), "US-ASCII"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Converts string from base64 string. * * @param str * @return */ public static String fromB64(final String str) { try { if (str == null || str.length() == 0) { return new String(new byte[0], "UTF-8"); } return new String(Base64.decodeBase64(str.getBytes("US-ASCII"))); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public final static String readString(final InputStream is) throws CloudBeesException { if (is == null) { return null; } try { Writer writer = new StringWriter(); char[] buffer = new char[1024]; try { Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); int n; while ((n = reader.read(buffer)) != -1) { writer.write(buffer, 0, n); } } finally { is.close(); } return writer.toString(); } catch (Exception e) { throw new CloudBeesException("Failed to read inputstream", e); } } /** * @param url * url to connec. Required to determine proxy settings if available. If <code>null</code> then proxy is not * configured for the client returned. * @return * @throws CloudBeesException */ public final static DefaultHttpClient getAPIClient(String url) throws CloudBeesException { DefaultHttpClient httpclient = new DefaultHttpClient(); try { HttpClientParams.setCookiePolicy(httpclient.getParams(), CookiePolicy.BROWSER_COMPATIBILITY); String version = null; if (CloudBeesCorePlugin.getDefault() != null) { version = CloudBeesCorePlugin.getDefault().getBundle().getVersion().toString(); } else { version = "n/a"; } HttpProtocolParams.setUserAgent(httpclient.getParams(), "CBEclipseToolkit/" + version); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); CloudBeesCorePlugin plugin = CloudBeesCorePlugin.getDefault(); URL truststore; if (plugin == null) { //Outside the OSGI environment, try to open the stream from the current dir. truststore = new File("truststore").toURI().toURL(); } else { truststore = plugin.getBundle().getResource("truststore"); } InputStream instream = truststore.openStream(); try { trustStore.load(instream, "123456".toCharArray()); } finally { instream.close(); } TrustStrategy trustAllStrategy = new TrustStrategy() { @Override public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { return true; } }; SSLSocketFactory socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, null, null, trustStore, null, trustAllStrategy, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); // Override https handling to use provided truststore @SuppressWarnings("deprecation") Scheme sch = new Scheme("https", socketFactory, 443); httpclient.getConnectionManager().getSchemeRegistry().register(sch); HttpParams params = httpclient.getParams(); //TODO Make configurable from the UI? HttpConnectionParams.setConnectionTimeout(params, 10000); HttpConnectionParams.setSoTimeout(params, 10000); if (CloudBeesCorePlugin.getDefault() != null) { // exclude proxy support when running outside eclipse IProxyService ps = CloudBeesCorePlugin.getDefault().getProxyService(); if (ps.isProxiesEnabled()) { IProxyData[] pr = ps.select(new URI(url)); //NOTE! For now we use just the first proxy settings with type HTTP or HTTPS to try out the connection. If configuration has more than 1 conf then for now this likely won't work! if (pr != null) { for (int i = 0; i < pr.length; i++) { IProxyData prd = pr[i]; if (IProxyData.HTTP_PROXY_TYPE.equals(prd.getType()) || IProxyData.HTTPS_PROXY_TYPE.equals(prd.getType())) { String proxyHost = prd.getHost(); int proxyPort = prd.getPort(); String proxyUser = prd.getUserId(); String proxyPass = prd.getPassword(); HttpHost proxy = new HttpHost(proxyHost, proxyPort); httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); if (prd.isRequiresAuthentication()) { List authpref = new ArrayList(); authpref.add(AuthPolicy.BASIC); AuthScope authScope = new AuthScope(proxyHost, proxyPort); httpclient.getCredentialsProvider().setCredentials(authScope, new UsernamePasswordCredentials(proxyUser, proxyPass)); } break; } } } } } /* httpclient.getHostConfiguration().setProxy(proxyHost,proxyPort); //if there are proxy credentials available, set those too Credentials proxyCredentials = null; String proxyUser = beesClientConfiguration.getProxyUser(); String proxyPassword = beesClientConfiguration.getProxyPassword(); if(proxyUser != null || proxyPassword != null) proxyCredentials = new UsernamePasswordCredentials(proxyUser, proxyPassword); if(proxyCredentials != null) client.getState().setProxyCredentials(AuthScope.ANY, proxyCredentials); */ return httpclient; } catch (Exception e) { throw new CloudBeesException("Error while initiating access to JSON APIs!", e); } } public static HttpPost jsonRequest(final String url, final Object req) throws UnsupportedEncodingException { HttpPost post = new HttpPost(url); Gson g = Utils.createGson(); String json = g.toJson(req); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); //System.out.println("JSON REQUEST STRING " + json); StringEntity se = new StringEntity(json); post.setEntity(se); return post; } public final static String getResponseBody(final HttpResponse resp) throws CloudBeesException { try { return Utils.readString(resp.getEntity().getContent()); } catch (IllegalStateException e) { throw new CloudBeesException("Failed to read response", e); } catch (IOException e) { throw new CloudBeesException("Failed to read response", e); } } public final static void checkResponseCode(final HttpResponse resp) throws CloudBeesException { checkResponseCode(resp, false); } public final static void checkResponseCode(final HttpResponse resp, final boolean expectCIRedirect, final boolean jenkinsAtCloud) throws CloudBeesException { int responseStatus = resp.getStatusLine().getStatusCode(); Header firstHeader = resp.getFirstHeader("Location"); if (expectCIRedirect && (responseStatus == 302 || responseStatus == 301) && firstHeader != null && firstHeader.getValue() != null) { //FIXME ugly way to detect a normal redirect within the site that does not redirect to signon but no good idea for better implementation if (!jenkinsAtCloud || firstHeader.getValue().indexOf(".ci.") > 0) { return; } } if (responseStatus == 302 || responseStatus == 301) { throw new CloudBeesException("Authentication required! Either wrong or no credentials were provided! Reason:" + resp.getStatusLine().getReasonPhrase()); } if (responseStatus != 200 && responseStatus != 201 && responseStatus != 202) { throw new CloudBeesException("Unexpected response code:" + responseStatus + ". Message:" + resp.getStatusLine().getReasonPhrase()); } } public final static void checkResponseCode(final HttpResponse resp, final boolean expectCIRedirect) throws CloudBeesException { int responseStatus = resp.getStatusLine().getStatusCode(); Header firstHeader = resp.getFirstHeader("Location"); if (expectCIRedirect && (responseStatus == 302 || responseStatus == 301) && firstHeader != null && firstHeader.getValue() != null) { //FIXME ugly way to detect a normal redirect within the site that does not redirect to signon but no good idea for better implementation if (firstHeader.getValue().indexOf(".ci.") > 0) { return; } } if (responseStatus == 302 || responseStatus == 301) { throw new CloudBeesException("Authentication required! Either wrong or no credentials were provided! Reason:" + resp.getStatusLine().getReasonPhrase()); } if (responseStatus != 200) { throw new CloudBeesException("Unexpected response code:" + responseStatus + ". Message:" + resp.getStatusLine().getReasonPhrase()); } } public static String humanReadableTime(final long duration) { String unit = ""; long mins = duration / (60L * 1000); long hr = mins / 60; long yrs = hr / 24 / 365; if (mins <= 0) { long secs = duration / 1000; unit = secs + " sec"; if (secs > 1) { unit = unit + "s"; } } else if (mins < 60) { unit = mins + " min"; if (mins > 1) { unit = unit + "s"; } } else if (mins < 60 * 24) { //long newmins = mins - (hr * 60); unit = hr + " hr" + (hr > 1 ? "s" : "");/* + " " + newmins + " min" + (newmins > 1 ? "s" : "");*/ } else if (yrs < 1) { long days = hr / 24L; unit = days + " day" + (days > 1 ? "s" : "")/* + ", " + hr + " hr" + (hr > 1 ? "s" : "")*/; } else { unit = yrs + " year" + (yrs > 1 ? "s" : ""); } return unit; } public static <T> T createInstance(final Class<T> clazz, final Class<?>[] types, final Object[] params) { try { Constructor<T> cnst; cnst = clazz.getConstructor(types); return cnst.newInstance(params); } catch (SecurityException e) { } catch (NoSuchMethodException e) { } catch (IllegalArgumentException e) { } catch (InstantiationException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } return null; } public static String createEmptyConfig(String description) throws Exception { XMLReplace xmlReplace = XMLReplace.getInstance(CloudBeesCorePlugin.getEmptyConfigXML()); return xmlReplace.addReplacement("description", description).replaceToString(); } public static String createSCMConfig(String description, String url) throws Exception { XMLReplace xmlReplace = XMLReplace.getInstance(CloudBeesCorePlugin.getSCMConfigXML()); return xmlReplace.addReplacement("description", description).addReplacement("remote", url).replaceToString(); } public static HttpGet jsonGetRequest(String url) { HttpGet get = new HttpGet(url); get.setHeader("Accept", "application/json"); get.setHeader("Content-type", "application/json"); return get; } }