/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.interchange.mws; import java.net.URI; import java.net.URLEncoder; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import divconq.util.Base64; import divconq.util.StringUtil; /** * Cherry picked a few methods from Amazon's MWS. Let's keep things simple :) * * @author Andy White */ public class Util { /** Match "%2F". */ private static final Pattern pct2FPtn = Pattern.compile("%2F", Pattern.LITERAL); /** Match "%7E". */ private static final Pattern pct7EPtn = Pattern.compile("%7E", Pattern.LITERAL); /** Match a + character. */ private static final Pattern plusPtn = Pattern.compile("+", Pattern.LITERAL); /** Match an asterisk character. */ private static final Pattern asteriskPtn = Pattern.compile("*", Pattern.LITERAL); /** * Computes RFC 2104-compliant HMAC signature. * * @param data * The data to sign. * @param key * The key to use for signing. * * @param algorithm * The signing algorithm. * * @return The signature. */ public static String sign(String data, String key, String algorithm) { try { Mac mac = Mac.getInstance(algorithm); mac.init(new SecretKeySpec(key.getBytes("UTF-8"), algorithm)); return Base64.encodeToString(mac.doFinal(data.getBytes("UTF-8")), false); } catch (Exception x) { return null; } } /** * Determine if a url uses the standard port. * Port 80 for http, 443 for https. * * @param uri * * @return true if standard port is used. */ public static boolean usesStandardPort(URI uri) { int portNumber = uri.getPort(); if (portNumber == -1) return true; int standardPort = uri.getScheme().equals("https") ? 443 : 80; return (portNumber == standardPort); } /** * URL encode a value. * * @param value * * @param path * true if is a path and '/' should not be encoded. * * @return The encoded string. */ public static String urlEncode(String value, boolean path) { try { value = URLEncoder.encode(value, "UTF-8"); value = replaceAll(value, plusPtn, "%20"); value = replaceAll(value, asteriskPtn, "%2A"); value = replaceAll(value, pct7EPtn, "~"); if (path) value = replaceAll(value, pct2FPtn, "/"); } catch (Exception x) { } return value; } /** * Replace a pattern in a string. * <p> * Do not do recursive replacement. Return the original string if no changes * are required. * * @param s * The string to search. * * @param p * The pattern to search for. * * @param r * The string to replace occurrences with. * * @return The new string. */ static String replaceAll(String s, Pattern p, String r) { if (StringUtil.isEmpty(s)) return s; Matcher m = p.matcher(s); if (!m.find()) return s; int k = 0; int n = s.length(); StringBuilder buf = new StringBuilder(n + 12); do { buf.append(s, k, m.start()); buf.append(r); k = m.end(); } while (m.find()); if (k < n) buf.append(s, k, n); return buf.toString(); } }