/* * Copyright (C) 2008-2017, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.juick.util; import com.juick.Message; import com.juick.Tag; import com.juick.User; import org.apache.commons.codec.CharEncoding; import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.UriComponentsBuilder; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Created by aalexeev on 11/13/16. */ public class MessageUtils { private MessageUtils() { throw new IllegalStateException(); } public static String formatQuote(final String quote) { String result = quote; if (quote != null) { if (quote.length() > 50) { result = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; } else if (!quote.isEmpty()) { result = ">" + quote.replace('\n', ' ') + "\n"; } } return result; } private final static String urlWhiteSpacePrefix = "((?<=\\s)|(?<=\\A))"; private final static String urlRegex = "((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\]\\s\\n\\\"\\>]*)"; private final static String urlWithWhitespacesRegex = urlWhiteSpacePrefix + urlRegex; private final static Pattern regexLinks2 = Pattern.compile("((?<=\\s)|(?<=\\A))([\\[\\{]|<)((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\\"\\)\\!]+)/?(?:[^\\]\\}](?", ">"); // http://juick.com/last?page=2 // http://juick.com/last?page=2 msg = msg.replaceAll(urlWithWhitespacesRegex, "$1$2"); // (http://juick.com/last?page=2) // (http://juick.com/last?page=2) Matcher m = regexLinks2.matcher(msg); StringBuffer sb = new StringBuffer(); while (m.find()) { String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", StringUtils.EMPTY); m.appendReplacement(sb, "$1$2" + url + "$5"); } m.appendTail(sb); msg = sb.toString(); return "
" + msg + "
"; } public static String formatMessage(String msg) { msg = msg.replaceAll("&", "&"); msg = msg.replaceAll("<", "<"); msg = msg.replaceAll(">", ">"); // -- // — msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\-\\-?((?=\\s)|(?=\\Z))", "$1—$2"); // http://juick.com/last?page=2 // juick.com msg = msg.replaceAll(urlWithWhitespacesRegex, "$1$3"); // [link text][http://juick.com/last?page=2] // link text msg = msg.replaceAll("\\[([^\\]]+)\\]\\[((?:ht|f)tps?://[^\\]]+)\\]", "$1"); msg = msg.replaceAll("\\[([^\\]]+)\\]\\(((?:ht|f)tps?://[^\\)]+)\\)", "$1"); // #12345 // #12345 msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)((?=\\s)|(?=\\Z)|(?=\\))|(?=\\.)|(?=\\,))", "$1#$2$3"); // #12345/65 // #12345/65 msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1#$2/$3$4"); // *bold* // bold msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))\\*([^\\*\\n<>]+)\\*((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); // /italic/ // italic msg = msg.replaceAll("((?<=\\s)|(?<=\\A))/([^\\/\\n<>]+)/((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); // _underline_ // underline msg = msg.replaceAll("((?<=\\s)|(?<=\\A))_([^\\_\\n<>]+)_((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); // /12 // /12 msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1/$2$3"); // @username@jabber.org // @username@jabber.org msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-\\.]+@[\\w\\-\\.]+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); // @username // @username msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-]{2,16})((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); // (http://juick.com/last?page=2) // (juick.com) Matcher m = regexLinks2.matcher(msg); StringBuffer sb = new StringBuffer(); while (m.find()) { String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", StringUtils.EMPTY); m.appendReplacement(sb, "$1$2$4$5"); } m.appendTail(sb); msg = sb.toString(); // > citate msg = msg.replaceAll("(?:(?<=\\n)|(?<=\\A))> *(.*)?(\\n|(?=\\Z))", "$1"); msg = msg.replaceAll("", "\n"); msg = msg.replaceAll("\n", "
\n"); return msg; } public static String formatHtml(Message jmsg) { StringBuilder sb = new StringBuilder(); boolean isReply = jmsg.getRid() > 0; String title = isReply ? "Reply by @" : "@"; String subtitle = isReply ? "
" + jmsg.getReplyQuote() + "
" : "" + getTagsString(jmsg) + ""; boolean isCode = jmsg.getTags().stream().anyMatch(t -> t.getName().equals("code")); sb.append(title).append(jmsg.getUser().getName()).append(":

