package helpers; import java.net.URLEncoder; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import models.openfire.User; import org.owasp.html.HtmlPolicyBuilder; import org.owasp.html.PolicyFactory; import play.Logger; import play.libs.F.Promise; import play.libs.ws.WS; import play.libs.ws.WSResponse; import com.typesafe.config.ConfigFactory; public class ContentHelper { private final static String HTTP_YOUTU_BE = "http://youtu.be"; private final static String HTTPS_YOUTU_BE = "https://youtu.be"; private final static String HTTP_WWW_YOUTUBE_COM = "http://www.youtube.com"; private final static String HTTPS_WWW_YOUTUBE_COM = "https://www.youtube.com"; private final static String HTTPS_WWW_YOUTUBE_COM_EMBED = "https://www.youtube.com/embed"; private String content; private final static Pattern URL_PATTERN = Pattern.compile("\\b(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); private final static int MAX_GET_TIMEOUT = 2500; private final static String USER_URL_TEMPLATE = ConfigFactory.load().getString("user.url.template"); private final static String[] INVALID_USERNAME_PARTS = {":", ","}; public ContentHelper(String content) { this.content = content; } public static String prepare(String content) { if (content == null) { return content; } ContentHelper ch = new ContentHelper(content.trim()); return ch.renderHtml().detectUsers().sanitize().detectLinks().normalize().toString(); } public ContentHelper normalize() { content = content.replace("\n", "
\n"); return this; } public ContentHelper renderHtml() { content = content.replace("<", "<").replace(">", ">"); return this; } public ContentHelper sanitize() { PolicyFactory policy = new HtmlPolicyBuilder() .allowUrlProtocols("http") .allowUrlProtocols("https") .allowElements("a") .allowElements("img") .allowElements("br") .allowAttributes("href") .onElements("a") .allowAttributes("target") .onElements("a") .allowAttributes("src") .onElements("img") .requireRelNofollowOnLinks() .toFactory(); content = policy.sanitize(content); return this; } public ContentHelper detectLinks() { String urlStr, oldLine; StringBuilder contentBuilder = new StringBuilder(); for (String line : content.split("\n")) { oldLine = line.trim(); Matcher m = URL_PATTERN.matcher(line); while (m.find()) { urlStr = m.group(); Logger.debug("found link |" + urlStr + "|"); line = line.replace(urlStr, embedLink(getUrl(urlStr))); if(urlStr.startsWith(HTTP_WWW_YOUTUBE_COM) || urlStr.startsWith(HTTPS_WWW_YOUTUBE_COM)) { Logger.debug("YOUTUBE_COM: "+line); line += "
"+embedYT(getUrl(urlStr)); } if(urlStr.startsWith(HTTP_YOUTU_BE) || urlStr.startsWith(HTTPS_YOUTU_BE)) { Logger.debug("YOUTU_BE: "+line); line += "
"+embedYT(convertShortYTUrl(urlStr)); } if(getUrl(oldLine).equals(urlStr)) { line += "
"+embedImage(getUrl(urlStr)); } } contentBuilder.append(line.trim()); contentBuilder.append("\n"); } content = contentBuilder.toString(); return this; } public ContentHelper detectUsers() { StringBuilder contentBuilder = new StringBuilder(); for (String line : content.split("\n")) { contentBuilder = detectUser(contentBuilder, line); } content = contentBuilder.toString(); return this; } private String getUrl(String url) { try { return URLDecoder.decode(url, "UTF-8"); } catch(java.lang.IllegalArgumentException ex) { Logger.warn(ex.getLocalizedMessage(), ex); } catch(java.io.UnsupportedEncodingException ex) { Logger.warn(ex.getLocalizedMessage(), ex); } String[] find = {"=", "%3A", "%2F"}; String[] replace = {"=", ":", "/"}; for(int i=0; i < find.length; i++) { url.replace(find[i], replace[i]); } return url; } private StringBuilder detectUser(StringBuilder contentBuilder, String line){ int start, end; String user, cleanuser; if (line.contains(" @") || line.startsWith("@")) { start = line.indexOf(" @") + 1; if (start < 0 || line.startsWith("@")) start = 0; end = Math.min(line.substring(start).indexOf(" ")+start, line.length()); if (end < start) end = line.length(); if(end > start){ user = line.substring(start + 1, end); cleanuser = user.trim().toLowerCase(); for(String part : INVALID_USERNAME_PARTS){ cleanuser = cleanuser.replace(part, ""); } Logger.debug("found user |" + user + "| " + start + "-" + end); if(User.exists(cleanuser)) { contentBuilder.append(line.substring(0, start)).append("@"+user+" "); } else { contentBuilder.append(line.trim()); } }else { Logger.warn("false user in: "+line.trim()); contentBuilder.append(line.trim()); } return detectUser(contentBuilder, line.substring(Math.min(end+1, line.length()))); } else { contentBuilder.append(line.trim()); } return contentBuilder; } public String toString() { return this.content; } private String embedLink(String urlStr) { return "" + urlStr + ""; } private String embedYT(String urlStr) { Map paras = getParametersFromUrl(urlStr); if(paras.containsKey("v")) { return ""; } return ""; } private String embedImage(String urlStr) { try { //urlStr = URLEncoder.encode(urlStr, "UTF-8"); Promise response = WS.url(urlStr).get(); try { if (response.get(MAX_GET_TIMEOUT).getHeader("Content-Type").startsWith("image")) { return ""; } } catch (Exception ex) { Logger.warn(ex.getLocalizedMessage()); } } catch (Exception ex) { Logger.warn(ex.getLocalizedMessage()); } return ""; } private static String buildEmbeddedYTUrl(Map paras){ StringBuilder sb = new StringBuilder(HTTPS_WWW_YOUTUBE_COM_EMBED); if(paras.containsKey("v")){ sb.append("/"+paras.get("v")); } if(paras.containsKey("t")){ sb.append("?start="+paras.get("t").replace("s", "")); } return sb.toString(); } private static String convertShortYTUrl(String shortYTUrl) { Map map = new HashMap(); shortYTUrl = shortYTUrl.replace(HTTPS_YOUTU_BE, "").replace(HTTP_YOUTU_BE, "").replace("=", "=").trim(); String params[] = shortYTUrl.split("[/,?]"); for (String param : params) { try { if(param.split("=").length == 2) { map.put(param.split("=")[0], param.split("=")[1]); } else if(!param.isEmpty()) { map.put("v", param); } } catch (Exception e) { Logger.warn("No value for parameter "+param+" in shortYTUrl "+shortYTUrl); } } return HTTPS_WWW_YOUTUBE_COM+"/watch?v="+map.get("v")+"&t="+map.get("t"); } private static Map getParametersFromUrl(String url) { Map map = new HashMap(); if (url != null) { String[] params = url.trim().split("[&,?]"); for (String param : params) { try { String name = param.split("=")[0]; String value = param.split("=")[1]; map.put(name, value); } catch (Exception e) { Logger.warn("No value for parameter "+param+" in "+url); } } } return map; } }