diff options
Diffstat (limited to 'juick-server/src/main/java/com/juick/server')
28 files changed, 11 insertions, 3179 deletions
diff --git a/juick-server/src/main/java/com/juick/server/CommandsManager.java b/juick-server/src/main/java/com/juick/server/CommandsManager.java deleted file mode 100644 index 6458382f..00000000 --- a/juick-server/src/main/java/com/juick/server/CommandsManager.java +++ /dev/null @@ -1,489 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.component.LikeEvent; -import com.juick.server.component.MessageEvent; -import com.juick.server.component.PingEvent; -import com.juick.server.component.SubscribeEvent; -import com.juick.server.helpers.TagStats; -import com.juick.server.util.HttpUtils; -import com.juick.server.util.ImageUtils; -import com.juick.server.xmpp.helpers.CommandResult; -import com.juick.server.xmpp.helpers.annotation.UserCommand; -import com.juick.service.*; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.ocpsoft.prettytime.PrettyTime; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; -import rocks.xmpp.addr.Jid; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URI; -import java.nio.file.Paths; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * - * @author ugnich - */ -@Component -public class CommandsManager { - private PrettyTime pt; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private ShowQueriesService showQueriesService; - @Inject - private PrivacyQueriesService privacyQueriesService; - @Inject - private SubscriptionService subscriptionService; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @PostConstruct - public void init() { - pt = new PrettyTime(new Locale("ru")); - } - - - public Optional<CommandResult> processCommand(User user, Jid from, String input, URI attachment) throws InvocationTargetException, - IllegalAccessException, NoSuchMethodException { - Optional<Method> cmd = MethodUtils.getMethodsListWithAnnotation(getClass(), UserCommand.class).stream() - .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), - m.getAnnotation(UserCommand.class).patternFlags()).matcher(input).matches()) - .findFirst(); - if (cmd.isPresent()) { - Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), - cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(input); - List<String> groups = new ArrayList<>(); - while (matcher.find()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - groups.add(matcher.group(i)); - } - } - return Optional.of((CommandResult) getClass().getMethod(cmd.get().getName(), User.class, Jid.class, URI.class, String[].class) - .invoke(this, user, from, attachment, groups.toArray(new String[groups.size()]))); - } - return Optional.empty(); - } - - @UserCommand(pattern = "^ping$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "PING - returns you a PONG") - public CommandResult commandPing(User user, Jid from, URI attachment, String[] input) { - applicationEventPublisher.publishEvent(new PingEvent(this, user)); - return CommandResult.fromString("PONG"); - } - - @UserCommand(pattern = "^help$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "HELP - returns this help message") - public CommandResult commandHelp(User user, Jid from, URI attachment, String[] input) { - return CommandResult.fromString(Arrays.stream(getClass().getDeclaredMethods()) - .filter(m -> m.isAnnotationPresent(UserCommand.class)) - .map(m -> m.getAnnotation(UserCommand.class).help()) - .collect(Collectors.joining("\n"))); - } - - @UserCommand(pattern = "^login$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "LOGIN - log in to Juick website") - public CommandResult commandLogin(User user_from, Jid from, URI attachment, String[] input) { - return CommandResult.fromString("http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid())); - } - @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") - public CommandResult commandPM(User user_from, Jid from, URI attachment, String... arguments) { - String body = arguments[1]; - - User user_to = userService.getUserByName(arguments[0]); - - if (user_to.getUid() > 0) { - if (!userService.isInBLAny(user_to.getUid(), user_from.getUid())) { - if (pmQueriesService.createPM(user_from.getUid(), user_to.getUid(), body)) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(user_from); - jmsg.setTo(user_to); - jmsg.setText(body); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg)); - return CommandResult.fromString("Private message sent"); - } - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^bl$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL - Show your blacklist") - public CommandResult commandBLShow(User user_from, Jid from, URI attachment, String... arguments) { - List<User> blusers = userService.getUserBLUsers(user_from.getUid()); - List<String> bltags = tagService.getUserBLTags(user_from.getUid()); - - String txt = StringUtils.EMPTY; - if (bltags.size() > 0) { - for (String bltag : bltags) { - txt += "*" + bltag + "\n"; - } - - if (blusers.size() > 0) { - txt += "\n"; - } - } - if (blusers.size() > 0) { - for (User bluser : blusers) { - txt += "@" + bluser.getName() + "\n"; - } - } - if (txt.isEmpty()) { - txt = "You don't have any users or tags in your blacklist."; - } - - return CommandResult.fromString(txt); - } - - @UserCommand(pattern = "^#\\+$", help = "#+ - Show last Juick messages") - public CommandResult commandLast(User user_from, Jid from, URI attachment, String... arguments) { - return CommandResult.fromString("Last messages:\n" - + printMessages(messagesService.getAll(user_from.getUid(), 0), true)); - } - - @UserCommand(pattern = "@", help = "@ - Show recommendations and popular personal blogs") - public CommandResult commandUsers(User user_from, Jid from, URI attachment, String... arguments) { - StringBuilder msg = new StringBuilder(); - msg.append("Recommended blogs"); - List<String> recommendedUsers = showQueriesService.getRecommendedUsers(user_from); - if (recommendedUsers.size() > 0) { - for (String user : recommendedUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo recommendations now. Subscribe to more blogs. ;)"); - } - msg.append("\n\nTop 10 personal blogs:"); - List<String> topUsers = showQueriesService.getTopUsers(); - if (topUsers.size() > 0) { - for (String user : topUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo top users. Empty DB? ;)"); - } - return CommandResult.fromString(msg.toString()); - } - @UserCommand(pattern = "^bl\\s+@([^\\s\\n\\+]+)", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL @username - add @username to your blacklist") - public CommandResult blacklistUser(User user_from, Jid from, URI attachment, String... arguments) { - User blUser = userService.getUserByName(arguments[0]); - if (blUser != null) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistUser(user_from, blUser); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("User added to your blacklist"); - } else { - return CommandResult.fromString("User removed from your blacklist"); - } - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^bl\\s\\*(\\S+)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL *tag - add *tag to your blacklist") - public CommandResult blacklistTag(User user_from, Jid from, URI attachment, String... arguments) { - User blUser = userService.getUserByName(arguments[0]); - if (blUser != null) { - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistTag(user_from, tag); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("Tag added to your blacklist"); - } else { - return CommandResult.fromString("Tag removed from your blacklist"); - } - } - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "\\*", help = "* - Show your tags") - public CommandResult commandTags(User currentUser, Jid from, URI attachment, String... args) { - List<TagStats> tags = tagService.getUserTagStats(currentUser.getUid()); - String msg = "Your tags: (tag - messages)\n" + - tags.stream() - .map(t -> String.format("\n*%s - %d", t.getTag().getName(), t.getUsageCount())).collect(Collectors.joining()); - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "S", help = "S - Show your subscriptions") - public CommandResult commandSubscriptions(User currentUser, Jid from, URI attachment, String... args) { - List<User> friends = userService.getUserFriends(currentUser.getUid()); - List<String> tags = subscriptionService.getSubscribedTags(currentUser); - String msg = friends.size() > 0 ? "You are subscribed to users:" + friends.stream().map(u -> "\n@" + u.getName()) - .collect(Collectors.joining()) - : "You are not subscribed to any user."; - msg += tags.size() > 0 ? "\nYou are subscribed to tags:" + tags.stream().map(t -> "\n*" + t) - .collect(Collectors.joining()) - : "\nYou are not subscribed to any tag."; - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "!", help = "! - Show your favorite messages") - public CommandResult commandFavorites(User currentUser, Jid from, URI attachment, String... args) { - List<Integer> mids = messagesService.getUserRecommendations(currentUser.getUid(), 0); - if (mids.size() > 0) { - return CommandResult.fromString("Favorite messages: \n" + printMessages(mids, false)); - } - return CommandResult.fromString("No favorite messages, try to \"like\" something ;)"); - } - @UserCommand(pattern = "^\\!\\s+#(\\d+)", help = "! #12345 - recommend message") - public CommandResult commandRecommend(User user, Jid from, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid > 0) { - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (msg.getUser() == user) { - return CommandResult.fromString("You can't recommend your own messages."); - } - MessagesService.RecommendStatus status = messagesService.recommendMessage(mid, user.getUid()); - switch (status) { - case Added: - applicationEventPublisher.publishEvent(new LikeEvent(this, user, msg)); - return CommandResult.fromString("Message is added to your recommendations"); - case Deleted: - return CommandResult.fromString("Message deleted from your recommendations."); - } - } - return CommandResult.fromString("Message not found"); - } - return CommandResult.fromString("Message not found"); - } - // TODO: target notification - @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @username - subscribe to user" + - "\nU @username - unsubscribe from user", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeUser(User user, Jid from, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - User toUser = userService.getUserByName(args[1]); - if (subscribe) { - if (subscriptionService.subscribeUser(user, toUser)) { - // TODO: already subscribed case - applicationEventPublisher.publishEvent(new SubscribeEvent(this, user, toUser)); - return CommandResult.fromString("Subscribed to @" + toUser.getName()); - } - } else { - if (subscriptionService.unSubscribeUser(user, toUser)) { - return CommandResult.fromString("Unsubscribed from @" + toUser.getName()); - } - return CommandResult.fromString("You was not subscribed to @" + toUser.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + - "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeTag(User user, Jid from, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - Tag tag = tagService.getTag(args[1], true); - if (subscribe) { - if (subscriptionService.subscribeTag(user, tag)) { - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeTag(user, tag)) { - return CommandResult.fromString("Unsubscribed from " + tag.getName()); - } - return CommandResult.fromString("You was not subscribed to " + tag.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments" + - "\nU #1234 - unsubscribe from comments", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeMessage(User user, Jid from, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - int mid = NumberUtils.toInt(args[1], 0); - if (messagesService.getMessage(mid) != null) { - if (subscribe) { - if (subscriptionService.subscribeMessage(mid, user.getUid())) { - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeMessage(mid, user.getUid())) { - return CommandResult.fromString("Unsubscribed from #" + mid); - } - return CommandResult.fromString("You was not subscribed to #" + mid); - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(on|off)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "ON/OFF - Enable/disable subscriptions delivery") - public CommandResult commandOnOff(User user, Jid from, URI attachment, String[] input) { - UserService.ActiveStatus newStatus; - String retValUpdated; - if (input[0].toLowerCase().equals("on")) { - newStatus = UserService.ActiveStatus.Active; - retValUpdated = "Notifications are activated for " + from.asBareJid().toEscapedString(); - } else { - newStatus = UserService.ActiveStatus.Inactive; - retValUpdated = "Notifications are disabled for " + from.asBareJid().toEscapedString(); - } - - if (userService.setActiveStatusForJID(from.asBareJid().toEscapedString(), newStatus)) { - return CommandResult.fromString(retValUpdated); - } else { - return CommandResult.fromString(String.format("Subscriptions status for %s was not changed", from.toEscapedString())); - } - } - @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", - help = "@username+ - Show user's info and last 20 messages") - public CommandResult commandUser(User user, Jid from, URI attachment, String... arguments) { - User blogUser = userService.getUserByName(arguments[0]); - int page = arguments[1].length(); - if (blogUser.getUid() > 0) { - List<Integer> mids = messagesService.getUserBlog(blogUser.getUid(), 0, 0); - return CommandResult.fromString(String.format("Last messages from @%s:\n%s", arguments[0], - printMessages(mids, false))); - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") - public CommandResult commandShow(User user, Jid from, URI attachment, String... arguments) { - boolean showReplies = arguments[1].length() > 0; - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid == 0) { - return CommandResult.fromString("Error"); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (showReplies) { - List<com.juick.Message> replies = messagesService.getReplies(mid); - replies.add(0, msg); - return CommandResult.fromString(String.join("\n", - replies.stream().map(PlainTextFormatter::formatPostSummary).collect(Collectors.toList()))); - } - return CommandResult.fromString(PlainTextFormatter.formatPost(msg)); - } - return CommandResult.fromString("Message not found"); - } - @UserCommand(pattern = "^#(\\d+)\\/(\\d+)$", help = "#1234/5 - Show reply") - public CommandResult commandShowReply(User user, Jid from, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - int rid = NumberUtils.toInt(arguments[1], 0); - com.juick.Message reply = messagesService.getReply(mid, rid); - if (reply != null) { - return CommandResult.fromString(PlainTextFormatter.formatPost(reply)); - } - return CommandResult.fromString("Reply not found"); - } - @UserCommand(pattern = "^\\*(\\S+)(\\+?)$", help = "*tag - Show last messages with tag") - public CommandResult commandShowTag(User user, Jid from, URI attachment, String... arguments) { - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - // TODO: synonims - List<Integer> mids = messagesService.getTag(tag.TID, user.getUid(), 0, 10); - return CommandResult.fromString("Last messages with *" + tag.getName() + ":\n" + printMessages(mids, true)); - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "^D #(\\d+)$", help = "D #1234 - Delete post", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeletePost(User user, Jid from, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - if (messagesService.deleteMessage(user.getUid(), mid)) { - return CommandResult.fromString("Message deleted"); - } - return CommandResult.fromString("This is not your message"); - } - @UserCommand(pattern = "^D #(\\d+)(\\.|\\-|\\/)(\\d+)$", help = "D #1234/5 - Delete comment", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteReply(User user, Jid from, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - int rid = Integer.valueOf(args[2]); - if (messagesService.deleteReply(user.getUid(), mid, rid)) { - return CommandResult.fromString("Reply deleted"); - } else { - return CommandResult.fromString("This is not your reply"); - } - } - @UserCommand(pattern = "^(D L|DL|D LAST)$", help = "D L - Delete last message", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteLast(User user, Jid from, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+\\@([a-zA-Z0-9\\-\\.\\@]+)\\s+([\\s\\S]+)$", help = "? @user string - search in user messages") - public CommandResult commandSearch(User user, Jid from, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+([\\s\\S]+)$", help = "? string - search in all messages") - public CommandResult commandSearchAll(User user, Jid from, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") - public CommandResult commandMyFeed(User user, Jid from, URI attachment, String... arguments) { - // number of # is the page count - int page = arguments[0].length() - 1; - List<Integer> mids = messagesService.getMyFeed(user.getUid(), page, false); - if (mids.size() > 0) { - return CommandResult.fromString("Your feed: \n" + printMessages(mids, true)); - } - return CommandResult.fromString("Your feed is empty"); - } - @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)", - help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") - public CommandResult EditOrReply(User user, Jid from, URI attachment, String... args) throws Exception { - int mid = NumberUtils.toInt(args[1]); - int rid = NumberUtils.toInt(args[4], 0); - String txt = args[5]; - List<Tag> messageTags = tagService.fromString(txt, true); - if (messageTags.size() > 0) { - if (user.getUid() != messagesService.getMessageAuthor(mid).getUid()) { - return CommandResult.fromString("It is not your message"); - } - tagService.updateTags(mid, messageTags); - return CommandResult.fromString("Tags are updated"); - } else { - String attachmentType = attachment != null && StringUtils.isNotEmpty(attachment.toString()) ? attachment.toString().substring(attachment.toString().length() - 3) : null; - int newrid = messagesService.createReply(mid, rid, user.getUid(), txt, attachmentType); - if (StringUtils.isNotEmpty(attachmentType)) { - String attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir); - String fname = String.format("%d-%d.%s", mid, newrid, attachmentType); - ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir); - } - Message reply = messagesService.getReply(mid, newrid); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply)); - return CommandResult.fromMessage(reply,"Reply posted.\n#" + mid + "/" + newrid + " " - + "https://juick.com/" + mid + "#" + newrid); - } - } - - String printMessages(List<Integer> mids, boolean crop) { - return messagesService.getMessages(mids).stream() - .sorted(Collections.reverseOrder()) - .map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n\n")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/MessengerManager.java b/juick-server/src/main/java/com/juick/server/MessengerManager.java index adef1e22..ca206bd4 100644 --- a/juick-server/src/main/java/com/juick/server/MessengerManager.java +++ b/juick-server/src/main/java/com/juick/server/MessengerManager.java @@ -50,7 +50,7 @@ public class MessengerManager implements ApplicationListener<MessageEvent> { @Inject private MessengerService messengerService; @Inject - private ServerManager serverManager; + private XMPPConnection xmppConnection; @Value("${fb_page_access_token:12345678}") private String facebookPageAccessToken; @@ -97,7 +97,7 @@ public class MessengerManager implements ApplicationListener<MessageEvent> { logger.info("Received text message from '{}' at '{}' with content: {} (mid: {})", senderId, timestamp, text, messageId); try { - serverManager.processMessage(user_from, text, null); + xmppConnection.processMessage(user_from, text, null); } catch (Exception e) { logger.warn("messenger error", e); } diff --git a/juick-server/src/main/java/com/juick/server/NotificationListener.java b/juick-server/src/main/java/com/juick/server/NotificationListener.java deleted file mode 100644 index f6330570..00000000 --- a/juick-server/src/main/java/com/juick/server/NotificationListener.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.juick.server; - -import com.juick.server.component.LikeEvent; -import com.juick.server.component.MessageEvent; -import com.juick.server.component.PingEvent; -import com.juick.server.component.SubscribeEvent; -import org.springframework.context.event.EventListener; - -public interface NotificationListener { - @EventListener - void processMessageEvent(MessageEvent messageEvent); - @EventListener - void processSubscribeEvent(SubscribeEvent subscribeEvent); - @EventListener - void processLikeEvent(LikeEvent likeEvent); - @EventListener - void ProcessPingEvent(PingEvent pingEvent); -} diff --git a/juick-server/src/main/java/com/juick/server/ServerManager.java b/juick-server/src/main/java/com/juick/server/ServerManager.java index 6cac270c..40de9d3e 100644 --- a/juick-server/src/main/java/com/juick/server/ServerManager.java +++ b/juick-server/src/main/java/com/juick/server/ServerManager.java @@ -57,16 +57,6 @@ public class ServerManager implements ApplicationListener<MessageEvent> { @Inject private XMPPConnection router; - public com.juick.Message processMessage(User visitor, String body, String attachmentName) throws Exception { - if (StringUtils.isNotEmpty(attachmentName)) { - URI httpUri = URI.create(attachmentName); - if (!httpUri.isAbsolute()) { - attachmentName = String.format("juick://%s", attachmentName); - } - } - return router.incomingMessageJuick(visitor, Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "perl"), body, URI.create(attachmentName)); - } - private void onJuickPM(final int uid_to, final com.juick.Message jmsg) { try { String json = jsonMapper.writeValueAsString(jmsg); diff --git a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java index c1ccc8ff..020d2433 100644 --- a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java +++ b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java @@ -79,7 +79,7 @@ public class TelegramBotManager implements NotificationListener { @Inject private UserService userService; @Inject - private ServerManager serverManager; + private XMPPConnection xmppConnection; @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") private String tmpDir; @@ -183,7 +183,7 @@ public class TelegramBotManager implements NotificationListener { int rid = Integer.valueOf(uriComponents.getFragment()); prefix = String.format("#%d/%d ", mid, rid); } - serverManager.processMessage(user_from, prefix + text, attachment); + xmppConnection.processMessage(user_from, prefix + text, attachment); telegramNotify(message.from().id().longValue(), "Reply sent", StringUtils.EMPTY); } else { logger.warn("invalid path: {}", path); @@ -200,7 +200,7 @@ public class TelegramBotManager implements NotificationListener { "Can not reply to this message", replyMessage.messageId(), StringUtils.EMPTY); } } else { - serverManager.processMessage(user_from, text, attachment); + xmppConnection.processMessage(user_from, text, attachment); telegramNotify(message.from().id().longValue(), "Message sent", StringUtils.EMPTY); } } diff --git a/juick-server/src/main/java/com/juick/server/WebsocketManager.java b/juick-server/src/main/java/com/juick/server/WebsocketManager.java index 309f605c..498bf064 100644 --- a/juick-server/src/main/java/com/juick/server/WebsocketManager.java +++ b/juick-server/src/main/java/com/juick/server/WebsocketManager.java @@ -70,7 +70,7 @@ public class WebsocketManager extends TextWebSocketHandler { @Inject private ObjectMapper jsonMapper; @Inject - private ServerManager serverManager; + private XMPPConnection xmppConnection; @Override @@ -151,7 +151,7 @@ public class WebsocketManager extends TextWebSocketHandler { Message draft = jsonMapper.readValue(message.asBytes(), Message.class); if (draft.getUser() != null && (draft.getText() != null || draft.getAttachment() != null)) { String attachmentFileName = draft.getAttachment() == null ? "" : draft.getAttachment().getUrl(); - serverManager.processMessage(draft.getUser(), draft.getText(), attachmentFileName); + xmppConnection.processMessage(draft.getUser(), draft.getText(), attachmentFileName); } } catch (Exception e) { throw new HttpBadRequestException(); diff --git a/juick-server/src/main/java/com/juick/server/XMPPConnection.java b/juick-server/src/main/java/com/juick/server/XMPPConnection.java deleted file mode 100644 index 4be4a5ba..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPConnection.java +++ /dev/null @@ -1,743 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.Tag; -import com.juick.User; -import com.juick.server.component.LikeEvent; -import com.juick.server.component.MessageEvent; -import com.juick.server.component.PingEvent; -import com.juick.server.component.SubscribeEvent; -import com.juick.server.helpers.UserInfo; -import com.juick.server.util.HttpUtils; -import com.juick.server.util.ImageUtils; -import com.juick.server.util.TagUtils; -import com.juick.server.xmpp.helpers.CommandResult; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import com.juick.server.xmpp.s2s.StanzaListener; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Component; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.stanza.AbstractIQHandler; -import rocks.xmpp.core.stanza.model.*; -import rocks.xmpp.core.stanza.model.client.ClientMessage; -import rocks.xmpp.core.stanza.model.client.ClientPresence; -import rocks.xmpp.core.stanza.model.errors.Condition; -import rocks.xmpp.extensions.caps.model.EntityCapabilities; -import rocks.xmpp.extensions.component.accept.ExternalComponent; -import rocks.xmpp.extensions.filetransfer.FileTransfer; -import rocks.xmpp.extensions.filetransfer.FileTransferManager; -import rocks.xmpp.extensions.nick.model.Nickname; -import rocks.xmpp.extensions.oob.model.x.OobX; -import rocks.xmpp.extensions.ping.PingManager; -import rocks.xmpp.extensions.vcard.temp.model.VCard; -import rocks.xmpp.extensions.version.SoftwareVersionManager; -import rocks.xmpp.extensions.version.model.SoftwareVersion; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -/** - * @author ugnich - */ -@Component -@DependsOn("XMPPRouter") -public class XMPPConnection implements StanzaListener, NotificationListener { - - private static final Logger logger = LoggerFactory.getLogger(XMPPConnection.class); - - private ExternalComponent router; - @Inject - private XMPPServer xmpp; - @Inject - private CommandsManager commandsManager; - @Value("${xmppbot_jid:juick@localhost}") - private Jid jid; - @Value("${componentname:localhost}") - private String componentName; - @Value("${component_port:5347}") - private int componentPort; - @Value("${xmpp_password:secret}") - private String password; - @Value("${xmpp_disabled:false}") - private boolean isXmppDisabled; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private TagService tagService; - @Inject - private BasicXmppSession session; - @Inject - private ExecutorService service; - @Inject - private ServerManager serverManager; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @PostConstruct - public void init() { - logger.info("stream router start connecting to {}", componentPort); - xmpp.addStanzaListener(this); - router = ExternalComponent.create(componentName, password, session.getConfiguration(), "localhost", - componentPort); - PingManager pingManager = router.getManager(PingManager.class); - pingManager.setEnabled(true); - router.disableFeature(EntityCapabilities.NAMESPACE); - SoftwareVersionManager softwareVersionManager = router.getManager(SoftwareVersionManager.class); - softwareVersionManager.setSoftwareVersion(new SoftwareVersion("Juick", "2.x", - System.getProperty("os.name", "generic"))); - VCard vCard = new VCard(); - vCard.setFormattedName("Juick"); - try { - vCard.setUrl(new URL("http://juick.com/")); - vCard.setPhoto(new VCard.Image("image/png", IOUtils.toByteArray( - getClass().getClassLoader().getResource("juick.png")))); - } catch (MalformedURLException e) { - logger.error("invalid url", e); - } catch (IOException e) { - logger.warn("invalid resource", e); - } - router.addIQHandler(VCard.class, new AbstractIQHandler(IQ.Type.GET) { - @Override - protected IQ processRequest(IQ iq) { - if (iq.getTo().equals(jid) || iq.getTo().asBareJid().equals(jid.asBareJid()) - || iq.getTo().asBareJid().toEscapedString().equals(jid.getDomain())) { - return iq.createResult(vCard); - } - User user = userService.getUserByName(iq.getTo().getLocal()); - if (user.getUid() > 0) { - UserInfo info = userService.getUserInfo(user); - VCard userVCard = new VCard(); - userVCard.setFormattedName(info.getFullName()); - userVCard.setNickname(user.getName()); - try { - userVCard.setPhoto(new VCard.Image(new URI("http://i.juick.com/a/" + user.getUid() + ".png"))); - if (info.getUrl() != null) { - userVCard.setUrl(new URL(info.getUrl())); - } - } catch (MalformedURLException | URISyntaxException e) { - logger.warn("url exception", e); - } - return iq.createResult(userVCard); - } - return iq.createError(Condition.BAD_REQUEST); - } - }); - router.addInboundMessageListener(e -> { - Message message = e.getMessage(); - Jid jid = message.getTo(); - if (jid.getDomain().equals(router.getDomain().toEscapedString()) || jid.equals(jid)) { - com.juick.Message jmsg = message.getExtension(com.juick.Message.class); - if (jmsg != null) { - if (jid.getLocal().equals("recomm")) { - User fromUser = jmsg.getUser(); - com.juick.Message msg = messagesService.getMessage(jmsg.getMid()); - applicationEventPublisher.publishEvent(new LikeEvent(this, fromUser, msg)); - } else if (jid.getLocal().equals("pm")) { - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg)); - } else { - if (jmsg.getRid() > 0) { - // to get quote and attachment - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - sendJuickComment(reply); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply)); - } else if (jmsg.getMid() > 0) { - sendJuickMessage(jmsg); - applicationEventPublisher.publishEvent(new MessageEvent(this, - messagesService.getMessage(jmsg.getMid()))); - } - } - } else { - String attachment = StringUtils.EMPTY; - OobX oobX = message.getExtension(OobX.class); - if (oobX != null) { - attachment = oobX.getUri().toString(); - } - try { - serverManager.processMessage(userService.getUserByUID(NumberUtils.toInt(message.getFrom().getLocal(), 0)).orElse(new User()), message.getBody(), attachment); - } catch (Exception e1) { - logger.warn("message exception", e1); - } - } - } else if (jid.getDomain().endsWith(jid.getDomain()) && (jid.getDomain().equals(this.jid.getDomain()) - || jid.getDomain().endsWith("." + jid.getDomain()))) { - if (logger.isInfoEnabled()) { - try { - logger.info("unhandled message: {}", stanzaToString(message)); - } catch (JAXBException | XMLStreamException ex) { - logger.error("JAXB exception", ex); - } - } - } else { - s2s(ClientMessage.from(message)); - } - }); - router.addInboundIQListener(e -> { - IQ iq = e.getIQ(); - Jid jid = iq.getTo(); - if (!jid.getDomain().equals(this.jid.getDomain())) { - s2s(iq); - } - }); - FileTransferManager fileTransferManager = router.getManager(FileTransferManager.class); - fileTransferManager.addFileTransferOfferListener(e -> { - try { - List<String> allowedTypes = new ArrayList<String>() {{ - add("png"); - add("jpg"); - }}; - String attachmentExtension = FilenameUtils.getExtension(e.getName()).toLowerCase(); - String targetFilename = String.format("%s.%s", - DigestUtils.md5Hex(String.format("%s-%s", - e.getInitiator().toString(), e.getSessionId()).getBytes()), attachmentExtension); - if (allowedTypes.contains(attachmentExtension)) { - Path filePath = Paths.get(tmpDir, targetFilename); - FileTransfer ft = e.accept(filePath).get(); - ft.addFileTransferStatusListener(st -> { - logger.debug("{}: received {} of {}", e.getName(), st.getBytesTransferred(), e.getSize()); - if (st.getStatus().equals(FileTransfer.Status.COMPLETED)) { - logger.info("transfer completed"); - try { - serverManager.processMessage(userService.getUserByJID(e.getInitiator().toEscapedString()), e.getDescription(), targetFilename); - } catch (Exception e1) { - logger.error("ft error", e1); - } - - } else if (st.getStatus().equals(FileTransfer.Status.FAILED)) { - logger.info("transfer failed", ft.getException()); - Message msg = new Message(); - msg.setType(Message.Type.CHAT); - msg.setFrom(jid); - msg.setTo(e.getInitiator()); - msg.setBody("File transfer failed, please report to us"); - router.sendMessage(msg); - } else if (st.getStatus().equals(FileTransfer.Status.CANCELED)) { - logger.info("transfer cancelled"); - } - }); - ft.transfer(); - logger.info("transfer started"); - } else { - e.reject(); - logger.info("transfer rejected"); - } - } catch (IOException | InterruptedException | ExecutionException e1) { - logger.error("ft error", e1); - } - }); - router.addConnectionListener(event -> { - if (event.getType().equals(rocks.xmpp.core.session.ConnectionEvent.Type.RECONNECTION_SUCCEEDED)) { - logger.info("component connected"); - } - }); - if (!isXmppDisabled) { - service.submit(() -> { - try { - Thread.sleep(3000); - router.connect(); - broadcastPresence(null); - } catch (InterruptedException | XmppException e) { - logger.warn("xmpp exception", e); - } - }); - } - } - - private String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - router.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - router.createMarshaller().marshal(stanza, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - return stanzaWriter.toString(); - } - - private void s2s(Stanza stanza) { - try { - String xml = stanzaToString(stanza); - logger.info("stream router (out): {}", xml); - xmpp.sendOut(stanza); - } catch (XMLStreamException | JAXBException e) { - logger.error("JAXB exception", e); - } - - } - - - - private void sendJuickMessage(com.juick.Message jmsg) { - List<String> jids = new ArrayList<>(); - - if (jmsg.FriendsOnly) { - jids = subscriptionService.getJIDSubscribedToUser(jmsg.getUser().getUid(), jmsg.FriendsOnly); - } else { - List<User> users = subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid()); - for (User user : users) { - jids.addAll(userService.getJIDsbyUID(user.getUid())); - } - } - com.juick.Message fullMsg = messagesService.getMessage(jmsg.getMid()); - String txt = "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(fullMsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(fullMsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid() + " http://juick.com/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - for (String jid : jids) { - msg.setTo(Jid.of(jid)); - s2s(ClientMessage.from(msg)); - } - } - - public void sendJuickComment(com.juick.Message jmsg) { - List<User> users; - String replyQuote; - String replyTo; - - com.juick.Message op = messagesService.getMessage(jmsg.getMid()); - users = subscriptionService.getUsersSubscribedToComments(op, jmsg); - com.juick.Message replyMessage = jmsg.getReplyto() > 0 ? messagesService.getReply(jmsg.getMid(), jmsg.getReplyto()) - : messagesService.getMessage(jmsg.getMid()); - replyTo = replyMessage.getUser().getName(); - com.juick.Message fullReply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - replyQuote = fullReply.getReplyQuote(); - - String txt = "Reply by @" + jmsg.getUser().getName() + ":\n" + replyQuote + "\n@" + replyTo + " "; - String attachmentUrl = MessageUtils.attachmentUrl(fullReply); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n" + "#" + jmsg.getMid() + "/" + jmsg.getRid() + " http://juick.com/" + jmsg.getMid() + "#" + jmsg.getRid(); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.addExtension(jmsg); - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - s2s(ClientMessage.from(msg)); - } - } - } - - @Override - public void processMessageEvent(MessageEvent event) { - com.juick.Message msg = event.getMessage(); - boolean isPM = msg.getMid() == 0; - boolean isReply = msg.getRid() > 0; - if (isPM) { - userService.getJIDsbyUID(msg.getTo().getUid()) - .forEach(userJid -> { - Message mm = new Message(); - mm.setTo(Jid.of(userJid)); - mm.setType(Message.Type.CHAT); - boolean inroster = pmQueriesService.havePMinRoster(msg.getUser().getUid(), userJid); - if (inroster) { - mm.setFrom(Jid.of(msg.getUser().getName(), "juick.com", "Juick")); - mm.setBody(msg.getText()); - } else { - mm.setFrom(jid); - mm.setBody("Private message from @" + msg.getUser().getName() + ":\n" + msg.getText()); - } - s2s(ClientMessage.from(mm)); - }); - } else if (!isReply) { - String notify = "New message posted.\n#" + msg.getMid() + " https://juick.com/" + msg.getMid(); - userService.getJIDsbyUID(msg.getUser().getUid()) - .forEach(jid -> sendReply(Jid.of(jid), notify)); - } - } - - private void sendReply(Jid jidTo, String txt) { - Message reply = new Message(); - reply.setFrom(jid); - reply.setTo(jidTo); - reply.setType(Message.Type.CHAT); - reply.setBody(txt); - s2s(ClientMessage.from(reply)); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - List<User> users; - com.juick.Message jmsg = likeEvent.getMessage(); - User liker = likeEvent.getUser(); - users = subscriptionService.getUsersSubscribedToUserRecommendations(liker.getUid(), - jmsg.getMid(), jmsg.getUser().getUid()); - - String txt = "Recommended by @" + liker.getName() + ":\n"; - txt += "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(jmsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(jmsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid(); - if (jmsg.getReplies() > 0) { - if (jmsg.getReplies() % 10 == 1 && jmsg.getReplies() % 100 != 11) { - txt += " (" + jmsg.getReplies() + " reply)"; - } else { - txt += " (" + jmsg.getReplies() + " replies)"; - } - } - txt += " http://juick.com/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - s2s(ClientMessage.from(msg)); - } - } - } - - @Override - public void ProcessPingEvent(PingEvent pingEvent) { - userService.getJIDsbyUID(pingEvent.getPinger().getUid()) - .forEach(userJid -> { - Presence p = new Presence(Jid.of(userJid)); - p.setFrom(jid); - p.setPriority((byte) 10); - s2s(ClientPresence.from(p)); - }); - } - - private void incomingPresence(Presence p) { - final String username = p.getTo().getLocal(); - final boolean toJuick = username.equals(jid.getLocal()); - - if (p.getType() == null) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().asBareJid()); - reply.setTo(p.getFrom().asBareJid()); - reply.setType(Presence.Type.UNSUBSCRIBE); - s2s(ClientPresence.from(reply)); - } else if (p.getType().equals(Presence.Type.PROBE)) { - int uid_to = 0; - if (!toJuick) { - uid_to = userService.getUIDbyName(username); - } - - if (toJuick || uid_to > 0) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().withResource(jid.getResource())); - reply.setTo(p.getFrom()); - reply.setPriority((byte)10); - if (!userService.getActiveJIDs().contains(p.getFrom().asBareJid().toEscapedString())) { - reply.setStatus("Send ON to enable notifications"); - } - s2s(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - s2s(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.SUBSCRIBE)) { - boolean canSubscribe = false; - if (toJuick) { - canSubscribe = true; - } else { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - canSubscribe = true; - } - } - if (canSubscribe) { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.SUBSCRIBED); - s2s(ClientPresence.from(reply)); - - reply.setFrom(reply.getFrom().withResource(jid.getResource())); - reply.setPriority((byte) 10); - reply.setType(null); - s2s(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - s2s(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.UNSUBSCRIBE)) { - if (!toJuick) { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - } - } - - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.UNSUBSCRIBED); - s2s(ClientPresence.from(reply)); - } - } - - public void incomingMessage(Message msg) { - if (msg.getType() != null && msg.getType().equals(Message.Type.ERROR)) { - StanzaError error = msg.getError(); - if (error != null && error.getCondition().equals(Condition.RESOURCE_CONSTRAINT)) { - // offline query is full, deactivating this jid - if (userService.setActiveStatusForJID(msg.getFrom().toEscapedString(), UserService.ActiveStatus.Inactive)) { - logger.info("{} is inactive now", msg.getFrom()); - return; - } - } - return; - } - if (StringUtils.isBlank(msg.getBody())) { - return; - } - String username = msg.getTo().getLocal(); - - User user_from; - String signuphash = StringUtils.EMPTY; - user_from = userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString()); - if (user_from == null) { - signuphash = userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString()); - } - - if (user_from == null) { - Message reply = new Message(); - reply.setFrom(msg.getTo()); - reply.setTo(msg.getFrom()); - reply.setType(Message.Type.CHAT); - reply.setBody("Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nTo start using Juick, please sign up: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nIf you already have an account on Juick, you will be proposed to attach this JabberID to your existing account."); - s2s(ClientMessage.from(reply)); - return; - } - - if (username.equals(jid.getLocal())) { - try { - OobX oobX = msg.getExtension(OobX.class); - if (oobX != null) { - incomingMessageJuick(user_from, msg.getFrom(), msg.getBody().trim(), oobX.getUri()); - } else { - incomingMessageJuick(user_from, msg.getFrom(), msg.getBody().trim(), null); - } - } catch (Exception e) { - return; - } - } - - int uid_to = userService.getUIDbyName(username); - - if (uid_to == 0) { - Message reply = new Message(); - reply.setFrom(msg.getTo()); - reply.setTo(msg.getFrom()); - reply.setType(Message.Type.ERROR); - reply.setId(msg.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - s2s(ClientMessage.from(reply)); - return; - } - - boolean success = false; - if (!userService.isInBLAny(uid_to, user_from.getUid())) { - success = pmQueriesService.createPM(user_from.getUid(), uid_to, msg.getBody()); - } - - if (success) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(user_from); - jmsg.setTo(userService.getUserByUID(uid_to).get()); - jmsg.setText(msg.getBody()); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg)); - } else { - Message reply = new Message(); - reply.setFrom(msg.getTo()); - reply.setTo(msg.getFrom()); - reply.setType(Message.Type.ERROR); - reply.setId(msg.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.NOT_ALLOWED)); - s2s(ClientMessage.from(reply)); - } - - return; - } - public com.juick.Message incomingMessageJuick(User user_from, Jid from, String command, URI attachment) throws Exception { - int commandlen = command.length(); - - // COMPATIBILITY - if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) { - command = command.substring(3).trim(); - } - - Optional<CommandResult> result = commandsManager.processCommand(user_from, from, command, attachment); - if (result.isPresent()) { - sendReply(from, result.get().getText()); - return result.get().getNewMessage(); - } else { - // new message - List<Tag> tags = tagService.fromString(command, false); - String body = command.substring(TagUtils.toString(tags).length()); - String attachmentType = StringUtils.isNotEmpty(attachment.toString()) ? attachment.toString().substring(attachment.toString().length() - 3) : null; - int mid = messagesService.createMessage(user_from.getUid(), body, attachmentType, tags); - subscriptionService.subscribeMessage(mid, user_from.getUid()); - if (StringUtils.isNotEmpty(attachmentType)) { - String attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir); - String fname = String.format("%d.%s", mid, attachmentType); - ImageUtils.saveImageWithPreviews(attachmentFName, fname, tmpDir, imgDir); - } - com.juick.Message msg = messagesService.getMessage(mid); - applicationEventPublisher.publishEvent(new MessageEvent(this, msg)); - return msg; - } - } - - @Override - public void stanzaReceived(Stanza xmlValue) { - if (xmlValue instanceof Presence) { - Presence p = (Presence) xmlValue; - if (p.getType() == null || !p.getType().equals(Presence.Type.ERROR)) { - incomingPresence(p); - } - } else if (xmlValue instanceof Message) { - Message msg = (Message) xmlValue; - incomingMessage(msg); - } else if (xmlValue instanceof IQ) { - IQ iq = (IQ) xmlValue; - router.send(iq); - } - } - - private void broadcastPresence(Presence.Type type) { - Presence presence = new Presence(); - presence.setFrom(jid); - if (type != null) { - presence.setType(type); - } - userService.getActiveJIDs().forEach(j -> { - try { - presence.setTo(Jid.of(j)); - s2s(ClientPresence.from(presence)); - } catch (IllegalArgumentException ex) { - logger.warn("Invalid jid: {}", j, ex); - } - }); - } - - @PreDestroy - public void close() throws Exception { - broadcastPresence(Presence.Type.UNAVAILABLE); - if (router != null) { - router.close(); - } - } - - ExternalComponent getRouter() { - return router; - } -} diff --git a/juick-server/src/main/java/com/juick/server/XMPPServer.java b/juick-server/src/main/java/com/juick/server/XMPPServer.java deleted file mode 100644 index c291202d..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPServer.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.server.xmpp.s2s.*; -import com.juick.service.UserService; -import com.juick.server.xmpp.extensions.JuickMessage; -import com.juick.xmpp.extensions.StreamError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.Stanza; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.net.ssl.*; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.*; -import java.net.*; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SecureRandom; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author ugnich - */ -@Component -public class XMPPServer implements ConnectionListener, AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(XMPPServer.class); - - private static final int TIMEOUT_MINUTES = 15; - - @Inject - public ExecutorService service; - @Value("${hostname:localhost}") - private Jid jid; - @Value("${s2s_port:5269}") - private int s2sPort; - @Value("${keystore:juick.p12}") - public String keystore; - @Value("${keystore_password:secret}") - public String keystorePassword; - @Value("${broken_ssl_hosts:}") - public String[] brokenSSLhosts; - @Value("${banned_hosts:}") - public String[] bannedHosts; - - private final List<ConnectionIn> inConnections = new CopyOnWriteArrayList<>(); - private final Map<ConnectionOut, Optional<Socket>> outConnections = new ConcurrentHashMap<>(); - private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>(); - private final List<StanzaListener> stanzaListeners = new CopyOnWriteArrayList<>(); - private final AtomicBoolean closeFlag = new AtomicBoolean(false); - - SSLContext sc; - private TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - } - }; - private boolean tlsConfigured = false; - - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - @Inject - private UserService userService; - - @PostConstruct - public void init() throws KeyStoreException { - closeFlag.set(false); - KeyStore ks = KeyStore.getInstance("PKCS12"); - try (InputStream ksIs = new FileInputStream(keystore)) { - ks.load(ksIs, keystorePassword.toCharArray()); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory - .getDefaultAlgorithm()); - kmf.init(ks, keystorePassword.toCharArray()); - sc = SSLContext.getInstance("TLSv1.2"); - sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); - tlsConfigured = true; - } catch (Exception e) { - logger.warn("tls unavailable"); - } - service.submit(() -> { - try { - listener = new ServerSocket(s2sPort); - logger.info("s2s listener ready"); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(this, socket); - addConnectionIn(client); - service.submit(client); - } - } catch (SocketException e) { - // shutdown - } catch (IOException | XmlPullParserException e) { - logger.warn("xmpp exception", e); - } - }); - } - - @Override - public void close() throws Exception { - if (listener != null && !listener.isClosed()) { - listener.close(); - } - outConnections.forEach((c, s) -> { - c.logoff(); - outConnections.remove(c); - }); - inConnections.forEach(c -> { - c.closeConnection(); - inConnections.remove(c); - }); - service.shutdown(); - logger.info("XMPP server destroyed"); - } - - public void addConnectionIn(ConnectionIn c) { - c.setListener(this); - inConnections.add(c); - } - - public void addConnectionOut(ConnectionOut c, Optional<Socket> socket) { - c.setListener(this); - outConnections.put(c, socket); - } - - public void removeConnectionIn(ConnectionIn c) { - inConnections.remove(c); - } - - public void removeConnectionOut(ConnectionOut c) { - outConnections.remove(c); - } - - public String getFromCache(Jid to) { - final String[] cache = new String[1]; - outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> { - cache[0] = c.xml; - outCache.remove(c); - }); - return cache[0]; - } - - public Optional<ConnectionOut> getConnectionOut(Jid hostname, boolean needReady) { - return outConnections.keySet().stream().filter(c -> c.to != null && - c.to.equals(hostname) && (!needReady || c.streamReady)).findFirst(); - } - - public Optional<ConnectionIn> getConnectionIn(String streamID) { - return inConnections.stream().filter(c -> c.streamID != null && c.streamID.equals(streamID)).findFirst(); - } - - public void sendOut(Stanza s) { - try { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - session.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - session.createMarshaller().marshal(s, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - String xml = stanzaWriter.toString(); - logger.debug("s2s (out): {}", xml); - sendOut(Jid.of(s.getTo().getDomain()), xml); - } catch (XMLStreamException | JAXBException e1) { - logger.info("jaxb exception", e1); - } - } - - public void sendOut(Jid hostname, String xml) { - boolean haveAnyConn = false; - - ConnectionOut connOut = null; - for (ConnectionOut c : outConnections.keySet()) { - if (c.to != null && c.to.equals(hostname)) { - if (c.streamReady) { - connOut = c; - break; - } else { - haveAnyConn = true; - break; - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - - boolean haveCache = false; - for (CacheEntry c : outCache) { - if (c.hostname != null && c.hostname.equals(hostname)) { - c.xml += xml; - c.updated = Instant.now(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(hostname, xml)); - } - - if (!haveAnyConn && !closeFlag.get()) { - try { - createDialbackConnection(hostname.toEscapedString(), null, null); - } catch (Exception e) { - logger.warn("dialback error", e); - } - } - } - - void createDialbackConnection(String to, String checkSID, String dbKey) throws Exception { - ConnectionOut connectionOut = new ConnectionOut(getJid(), Jid.of(to), null, null, checkSID, dbKey); - addConnectionOut(connectionOut, Optional.empty()); - service.submit(() -> { - try { - Socket socket = new Socket(); - socket.connect(DNSQueries.getServerAddress(to)); - connectionOut.setInputStream(socket.getInputStream()); - connectionOut.setOutputStream(socket.getOutputStream()); - addConnectionOut(connectionOut, Optional.of(socket)); - connectionOut.addChildParser(new JuickMessage()); - connectionOut.connect(); - } catch (IOException e) { - userService.getActiveJIDs().stream().filter(j -> Jid.of(j).getDomain().equals(to)) - .forEach(j -> { - userService.setActiveStatusForJID(j, UserService.ActiveStatus.Inactive); - logger.info("{} is inactive now", j); - }); - } - }); - } - - public void startDialback(Jid from, String streamId, String dbKey) throws Exception { - Optional<ConnectionOut> c = getConnectionOut(from, false); - if (c.isPresent()) { - c.get().sendDialbackVerify(streamId, dbKey); - } else { - createDialbackConnection(from.toEscapedString(), streamId, dbKey); - } - } - - public void addStanzaListener(StanzaListener listener) { - stanzaListeners.add(listener); - } - - public void onStanzaReceived(String xmlValue) { - Stanza stanza = parse(xmlValue); - stanzaListeners.forEach(l -> l.stanzaReceived(stanza)); - } - - public BasicXmppSession getSession() { - return session; - } - - public List<ConnectionIn> getInConnections() { - return inConnections; - } - - public Map<ConnectionOut, Optional<Socket>> getOutConnections() { - return outConnections; - } - - @Override - public boolean isTlsAvailable() { - return tlsConfigured; - } - - @Override - public void starttls(ConnectionIn connection) { - logger.debug("stream {} securing", connection.streamID); - connection.sendStanza("<proceed xmlns=\"" + Connection.NS_TLS + "\" />"); - try { - connection.setSocket(sc.getSocketFactory().createSocket(connection.getSocket(), connection.getSocket().getInetAddress().getHostAddress(), - connection.getSocket().getPort(), true)); - ((SSLSocket) connection.getSocket()).setUseClientMode(false); - ((SSLSocket) connection.getSocket()).startHandshake(); - connection.setSecured(true); - logger.debug("stream {} secured", connection.streamID); - connection.restartParser(); - } catch (XmlPullParserException | IOException sex) { - logger.warn("stream {} ssl error {}", connection.streamID, sex); - connection.sendStanza("<failed xmlns\"" + Connection.NS_TLS + "\" />"); - removeConnectionIn(connection); - connection.closeConnection(); - } - } - - @Override - public void proceed(ConnectionOut connection) { - try { - Socket socket = outConnections.get(connection).get(); - socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), true); - ((SSLSocket) socket).startHandshake(); - connection.setSecured(true); - logger.debug("stream {} secured", connection.getStreamID()); - connection.setInputStream(socket.getInputStream()); - connection.setOutputStream(socket.getOutputStream()); - connection.restartStream(); - connection.sendOpenStream(); - } catch (NoSuchElementException | XmlPullParserException | IOException sex) { - logger.error("s2s ssl error: {} {}, error {}", connection.to, connection.getStreamID(), sex); - connection.send("<failed xmlns\"" + Connection.NS_TLS + "\" />"); - removeConnectionOut(connection); - connection.logoff(); - } - } - - @Override - public void verify(ConnectionOut connection, String from, String type, String sid) { - if (from != null && from.equals(connection.to.toEscapedString()) && sid != null && !sid.isEmpty() && type != null) { - getConnectionIn(sid).ifPresent(c -> c.sendDialbackResult(Jid.of(from), type)); - } - } - - @Override - public void dialbackError(ConnectionOut connection, StreamError error) { - logger.warn("Stream error from {}: {}", connection.getStreamID(), error.getCondition()); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void finished(ConnectionOut connection, boolean dirty) { - logger.warn("stream to {} {} finished, dirty={}", connection.to, connection.getStreamID(), dirty); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void exception(ConnectionOut connection, Exception ex) { - logger.error("s2s out exception: {} {}, exception {}", connection.to, connection.getStreamID(), ex); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void ready(ConnectionOut connection) { - logger.debug("stream to {} {} ready", connection.to, connection.getStreamID()); - String cache = getFromCache(connection.to); - if (cache != null) { - logger.debug("stream to {} {} sending cache", connection.to, connection.getStreamID()); - connection.send(cache); - } - } - - @Override - public boolean securing(ConnectionOut connection) { - return !Arrays.asList(brokenSSLhosts).contains(connection.to.toEscapedString()); - } - - public Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - - public Jid getJid() { - return jid; - } - @Scheduled(fixedDelay = 10000) - public void cleanUp() { - Instant now = Instant.now(); - outConnections.keySet().stream().filter(c -> Duration.between(now, c.getUpdated()).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle outgoing connection to {}", c.to); - c.logoff(); - outConnections.remove(c); - }); - - inConnections.stream().filter(c -> Duration.between(now, c.updated).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle incoming connection from {}", c.from); - c.closeConnection(); - inConnections.remove(c); - }); - } - @PreDestroy - public void preDestroy() { - closeFlag.set(true); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Post.java b/juick-server/src/main/java/com/juick/server/api/Post.java index 3d9703fd..312df52d 100644 --- a/juick-server/src/main/java/com/juick/server/api/Post.java +++ b/juick-server/src/main/java/com/juick/server/api/Post.java @@ -71,7 +71,7 @@ public class Post { @Inject private UserService userService; @Inject - private ServerManager serverManager; + private XMPPConnection xmppConnection; @Inject private MessagesService messagesService; @Inject @@ -112,7 +112,7 @@ public class Post { throw new HttpBadRequestException(); } } - serverManager.processMessage(visitor, body, attachmentFName); + xmppConnection.processMessage(visitor, body, attachmentFName); } @PostMapping("/upload") public String doUploadFile(@RequestParam(required = true) MultipartFile attach) { @@ -169,7 +169,7 @@ public class Post { } } - return serverManager.processMessage(visitor, body, attachmentFName); + return xmppConnection.processMessage(visitor, body, attachmentFName); } Session session = Session.getDefaultInstance(new Properties()); @@ -231,7 +231,7 @@ public class Post { body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0]) : String.format("#%d %s", mid, body[0]); } - serverManager.processMessage(visitor, body[0], attachmentFName[0]); + xmppConnection.processMessage(visitor, body[0], attachmentFName[0]); } else { logger.info("not registered: {}", from); } diff --git a/juick-server/src/main/java/com/juick/server/xmpp/helpers/CommandResult.java b/juick-server/src/main/java/com/juick/server/xmpp/helpers/CommandResult.java deleted file mode 100644 index f952b579..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/helpers/CommandResult.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.juick.server.xmpp.helpers; - -import com.juick.Message; - -public class CommandResult { - private String text; - private Message newMessage; - - public String getText() { - return text; - } - - public Message getNewMessage() { - return newMessage; - } - public static CommandResult fromMessage(Message newMessage, String text) { - CommandResult result = new CommandResult(); - result.newMessage = newMessage; - result.text = text; - return result; - } - public static CommandResult fromString(String text) { - CommandResult result = new CommandResult(); - result.text = text; - return result; - } - -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java b/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java deleted file mode 100644 index 7978ceb3..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.helpers; - -import com.juick.server.xmpp.s2s.ConnectionIn; -import com.juick.server.xmpp.s2s.ConnectionOut; - -import java.util.List; -import java.util.Set; - -/** - * Created by vitalyster on 16.02.2017. - */ -public class XMPPStatus { - private List<ConnectionIn> inbound; - private Set<ConnectionOut> outbound; - - public List<ConnectionIn> getInbound() { - return inbound; - } - - public void setInbound(List<ConnectionIn> inbound) { - this.inbound = inbound; - } - - public Set<ConnectionOut> getOutbound() { - return outbound; - } - - public void setOutbound(Set<ConnectionOut> outbound) { - this.outbound = outbound; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/helpers/annotation/UserCommand.java b/juick-server/src/main/java/com/juick/server/xmpp/helpers/annotation/UserCommand.java deleted file mode 100644 index 383383c9..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/helpers/annotation/UserCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.helpers.annotation; - -import org.apache.commons.lang3.StringUtils; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by oxpa on 22.03.16. - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UserCommand { - /** - * - * @return a command pattern - */ - String pattern() default StringUtils.EMPTY; - - /** - * - * @return pattern flags - */ - int patternFlags() default 0; - - /** - * - * @return a string used in HELP command output. Basically, only 1 string - */ - String help() default StringUtils.EMPTY; -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java b/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java deleted file mode 100644 index 7532443c..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * 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 <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public abstract class Stream { - - public boolean isLoggedIn() { - return loggedIn; - } - - public void setLoggedIn(boolean loggedIn) { - this.loggedIn = loggedIn; - } - - Jid from; - public Jid to; - private InputStream is; - private OutputStream os; - private XmlPullParserFactory factory; - protected XmlPullParser parser; - private OutputStreamWriter writer; - StreamHandler streamHandler; - private boolean loggedIn; - private Instant created; - private Instant updated; - String streamId; - private boolean secured; - - public Stream(final Jid from, final Jid to, final InputStream is, final OutputStream os) throws XmlPullParserException { - this.from = from; - this.to = to; - this.is = is; - this.os = os; - factory = XmlPullParserFactory.newInstance(); - created = updated = Instant.now(); - streamId = UUID.randomUUID().toString(); - } - - void restartStream() throws XmlPullParserException { - parser = factory.newPullParser(); - parser.setInput(new InputStreamReader(is, StandardCharsets.UTF_8)); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - writer = new OutputStreamWriter(os, StandardCharsets.UTF_8); - } - - public void connect() { - try { - restartStream(); - handshake(); - parse(); - } catch (XmlPullParserException e) { - StreamError invalidXmlError = new StreamError("invalid-xml"); - send(invalidXmlError.toString()); - connectionFailed(new Exception(invalidXmlError.getCondition())); - } catch (IOException e) { - connectionFailed(e); - } - } - - public void setHandler(final StreamHandler streamHandler) { - this.streamHandler = streamHandler; - } - - public abstract void handshake() throws XmlPullParserException, IOException; - - public void logoff() { - setLoggedIn(false); - try { - writer.flush(); - writer.close(); - //TODO close parser - } catch (final Exception e) { - connectionFailed(e); - } - } - - public void send(final String str) { - try { - updated = Instant.now(); - writer.write(str); - writer.flush(); - } catch (final Exception e) { - connectionFailed(e); - } - } - - private void parse() throws IOException, XmlPullParserException { - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() == XmlPullParser.IGNORABLE_WHITESPACE) { - setUpdated(); - } - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - setUpdated(); - final String tag = parser.getName(); - switch (tag) { - case "message": - case "presence": - case "iq": - streamHandler.stanzaReceived(XmlUtils.parseToString(parser, false)); - break; - case "error": - StreamError error = StreamError.parse(parser); - connectionFailed(new Exception(error.getCondition())); - return; - default: - XmlUtils.skip(parser); - break; - } - } - } - - /** - * This method is used to be called on a parser or a connection error. - * It tries to close the XML-Reader and XML-Writer one last time. - */ - private void connectionFailed(final Exception ex) { - if (isLoggedIn()) { - try { - writer.close(); - //TODO close parser - } catch (Exception e) { - } - } - streamHandler.fail(ex); - } - - public Instant getCreated() { - return created; - } - - public Instant getUpdated() { - return updated; - } - public String getStreamId() { - return streamId; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void setUpdated() { - this.updated = Instant.now(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java deleted file mode 100644 index 5e2f6f82..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.juick.server.xmpp.router; - -import com.juick.xmpp.extensions.Handshake; -import org.apache.commons.codec.digest.DigestUtils; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.UUID; - -/** - * Created by vitalyster on 30.01.2017. - */ -public class StreamComponentServer extends Stream { - - private String streamId, secret; - - public String getStreamId() { - return streamId; - } - - - public StreamComponentServer(InputStream is, OutputStream os, String password) throws XmlPullParserException { - super(null, null, is, os); - secret = password; - streamId = UUID.randomUUID().toString(); - } - @Override - public void handshake() throws XmlPullParserException, IOException { - parser.next(); - if (!parser.getName().equals("stream") - || !parser.getNamespace(null).equals(StreamNamespaces.NS_COMPONENT_ACCEPT) - || !parser.getNamespace("stream").equals(StreamNamespaces.NS_STREAM)) { - throw new IOException("invalid stream"); - } - Jid domain = Jid.of(parser.getAttributeValue(null, "to")); - if (streamHandler.filter(null, domain)) { - send(new XMPPError(XMPPError.Type.cancel, "forbidden").toString()); - throw new IOException("invalid domain"); - } - from = domain; - to = domain; - send(String.format("<stream:stream xmlns:stream='%s' " + - "xmlns='%s' from='%s' id='%s'>", StreamNamespaces.NS_STREAM, StreamNamespaces.NS_COMPONENT_ACCEPT, from.asBareJid().toEscapedString(), streamId)); - Handshake handshake = Handshake.parse(parser); - boolean authenticated = handshake.getValue().equals(DigestUtils.sha1Hex(streamId + secret)); - setLoggedIn(authenticated); - if (!authenticated) { - send(new XMPPError(XMPPError.Type.cancel, "not-authorized").toString()); - streamHandler.fail(new IOException("stream:stream, failed authentication")); - return; - } - send(new Handshake().toString()); - streamHandler.ready(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java deleted file mode 100644 index 7eacfc94..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; - - -/** - * Created by vitalyster on 03.02.2017. - */ -public class StreamError { - - private String condition; - - public StreamError() {} - - public StreamError(String condition) { - this.condition = condition; - } - - public static StreamError parse(XmlPullParser parser) throws IOException, XmlPullParserException { - StreamError streamError = new StreamError(); - while (parser.next() == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) { - streamError.condition = tag; - } else { - XmlUtils.skip(parser); - } - } - return streamError; - } - - public String getCondition() { - return condition; - } - - @Override - public String toString() { - return String.format("<stream:error><%s xmlns='%s'/></stream:error>", condition, StreamNamespaces.NS_XMPP_STREAMS); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java deleted file mode 100644 index 43836c2d..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.juick.server.xmpp.router; - -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 01.02.2017. - */ -public interface StreamHandler { - void ready(); - void fail(final Exception ex); - boolean filter(Jid from, Jid to); - void stanzaReceived(String stanza); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java deleted file mode 100644 index 1b9b1965..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.juick.server.xmpp.router; - -public class StreamNamespaces { - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_SERVER = "jabber:server"; - public static final String NS_COMPONENT_ACCEPT = "jabber:component:accept"; - public static final String NS_XMPP_STREAMS = "urn:ietf:params:xml:ns:xmpp-streams"; -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java deleted file mode 100644 index 0cf9a3bc..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, ugnich - * - * 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 <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import org.apache.commons.text.StringEscapeUtils; - -/** - * - * @author ugnich - */ -public class XMPPError { - - public static final class Type { - - public static final String auth = "auth"; - public static final String cancel = "cancel"; - public static final String continue_ = "continue"; - public static final String modify = "modify"; - public static final String wait = "wait"; - } - private final static String TagName = "error"; - public String by = null; - private String type; - private String condition; - private String text = null; - - public XMPPError(String type, String condition) { - this.type = type; - this.condition = condition; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder("<").append(TagName).append(""); - if (by != null) { - str.append(" by=\"").append(StringEscapeUtils.escapeXml10(by)).append("\""); - } - if (type != null) { - str.append(" type=\"").append(StringEscapeUtils.escapeXml10(type)).append("\""); - } - - if (condition != null) { - str.append(">"); - str.append("<").append(StringEscapeUtils.escapeXml10(condition)).append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\""); - if (text != null) { - str.append(">").append(StringEscapeUtils.escapeXml10(text)).append("</").append(StringEscapeUtils.escapeXml10(condition)) - .append(">"); - } else { - str.append("/>"); - } - str.append("</").append(TagName).append(">"); - } else { - str.append("/>"); - } - - return str.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java deleted file mode 100644 index a262e941..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.juick.server.xmpp.router; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.Stanza; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ExecutorService; - -@Component -public class XMPPRouter implements StreamHandler { - private static final Logger logger = LoggerFactory.getLogger(XMPPRouter.class); - - @Inject - private ExecutorService service; - - private final List<StreamComponentServer> connections = Collections.synchronizedList(new ArrayList<>()); - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - - @Value("${router_port:5347}") - private int routerPort; - - @Inject - private XMPPServer xmppServer; - - @PostConstruct - public void init() { - logger.info("component router initialized"); - service.submit(() -> { - try { - listener = new ServerSocket(routerPort); - logger.info("component router listening on {}", routerPort); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - service.submit(() -> { - try { - StreamComponentServer client = new StreamComponentServer(socket.getInputStream(), socket.getOutputStream(), "secret"); - addConnectionIn(client); - client.setHandler(this); - client.connect(); - } catch (IOException e) { - logger.error("component error", e); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - }); - } - } catch (SocketException e) { - // shutdown - } catch (IOException e) { - logger.warn("io exception", e); - } - }); - } - - @PreDestroy - public void close() throws Exception { - if (!listener.isClosed()) { - listener.close(); - } - synchronized (getConnections()) { - for (Iterator<StreamComponentServer> i = getConnections().iterator(); i.hasNext(); ) { - StreamComponentServer c = i.next(); - c.logoff(); - i.remove(); - } - } - service.shutdown(); - logger.info("XMPP router destroyed"); - } - - private void addConnectionIn(StreamComponentServer c) { - synchronized (getConnections()) { - getConnections().add(c); - } - } - - private void sendOut(Stanza s) { - try { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - session.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - session.createMarshaller().marshal(s, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - String xml = stanzaWriter.toString(); - logger.info("XMPPRouter (out): {}", xml); - sendOut(s.getTo().getDomain(), xml); - } catch (XMLStreamException | JAXBException e1) { - logger.info("jaxb exception", e1); - } - } - - private void sendOut(String hostname, String xml) { - boolean haveAnyConn = false; - - StreamComponentServer connOut = null; - synchronized (getConnections()) { - for (StreamComponentServer c : getConnections()) { - if (c.to != null && c.to.getDomain().equals(hostname)) { - if (c.isLoggedIn()) { - connOut = c; - break; - } - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - xmppServer.sendOut(Jid.of(hostname), xml); - - } - - public List<StreamComponentServer> getConnections() { - return connections; - } - - private Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - @Override - public void stanzaReceived(String stanza) { - sendOut(parse(stanza)); - } - - @Override - public void ready() { - - } - - @Override - public void fail(Exception e) { - - } - - @Override - public boolean filter(Jid jid, Jid jid1) { - return false; - } -}
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java deleted file mode 100644 index 7579489f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * 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 <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import java.io.IOException; - -import org.apache.commons.text.StringEscapeUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class XmlUtils { - - public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { - String tag = parser.getName(); - while (parser.getName() != null && !(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - } - } - - public static String getTagText(XmlPullParser parser) throws XmlPullParserException, IOException { - String ret = ""; - String tag = parser.getName(); - - if (parser.next() == XmlPullParser.TEXT) { - ret = parser.getText(); - } - - while (!(parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - parser.next(); - } - - return ret; - } - - public static String parseToString(XmlPullParser parser, boolean skipXMLNS) throws XmlPullParserException, IOException { - String tag = parser.getName(); - StringBuilder ret = new StringBuilder("<").append(tag); - - // skipXMLNS for xmlns="jabber:client" - - String ns = parser.getNamespace(); - if (!skipXMLNS && ns != null && !ns.isEmpty()) { - ret.append(" xmlns=\"").append(ns).append("\""); - } - - for (int i = 0; i < parser.getAttributeCount(); i++) { - String attr = parser.getAttributeName(i); - if ((!skipXMLNS || !attr.equals("xmlns")) && !attr.contains(":")) { - ret.append(" ").append(attr).append("=\"").append(StringEscapeUtils.escapeXml10(parser.getAttributeValue(i))).append("\""); - } - } - ret.append(">"); - - while (!(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - int event = parser.getEventType(); - if (event == XmlPullParser.START_TAG) { - if (!parser.getName().contains(":")) { - ret.append(parseToString(parser, false)); - } else { - skip(parser); - } - } else if (event == XmlPullParser.TEXT) { - ret.append(StringEscapeUtils.escapeXml10(parser.getText())); - } - } - - ret.append("</").append(tag).append(">"); - return ret.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java deleted file mode 100644 index 33e875bd..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import rocks.xmpp.addr.Jid; - -import java.time.Instant; - -/** - * - * @author ugnich - */ -public class CacheEntry { - - public Jid hostname; - public Instant created; - public Instant updated; - public String xml; - - public CacheEntry(Jid hostname, String xml) { - this.hostname = hostname; - this.created = this.updated =Instant.now(); - this.xml = xml; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java deleted file mode 100644 index 6bf61169..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.juick.server.XMPPServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author ugnich - */ -public class Connection { - - protected static final Logger logger = LoggerFactory.getLogger(Connection.class); - - public String streamID; - public Instant created; - public Instant updated; - public long bytesLocal = 0; - public long packetsLocal = 0; - XMPPServer xmpp; - private Socket socket; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - XmlPullParser parser = factory.newPullParser(); - OutputStreamWriter writer; - private boolean secured = false; - - - - public Connection(XMPPServer xmpp) throws XmlPullParserException { - this.xmpp = xmpp; - created = updated = Instant.now(); - } - - public void logParser() { - if (streamID == null) { - return; - } - String tag = "IN: <" + parser.getName(); - for (int i = 0; i < parser.getAttributeCount(); i++) { - tag += " " + parser.getAttributeName(i) + "=\"" + parser.getAttributeValue(i) + "\""; - } - tag += ">...</" + parser.getName() + ">\n"; - logger.trace(tag); - } - - public void sendStanza(String xml) { - if (streamID != null) { - logger.trace("OUT: {}\n", xml); - } - try { - writer.write(xml); - writer.flush(); - } catch (IOException e) { - logger.error("send stanza failed", e); - } - - updated = Instant.now(); - bytesLocal += xml.length(); - packetsLocal++; - } - - public void closeConnection() { - if (streamID != null) { - logger.debug("closing stream {}", streamID); - } - - try { - writer.write("</stream:stream>"); - } catch (Exception e) { - } - - try { - writer.close(); - } catch (Exception e) { - } - - try { - socket.close(); - } catch (Exception e) { - } - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void restartParser() throws XmlPullParserException, IOException { - streamID = UUID.randomUUID().toString(); - parser = factory.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new InputStreamReader(socket.getInputStream())); - writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8); - } - - @JsonIgnore - public Socket getSocket() { - return socket; - } - - public void setSocket(Socket socket) { - this.socket = socket; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java deleted file mode 100644 index 9ee81d4d..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.XMPPServer; -import com.juick.xmpp.extensions.StreamError; -import com.juick.xmpp.utils.XmlUtils; -import org.apache.commons.lang3.StringUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.net.Socket; -import java.net.SocketException; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * @author ugnich - */ -public class ConnectionIn extends Connection implements Runnable { - - final public List<Jid> from = new CopyOnWriteArrayList<>(); - public Instant received; - public long packetsRemote = 0; - ConnectionListener listener; - - public ConnectionIn(XMPPServer xmpp, Socket socket) throws XmlPullParserException, IOException { - super(xmpp); - this.setSocket(socket); - restartParser(); - } - - @Override - public void run() { - try { - parser.next(); // stream:stream - updateTsRemoteData(); - if (!parser.getName().equals("stream") - || !parser.getNamespace("stream").equals(NS_STREAM)) { -// || !parser.getAttributeValue(null, "version").equals("1.0") -// || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) { - throw new Exception(String.format("stream from %s invalid", getSocket().getRemoteSocketAddress())); - } - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null) { - streamID = UUID.randomUUID().toString(); - } - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - String from = parser.getAttributeValue(null, "from"); - - if (Arrays.asList(xmpp.bannedHosts).contains(from)) { - closeConnection(); - return; - } - sendOpenStream(from, xmppversionnew); - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - updateTsRemoteData(); - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - logParser(); - - packetsRemote++; - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String dfrom = parser.getAttributeValue(null, "from"); - String to = parser.getAttributeValue(null, "to"); - logger.debug("stream from {} to {} {} asking for dialback", dfrom, to, streamID); - if (dfrom.endsWith(xmpp.getJid().toEscapedString()) && (dfrom.equals(xmpp.getJid().toEscapedString()) - || dfrom.endsWith("." + xmpp.getJid()))) { - logger.warn("stream from {} is invalid", dfrom); - break; - } - if (to != null && to.equals(xmpp.getJid().toEscapedString())) { - String dbKey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - xmpp.startDialback(Jid.of(dfrom), streamID, dbKey); - } else { - logger.warn("stream from " + dfrom + " " + streamID + " invalid to " + to); - break; - } - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String vfrom = parser.getAttributeValue(null, "from"); - String vto = parser.getAttributeValue(null, "to"); - String vid = parser.getAttributeValue(null, "id"); - String vkey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - final boolean[] valid = {false}; - if (vfrom != null && vto != null && vid != null && vkey != null) { - xmpp.getConnectionOut(Jid.of(vfrom), false).ifPresent(c -> { - String dialbackKey = c.dbKey; - valid[0] = vkey.equals(dialbackKey); - }); - } - if (valid[0]) { - sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='valid'/>"); - logger.debug("stream from {} {} dialback verify valid", vfrom, streamID); - } else { - sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='invalid'/>"); - logger.warn("stream from {} {} dialback verify invalid", vfrom, streamID); - } - } else if (tag.equals("presence") && checkFromTo(parser)) { - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} presence: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } else if (tag.equals("message") && checkFromTo(parser)) { - updateTsRemoteData(); - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} message: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - - } else if (tag.equals("iq") && checkFromTo(parser)) { - updateTsRemoteData(); - String type = parser.getAttributeValue(null, "type"); - String xml = XmlUtils.parseToString(parser, false); - if (type == null || !type.equals("error")) { - logger.debug("stream {} iq: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } - } else if (!isSecured() && tag.equals("starttls")) { - listener.starttls(this); - } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - sendOpenStream(null, true); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - logger.debug("Stream error from {}: {}", streamID, streamError.getCondition()); - xmpp.removeConnectionIn(this); - closeConnection(); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, true); - logger.warn("Unhandled stanza from {}: {}", streamID, unhandledStanza); - } - } - logger.warn("stream {} finished", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (EOFException | SocketException ex) { - logger.debug("stream {} closed (dirty)", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (Exception e) { - logger.debug("stream {} error {}", streamID, e); - xmpp.removeConnectionIn(this); - closeConnection(); - } - } - - void updateTsRemoteData() { - received = Instant.now(); - } - - void sendOpenStream(String from, boolean xmppversionnew) throws IOException { - String openStream = "<?xml version='1.0'?><stream:stream xmlns='jabber:server' " + - "xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - xmpp.getJid().toEscapedString() + "' id='" + streamID + "' version='1.0'>"; - if (xmppversionnew) { - openStream += "<stream:features>"; - if (listener != null && listener.isTlsAvailable() && !isSecured() && !Arrays.asList(xmpp.brokenSSLhosts).contains(from)) { - openStream += "<starttls xmlns=\"" + NS_TLS + "\"><optional/></starttls>"; - } - openStream += "</stream:features>"; - } - sendStanza(openStream); - } - - public void sendDialbackResult(Jid sfrom, String type) { - sendStanza("<db:result from='" + xmpp.getJid().toEscapedString() + "' to='" + sfrom + "' type='" + type + "'/>"); - if (type.equals("valid")) { - from.add(sfrom); - logger.debug("stream from {} {} ready", sfrom, streamID); - } - } - - boolean checkFromTo(XmlPullParser parser) throws Exception { - String cfrom = parser.getAttributeValue(null, "from"); - String cto = parser.getAttributeValue(null, "to"); - if (StringUtils.isNotEmpty(cfrom) && StringUtils.isNotEmpty(cto)) { - Jid jidfrom = Jid.of(cfrom); - for (Jid aFrom : from) { - if (aFrom.equals(Jid.of(jidfrom.getDomain()))) { - return true; - } - } - } - return false; - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java deleted file mode 100644 index fde7a0e7..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.xmpp.s2s; - -import com.juick.xmpp.extensions.StreamError; - -public interface ConnectionListener { - boolean isTlsAvailable(); - void starttls(ConnectionIn connection); - void proceed(ConnectionOut connection); - void verify(ConnectionOut connection, String from, String type, String sid); - void dialbackError(ConnectionOut connection, StreamError error); - void finished(ConnectionOut connection, boolean dirty); - void exception(ConnectionOut connection, Exception ex); - void ready(ConnectionOut connection); - boolean securing(ConnectionOut connection); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java deleted file mode 100644 index e3bd53e9..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.xmpp.s2s.util.DialbackUtils; -import com.juick.xmpp.Stream; -import com.juick.xmpp.extensions.StreamError; -import com.juick.xmpp.extensions.StreamFeatures; -import com.juick.xmpp.utils.XmlUtils; -import org.apache.commons.text.RandomStringGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.SocketException; -import java.util.UUID; - -/** - * @author ugnich - */ -public class ConnectionOut extends Stream { - protected static final Logger logger = LoggerFactory.getLogger(ConnectionOut.class); - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - private boolean secured = false; - - public boolean streamReady = false; - String checkSID = null; - String dbKey = null; - private String streamID; - ConnectionListener listener; - RandomStringGenerator generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build(); - - public ConnectionOut(Jid from, Jid to, InputStream is, OutputStream os, String checkSID, String dbKey) throws Exception { - super(from, to, is, os); - this.to = to; - this.checkSID = checkSID; - this.dbKey = dbKey; - if (dbKey == null) { - this.dbKey = DialbackUtils.generateDialbackKey(generator.generate(15), to, from, streamID); - } - streamID = UUID.randomUUID().toString(); - } - - public void sendOpenStream() throws IOException { - send("<?xml version='1.0'?><stream:stream xmlns='jabber:server' id='" + streamID + - "' xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - from.toEscapedString() + "' to='" + to.toEscapedString() + "' version='1.0'>"); - } - - void processDialback() throws Exception { - if (checkSID != null) { - sendDialbackVerify(checkSID, dbKey); - } - send("<db:result from='" + from.toEscapedString() + "' to='" + to.toEscapedString() + "'>" + - dbKey + "</db:result>"); - } - - @Override - public void handshake() { - try { - restartStream(); - - sendOpenStream(); - - parser.next(); // stream:stream - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null || streamID.isEmpty()) { - throw new Exception("stream to " + to + " invalid first packet"); - } - - logger.debug("stream to {} {} open", to, streamID); - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - if (!xmppversionnew) { - processDialback(); - } - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String type = parser.getAttributeValue(null, "type"); - if (type != null && type.equals("valid")) { - streamReady = true; - listener.ready(this); - } else { - logger.warn("stream to {} {} dialback fail", to, streamID); - } - XmlUtils.skip(parser); - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String from = parser.getAttributeValue(null, "from"); - String type = parser.getAttributeValue(null, "type"); - String sid = parser.getAttributeValue(null, "id"); - listener.verify(this, from, type, sid); - XmlUtils.skip(parser); - } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { - StreamFeatures features = StreamFeatures.parse(parser); - if (listener != null && !secured && features.STARTTLS >= 0 - && listener.securing(this)) { - logger.debug("stream to {} {} securing", to.toEscapedString(), streamID); - send("<starttls xmlns=\"" + NS_TLS + "\" />"); - } else { - processDialback(); - } - } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) { - listener.proceed(this); - } else if (secured && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - streamID = parser.getAttributeValue(null, "id"); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - listener.dialbackError(this, streamError); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, true); - logger.warn("Unhandled stanza from {} {} : {}", to, streamID, unhandledStanza); - } - } - listener.finished(this, false); - } catch (EOFException | SocketException eofex) { - listener.finished(this, true); - } catch (Exception e) { - listener.exception(this, e); - } - } - - public void sendDialbackVerify(String sid, String key) { - send("<db:verify from='" + from.toEscapedString() + "' to='" + to + "' id='" + sid + "'>" + - key + "</db:verify>"); - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } - - public String getStreamID() { - return streamID; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java deleted file mode 100644 index 1367d333..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.InetSocketAddress; -import java.util.Hashtable; -import java.util.Random; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -/** - * - * @author ugnich - */ -public class DNSQueries { - - private static final Logger logger = LoggerFactory.getLogger(DNSQueries.class); - - private static Random rand = new Random(); - - public static InetSocketAddress getServerAddress(String hostname) { - - String host = hostname; - int port = 5269; - - Hashtable<String, String> env = new Hashtable<>(5); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - try { - DirContext ctx = new InitialDirContext(env); - Attribute att = ctx.getAttributes("_xmpp-server._tcp." + hostname, new String[]{"SRV"}).get("SRV"); - - if (att != null && att.size() > 0) { - int i = rand.nextInt(att.size()); - String srv[] = att.get(i).toString().split(" "); - port = NumberUtils.toInt(srv[2], 5269); - host = srv[3]; - } - ctx.close(); - } catch (NamingException e) { - logger.debug("SRV record for {} is not resolved, falling back to A record", hostname); - } - return new InetSocketAddress(host, port); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java deleted file mode 100644 index 6932298f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - - -import rocks.xmpp.core.stanza.model.Stanza; - -/** - * Created by vitalyster on 07.12.2016. - */ -public interface StanzaListener { - void stanzaReceived(Stanza xmlValue); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java deleted file mode 100644 index d25dbad8..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 05.12.2016. - */ -public class DialbackUtils { - private DialbackUtils() { - throw new IllegalStateException(); - } - - public static String generateDialbackKey(String secret, Jid to, Jid from, String id) { - return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, DigestUtils.sha256(secret)) - .hmacHex(to.toEscapedString() + " " + from.toEscapedString() + " " + id); - } -} |