diff options
author | 2016-07-07 11:25:23 +0300 | |
---|---|---|
committer | 2016-07-07 11:25:23 +0300 | |
commit | a93c85c5ea1bf6683f604bef2608053957577ff0 (patch) | |
tree | f80998a4cf339a2b85cd5f095f4386de9d46597b /src/main/java/com/juick/xmpp | |
parent | 3e48ba7c9cae9027b0a045aa25ae40b533518865 (diff) | |
parent | 857a930d31c425185c9b9405f5a7820e6cec9491 (diff) |
merge rebase_all
Diffstat (limited to 'src/main/java/com/juick/xmpp')
-rw-r--r-- | src/main/java/com/juick/xmpp/extensions/JuickMessage.java | 20 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/CleaningUp.java | 25 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/Connection.java | 10 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/ConnectionIn.java | 42 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/ConnectionListener.java | 35 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/ConnectionOut.java | 34 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/JuickBot.java | 185 | ||||
-rw-r--r-- | src/main/java/com/juick/xmpp/s2s/XMPPComponent.java | 377 |
8 files changed, 441 insertions, 287 deletions
diff --git a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java index 53dd6deb..885b2375 100644 --- a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java +++ b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java @@ -20,6 +20,10 @@ package com.juick.xmpp.extensions; import com.juick.xmpp.utils.XmlUtils; import com.juick.xmpp.*; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -31,12 +35,17 @@ 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")); } public JuickMessage(com.juick.Message msg) { super(msg); + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); } @Override @@ -45,7 +54,7 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { } @Override - public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException { + public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException { JuickMessage jmsg = new JuickMessage(); final String sMID = parser.getAttributeValue(null, "mid"); @@ -72,7 +81,10 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { if (sReadOnly != null) { jmsg.ReadOnly = true; } - jmsg.TimestampString = parser.getAttributeValue(null, "ts"); + String ts = parser.getAttributeValue(null, "timestamp"); + if (ts != null) { + jmsg.setDate(df.parse(ts)); + } jmsg.AttachmentType = parser.getAttributeValue(null, "attach"); while (parser.next() == XmlPullParser.START_TAG) { @@ -112,8 +124,8 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { if (ReadOnly) { ret += " readonly=\"1\""; } - if (TimestampString != null) { - ret += " ts=\"" + TimestampString + "\""; + if (getDate() != null) { + ret += " ts=\"" + df.format(getDate()) + "\""; } if (AttachmentType != null) { ret += " attach=\"" + AttachmentType + "\""; diff --git a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java b/src/main/java/com/juick/xmpp/s2s/CleaningUp.java index 48771580..14d97ed8 100644 --- a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java +++ b/src/main/java/com/juick/xmpp/s2s/CleaningUp.java @@ -10,19 +10,24 @@ import java.util.Iterator; * @author ugnich */ public class CleaningUp implements Runnable { + XMPPComponent xmpp; + + public CleaningUp(XMPPComponent xmpp) { + this.xmpp = xmpp; + } @Override public void run() { while (true) { try { - PrintWriter statsFile = new PrintWriter(XMPPComponent.STATSFILE, "UTF-8"); + PrintWriter statsFile = new PrintWriter(xmpp.STATSFILE, "UTF-8"); statsFile.write("<html><body><h2>Threads: " + Thread.activeCount() + "</h2>"); - statsFile.write("<h2>Out (" + XMPPComponent.outConnections.size() + ")</h2><table border=1><tr><th>to</th><th>sid</th><th>inactive</th><th>out packets</th><th>out bytes</th></tr>"); + statsFile.write("<h2>Out (" + xmpp.outConnections.size() + ")</h2><table border=1><tr><th>to</th><th>sid</th><th>inactive</th><th>out packets</th><th>out bytes</th></tr>"); long now = System.currentTimeMillis(); - synchronized (XMPPComponent.outConnections) { - for (Iterator<ConnectionOut> i = XMPPComponent.outConnections.iterator(); i.hasNext();) { + synchronized (xmpp.outConnections) { + for (Iterator<ConnectionOut> i = xmpp.outConnections.iterator(); i.hasNext();) { ConnectionOut c = i.next(); int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0); if (inactive > 900) { @@ -40,10 +45,10 @@ public class CleaningUp implements Runnable { } } - statsFile.write("</table><h2>In (" + XMPPComponent.inConnections.size() + ")</h2><table border=1><tr><th>from</th><th>sid</th><th>inactive</th><th>in packets</th></tr>"); + statsFile.write("</table><h2>In (" + xmpp.inConnections.size() + ")</h2><table border=1><tr><th>from</th><th>sid</th><th>inactive</th><th>in packets</th></tr>"); - synchronized (XMPPComponent.inConnections) { - for (Iterator<ConnectionIn> i = XMPPComponent.inConnections.iterator(); i.hasNext();) { + synchronized (xmpp.inConnections) { + for (Iterator<ConnectionIn> i = xmpp.inConnections.iterator(); i.hasNext();) { ConnectionIn c = i.next(); int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0); if (inactive > 900) { @@ -73,10 +78,10 @@ public class CleaningUp implements Runnable { } } - statsFile.write("</table><h2>Cache (" + XMPPComponent.outCache.size() + ")</h2><table border=1><tr><th>host</th><th>live</th><th>size</th></tr>"); + statsFile.write("</table><h2>Cache (" + xmpp.outCache.size() + ")</h2><table border=1><tr><th>host</th><th>live</th><th>size</th></tr>"); - synchronized (XMPPComponent.outCache) { - for (Iterator<CacheEntry> i = XMPPComponent.outCache.iterator(); i.hasNext();) { + synchronized (xmpp.outCache) { + for (Iterator<CacheEntry> i = xmpp.outCache.iterator(); i.hasNext();) { CacheEntry c = i.next(); int inactive = (int) ((double) (now - c.tsCreated) / 1000.0); if (inactive > 600) { diff --git a/src/main/java/com/juick/xmpp/s2s/Connection.java b/src/main/java/com/juick/xmpp/s2s/Connection.java index c1bbf22e..eae6efaa 100644 --- a/src/main/java/com/juick/xmpp/s2s/Connection.java +++ b/src/main/java/com/juick/xmpp/s2s/Connection.java @@ -30,6 +30,7 @@ public class Connection { public long tsLocalData = 0; public long bytesLocal = 0; public long packetsLocal = 0; + XMPPComponent xmpp; Socket socket; public static final String NS_DB = "jabber:server:dialback"; public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; @@ -52,17 +53,18 @@ public class Connection { }; - public Connection() throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException { + public Connection(XMPPComponent 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(XMPPComponent.keystore)) { - ks.load(ksIs, XMPPComponent.keystorePassword.toCharArray()); + try (InputStream ksIs = new FileInputStream(xmpp.keystore)) { + ks.load(ksIs, xmpp.keystorePassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); - kmf.init(ks, XMPPComponent.keystorePassword.toCharArray()); + kmf.init(ks, xmpp.keystorePassword.toCharArray()); sc = SSLContext.getInstance("TLSv1.2"); sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java b/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java index 93e761a0..345caea9 100644 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java +++ b/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java @@ -31,9 +31,11 @@ public class ConnectionIn extends Connection implements Runnable { final public List<String> from = new ArrayList<>(); public long tsRemoteData = 0; public long packetsRemote = 0; + JuickBot bot; - public ConnectionIn(Socket socket) throws Exception { - super(); + public ConnectionIn(XMPPComponent xmpp, JuickBot bot, Socket socket) throws Exception { + super(xmpp); + this.bot = bot; this.socket = socket; streamID = UUID.randomUUID().toString(); restartParser(); @@ -69,19 +71,19 @@ public class ConnectionIn extends Connection implements Runnable { String dfrom = parser.getAttributeValue(null, "from"); String to = parser.getAttributeValue(null, "to"); LOGGER.info("STREAM FROM " + dfrom + " TO " + to + " " + streamID + " ASKING FOR DIALBACK"); - if (dfrom.endsWith(XMPPComponent.HOSTNAME) && (dfrom.equals(XMPPComponent.HOSTNAME) || dfrom.endsWith("." + XMPPComponent.HOSTNAME))) { + if (dfrom.endsWith(xmpp.HOSTNAME) && (dfrom.equals(xmpp.HOSTNAME) || dfrom.endsWith("." + xmpp.HOSTNAME))) { break; } - if (to != null && to.equals(XMPPComponent.HOSTNAME)) { + if (to != null && to.equals(xmpp.HOSTNAME)) { String dbKey = XmlUtils.getTagText(parser); updateTsRemoteData(); - ConnectionOut c = XMPPComponent.getConnectionOut(dfrom, false); + ConnectionOut c = xmpp.getConnectionOut(dfrom, false); if (c != null) { c.sendDialbackVerify(streamID, dbKey); } else { - c = new ConnectionOut(dfrom, streamID, dbKey); - XMPPComponent.executorService.submit(c); + c = new ConnectionOut(xmpp, dfrom, streamID, dbKey); + xmpp.executorService.submit(c); } } else { throw new HostUnknownException("STREAM FROM " + dfrom + " " + streamID + " INVALID TO " + to); @@ -107,15 +109,15 @@ public class ConnectionIn extends Connection implements Runnable { } else if (tag.equals("presence") && checkFromTo(parser)) { Presence p = Presence.parse(parser, null); if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { - JuickBot.incomingPresence(p); + bot.incomingPresence(p); } } else if (tag.equals("message") && checkFromTo(parser)) { updateTsRemoteData(); - Message msg = Message.parse(parser, XMPPComponent.childParsers); + Message msg = Message.parse(parser, xmpp.childParsers); if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { LOGGER.info("STREAM " + streamID + ": " + msg.toString()); - if (!JuickBot.incomingMessage(msg)) { - XMPPComponent.connRouter.router.send(msg.toString()); + if (!bot.incomingMessage(msg)) { + xmpp.router.send(msg.toString()); } } } else if (tag.equals("iq") && checkFromTo(parser)) { @@ -124,7 +126,7 @@ public class ConnectionIn extends Connection implements Runnable { String xml = XmlUtils.parseToString(parser, true); if (type == null || !type.equals(Iq.Type.error)) { LOGGER.info("STREAM " + streamID + ": " + xml); - XMPPComponent.connRouter.router.send(xml); + xmpp.router.send(xml); } } else if (!isSecured() && tag.equals("starttls")) { LOGGER.info("STREAM " + streamID + " SECURING"); @@ -140,7 +142,7 @@ public class ConnectionIn extends Connection implements Runnable { } catch (SSLException sex) { LOGGER.warning("STREAM " + streamID + " SSL ERROR"); sendStanza("<failed xmlns\"" + NS_TLS + "\" />"); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { @@ -150,17 +152,17 @@ public class ConnectionIn extends Connection implements Runnable { } } LOGGER.warning("STREAM " + streamID + " FINISHED"); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } catch (EOFException ex) { LOGGER.info(String.format("STREAM %s CLOSED (dirty)", streamID)); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } catch (HostUnknownException e) { LOGGER.warning(e.getMessage()); } catch (Exception e) { LOGGER.log(Level.WARNING, "STREAM " + streamID + " ERROR", e); - XMPPComponent.removeConnectionIn(this); + xmpp.removeConnectionIn(this); closeConnection(); } } @@ -172,10 +174,10 @@ public class ConnectionIn extends Connection implements Runnable { 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='" + - XMPPComponent.HOSTNAME + "' id='" + streamID + "' version='1.0'>"; + xmpp.HOSTNAME + "' id='" + streamID + "' version='1.0'>"; if (xmppversionnew) { openStream += "<stream:features>"; - if (!isSecured() && !XMPPComponent.brokenSSLhosts.contains(from)) { + if (!isSecured() && !xmpp.brokenSSLhosts.contains(from)) { openStream += "<starttls xmlns=\"" + NS_TLS + "\"><optional/></starttls>"; } openStream += "</stream:features>"; @@ -185,7 +187,7 @@ public class ConnectionIn extends Connection implements Runnable { public void sendDialbackResult(String sfrom, String type) { try { - sendStanza("<db:result from='" + XMPPComponent.HOSTNAME + "' to='" + sfrom + "' type='" + type + "'/>"); + sendStanza("<db:result from='" + xmpp.HOSTNAME + "' to='" + sfrom + "' type='" + type + "'/>"); if (type.equals("valid")) { from.add(sfrom); LOGGER.info("STREAM FROM " + sfrom + " " + streamID + " READY"); @@ -200,7 +202,7 @@ public class ConnectionIn extends Connection implements Runnable { String cto = parser.getAttributeValue(null, "to"); if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { JID jidto = new JID(cto); - if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(XMPPComponent.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { + if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(xmpp.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { JID jidfrom = new JID(cfrom); int size = from.size(); for (int i = 0; i < size; i++) { diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java b/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java deleted file mode 100644 index 569d3db0..00000000 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.juick.xmpp.s2s; - -import java.net.ServerSocket; -import java.net.Socket; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author vt - */ -public class ConnectionListener implements Runnable { - - private static final Logger logger = Logger.getLogger(ConnectionListener.class.getName()); - - @Override - public void run() { - try { - final ServerSocket listener = new ServerSocket(5269); - logger.info("s2s listener ready"); - while (true) { - try { - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(socket); - XMPPComponent.addConnectionIn(client); - XMPPComponent.executorService.submit(client); - } catch (Exception e) { - logger.log(Level.SEVERE, "s2s error", e); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "s2s listener exception", e); - } - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java b/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java index d3a10406..fede701e 100644 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java +++ b/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java @@ -17,6 +17,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.util.UUID; import java.util.logging.Level; /** @@ -29,30 +30,31 @@ public class ConnectionOut extends Connection implements Runnable { String checkSID = null; String dbKey = null; - public ConnectionOut(String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { - super(); + public ConnectionOut(XMPPComponent xmpp, String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { + super(xmpp); to = hostname; } - public ConnectionOut(String hostname, String checkSID, String dbKey) throws Exception { - super(); + public ConnectionOut(XMPPComponent xmpp, String hostname, String checkSID, String dbKey) throws Exception { + super(xmpp); to = hostname; this.checkSID = checkSID; this.dbKey = dbKey; + streamID = UUID.randomUUID().toString(); } void sendOpenStream() throws IOException { sendStanza("<?xml version='1.0'?><stream:stream xmlns='jabber:server' id='" + streamID + "' xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - XMPPComponent.HOSTNAME + "' to='" + to + "' version='1.0'>"); + xmpp.HOSTNAME + "' to='" + to + "' version='1.0'>"); } void processDialback() throws Exception { if (checkSID != null) { sendDialbackVerify(checkSID, dbKey); } - sendStanza("<db:result from='" + XMPPComponent.HOSTNAME + "' to='" + to + "'>" + - generateDialbackKey(to, XMPPComponent.HOSTNAME, streamID) + "</db:result>"); + sendStanza("<db:result from='" + xmpp.HOSTNAME + "' to='" + to + "'>" + + generateDialbackKey(to, xmpp.HOSTNAME, streamID) + "</db:result>"); } @Override @@ -76,7 +78,7 @@ public class ConnectionOut extends Connection implements Runnable { } LOGGER.info("STREAM TO " + to + " " + streamID + " OPEN"); - XMPPComponent.addConnectionOut(ConnectionOut.this); + xmpp.addConnectionOut(ConnectionOut.this); boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; if (!xmppversionnew) { processDialback(); @@ -95,7 +97,7 @@ public class ConnectionOut extends Connection implements Runnable { streamReady = true; LOGGER.info("STREAM TO " + to + " " + streamID + " READY"); - String cache = XMPPComponent.getFromCache(to); + String cache = xmpp.getFromCache(to); if (cache != null) { LOGGER.info("STREAM TO " + to + " " + streamID + " SENDING CACHE"); sendStanza(cache); @@ -110,7 +112,7 @@ 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 = XMPPComponent.getConnectionIn(sid); + ConnectionIn c = xmpp.getConnectionIn(sid); if (c != null) { c.sendDialbackResult(from, type); } @@ -118,7 +120,7 @@ public class ConnectionOut extends Connection implements Runnable { XmlUtils.skip(parser); } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { StreamFeatures features = StreamFeatures.parse(parser); - if (!isSecured() && features.STARTTLS >= 0 && !XMPPComponent.brokenSSLhosts.contains(to)) { + if (!isSecured() && features.STARTTLS >= 0 && !xmpp.brokenSSLhosts.contains(to)) { System.out.println("STREAM TO " + to + " " + streamID + " SECURING"); sendStanza("<starttls xmlns=\"" + NS_TLS + "\" />"); } else { @@ -136,7 +138,7 @@ public class ConnectionOut extends Connection implements Runnable { } catch (SSLException sex) { LOGGER.log(Level.SEVERE, String.format("s2s ssl error: %s %s", to, streamID), sex); sendStanza("<failed xmlns\"" + NS_TLS + "\" />"); - XMPPComponent.removeConnectionOut(this); + xmpp.removeConnectionOut(this); closeConnection(); } } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { @@ -147,22 +149,22 @@ public class ConnectionOut extends Connection implements Runnable { } LOGGER.warning("STREAM TO " + to + " " + streamID + " FINISHED"); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } catch (EOFException eofex) { LOGGER.info(String.format("STREAM %s %s CLOSED (dirty)", to, streamID)); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } catch (Exception e) { LOGGER.log(Level.SEVERE, String.format("s2s out exception: %s %s", to, streamID), e); - XMPPComponent.removeConnectionOut(ConnectionOut.this); + xmpp.removeConnectionOut(ConnectionOut.this); closeConnection(); } } public void sendDialbackVerify(String sid, String key) { try { - sendStanza("<db:verify from='" + XMPPComponent.HOSTNAME + "' to='" + to + "' id='" + sid + "'>" + key + "</db:verify>"); + sendStanza("<db:verify from='" + xmpp.HOSTNAME + "' to='" + to + "' id='" + sid + "'>" + key + "</db:verify>"); } catch (IOException e) { LOGGER.log(Level.WARNING, "STREAM TO " + to + " " + streamID + " ERROR", e); } diff --git a/src/main/java/com/juick/xmpp/s2s/JuickBot.java b/src/main/java/com/juick/xmpp/s2s/JuickBot.java index 25c75dfe..f0b71689 100644 --- a/src/main/java/com/juick/xmpp/s2s/JuickBot.java +++ b/src/main/java/com/juick/xmpp/s2s/JuickBot.java @@ -19,8 +19,13 @@ import java.util.regex.Pattern; * @author ugnich */ public class JuickBot { + XMPPComponent xmpp; + public JuickBot(XMPPComponent xmpp, JID JuickJID) { + this.xmpp = xmpp; + this.JuickJID = JuickJID; + } - public static final JID JuickJID = new JID("juick", "juick.com", "Juick"); + public final JID JuickJID; private static final String HELPTEXT = "@username text - Send private message\n" + "*tagname Blah-blah-blah - Post a message with tag 'tagname'\n" @@ -55,7 +60,7 @@ public class JuickBot { + "\n" + "Read more: http://juick.com/help/"; - public static boolean incomingPresence(Presence p) throws Exception { + public boolean incomingPresence(Presence p) throws Exception { final String username = p.to.Username.toLowerCase(); final boolean toJuick = username.equals("juick"); @@ -64,14 +69,12 @@ public class JuickBot { reply.from = new JID(p.to.Username, p.to.Host, null); reply.to = new JID(p.from.Username, p.from.Host, null); reply.type = Presence.Type.unsubscribe; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } else if (p.type.equals(Presence.Type.probe)) { int uid_to = 0; if (!toJuick) { - synchronized (XMPPComponent.sqlSync) { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - } + uid_to = UserQueries.getUIDbyName(xmpp.sql, username); } if (toJuick || uid_to > 0) { @@ -80,12 +83,12 @@ public class JuickBot { reply.from.Resource = "Juick"; reply.to = p.from; reply.priority = 10; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } else { Presence reply = new Presence(p.to, p.from, Presence.Type.error); reply.id = p.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } return true; @@ -94,50 +97,46 @@ public class JuickBot { if (toJuick) { canSubscribe = true; } else { - synchronized (XMPPComponent.sqlSync) { - int uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - if (uid_to > 0) { - PMQueries.addPMinRoster(XMPPComponent.sql, uid_to, p.from.Bare()); - canSubscribe = true; - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.addPMinRoster(xmpp.sql, uid_to, p.from.Bare()); + canSubscribe = true; } } if (canSubscribe) { Presence reply = new Presence(p.to, p.from, Presence.Type.subscribed); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); reply.from.Resource = "Juick"; reply.priority = 10; reply.type = null; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } else { Presence reply = new Presence(p.to, p.from, Presence.Type.error); reply.id = p.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } } else if (p.type.equals(Presence.Type.unsubscribe)) { if (!toJuick) { - synchronized (XMPPComponent.sqlSync) { - int uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - if (uid_to > 0) { - PMQueries.removePMinRoster(XMPPComponent.sql, uid_to, p.from.Bare()); - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.removePMinRoster(xmpp.sql, uid_to, p.from.Bare()); } } Presence reply = new Presence(p.to, p.from, Presence.Type.unsubscribed); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } return false; } - public static boolean incomingMessage(Message msg) throws Exception { + public boolean incomingMessage(Message msg) throws Exception { if (msg.body == null || msg.body.isEmpty()) { return true; } @@ -146,11 +145,9 @@ public class JuickBot { User user_from = null; String signuphash = ""; - synchronized (XMPPComponent.sqlSync) { - user_from = UserQueries.getUserByJID(XMPPComponent.sql, msg.from.Bare()); - if (user_from == null) { - signuphash = UserQueries.getSignUpHashByJID(XMPPComponent.sql, msg.from.Bare()); - } + user_from = UserQueries.getUserByJID(xmpp.sql, msg.from.Bare()); + if (user_from == null) { + signuphash = UserQueries.getSignUpHashByJID(xmpp.sql, msg.from.Bare()); } if (user_from == null) { @@ -160,7 +157,7 @@ public class JuickBot { } else { reply.body = "Внимание, системное сообщение!\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."; } - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } @@ -168,24 +165,19 @@ public class JuickBot { return incomingMessageJuick(user_from, msg); } - int uid_to = 0; - synchronized (XMPPComponent.sqlSync) { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, username); - } + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); if (uid_to == 0) { Message reply = new Message(msg.to, msg.from, Message.Type.error); reply.id = msg.id; reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); return true; } boolean success = false; - synchronized (XMPPComponent.sqlSync) { - if (!UserQueries.isInBLAny(XMPPComponent.sql, uid_to, user_from.getUID())) { - success = PMQueries.createPM(XMPPComponent.sql, user_from.getUID(), uid_to, msg.body); - } + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + success = PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, msg.body); } if (success) { @@ -193,29 +185,22 @@ public class JuickBot { m.from = new JID("juick", "juick.com", null); m.to = new JID(Integer.toString(uid_to), "push.juick.com", null); JuickMessage jmsg = new JuickMessage(); - synchronized (XMPPComponent.sqlSync) { - jmsg.setUser(UserQueries.getUserByUID(XMPPComponent.sql, user_from.getUID())); - } + jmsg.setUser(user_from); jmsg.setText(msg.body); m.childs.add(jmsg); - XMPPComponent.connRouter.router.send(m.toString()); + xmpp.router.send(m.toString()); m.to.Host = "ws.juick.com"; - XMPPComponent.connRouter.router.send(m.toString()); + xmpp.router.send(m.toString()); - String jid; + List<String> jids; boolean inroster = false; - synchronized (XMPPComponent.sqlSync) { - jid = UserQueries.getJIDbyUID(XMPPComponent.sql, uid_to); - if (jid != null) { - inroster = PMQueries.havePMinRoster(XMPPComponent.sql, user_from.getUID(), jid); - } - } - - if (jid != null) { + jids = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + for (String jid : jids) { Message mm = new Message(); mm.to = new JID(jid); mm.type = Message.Type.chat; + inroster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); if (inroster) { mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); mm.body = msg.body; @@ -223,21 +208,20 @@ public class JuickBot { mm.from = new JID("juick", "juick.com", "Juick"); mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + msg.body; } - XMPPComponent.sendOut(mm); + xmpp.sendOut(mm); } - } else { Message reply = new Message(msg.to, msg.from, Message.Type.error); reply.id = msg.id; reply.addChild(new Error(Error.Type.cancel, "not-allowed")); - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } return true; } private static Pattern regexPM = Pattern.compile("^\\@(\\S+)\\s+([\\s\\S]+)$"); - public static boolean incomingMessageJuick(User user_from, Message msg) throws Exception { + public boolean incomingMessageJuick(User user_from, Message msg) throws Exception { String command = msg.body.trim(); int commandlen = command.length(); @@ -274,59 +258,54 @@ public class JuickBot { return false; } - private static void commandPing(Message m) throws Exception { + private void commandPing(Message m) throws Exception { Presence p = new Presence(JuickJID, m.from); p.priority = 10; - XMPPComponent.sendOut(p); + xmpp.sendOut(p); Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = "PONG"; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandHelp(Message m) throws Exception { + private void commandHelp(Message m) throws Exception { Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = HELPTEXT; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandLogin(Message m, User user_from) throws Exception { + private void commandLogin(Message m, User user_from) throws Exception { Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(XMPPComponent.sql, user_from.getUID()); - XMPPComponent.sendOut(reply); + reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(xmpp.sql, user_from.getUID()); + xmpp.sendOut(reply); } - private static void commandPM(Message m, User user_from, String user_to, String body) throws Exception { + private void commandPM(Message m, User user_from, String user_to, String body) throws Exception { int ret = 0; int uid_to = 0; - String jid_to = null; + List<String> jids_to = null; boolean haveInRoster = false; - synchronized (XMPPComponent.sqlSync) { - if (user_to.indexOf('@') > 0) { - uid_to = UserQueries.getUIDbyJID(XMPPComponent.sql, user_to); - } else { - uid_to = UserQueries.getUIDbyName(XMPPComponent.sql, user_to); - } + if (user_to.indexOf('@') > 0) { + uid_to = UserQueries.getUIDbyJID(xmpp.sql, user_to); + } else { + uid_to = UserQueries.getUIDbyName(xmpp.sql, user_to); + } - if (uid_to > 0) { - if (!UserQueries.isInBLAny(XMPPComponent.sql, uid_to, user_from.getUID())) { - if (PMQueries.createPM(XMPPComponent.sql, user_from.getUID(), uid_to, body)) { - jid_to = UserQueries.getJIDbyUID(XMPPComponent.sql, uid_to); - if (jid_to != null) { - haveInRoster = PMQueries.havePMinRoster(XMPPComponent.sql, user_from.getUID(), jid_to); - } - ret = 200; - } else { - ret = 500; - } + if (uid_to > 0) { + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + if (PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, body)) { + jids_to = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + ret = 200; } else { - ret = 403; + ret = 500; } } else { - ret = 404; + ret = 403; } + } else { + ret = 404; } if (ret == 200) { @@ -337,15 +316,16 @@ public class JuickBot { jmsg.setUser(user_from); jmsg.setText(body); msg.childs.add(jmsg); - XMPPComponent.connRouter.router.send(msg.toString()); + xmpp.router.send(msg.toString()); msg.to.Host = "ws.juick.com"; - XMPPComponent.connRouter.router.send(msg.toString()); + xmpp.router.send(msg.toString()); - if (jid_to != null) { + for (String jid : jids_to) { Message mm = new Message(); - mm.to = new JID(jid_to); + mm.to = new JID(jid); mm.type = Message.Type.chat; + haveInRoster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); if (haveInRoster) { mm.from = new JID(user_from.getUName(), "juick.com", "Juick"); mm.body = body; @@ -353,7 +333,7 @@ public class JuickBot { mm.from = new JID("juick", "juick.com", "Juick"); mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; } - XMPPComponent.sendOut(mm); + xmpp.sendOut(mm); } } @@ -365,22 +345,17 @@ public class JuickBot { reply.type = Message.Type.error; reply.body = "Error " + ret; } - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } - private static void commandBLShow(Message m, User user_from) throws Exception { - List<User> blusers; - List<String> bltags; - - synchronized (XMPPComponent.sqlSync) { - blusers = UserQueries.getUserBLUsers(XMPPComponent.sql, user_from.getUID()); - bltags = TagQueries.getUserBLTags(XMPPComponent.sql, user_from.getUID()); - } + private void commandBLShow(Message m, User user_from) throws Exception { + List<User> blusers = UserQueries.getUserBLUsers(xmpp.sql, user_from.getUID()); + List<String> bltags = TagQueries.getUserBLTags(xmpp.sql, user_from.getUID()); String txt = ""; if (bltags.size() > 0) { - for (int i = 0; i < bltags.size(); i++) { - txt += "*" + bltags.get(i) + "\n"; + for (String bltag : bltags) { + txt += "*" + bltag + "\n"; } if (blusers.size() > 0) { @@ -388,8 +363,8 @@ public class JuickBot { } } if (blusers.size() > 0) { - for (int i = 0; i < blusers.size(); i++) { - txt += "@" + blusers.get(i).getUName() + "\n"; + for (User bluser : blusers) { + txt += "@" + bluser.getUName() + "\n"; } } if (txt.isEmpty()) { @@ -398,6 +373,6 @@ public class JuickBot { Message reply = new Message(JuickJID, m.from, Message.Type.chat); reply.body = txt; - XMPPComponent.sendOut(reply); + xmpp.sendOut(reply); } } diff --git a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java index 13df5fd0..d0b231e2 100644 --- a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java +++ b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java @@ -1,21 +1,27 @@ package com.juick.xmpp.s2s; -import com.juick.xmpp.Stanza; -import com.juick.xmpp.StanzaChild; +import com.juick.User; +import com.juick.server.MessagesQueries; +import com.juick.server.SubscriptionsQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.*; import com.juick.xmpp.extensions.JuickMessage; +import com.juick.xmpp.extensions.Nickname; +import com.juick.xmpp.extensions.XOOB; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.xmlpull.v1.XmlPullParserException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -26,50 +32,51 @@ import java.util.logging.Logger; * * @author ugnich */ -public class XMPPComponent implements ServletContextListener { - - private static final Logger LOGGER = Logger.getLogger(XMPPComponent.class.getName()); - - public static final ExecutorService executorService = Executors.newCachedThreadPool(); - - public static String HOSTNAME = null; - public static String STATSFILE = null; - public static String keystore; - public static String keystorePassword; - public static List<String> brokenSSLhosts; - public static ConnectionRouter connRouter; - static final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>()); - static final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>()); - static final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>()); - static final Integer sqlSync = 0; - static java.sql.Connection sql; - final public static HashMap<String, StanzaChild> childParsers = new HashMap<>(); - - public static void addConnectionIn(ConnectionIn c) { +public class XMPPComponent implements ServletContextListener, Stream.StreamListener, + Message.MessageListener, Iq.IqListener, Presence.PresenceListener { + + private static final Logger logger = Logger.getLogger(XMPPComponent.class.getName()); + + public final ExecutorService executorService = Executors.newCachedThreadPool(); + StreamComponent router; + JuickBot bot; + + public String HOSTNAME, componentName; + public String STATSFILE = null; + public String keystore; + public String keystorePassword; + public List<String> brokenSSLhosts; + final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>()); + final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>()); + final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>()); + JdbcTemplate sql; + final public HashMap<String, StanzaChild> childParsers = new HashMap<>(); + + public void addConnectionIn(ConnectionIn c) { synchronized (inConnections) { inConnections.add(c); } } - public static void addConnectionOut(ConnectionOut c) { + public void addConnectionOut(ConnectionOut c) { synchronized (outConnections) { outConnections.add(c); } } - public static void removeConnectionIn(ConnectionIn c) { + public void removeConnectionIn(ConnectionIn c) { synchronized (inConnections) { inConnections.remove(c); } } - public static void removeConnectionOut(ConnectionOut c) { + public void removeConnectionOut(ConnectionOut c) { synchronized (outConnections) { outConnections.remove(c); } } - public static String getFromCache(String hostname) { + public String getFromCache(String hostname) { CacheEntry ret = null; synchronized (outCache) { for (Iterator<CacheEntry> i = outCache.iterator(); i.hasNext();) { @@ -84,7 +91,7 @@ public class XMPPComponent implements ServletContextListener { return (ret != null) ? ret.xml : null; } - public static ConnectionOut getConnectionOut(String hostname, boolean needReady) { + public ConnectionOut getConnectionOut(String hostname, boolean needReady) { synchronized (outConnections) { for (ConnectionOut c : outConnections) { if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) { @@ -95,7 +102,7 @@ public class XMPPComponent implements ServletContextListener { return null; } - public static ConnectionIn getConnectionIn(String streamID) { + public ConnectionIn getConnectionIn(String streamID) { synchronized (inConnections) { for (ConnectionIn c : inConnections) { if (c.streamID != null && c.streamID.equals(streamID)) { @@ -106,11 +113,11 @@ public class XMPPComponent implements ServletContextListener { return null; } - public static void sendOut(Stanza s) { + public void sendOut(Stanza s) { sendOut(s.to.Host, s.toString()); } - public static void sendOut(String hostname, String xml) { + public void sendOut(String hostname, String xml) { boolean haveAnyConn = false; ConnectionOut connOut = null; @@ -131,7 +138,7 @@ public class XMPPComponent implements ServletContextListener { try { connOut.sendStanza(xml); } catch (IOException e) { - LOGGER.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); + logger.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); } return; } @@ -152,12 +159,11 @@ public class XMPPComponent implements ServletContextListener { } if (!haveAnyConn) { - ConnectionOut connectionOut = null; try { - connectionOut = new ConnectionOut(hostname); - XMPPComponent.executorService.submit(connectionOut); + ConnectionOut connectionOut = new ConnectionOut(this, hostname); + executorService.submit(connectionOut); } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) { - LOGGER.log(Level.SEVERE, "s2s out error", e); + logger.log(Level.SEVERE, "s2s out error", e); } } } @@ -165,45 +171,74 @@ public class XMPPComponent implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { - LOGGER.info("component initialized"); - executorService.submit(() -> { - Properties conf = new Properties(); - try { - conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - HOSTNAME = conf.getProperty("hostname"); - String componentName = conf.getProperty("componentname"); - STATSFILE = conf.getProperty("statsfile"); - keystore = conf.getProperty("keystore"); - keystorePassword = conf.getProperty("keystore_password"); - brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); - Class.forName("com.mysql.jdbc.Driver"); - sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + - conf.getProperty("mysql_username", "") + "&password=" + conf.getProperty("mysql_password", "")); - - childParsers.put(JuickMessage.XMLNS, new JuickMessage()); - executorService.submit(() -> connRouter = new ConnectionRouter(componentName, conf.getProperty("xmpp_password"))); - executorService.submit(new ConnectionListener()); - executorService.submit(new CleaningUp()); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "XMPPComponent error", e); - } - }); + logger.info("component initialized"); + Properties conf = new Properties(); + try { + conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + HOSTNAME = conf.getProperty("hostname"); + componentName = conf.getProperty("componentname"); + JID Jid = new JID(conf.getProperty("xmppbot_jid")); + STATSFILE = conf.getProperty("statsfile"); + keystore = conf.getProperty("keystore"); + keystorePassword = conf.getProperty("keystore_password"); + brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(conf.getProperty("datasource_url")); + sql = new JdbcTemplate(dataSource); + bot = new JuickBot(this, Jid); + + childParsers.put(JuickMessage.XMLNS, new JuickMessage()); + + executorService.submit(() -> { + Socket routerSocket = null; + try { + routerSocket = new Socket("localhost", 5347); + router = new StreamComponent(new JID("s2s"), routerSocket.getInputStream(), routerSocket.getOutputStream(), conf.getProperty("xmpp_password")); + router.addChildParser(new JuickMessage()); + router.addListener((Stream.StreamListener) this); + router.addListener((Message.MessageListener) this); + router.addListener((Iq.IqListener) this); + router.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, "router error", e); + } + }); + executorService.submit(() -> { + final ServerSocket listener = new ServerSocket(5269); + logger.info("s2s listener ready"); + while (true) { + try { + Socket socket = listener.accept(); + ConnectionIn client = new ConnectionIn(this, bot, socket); + addConnectionIn(client); + executorService.submit(client); + } catch (Exception e) { + logger.log(Level.SEVERE, "s2s error", e); + } + } + }); + executorService.submit(new CleaningUp(this)); + + } catch (Exception e) { + logger.log(Level.SEVERE, "XMPPComponent error", e); + } } @Override public void contextDestroyed(ServletContextEvent sce) { - synchronized (XMPPComponent.outConnections) { - for (Iterator<ConnectionOut> i = XMPPComponent.outConnections.iterator(); i.hasNext();) { + synchronized (outConnections) { + for (Iterator<ConnectionOut> i = outConnections.iterator(); i.hasNext();) { ConnectionOut c = i.next(); c.closeConnection(); i.remove(); } } - synchronized (XMPPComponent.inConnections) { - for (Iterator<ConnectionIn> i = XMPPComponent.inConnections.iterator(); i.hasNext();) { + synchronized (inConnections) { + for (Iterator<ConnectionIn> i = inConnections.iterator(); i.hasNext();) { ConnectionIn c = i.next(); c.closeConnection(); i.remove(); @@ -211,42 +246,198 @@ public class XMPPComponent implements ServletContextListener { } try { - XMPPComponent.connRouter.closeConnection(); + closeRouterConnection(); } catch (IOException e) { - LOGGER.log(Level.WARNING, "router warning", e); + logger.log(Level.WARNING, "router warning", e); } + executorService.shutdown(); + logger.info("component destroyed"); + } + public void closeRouterConnection() throws IOException { + router.logoff(); + } - synchronized (XMPPComponent.sqlSync) { - if (XMPPComponent.sql != null) { - try { - XMPPComponent.sql.close(); - XMPPComponent.sql = null; - } catch (SQLException e) { - LOGGER.warning("SQL ERROR: " + e); + public void sendJuickMessage(JuickMessage jmsg) { + List<String> jids = new ArrayList<>(); + + if (jmsg.FriendsOnly) { + jids = SubscriptionsQueries.getJIDSubscribedToUser(sql, jmsg.getUser().getUID(), jmsg.FriendsOnly); + } else { + List<User> users = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + jids.add(jid); } } } - // Now deregister JDBC drivers in this context's ClassLoader: - // Get the webapp's ClassLoader - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - // Loop through all drivers - Enumeration<Driver> drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - if (driver.getClass().getClassLoader() == cl) { - // This driver was registered by the webapp's ClassLoader, so deregister it: - try { - LOGGER.info(String.format("Deregistering JDBC driver %s", driver.toString())); - DriverManager.deregisterDriver(driver); - } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, String.format("Error deregistering JDBC driver %s", driver), ex); - } + + String txt = "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID() + " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (String jid : jids) { + msg.to = new JID(jid); + sendOut(msg); + } + } + + public void sendJuickComment(JuickMessage jmsg) { + List<User> users; + String replyQuote; + String replyTo; + + users = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); + com.juick.Message replyMessage = jmsg.ReplyTo > 0 ? MessagesQueries.getReply(sql, jmsg.getMID(), jmsg.ReplyTo) + : MessagesQueries.getMessage(sql, jmsg.getMID()); + replyTo = replyMessage.getUser().getUName(); + replyQuote = getReplyQuote(replyMessage); + + String txt = "Reply by @" + jmsg.getUser().getUName() + ":\n" + replyQuote + "\n@" + replyTo + " "; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMID() + "/" + jmsg.getRID() + " http://juick.com/" + jmsg.getMID() + "#" + jmsg.getRID(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.addChild(jmsg); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + private String getReplyQuote(com.juick.Message q) { + String quote = q.getText(); + if (quote.length() > 50) { + quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; + } else if (quote.length() > 0) { + quote = ">" + quote.replace('\n', ' ') + "\n"; + } + return quote; + } + + public void sendJuickRecommendation(JuickMessage recomm) { + List<User> users; + JuickMessage jmsg; + jmsg = new JuickMessage(MessagesQueries.getMessage(sql, recomm.getMID())); + users = SubscriptionsQueries.getUsersSubscribedToUserRecommendations(sql, + recomm.getUser().getUID(), recomm.getMID(), jmsg.getUser().getUID()); + + String txt = "Recommended by @" + recomm.getUser().getUName() + ":\n"; + txt += "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID(); + if (jmsg.Replies > 0) { + if (jmsg.Replies % 10 == 1 && jmsg.Replies % 100 != 11) { + txt += " (" + jmsg.Replies + " reply)"; } else { - // driver was not registered by the webapp's ClassLoader and may be in use elsewhere - LOGGER.log(Level.SEVERE, String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver)); + txt += " (" + jmsg.Replies + " replies)"; } } - executorService.shutdown(); - LOGGER.info("component destroyed"); + txt += " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + @Override + public boolean onIq(Iq iq) { + JID jid = iq.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (IQ): " + iq.toString()); + sendOut(iq); + } + return false; + } + + @Override + public void onMessage(Message xmsg) { + logger.info("STREAM ROUTER (PROCESS): " + xmsg.toString()); + JuickMessage jmsg = (JuickMessage) xmsg.getChild(JuickMessage.XMLNS); + JID jid = xmsg.to; + if (jid.Host.equals(componentName)) { + if (jmsg != null) { + if (jid.Username != null && jid.Username.equals("recomm")) { + sendJuickRecommendation(jmsg); + } else { + if (jmsg.getRID() > 0) { + sendJuickComment(jmsg); + } else if (jmsg.getMID() > 0) { + sendJuickMessage(jmsg); + } + } + } + } else { + sendOut(xmsg); + } + } + + @Override + public void onPresence(Presence presence) { + JID jid = presence.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (PRESENCE): " + presence.toString()); + sendOut(presence); + } + } + + @Override + public void onStreamReady() { + logger.info("STREAM ROUTER (READY)"); + } + + @Override + public void onStreamFail(Exception ex) { + logger.log(Level.SEVERE, "STREAM ROUTER (FAIL)", ex); } } |