aboutsummaryrefslogtreecommitdiff
path: root/juick-xmpp
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2017-10-04 15:31:44 +0300
committerGravatar Vitaly Takmazov2017-10-09 14:34:51 +0300
commit51bfc341be1975b7a11e0b3a59cfbb4710e78446 (patch)
tree548bef919082406c510a7b653c2a2ac4e2aa4b2e /juick-xmpp
parent2996fb8279645b1329b38c5c7b6d81ba3c10612e (diff)
juick-xmpp-wip: router component
Diffstat (limited to 'juick-xmpp')
-rw-r--r--juick-xmpp/build.gradle4
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/CleaningUp.java (renamed from juick-xmpp/src/main/java/com/juick/components/s2s/CleaningUp.java)46
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/JuickBot.java (renamed from juick-xmpp/src/main/java/com/juick/components/s2s/JuickBot.java)150
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/XMPPConnection.java124
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/XMPPServer.java281
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java45
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/s2s/Connection.java3
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionIn.java30
-rw-r--r--juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionOut.java15
-rw-r--r--juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickMessage.java164
-rw-r--r--juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickUser.java65
-rw-r--r--juick-xmpp/src/test/java/com/juick/xmpp/XMPPTests.java76
12 files changed, 554 insertions, 449 deletions
diff --git a/juick-xmpp/build.gradle b/juick-xmpp/build.gradle
index 0da3231a..a68ac356 100644
--- a/juick-xmpp/build.gradle
+++ b/juick-xmpp/build.gradle
@@ -9,10 +9,6 @@ dependencies {
exclude group: 'xmlpull'
}
providedCompile 'xpp3:xpp3:1.1.4c'
- testCompile project(path: ':juick-core', configuration: 'testArtifacts')
- testCompile project(path: ':juick-server-web', configuration: 'testArtifacts')
- testCompile project(path: ':juick-server-jdbc', configuration: 'testArtifacts')
- testCompile ('net.sourceforge.htmlunit:htmlunit:2.27')
}
compileJava.options.encoding = 'UTF-8'
diff --git a/juick-xmpp/src/main/java/com/juick/components/s2s/CleaningUp.java b/juick-xmpp/src/main/java/com/juick/components/CleaningUp.java
index 45933141..067af6e8 100644
--- a/juick-xmpp/src/main/java/com/juick/components/s2s/CleaningUp.java
+++ b/juick-xmpp/src/main/java/com/juick/components/CleaningUp.java
@@ -15,20 +15,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package com.juick.components.s2s;
+package com.juick.components;
-import com.juick.components.XMPPServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
import javax.inject.Inject;
-import java.util.Iterator;
/**
*
* @author ugnich
*/
+@Component
public class CleaningUp {
private static final Logger logger = LoggerFactory.getLogger(CleaningUp.class);
@@ -40,28 +40,22 @@ public class CleaningUp {
public void cleanUp() {
long now = System.currentTimeMillis();
- synchronized (xmpp.getOutConnections()) {
- for (Iterator<ConnectionOut> i = xmpp.getOutConnections().iterator(); i.hasNext(); ) {
- ConnectionOut c = i.next();
- int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0);
- if (inactive > 900) {
- logger.info("closing idle outgoing connection to {}", c.to);
- c.closeConnection();
- i.remove();
- }
- }
- }
-
- synchronized (xmpp.getInConnections()) {
- for (Iterator<ConnectionIn> i = xmpp.getInConnections().iterator(); i.hasNext(); ) {
- ConnectionIn c = i.next();
- int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0);
- if (inactive > 900) {
- logger.info("closing idle incoming connection from {}", c.from);
- c.closeConnection();
- i.remove();
- }
- }
- }
+ xmpp.getOutConnections().stream().filter(c -> {
+ int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0);
+ return inactive > 900;
+ }).forEach(c -> {
+ logger.info("closing idle outgoing connection to {}", c.to);
+ c.closeConnection();
+ xmpp.getOutConnections().remove(c);
+ });
+
+ xmpp.getInConnections().stream().filter(c -> {
+ int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0);
+ return inactive > 900;
+ }).forEach(c -> {
+ logger.info("closing idle incoming connection from {}", c.from);
+ c.closeConnection();
+ xmpp.getInConnections().remove(c);
+ });
}
}
diff --git a/juick-xmpp/src/main/java/com/juick/components/s2s/JuickBot.java b/juick-xmpp/src/main/java/com/juick/components/JuickBot.java
index 2a7e02e9..4f838344 100644
--- a/juick-xmpp/src/main/java/com/juick/components/s2s/JuickBot.java
+++ b/juick-xmpp/src/main/java/com/juick/components/JuickBot.java
@@ -15,24 +15,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package com.juick.components.s2s;
+package com.juick.components;
import com.juick.User;
-import com.juick.components.XMPPServer;
+import com.juick.components.s2s.StanzaListener;
import com.juick.formatters.PlainTextFormatter;
+import com.juick.service.*;
import org.apache.commons.lang3.StringUtils;
import org.ocpsoft.prettytime.PrettyTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
import rocks.xmpp.addr.Jid;
-import rocks.xmpp.core.stanza.model.Message;
-import rocks.xmpp.core.stanza.model.Presence;
-import rocks.xmpp.core.stanza.model.Stanza;
-import rocks.xmpp.core.stanza.model.StanzaError;
+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 javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Collections;
import java.util.List;
@@ -45,21 +46,45 @@ import java.util.stream.Collectors;
*
* @author ugnich
*/
+@Component
public class JuickBot implements StanzaListener, AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(JuickBot.class);
- XMPPServer xmpp;
- PrettyTime pt;
+ @Inject
+ private XMPPServer xmpp;
+ @Inject
+ private XMPPConnection router;
+ @Value("${xmppbot_jid}")
+ private String xmppbotJidStr;
+
+ private Jid jid;
+
+ private PrettyTime pt;
@Inject
- public JuickBot(XMPPServer xmpp) {
- this.xmpp = xmpp;
+ public MessagesService messagesService;
+ @Inject
+ public UserService userService;
+ @Inject
+ public TagService tagService;
+ @Inject
+ public PMQueriesService pmQueriesService;
+ @Inject
+ public ShowQueriesService showQueriesService;
+
+ @PostConstruct
+ public void init() {
xmpp.addStanzaListener(this);
+ jid = Jid.of(xmppbotJidStr);
broadcastPresence(null);
pt = new PrettyTime(new Locale("ru"));
}
+ public Jid getJid() {
+ return jid;
+ }
+
private static final String HELPTEXT =
"@username text - Send private message\n"
+ "*tagname Blah-blah-blah - Post a message with tag 'tagname'\n"
@@ -96,7 +121,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
public boolean incomingPresence(Presence p) {
final String username = p.getTo().getLocal();
- final boolean toJuick = username.equals(xmpp.getJid().getLocal());
+ final boolean toJuick = username.equals(jid.getLocal());
if (p.getType() == null) {
Presence reply = new Presence();
@@ -108,12 +133,12 @@ public class JuickBot implements StanzaListener, AutoCloseable {
} else if (p.getType().equals(Presence.Type.PROBE)) {
int uid_to = 0;
if (!toJuick) {
- uid_to = xmpp.userService.getUIDbyName(username);
+ uid_to = userService.getUIDbyName(username);
}
if (toJuick || uid_to > 0) {
Presence reply = new Presence();
- reply.setFrom(p.getTo().withResource(xmpp.getJid().getResource()));
+ reply.setFrom(p.getTo().withResource(jid.getResource()));
reply.setTo(p.getFrom());
reply.setPriority((byte)10);
xmpp.sendOut(ClientPresence.from(reply));
@@ -133,9 +158,9 @@ public class JuickBot implements StanzaListener, AutoCloseable {
if (toJuick) {
canSubscribe = true;
} else {
- int uid_to = xmpp.userService.getUIDbyName(username);
+ int uid_to = userService.getUIDbyName(username);
if (uid_to > 0) {
- xmpp.pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
+ pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
canSubscribe = true;
}
}
@@ -147,7 +172,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
reply.setType(Presence.Type.SUBSCRIBED);
xmpp.sendOut(ClientPresence.from(reply));
- reply.setFrom(reply.getFrom().withResource(xmpp.getJid().getResource()));
+ reply.setFrom(reply.getFrom().withResource(jid.getResource()));
reply.setPriority((byte) 10);
reply.setType(null);
xmpp.sendOut(ClientPresence.from(reply));
@@ -165,9 +190,9 @@ public class JuickBot implements StanzaListener, AutoCloseable {
}
} else if (p.getType().equals(Presence.Type.UNSUBSCRIBE)) {
if (!toJuick) {
- int uid_to = xmpp.userService.getUIDbyName(username);
+ int uid_to = userService.getUIDbyName(username);
if (uid_to > 0) {
- xmpp.pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
+ pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString());
}
}
@@ -190,9 +215,9 @@ public class JuickBot implements StanzaListener, AutoCloseable {
User user_from;
String signuphash = StringUtils.EMPTY;
- user_from = xmpp.userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString());
+ user_from = userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString());
if (user_from == null) {
- signuphash = xmpp.userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString());
+ signuphash = userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString());
}
if (user_from == null) {
@@ -200,7 +225,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
reply.setFrom(msg.getTo());
reply.setTo(msg.getFrom());
reply.setType(Message.Type.CHAT);
- if (username.equals(xmpp.getJid().getLocal())) {
+ if (username.equals(jid.getLocal())) {
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.");
} else {
reply.setBody("Внимание, системное сообщение!\nВаш JabberID не обнаружен в списке доверенных. Для того, чтобы отправить сообщение пользователю " + username + "@juick.com, пожалуйста зарегистрируйте свой JabberID в системе: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nWarning, system message!\nYour JabberID is not found in our server's white list. To send a message to " + username + "@juick.com, 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.");
@@ -209,11 +234,11 @@ public class JuickBot implements StanzaListener, AutoCloseable {
return true;
}
- if (username.equals(xmpp.getJid().getLocal())) {
+ if (username.equals(jid.getLocal())) {
return incomingMessageJuick(user_from, msg);
}
- int uid_to = xmpp.userService.getUIDbyName(username);
+ int uid_to = userService.getUIDbyName(username);
if (uid_to == 0) {
Message reply = new Message();
@@ -227,36 +252,36 @@ public class JuickBot implements StanzaListener, AutoCloseable {
}
boolean success = false;
- if (!xmpp.userService.isInBLAny(uid_to, user_from.getUid())) {
- success = xmpp.pmQueriesService.createPM(user_from.getUid(), uid_to, msg.getBody());
+ if (!userService.isInBLAny(uid_to, user_from.getUid())) {
+ success = pmQueriesService.createPM(user_from.getUid(), uid_to, msg.getBody());
}
if (success) {
Message m = new Message();
- m.setFrom(xmpp.getJid().asBareJid());
+ m.setFrom(jid.asBareJid());
m.setTo(Jid.of(Integer.toString(uid_to), "push.juick.com", null));
com.juick.Message jmsg = new com.juick.Message();
jmsg.setUser(user_from);
jmsg.setText(msg.getBody());
m.addExtension(jmsg);
- xmpp.getRouter().sendStanza(m);
+ router.sendStanza(m);
m.setTo(Jid.of(Integer.toString(uid_to), "ws.juick.com", null));
- xmpp.getRouter().sendStanza(m);
+ router.sendStanza(m);
List<String> jids;
boolean inroster = false;
- jids = xmpp.userService.getJIDsbyUID(uid_to);
- for (String jid : jids) {
+ jids = userService.getJIDsbyUID(uid_to);
+ for (String userJid : jids) {
Message mm = new Message();
- mm.setTo(Jid.of(jid));
+ mm.setTo(Jid.of(userJid));
mm.setType(Message.Type.CHAT);
- inroster = xmpp.pmQueriesService.havePMinRoster(user_from.getUid(), jid);
+ inroster = pmQueriesService.havePMinRoster(user_from.getUid(), userJid);
if (inroster) {
mm.setFrom(Jid.of(jmsg.getUser().getName(), "juick.com", "Juick"));
mm.setBody(msg.getBody());
} else {
- mm.setFrom(xmpp.getJid());
+ mm.setFrom(jid);
mm.setBody("Private message from @" + jmsg.getUser().getName() + ":\n" + msg.getBody());
}
xmpp.sendOut(ClientMessage.from(mm));
@@ -318,7 +343,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
private void commandPing(Message m) {
Presence p = new Presence(m.getFrom());
- p.setFrom(xmpp.getJid());
+ p.setFrom(jid);
p.setPriority((byte) 10);
xmpp.sendOut(ClientPresence.from(p));
@@ -330,7 +355,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
}
private void commandLogin(Message m, User user_from) {
- sendReply(m.getFrom(), "http://juick.com/login?hash=" + xmpp.userService.getHashByUID(user_from.getUid()));
+ sendReply(m.getFrom(), "http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid()));
}
private void commandPM(Message m, User user_from, String user_to, String body) {
@@ -341,15 +366,15 @@ public class JuickBot implements StanzaListener, AutoCloseable {
boolean haveInRoster = false;
if (user_to.indexOf('@') > 0) {
- uid_to = xmpp.userService.getUIDbyJID(user_to);
+ uid_to = userService.getUIDbyJID(user_to);
} else {
- uid_to = xmpp.userService.getUIDbyName(user_to);
+ uid_to = userService.getUIDbyName(user_to);
}
if (uid_to > 0) {
- if (!xmpp.userService.isInBLAny(uid_to, user_from.getUid())) {
- if (xmpp.pmQueriesService.createPM(user_from.getUid(), uid_to, body)) {
- jids_to = xmpp.userService.getJIDsbyUID(uid_to);
+ if (!userService.isInBLAny(uid_to, user_from.getUid())) {
+ if (pmQueriesService.createPM(user_from.getUid(), uid_to, body)) {
+ jids_to = userService.getJIDsbyUID(uid_to);
ret = 200;
} else {
ret = 500;
@@ -363,27 +388,27 @@ public class JuickBot implements StanzaListener, AutoCloseable {
if (ret == 200) {
Message msg = new Message();
- msg.setFrom(xmpp.getJid().asBareJid());
+ msg.setFrom(jid.asBareJid());
msg.setTo(Jid.of(Integer.toString(uid_to), "push.juick.com", null));
com.juick.Message jmsg = new com.juick.Message();
jmsg.setUser(user_from);
jmsg.setText(body);
msg.addExtension(jmsg);
- xmpp.getRouter().sendStanza(msg);
+ router.sendStanza(msg);
msg.setTo(Jid.of(Integer.toString(uid_to), "ws.juick.com", null));
- xmpp.getRouter().sendStanza(msg);
+ router.sendStanza(msg);
- for (String jid : jids_to) {
+ for (String userJid : jids_to) {
Message mm = new Message();
- mm.setTo(Jid.of(jid));
+ mm.setTo(Jid.of(userJid));
mm.setType(Message.Type.CHAT);
- haveInRoster = xmpp.pmQueriesService.havePMinRoster(user_from.getUid(), jid);
+ haveInRoster = pmQueriesService.havePMinRoster(user_from.getUid(), userJid);
if (haveInRoster) {
mm.setFrom(Jid.of(user_from.getName(), "juick.com", "Juick"));
mm.setBody(body);
} else {
- mm.setFrom(xmpp.getJid());
+ mm.setFrom(jid);
mm.setBody("Private message from @" + user_from.getName() + ":\n" + body);
}
xmpp.sendOut(ClientMessage.from(mm));
@@ -404,8 +429,8 @@ public class JuickBot implements StanzaListener, AutoCloseable {
}
private void commandBLShow(Message m, User user_from) {
- List<User> blusers = xmpp.userService.getUserBLUsers(user_from.getUid());
- List<String> bltags = xmpp.tagService.getUserBLTags(user_from.getUid());
+ List<User> blusers = userService.getUserBLUsers(user_from.getUid());
+ List<String> bltags = tagService.getUserBLTags(user_from.getUid());
String txt = StringUtils.EMPTY;
if (bltags.size() > 0) {
@@ -430,17 +455,17 @@ public class JuickBot implements StanzaListener, AutoCloseable {
}
boolean commandLast(Jid jidFrom) {
- User user = xmpp.userService.getUserByJID(jidFrom.asBareJid().toEscapedString());
+ User user = userService.getUserByJID(jidFrom.asBareJid().toEscapedString());
sendReply(jidFrom, "Last messages:\n"
- + printMessages(xmpp.messagesService.getAll(user.getUid(), 0), true));
+ + printMessages(messagesService.getAll(user.getUid(), 0), true));
return true;
}
boolean commandUsers(Jid jidFrom) {
StringBuilder msg = new StringBuilder();
msg.append("Recommended blogs");
- User currentUser = xmpp.userService.getUserByJID(jidFrom.asBareJid().toEscapedString());
- List<String> recommendedUsers = xmpp.showQueriesService.getRecommendedUsers(currentUser);
+ User currentUser = userService.getUserByJID(jidFrom.asBareJid().toEscapedString());
+ List<String> recommendedUsers = showQueriesService.getRecommendedUsers(currentUser);
if (recommendedUsers.size() > 0) {
for (String user : recommendedUsers) {
msg.append("\n@").append(user);
@@ -449,7 +474,7 @@ public class JuickBot implements StanzaListener, AutoCloseable {
msg.append("\nNo recommendations now. Subscribe to more blogs. ;)");
}
msg.append("\n\nTop 10 personal blogs:");
- List<String> topUsers = xmpp.showQueriesService.getTopUsers();
+ List<String> topUsers = showQueriesService.getTopUsers();
if (topUsers.size() > 0) {
for (String user : topUsers) {
msg.append("\n@").append(user);
@@ -463,13 +488,17 @@ public class JuickBot implements StanzaListener, AutoCloseable {
void sendReply(Jid jidTo, String txt) {
Message reply = new Message();
- reply.setFrom(xmpp.getJid());
+ reply.setFrom(jid);
reply.setTo(jidTo);
reply.setType(Message.Type.CHAT);
reply.setBody(txt);
xmpp.sendOut(ClientMessage.from(reply));
}
+ void sendNotification(Stanza stanza) {
+ xmpp.sendOut(stanza);
+ }
+
@Override
public void stanzaReceived(Stanza xmlValue) {
if (xmlValue instanceof Presence) {
@@ -480,24 +509,27 @@ public class JuickBot implements StanzaListener, AutoCloseable {
} else if (xmlValue instanceof Message) {
Message msg = (Message) xmlValue;
if (!incomingMessage(msg)) {
- xmpp.getRouter().sendStanza(msg);
+ router.sendStanza(msg);
}
+ } else if (xmlValue instanceof IQ) {
+ IQ iq = (IQ) xmlValue;
+ router.sendStanza(iq);
}
}
String printMessages(List<Integer> mids, boolean crop) {
- return xmpp.messagesService.getMessages(mids).stream()
+ return messagesService.getMessages(mids).stream()
.sorted(Collections.reverseOrder())
.map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n\n"));
}
void broadcastPresence(Presence.Type type) {
Presence presence = new Presence();
- presence.setFrom(xmpp.getJid());
+ presence.setFrom(jid);
if (type != null) {
presence.setType(type);
}
- xmpp.userService.getActiveJIDs().forEach(j -> {
+ userService.getActiveJIDs().forEach(j -> {
try {
presence.setTo(Jid.of(j));
xmpp.sendOut(ClientPresence.from(presence));
diff --git a/juick-xmpp/src/main/java/com/juick/components/XMPPConnection.java b/juick-xmpp/src/main/java/com/juick/components/XMPPConnection.java
index a124c461..1bfaf429 100644
--- a/juick-xmpp/src/main/java/com/juick/components/XMPPConnection.java
+++ b/juick-xmpp/src/main/java/com/juick/components/XMPPConnection.java
@@ -18,14 +18,19 @@
package com.juick.components;
import com.juick.User;
+import com.juick.components.s2s.BasicXmppSession;
import com.juick.server.helpers.UserInfo;
+import com.juick.service.MessagesService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.UserService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.core.env.Environment;
+import org.springframework.beans.factory.annotation.Value;
+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;
@@ -41,7 +46,6 @@ 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.receipts.MessageDeliveryReceiptsManager;
import rocks.xmpp.extensions.vcard.temp.model.VCard;
import rocks.xmpp.util.XmppUtils;
@@ -61,27 +65,44 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
/**
* @author ugnich
*/
+@Component
public class XMPPConnection implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(XMPPConnection.class);
private ExternalComponent router;
@Inject
- private XMPPServer xmpp;
+ private JuickBot bot;
+
+ @Value("${componentname:localhost}")
+ private String componentName;
+ @Value("${component_port:5347}")
+ private int componentPort;
+ @Value("${xmpp_password:secret}")
+ private String password;
+ @Value("${upload_tmp_dir:/tmp}")
+ private String tmpDir;
+
+ @Inject
+ public MessagesService messagesService;
+ @Inject
+ public UserService userService;
@Inject
- private Environment env;
+ public SubscriptionService subscriptionService;
+ @Inject
+ private BasicXmppSession session;
+ @Inject
+ private ExecutorService service;
@PostConstruct
public void init() {
- logger.info("stream router start");
- String componentName = env.getProperty("componentname");
- int componentPort = NumberUtils.toInt(env.getProperty("component_port"), 5347);
- String password = env.getProperty("xmpp_password");
- router = ExternalComponent.create(componentName, password, xmpp.getSession().getConfiguration(), "localhost", componentPort);
+ logger.info("stream router start connecting to {}", componentPort);
+ router = ExternalComponent.create(componentName, password, session.getConfiguration(), "localhost", componentPort);
PingManager pingManager = router.getManager(PingManager.class);
pingManager.setEnabled(true);
router.disableFeature(EntityCapabilities.NAMESPACE);
@@ -98,13 +119,13 @@ public class XMPPConnection implements AutoCloseable {
router.addIQHandler(VCard.class, new AbstractIQHandler(IQ.Type.GET) {
@Override
protected IQ processRequest(IQ iq) {
- if (iq.getTo().equals(xmpp.getJid()) || iq.getTo().asBareJid().equals(xmpp.getJid().asBareJid())
- || iq.getTo().asBareJid().toEscapedString().equals(xmpp.getJid().getDomain())) {
+ if (iq.getTo().equals(bot.getJid()) || iq.getTo().asBareJid().equals(bot.getJid().asBareJid())
+ || iq.getTo().asBareJid().toEscapedString().equals(bot.getJid().getDomain())) {
return iq.createResult(vCard);
}
- User user = xmpp.userService.getUserByName(iq.getTo().getLocal());
+ User user = userService.getUserByName(iq.getTo().getLocal());
if (user.getUid() > 0) {
- UserInfo info = xmpp.userService.getUserInfo(user);
+ UserInfo info = userService.getUserInfo(user);
VCard userVCard = new VCard();
userVCard.setFormattedName(info.getFullName());
userVCard.setNickname(user.getName());
@@ -124,7 +145,7 @@ public class XMPPConnection implements AutoCloseable {
router.addInboundMessageListener(e -> {
Message message = e.getMessage();
Jid jid = message.getTo();
- if (jid.getDomain().equals(Jid.of(componentName).getDomain())) {
+ if (jid.getDomain().equals(router.getDomain().toEscapedString())) {
com.juick.Message jmsg = message.getExtension(com.juick.Message.class);
if (jmsg != null) {
if (jid.getLocal().equals("recomm")) {
@@ -137,8 +158,8 @@ public class XMPPConnection implements AutoCloseable {
}
}
}
- } else if (jid.getDomain().endsWith(xmpp.HOSTNAME) && (jid.getDomain().equals(xmpp.HOSTNAME)
- || jid.getDomain().endsWith("." + xmpp.HOSTNAME))) {
+ } else if (jid.getDomain().endsWith(bot.getJid().getDomain()) && (jid.getDomain().equals(bot.getJid().getDomain())
+ || jid.getDomain().endsWith("." + bot.getJid().getDomain()))) {
if (logger.isInfoEnabled()) {
try {
logger.info("unhandled message: {}", stanzaToString(message));
@@ -147,17 +168,16 @@ public class XMPPConnection implements AutoCloseable {
}
}
} else {
- route(jid.getDomain(), ClientMessage.from(message));
+ route(ClientMessage.from(message));
}
});
router.addInboundIQListener(e -> {
IQ iq = e.getIQ();
Jid jid = iq.getTo();
- if (!jid.getDomain().equals(xmpp.HOSTNAME)) {
- route(jid.getDomain(), iq);
+ if (!jid.getDomain().equals(bot.getJid().getDomain())) {
+ route(iq);
}
});
- String tmpDir = env.getProperty("upload_tmp_dir", "/tmp");
FileTransferManager fileTransferManager = router.getManager(FileTransferManager.class);
fileTransferManager.addFileTransferOfferListener(e -> {
try {
@@ -179,7 +199,7 @@ public class XMPPConnection implements AutoCloseable {
Message msg = new Message();
msg.setType(Message.Type.CHAT);
msg.setFrom(e.getInitiator());
- msg.setTo(xmpp.getJid());
+ msg.setTo(bot.getJid());
msg.setBody(e.getDescription());
try {
String attachmentUrl = String.format("juick://%s", targetFilename);
@@ -192,7 +212,7 @@ public class XMPPConnection implements AutoCloseable {
logger.info("transfer failed", ft.getException());
Message msg = new Message();
msg.setType(Message.Type.CHAT);
- msg.setFrom(xmpp.getJid());
+ msg.setFrom(bot.getJid());
msg.setTo(e.getInitiator());
msg.setBody("File transfer failed, please report to us");
router.sendMessage(msg);
@@ -210,11 +230,19 @@ public class XMPPConnection implements AutoCloseable {
logger.error("ft error", e1);
}
});
- try {
- router.connect();
- } catch (XmppException e) {
- logger.warn("xmpp exception", e);
- }
+ router.addConnectionListener(event -> {
+ if (event.getType().equals(rocks.xmpp.core.session.ConnectionEvent.Type.RECONNECTION_SUCCEEDED)) {
+ logger.info("component connected");
+ }
+ });
+ service.submit(() -> {
+ try {
+ Thread.sleep(3000);
+ router.connect();
+ } catch (InterruptedException | XmppException e) {
+ logger.warn("xmpp exception", e);
+ }
+ });
}
String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException {
@@ -227,11 +255,11 @@ public class XMPPConnection implements AutoCloseable {
return stanzaWriter.toString();
}
- void route(String domain, Stanza stanza) {
+ void route(Stanza stanza) {
try {
String xml = stanzaToString(stanza);
logger.info("stream router (out): {}", xml);
- xmpp.sendOut(domain, xml);
+ bot.sendNotification(stanza);
} catch (XMLStreamException | JAXBException e) {
logger.error("JAXB exception", e);
}
@@ -248,16 +276,16 @@ public class XMPPConnection implements AutoCloseable {
List<String> jids = new ArrayList<>();
if (jmsg.FriendsOnly) {
- jids = xmpp.subscriptionService.getJIDSubscribedToUser(jmsg.getUser().getUid(), jmsg.FriendsOnly);
+ jids = subscriptionService.getJIDSubscribedToUser(jmsg.getUser().getUid(), jmsg.FriendsOnly);
} else {
- List<User> users = xmpp.subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid());
+ List<User> users = subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid());
for (User user : users) {
- for (String jid : xmpp.userService.getJIDsbyUID(user.getUid())) {
+ for (String jid : userService.getJIDsbyUID(user.getUid())) {
jids.add(jid);
}
}
}
- com.juick.Message fullMsg = xmpp.messagesService.getMessage(jmsg.getMid());
+ com.juick.Message fullMsg = messagesService.getMessage(jmsg.getMid());
String txt = "@" + jmsg.getUser().getName() + ":" + fullMsg.getTagsString() + "\n";
String attachment = fullMsg.getAttachmentURL();
if (attachment != null) {
@@ -269,7 +297,7 @@ public class XMPPConnection implements AutoCloseable {
Nickname nick = new Nickname("@" + jmsg.getUser().getName());
Message msg = new Message();
- msg.setFrom(xmpp.getJid());
+ msg.setFrom(bot.getJid());
msg.setBody(txt);
msg.setType(Message.Type.CHAT);
msg.setThread("juick-" + jmsg.getMid());
@@ -285,7 +313,7 @@ public class XMPPConnection implements AutoCloseable {
}
for (String jid : jids) {
msg.setTo(Jid.of(jid));
- route(msg.getTo().getDomain(), ClientMessage.from(msg));
+ route(ClientMessage.from(msg));
}
}
@@ -294,11 +322,11 @@ public class XMPPConnection implements AutoCloseable {
String replyQuote;
String replyTo;
- users = xmpp.subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid());
- com.juick.Message replyMessage = jmsg.getReplyto() > 0 ? xmpp.messagesService.getReply(jmsg.getMid(), jmsg.getReplyto())
- : xmpp.messagesService.getMessage(jmsg.getMid());
+ users = subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid());
+ 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 = xmpp.messagesService.getReply(jmsg.getMid(), jmsg.getRid());
+ com.juick.Message fullReply = messagesService.getReply(jmsg.getMid(), jmsg.getRid());
replyQuote = fullReply.getReplyQuote();
String txt = "Reply by @" + jmsg.getUser().getName() + ":\n" + replyQuote + "\n@" + replyTo + " ";
@@ -309,22 +337,22 @@ public class XMPPConnection implements AutoCloseable {
txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMid() + "/" + jmsg.getRid() + " http://juick.com/" + jmsg.getMid() + "#" + jmsg.getRid();
Message msg = new Message();
- msg.setFrom(xmpp.getJid());
+ msg.setFrom(bot.getJid());
msg.setBody(txt);
msg.setType(Message.Type.CHAT);
msg.addExtension(jmsg);
for (User user : users) {
- for (String jid : xmpp.userService.getJIDsbyUID(user.getUid())) {
+ for (String jid : userService.getJIDsbyUID(user.getUid())) {
msg.setTo(Jid.of(jid));
- route(msg.getTo().getDomain(), ClientMessage.from(msg));
+ route(ClientMessage.from(msg));
}
}
}
public void sendJuickRecommendation(com.juick.Message recomm) {
List<User> users;
- com.juick.Message jmsg = xmpp.messagesService.getMessage(recomm.getMid());
- users = xmpp.subscriptionService.getUsersSubscribedToUserRecommendations(recomm.getUser().getUid(),
+ com.juick.Message jmsg = messagesService.getMessage(recomm.getMid());
+ users = subscriptionService.getUsersSubscribedToUserRecommendations(recomm.getUser().getUid(),
recomm.getMid(), jmsg.getUser().getUid());
String txt = "Recommended by @" + recomm.getUser().getName() + ":\n";
@@ -347,7 +375,7 @@ public class XMPPConnection implements AutoCloseable {
Nickname nick = new Nickname("@" + jmsg.getUser().getName());
Message msg = new Message();
- msg.setFrom(xmpp.getJid());
+ msg.setFrom(bot.getJid());
msg.setBody(txt);
msg.setType(Message.Type.CHAT);
msg.setThread("juick-" + jmsg.getMid());
@@ -363,9 +391,9 @@ public class XMPPConnection implements AutoCloseable {
}
for (User user : users) {
- for (String jid : xmpp.userService.getJIDsbyUID(user.getUid())) {
+ for (String jid : userService.getJIDsbyUID(user.getUid())) {
msg.setTo(Jid.of(jid));
- route(msg.getTo().getDomain(), ClientMessage.from(msg));
+ route(ClientMessage.from(msg));
}
}
}
diff --git a/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java b/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java
index e1ca72ad..1df7d575 100644
--- a/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java
+++ b/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java
@@ -18,20 +18,15 @@
package com.juick.components;
import com.juick.components.s2s.*;
-import com.juick.service.*;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.core.env.Environment;
+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.session.Extension;
-import rocks.xmpp.core.session.XmppSessionConfiguration;
-import rocks.xmpp.core.session.debug.LogbackDebugger;
import rocks.xmpp.core.stanza.model.Stanza;
import rocks.xmpp.util.XmppUtils;
+import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;
@@ -40,180 +35,126 @@ import java.io.IOException;
import java.io.StringWriter;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.SocketException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
-import java.util.*;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
/**
* @author ugnich
*/
+@Component
public class XMPPServer implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(XMPPServer.class);
+ @Inject
public ExecutorService service;
-
+ @Value("${hostname}")
public String HOSTNAME;
+ @Value("${s2s_port:5269}")
+ private int s2sPort;
+ @Value("${keystore}")
public String keystore;
+ @Value("${keystore_password}")
public String keystorePassword;
- public List<String> brokenSSLhosts;
- public List<String> bannedHosts;
+ @Value("${broken_ssl_hosts}")
+ public String[] brokenSSLhosts;
+ @Value("${banned_hosts}")
+ public String[] bannedHosts;
- private final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>());
- private final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>());
- private final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>());
- private final List<StanzaListener> stanzaListeners = Collections.synchronizedList(new ArrayList<>());
+ private final List<ConnectionIn> inConnections = new CopyOnWriteArrayList<>();
+ private final List<ConnectionOut> outConnections = new CopyOnWriteArrayList<>();
+ private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>();
+ private final List<StanzaListener> stanzaListeners = new CopyOnWriteArrayList<>();
- @Inject
- private XMPPConnection router;
- @Inject
- public MessagesService messagesService;
- @Inject
- public UserService userService;
- @Inject
- public TagService tagService;
- @Inject
- public PMQueriesService pmQueriesService;
- @Inject
- public SubscriptionService subscriptionService;
- @Inject
- public ShowQueriesService showQueriesService;
-
- private Jid jid;
-
private ServerSocket listener;
+ @Inject
private BasicXmppSession session;
- public XMPPServer(Environment env, ExecutorService service) {
- this.service = service;
- XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
- .extensions(Extension.of(com.juick.Message.class))
- .debugger(LogbackDebugger.class)
- .build();
+ @PostConstruct
+ public void init() {
logger.info("component initialized");
- try {
- HOSTNAME = env.getProperty("hostname");
- session = BasicXmppSession.create(HOSTNAME, configuration);
- int s2sPort = NumberUtils.toInt(env.getProperty("s2s_port"), 5269);
- keystore = env.getProperty("keystore");
- keystorePassword = env.getProperty("keystore_password");
- brokenSSLhosts = Arrays.asList(env.getProperty("broken_ssl_hosts", StringUtils.EMPTY).split(","));
- bannedHosts = Arrays.asList(env.getProperty("banned_hosts", StringUtils.EMPTY).split(","));
- jid = Jid.of(env.getProperty("xmppbot_jid"));
-
- service.submit(() -> {
- try {
- listener = new ServerSocket(s2sPort);
- logger.info("s2s listener ready");
- while (true) {
- if (Thread.currentThread().isInterrupted()) break;
- Socket socket = listener.accept();
- ConnectionIn client = new ConnectionIn(this, socket);
- addConnectionIn(client);
- service.submit(client);
- }
- } catch (IOException e) {
- logger.warn("io exception", e);
- Thread.currentThread().interrupt();
- } catch (Exception ex) {
- logger.warn("s2s error", ex);
+ 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);
}
- logger.info("s2s interrupted");
- });
-
- } catch (Exception e) {
- logger.error("XMPPComponent error", e);
- }
+ } catch (SocketException e) {
+ // shutdown
+ } catch (IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | XmlPullParserException | KeyManagementException e) {
+ logger.warn("xmpp exception", e);
+ }
+ });
}
@Override
public void close() throws Exception {
- synchronized (getOutConnections()) {
- for (Iterator<ConnectionOut> i = getOutConnections().iterator(); i.hasNext(); ) {
- ConnectionOut c = i.next();
- c.closeConnection();
- i.remove();
- }
- }
-
- synchronized (getInConnections()) {
- for (Iterator<ConnectionIn> i = getInConnections().iterator(); i.hasNext(); ) {
- ConnectionIn c = i.next();
- c.closeConnection();
- i.remove();
- }
+ if (!listener.isClosed()) {
+ listener.close();
}
+ outConnections.forEach(c -> {
+ c.closeConnection();
+ outConnections.remove(c);
+ });
+ inConnections.forEach(c -> {
+ c.closeConnection();
+ inConnections.remove(c);
+ });
if (!listener.isClosed()) {
listener.close();
}
+ service.shutdown();
logger.info("XMPP server destroyed");
}
public void addConnectionIn(ConnectionIn c) {
- synchronized (getInConnections()) {
- getInConnections().add(c);
- }
+ inConnections.add(c);
}
public void addConnectionOut(ConnectionOut c) {
- synchronized (getOutConnections()) {
- getOutConnections().add(c);
- }
+ outConnections.add(c);
}
public void removeConnectionIn(ConnectionIn c) {
- synchronized (getInConnections()) {
- getInConnections().remove(c);
- }
+ inConnections.remove(c);
}
public void removeConnectionOut(ConnectionOut c) {
- synchronized (getOutConnections()) {
- getOutConnections().remove(c);
- }
+ outConnections.remove(c);
}
public String getFromCache(String hostname) {
- CacheEntry ret = null;
- synchronized (getOutCache()) {
- for (Iterator<CacheEntry> i = getOutCache().iterator(); i.hasNext(); ) {
- CacheEntry c = i.next();
- if (c.hostname != null && c.hostname.equals(hostname)) {
- ret = c;
- i.remove();
- break;
- }
- }
- }
- return (ret != null) ? ret.xml : null;
+ final String[] cache = new String[1];
+ outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(hostname)).findFirst().ifPresent(c -> {
+ cache[0] = c.xml;
+ outCache.remove(c);
+ });
+ return cache[0];
}
- public ConnectionOut getConnectionOut(String hostname, boolean needReady) {
- synchronized (getOutConnections()) {
- for (ConnectionOut c : getOutConnections()) {
- if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) {
- return c;
- }
- }
- }
- return null;
+ public Optional<ConnectionOut> getConnectionOut(String hostname, boolean needReady) {
+ return outConnections.stream().filter(c -> c.to != null &&
+ c.to.equals(hostname) && (!needReady || c.streamReady)).findFirst();
}
- public ConnectionIn getConnectionIn(String streamID) {
- synchronized (getInConnections()) {
- for (ConnectionIn c : getInConnections()) {
- if (c.streamID != null && c.streamID.equals(streamID)) {
- return c;
- }
- }
- }
- return null;
+ public Optional<ConnectionIn> getConnectionIn(String streamID) {
+ return inConnections.stream().filter(c -> c.streamID != null && c.streamID.equals(streamID)).findFirst();
}
public void sendOut(Stanza s) {
@@ -236,16 +177,14 @@ public class XMPPServer implements AutoCloseable {
boolean haveAnyConn = false;
ConnectionOut connOut = null;
- synchronized (getOutConnections()) {
- for (ConnectionOut c : getOutConnections()) {
- if (c.to != null && c.to.equals(hostname)) {
- if (c.streamReady) {
- connOut = c;
- break;
- } else {
- haveAnyConn = true;
- break;
- }
+ for (ConnectionOut c : outConnections) {
+ if (c.to != null && c.to.equals(hostname)) {
+ if (c.streamReady) {
+ connOut = c;
+ break;
+ } else {
+ haveAnyConn = true;
+ break;
}
}
}
@@ -255,23 +194,22 @@ public class XMPPServer implements AutoCloseable {
}
boolean haveCache = false;
- synchronized (getOutCache()) {
- for (CacheEntry c : getOutCache()) {
- if (c.hostname != null && c.hostname.equals(hostname)) {
- c.xml += xml;
- c.tsUpdated = System.currentTimeMillis();
- haveCache = true;
- break;
- }
- }
- if (!haveCache) {
- getOutCache().add(new CacheEntry(hostname, xml));
+ for (CacheEntry c : outCache) {
+ if (c.hostname != null && c.hostname.equals(hostname)) {
+ c.xml += xml;
+ c.tsUpdated = System.currentTimeMillis();
+ haveCache = true;
+ break;
}
}
+ if (!haveCache) {
+ outCache.add(new CacheEntry(hostname, xml));
+ }
if (!haveAnyConn) {
try {
ConnectionOut connectionOut = new ConnectionOut(this, hostname);
+ addConnectionOut(connectionOut);
service.submit(connectionOut);
} catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) {
logger.error("s2s out error", e);
@@ -279,55 +217,34 @@ public class XMPPServer implements AutoCloseable {
}
}
- public XMPPConnection getRouter() {
- return router;
- }
-
- public List<ConnectionIn> getInConnections() {
- return inConnections;
- }
-
- public List<ConnectionOut> getOutConnections() {
- return outConnections;
- }
-
- public List<CacheEntry> getOutCache() {
- return outCache;
- }
-
public void startDialback(String from, String streamId, String dbKey) throws Exception {
- ConnectionOut c = getConnectionOut(from, false);
- if (c != null) {
- c.sendDialbackVerify(streamId, dbKey);
+ Optional<ConnectionOut> c = getConnectionOut(from, false);
+ if (c.isPresent()) {
+ c.get().sendDialbackVerify(streamId, dbKey);
} else {
- c = new ConnectionOut(this, from, streamId, dbKey);
- service.submit(c);
+ ConnectionOut newConnection = new ConnectionOut(this, from, streamId, dbKey);
+ addConnectionOut(newConnection);
+ service.submit(newConnection);
}
}
- public List<StanzaListener> getStanzaListeners() {
- return stanzaListeners;
- }
-
public void addStanzaListener(StanzaListener listener) {
- synchronized (stanzaListeners) {
- stanzaListeners.add(listener);
- }
+ stanzaListeners.add(listener);
}
public void onStanzaReceived(Stanza xmlValue) {
stanzaListeners.forEach(l -> l.stanzaReceived(xmlValue));
}
- public Jid getJid() {
- return jid;
- }
-
public BasicXmppSession getSession() {
return session;
}
- public void setSession(BasicXmppSession session) {
- this.session = session;
+ public List<ConnectionIn> getInConnections() {
+ return inConnections;
+ }
+
+ public List<ConnectionOut> getOutConnections() {
+ return outConnections;
}
}
diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java
index 92506cbd..02b1556d 100644
--- a/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java
+++ b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java
@@ -21,50 +21,41 @@ package com.juick.components.configuration;
* Created by aalexeev on 11/12/16.
*/
-import com.juick.components.XMPPConnection;
-import com.juick.components.XMPPServer;
-import com.juick.components.s2s.CleaningUp;
-import com.juick.components.s2s.JuickBot;
+import com.juick.components.s2s.BasicXmppSession;
import com.juick.server.configuration.BaseWebConfiguration;
-import org.apache.commons.lang3.BooleanUtils;
-import org.springframework.context.annotation.*;
-import org.springframework.core.env.Environment;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import rocks.xmpp.core.session.Extension;
+import rocks.xmpp.core.session.XmppSessionConfiguration;
+import rocks.xmpp.core.session.debug.LogbackDebugger;
-import javax.inject.Inject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
-@ComponentScan(basePackages = {"com.juick.components.controllers"})
+@ComponentScan(basePackages = {"com.juick.components"})
@PropertySource("classpath:juick.conf")
@EnableScheduling
@EnableWebMvc
public class XmppAppConfiguration extends BaseWebConfiguration {
- @Inject
- private Environment env;
-
- @Bean
- public XMPPServer xmpp() {
- return new XMPPServer(env, service());
- }
+ @Value("${hostname}")
+ private String hostname;
@Bean
public ExecutorService service() {
return Executors.newCachedThreadPool();
}
@Bean
- public CleaningUp cleaningUp() {
- return new CleaningUp();
- }
- @Bean
- public JuickBot bot() {
- return new JuickBot(xmpp());
- }
- @Bean
- public XMPPConnection router() {
- boolean disabled = BooleanUtils.toBoolean(env.getProperty("xmpp_disabled", "false"));
- return disabled ? null : new XMPPConnection();
+ public BasicXmppSession session() {
+ XmppSessionConfiguration configuration = XmppSessionConfiguration.builder()
+ .extensions(Extension.of(com.juick.Message.class))
+ .debugger(LogbackDebugger.class)
+ .build();
+ return BasicXmppSession.create(hostname, configuration);
}
}
diff --git a/juick-xmpp/src/main/java/com/juick/components/s2s/Connection.java b/juick-xmpp/src/main/java/com/juick/components/s2s/Connection.java
index 693b278e..7fd036eb 100644
--- a/juick-xmpp/src/main/java/com/juick/components/s2s/Connection.java
+++ b/juick-xmpp/src/main/java/com/juick/components/s2s/Connection.java
@@ -33,6 +33,7 @@ import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
+import java.util.UUID;
/**
*
@@ -74,7 +75,6 @@ public class Connection {
public Connection(XMPPServer xmpp) throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException {
this.xmpp = xmpp;
tsCreated = System.currentTimeMillis();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
KeyStore ks = KeyStore.getInstance("JKS");
try (InputStream ksIs = new FileInputStream(xmpp.keystore)) {
ks.load(ksIs, xmpp.keystorePassword.toCharArray());
@@ -146,6 +146,7 @@ public class Connection {
}
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()));
diff --git a/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionIn.java b/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionIn.java
index eed72a18..e6f404ef 100644
--- a/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionIn.java
+++ b/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionIn.java
@@ -22,8 +22,8 @@ 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 rocks.xmpp.core.session.XmppSessionConfiguration;
import rocks.xmpp.core.stanza.model.Stanza;
import javax.net.ssl.SSLException;
@@ -35,7 +35,13 @@ import java.io.IOException;
import java.io.StringReader;
import java.net.Socket;
import java.net.SocketException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@@ -47,9 +53,8 @@ public class ConnectionIn extends Connection implements Runnable {
final public List<String> from = new ArrayList<>();
public long tsRemoteData = 0;
public long packetsRemote = 0;
- XmppSessionConfiguration configuration;
- public ConnectionIn(XMPPServer xmpp, Socket socket) throws Exception {
+ public ConnectionIn(XMPPServer xmpp, Socket socket) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException {
super(xmpp);
this.socket = socket;
restartParser();
@@ -83,7 +88,7 @@ public class ConnectionIn extends Connection implements Runnable {
boolean xmppversionnew = parser.getAttributeValue(null, "version") != null;
String from = parser.getAttributeValue(null, "from");
- if (xmpp.bannedHosts.contains(from)) {
+ if (Arrays.asList(xmpp.bannedHosts).contains(from)) {
closeConnection();
return;
}
@@ -121,17 +126,14 @@ public class ConnectionIn extends Connection implements Runnable {
String vid = parser.getAttributeValue(null, "id");
String vkey = XmlUtils.getTagText(parser);
updateTsRemoteData();
- boolean valid = false;
+ final boolean[] valid = {false};
if (vfrom != null && vto != null && vid != null && vkey != null) {
- ConnectionOut c = xmpp.getConnectionOut(vfrom, false);
- if (c == null) {
- logger.warn("outgoing connection to {} not found", vfrom);
- } else {
+ xmpp.getConnectionOut(vfrom, false).ifPresent(c -> {
String dialbackKey = c.dbKey;
- valid = vkey.equals(dialbackKey);
- }
+ valid[0] = vkey.equals(dialbackKey);
+ });
}
- if (valid) {
+ if (valid[0]) {
sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='valid'/>");
logger.info("stream from {} {} dialback verify valid", vfrom, streamID);
} else {
@@ -154,7 +156,7 @@ public class ConnectionIn extends Connection implements Runnable {
String xml = XmlUtils.parseToString(parser, false);
if (type == null || !type.equals("error")) {
logger.info("stream {} iq: {}", streamID, xml);
- xmpp.getRouter().sendStanza(parse(xml));
+ xmpp.onStanzaReceived(parse(xml));
}
} else if (sc != null && !isSecured() && tag.equals("starttls")) {
logger.info("stream {} securing", streamID);
@@ -209,7 +211,7 @@ public class ConnectionIn extends Connection implements Runnable {
xmpp.HOSTNAME + "' id='" + streamID + "' version='1.0'>";
if (xmppversionnew) {
openStream += "<stream:features>";
- if (sc != null && !isSecured() && !xmpp.brokenSSLhosts.contains(from)) {
+ if (sc != null && !isSecured() && !Arrays.asList(xmpp.brokenSSLhosts).contains(from)) {
openStream += "<starttls xmlns=\"" + NS_TLS + "\"><optional/></starttls>";
}
openStream += "</stream:features>";
diff --git a/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionOut.java b/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionOut.java
index 903eec0c..589ed18a 100644
--- a/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionOut.java
+++ b/juick-xmpp/src/main/java/com/juick/components/s2s/ConnectionOut.java
@@ -38,6 +38,7 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.util.Arrays;
import java.util.UUID;
/**
@@ -97,7 +98,6 @@ public class ConnectionOut extends Connection implements Runnable {
}
logger.info("stream to {} {} open", to, streamID);
- xmpp.addConnectionOut(ConnectionOut.this);
boolean xmppversionnew = parser.getAttributeValue(null, "version") != null;
if (!xmppversionnew) {
processDialback();
@@ -131,15 +131,12 @@ public class ConnectionOut extends Connection implements Runnable {
String type = parser.getAttributeValue(null, "type");
String sid = parser.getAttributeValue(null, "id");
if (from != null && from.equals(to) && sid != null && !sid.isEmpty() && type != null) {
- ConnectionIn c = xmpp.getConnectionIn(sid);
- if (c != null) {
- c.sendDialbackResult(from, type);
- }
+ xmpp.getConnectionIn(sid).ifPresent(c -> c.sendDialbackResult(from, type));
}
XmlUtils.skip(parser);
} else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) {
StreamFeatures features = StreamFeatures.parse(parser);
- if (sc != null && !isSecured() && features.STARTTLS >= 0 && !xmpp.brokenSSLhosts.contains(to)) {
+ if (sc != null && !isSecured() && features.STARTTLS >= 0 && !Arrays.asList(xmpp.brokenSSLhosts).contains(to)) {
logger.info("stream to {} {} securing", to, streamID);
sendStanza("<starttls xmlns=\"" + NS_TLS + "\" />");
} else {
@@ -191,10 +188,4 @@ public class ConnectionOut extends Connection implements Runnable {
sendStanza("<db:verify from='" + xmpp.HOSTNAME + "' to='" + to + "' id='" + sid + "'>" +
key + "</db:verify>");
}
-
- @Override
- public void restartParser() throws XmlPullParserException, IOException {
- super.restartParser();
- streamID = UUID.randomUUID().toString();
- }
}
diff --git a/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickMessage.java
new file mode 100644
index 00000000..2e10606a
--- /dev/null
+++ b/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickMessage.java
@@ -0,0 +1,164 @@
+/*
+ * 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.xmpp.extensions;
+
+import com.juick.Tag;
+import com.juick.xmpp.StanzaChild;
+import com.juick.xmpp.utils.XmlUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+/**
+ *
+ * @author Ugnich Anton
+ */
+public class JuickMessage extends com.juick.Message implements StanzaChild {
+ public final static String XMLNS = "http://juick.com/message";
+ public final static String TagName = "juick";
+ private SimpleDateFormat df;
+ public JuickMessage() {
+ df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ @Override
+ public String getXMLNS() {
+ return XMLNS;
+ }
+ @Override
+ public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
+ JuickMessage jmsg = new JuickMessage();
+ final String sMID = parser.getAttributeValue(null, "mid");
+ if (sMID != null) {
+ jmsg.setMid(Integer.parseInt(sMID));
+ }
+ final String sRID = parser.getAttributeValue(null, "rid");
+ if (sRID != null) {
+ jmsg.setRid(Integer.parseInt(sRID));
+ }
+ final String sReplyTo = parser.getAttributeValue(null, "replyto");
+ if (sReplyTo != null) {
+ jmsg.setReplyto(Integer.parseInt(sReplyTo));
+ }
+ final String sPrivacy = parser.getAttributeValue(null, "privacy");
+ if (sPrivacy != null) {
+ jmsg.setPrivacy(Integer.parseInt(sPrivacy));
+ }
+ final String sFriendsOnly = parser.getAttributeValue(null, "friendsonly");
+ if (sFriendsOnly != null) {
+ jmsg.FriendsOnly = true;
+ }
+ final String sReadOnly = parser.getAttributeValue(null, "readonly");
+ if (sReadOnly != null) {
+ jmsg.ReadOnly = true;
+ }
+ jmsg.setTimestamp(df.parse(parser.getAttributeValue(null, "ts")).toInstant());
+ jmsg.setAttachmentType(parser.getAttributeValue(null, "attach"));
+ while (parser.next() == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ final String xmlns = parser.getNamespace();
+ if (tag.equals("body")) {
+ jmsg.setText(XmlUtils.getTagText(parser));
+ } else if (tag.equals(JuickUser.TagName) && xmlns != null && xmlns.equals(JuickUser.XMLNS)) {
+ jmsg.setUser(new JuickUser().parse(parser));
+ } else if (tag.equals("tag")) {
+ jmsg.getTags().add(new Tag(XmlUtils.getTagText(parser)));
+ } else {
+ XmlUtils.skip(parser);
+ }
+ }
+ return jmsg;
+ }
+ @Override
+ public String toString() {
+ String ret = "";
+ ret = "<" + TagName + " xmlns=\"" + XMLNS + "\"";
+ if (getMid() > 0) {
+ ret += " mid=\"" + getMid() + "\"";
+ }
+ if (getRid() > 0) {
+ ret += " rid=\"" + getRid() + "\"";
+ }
+ if (getReplyto() > 0) {
+ ret += " replyto=\"" + getReplyto() + "\"";
+ }
+ ret += " privacy=\"" + getPrivacy() + "\"";
+ if (FriendsOnly) {
+ ret += " friendsonly=\"1\"";
+ }
+ if (ReadOnly) {
+ ret += " readonly=\"1\"";
+ }
+ if (getTimestamp() != null) {
+ ret += " ts=\"" + df.format(Date.from(getTimestamp())) + "\"";
+ }
+ if (getAttachmentType() != null) {
+ ret += " attach=\"" + getAttachmentType() + "\"";
+ }
+ ret += ">";
+ if (getUser() != null) {
+ ret += JuickUser.toString(getUser());
+ }
+ if (getText() != null) {
+ ret += "<body>" + StringEscapeUtils.escapeXml10(getText()) + "</body>";
+ }
+ if (!getTags().isEmpty()) {
+ for (Tag Tag : getTags()) {
+ ret += "<tag>" + StringEscapeUtils.escapeXml10(Tag.getName()) + "</tag>";
+ }
+ }
+ ret += "</" + TagName + ">";
+ return ret;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof JuickMessage)) {
+ return false;
+ }
+ JuickMessage jmsg = (JuickMessage) obj;
+ return (this.getMid() == jmsg.getMid() && this.getRid() == jmsg.getRid());
+ }
+ @Override
+ public int compareTo(Object obj) throws ClassCastException {
+ if (!(obj instanceof JuickMessage)) {
+ throw new ClassCastException();
+ }
+ JuickMessage jmsg = (JuickMessage) obj;
+ if (this.getMid() != jmsg.getMid()) {
+ if (this.getMid() > jmsg.getMid()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ if (this.getRid() != jmsg.getRid()) {
+ if (this.getRid() < jmsg.getRid()) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+ }
+} \ No newline at end of file
diff --git a/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickUser.java b/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickUser.java
new file mode 100644
index 00000000..10d2b564
--- /dev/null
+++ b/juick-xmpp/src/main/java/com/juick/xmpp/extensions/JuickUser.java
@@ -0,0 +1,65 @@
+/*
+ * 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.xmpp.extensions;
+import com.juick.xmpp.StanzaChild;
+import com.juick.xmpp.utils.XmlUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+/**
+ *
+ * @author Ugnich Anton
+ */
+public class JuickUser extends com.juick.User implements StanzaChild {
+ public final static String XMLNS = "http://juick.com/user";
+ public final static String TagName = "user";
+ public JuickUser() {
+ }
+ @Override
+ public String getXMLNS() {
+ return XMLNS;
+ }
+ @Override
+ public JuickUser parse(final XmlPullParser parser) throws XmlPullParserException, IOException {
+ JuickUser juser = new JuickUser();
+ String strUID = parser.getAttributeValue(null, "uid");
+ if (strUID != null) {
+ juser.setUid(Integer.parseInt(strUID));
+ }
+ juser.setName(parser.getAttributeValue(null, "uname"));
+ XmlUtils.skip(parser);
+ return juser;
+ }
+ public static String toString(com.juick.User user) {
+ String str = "<" + TagName + " xmlns='" + XMLNS + "'";
+ if (user.getUid() > 0) {
+ str += " uid='" + user.getUid() + "'";
+ }
+ if (user.getName() != null && user.getName().length() > 0) {
+ str += " uname='" + StringEscapeUtils.escapeXml10(user.getName()) + "'";
+ }
+ str += "/>";
+ return str;
+ }
+ @Override
+ public String toString() {
+ return toString(this);
+ }
+} \ No newline at end of file
diff --git a/juick-xmpp/src/test/java/com/juick/xmpp/XMPPTests.java b/juick-xmpp/src/test/java/com/juick/xmpp/XMPPTests.java
deleted file mode 100644
index 0888b041..00000000
--- a/juick-xmpp/src/test/java/com/juick/xmpp/XMPPTests.java
+++ /dev/null
@@ -1,76 +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.xmpp;
-
-import com.gargoylesoftware.htmlunit.Page;
-import com.gargoylesoftware.htmlunit.WebClient;
-import com.juick.components.configuration.XmppAppConfiguration;
-import com.juick.configuration.MockDataConfiguration;
-import com.juick.server.configuration.BaseWebConfiguration;
-import com.juick.service.ShowQueriesService;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder;
-import org.springframework.web.context.WebApplicationContext;
-
-import javax.inject.Inject;
-
-import java.io.IOException;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@WebAppConfiguration
-@ContextConfiguration
-public class XMPPTests {
- @Configuration
- @Import(value = {
- BaseWebConfiguration.class, XmppAppConfiguration.class, MockDataConfiguration.class
- })
- static class Config {
- @Bean
- public ShowQueriesService showQueriesService() {
- return Mockito.mock(ShowQueriesService.class);
- }
- }
-
- @Inject
- private WebApplicationContext wac;
-
- private WebClient webClient;
-
- @Before
- public void setup() {
- webClient = MockMvcWebClientBuilder.webAppContextSetup(this.wac).build();
- webClient.getOptions().setJavaScriptEnabled(false);
- }
- @Test
- public void statusPageIsUp() throws IOException {
- Page statusPage = webClient.getPage("http://localhost:8080/status");
- assertThat(statusPage.getWebResponse().getStatusCode(), equalTo(200));
- }
-}