package org.joget.commons.util; import java.math.BigInteger; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.validator.EmailValidator; import org.json.simple.JSONObject; import org.jsoup.Jsoup; import org.jsoup.safety.Whitelist; import org.springframework.util.ReflectionUtils; import javax.mail.internet.MimeUtility; /** * Utility methods for String processing * */ public class StringUtil { public static final String TYPE_REGEX = "regex"; public static final String TYPE_JSON = "json"; public static final String TYPE_JAVASCIPT = "javascript"; public static final String TYPE_HTML = "html"; public static final String TYPE_XML = "xml"; public static final String TYPE_JAVA = "java"; public static final String TYPE_SQL = "sql"; public static final String TYPE_URL = "url"; static final Whitelist whitelistRelaxed; static { // configure jsoup whitelist whitelistRelaxed = Whitelist.relaxed().addTags("span", "div").addAttributes(":all","id","style","class","title","target", "name"); java.lang.reflect.Field field = ReflectionUtils.findField(whitelistRelaxed.getClass(), "protocols"); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, whitelistRelaxed, new HashMap()); } /** * Method used to properly encode the parameters in a URL string * @param url * @return */ public static String encodeUrlParam(String url) { String urlResult = url; try { String[] urlPart = urlResult.split("\\?"); urlResult = urlPart[0]; if (urlPart.length > 1) { urlResult += "?" + constructUrlQueryString(getUrlParams(urlPart[1])); } } catch (Exception e) { LogUtil.error(StringUtil.class.getName(), e, ""); } return urlResult; } /** * Method used to merge 2 query string. If same parameter found, the one from * second query string will override the first query string. * @param queryString1 * @param queryString2 * @return */ public static String mergeRequestQueryString(String queryString1, String queryString2) { if (queryString1 == null || queryString2 == null) { return queryString1; } Map<String, String[]> params = getUrlParams(queryString1); params.putAll(getUrlParams(queryString2)); return constructUrlQueryString(params); } /** * Add parameter and its value to url. Override the value if the parameter * is exist in the url * @param url * @param paramKey * @param paramValue * @return */ public static String addParamsToUrl(String url, String paramKey, String paramValue) { return addParamsToUrl(url, paramKey, new String[]{paramValue}); } /** * Add parameter and its values to url. Override the value if the parameter * is exist in the url * @param url * @param paramKey * @param paramValues * @return */ public static String addParamsToUrl(String url, String paramKey, String[] paramValues) { Map<String, String[]> params = new HashMap<String, String[]>(); params.put(paramKey, paramValues); return addParamsToUrl(url, params); } /** * Add parameters and its values to url. Override the value if the parameter * is exist in the url * @param url * @param params * @return */ public static String addParamsToUrl(String url, Map<String, String[]> params) { String urlResult = url; try { String[] urlPart = urlResult.split("\\?"); urlResult = urlPart[0]; Map<String, String[]> resultParams = new HashMap<String, String[]>(); if (urlPart.length > 1) { resultParams.putAll(getUrlParams(urlPart[1])); } resultParams.putAll(params); if (!resultParams.isEmpty()) { urlResult += "?" + constructUrlQueryString(resultParams); } } catch (Exception e) { LogUtil.error(StringUtil.class.getName(), e, ""); } return urlResult; } /** * Converts all request parameters in url to a map * @param url * @return */ public static Map<String, String[]> getUrlParams(String url) { Map<String, String[]> result = new HashMap<String, String[]>(); try { String queryString = url; if (url.contains("?")) { queryString = url.substring(url.indexOf("?") + 1); } if (queryString.length() > 1) { String[] params = queryString.split("&"); for (String a : params) { if (!a.isEmpty()) { String[] param = a.split("="); String key = URLDecoder.decode(param[0], "UTF-8"); String value = ""; if (param.length > 1 && !param[1].isEmpty()) { value = URLDecoder.decode(param[1], "UTF-8"); } String[] values = (String[]) result.get(key); if (values != null) { List temp = Arrays.asList(values); temp.add(value); values = (String[]) temp.toArray(new String[0]); } else { values = new String[]{value}; } result.put(key, values); } } } } catch (Exception e) { } return result; } /** * Builds a query string based on parameters and its values * @param params * @return */ public static String constructUrlQueryString(Map<String, String[]> params) { String queryString = ""; try { for (String key : params.keySet()) { String[] values = params.get(key); for (String value : values) { queryString += URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8") + "&"; } } if (queryString.endsWith("&")) { queryString = queryString.substring(0, queryString.length()-1); } } catch (Exception e) { } return queryString; } /** * Decodes provided url * @param url * @return */ public static String decodeURL(String url) { try { url = URLDecoder.decode(url, "UTF-8"); } catch (Exception e) { } return url; } /** * Escape regex syntax in a string * @param inStr * @return */ public static String escapeRegex(String inStr) { return (inStr != null) ? inStr.replaceAll("([\\\\*+\\[\\](){}\\$.?\\^|])", "\\\\$1") : null; } /** * Escape a string based on format and replaced string based on the replace keyword map * @param inStr input String * @param format TYPE_HTML, TYPE_JAVA, TYPE_JAVASCIPT, TYPE_JSON, TYPE_SQL, TYPE_XML, TYPE_URL or TYPE_REGEX. Support chain escaping by separate the format in semicolon (;) * @param replaceMap A map of keyword and new keyword pair to be replaced before escaping * @return */ public static String escapeString(String inStr, String format, Map<String, String> replaceMap) { if (replaceMap != null) { Iterator it = replaceMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> pairs = (Map.Entry<String, String>) it.next(); inStr = inStr.replaceAll(pairs.getKey(), escapeRegex(pairs.getValue())); } } if (format == null || inStr == null) { return inStr; } String[] formats = format.split(";"); for (String f : formats) { if (TYPE_REGEX.equals(f)) { inStr = escapeRegex(inStr); } else if (TYPE_JSON.equals(f)) { inStr = JSONObject.escape(inStr); } else if (TYPE_JAVASCIPT.equals(f)) { inStr = StringEscapeUtils.escapeJavaScript(inStr); } else if (TYPE_HTML.equals(f)) { inStr = StringEscapeUtils.escapeHtml(inStr); } else if (TYPE_XML.equals(f)) { inStr = StringEscapeUtils.escapeXml(inStr); } else if (TYPE_JAVA.equals(f)) { inStr = StringEscapeUtils.escapeJava(inStr); } else if (TYPE_SQL.equals(f)) { inStr = StringEscapeUtils.escapeSql(inStr); } else if (TYPE_URL.equals(f)) { try { inStr = URLEncoder.encode(inStr, "UTF-8"); } catch (Exception e) {/* ignored */} } } return inStr; } /** * A comparator to compare string value with letter case ignored */ public class IgnoreCaseComparator implements Comparator<String> { /** * Compare 2 strings with letter case ignored * @param strA * @param strB * @return */ public int compare(String strA, String strB) { return strA.compareToIgnoreCase(strB); } } /** * Encrypt the content with MD5 * @param content * @return */ public static String md5(String content) { try { MessageDigest m = MessageDigest.getInstance("MD5"); byte[] data = content.getBytes(); m.update(data, 0, data.length); BigInteger i = new BigInteger(1, m.digest()); return String.format("%1$032X", i); } catch (Exception ex) { LogUtil.error(StringUtil.class.getName(), ex, ""); } return ""; } /** * Encrypt the content with MD5 base16 * @param content * @return */ public static String md5Base16(String content) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(content.getBytes()); StringBuffer sb = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; String hex = Integer.toHexString((int) 0x00FF & b); if (hex.length() == 1) { sb.append("0"); } sb.append(hex); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Remove all HTML tags from the content * @param content * @return */ public static String stripAllHtmlTag(String content) { if (content != null && !content.isEmpty()) { content = Jsoup.clean(content, Whitelist.none()); } return content; } /** * Removed all HTML tags not in the allowed map from the content * @param content * @param allowedTag * @return */ public static String stripHtmlTag(String content, String[] allowedTag) { if (content != null && !content.isEmpty()) { Whitelist whitelist = Whitelist.none().addAttributes(":all","style","class","title","id","src","href","target"); for (String tag : allowedTag) { whitelist.addTags(tag); } java.lang.reflect.Field field = ReflectionUtils.findField(whitelist.getClass(), "protocols"); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, whitelist, new HashMap()); content = Jsoup.clean(content, whitelist); } return content; } /** * Remove script and unknown tag from the content * @param content * @return */ public static String stripHtmlRelaxed(String content) { if (content != null && content.indexOf("<") >= 0) { content = Jsoup.clean(content, whitelistRelaxed); } return content; } /** * Encrypt all keywords in the content which wrapped in SecurityUtil.ENVELOPE * with SecurityUtil.encrypt method * @param content * @return */ public static String encryptContent(String content) { //parse content if (content != null && content.contains(SecurityUtil.ENVELOPE)) { Pattern pattern = Pattern.compile(SecurityUtil.ENVELOPE + "((?!" + SecurityUtil.ENVELOPE + ").)*" + SecurityUtil.ENVELOPE); Matcher matcher = pattern.matcher(content); Set<String> sList = new HashSet<String>(); while (matcher.find()) { sList.add(matcher.group(0)); } try { if (!sList.isEmpty()) { for (String s : sList) { String tempS = s.replaceAll(SecurityUtil.ENVELOPE, ""); tempS = SecurityUtil.encrypt(tempS); content = content.replaceAll(s, tempS); } } } catch (Exception ex) { LogUtil.error(StringUtil.class.getName(), ex, ""); } } return content; } /** * Decrypt all keywords in the content which wrapped in SecurityUtil.ENVELOPE * with SecurityUtil.decrypt method * @param content * @return */ public static String decryptContent(String content) { //parse content if (content != null && content.contains(SecurityUtil.ENVELOPE)) { Pattern pattern = Pattern.compile(SecurityUtil.ENVELOPE + "((?!" + SecurityUtil.ENVELOPE + ").)*" + SecurityUtil.ENVELOPE); Matcher matcher = pattern.matcher(content); Set<String> sList = new HashSet<String>(); while (matcher.find()) { sList.add(matcher.group(0)); } try { if (!sList.isEmpty()) { for (String s : sList) { String tempS = SecurityUtil.decrypt(s); content = content.replaceAll(StringUtil.escapeRegex(s), tempS); } } } catch (Exception ex) { LogUtil.error(StringUtil.class.getName(), ex, ""); } } return content; } /** * Search a keyword and replace it with a new keyword in byte content * @param bytes * @param search * @param replacement * @return */ public static byte[] searchAndReplaceByteContent(byte[] bytes, String search, String replacement) { if (search != null && replacement != null) { try { String content = new String(bytes, "UTF-8"); content = content.replaceAll(search, replacement); bytes = content.getBytes("UTF-8"); } catch (Exception e) { //ignore } } return bytes; } /** * Search keywords and replace it with corresponding new keyword in byte content * @param bytes * @param replacements * @return */ public static byte[] searchAndReplaceByteContent(byte[] bytes, Map<String, String> replacements) { if (replacements != null && !replacements.isEmpty()) { try { String content = new String(bytes, "UTF-8"); for (String search : replacements.keySet()) { content = content.replaceAll(search, replacements.get(search)); } bytes = content.getBytes("UTF-8"); } catch (Exception e) { //ignore } } return bytes; } /** * Method used for validate an email. Options to validate multiple email separated * by semicolon (;) * @param email * @param multiple * @return */ public static boolean validateEmail(String email, boolean multiple) { String[] emails; if (multiple) { emails = email.split(";"); } else { emails = new String[]{email}; } EmailValidator validator = EmailValidator.getInstance(); boolean valid = true; for (String e : emails) { if (!validator.isValid(e.trim())) { valid = false; break; } } return valid; } /** * Method used for encode personal name in an email. * by semicolon (;) * @param email * @param multiple * @return */ public static String encodeEmail(String email) { if (email.contains("<") && email.contains(">")) { try { email = MimeUtility.encodeWord(email.substring(0, email.indexOf("<")), "UTF-8", null) + email.substring(email.indexOf("<")); } catch (Exception e) { LogUtil.debug(StringUtil.class.getName(), "Not able to encode " + email); } } return email; } }