") .append(subtitle).append("
") .append(isCode ? formatMessageCode(StringUtils.defaultString(jmsg.getText())) : formatMessage(StringUtils.defaultString(jmsg.getText()))).append("
"); if (StringUtils.isNotEmpty(jmsg.getAttachmentType())) { // FIXME: attachment does not serialized to xml if (jmsg.getAttachment() == null) { if (jmsg.getRid() > 0) { sb.append(String.format("", jmsg.getMid(), jmsg.getRid(), jmsg.getAttachmentType())); } else { sb.append(String.format("", jmsg.getMid(), jmsg.getAttachmentType())); } } else { sb.append(""); } } return sb.toString(); } public static String getMessageHashTags(final Message jmsg) { StringBuilder hashtags = new StringBuilder(); for (Tag tag : jmsg.getTags()) { hashtags.append("#").append(tag).append(" "); } return hashtags.toString(); } public static String getMarkdownTags(final Message jmsg) { return jmsg.getTags().stream().map(t -> String.format("[%s](http://juick.com/tag/%s)", t.getName(), percentEncode(t.getName()))) .collect(Collectors.joining(", ")); } public static String getMarkdownUser(final User user) { return String.format("[%s](https://juick.com/%s/)", user.getName(), user.getName()); } // TODO: check if it is really needed public static String percentEncode(final String s) { String ret = StringUtils.EMPTY; try { ret = URLEncoder.encode(s, CharEncoding.UTF_8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); } catch (UnsupportedEncodingException e) { } return ret; } public static String escapeMarkdown(final String s) { return s.replace("_", "\\_").replace("*", "\\*") .replace("`", "\\`"); } public static String attachmentUrl(final Message jmsg) { if (StringUtils.isEmpty(jmsg.getAttachmentType())) { return StringUtils.EMPTY; } // FIXME: attachment does not serialized to xml if (jmsg.getAttachment() == null) { if (jmsg.getRid() > 0) { return String.format("http://i.juick.com/photos-1024/%d-%d.%s", jmsg.getMid(), jmsg.getRid(), jmsg.getAttachmentType()); } else { return String.format("http://i.juick.com/photos-1024/%d.%s", jmsg.getMid(), jmsg.getAttachmentType()); } } else { return jmsg.getAttachment().getMedium().getUrl(); } } public static boolean replyStartsWithQuote(Message msg) { return msg.getRid() > 0 && StringUtils.defaultString(msg.getText()).startsWith(">"); } public static List parseTags(String strTags) { List tags = new ArrayList<>(); if (StringUtils.isNotEmpty(strTags)) { Set tagSet = new TreeSet<>(tags); for (String str : strTags.split(" ")) { Tag tag = new Tag(str); if (!tagSet.contains(tag)) { tags.add(tag); tagSet.add(tag); } } } return tags; } public static String getTagsString(Message msg) { StringBuilder builder = new StringBuilder(); List tags = msg.getTags(); if (!tags.isEmpty()) { for (Tag Tag : tags) builder.append(" *").append(Tag.getName()); if (msg.FriendsOnly) builder.append(" *friends"); if (msg.getPrivacy() == -2) builder.append(" *private"); else if (msg.getPrivacy() == -1) builder.append(" *friends"); else if (msg.getPrivacy() == 2) builder.append(" *public"); if (msg.ReadOnly) builder.append(" *readonly"); } return builder.toString(); } public static boolean isPM(Message message) { return message.getMid() == 0; } public static boolean isReply(Message message) { return message.getRid() > 0; } public static String stripNonSafeUrls(String input) { // strip login urls Matcher urlMatcher = Pattern.compile(MessageUtils.urlRegex).matcher(input); while (urlMatcher.find()) { URI uri = URI.create(urlMatcher.group(0)); if (uri.getHost().equals("juick.com")) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri); uriComponentsBuilder.replaceQueryParam("hash"); input = input.replace(urlMatcher.group(0), uriComponentsBuilder.build().toUriString()); } } return input; } }