package jenkins.plugins.zulip; import java.io.IOException; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.nio.charset.Charset; import java.util.logging.Level; import java.util.logging.Logger; import hudson.ProxyConfiguration; import hudson.util.Secret; import jenkins.model.Jenkins; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.util.EncodingUtil; /** * Sends message to Zulip stream */ public class Zulip { private static final Charset encodingCharset = Charset.forName("UTF-8"); private String url; private String email; private String apiKey; private static final Logger LOGGER = Logger.getLogger(Zulip.class.getName()); public Zulip(String url, String email, Secret apiKey) { super(); if (url != null && url.length() > 0 && !url.endsWith("/") ) { url = url + "/"; } this.url = url; this.email = email; this.apiKey = Secret.toString(apiKey); } /** * Configures proxy connection on {@link HttpClient} based on Jenkins settings * * @param httpClient */ protected void configureProxy(HttpClient httpClient) throws MalformedURLException { LOGGER.log(Level.FINE, "Setting up HttpClient proxy"); ProxyConfiguration proxyConfiguration = Jenkins.getInstance().proxy; if (proxyConfiguration != null && ZulipUtil.isValueSet(proxyConfiguration.name)) { URL urlObj = new URL(url); Proxy proxy = proxyConfiguration.createProxy(urlObj.getHost()); if (proxy != Proxy.NO_PROXY) { // Set proxy on http client InetSocketAddress addr = (InetSocketAddress) proxy.address(); LOGGER.log(Level.FINE, "Using configured Jenkins proxy host: {0}, port: {1}", new Object[] {addr.getHostName(), addr.getPort()} ); httpClient.getHostConfiguration().setProxy(addr.getHostName(), addr.getPort()); // Setup user name password credentials if (ZulipUtil.isValueSet(proxyConfiguration.getUserName())) { LOGGER.log(Level.FINE, "Using proxy authentication username: {0}, password: ******", proxyConfiguration.getUserName()); httpClient.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyConfiguration.getUserName(), proxyConfiguration.getPassword())); } } else { LOGGER.log(Level.FINE, "Target url {0} is a no proxy host", url); } } else { LOGGER.fine("Proxy not configured for the Jenkins instance"); } } protected HttpClient getClient() throws MalformedURLException { HttpClient client = new HttpClient(); // TODO: It would be nice if this version number read from the Maven XML file // (which is possible, but annoying) // http://stackoverflow.com/questions/8829147/maven-version-number-in-java-file client.getParams().setParameter("http.useragent", "ZulipJenkins/0.1.2"); configureProxy(client); return client; } protected String getApiEndpoint() { if (this.url.length() > 0) { return this.url + "api/v1/"; } return "https://api.zulip.com/v1/"; } public String getApiKey() { return this.apiKey; } public String getEmail() { return this.email; } public String post(String method, NameValuePair[] parameters) { PostMethod post = new PostMethod(getApiEndpoint() + method); post.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE); String auth_info = this.getEmail() + ":" + this.getApiKey(); String encoded_auth = new String(Base64.encodeBase64(auth_info.getBytes(encodingCharset)), encodingCharset); post.setRequestHeader("Authorization", "Basic " + encoded_auth); try { post.setRequestBody(EncodingUtil.formUrlEncode(parameters, Charset.defaultCharset().name())); HttpClient client = getClient(); client.executeMethod(post); String response = post.getResponseBodyAsString(); if (post.getStatusCode() != HttpStatus.SC_OK) { StringBuilder params = new StringBuilder(); for (NameValuePair pair: parameters) { params.append("\n").append(pair.getName()).append(":").append(pair.getValue()); } LOGGER.log(Level.SEVERE, "Error sending Zulip message:\n" + response + "\n\n" + "We sent:" + params.toString()); } return response; } catch (IOException e) { LOGGER.log(Level.SEVERE, "Error sending Zulip message: ", e); } finally { post.releaseConnection(); } return null; } public String sendStreamMessage(String stream, String subject, String message) { NameValuePair[] body = {new NameValuePair("api-key", this.getApiKey()), new NameValuePair("email", this.getEmail()), new NameValuePair("type", "stream"), new NameValuePair("to", stream), new NameValuePair("subject", subject), new NameValuePair("content", message)}; return post("messages", body); } }