From d416f39516085329263dccfe2521f5fe64ed6cf5 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sun, 25 Dec 2011 15:11:43 +0700 Subject: Initial commit --- src/com/juick/jabber/ws/Main.java | 518 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 src/com/juick/jabber/ws/Main.java (limited to 'src') diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java new file mode 100644 index 00000000..f8cad85b --- /dev/null +++ b/src/com/juick/jabber/ws/Main.java @@ -0,0 +1,518 @@ +/* + * 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 . + */ +package com.juick.jabber.ws; + +import com.juick.xmpp.JID; +import com.juick.xmpp.MessageListener; +import com.juick.xmpp.XmppConnection; +import com.juick.xmpp.XmppConnectionComponent; +import com.juick.xmpp.XmppListener; +import com.juick.xmpp.extensions.JuickMessage; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; +import java.util.Properties; +import java.util.Vector; + +/** + * + * @author Ugnich Anton + */ +public class Main implements XmppListener, MessageListener { + + Connection sql; + XmppConnection xmpp; + Vector sockReplies = new Vector(); + Vector sockMessages = new Vector(); + Vector sockAll = new Vector(); + Selector sel; + + public static void main(String[] args) { + new Main().start(); + } + + public void start() { + try { + Properties conf = new Properties(); + conf.load(new FileInputStream("/etc/juick/ws.conf")); + + setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); + setupXmppComponent(conf.getProperty("xmpp_password", "")); + setupWsServer(); + } catch (Exception e) { + System.err.println(e); + } + } + + public void setupSql(String username, String password) { + try { + sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + username + "&password=" + password); + } catch (SQLException e) { + System.err.println(e); + } + } + + public void setupXmppComponent(String password) { + xmpp = new XmppConnectionComponent(new JID("ws.juick.com"), password, "127.0.0.1", 5347, false); + xmpp.addListener((XmppListener) this); + xmpp.addListener((MessageListener) this); + xmpp.start(); + } + + @Override + public void onConnectionFailed(String msg) { + System.err.println("XMPP onConnFailed " + msg); + } + + @Override + public void onAuth(String resource) { + System.err.println("XMPP onAuth " + resource); + } + + @Override + public void onAuthFailed(String message) { + System.err.println("XMPP onAuthFailed " + message); + } + + public void setupWsServer() { + try { + sel = Selector.open(); + ServerSocketChannel listensock = ServerSocketChannel.open(); + listensock.configureBlocking(false); + listensock.socket().bind(new InetSocketAddress(8080)); + listensock.register(sel, SelectionKey.OP_ACCEPT); + + while (true) { + sel.select(); + System.out.println("ONE"); + Iterator it = sel.selectedKeys().iterator(); + while (it.hasNext()) { + System.out.println("TWO"); + SelectionKey selKey = (SelectionKey) it.next(); + it.remove(); + if (selKey.isAcceptable()) { + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); + SocketChannel sChannel = ssChannel.accept(); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + sChannel.configureBlocking(false); + sChannel.register(sel, SelectionKey.OP_READ); + } else if (selKey.isReadable()) { + System.out.println("THREE"); + SocketChannel sChannel = (SocketChannel) selKey.channel(); + ByteBuffer buf = ByteBuffer.allocate(10240); + try { + if (sChannel.read(buf) > 0) { + buf.flip(); + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + if (charbuf.charAt(0) == 0 && charbuf.charAt(charbuf.length() - 1) == 0xFF) { + wsTextFrame(sChannel, charbuf.subSequence(1, charbuf.length() - 2)); + } else if (charbuf.charAt(0) == 'G' && charbuf.charAt(1) == 'E' && charbuf.charAt(2) == 'T' && charbuf.charAt(3) == ' ') { + wsHandshake(sChannel, buf); + } else { + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); + System.out.println("FOUR"); + sChannel.close(); + selKey.cancel(); + } + } else { + sChannel.close(); + selKey.cancel(); + System.out.println("SIX"); + } + } catch (IOException e) { + System.err.println(e); + sChannel.close(); + System.out.println("FIVE"); + selKey.cancel(); + } + } + } + } + } catch (Exception e) { + System.err.println(e); + } + } + + public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { + String hOrigin = null; + String hHost = null; + String hLocation = null; + String hSecWebSocketKey1 = null; + String hSecWebSocketKey2 = null; + String hCookie = null; + + buf.rewind(); + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + String headers[] = charbuf.toString().split("\r\n"); + for (int i = 0; i < headers.length; i++) { + String h[] = headers[i].split(" ", 2); + if (h.length == 2) { + if (h[0].equals("GET")) { + hLocation = headers[i].split(" ", 3)[1]; + } else if (h[0].equals("Origin:")) { + hOrigin = h[1]; + } else if (h[0].equals("Host:")) { + hHost = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key1:")) { + hSecWebSocketKey1 = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key2:")) { + hSecWebSocketKey2 = h[1]; + } else if (h[0].equals("Cookie:")) { + hCookie = h[1]; + } + } + } + + if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey1 == null || hSecWebSocketKey2 == null) { + System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); + sock.close(); + return; + } + + // Cookies + int UID = 0; + + if (hCookie != null) { + String hash = null; + + String cookies[] = hCookie.split("; "); + for (int i = 0; i < cookies.length; i++) { + String cookie[] = cookies[i].split("=", 2); + if (cookie[0].equals("hash")) { + hash = cookie[1]; + break; + } + } + + if (hash != null) { + UID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); + } + } + + + // URL + + String loc[] = hLocation.split("/"); + int MID = 0; + if (hLocation.equals("/my") && UID > 0) { + sockMessages.add(new SocketSubscribed(sock, UID, 0)); + } else if (hLocation.equals("/all")) { + sockAll.add(new SocketSubscribed(sock, UID, 0)); + } else if ((loc.length == 2 || loc.length == 3) && loc[1].equals("replies")) { + if (loc.length == 2) { + sockReplies.add(new SocketSubscribed(sock, UID, 0)); + } else { + try { + MID = Integer.parseInt(loc[2]); + } catch (Exception e) { + } + if (MID > 0) { + sockReplies.add(new SocketSubscribed(sock, UID, MID)); + } else { + System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid MID"); + sock.close(); + return; + } + } + } else { + System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid location"); + sock.close(); + return; + } + + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (UID=" + UID + ", MID=" + MID + ")"); + + Long lSecNum1 = calcSecKeyNum(hSecWebSocketKey1); + Long lSecNum2 = calcSecKeyNum(hSecWebSocketKey2); + + BigInteger sec1 = new BigInteger(lSecNum1.toString()); + BigInteger sec2 = new BigInteger(lSecNum2.toString()); + + // concatenate 3 parts secNum1 + secNum2 + secKey (16 Bytes) + byte[] l128Bit = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + byte[] lTmp; + + lTmp = sec1.toByteArray(); + int lIdx = lTmp.length; + int lCnt = 0; + while (lIdx > 0 && lCnt < 4) { + lIdx--; + lCnt++; + l128Bit[4 - lCnt] = lTmp[lIdx]; + } + + lTmp = sec2.toByteArray(); + lIdx = lTmp.length; + lCnt = 0; + while (lIdx > 0 && lCnt < 4) { + lIdx--; + lCnt++; + l128Bit[8 - lCnt] = lTmp[lIdx]; + } + + buf.rewind(); + for (int i = 0; i < 8; i++) { + l128Bit[8 + i] = buf.get(buf.limit() - 8 + i); + } + + String outstr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + + "Upgrade: WebSocket\r\n" + + "Connection: Upgrade\r\n" + + "Sec-WebSocket-Origin: " + hOrigin + "\r\n" + + "Sec-WebSocket-Location: ws://" + hHost + hLocation + "\r\n" + + "Sec-WebSocket-Protocol: sample\r\n" + + "\r\n"; + ByteBuffer out = ByteBuffer.allocate(4096); + out.put(Charset.forName("ISO-8859-1").encode(outstr)); + out.put(MessageDigest.getInstance("MD5").digest(l128Bit)); + out.flip(); + + sock.write(out); + } + + private static long calcSecKeyNum(String aKey) { + StringBuilder lSB = new StringBuilder(); + // StringBuuffer lSB = new StringBuuffer(); + int lSpaces = 0; + for (int i = 0; i < aKey.length(); i++) { + char lC = aKey.charAt(i); + if (lC == ' ') { + lSpaces++; + } else if (lC >= '0' && lC <= '9') { + lSB.append(lC); + } + } + long lRes = -1; + if (lSpaces > 0) { + try { + lRes = Long.parseLong(lSB.toString()) / lSpaces; + // log.debug("Key: " + aKey + ", Numbers: " + lSB.toString() + + // ", Spaces: " + lSpaces + ", Result: " + lRes); + } catch (NumberFormatException ex) { + // use default result + } + } + return lRes; + } + + public void wsTextFrame(SocketChannel sock, CharSequence csbuf) { + String buf = csbuf.toString(); + if (buf.equals(" ")) { + ByteBuffer out = ByteBuffer.allocate(4); + out.put((byte) 0x00); + out.put((byte) 0x20); + out.put((byte) 0xFF); + out.flip(); + out.rewind(); + try { + sock.write(out); + } catch (IOException e) { + } + } else { + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " DATA '" + buf + "'"); + } + } + + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); + if (jmsg != null) { + if (jmsg.RID == 0) { + onJuickMessagePost(jmsg); + } else { + onJuickMessageReply(jmsg); + } + } + } + + private void onJuickMessagePost(com.juick.Message jmsg) { + String json = "{" + + "\"mid\":" + jmsg.MID + "," + + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," + + "\"timestamp\":\"" + jmsg.Timestamp + "\"," + + "\"body\":\"" + encloseJSON(jmsg.Text) + "\""; + if (jmsg.tags.size() > 0) { + json += ",\"tags\":["; + for (int i = 0; i < jmsg.tags.size(); i++) { + if (i > 0) { + json += ","; + } + json += "\"" + encloseJSON((String) jmsg.tags.get(i)) + "\""; + } + json += "]"; + } + json += "}"; + + ByteBuffer out = ByteBuffer.allocate(10240); + out.put((byte) 0x00); + out.put(Charset.forName("UTF-8").encode(json)); + out.put((byte) 0xFF); + out.flip(); + + + String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; + if (jmsg.Privacy < 0) { + query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; + } + + Statement stmt = null; + ResultSet rs = null; + try { + stmt = sql.createStatement(); + rs = stmt.executeQuery(query); + rs.beforeFirst(); + while (rs.next()) { + int UID = rs.getInt(1); + + for (int i = sockMessages.size() - 1; i >= 0; i--) { + SocketSubscribed ss = sockMessages.get(i); + if (ss.UID == UID) { + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + sockMessages.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + + if (jmsg.Privacy <= 0) { + for (int i = sockAll.size() - 1; i >= 0; i--) { + SocketSubscribed ss = sockAll.get(i); + if (ss.UID == UID) { + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + sockAll.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + } + + } + } catch (SQLException e) { + System.err.println(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + } + } + } + + // Send to all + if (jmsg.Privacy > 0) { + for (int i = sockAll.size() - 1; i >= 0; i--) { + SocketSubscribed ss = sockAll.get(i); + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + sockAll.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + + + + } + + private void onJuickMessageReply(com.juick.Message jmsg) { + String json = "{" + + "\"mid\":" + jmsg.MID + "," + + "\"rid\":" + jmsg.RID + "," + + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," + + "\"timestamp\":\"" + jmsg.Timestamp + "\"," + + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" + + "}"; + + ByteBuffer out = ByteBuffer.allocate(10240); + out.put((byte) 0x00); + out.put(Charset.forName("UTF-8").encode(json)); + out.put((byte) 0xFF); + out.flip(); + + for (int i = sockReplies.size() - 1; i >= 0; i--) { + SocketSubscribed ss = sockReplies.get(i); + if (ss.MID == 0 || ss.MID == jmsg.MID) { + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + sockReplies.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + } + + public static String encloseJSON(String str) { + return str.replace("\"", """).replace("\\", "\\\\").replace("\n", "\\n"); + } +} + +class SocketSubscribed { + + public SocketChannel sock; + public int UID; + public int MID; + + public SocketSubscribed(SocketChannel sock, int UID, int MID) { + this.sock = sock; + this.UID = UID; + this.MID = MID; + } +} -- cgit v1.2.3 From 35e57155cada91682f7cf0bb9c6afa25c570e7af Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sun, 23 Sep 2012 13:01:15 +0700 Subject: replies.reply_to --- src/com/juick/jabber/ws/Main.java | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index f8cad85b..95008259 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -471,6 +471,7 @@ public class Main implements XmppListener, MessageListener { String json = "{" + "\"mid\":" + jmsg.MID + "," + "\"rid\":" + jmsg.RID + "," + + "\"replyto\":" + jmsg.ReplyTo + "," + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," + "\"timestamp\":\"" + jmsg.Timestamp + "\"," + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" -- cgit v1.2.3 From 1c70f9be446a5002d33e18f344429f3b75cb3a48 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sun, 4 Nov 2012 22:49:10 +0700 Subject: Logging --- nbproject/project.properties | 2 +- src/com/juick/jabber/ws/Main.java | 46 ++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/nbproject/project.properties b/nbproject/project.properties index 75d09036..9b3f6bc4 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -58,7 +58,7 @@ javadoc.windowtitle= main.class=com.juick.jabber.ws.Main manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=true +mkdist.disabled=false platform.active=default_platform project.com_juick=../com.juick project.com_juick_server=../com.juick.server diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index 95008259..9107c408 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -40,9 +40,9 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; -import java.util.Vector; /** * @@ -52,9 +52,9 @@ public class Main implements XmppListener, MessageListener { Connection sql; XmppConnection xmpp; - Vector sockReplies = new Vector(); - Vector sockMessages = new Vector(); - Vector sockAll = new Vector(); + ArrayList sockReplies = new ArrayList(); + ArrayList sockMessages = new ArrayList(); + ArrayList sockAll = new ArrayList(); Selector sel; public static void main(String[] args) { @@ -83,7 +83,7 @@ public class Main implements XmppListener, MessageListener { } public void setupXmppComponent(String password) { - xmpp = new XmppConnectionComponent(new JID("ws.juick.com"), password, "127.0.0.1", 5347, false); + xmpp = new XmppConnectionComponent(new JID("", "ws.juick.com", ""), password, "127.0.0.1", 5347, false); xmpp.addListener((XmppListener) this); xmpp.addListener((MessageListener) this); xmpp.start(); @@ -114,10 +114,10 @@ public class Main implements XmppListener, MessageListener { while (true) { sel.select(); - System.out.println("ONE"); +// System.out.println("ONE"); Iterator it = sel.selectedKeys().iterator(); while (it.hasNext()) { - System.out.println("TWO"); +// System.out.println("TWO"); SelectionKey selKey = (SelectionKey) it.next(); it.remove(); if (selKey.isAcceptable()) { @@ -127,7 +127,7 @@ public class Main implements XmppListener, MessageListener { sChannel.configureBlocking(false); sChannel.register(sel, SelectionKey.OP_READ); } else if (selKey.isReadable()) { - System.out.println("THREE"); +// System.out.println("THREE"); SocketChannel sChannel = (SocketChannel) selKey.channel(); ByteBuffer buf = ByteBuffer.allocate(10240); try { @@ -140,19 +140,19 @@ public class Main implements XmppListener, MessageListener { wsHandshake(sChannel, buf); } else { System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); - System.out.println("FOUR"); +// System.out.println("FOUR"); sChannel.close(); selKey.cancel(); } } else { sChannel.close(); selKey.cancel(); - System.out.println("SIX"); +// System.out.println("SIX"); } } catch (IOException e) { System.err.println(e); sChannel.close(); - System.out.println("FIVE"); +// System.out.println("FIVE"); selKey.cancel(); } } @@ -201,10 +201,8 @@ public class Main implements XmppListener, MessageListener { // Cookies int UID = 0; - + String hash = null; if (hCookie != null) { - String hash = null; - String cookies[] = hCookie.split("; "); for (int i = 0; i < cookies.length; i++) { String cookie[] = cookies[i].split("=", 2); @@ -213,15 +211,12 @@ public class Main implements XmppListener, MessageListener { break; } } - if (hash != null) { UID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); } } - // URL - String loc[] = hLocation.split("/"); int MID = 0; if (hLocation.equals("/my") && UID > 0) { @@ -250,7 +245,7 @@ public class Main implements XmppListener, MessageListener { return; } - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (UID=" + UID + ", MID=" + MID + ")"); + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (Hash=" + hash + "; UID = " + UID + "; MID = " + MID + ")"); Long lSecNum1 = calcSecKeyNum(hSecWebSocketKey1); Long lSecNum2 = calcSecKeyNum(hSecWebSocketKey2); @@ -347,6 +342,7 @@ public class Main implements XmppListener, MessageListener { public void onMessage(com.juick.xmpp.Message msg) { JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (jmsg != null) { + System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); if (jmsg.RID == 0) { onJuickMessagePost(jmsg); } else { @@ -359,15 +355,15 @@ public class Main implements XmppListener, MessageListener { String json = "{" + "\"mid\":" + jmsg.MID + "," + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.Timestamp + "\"," + + "\"timestamp\":\"" + jmsg.TimestampString + "\"," + "\"body\":\"" + encloseJSON(jmsg.Text) + "\""; - if (jmsg.tags.size() > 0) { + if (jmsg.Tags.size() > 0) { json += ",\"tags\":["; - for (int i = 0; i < jmsg.tags.size(); i++) { + for (int i = 0; i < jmsg.Tags.size(); i++) { if (i > 0) { json += ","; } - json += "\"" + encloseJSON((String) jmsg.tags.get(i)) + "\""; + json += "\"" + encloseJSON((String) jmsg.Tags.get(i)) + "\""; } json += "]"; } @@ -453,6 +449,7 @@ public class Main implements XmppListener, MessageListener { try { out.rewind(); ss.sock.write(out); + System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); } catch (IOException e) { sockAll.remove(i); try { @@ -463,8 +460,6 @@ public class Main implements XmppListener, MessageListener { } } - - } private void onJuickMessageReply(com.juick.Message jmsg) { @@ -473,7 +468,7 @@ public class Main implements XmppListener, MessageListener { + "\"rid\":" + jmsg.RID + "," + "\"replyto\":" + jmsg.ReplyTo + "," + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.Timestamp + "\"," + + "\"timestamp\":\"" + jmsg.TimestampString + "\"," + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" + "}"; @@ -489,6 +484,7 @@ public class Main implements XmppListener, MessageListener { try { out.rewind(); ss.sock.write(out); + System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); } catch (IOException e) { sockReplies.remove(i); try { -- cgit v1.2.3 From 9239345c909ecdba66d061bd6d7a78cdf9b605c3 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sun, 14 Apr 2013 06:43:53 +0700 Subject: socket.close --- nbproject/project.properties | 3 ++- src/com/juick/jabber/ws/Main.java | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/nbproject/project.properties b/nbproject/project.properties index 9b3f6bc4..8858bd3a 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -31,7 +31,8 @@ jar.compress=false javac.classpath=\ ${reference.com_juick_xmpp.jar}:\ ${reference.com_juick_server.jar}:\ - ${reference.com_juick.jar} + ${reference.com_juick.jar}:\ + ${libs.MySQLDriver.classpath} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index 9107c408..32f3e962 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -141,16 +141,19 @@ public class Main implements XmppListener, MessageListener { } else { System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); // System.out.println("FOUR"); + sChannel.socket().close(); sChannel.close(); selKey.cancel(); } } else { + sChannel.socket().close(); sChannel.close(); selKey.cancel(); // System.out.println("SIX"); } } catch (IOException e) { System.err.println(e); + sChannel.socket().close(); sChannel.close(); // System.out.println("FIVE"); selKey.cancel(); -- cgit v1.2.3 From 44c7c01059640147fbb9df129c2cff7b0a127329 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sat, 3 Aug 2013 19:19:34 +0700 Subject: XMPP library update --- src/com/juick/jabber/ws/Main.java | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index 32f3e962..dec38586 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -18,15 +18,15 @@ package com.juick.jabber.ws; import com.juick.xmpp.JID; -import com.juick.xmpp.MessageListener; -import com.juick.xmpp.XmppConnection; -import com.juick.xmpp.XmppConnectionComponent; -import com.juick.xmpp.XmppListener; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; import com.juick.xmpp.extensions.JuickMessage; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; import java.net.InetSocketAddress; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.SelectionKey; @@ -48,10 +48,10 @@ import java.util.Properties; * * @author Ugnich Anton */ -public class Main implements XmppListener, MessageListener { +public class Main implements Stream.StreamListener, Message.MessageListener { Connection sql; - XmppConnection xmpp; + Stream xmpp; ArrayList sockReplies = new ArrayList(); ArrayList sockMessages = new ArrayList(); ArrayList sockAll = new ArrayList(); @@ -83,25 +83,26 @@ public class Main implements XmppListener, MessageListener { } public void setupXmppComponent(String password) { - xmpp = new XmppConnectionComponent(new JID("", "ws.juick.com", ""), password, "127.0.0.1", 5347, false); - xmpp.addListener((XmppListener) this); - xmpp.addListener((MessageListener) this); - xmpp.start(); - } - - @Override - public void onConnectionFailed(String msg) { - System.err.println("XMPP onConnFailed " + msg); + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((Message.MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + System.err.println(e); + } } @Override - public void onAuth(String resource) { - System.err.println("XMPP onAuth " + resource); + public void onStreamFail(String msg) { + System.err.println("Component stream failed: " + msg); } @Override - public void onAuthFailed(String message) { - System.err.println("XMPP onAuthFailed " + message); + public void onStreamReady() { + System.err.println("Component stream ready"); } public void setupWsServer() { @@ -115,10 +116,10 @@ public class Main implements XmppListener, MessageListener { while (true) { sel.select(); // System.out.println("ONE"); - Iterator it = sel.selectedKeys().iterator(); + Iterator it = sel.selectedKeys().iterator(); while (it.hasNext()) { // System.out.println("TWO"); - SelectionKey selKey = (SelectionKey) it.next(); + SelectionKey selKey = it.next(); it.remove(); if (selKey.isAcceptable()) { ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); -- cgit v1.2.3 From db2d48c068715c2841fd60b955c98580236c400d Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sun, 4 Aug 2013 02:50:44 +0700 Subject: 4 threads --- src/com/juick/jabber/ws/Main.java | 475 +------------------------- src/com/juick/jabber/ws/SocketSubscribed.java | 22 ++ src/com/juick/jabber/ws/WSConnections.java | 44 +++ src/com/juick/jabber/ws/WSData.java | 233 +++++++++++++ src/com/juick/jabber/ws/WSKeepAlive.java | 20 ++ src/com/juick/jabber/ws/XMPPConnection.java | 217 ++++++++++++ 6 files changed, 547 insertions(+), 464 deletions(-) create mode 100644 src/com/juick/jabber/ws/SocketSubscribed.java create mode 100644 src/com/juick/jabber/ws/WSConnections.java create mode 100644 src/com/juick/jabber/ws/WSData.java create mode 100644 src/com/juick/jabber/ws/WSKeepAlive.java create mode 100644 src/com/juick/jabber/ws/XMPPConnection.java (limited to 'src') diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index dec38586..7036b0d4 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -17,45 +17,24 @@ */ package com.juick.jabber.ws; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import com.juick.xmpp.extensions.JuickMessage; import java.io.FileInputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; -import java.security.MessageDigest; import java.sql.Connection; import java.sql.DriverManager; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; -import java.util.Iterator; import java.util.Properties; /** * * @author Ugnich Anton */ -public class Main implements Stream.StreamListener, Message.MessageListener { +public class Main { Connection sql; - Stream xmpp; - ArrayList sockReplies = new ArrayList(); - ArrayList sockMessages = new ArrayList(); - ArrayList sockAll = new ArrayList(); - Selector sel; + XMPPConnection xmpp; + public final static ArrayList sockReplies = new ArrayList(); + public final static ArrayList sockMessages = new ArrayList(); + public final static ArrayList sockAll = new ArrayList(); public static void main(String[] args) { new Main().start(); @@ -67,8 +46,11 @@ public class Main implements Stream.StreamListener, Message.MessageListener { conf.load(new FileInputStream("/etc/juick/ws.conf")); setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); - setupXmppComponent(conf.getProperty("xmpp_password", "")); - setupWsServer(); + xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); + new Thread(xmpp).start(); + new Thread(new WSData(sql)).start(); + new Thread(new WSConnections()).start(); + new Thread(new WSKeepAlive(sql)).start(); } catch (Exception e) { System.err.println(e); } @@ -81,439 +63,4 @@ public class Main implements Stream.StreamListener, Message.MessageListener { System.err.println(e); } } - - public void setupXmppComponent(String password) { - try { - Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); - xmpp.addChildParser(new JuickMessage()); - xmpp.addListener((Stream.StreamListener) this); - xmpp.addListener((Message.MessageListener) this); - xmpp.startParsing(); - } catch (IOException e) { - System.err.println(e); - } - } - - @Override - public void onStreamFail(String msg) { - System.err.println("Component stream failed: " + msg); - } - - @Override - public void onStreamReady() { - System.err.println("Component stream ready"); - } - - public void setupWsServer() { - try { - sel = Selector.open(); - ServerSocketChannel listensock = ServerSocketChannel.open(); - listensock.configureBlocking(false); - listensock.socket().bind(new InetSocketAddress(8080)); - listensock.register(sel, SelectionKey.OP_ACCEPT); - - while (true) { - sel.select(); -// System.out.println("ONE"); - Iterator it = sel.selectedKeys().iterator(); - while (it.hasNext()) { -// System.out.println("TWO"); - SelectionKey selKey = it.next(); - it.remove(); - if (selKey.isAcceptable()) { - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); - sChannel.configureBlocking(false); - sChannel.register(sel, SelectionKey.OP_READ); - } else if (selKey.isReadable()) { -// System.out.println("THREE"); - SocketChannel sChannel = (SocketChannel) selKey.channel(); - ByteBuffer buf = ByteBuffer.allocate(10240); - try { - if (sChannel.read(buf) > 0) { - buf.flip(); - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - if (charbuf.charAt(0) == 0 && charbuf.charAt(charbuf.length() - 1) == 0xFF) { - wsTextFrame(sChannel, charbuf.subSequence(1, charbuf.length() - 2)); - } else if (charbuf.charAt(0) == 'G' && charbuf.charAt(1) == 'E' && charbuf.charAt(2) == 'T' && charbuf.charAt(3) == ' ') { - wsHandshake(sChannel, buf); - } else { - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); -// System.out.println("FOUR"); - sChannel.socket().close(); - sChannel.close(); - selKey.cancel(); - } - } else { - sChannel.socket().close(); - sChannel.close(); - selKey.cancel(); -// System.out.println("SIX"); - } - } catch (IOException e) { - System.err.println(e); - sChannel.socket().close(); - sChannel.close(); -// System.out.println("FIVE"); - selKey.cancel(); - } - } - } - } - } catch (Exception e) { - System.err.println(e); - } - } - - public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { - String hOrigin = null; - String hHost = null; - String hLocation = null; - String hSecWebSocketKey1 = null; - String hSecWebSocketKey2 = null; - String hCookie = null; - - buf.rewind(); - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - String headers[] = charbuf.toString().split("\r\n"); - for (int i = 0; i < headers.length; i++) { - String h[] = headers[i].split(" ", 2); - if (h.length == 2) { - if (h[0].equals("GET")) { - hLocation = headers[i].split(" ", 3)[1]; - } else if (h[0].equals("Origin:")) { - hOrigin = h[1]; - } else if (h[0].equals("Host:")) { - hHost = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key1:")) { - hSecWebSocketKey1 = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key2:")) { - hSecWebSocketKey2 = h[1]; - } else if (h[0].equals("Cookie:")) { - hCookie = h[1]; - } - } - } - - if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey1 == null || hSecWebSocketKey2 == null) { - System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); - sock.close(); - return; - } - - // Cookies - int UID = 0; - String hash = null; - if (hCookie != null) { - String cookies[] = hCookie.split("; "); - for (int i = 0; i < cookies.length; i++) { - String cookie[] = cookies[i].split("=", 2); - if (cookie[0].equals("hash")) { - hash = cookie[1]; - break; - } - } - if (hash != null) { - UID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); - } - } - - // URL - String loc[] = hLocation.split("/"); - int MID = 0; - if (hLocation.equals("/my") && UID > 0) { - sockMessages.add(new SocketSubscribed(sock, UID, 0)); - } else if (hLocation.equals("/all")) { - sockAll.add(new SocketSubscribed(sock, UID, 0)); - } else if ((loc.length == 2 || loc.length == 3) && loc[1].equals("replies")) { - if (loc.length == 2) { - sockReplies.add(new SocketSubscribed(sock, UID, 0)); - } else { - try { - MID = Integer.parseInt(loc[2]); - } catch (Exception e) { - } - if (MID > 0) { - sockReplies.add(new SocketSubscribed(sock, UID, MID)); - } else { - System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid MID"); - sock.close(); - return; - } - } - } else { - System.err.println(sock.socket().getRemoteSocketAddress().toString() + " Invalid location"); - sock.close(); - return; - } - - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (Hash=" + hash + "; UID = " + UID + "; MID = " + MID + ")"); - - Long lSecNum1 = calcSecKeyNum(hSecWebSocketKey1); - Long lSecNum2 = calcSecKeyNum(hSecWebSocketKey2); - - BigInteger sec1 = new BigInteger(lSecNum1.toString()); - BigInteger sec2 = new BigInteger(lSecNum2.toString()); - - // concatenate 3 parts secNum1 + secNum2 + secKey (16 Bytes) - byte[] l128Bit = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - byte[] lTmp; - - lTmp = sec1.toByteArray(); - int lIdx = lTmp.length; - int lCnt = 0; - while (lIdx > 0 && lCnt < 4) { - lIdx--; - lCnt++; - l128Bit[4 - lCnt] = lTmp[lIdx]; - } - - lTmp = sec2.toByteArray(); - lIdx = lTmp.length; - lCnt = 0; - while (lIdx > 0 && lCnt < 4) { - lIdx--; - lCnt++; - l128Bit[8 - lCnt] = lTmp[lIdx]; - } - - buf.rewind(); - for (int i = 0; i < 8; i++) { - l128Bit[8 + i] = buf.get(buf.limit() - 8 + i); - } - - String outstr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" - + "Upgrade: WebSocket\r\n" - + "Connection: Upgrade\r\n" - + "Sec-WebSocket-Origin: " + hOrigin + "\r\n" - + "Sec-WebSocket-Location: ws://" + hHost + hLocation + "\r\n" - + "Sec-WebSocket-Protocol: sample\r\n" - + "\r\n"; - ByteBuffer out = ByteBuffer.allocate(4096); - out.put(Charset.forName("ISO-8859-1").encode(outstr)); - out.put(MessageDigest.getInstance("MD5").digest(l128Bit)); - out.flip(); - - sock.write(out); - } - - private static long calcSecKeyNum(String aKey) { - StringBuilder lSB = new StringBuilder(); - // StringBuuffer lSB = new StringBuuffer(); - int lSpaces = 0; - for (int i = 0; i < aKey.length(); i++) { - char lC = aKey.charAt(i); - if (lC == ' ') { - lSpaces++; - } else if (lC >= '0' && lC <= '9') { - lSB.append(lC); - } - } - long lRes = -1; - if (lSpaces > 0) { - try { - lRes = Long.parseLong(lSB.toString()) / lSpaces; - // log.debug("Key: " + aKey + ", Numbers: " + lSB.toString() + - // ", Spaces: " + lSpaces + ", Result: " + lRes); - } catch (NumberFormatException ex) { - // use default result - } - } - return lRes; - } - - public void wsTextFrame(SocketChannel sock, CharSequence csbuf) { - String buf = csbuf.toString(); - if (buf.equals(" ")) { - ByteBuffer out = ByteBuffer.allocate(4); - out.put((byte) 0x00); - out.put((byte) 0x20); - out.put((byte) 0xFF); - out.flip(); - out.rewind(); - try { - sock.write(out); - } catch (IOException e) { - } - } else { - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " DATA '" + buf + "'"); - } - } - - @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); - if (jmsg != null) { - System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); - if (jmsg.RID == 0) { - onJuickMessagePost(jmsg); - } else { - onJuickMessageReply(jmsg); - } - } - } - - private void onJuickMessagePost(com.juick.Message jmsg) { - String json = "{" - + "\"mid\":" + jmsg.MID + "," - + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.TimestampString + "\"," - + "\"body\":\"" + encloseJSON(jmsg.Text) + "\""; - if (jmsg.Tags.size() > 0) { - json += ",\"tags\":["; - for (int i = 0; i < jmsg.Tags.size(); i++) { - if (i > 0) { - json += ","; - } - json += "\"" + encloseJSON((String) jmsg.Tags.get(i)) + "\""; - } - json += "]"; - } - json += "}"; - - ByteBuffer out = ByteBuffer.allocate(10240); - out.put((byte) 0x00); - out.put(Charset.forName("UTF-8").encode(json)); - out.put((byte) 0xFF); - out.flip(); - - - String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; - if (jmsg.Privacy < 0) { - query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; - } - - Statement stmt = null; - ResultSet rs = null; - try { - stmt = sql.createStatement(); - rs = stmt.executeQuery(query); - rs.beforeFirst(); - while (rs.next()) { - int UID = rs.getInt(1); - - for (int i = sockMessages.size() - 1; i >= 0; i--) { - SocketSubscribed ss = sockMessages.get(i); - if (ss.UID == UID) { - try { - out.rewind(); - ss.sock.write(out); - } catch (IOException e) { - sockMessages.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - - if (jmsg.Privacy <= 0) { - for (int i = sockAll.size() - 1; i >= 0; i--) { - SocketSubscribed ss = sockAll.get(i); - if (ss.UID == UID) { - try { - out.rewind(); - ss.sock.write(out); - } catch (IOException e) { - sockAll.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - } - - } - } catch (SQLException e) { - System.err.println(e); - } finally { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - } - } - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException e) { - } - } - } - - // Send to all - if (jmsg.Privacy > 0) { - for (int i = sockAll.size() - 1; i >= 0; i--) { - SocketSubscribed ss = sockAll.get(i); - try { - out.rewind(); - ss.sock.write(out); - System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); - } catch (IOException e) { - sockAll.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - - } - - private void onJuickMessageReply(com.juick.Message jmsg) { - String json = "{" - + "\"mid\":" + jmsg.MID + "," - + "\"rid\":" + jmsg.RID + "," - + "\"replyto\":" + jmsg.ReplyTo + "," - + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.TimestampString + "\"," - + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" - + "}"; - - ByteBuffer out = ByteBuffer.allocate(10240); - out.put((byte) 0x00); - out.put(Charset.forName("UTF-8").encode(json)); - out.put((byte) 0xFF); - out.flip(); - - for (int i = sockReplies.size() - 1; i >= 0; i--) { - SocketSubscribed ss = sockReplies.get(i); - if (ss.MID == 0 || ss.MID == jmsg.MID) { - try { - out.rewind(); - ss.sock.write(out); - System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); - } catch (IOException e) { - sockReplies.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - } - - public static String encloseJSON(String str) { - return str.replace("\"", """).replace("\\", "\\\\").replace("\n", "\\n"); - } -} - -class SocketSubscribed { - - public SocketChannel sock; - public int UID; - public int MID; - - public SocketSubscribed(SocketChannel sock, int UID, int MID) { - this.sock = sock; - this.UID = UID; - this.MID = MID; - } -} +} \ No newline at end of file diff --git a/src/com/juick/jabber/ws/SocketSubscribed.java b/src/com/juick/jabber/ws/SocketSubscribed.java new file mode 100644 index 00000000..29baf0b8 --- /dev/null +++ b/src/com/juick/jabber/ws/SocketSubscribed.java @@ -0,0 +1,22 @@ +package com.juick.jabber.ws; + +import java.nio.channels.SocketChannel; + +/** + * + * @author ugnich + */ +public class SocketSubscribed { + + public SocketChannel sock = null; + public int UID = 0; + public int MID = 0; + public long tsConnected = 0; + public long tsLastData = 0; + + public SocketSubscribed(SocketChannel sock, int UID, int MID) { + this.sock = sock; + this.UID = UID; + this.MID = MID; + } +} diff --git a/src/com/juick/jabber/ws/WSConnections.java b/src/com/juick/jabber/ws/WSConnections.java new file mode 100644 index 00000000..020442f7 --- /dev/null +++ b/src/com/juick/jabber/ws/WSConnections.java @@ -0,0 +1,44 @@ +package com.juick.jabber.ws; + +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class WSConnections implements Runnable { + + Selector sel; + + @Override + public void run() { + try { + sel = Selector.open(); + ServerSocketChannel listensock = ServerSocketChannel.open(); + listensock.configureBlocking(false); + listensock.socket().bind(new InetSocketAddress(8081)); + listensock.register(sel, SelectionKey.OP_ACCEPT); + + while (true) { + sel.select(); + Iterator it = sel.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + it.remove(); + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); + SocketChannel sChannel = ssChannel.accept(); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + sChannel.configureBlocking(false); + sChannel.register(WSData.sel, SelectionKey.OP_READ); + } + } + } catch (Exception e) { + System.err.println("WSConnections: " + e); + } + } +} diff --git a/src/com/juick/jabber/ws/WSData.java b/src/com/juick/jabber/ws/WSData.java new file mode 100644 index 00000000..97193510 --- /dev/null +++ b/src/com/juick/jabber/ws/WSData.java @@ -0,0 +1,233 @@ +package com.juick.jabber.ws; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.sql.Connection; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class WSData implements Runnable { + + Connection sql; + public static Selector sel; + + public WSData(Connection sql) { + this.sql = sql; + } + + @Override + public void run() { + try { + sel = Selector.open(); + while (true) { + sel.select(); + Iterator it = sel.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + it.remove(); + + SocketChannel sChannel = (SocketChannel) selKey.channel(); + ByteBuffer buf = ByteBuffer.allocate(10240); + try { + if (sChannel.read(buf) > 0) { + buf.flip(); + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + if (charbuf.charAt(0) == 0 && charbuf.charAt(charbuf.length() - 1) == 0xFF) { + wsTextFrame(sChannel, charbuf.subSequence(1, charbuf.length() - 2)); + } else if (charbuf.charAt(0) == 'G' && charbuf.charAt(1) == 'E' && charbuf.charAt(2) == 'T' && charbuf.charAt(3) == ' ') { + wsHandshake(sChannel, buf); + } else { + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); + } + } else { + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString()+ " NO DATA"); + } + } catch (IOException e) { + System.err.println("WSData: " + e); + sChannel.socket().close(); + sChannel.close(); + selKey.cancel(); + } + } + } + } catch (Exception e) { + System.err.println("WSData: " + e); + } + } + + public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { + String hOrigin = null; + String hHost = null; + String hLocation = null; + String hSecWebSocketKey1 = null; + String hSecWebSocketKey2 = null; + String hCookie = null; + + buf.rewind(); + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + String headers[] = charbuf.toString().split("\r\n"); + for (int i = 0; i < headers.length; i++) { + String h[] = headers[i].split(" ", 2); + if (h.length == 2) { + if (h[0].equals("GET")) { + hLocation = headers[i].split(" ", 3)[1]; + } else if (h[0].equals("Origin:")) { + hOrigin = h[1]; + } else if (h[0].equals("Host:")) { + hHost = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key1:")) { + hSecWebSocketKey1 = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key2:")) { + hSecWebSocketKey2 = h[1]; + } else if (h[0].equals("Cookie:")) { + hCookie = h[1]; + } + } + } + + if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey1 == null || hSecWebSocketKey2 == null) { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); + } + + // Cookies + int UID = 0; + String hash = null; + if (hCookie != null) { + String cookies[] = hCookie.split("; "); + for (int i = 0; i < cookies.length; i++) { + String cookie[] = cookies[i].split("=", 2); + if (cookie[0].equals("hash")) { + hash = cookie[1]; + break; + } + } + if (hash != null) { + UID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); + } + } + + // URL + String loc[] = hLocation.split("/"); + int MID = 0; + if (hLocation.equals("/my") && UID > 0) { + Main.sockMessages.add(new SocketSubscribed(sock, UID, 0)); + } else if (hLocation.equals("/all")) { + Main.sockAll.add(new SocketSubscribed(sock, UID, 0)); + } else if ((loc.length == 2 || loc.length == 3) && loc[1].equals("replies")) { + if (loc.length == 2) { + Main.sockReplies.add(new SocketSubscribed(sock, UID, 0)); + } else { + try { + MID = Integer.parseInt(loc[2]); + } catch (Exception e) { + } + if (MID > 0) { + Main.sockReplies.add(new SocketSubscribed(sock, UID, MID)); + } else { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid MID"); + } + } + } else { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid location"); + } + + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (Hash=" + hash + "; UID = " + UID + "; MID = " + MID + ")"); + + Long lSecNum1 = calcSecKeyNum(hSecWebSocketKey1); + Long lSecNum2 = calcSecKeyNum(hSecWebSocketKey2); + + BigInteger sec1 = new BigInteger(lSecNum1.toString()); + BigInteger sec2 = new BigInteger(lSecNum2.toString()); + + // concatenate 3 parts secNum1 + secNum2 + secKey (16 Bytes) + byte[] l128Bit = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + byte[] lTmp; + + lTmp = sec1.toByteArray(); + int lIdx = lTmp.length; + int lCnt = 0; + while (lIdx > 0 && lCnt < 4) { + lIdx--; + lCnt++; + l128Bit[4 - lCnt] = lTmp[lIdx]; + } + + lTmp = sec2.toByteArray(); + lIdx = lTmp.length; + lCnt = 0; + while (lIdx > 0 && lCnt < 4) { + lIdx--; + lCnt++; + l128Bit[8 - lCnt] = lTmp[lIdx]; + } + + buf.rewind(); + for (int i = 0; i < 8; i++) { + l128Bit[8 + i] = buf.get(buf.limit() - 8 + i); + } + + String outstr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + + "Upgrade: WebSocket\r\n" + + "Connection: Upgrade\r\n" + + "Sec-WebSocket-Origin: " + hOrigin + "\r\n" + + "Sec-WebSocket-Location: ws://" + hHost + hLocation + "\r\n" + + "Sec-WebSocket-Protocol: sample\r\n" + + "\r\n"; + ByteBuffer out = ByteBuffer.allocate(4096); + out.put(Charset.forName("ISO-8859-1").encode(outstr)); + out.put(MessageDigest.getInstance("MD5").digest(l128Bit)); + out.flip(); + + sock.write(out); + } + + private static long calcSecKeyNum(String aKey) { + StringBuilder lSB = new StringBuilder(); + int lSpaces = 0; + for (int i = 0; i < aKey.length(); i++) { + char lC = aKey.charAt(i); + if (lC == ' ') { + lSpaces++; + } else if (lC >= '0' && lC <= '9') { + lSB.append(lC); + } + } + long lRes = -1; + if (lSpaces > 0) { + try { + lRes = Long.parseLong(lSB.toString()) / lSpaces; + } catch (NumberFormatException ex) { + // use default result + } + } + return lRes; + } + + public void wsTextFrame(SocketChannel sock, CharSequence csbuf) { + String buf = csbuf.toString(); + if (buf.equals(" ")) { + ByteBuffer out = ByteBuffer.allocate(4); + out.put((byte) 0x00); + out.put((byte) 0x20); + out.put((byte) 0xFF); + out.flip(); + out.rewind(); + try { + sock.write(out); + } catch (IOException e) { + } + } else { + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " DATA '" + buf + "'"); + } + } +} diff --git a/src/com/juick/jabber/ws/WSKeepAlive.java b/src/com/juick/jabber/ws/WSKeepAlive.java new file mode 100644 index 00000000..ba6376a4 --- /dev/null +++ b/src/com/juick/jabber/ws/WSKeepAlive.java @@ -0,0 +1,20 @@ +package com.juick.jabber.ws; + +import java.sql.Connection; + +/** + * + * @author ugnich + */ +public class WSKeepAlive implements Runnable { + + Connection sql; + + public WSKeepAlive(Connection sql) { + this.sql = sql; + } + + @Override + public void run() { + } +} diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java new file mode 100644 index 00000000..91a8387b --- /dev/null +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -0,0 +1,217 @@ +package com.juick.jabber.ws; + +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import com.juick.xmpp.extensions.JuickMessage; +import java.io.IOException; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * + * @author ugnich + */ +public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { + + Connection sql; + Stream xmpp; + String xmppPassword; + + public XMPPConnection(Connection sql, String password) { + this.sql = sql; + xmppPassword = password; + } + + @Override + public void run() { + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((Message.MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + System.err.println("XMPPConnection: " + e); + } + } + + @Override + public void onStreamReady() { + System.err.println("Stream ready"); + } + + @Override + public void onStreamFail(String msg) { + System.err.println("Stream failed: " + msg); + } + + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); + if (jmsg != null) { + System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); + if (jmsg.RID == 0) { + onJuickMessagePost(jmsg); + } else { + onJuickMessageReply(jmsg); + } + } + } + + private void onJuickMessagePost(com.juick.Message jmsg) { + String json = "{" + + "\"mid\":" + jmsg.MID + "," + + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," + + "\"timestamp\":\"" + jmsg.TimestampString + "\"," + + "\"body\":\"" + encloseJSON(jmsg.Text) + "\""; + if (jmsg.Tags.size() > 0) { + json += ",\"tags\":["; + for (int i = 0; i < jmsg.Tags.size(); i++) { + if (i > 0) { + json += ","; + } + json += "\"" + encloseJSON((String) jmsg.Tags.get(i)) + "\""; + } + json += "]"; + } + json += "}"; + + ByteBuffer out = ByteBuffer.allocate(10240); + out.put((byte) 0x00); + out.put(Charset.forName("UTF-8").encode(json)); + out.put((byte) 0xFF); + out.flip(); + + + String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; + if (jmsg.Privacy < 0) { + query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; + } + + Statement stmt = null; + ResultSet rs = null; + try { + stmt = sql.createStatement(); + rs = stmt.executeQuery(query); + rs.beforeFirst(); + while (rs.next()) { + int UID = rs.getInt(1); + + for (int i = Main.sockMessages.size() - 1; i >= 0; i--) { + SocketSubscribed ss = Main.sockMessages.get(i); + if (ss.UID == UID) { + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + Main.sockMessages.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + + if (jmsg.Privacy <= 0) { + for (int i = Main.sockAll.size() - 1; i >= 0; i--) { + SocketSubscribed ss = Main.sockAll.get(i); + if (ss.UID == UID) { + try { + out.rewind(); + ss.sock.write(out); + } catch (IOException e) { + Main.sockAll.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + } + + } + } catch (SQLException e) { + System.err.println(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + } + } + } + + // Send to all + if (jmsg.Privacy > 0) { + for (int i = Main.sockAll.size() - 1; i >= 0; i--) { + SocketSubscribed ss = Main.sockAll.get(i); + try { + out.rewind(); + ss.sock.write(out); + System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); + } catch (IOException e) { + Main.sockAll.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + + } + + private void onJuickMessageReply(com.juick.Message jmsg) { + String json = "{" + + "\"mid\":" + jmsg.MID + "," + + "\"rid\":" + jmsg.RID + "," + + "\"replyto\":" + jmsg.ReplyTo + "," + + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," + + "\"timestamp\":\"" + jmsg.TimestampString + "\"," + + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" + + "}"; + + ByteBuffer out = ByteBuffer.allocate(10240); + out.put((byte) 0x00); + out.put(Charset.forName("UTF-8").encode(json)); + out.put((byte) 0xFF); + out.flip(); + + for (int i = Main.sockReplies.size() - 1; i >= 0; i--) { + SocketSubscribed ss = Main.sockReplies.get(i); + if (ss.MID == 0 || ss.MID == jmsg.MID) { + try { + out.rewind(); + ss.sock.write(out); + System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); + } catch (IOException e) { + Main.sockReplies.remove(i); + try { + ss.sock.close(); + } catch (IOException ex) { + } + } + } + } + } + + public static String encloseJSON(String str) { + return str.replace("\"", """).replace("\\", "\\\\").replace("\n", "\\n"); + } +} -- cgit v1.2.3 From 2a8c4f613f397619c563fbb74c04e39074d6e98e Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Wed, 14 Aug 2013 09:21:47 +0700 Subject: RFC 6455 --- lib/org.json.jar | Bin 0 -> 80177 bytes lib/xpp3_min-1.1.4.jar | Bin 0 -> 24955 bytes nbproject/build-impl.xml | 14 ++ nbproject/genfiles.properties | 6 +- nbproject/project.properties | 9 +- nbproject/project.xml | 8 + src/com/juick/jabber/ws/Main.java | 10 +- src/com/juick/jabber/ws/SocketSubscribed.java | 15 +- src/com/juick/jabber/ws/WSConnections.java | 4 +- src/com/juick/jabber/ws/WSData.java | 337 +++++++++++++++----------- src/com/juick/jabber/ws/WSKeepAlive.java | 106 ++++++++ src/com/juick/jabber/ws/XMPPConnection.java | 179 +++++--------- 12 files changed, 412 insertions(+), 276 deletions(-) create mode 100644 lib/org.json.jar create mode 100644 lib/xpp3_min-1.1.4.jar (limited to 'src') diff --git a/lib/org.json.jar b/lib/org.json.jar new file mode 100644 index 00000000..911ca8f1 Binary files /dev/null and b/lib/org.json.jar differ diff --git a/lib/xpp3_min-1.1.4.jar b/lib/xpp3_min-1.1.4.jar new file mode 100644 index 00000000..e02ea412 Binary files /dev/null and b/lib/xpp3_min-1.1.4.jar differ diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index d38c01df..06162daf 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -576,6 +576,13 @@ is divided into following sections: + + + + + + + @@ -1031,6 +1038,13 @@ is divided into following sections: + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index d01461cb..8b83736c 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=935965cc +build.xml.data.CRC32=68cbdaea build.xml.script.CRC32=2ec0c6a9 build.xml.stylesheet.CRC32=28e38971@1.44.1.45 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=935965cc -nbproject/build-impl.xml.script.CRC32=3d733aec +nbproject/build-impl.xml.data.CRC32=68cbdaea +nbproject/build-impl.xml.script.CRC32=e6ac4bc8 nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45 diff --git a/nbproject/project.properties b/nbproject/project.properties index 8858bd3a..e6b5db4c 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -26,13 +26,18 @@ dist.jar=${dist.dir}/com.juick.jabber.ws.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= +file.reference.org.json.jar=lib/org.json.jar +file.reference.xpp3_min-1.1.4.jar=lib/xpp3_min-1.1.4.jar includes=** jar.compress=false javac.classpath=\ ${reference.com_juick_xmpp.jar}:\ ${reference.com_juick_server.jar}:\ + ${reference.com_juick_json.jar}:\ ${reference.com_juick.jar}:\ - ${libs.MySQLDriver.classpath} + ${libs.MySQLDriver.classpath}:\ + ${file.reference.org.json.jar}:\ + ${file.reference.xpp3_min-1.1.4.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false @@ -62,9 +67,11 @@ meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false platform.active=default_platform project.com_juick=../com.juick +project.com_juick_json=../com.juick.json project.com_juick_server=../com.juick.server project.com_juick_xmpp=../com.juick.xmpp reference.com_juick.jar=${project.com_juick}/dist/com.juick.jar +reference.com_juick_json.jar=${project.com_juick_json}/dist/com.juick.json.jar reference.com_juick_server.jar=${project.com_juick_server}/dist/com.juick.server.jar reference.com_juick_xmpp.jar=${project.com_juick_xmpp}/dist/com.juick.xmpp.jar run.classpath=\ diff --git a/nbproject/project.xml b/nbproject/project.xml index 0445b566..48bac001 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -18,6 +18,14 @@ clean jar + + com_juick_json + jar + + jar + clean + jar + com_juick_server jar diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java index 7036b0d4..a7aea543 100644 --- a/src/com/juick/jabber/ws/Main.java +++ b/src/com/juick/jabber/ws/Main.java @@ -32,9 +32,8 @@ public class Main { Connection sql; XMPPConnection xmpp; - public final static ArrayList sockReplies = new ArrayList(); - public final static ArrayList sockMessages = new ArrayList(); - public final static ArrayList sockAll = new ArrayList(); + public static String STATSFILE = null; + public final static ArrayList clients = new ArrayList(); public static void main(String[] args) { new Main().start(); @@ -45,11 +44,14 @@ public class Main { Properties conf = new Properties(); conf.load(new FileInputStream("/etc/juick/ws.conf")); + STATSFILE = conf.getProperty("statsfile"); + setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); new Thread(xmpp).start(); + + //new Thread(new WSConnections()).start(); new Thread(new WSData(sql)).start(); - new Thread(new WSConnections()).start(); new Thread(new WSKeepAlive(sql)).start(); } catch (Exception e) { System.err.println(e); diff --git a/src/com/juick/jabber/ws/SocketSubscribed.java b/src/com/juick/jabber/ws/SocketSubscribed.java index 29baf0b8..f865ce6a 100644 --- a/src/com/juick/jabber/ws/SocketSubscribed.java +++ b/src/com/juick/jabber/ws/SocketSubscribed.java @@ -9,14 +9,19 @@ import java.nio.channels.SocketChannel; public class SocketSubscribed { public SocketChannel sock = null; + public String clientName = null; + public int VUID = 0; public int UID = 0; public int MID = 0; - public long tsConnected = 0; - public long tsLastData = 0; + public boolean allMessages = false; + public boolean allReplies = false; + public long tsConnected; + public long tsLastData; - public SocketSubscribed(SocketChannel sock, int UID, int MID) { + public SocketSubscribed(SocketChannel sock, String clientName, int VUID) { this.sock = sock; - this.UID = UID; - this.MID = MID; + this.clientName = clientName; + this.VUID = VUID; + tsConnected = tsLastData = System.currentTimeMillis(); } } diff --git a/src/com/juick/jabber/ws/WSConnections.java b/src/com/juick/jabber/ws/WSConnections.java index 020442f7..15fbe4e8 100644 --- a/src/com/juick/jabber/ws/WSConnections.java +++ b/src/com/juick/jabber/ws/WSConnections.java @@ -32,9 +32,9 @@ public class WSConnections implements Runnable { it.remove(); ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); SocketChannel sChannel = ssChannel.accept(); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); sChannel.configureBlocking(false); - sChannel.register(WSData.sel, SelectionKey.OP_READ); + sChannel.register(sel, SelectionKey.OP_READ); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); } } } catch (Exception e) { diff --git a/src/com/juick/jabber/ws/WSData.java b/src/com/juick/jabber/ws/WSData.java index 97193510..d77257e6 100644 --- a/src/com/juick/jabber/ws/WSData.java +++ b/src/com/juick/jabber/ws/WSData.java @@ -1,14 +1,19 @@ package com.juick.jabber.ws; +import com.juick.server.MessagesQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.utils.Base64; import java.io.IOException; -import java.math.BigInteger; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.sql.Connection; import java.util.Iterator; @@ -17,46 +22,77 @@ import java.util.Iterator; * @author ugnich */ public class WSData implements Runnable { - + + static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; Connection sql; - public static Selector sel; - + public Selector sel; + public WSData(Connection sql) { this.sql = sql; } - + @Override public void run() { try { sel = Selector.open(); + ServerSocketChannel listensock = ServerSocketChannel.open(); + listensock.configureBlocking(false); + listensock.socket().bind(new InetSocketAddress(8081)); + listensock.register(sel, SelectionKey.OP_ACCEPT); + while (true) { sel.select(); Iterator it = sel.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey selKey = it.next(); it.remove(); - - SocketChannel sChannel = (SocketChannel) selKey.channel(); - ByteBuffer buf = ByteBuffer.allocate(10240); - try { - if (sChannel.read(buf) > 0) { - buf.flip(); - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - if (charbuf.charAt(0) == 0 && charbuf.charAt(charbuf.length() - 1) == 0xFF) { - wsTextFrame(sChannel, charbuf.subSequence(1, charbuf.length() - 2)); - } else if (charbuf.charAt(0) == 'G' && charbuf.charAt(1) == 'E' && charbuf.charAt(2) == 'T' && charbuf.charAt(3) == ' ') { - wsHandshake(sChannel, buf); - } else { - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " INVALID FRAME"); + + if (selKey.isAcceptable()) { + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); + SocketChannel sChannel = ssChannel.accept(); + sChannel.configureBlocking(false); + sChannel.register(sel, SelectionKey.OP_READ); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + } else if (selKey.isReadable()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + ByteBuffer buf = ByteBuffer.allocate(10240); + try { + int readbytes = sChannel.read(buf); + if (readbytes > 0) { + buf.flip(); + + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + System.out.println("DATA: " + charbuf.toString()); + buf.rewind(); + + switch (buf.get(0)) { + case (byte) 0x89: // PING + updateSocketTS(sChannel); + wsPing(sChannel); + break; + case (byte) 0x8A: // PONG + updateSocketTS(sChannel); + break; + case (byte) 0x81: // TEXT FRAME + updateSocketTS(sChannel); + wsTextFrame(sChannel, buf); + break; + case (byte) 'G': // HTTP + updateSocketTS(sChannel); + wsHandshake(sChannel, buf); + break; + case (byte) 0x88: // CONNECTION CLOSE + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " CONNECTION CLOSE"); + } + } else if (readbytes < 0) { + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " END OF STREAM"); } - } else { - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString()+ " NO DATA"); + } catch (IOException e) { + System.err.println("WSData: " + e); + sChannel.socket().close(); + sChannel.close(); + selKey.cancel(); } - } catch (IOException e) { - System.err.println("WSData: " + e); - sChannel.socket().close(); - sChannel.close(); - selKey.cancel(); } } } @@ -64,15 +100,15 @@ public class WSData implements Runnable { System.err.println("WSData: " + e); } } - + public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { String hOrigin = null; String hHost = null; String hLocation = null; - String hSecWebSocketKey1 = null; - String hSecWebSocketKey2 = null; - String hCookie = null; - + String hSecWebSocketKey = null; + String hSecWebSocketVersion = null; + String hXRealIP = null; + buf.rewind(); CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); String headers[] = charbuf.toString().split("\r\n"); @@ -85,149 +121,156 @@ public class WSData implements Runnable { hOrigin = h[1]; } else if (h[0].equals("Host:")) { hHost = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key1:")) { - hSecWebSocketKey1 = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key2:")) { - hSecWebSocketKey2 = h[1]; - } else if (h[0].equals("Cookie:")) { - hCookie = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key:")) { + hSecWebSocketKey = h[1]; + } else if (h[0].equals("Sec-WebSocket-Version:")) { + hSecWebSocketVersion = h[1]; + } else if (h[0].equals("X-Real-IP:")) { + hXRealIP = h[1]; } } } - - if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey1 == null || hSecWebSocketKey2 == null) { + + if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey == null || hSecWebSocketVersion == null || !hSecWebSocketVersion.equals("13")) { throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); } - // Cookies - int UID = 0; - String hash = null; - if (hCookie != null) { - String cookies[] = hCookie.split("; "); - for (int i = 0; i < cookies.length; i++) { - String cookie[] = cookies[i].split("=", 2); - if (cookie[0].equals("hash")) { - hash = cookie[1]; - break; - } + // Auth + int VUID = 0; + int hashloc = hLocation.indexOf("hash="); + if (hashloc > 0) { + String hash = hLocation.substring(hashloc + 5); + if (hash.indexOf('&') > 0) { + hash = hash.substring(0, hash.indexOf('&')); } - if (hash != null) { - UID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); + if (hash.length() == 16) { + VUID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); } } // URL - String loc[] = hLocation.split("/"); + int hLocationQM = hLocation.indexOf('?'); + if (hLocationQM > 0) { + hLocation = hLocation.substring(0, hLocationQM); + } + int MID = 0; - if (hLocation.equals("/my") && UID > 0) { - Main.sockMessages.add(new SocketSubscribed(sock, UID, 0)); - } else if (hLocation.equals("/all")) { - Main.sockAll.add(new SocketSubscribed(sock, UID, 0)); - } else if ((loc.length == 2 || loc.length == 3) && loc[1].equals("replies")) { - if (loc.length == 2) { - Main.sockReplies.add(new SocketSubscribed(sock, UID, 0)); - } else { - try { - MID = Integer.parseInt(loc[2]); - } catch (Exception e) { - } - if (MID > 0) { - Main.sockReplies.add(new SocketSubscribed(sock, UID, MID)); + int responseCode = 404; + SocketSubscribed sockSubscr = null; + if (hLocation.equals("/") && VUID > 0) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + responseCode = 101; + } else if (hLocation.equals("/_all")) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.allMessages = true; + responseCode = 101; + } else if (hLocation.equals("/_replies")) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.allReplies = true; + responseCode = 101; + } else if (hLocation.matches("^/\\d+$")) { + try { + MID = Integer.parseInt(hLocation.substring(1)); + } catch (Exception e) { + } + if (MID > 0) { + if (MessagesQueries.canViewThread(sql, MID, VUID)) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.MID = MID; + responseCode = 101; } else { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid MID"); + responseCode = 403; } } - } else { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid location"); - } - - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (Hash=" + hash + "; UID = " + UID + "; MID = " + MID + ")"); - - Long lSecNum1 = calcSecKeyNum(hSecWebSocketKey1); - Long lSecNum2 = calcSecKeyNum(hSecWebSocketKey2); - - BigInteger sec1 = new BigInteger(lSecNum1.toString()); - BigInteger sec2 = new BigInteger(lSecNum2.toString()); - - // concatenate 3 parts secNum1 + secNum2 + secKey (16 Bytes) - byte[] l128Bit = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - byte[] lTmp; - - lTmp = sec1.toByteArray(); - int lIdx = lTmp.length; - int lCnt = 0; - while (lIdx > 0 && lCnt < 4) { - lIdx--; - lCnt++; - l128Bit[4 - lCnt] = lTmp[lIdx]; + } else if (hLocation.matches("^/[a-zA-Z0-9\\-]{2,16}/?$")) { + String uname; + if (hLocation.endsWith("/")) { + uname = hLocation.substring(1, hLocation.length() - 2); + } else { + uname = hLocation.substring(1); + } + + int UID = UserQueries.getUIDbyName(sql, uname); + if (UID > 0) { + // check access + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.UID = UID; + responseCode = 101; + } } - - lTmp = sec2.toByteArray(); - lIdx = lTmp.length; - lCnt = 0; - while (lIdx > 0 && lCnt < 4) { - lIdx--; - lCnt++; - l128Bit[8 - lCnt] = lTmp[lIdx]; + if (sockSubscr != null) { + synchronized (Main.clients) { + Main.clients.add(sockSubscr); + } } - buf.rewind(); - for (int i = 0; i < 8; i++) { - l128Bit[8 + i] = buf.get(buf.limit() - 8 + i); + // Response + String outstr; + if (responseCode == 101) { + outstr = "HTTP/1.1 101 Switching Protocols\r\n" + + "Upgrade: websocket\r\n" + + "Connection: Upgrade\r\n" + + "Sec-WebSocket-Accept: " + calcHeaderAccept(hSecWebSocketKey) + "\r\n" + + "\r\n"; + } else if (responseCode == 403) { + outstr = "HTTP/1.1 403 Forbidden\r\n\r\n"; + } else { + outstr = "HTTP/1.1 404 Not Found\r\n\r\n"; } - - String outstr = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" - + "Upgrade: WebSocket\r\n" - + "Connection: Upgrade\r\n" - + "Sec-WebSocket-Origin: " + hOrigin + "\r\n" - + "Sec-WebSocket-Location: ws://" + hHost + hLocation + "\r\n" - + "Sec-WebSocket-Protocol: sample\r\n" - + "\r\n"; - ByteBuffer out = ByteBuffer.allocate(4096); + ByteBuffer out = ByteBuffer.allocate(1024); out.put(Charset.forName("ISO-8859-1").encode(outstr)); - out.put(MessageDigest.getInstance("MD5").digest(l128Bit)); out.flip(); - sock.write(out); - } - - private static long calcSecKeyNum(String aKey) { - StringBuilder lSB = new StringBuilder(); - int lSpaces = 0; - for (int i = 0; i < aKey.length(); i++) { - char lC = aKey.charAt(i); - if (lC == ' ') { - lSpaces++; - } else if (lC >= '0' && lC <= '9') { - lSB.append(lC); - } + + if (responseCode == 101) { + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); + } else { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " " + responseCode); } - long lRes = -1; - if (lSpaces > 0) { - try { - lRes = Long.parseLong(lSB.toString()) / lSpaces; - } catch (NumberFormatException ex) { - // use default result - } + } + + private String calcHeaderAccept(String key) { + String base = key + WEBSOCKET_GUID; + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + return Base64.encode(md.digest(base.getBytes())); + } catch (NoSuchAlgorithmException e) { + System.err.println("calcHeaderAccept: " + e); } - return lRes; + return ""; } - - public void wsTextFrame(SocketChannel sock, CharSequence csbuf) { - String buf = csbuf.toString(); - if (buf.equals(" ")) { - ByteBuffer out = ByteBuffer.allocate(4); - out.put((byte) 0x00); - out.put((byte) 0x20); - out.put((byte) 0xFF); - out.flip(); - out.rewind(); - try { - sock.write(out); - } catch (IOException e) { + + public void wsPing(SocketChannel sock) throws Exception { + ByteBuffer out = ByteBuffer.allocate(2); + out.put((byte) 0x8A); // PONG FRAME + out.put((byte) 0x00); // 1 byte long + out.flip(); + out.rewind(); + sock.write(out); + } + + public void wsTextFrame(SocketChannel sock, ByteBuffer buf) throws Exception { + /* + ByteBuffer out = ByteBuffer.allocate(3); + out.put((byte) 0x81); // TEXT FRAME + out.put((byte) 0x01); // 1 byte long + out.put((byte) 0x20); // ' ' + out.flip(); + out.rewind(); + sock.write(out); + */ + } + + public void updateSocketTS(SocketChannel sock) { + synchronized (Main.clients) { + Iterator i = Main.clients.iterator(); + while (i.hasNext()) { + SocketSubscribed s = i.next(); + if (s.sock == sock) { + s.tsLastData = System.currentTimeMillis(); + break; + } } - } else { - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " DATA '" + buf + "'"); } } } diff --git a/src/com/juick/jabber/ws/WSKeepAlive.java b/src/com/juick/jabber/ws/WSKeepAlive.java index ba6376a4..2e36b100 100644 --- a/src/com/juick/jabber/ws/WSKeepAlive.java +++ b/src/com/juick/jabber/ws/WSKeepAlive.java @@ -1,6 +1,9 @@ package com.juick.jabber.ws; +import java.io.PrintWriter; +import java.nio.ByteBuffer; import java.sql.Connection; +import java.util.Iterator; /** * @@ -9,12 +12,115 @@ import java.sql.Connection; public class WSKeepAlive implements Runnable { Connection sql; + ByteBuffer pingBytes; + ByteBuffer closeBytes; public WSKeepAlive(Connection sql) { this.sql = sql; + + //pingBytes = ByteBuffer.allocate(2); + //pingBytes.put((byte) 0x8A); // PONG FRAME + //pingBytes.put((byte) 0x00); // 0 byte long + pingBytes = ByteBuffer.allocate(3); + pingBytes.put((byte) 0x81); // TEXT FRAME + pingBytes.put((byte) 0x01); // 1 byte long + pingBytes.put((byte) 0x20); // ' ' + pingBytes.flip(); + + closeBytes = ByteBuffer.allocate(2); + closeBytes.put((byte) 0x88); // CLOSE FRAME + closeBytes.put((byte) 0x00); // 0 byte long + closeBytes.flip(); } @Override public void run() { + while (true) { + PrintWriter statsFile = null; + + if (Main.STATSFILE != null) { + try { + statsFile = new PrintWriter(Main.STATSFILE, "UTF-8"); + } catch (Exception e) { + statsFile = null; + System.err.println("WSKeepAlive statsFile: " + e); + } + } + + long now = System.currentTimeMillis(); + + synchronized (Main.clients) { + if (statsFile != null) { + statsFile.write("

Connections (" + Main.clients.size() + ")

"); + } + + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + int inactive = (int) ((double) (now - s.tsLastData) / 1000.0); + + if (statsFile != null) { + try { + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + } catch (Exception e) { + System.err.println("WSKeepAlive statsFile print: " + e); + } + } + + if (inactive > 180) { + closeBytes.rewind(); + try { + s.sock.write(closeBytes); + } catch (Exception e) { + } finally { + try { + s.sock.socket().close(); + } catch (Exception ex) { + } + try { + s.sock.close(); + } catch (Exception ex) { + } + i.remove(); + } + } else if (inactive > 60) { + pingBytes.rewind(); + try { + s.sock.write(pingBytes); + } catch (Exception e) { + System.err.println("WSKeepAlive ping: " + e); + try { + s.sock.socket().close(); + } catch (Exception ex) { + } + try { + s.sock.close(); + } catch (Exception ex) { + } + i.remove(); + } + } + } + } + + if (Main.STATSFILE != null) { + try { + statsFile.write("
IPinactiveVUIDUIDMIDallMallR
" + (s.clientName != null ? s.clientName : "?") + "" + inactive + "" + (s.VUID > 0 ? s.VUID : "") + "" + (s.UID > 0 ? s.UID : "") + "" + (s.MID > 0 ? s.MID : "") + "" + (s.allMessages ? "+" : "") + "" + (s.allReplies ? "+" : "") + "
"); + statsFile.close(); + } catch (Exception e) { + System.err.println("WSKeepAlive statsFile close: " + e); + } + } + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + } } } diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java index 91a8387b..24329992 100644 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -1,5 +1,7 @@ package com.juick.jabber.ws; +import com.juick.server.MessagesQueries; +import com.juick.server.Utils; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; @@ -13,6 +15,8 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; +import java.util.Iterator; /** * @@ -33,7 +37,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. public void run() { try { Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + xmpp = new StreamComponent(new JID("", "ws2.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); xmpp.addChildParser(new JuickMessage()); xmpp.addListener((Stream.StreamListener) this); xmpp.addListener((Message.MessageListener) this); @@ -67,35 +71,24 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } private void onJuickMessagePost(com.juick.Message jmsg) { - String json = "{" - + "\"mid\":" + jmsg.MID + "," - + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.TimestampString + "\"," - + "\"body\":\"" + encloseJSON(jmsg.Text) + "\""; - if (jmsg.Tags.size() > 0) { - json += ",\"tags\":["; - for (int i = 0; i < jmsg.Tags.size(); i++) { - if (i > 0) { - json += ","; - } - json += "\"" + encloseJSON((String) jmsg.Tags.get(i)) + "\""; - } - json += "]"; + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); + ByteBuffer bbMsg = ByteBuffer.allocate(10240); + bbMsg.put((byte) 0x81); + if (jsonbytes.limit() <= 125) { + bbMsg.put((byte) jsonbytes.limit()); + } else { + bbMsg.put((byte) 126); + bbMsg.putShort((short) jsonbytes.limit()); } - json += "}"; - - ByteBuffer out = ByteBuffer.allocate(10240); - out.put((byte) 0x00); - out.put(Charset.forName("UTF-8").encode(json)); - out.put((byte) 0xFF); - out.flip(); - + bbMsg.put(jsonbytes); + bbMsg.flip(); + ArrayList uids = new ArrayList(); String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; if (jmsg.Privacy < 0) { query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; } - Statement stmt = null; ResultSet rs = null; try { @@ -103,115 +96,73 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. rs = stmt.executeQuery(query); rs.beforeFirst(); while (rs.next()) { - int UID = rs.getInt(1); - - for (int i = Main.sockMessages.size() - 1; i >= 0; i--) { - SocketSubscribed ss = Main.sockMessages.get(i); - if (ss.UID == UID) { - try { - out.rewind(); - ss.sock.write(out); - } catch (IOException e) { - Main.sockMessages.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - - if (jmsg.Privacy <= 0) { - for (int i = Main.sockAll.size() - 1; i >= 0; i--) { - SocketSubscribed ss = Main.sockAll.get(i); - if (ss.UID == UID) { - try { - out.rewind(); - ss.sock.write(out); - } catch (IOException e) { - Main.sockAll.remove(i); - try { - ss.sock.close(); - } catch (IOException ex) { - } - } - } - } - } - + uids.add(rs.getInt(1)); } } catch (SQLException e) { - System.err.println(e); + System.err.println("onJuickMessagePost: " + e); } finally { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - } - } - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException e) { - } - } + Utils.finishSQL(rs, stmt); } - // Send to all - if (jmsg.Privacy > 0) { - for (int i = Main.sockAll.size() - 1; i >= 0; i--) { - SocketSubscribed ss = Main.sockAll.get(i); - try { - out.rewind(); - ss.sock.write(out); - System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); - } catch (IOException e) { - Main.sockAll.remove(i); + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID)) { + bbMsg.rewind(); try { - ss.sock.close(); - } catch (IOException ex) { + s.sock.write(bbMsg); + } catch (Exception e) { + try { + s.sock.socket().close(); + } catch (Exception ex) { + } + try { + s.sock.close(); + } catch (Exception ex) { + } + i.remove(); } } } } - } private void onJuickMessageReply(com.juick.Message jmsg) { - String json = "{" - + "\"mid\":" + jmsg.MID + "," - + "\"rid\":" + jmsg.RID + "," - + "\"replyto\":" + jmsg.ReplyTo + "," - + "\"user\":{" + "\"uid\":" + jmsg.User.UID + "," + "\"uname\":\"" + encloseJSON(jmsg.User.UName) + "\"}," - + "\"timestamp\":\"" + jmsg.TimestampString + "\"," - + "\"body\":\"" + encloseJSON(jmsg.Text) + "\"" - + "}"; + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); + ByteBuffer bbMsg = ByteBuffer.allocate(10240); + bbMsg.put((byte) 0x81); + if (jsonbytes.limit() <= 125) { + bbMsg.put((byte) jsonbytes.limit()); + } else { + bbMsg.put((byte) 126); + bbMsg.putShort((short) jsonbytes.limit()); + } + bbMsg.put(jsonbytes); + bbMsg.flip(); - ByteBuffer out = ByteBuffer.allocate(10240); - out.put((byte) 0x00); - out.put(Charset.forName("UTF-8").encode(json)); - out.put((byte) 0xFF); - out.flip(); + int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.MID); - for (int i = Main.sockReplies.size() - 1; i >= 0; i--) { - SocketSubscribed ss = Main.sockReplies.get(i); - if (ss.MID == 0 || ss.MID == jmsg.MID) { - try { - out.rewind(); - ss.sock.write(out); - System.err.println(" --->" + ss.sock.socket().getRemoteSocketAddress()); - } catch (IOException e) { - Main.sockReplies.remove(i); + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.MID) { + bbMsg.rewind(); try { - ss.sock.close(); - } catch (IOException ex) { + s.sock.write(bbMsg); + } catch (Exception e) { + try { + s.sock.socket().close(); + } catch (Exception ex) { + } + try { + s.sock.close(); + } catch (Exception ex) { + } + i.remove(); } } } } } - - public static String encloseJSON(String str) { - return str.replace("\"", """).replace("\\", "\\\\").replace("\n", "\\n"); - } } -- cgit v1.2.3 From 0f51682e893ee75f387617daa1dccd51e2ae95c5 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Fri, 30 Aug 2013 13:46:41 +0700 Subject: ws2 -> ws --- src/com/juick/jabber/ws/XMPPConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java index 24329992..0eef26bf 100644 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -37,7 +37,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. public void run() { try { Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "ws2.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); xmpp.addChildParser(new JuickMessage()); xmpp.addListener((Stream.StreamListener) this); xmpp.addListener((Message.MessageListener) this); -- cgit v1.2.3 From 265e629e5bc5ad8f9d2a8e94dce864ebe995bcef Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sat, 7 Sep 2013 19:34:34 +0700 Subject: onJuickPM --- src/com/juick/jabber/ws/XMPPConnection.java | 48 ++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java index 0eef26bf..a8af8c44 100644 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -62,7 +62,16 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (jmsg != null) { System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); - if (jmsg.RID == 0) { + if (jmsg.MID == 0) { + int uid_to = 0; + try { + uid_to = Integer.parseInt(msg.to.Username); + } catch (Exception e) { + } + if (uid_to > 0) { + onJuickPM(uid_to, jmsg); + } + } else if (jmsg.RID == 0) { onJuickMessagePost(jmsg); } else { onJuickMessageReply(jmsg); @@ -70,6 +79,43 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } } + private void onJuickPM(int uid_to, com.juick.Message jmsg) { + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); + ByteBuffer bbMsg = ByteBuffer.allocate(10240); + bbMsg.put((byte) 0x81); + if (jsonbytes.limit() <= 125) { + bbMsg.put((byte) jsonbytes.limit()); + } else { + bbMsg.put((byte) 126); + bbMsg.putShort((short) jsonbytes.limit()); + } + bbMsg.put(jsonbytes); + bbMsg.flip(); + + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if (s.VUID == uid_to) { + bbMsg.rewind(); + try { + s.sock.write(bbMsg); + } catch (Exception e) { + try { + s.sock.socket().close(); + } catch (Exception ex) { + } + try { + s.sock.close(); + } catch (Exception ex) { + } + i.remove(); + } + } + } + } + } + private void onJuickMessagePost(com.juick.Message jmsg) { String json = com.juick.json.Message.toJSON(jmsg).toString(); ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); -- cgit v1.2.3 From b86eb130ef8a1b06a34454a47738e0a8ba9b50dd Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Sat, 7 Sep 2013 19:54:51 +0700 Subject: XMPPConnection.buildTextFrame; SocketSubscribed.sendByteBuffer, close --- src/com/juick/jabber/ws/SocketSubscribed.java | 24 +++++++ src/com/juick/jabber/ws/WSKeepAlive.java | 32 ++-------- src/com/juick/jabber/ws/XMPPConnection.java | 90 +++++++-------------------- 3 files changed, 49 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/com/juick/jabber/ws/SocketSubscribed.java b/src/com/juick/jabber/ws/SocketSubscribed.java index f865ce6a..6144380c 100644 --- a/src/com/juick/jabber/ws/SocketSubscribed.java +++ b/src/com/juick/jabber/ws/SocketSubscribed.java @@ -1,5 +1,6 @@ package com.juick.jabber.ws; +import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** @@ -24,4 +25,27 @@ public class SocketSubscribed { this.VUID = VUID; tsConnected = tsLastData = System.currentTimeMillis(); } + + public boolean sendByteBuffer(ByteBuffer bb) { + boolean ret = false; + bb.rewind(); + try { + sock.write(bb); + ret = true; + } catch (Exception e) { + close(); + } + return ret; + } + + public void close() { + try { + sock.socket().close(); + } catch (Exception e) { + } + try { + sock.close(); + } catch (Exception e) { + } + } } diff --git a/src/com/juick/jabber/ws/WSKeepAlive.java b/src/com/juick/jabber/ws/WSKeepAlive.java index 2e36b100..2deef594 100644 --- a/src/com/juick/jabber/ws/WSKeepAlive.java +++ b/src/com/juick/jabber/ws/WSKeepAlive.java @@ -73,35 +73,11 @@ public class WSKeepAlive implements Runnable { } if (inactive > 180) { - closeBytes.rewind(); - try { - s.sock.write(closeBytes); - } catch (Exception e) { - } finally { - try { - s.sock.socket().close(); - } catch (Exception ex) { - } - try { - s.sock.close(); - } catch (Exception ex) { - } - i.remove(); - } + s.sendByteBuffer(closeBytes); + s.close(); + i.remove(); } else if (inactive > 60) { - pingBytes.rewind(); - try { - s.sock.write(pingBytes); - } catch (Exception e) { - System.err.println("WSKeepAlive ping: " + e); - try { - s.sock.socket().close(); - } catch (Exception ex) { - } - try { - s.sock.close(); - } catch (Exception ex) { - } + if (!s.sendByteBuffer(pingBytes)) { i.remove(); } } diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java index a8af8c44..b85c8bee 100644 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -81,34 +81,13 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickPM(int uid_to, com.juick.Message jmsg) { String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); - ByteBuffer bbMsg = ByteBuffer.allocate(10240); - bbMsg.put((byte) 0x81); - if (jsonbytes.limit() <= 125) { - bbMsg.put((byte) jsonbytes.limit()); - } else { - bbMsg.put((byte) 126); - bbMsg.putShort((short) jsonbytes.limit()); - } - bbMsg.put(jsonbytes); - bbMsg.flip(); + ByteBuffer bbMsg = buildTextFrame(json); synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); if (s.VUID == uid_to) { - bbMsg.rewind(); - try { - s.sock.write(bbMsg); - } catch (Exception e) { - try { - s.sock.socket().close(); - } catch (Exception ex) { - } - try { - s.sock.close(); - } catch (Exception ex) { - } + if (!s.sendByteBuffer(bbMsg)) { i.remove(); } } @@ -118,17 +97,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickMessagePost(com.juick.Message jmsg) { String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); - ByteBuffer bbMsg = ByteBuffer.allocate(10240); - bbMsg.put((byte) 0x81); - if (jsonbytes.limit() <= 125) { - bbMsg.put((byte) jsonbytes.limit()); - } else { - bbMsg.put((byte) 126); - bbMsg.putShort((short) jsonbytes.limit()); - } - bbMsg.put(jsonbytes); - bbMsg.flip(); + ByteBuffer bbMsg = buildTextFrame(json); ArrayList uids = new ArrayList(); String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; @@ -154,18 +123,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); if ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID)) { - bbMsg.rewind(); - try { - s.sock.write(bbMsg); - } catch (Exception e) { - try { - s.sock.socket().close(); - } catch (Exception ex) { - } - try { - s.sock.close(); - } catch (Exception ex) { - } + if (!s.sendByteBuffer(bbMsg)) { i.remove(); } } @@ -175,17 +133,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickMessageReply(com.juick.Message jmsg) { String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); - ByteBuffer bbMsg = ByteBuffer.allocate(10240); - bbMsg.put((byte) 0x81); - if (jsonbytes.limit() <= 125) { - bbMsg.put((byte) jsonbytes.limit()); - } else { - bbMsg.put((byte) 126); - bbMsg.putShort((short) jsonbytes.limit()); - } - bbMsg.put(jsonbytes); - bbMsg.flip(); + ByteBuffer bbMsg = buildTextFrame(json); int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.MID); @@ -193,22 +141,26 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.MID) { - bbMsg.rewind(); - try { - s.sock.write(bbMsg); - } catch (Exception e) { - try { - s.sock.socket().close(); - } catch (Exception ex) { - } - try { - s.sock.close(); - } catch (Exception ex) { - } + if (!s.sendByteBuffer(bbMsg)) { i.remove(); } } } } } + + private ByteBuffer buildTextFrame(String json) { + ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); + ByteBuffer bbMsg = ByteBuffer.allocate(jsonbytes.limit() + 8); + bbMsg.put((byte) 0x81); + if (jsonbytes.limit() <= 125) { + bbMsg.put((byte) jsonbytes.limit()); + } else { + bbMsg.put((byte) 126); + bbMsg.putShort((short) jsonbytes.limit()); + } + bbMsg.put(jsonbytes); + bbMsg.flip(); + return bbMsg; + } } -- cgit v1.2.3 From 694bc7b9dd4f3e7d0a918ddb2327dbb676fc6465 Mon Sep 17 00:00:00 2001 From: Ugnich Anton Date: Tue, 5 Aug 2014 04:51:57 +0700 Subject: bugfix (messages sent to subscribers) --- src/com/juick/jabber/ws/XMPPConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java index b85c8bee..39815da5 100644 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ b/src/com/juick/jabber/ws/XMPPConnection.java @@ -86,7 +86,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); - if (s.VUID == uid_to) { + if (s.VUID == uid_to && s.MID == 0 && s.allMessages == false && s.allReplies == false) { if (!s.sendByteBuffer(bbMsg)) { i.remove(); } @@ -122,7 +122,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); - if ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID)) { + if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID))) { if (!s.sendByteBuffer(bbMsg)) { i.remove(); } -- cgit v1.2.3 From e746ca6970bea80ec9b5ae79dda8f6332254e301 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Sat, 16 Jan 2016 16:43:43 +0300 Subject: moved to Gradle --- .gitignore | 2 +- .gitmodules | 12 + build.gradle | 70 ++ build.xml | 74 -- deps/com.juick | 1 + deps/com.juick.json | 1 + deps/com.juick.server | 1 + deps/com.juick.xmpp | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53637 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 +++ gradlew.bat | 90 ++ lib/org.json.jar | Bin 80177 -> 0 bytes lib/xpp3_min-1.1.4.jar | Bin 24955 -> 0 bytes manifest.mf | 3 - nbproject/build-impl.xml | 1089 -------------------- nbproject/genfiles.properties | 8 - nbproject/project.properties | 88 -- nbproject/project.xml | 50 - settings.gradle | 1 + src/com/juick/jabber/ws/Main.java | 68 -- src/com/juick/jabber/ws/SocketSubscribed.java | 51 - src/com/juick/jabber/ws/WSConnections.java | 44 - src/com/juick/jabber/ws/WSData.java | 276 ----- src/com/juick/jabber/ws/WSKeepAlive.java | 102 -- src/com/juick/jabber/ws/XMPPConnection.java | 166 --- src/main/java/com/juick/jabber/ws/Main.java | 68 ++ .../java/com/juick/jabber/ws/SocketSubscribed.java | 51 + .../java/com/juick/jabber/ws/WSConnections.java | 44 + src/main/java/com/juick/jabber/ws/WSData.java | 276 +++++ src/main/java/com/juick/jabber/ws/WSKeepAlive.java | 102 ++ .../java/com/juick/jabber/ws/XMPPConnection.java | 166 +++ 32 files changed, 1051 insertions(+), 2020 deletions(-) create mode 100644 .gitmodules create mode 100644 build.gradle delete mode 100644 build.xml create mode 160000 deps/com.juick create mode 160000 deps/com.juick.json create mode 160000 deps/com.juick.server create mode 160000 deps/com.juick.xmpp create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat delete mode 100644 lib/org.json.jar delete mode 100644 lib/xpp3_min-1.1.4.jar delete mode 100644 manifest.mf delete mode 100644 nbproject/build-impl.xml delete mode 100644 nbproject/genfiles.properties delete mode 100644 nbproject/project.properties delete mode 100644 nbproject/project.xml create mode 100644 settings.gradle delete mode 100644 src/com/juick/jabber/ws/Main.java delete mode 100644 src/com/juick/jabber/ws/SocketSubscribed.java delete mode 100644 src/com/juick/jabber/ws/WSConnections.java delete mode 100644 src/com/juick/jabber/ws/WSData.java delete mode 100644 src/com/juick/jabber/ws/WSKeepAlive.java delete mode 100644 src/com/juick/jabber/ws/XMPPConnection.java create mode 100644 src/main/java/com/juick/jabber/ws/Main.java create mode 100644 src/main/java/com/juick/jabber/ws/SocketSubscribed.java create mode 100644 src/main/java/com/juick/jabber/ws/WSConnections.java create mode 100644 src/main/java/com/juick/jabber/ws/WSData.java create mode 100644 src/main/java/com/juick/jabber/ws/WSKeepAlive.java create mode 100644 src/main/java/com/juick/jabber/ws/XMPPConnection.java (limited to 'src') diff --git a/.gitignore b/.gitignore index 315ea15c..e3baf212 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /dist/ /build/ -/nbproject/private/ \ No newline at end of file +.gradle/* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e94e84e4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "deps/com.juick.server"] + path = deps/com.juick.server + url = ssh://den.jabber.ru:2205/var/lib/git/com.juick.server.git +[submodule "deps/com.juick.xmpp"] + path = deps/com.juick.xmpp + url = ssh://den.jabber.ru:2205/var/lib/git/com.juick.xmpp.git +[submodule "deps/com.juick"] + path = deps/com.juick + url = ssh://den.jabber.ru:2205/var/lib/git/com.juick.git +[submodule "deps/com.juick.json"] + path = deps/com.juick.json + url = ssh://den.jabber.ru:2205/var/lib/git/com.juick.json.git diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..0091459a --- /dev/null +++ b/build.gradle @@ -0,0 +1,70 @@ +subprojects { + apply plugin: 'java' + repositories { + mavenCentral() + } +} + +buildscript { + repositories { + mavenCentral() + jcenter() + } + dependencies { + classpath 'com.bmuschko:gradle-tomcat-plugin:2.2.+' + classpath 'com.tkruse.gradle:gradle-groovysh-plugin:1.0.7' + } +} + +apply plugin: 'java' +apply plugin: 'war' +apply plugin: 'com.bmuschko.tomcat' +apply plugin: 'com.github.tkruse.groovysh' + +repositories { + mavenCentral() +} + +def core = project(':deps:com.juick') +def server = project(':deps:com.juick.server') +def xmpp = project(':deps:com.juick.xmpp') +def json = project(':deps:com.juick.json') +project(':deps:com.juick.server') { + dependencies { + compile core + } +} + +project(':deps:com.juick.xmpp') { + dependencies { + compile core + } +} + +project(':deps:com.juick.json') { + dependencies { + compile core + } +} + +dependencies { + compile core + compile server + compile xmpp + compile json + compile "org.springframework:spring-jdbc:4.2.4.RELEASE" + providedCompile 'javax.servlet:javax.servlet-api:3.1.0' + def tomcatVersion = '7.0.+' + tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", + "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}", + "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}" + testCompile 'junit:junit:4.12' + runtime 'mysql:mysql-connector-java:5.1.37' +} + +compileJava.options.encoding = 'UTF-8' + +tomcat { + httpPort = 8080 + contextPath = '/' +} diff --git a/build.xml b/build.xml deleted file mode 100644 index c7a42b44..00000000 --- a/build.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project com.juick.jabber.ws. - - - diff --git a/deps/com.juick b/deps/com.juick new file mode 160000 index 00000000..1df19b47 --- /dev/null +++ b/deps/com.juick @@ -0,0 +1 @@ +Subproject commit 1df19b47f7f463e6cafa7dcf877cb42967fff166 diff --git a/deps/com.juick.json b/deps/com.juick.json new file mode 160000 index 00000000..ee0e13e7 --- /dev/null +++ b/deps/com.juick.json @@ -0,0 +1 @@ +Subproject commit ee0e13e7243abecf242236c224551774195524a3 diff --git a/deps/com.juick.server b/deps/com.juick.server new file mode 160000 index 00000000..96188f8d --- /dev/null +++ b/deps/com.juick.server @@ -0,0 +1 @@ +Subproject commit 96188f8d8a31df2474bf0eaf7a91773280ddccb6 diff --git a/deps/com.juick.xmpp b/deps/com.juick.xmpp new file mode 160000 index 00000000..1f08c012 --- /dev/null +++ b/deps/com.juick.xmpp @@ -0,0 +1 @@ +Subproject commit 1f08c01254fcbca79d1571ebb1ed2d68f86b8503 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..05ef575b Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..65a9525c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Oct 29 12:36:21 MSK 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..9d82f789 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/org.json.jar b/lib/org.json.jar deleted file mode 100644 index 911ca8f1..00000000 Binary files a/lib/org.json.jar and /dev/null differ diff --git a/lib/xpp3_min-1.1.4.jar b/lib/xpp3_min-1.1.4.jar deleted file mode 100644 index e02ea412..00000000 Binary files a/lib/xpp3_min-1.1.4.jar and /dev/null differ diff --git a/manifest.mf b/manifest.mf deleted file mode 100644 index 328e8e5b..00000000 --- a/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -X-COMMENT: Main-Class will be added automatically by build - diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml deleted file mode 100644 index 06162daf..00000000 --- a/nbproject/build-impl.xml +++ /dev/null @@ -1,1089 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - - - - - - java -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - - - - - - - - Must select one file in the IDE or set profile.class - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties deleted file mode 100644 index 8b83736c..00000000 --- a/nbproject/genfiles.properties +++ /dev/null @@ -1,8 +0,0 @@ -build.xml.data.CRC32=68cbdaea -build.xml.script.CRC32=2ec0c6a9 -build.xml.stylesheet.CRC32=28e38971@1.44.1.45 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=68cbdaea -nbproject/build-impl.xml.script.CRC32=e6ac4bc8 -nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45 diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index e6b5db4c..00000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,88 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -application.title=com.juick.jabber.ws -application.vendor=ugnich -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/com.juick.jabber.ws.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -file.reference.org.json.jar=lib/org.json.jar -file.reference.xpp3_min-1.1.4.jar=lib/xpp3_min-1.1.4.jar -includes=** -jar.compress=false -javac.classpath=\ - ${reference.com_juick_xmpp.jar}:\ - ${reference.com_juick_server.jar}:\ - ${reference.com_juick_json.jar}:\ - ${reference.com_juick.jar}:\ - ${libs.MySQLDriver.classpath}:\ - ${file.reference.org.json.jar}:\ - ${file.reference.xpp3_min-1.1.4.jar} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.6 -javac.target=1.6 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -main.class=com.juick.jabber.ws.Main -manifest.file=manifest.mf -meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=false -platform.active=default_platform -project.com_juick=../com.juick -project.com_juick_json=../com.juick.json -project.com_juick_server=../com.juick.server -project.com_juick_xmpp=../com.juick.xmpp -reference.com_juick.jar=${project.com_juick}/dist/com.juick.jar -reference.com_juick_json.jar=${project.com_juick_json}/dist/com.juick.json.jar -reference.com_juick_server.jar=${project.com_juick_server}/dist/com.juick.server.jar -reference.com_juick_xmpp.jar=${project.com_juick_xmpp}/dist/com.juick.xmpp.jar -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project -# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value -# or test-sys-prop.name=value to set system properties for unit tests): -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.dir=src diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 48bac001..00000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - com.juick.jabber.ws - - - - - - - - com_juick - jar - - jar - clean - jar - - - com_juick_json - jar - - jar - clean - jar - - - com_juick_server - jar - - jar - clean - jar - - - com_juick_xmpp - jar - - jar - clean - jar - - - - - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..56142dd6 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':deps:com.juick', ':deps:com.juick.json', ':deps:com.juick.server', ':deps:com.juick.xmpp' diff --git a/src/com/juick/jabber/ws/Main.java b/src/com/juick/jabber/ws/Main.java deleted file mode 100644 index a7aea543..00000000 --- a/src/com/juick/jabber/ws/Main.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.jabber.ws; - -import java.io.FileInputStream; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Properties; - -/** - * - * @author Ugnich Anton - */ -public class Main { - - Connection sql; - XMPPConnection xmpp; - public static String STATSFILE = null; - public final static ArrayList clients = new ArrayList(); - - public static void main(String[] args) { - new Main().start(); - } - - public void start() { - try { - Properties conf = new Properties(); - conf.load(new FileInputStream("/etc/juick/ws.conf")); - - STATSFILE = conf.getProperty("statsfile"); - - setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); - xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); - new Thread(xmpp).start(); - - //new Thread(new WSConnections()).start(); - new Thread(new WSData(sql)).start(); - new Thread(new WSKeepAlive(sql)).start(); - } catch (Exception e) { - System.err.println(e); - } - } - - public void setupSql(String username, String password) { - try { - sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + username + "&password=" + password); - } catch (SQLException e) { - System.err.println(e); - } - } -} \ No newline at end of file diff --git a/src/com/juick/jabber/ws/SocketSubscribed.java b/src/com/juick/jabber/ws/SocketSubscribed.java deleted file mode 100644 index 6144380c..00000000 --- a/src/com/juick/jabber/ws/SocketSubscribed.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.jabber.ws; - -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; - -/** - * - * @author ugnich - */ -public class SocketSubscribed { - - public SocketChannel sock = null; - public String clientName = null; - public int VUID = 0; - public int UID = 0; - public int MID = 0; - public boolean allMessages = false; - public boolean allReplies = false; - public long tsConnected; - public long tsLastData; - - public SocketSubscribed(SocketChannel sock, String clientName, int VUID) { - this.sock = sock; - this.clientName = clientName; - this.VUID = VUID; - tsConnected = tsLastData = System.currentTimeMillis(); - } - - public boolean sendByteBuffer(ByteBuffer bb) { - boolean ret = false; - bb.rewind(); - try { - sock.write(bb); - ret = true; - } catch (Exception e) { - close(); - } - return ret; - } - - public void close() { - try { - sock.socket().close(); - } catch (Exception e) { - } - try { - sock.close(); - } catch (Exception e) { - } - } -} diff --git a/src/com/juick/jabber/ws/WSConnections.java b/src/com/juick/jabber/ws/WSConnections.java deleted file mode 100644 index 15fbe4e8..00000000 --- a/src/com/juick/jabber/ws/WSConnections.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.juick.jabber.ws; - -import java.net.InetSocketAddress; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class WSConnections implements Runnable { - - Selector sel; - - @Override - public void run() { - try { - sel = Selector.open(); - ServerSocketChannel listensock = ServerSocketChannel.open(); - listensock.configureBlocking(false); - listensock.socket().bind(new InetSocketAddress(8081)); - listensock.register(sel, SelectionKey.OP_ACCEPT); - - while (true) { - sel.select(); - Iterator it = sel.selectedKeys().iterator(); - while (it.hasNext()) { - SelectionKey selKey = it.next(); - it.remove(); - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - sChannel.configureBlocking(false); - sChannel.register(sel, SelectionKey.OP_READ); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); - } - } - } catch (Exception e) { - System.err.println("WSConnections: " + e); - } - } -} diff --git a/src/com/juick/jabber/ws/WSData.java b/src/com/juick/jabber/ws/WSData.java deleted file mode 100644 index d77257e6..00000000 --- a/src/com/juick/jabber/ws/WSData.java +++ /dev/null @@ -1,276 +0,0 @@ -package com.juick.jabber.ws; - -import com.juick.server.MessagesQueries; -import com.juick.server.UserQueries; -import com.juick.xmpp.utils.Base64; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.sql.Connection; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class WSData implements Runnable { - - static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - Connection sql; - public Selector sel; - - public WSData(Connection sql) { - this.sql = sql; - } - - @Override - public void run() { - try { - sel = Selector.open(); - ServerSocketChannel listensock = ServerSocketChannel.open(); - listensock.configureBlocking(false); - listensock.socket().bind(new InetSocketAddress(8081)); - listensock.register(sel, SelectionKey.OP_ACCEPT); - - while (true) { - sel.select(); - Iterator it = sel.selectedKeys().iterator(); - while (it.hasNext()) { - SelectionKey selKey = it.next(); - it.remove(); - - if (selKey.isAcceptable()) { - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - sChannel.configureBlocking(false); - sChannel.register(sel, SelectionKey.OP_READ); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); - } else if (selKey.isReadable()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - ByteBuffer buf = ByteBuffer.allocate(10240); - try { - int readbytes = sChannel.read(buf); - if (readbytes > 0) { - buf.flip(); - - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - System.out.println("DATA: " + charbuf.toString()); - buf.rewind(); - - switch (buf.get(0)) { - case (byte) 0x89: // PING - updateSocketTS(sChannel); - wsPing(sChannel); - break; - case (byte) 0x8A: // PONG - updateSocketTS(sChannel); - break; - case (byte) 0x81: // TEXT FRAME - updateSocketTS(sChannel); - wsTextFrame(sChannel, buf); - break; - case (byte) 'G': // HTTP - updateSocketTS(sChannel); - wsHandshake(sChannel, buf); - break; - case (byte) 0x88: // CONNECTION CLOSE - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " CONNECTION CLOSE"); - } - } else if (readbytes < 0) { - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " END OF STREAM"); - } - } catch (IOException e) { - System.err.println("WSData: " + e); - sChannel.socket().close(); - sChannel.close(); - selKey.cancel(); - } - } - } - } - } catch (Exception e) { - System.err.println("WSData: " + e); - } - } - - public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { - String hOrigin = null; - String hHost = null; - String hLocation = null; - String hSecWebSocketKey = null; - String hSecWebSocketVersion = null; - String hXRealIP = null; - - buf.rewind(); - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - String headers[] = charbuf.toString().split("\r\n"); - for (int i = 0; i < headers.length; i++) { - String h[] = headers[i].split(" ", 2); - if (h.length == 2) { - if (h[0].equals("GET")) { - hLocation = headers[i].split(" ", 3)[1]; - } else if (h[0].equals("Origin:")) { - hOrigin = h[1]; - } else if (h[0].equals("Host:")) { - hHost = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key:")) { - hSecWebSocketKey = h[1]; - } else if (h[0].equals("Sec-WebSocket-Version:")) { - hSecWebSocketVersion = h[1]; - } else if (h[0].equals("X-Real-IP:")) { - hXRealIP = h[1]; - } - } - } - - if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey == null || hSecWebSocketVersion == null || !hSecWebSocketVersion.equals("13")) { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); - } - - // Auth - int VUID = 0; - int hashloc = hLocation.indexOf("hash="); - if (hashloc > 0) { - String hash = hLocation.substring(hashloc + 5); - if (hash.indexOf('&') > 0) { - hash = hash.substring(0, hash.indexOf('&')); - } - if (hash.length() == 16) { - VUID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); - } - } - - // URL - int hLocationQM = hLocation.indexOf('?'); - if (hLocationQM > 0) { - hLocation = hLocation.substring(0, hLocationQM); - } - - int MID = 0; - int responseCode = 404; - SocketSubscribed sockSubscr = null; - if (hLocation.equals("/") && VUID > 0) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - responseCode = 101; - } else if (hLocation.equals("/_all")) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.allMessages = true; - responseCode = 101; - } else if (hLocation.equals("/_replies")) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.allReplies = true; - responseCode = 101; - } else if (hLocation.matches("^/\\d+$")) { - try { - MID = Integer.parseInt(hLocation.substring(1)); - } catch (Exception e) { - } - if (MID > 0) { - if (MessagesQueries.canViewThread(sql, MID, VUID)) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.MID = MID; - responseCode = 101; - } else { - responseCode = 403; - } - } - } else if (hLocation.matches("^/[a-zA-Z0-9\\-]{2,16}/?$")) { - String uname; - if (hLocation.endsWith("/")) { - uname = hLocation.substring(1, hLocation.length() - 2); - } else { - uname = hLocation.substring(1); - } - - int UID = UserQueries.getUIDbyName(sql, uname); - if (UID > 0) { - // check access - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.UID = UID; - responseCode = 101; - } - } - if (sockSubscr != null) { - synchronized (Main.clients) { - Main.clients.add(sockSubscr); - } - } - - // Response - String outstr; - if (responseCode == 101) { - outstr = "HTTP/1.1 101 Switching Protocols\r\n" - + "Upgrade: websocket\r\n" - + "Connection: Upgrade\r\n" - + "Sec-WebSocket-Accept: " + calcHeaderAccept(hSecWebSocketKey) + "\r\n" - + "\r\n"; - } else if (responseCode == 403) { - outstr = "HTTP/1.1 403 Forbidden\r\n\r\n"; - } else { - outstr = "HTTP/1.1 404 Not Found\r\n\r\n"; - } - ByteBuffer out = ByteBuffer.allocate(1024); - out.put(Charset.forName("ISO-8859-1").encode(outstr)); - out.flip(); - sock.write(out); - - if (responseCode == 101) { - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); - } else { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " " + responseCode); - } - } - - private String calcHeaderAccept(String key) { - String base = key + WEBSOCKET_GUID; - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - return Base64.encode(md.digest(base.getBytes())); - } catch (NoSuchAlgorithmException e) { - System.err.println("calcHeaderAccept: " + e); - } - return ""; - } - - public void wsPing(SocketChannel sock) throws Exception { - ByteBuffer out = ByteBuffer.allocate(2); - out.put((byte) 0x8A); // PONG FRAME - out.put((byte) 0x00); // 1 byte long - out.flip(); - out.rewind(); - sock.write(out); - } - - public void wsTextFrame(SocketChannel sock, ByteBuffer buf) throws Exception { - /* - ByteBuffer out = ByteBuffer.allocate(3); - out.put((byte) 0x81); // TEXT FRAME - out.put((byte) 0x01); // 1 byte long - out.put((byte) 0x20); // ' ' - out.flip(); - out.rewind(); - sock.write(out); - */ - } - - public void updateSocketTS(SocketChannel sock) { - synchronized (Main.clients) { - Iterator i = Main.clients.iterator(); - while (i.hasNext()) { - SocketSubscribed s = i.next(); - if (s.sock == sock) { - s.tsLastData = System.currentTimeMillis(); - break; - } - } - } - } -} diff --git a/src/com/juick/jabber/ws/WSKeepAlive.java b/src/com/juick/jabber/ws/WSKeepAlive.java deleted file mode 100644 index 2deef594..00000000 --- a/src/com/juick/jabber/ws/WSKeepAlive.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.juick.jabber.ws; - -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.sql.Connection; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class WSKeepAlive implements Runnable { - - Connection sql; - ByteBuffer pingBytes; - ByteBuffer closeBytes; - - public WSKeepAlive(Connection sql) { - this.sql = sql; - - //pingBytes = ByteBuffer.allocate(2); - //pingBytes.put((byte) 0x8A); // PONG FRAME - //pingBytes.put((byte) 0x00); // 0 byte long - pingBytes = ByteBuffer.allocate(3); - pingBytes.put((byte) 0x81); // TEXT FRAME - pingBytes.put((byte) 0x01); // 1 byte long - pingBytes.put((byte) 0x20); // ' ' - pingBytes.flip(); - - closeBytes = ByteBuffer.allocate(2); - closeBytes.put((byte) 0x88); // CLOSE FRAME - closeBytes.put((byte) 0x00); // 0 byte long - closeBytes.flip(); - } - - @Override - public void run() { - while (true) { - PrintWriter statsFile = null; - - if (Main.STATSFILE != null) { - try { - statsFile = new PrintWriter(Main.STATSFILE, "UTF-8"); - } catch (Exception e) { - statsFile = null; - System.err.println("WSKeepAlive statsFile: " + e); - } - } - - long now = System.currentTimeMillis(); - - synchronized (Main.clients) { - if (statsFile != null) { - statsFile.write("

Connections (" + Main.clients.size() + ")

"); - } - - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - int inactive = (int) ((double) (now - s.tsLastData) / 1000.0); - - if (statsFile != null) { - try { - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - } catch (Exception e) { - System.err.println("WSKeepAlive statsFile print: " + e); - } - } - - if (inactive > 180) { - s.sendByteBuffer(closeBytes); - s.close(); - i.remove(); - } else if (inactive > 60) { - if (!s.sendByteBuffer(pingBytes)) { - i.remove(); - } - } - } - } - - if (Main.STATSFILE != null) { - try { - statsFile.write("
IPinactiveVUIDUIDMIDallMallR
" + (s.clientName != null ? s.clientName : "?") + "" + inactive + "" + (s.VUID > 0 ? s.VUID : "") + "" + (s.UID > 0 ? s.UID : "") + "" + (s.MID > 0 ? s.MID : "") + "" + (s.allMessages ? "+" : "") + "" + (s.allReplies ? "+" : "") + "
"); - statsFile.close(); - } catch (Exception e) { - System.err.println("WSKeepAlive statsFile close: " + e); - } - } - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - } - } - } -} diff --git a/src/com/juick/jabber/ws/XMPPConnection.java b/src/com/juick/jabber/ws/XMPPConnection.java deleted file mode 100644 index 39815da5..00000000 --- a/src/com/juick/jabber/ws/XMPPConnection.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.juick.jabber.ws; - -import com.juick.server.MessagesQueries; -import com.juick.server.Utils; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import com.juick.xmpp.extensions.JuickMessage; -import java.io.IOException; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { - - Connection sql; - Stream xmpp; - String xmppPassword; - - public XMPPConnection(Connection sql, String password) { - this.sql = sql; - xmppPassword = password; - } - - @Override - public void run() { - try { - Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); - xmpp.addChildParser(new JuickMessage()); - xmpp.addListener((Stream.StreamListener) this); - xmpp.addListener((Message.MessageListener) this); - xmpp.startParsing(); - } catch (IOException e) { - System.err.println("XMPPConnection: " + e); - } - } - - @Override - public void onStreamReady() { - System.err.println("Stream ready"); - } - - @Override - public void onStreamFail(String msg) { - System.err.println("Stream failed: " + msg); - } - - @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); - if (jmsg != null) { - System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); - if (jmsg.MID == 0) { - int uid_to = 0; - try { - uid_to = Integer.parseInt(msg.to.Username); - } catch (Exception e) { - } - if (uid_to > 0) { - onJuickPM(uid_to, jmsg); - } - } else if (jmsg.RID == 0) { - onJuickMessagePost(jmsg); - } else { - onJuickMessageReply(jmsg); - } - } - } - - private void onJuickPM(int uid_to, com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if (s.VUID == uid_to && s.MID == 0 && s.allMessages == false && s.allReplies == false) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private void onJuickMessagePost(com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - ArrayList uids = new ArrayList(); - String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; - if (jmsg.Privacy < 0) { - query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; - } - Statement stmt = null; - ResultSet rs = null; - try { - stmt = sql.createStatement(); - rs = stmt.executeQuery(query); - rs.beforeFirst(); - while (rs.next()) { - uids.add(rs.getInt(1)); - } - } catch (SQLException e) { - System.err.println("onJuickMessagePost: " + e); - } finally { - Utils.finishSQL(rs, stmt); - } - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID))) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private void onJuickMessageReply(com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.MID); - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.MID) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private ByteBuffer buildTextFrame(String json) { - ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); - ByteBuffer bbMsg = ByteBuffer.allocate(jsonbytes.limit() + 8); - bbMsg.put((byte) 0x81); - if (jsonbytes.limit() <= 125) { - bbMsg.put((byte) jsonbytes.limit()); - } else { - bbMsg.put((byte) 126); - bbMsg.putShort((short) jsonbytes.limit()); - } - bbMsg.put(jsonbytes); - bbMsg.flip(); - return bbMsg; - } -} diff --git a/src/main/java/com/juick/jabber/ws/Main.java b/src/main/java/com/juick/jabber/ws/Main.java new file mode 100644 index 00000000..a7aea543 --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/Main.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package com.juick.jabber.ws; + +import java.io.FileInputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Properties; + +/** + * + * @author Ugnich Anton + */ +public class Main { + + Connection sql; + XMPPConnection xmpp; + public static String STATSFILE = null; + public final static ArrayList clients = new ArrayList(); + + public static void main(String[] args) { + new Main().start(); + } + + public void start() { + try { + Properties conf = new Properties(); + conf.load(new FileInputStream("/etc/juick/ws.conf")); + + STATSFILE = conf.getProperty("statsfile"); + + setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); + xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); + new Thread(xmpp).start(); + + //new Thread(new WSConnections()).start(); + new Thread(new WSData(sql)).start(); + new Thread(new WSKeepAlive(sql)).start(); + } catch (Exception e) { + System.err.println(e); + } + } + + public void setupSql(String username, String password) { + try { + sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + username + "&password=" + password); + } catch (SQLException e) { + System.err.println(e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/juick/jabber/ws/SocketSubscribed.java b/src/main/java/com/juick/jabber/ws/SocketSubscribed.java new file mode 100644 index 00000000..6144380c --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/SocketSubscribed.java @@ -0,0 +1,51 @@ +package com.juick.jabber.ws; + +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +/** + * + * @author ugnich + */ +public class SocketSubscribed { + + public SocketChannel sock = null; + public String clientName = null; + public int VUID = 0; + public int UID = 0; + public int MID = 0; + public boolean allMessages = false; + public boolean allReplies = false; + public long tsConnected; + public long tsLastData; + + public SocketSubscribed(SocketChannel sock, String clientName, int VUID) { + this.sock = sock; + this.clientName = clientName; + this.VUID = VUID; + tsConnected = tsLastData = System.currentTimeMillis(); + } + + public boolean sendByteBuffer(ByteBuffer bb) { + boolean ret = false; + bb.rewind(); + try { + sock.write(bb); + ret = true; + } catch (Exception e) { + close(); + } + return ret; + } + + public void close() { + try { + sock.socket().close(); + } catch (Exception e) { + } + try { + sock.close(); + } catch (Exception e) { + } + } +} diff --git a/src/main/java/com/juick/jabber/ws/WSConnections.java b/src/main/java/com/juick/jabber/ws/WSConnections.java new file mode 100644 index 00000000..15fbe4e8 --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/WSConnections.java @@ -0,0 +1,44 @@ +package com.juick.jabber.ws; + +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class WSConnections implements Runnable { + + Selector sel; + + @Override + public void run() { + try { + sel = Selector.open(); + ServerSocketChannel listensock = ServerSocketChannel.open(); + listensock.configureBlocking(false); + listensock.socket().bind(new InetSocketAddress(8081)); + listensock.register(sel, SelectionKey.OP_ACCEPT); + + while (true) { + sel.select(); + Iterator it = sel.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + it.remove(); + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); + SocketChannel sChannel = ssChannel.accept(); + sChannel.configureBlocking(false); + sChannel.register(sel, SelectionKey.OP_READ); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + } + } + } catch (Exception e) { + System.err.println("WSConnections: " + e); + } + } +} diff --git a/src/main/java/com/juick/jabber/ws/WSData.java b/src/main/java/com/juick/jabber/ws/WSData.java new file mode 100644 index 00000000..d77257e6 --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/WSData.java @@ -0,0 +1,276 @@ +package com.juick.jabber.ws; + +import com.juick.server.MessagesQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.utils.Base64; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.sql.Connection; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class WSData implements Runnable { + + static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + Connection sql; + public Selector sel; + + public WSData(Connection sql) { + this.sql = sql; + } + + @Override + public void run() { + try { + sel = Selector.open(); + ServerSocketChannel listensock = ServerSocketChannel.open(); + listensock.configureBlocking(false); + listensock.socket().bind(new InetSocketAddress(8081)); + listensock.register(sel, SelectionKey.OP_ACCEPT); + + while (true) { + sel.select(); + Iterator it = sel.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + it.remove(); + + if (selKey.isAcceptable()) { + ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); + SocketChannel sChannel = ssChannel.accept(); + sChannel.configureBlocking(false); + sChannel.register(sel, SelectionKey.OP_READ); + System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + } else if (selKey.isReadable()) { + SocketChannel sChannel = (SocketChannel) selKey.channel(); + ByteBuffer buf = ByteBuffer.allocate(10240); + try { + int readbytes = sChannel.read(buf); + if (readbytes > 0) { + buf.flip(); + + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + System.out.println("DATA: " + charbuf.toString()); + buf.rewind(); + + switch (buf.get(0)) { + case (byte) 0x89: // PING + updateSocketTS(sChannel); + wsPing(sChannel); + break; + case (byte) 0x8A: // PONG + updateSocketTS(sChannel); + break; + case (byte) 0x81: // TEXT FRAME + updateSocketTS(sChannel); + wsTextFrame(sChannel, buf); + break; + case (byte) 'G': // HTTP + updateSocketTS(sChannel); + wsHandshake(sChannel, buf); + break; + case (byte) 0x88: // CONNECTION CLOSE + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " CONNECTION CLOSE"); + } + } else if (readbytes < 0) { + throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " END OF STREAM"); + } + } catch (IOException e) { + System.err.println("WSData: " + e); + sChannel.socket().close(); + sChannel.close(); + selKey.cancel(); + } + } + } + } + } catch (Exception e) { + System.err.println("WSData: " + e); + } + } + + public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { + String hOrigin = null; + String hHost = null; + String hLocation = null; + String hSecWebSocketKey = null; + String hSecWebSocketVersion = null; + String hXRealIP = null; + + buf.rewind(); + CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); + String headers[] = charbuf.toString().split("\r\n"); + for (int i = 0; i < headers.length; i++) { + String h[] = headers[i].split(" ", 2); + if (h.length == 2) { + if (h[0].equals("GET")) { + hLocation = headers[i].split(" ", 3)[1]; + } else if (h[0].equals("Origin:")) { + hOrigin = h[1]; + } else if (h[0].equals("Host:")) { + hHost = h[1]; + } else if (h[0].equals("Sec-WebSocket-Key:")) { + hSecWebSocketKey = h[1]; + } else if (h[0].equals("Sec-WebSocket-Version:")) { + hSecWebSocketVersion = h[1]; + } else if (h[0].equals("X-Real-IP:")) { + hXRealIP = h[1]; + } + } + } + + if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey == null || hSecWebSocketVersion == null || !hSecWebSocketVersion.equals("13")) { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); + } + + // Auth + int VUID = 0; + int hashloc = hLocation.indexOf("hash="); + if (hashloc > 0) { + String hash = hLocation.substring(hashloc + 5); + if (hash.indexOf('&') > 0) { + hash = hash.substring(0, hash.indexOf('&')); + } + if (hash.length() == 16) { + VUID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); + } + } + + // URL + int hLocationQM = hLocation.indexOf('?'); + if (hLocationQM > 0) { + hLocation = hLocation.substring(0, hLocationQM); + } + + int MID = 0; + int responseCode = 404; + SocketSubscribed sockSubscr = null; + if (hLocation.equals("/") && VUID > 0) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + responseCode = 101; + } else if (hLocation.equals("/_all")) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.allMessages = true; + responseCode = 101; + } else if (hLocation.equals("/_replies")) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.allReplies = true; + responseCode = 101; + } else if (hLocation.matches("^/\\d+$")) { + try { + MID = Integer.parseInt(hLocation.substring(1)); + } catch (Exception e) { + } + if (MID > 0) { + if (MessagesQueries.canViewThread(sql, MID, VUID)) { + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.MID = MID; + responseCode = 101; + } else { + responseCode = 403; + } + } + } else if (hLocation.matches("^/[a-zA-Z0-9\\-]{2,16}/?$")) { + String uname; + if (hLocation.endsWith("/")) { + uname = hLocation.substring(1, hLocation.length() - 2); + } else { + uname = hLocation.substring(1); + } + + int UID = UserQueries.getUIDbyName(sql, uname); + if (UID > 0) { + // check access + sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); + sockSubscr.UID = UID; + responseCode = 101; + } + } + if (sockSubscr != null) { + synchronized (Main.clients) { + Main.clients.add(sockSubscr); + } + } + + // Response + String outstr; + if (responseCode == 101) { + outstr = "HTTP/1.1 101 Switching Protocols\r\n" + + "Upgrade: websocket\r\n" + + "Connection: Upgrade\r\n" + + "Sec-WebSocket-Accept: " + calcHeaderAccept(hSecWebSocketKey) + "\r\n" + + "\r\n"; + } else if (responseCode == 403) { + outstr = "HTTP/1.1 403 Forbidden\r\n\r\n"; + } else { + outstr = "HTTP/1.1 404 Not Found\r\n\r\n"; + } + ByteBuffer out = ByteBuffer.allocate(1024); + out.put(Charset.forName("ISO-8859-1").encode(outstr)); + out.flip(); + sock.write(out); + + if (responseCode == 101) { + System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); + } else { + throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " " + responseCode); + } + } + + private String calcHeaderAccept(String key) { + String base = key + WEBSOCKET_GUID; + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + return Base64.encode(md.digest(base.getBytes())); + } catch (NoSuchAlgorithmException e) { + System.err.println("calcHeaderAccept: " + e); + } + return ""; + } + + public void wsPing(SocketChannel sock) throws Exception { + ByteBuffer out = ByteBuffer.allocate(2); + out.put((byte) 0x8A); // PONG FRAME + out.put((byte) 0x00); // 1 byte long + out.flip(); + out.rewind(); + sock.write(out); + } + + public void wsTextFrame(SocketChannel sock, ByteBuffer buf) throws Exception { + /* + ByteBuffer out = ByteBuffer.allocate(3); + out.put((byte) 0x81); // TEXT FRAME + out.put((byte) 0x01); // 1 byte long + out.put((byte) 0x20); // ' ' + out.flip(); + out.rewind(); + sock.write(out); + */ + } + + public void updateSocketTS(SocketChannel sock) { + synchronized (Main.clients) { + Iterator i = Main.clients.iterator(); + while (i.hasNext()) { + SocketSubscribed s = i.next(); + if (s.sock == sock) { + s.tsLastData = System.currentTimeMillis(); + break; + } + } + } + } +} diff --git a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java new file mode 100644 index 00000000..2deef594 --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java @@ -0,0 +1,102 @@ +package com.juick.jabber.ws; + +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.sql.Connection; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class WSKeepAlive implements Runnable { + + Connection sql; + ByteBuffer pingBytes; + ByteBuffer closeBytes; + + public WSKeepAlive(Connection sql) { + this.sql = sql; + + //pingBytes = ByteBuffer.allocate(2); + //pingBytes.put((byte) 0x8A); // PONG FRAME + //pingBytes.put((byte) 0x00); // 0 byte long + pingBytes = ByteBuffer.allocate(3); + pingBytes.put((byte) 0x81); // TEXT FRAME + pingBytes.put((byte) 0x01); // 1 byte long + pingBytes.put((byte) 0x20); // ' ' + pingBytes.flip(); + + closeBytes = ByteBuffer.allocate(2); + closeBytes.put((byte) 0x88); // CLOSE FRAME + closeBytes.put((byte) 0x00); // 0 byte long + closeBytes.flip(); + } + + @Override + public void run() { + while (true) { + PrintWriter statsFile = null; + + if (Main.STATSFILE != null) { + try { + statsFile = new PrintWriter(Main.STATSFILE, "UTF-8"); + } catch (Exception e) { + statsFile = null; + System.err.println("WSKeepAlive statsFile: " + e); + } + } + + long now = System.currentTimeMillis(); + + synchronized (Main.clients) { + if (statsFile != null) { + statsFile.write("

Connections (" + Main.clients.size() + ")

"); + } + + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + int inactive = (int) ((double) (now - s.tsLastData) / 1000.0); + + if (statsFile != null) { + try { + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + statsFile.print(""); + } catch (Exception e) { + System.err.println("WSKeepAlive statsFile print: " + e); + } + } + + if (inactive > 180) { + s.sendByteBuffer(closeBytes); + s.close(); + i.remove(); + } else if (inactive > 60) { + if (!s.sendByteBuffer(pingBytes)) { + i.remove(); + } + } + } + } + + if (Main.STATSFILE != null) { + try { + statsFile.write("
IPinactiveVUIDUIDMIDallMallR
" + (s.clientName != null ? s.clientName : "?") + "" + inactive + "" + (s.VUID > 0 ? s.VUID : "") + "" + (s.UID > 0 ? s.UID : "") + "" + (s.MID > 0 ? s.MID : "") + "" + (s.allMessages ? "+" : "") + "" + (s.allReplies ? "+" : "") + "
"); + statsFile.close(); + } catch (Exception e) { + System.err.println("WSKeepAlive statsFile close: " + e); + } + } + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + } + } +} diff --git a/src/main/java/com/juick/jabber/ws/XMPPConnection.java b/src/main/java/com/juick/jabber/ws/XMPPConnection.java new file mode 100644 index 00000000..39815da5 --- /dev/null +++ b/src/main/java/com/juick/jabber/ws/XMPPConnection.java @@ -0,0 +1,166 @@ +package com.juick.jabber.ws; + +import com.juick.server.MessagesQueries; +import com.juick.server.Utils; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import com.juick.xmpp.extensions.JuickMessage; +import java.io.IOException; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { + + Connection sql; + Stream xmpp; + String xmppPassword; + + public XMPPConnection(Connection sql, String password) { + this.sql = sql; + xmppPassword = password; + } + + @Override + public void run() { + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((Message.MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + System.err.println("XMPPConnection: " + e); + } + } + + @Override + public void onStreamReady() { + System.err.println("Stream ready"); + } + + @Override + public void onStreamFail(String msg) { + System.err.println("Stream failed: " + msg); + } + + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); + if (jmsg != null) { + System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); + if (jmsg.MID == 0) { + int uid_to = 0; + try { + uid_to = Integer.parseInt(msg.to.Username); + } catch (Exception e) { + } + if (uid_to > 0) { + onJuickPM(uid_to, jmsg); + } + } else if (jmsg.RID == 0) { + onJuickMessagePost(jmsg); + } else { + onJuickMessageReply(jmsg); + } + } + } + + private void onJuickPM(int uid_to, com.juick.Message jmsg) { + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer bbMsg = buildTextFrame(json); + + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if (s.VUID == uid_to && s.MID == 0 && s.allMessages == false && s.allReplies == false) { + if (!s.sendByteBuffer(bbMsg)) { + i.remove(); + } + } + } + } + } + + private void onJuickMessagePost(com.juick.Message jmsg) { + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer bbMsg = buildTextFrame(json); + + ArrayList uids = new ArrayList(); + String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; + if (jmsg.Privacy < 0) { + query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; + } + Statement stmt = null; + ResultSet rs = null; + try { + stmt = sql.createStatement(); + rs = stmt.executeQuery(query); + rs.beforeFirst(); + while (rs.next()) { + uids.add(rs.getInt(1)); + } + } catch (SQLException e) { + System.err.println("onJuickMessagePost: " + e); + } finally { + Utils.finishSQL(rs, stmt); + } + + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID))) { + if (!s.sendByteBuffer(bbMsg)) { + i.remove(); + } + } + } + } + } + + private void onJuickMessageReply(com.juick.Message jmsg) { + String json = com.juick.json.Message.toJSON(jmsg).toString(); + ByteBuffer bbMsg = buildTextFrame(json); + + int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.MID); + + synchronized (Main.clients) { + for (Iterator i = Main.clients.iterator(); i.hasNext();) { + SocketSubscribed s = i.next(); + if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.MID) { + if (!s.sendByteBuffer(bbMsg)) { + i.remove(); + } + } + } + } + } + + private ByteBuffer buildTextFrame(String json) { + ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); + ByteBuffer bbMsg = ByteBuffer.allocate(jsonbytes.limit() + 8); + bbMsg.put((byte) 0x81); + if (jsonbytes.limit() <= 125) { + bbMsg.put((byte) jsonbytes.limit()); + } else { + bbMsg.put((byte) 126); + bbMsg.putShort((short) jsonbytes.limit()); + } + bbMsg.put(jsonbytes); + bbMsg.flip(); + return bbMsg; + } +} -- cgit v1.2.3 From 27e6c9c5edb2642c31735fcb0effb9acd82bd4af Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 28 Jan 2016 17:29:55 +0300 Subject: fix build --- build.gradle | 1 + .../java/com/juick/jabber/ws/XMPPConnection.java | 30 +++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/build.gradle b/build.gradle index 0091459a..63ba360a 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,7 @@ dependencies { compile json compile "org.springframework:spring-jdbc:4.2.4.RELEASE" providedCompile 'javax.servlet:javax.servlet-api:3.1.0' + providedCompile 'javax.websocket:javax.websocket-api:1.0' def tomcatVersion = '7.0.+' tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}", diff --git a/src/main/java/com/juick/jabber/ws/XMPPConnection.java b/src/main/java/com/juick/jabber/ws/XMPPConnection.java index 39815da5..df403083 100644 --- a/src/main/java/com/juick/jabber/ws/XMPPConnection.java +++ b/src/main/java/com/juick/jabber/ws/XMPPConnection.java @@ -1,5 +1,6 @@ package com.juick.jabber.ws; +import com.juick.json.MessageSerializer; import com.juick.server.MessagesQueries; import com.juick.server.Utils; import com.juick.xmpp.JID; @@ -53,7 +54,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } @Override - public void onStreamFail(String msg) { + public void onStreamFail(Exception msg) { System.err.println("Stream failed: " + msg); } @@ -61,8 +62,8 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. public void onMessage(com.juick.xmpp.Message msg) { JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (jmsg != null) { - System.err.println("MID=" + jmsg.MID + "; RID=" + jmsg.RID); - if (jmsg.MID == 0) { + System.err.println("MID=" + jmsg.getMID() + "; RID=" + jmsg.getRID()); + if (jmsg.getMID() == 0) { int uid_to = 0; try { uid_to = Integer.parseInt(msg.to.Username); @@ -71,7 +72,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. if (uid_to > 0) { onJuickPM(uid_to, jmsg); } - } else if (jmsg.RID == 0) { + } else if (jmsg.getRID() == 0) { onJuickMessagePost(jmsg); } else { onJuickMessageReply(jmsg); @@ -79,8 +80,10 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } } + MessageSerializer messageSerializer = new MessageSerializer(); + private void onJuickPM(int uid_to, com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); + String json = messageSerializer.serialize(jmsg).toString(); ByteBuffer bbMsg = buildTextFrame(json); synchronized (Main.clients) { @@ -96,13 +99,15 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } private void onJuickMessagePost(com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); + String json = messageSerializer.serialize(jmsg).toString(); ByteBuffer bbMsg = buildTextFrame(json); ArrayList uids = new ArrayList(); - String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.User.UID + " AND suser_id NOT IN (SELECT user_id FROM bl_tags INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.MID + ")"; + String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.getUser().getUID() + + " AND suser_id NOT IN (SELECT user_id FROM bl_tags " + + "INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.getMID() + ")"; if (jmsg.Privacy < 0) { - query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.User.UID + ")"; + query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.getUser().getUID() + ")"; } Statement stmt = null; ResultSet rs = null; @@ -122,7 +127,8 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); - if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 && (s.allMessages || s.UID == jmsg.User.UID)) || uids.contains(s.VUID))) { + if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 + && (s.allMessages || s.UID == jmsg.getUser().getUID())) || uids.contains(s.VUID))) { if (!s.sendByteBuffer(bbMsg)) { i.remove(); } @@ -132,15 +138,15 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } private void onJuickMessageReply(com.juick.Message jmsg) { - String json = com.juick.json.Message.toJSON(jmsg).toString(); + String json = messageSerializer.serialize(jmsg).toString(); ByteBuffer bbMsg = buildTextFrame(json); - int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.MID); + int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.getMID()); synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { SocketSubscribed s = i.next(); - if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.MID) { + if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.getMID()) { if (!s.sendByteBuffer(bbMsg)) { i.remove(); } -- cgit v1.2.3 From 095dee1388919eac7d3a3fa6fc93767f82c16365 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 24 Jun 2016 15:06:58 +0300 Subject: spring-jdbc --- deps/com.juick | 2 +- deps/com.juick.json | 2 +- deps/com.juick.server | 2 +- deps/com.juick.xmpp | 2 +- src/main/java/com/juick/jabber/ws/Main.java | 19 +++++----- .../java/com/juick/jabber/ws/WSConnections.java | 44 ---------------------- src/main/java/com/juick/jabber/ws/WSData.java | 7 ++-- src/main/java/com/juick/jabber/ws/WSKeepAlive.java | 7 ++-- .../java/com/juick/jabber/ws/XMPPConnection.java | 41 ++++++-------------- 9 files changed, 33 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/com/juick/jabber/ws/WSConnections.java (limited to 'src') diff --git a/deps/com.juick b/deps/com.juick index 1df19b47..39ec74ab 160000 --- a/deps/com.juick +++ b/deps/com.juick @@ -1 +1 @@ -Subproject commit 1df19b47f7f463e6cafa7dcf877cb42967fff166 +Subproject commit 39ec74abe77cdab5aa8f50c2524e6f71c2dbbd01 diff --git a/deps/com.juick.json b/deps/com.juick.json index ee0e13e7..9dfad84f 160000 --- a/deps/com.juick.json +++ b/deps/com.juick.json @@ -1 +1 @@ -Subproject commit ee0e13e7243abecf242236c224551774195524a3 +Subproject commit 9dfad84fa487960fde4cc4b0ac6760c8dd5f4ac9 diff --git a/deps/com.juick.server b/deps/com.juick.server index 96188f8d..1ce6bc55 160000 --- a/deps/com.juick.server +++ b/deps/com.juick.server @@ -1 +1 @@ -Subproject commit 96188f8d8a31df2474bf0eaf7a91773280ddccb6 +Subproject commit 1ce6bc55817129159b1cda5076d039e9c3bc8508 diff --git a/deps/com.juick.xmpp b/deps/com.juick.xmpp index 1f08c012..a096ecc6 160000 --- a/deps/com.juick.xmpp +++ b/deps/com.juick.xmpp @@ -1 +1 @@ -Subproject commit 1f08c01254fcbca79d1571ebb1ed2d68f86b8503 +Subproject commit a096ecc6d011bf0f6c738b10d7d0a7319ffcf2dc diff --git a/src/main/java/com/juick/jabber/ws/Main.java b/src/main/java/com/juick/jabber/ws/Main.java index a7aea543..3a26c3ad 100644 --- a/src/main/java/com/juick/jabber/ws/Main.java +++ b/src/main/java/com/juick/jabber/ws/Main.java @@ -17,8 +17,10 @@ */ package com.juick.jabber.ws; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + import java.io.FileInputStream; -import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; @@ -30,7 +32,7 @@ import java.util.Properties; */ public class Main { - Connection sql; + JdbcTemplate sql; XMPPConnection xmpp; public static String STATSFILE = null; public final static ArrayList clients = new ArrayList(); @@ -46,7 +48,7 @@ public class Main { STATSFILE = conf.getProperty("statsfile"); - setupSql(conf.getProperty("mysql_username", ""), conf.getProperty("mysql_password", "")); + setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url")); xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); new Thread(xmpp).start(); @@ -58,11 +60,10 @@ public class Main { } } - public void setupSql(String username, String password) { - try { - sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + username + "&password=" + password); - } catch (SQLException e) { - System.err.println(e); - } + public void setupSql(String driver, String url) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUrl(url); + sql = new JdbcTemplate(dataSource); } } \ No newline at end of file diff --git a/src/main/java/com/juick/jabber/ws/WSConnections.java b/src/main/java/com/juick/jabber/ws/WSConnections.java deleted file mode 100644 index 15fbe4e8..00000000 --- a/src/main/java/com/juick/jabber/ws/WSConnections.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.juick.jabber.ws; - -import java.net.InetSocketAddress; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class WSConnections implements Runnable { - - Selector sel; - - @Override - public void run() { - try { - sel = Selector.open(); - ServerSocketChannel listensock = ServerSocketChannel.open(); - listensock.configureBlocking(false); - listensock.socket().bind(new InetSocketAddress(8081)); - listensock.register(sel, SelectionKey.OP_ACCEPT); - - while (true) { - sel.select(); - Iterator it = sel.selectedKeys().iterator(); - while (it.hasNext()) { - SelectionKey selKey = it.next(); - it.remove(); - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - sChannel.configureBlocking(false); - sChannel.register(sel, SelectionKey.OP_READ); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); - } - } - } catch (Exception e) { - System.err.println("WSConnections: " + e); - } - } -} diff --git a/src/main/java/com/juick/jabber/ws/WSData.java b/src/main/java/com/juick/jabber/ws/WSData.java index d77257e6..3abc63d8 100644 --- a/src/main/java/com/juick/jabber/ws/WSData.java +++ b/src/main/java/com/juick/jabber/ws/WSData.java @@ -3,6 +3,8 @@ package com.juick.jabber.ws; import com.juick.server.MessagesQueries; import com.juick.server.UserQueries; import com.juick.xmpp.utils.Base64; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -14,7 +16,6 @@ import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.sql.Connection; import java.util.Iterator; /** @@ -24,10 +25,10 @@ import java.util.Iterator; public class WSData implements Runnable { static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - Connection sql; + JdbcTemplate sql; public Selector sel; - public WSData(Connection sql) { + public WSData(JdbcTemplate sql) { this.sql = sql; } diff --git a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java index 2deef594..f253439d 100644 --- a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java +++ b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java @@ -1,8 +1,9 @@ package com.juick.jabber.ws; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.PrintWriter; import java.nio.ByteBuffer; -import java.sql.Connection; import java.util.Iterator; /** @@ -11,11 +12,11 @@ import java.util.Iterator; */ public class WSKeepAlive implements Runnable { - Connection sql; + JdbcTemplate sql; ByteBuffer pingBytes; ByteBuffer closeBytes; - public WSKeepAlive(Connection sql) { + public WSKeepAlive(JdbcTemplate sql) { this.sql = sql; //pingBytes = ByteBuffer.allocate(2); diff --git a/src/main/java/com/juick/jabber/ws/XMPPConnection.java b/src/main/java/com/juick/jabber/ws/XMPPConnection.java index df403083..1679ae8f 100644 --- a/src/main/java/com/juick/jabber/ws/XMPPConnection.java +++ b/src/main/java/com/juick/jabber/ws/XMPPConnection.java @@ -1,23 +1,23 @@ package com.juick.jabber.ws; +import com.juick.User; import com.juick.json.MessageSerializer; import com.juick.server.MessagesQueries; -import com.juick.server.Utils; +import com.juick.server.SubscriptionsQueries; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; import com.juick.xmpp.StreamComponent; import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.jdbc.core.JdbcTemplate; + import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; /** * @@ -25,11 +25,11 @@ import java.util.Iterator; */ public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { - Connection sql; + JdbcTemplate sql; Stream xmpp; String xmppPassword; - public XMPPConnection(Connection sql, String password) { + public XMPPConnection(JdbcTemplate sql, String password) { this.sql = sql; xmppPassword = password; } @@ -54,7 +54,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } @Override - public void onStreamFail(Exception msg) { + public void onStreamFail(String msg) { System.err.println("Stream failed: " + msg); } @@ -102,27 +102,8 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. String json = messageSerializer.serialize(jmsg).toString(); ByteBuffer bbMsg = buildTextFrame(json); - ArrayList uids = new ArrayList(); - String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + jmsg.getUser().getUID() + - " AND suser_id NOT IN (SELECT user_id FROM bl_tags " + - "INNER JOIN messages_tags USING(tag_id) WHERE message_id=" + jmsg.getMID() + ")"; - if (jmsg.Privacy < 0) { - query += " AND suser_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=" + jmsg.getUser().getUID() + ")"; - } - Statement stmt = null; - ResultSet rs = null; - try { - stmt = sql.createStatement(); - rs = stmt.executeQuery(query); - rs.beforeFirst(); - while (rs.next()) { - uids.add(rs.getInt(1)); - } - } catch (SQLException e) { - System.err.println("onJuickMessagePost: " + e); - } finally { - Utils.finishSQL(rs, stmt); - } + List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) + .stream().map(User::getUID).collect(Collectors.toList()); synchronized (Main.clients) { for (Iterator i = Main.clients.iterator(); i.hasNext();) { -- cgit v1.2.3 From 2f5b0da83de728d7ba1b35b3528055462002de8a Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 24 Jun 2016 15:18:24 +0300 Subject: logging --- src/main/java/com/juick/jabber/ws/Main.java | 10 +++++++--- src/main/java/com/juick/jabber/ws/WSData.java | 20 ++++++++++---------- src/main/java/com/juick/jabber/ws/WSKeepAlive.java | 9 ++++++--- .../java/com/juick/jabber/ws/XMPPConnection.java | 13 +++++++++---- 4 files changed, 32 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/jabber/ws/Main.java b/src/main/java/com/juick/jabber/ws/Main.java index 3a26c3ad..6f5ed96a 100644 --- a/src/main/java/com/juick/jabber/ws/Main.java +++ b/src/main/java/com/juick/jabber/ws/Main.java @@ -21,10 +21,11 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.io.FileInputStream; -import java.sql.DriverManager; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; /** * @@ -32,6 +33,8 @@ import java.util.Properties; */ public class Main { + private static final Logger logger = Logger.getLogger("Websockets"); + JdbcTemplate sql; XMPPConnection xmpp; public static String STATSFILE = null; @@ -43,6 +46,7 @@ public class Main { public void start() { try { + LogManager.getLogManager().readConfiguration(new FileInputStream("/etc/juick/ws_logging.properties")); Properties conf = new Properties(); conf.load(new FileInputStream("/etc/juick/ws.conf")); @@ -56,7 +60,7 @@ public class Main { new Thread(new WSData(sql)).start(); new Thread(new WSKeepAlive(sql)).start(); } catch (Exception e) { - System.err.println(e); + logger.log(Level.SEVERE, "websockets initialization error", e); } } diff --git a/src/main/java/com/juick/jabber/ws/WSData.java b/src/main/java/com/juick/jabber/ws/WSData.java index 3abc63d8..9ae98bcf 100644 --- a/src/main/java/com/juick/jabber/ws/WSData.java +++ b/src/main/java/com/juick/jabber/ws/WSData.java @@ -17,13 +17,16 @@ import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; /** * * @author ugnich */ public class WSData implements Runnable { - + + private static final Logger logger = Logger.getLogger("Websockets"); static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; JdbcTemplate sql; public Selector sel; @@ -53,7 +56,7 @@ public class WSData implements Runnable { SocketChannel sChannel = ssChannel.accept(); sChannel.configureBlocking(false); sChannel.register(sel, SelectionKey.OP_READ); - System.out.println(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); + logger.info(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); } else if (selKey.isReadable()) { SocketChannel sChannel = (SocketChannel) selKey.channel(); ByteBuffer buf = ByteBuffer.allocate(10240); @@ -63,7 +66,6 @@ public class WSData implements Runnable { buf.flip(); CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - System.out.println("DATA: " + charbuf.toString()); buf.rewind(); switch (buf.get(0)) { @@ -89,7 +91,7 @@ public class WSData implements Runnable { throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " END OF STREAM"); } } catch (IOException e) { - System.err.println("WSData: " + e); + logger.log(Level.SEVERE, "websocket exception", e); sChannel.socket().close(); sChannel.close(); selKey.cancel(); @@ -98,7 +100,7 @@ public class WSData implements Runnable { } } } catch (Exception e) { - System.err.println("WSData: " + e); + logger.log(Level.SEVERE, "websocket exception", e); } } @@ -224,7 +226,7 @@ public class WSData implements Runnable { sock.write(out); if (responseCode == 101) { - System.out.println(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); + logger.info(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); } else { throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " " + responseCode); } @@ -236,7 +238,7 @@ public class WSData implements Runnable { MessageDigest md = MessageDigest.getInstance("SHA-1"); return Base64.encode(md.digest(base.getBytes())); } catch (NoSuchAlgorithmException e) { - System.err.println("calcHeaderAccept: " + e); + logger.severe("calcHeaderAccept: " + e); } return ""; } @@ -264,9 +266,7 @@ public class WSData implements Runnable { public void updateSocketTS(SocketChannel sock) { synchronized (Main.clients) { - Iterator i = Main.clients.iterator(); - while (i.hasNext()) { - SocketSubscribed s = i.next(); + for (SocketSubscribed s : Main.clients) { if (s.sock == sock) { s.tsLastData = System.currentTimeMillis(); break; diff --git a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java index f253439d..6244013d 100644 --- a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java +++ b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java @@ -5,12 +5,15 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; /** * * @author ugnich */ public class WSKeepAlive implements Runnable { + private static final Logger logger = Logger.getLogger(WSKeepAlive.class.getName()); JdbcTemplate sql; ByteBuffer pingBytes; @@ -44,7 +47,7 @@ public class WSKeepAlive implements Runnable { statsFile = new PrintWriter(Main.STATSFILE, "UTF-8"); } catch (Exception e) { statsFile = null; - System.err.println("WSKeepAlive statsFile: " + e); + logger.severe("WSKeepAlive statsFile: " + e); } } @@ -69,7 +72,7 @@ public class WSKeepAlive implements Runnable { statsFile.print("" + (s.allMessages ? "+" : "") + ""); statsFile.print("" + (s.allReplies ? "+" : "") + ""); } catch (Exception e) { - System.err.println("WSKeepAlive statsFile print: " + e); + logger.log(Level.SEVERE, "WSKeepAlive statsFile print", e); } } @@ -90,7 +93,7 @@ public class WSKeepAlive implements Runnable { statsFile.write(""); statsFile.close(); } catch (Exception e) { - System.err.println("WSKeepAlive statsFile close: " + e); + logger.log(Level.SEVERE, "WSKeepAlive statsFile close", e); } } diff --git a/src/main/java/com/juick/jabber/ws/XMPPConnection.java b/src/main/java/com/juick/jabber/ws/XMPPConnection.java index 1679ae8f..8c187a36 100644 --- a/src/main/java/com/juick/jabber/ws/XMPPConnection.java +++ b/src/main/java/com/juick/jabber/ws/XMPPConnection.java @@ -17,6 +17,8 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Collectors; /** @@ -24,14 +26,17 @@ import java.util.stream.Collectors; * @author ugnich */ public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { + private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); JdbcTemplate sql; Stream xmpp; String xmppPassword; + MessageSerializer ms; public XMPPConnection(JdbcTemplate sql, String password) { this.sql = sql; xmppPassword = password; + ms = new MessageSerializer(); } @Override @@ -44,25 +49,25 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. xmpp.addListener((Message.MessageListener) this); xmpp.startParsing(); } catch (IOException e) { - System.err.println("XMPPConnection: " + e); + logger.log(Level.SEVERE, "XMPPConnection error", e); } } @Override public void onStreamReady() { - System.err.println("Stream ready"); + logger.info("XMPP stream ready"); } @Override public void onStreamFail(String msg) { - System.err.println("Stream failed: " + msg); + logger.severe("XMPP stream failed: " + msg); } @Override public void onMessage(com.juick.xmpp.Message msg) { JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (jmsg != null) { - System.err.println("MID=" + jmsg.getMID() + "; RID=" + jmsg.getRID()); + logger.info("got jmsg: " + ms.serialize(jmsg).toString()); if (jmsg.getMID() == 0) { int uid_to = 0; try { -- cgit v1.2.3 From f1b9807af10d45fee99042af88e21e1b4dc161a2 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 24 Jun 2016 16:14:16 +0300 Subject: update deps --- deps/com.juick.server | 2 +- src/main/java/com/juick/jabber/ws/Main.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/deps/com.juick.server b/deps/com.juick.server index 1ce6bc55..c1fcf803 160000 --- a/deps/com.juick.server +++ b/deps/com.juick.server @@ -1 +1 @@ -Subproject commit 1ce6bc55817129159b1cda5076d039e9c3bc8508 +Subproject commit c1fcf803adfe06a0a54b3e51c4fd2d67e6b732d8 diff --git a/src/main/java/com/juick/jabber/ws/Main.java b/src/main/java/com/juick/jabber/ws/Main.java index 6f5ed96a..6a397fff 100644 --- a/src/main/java/com/juick/jabber/ws/Main.java +++ b/src/main/java/com/juick/jabber/ws/Main.java @@ -21,8 +21,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.Properties; +import java.util.*; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -38,7 +37,7 @@ public class Main { JdbcTemplate sql; XMPPConnection xmpp; public static String STATSFILE = null; - public final static ArrayList clients = new ArrayList(); + public final static List clients = Collections.synchronizedList(new ArrayList()); public static void main(String[] args) { new Main().start(); -- cgit v1.2.3 From 331645f0fd7fbe7d9679d39dcce453cc3b2cab6e Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 10:36:28 +0300 Subject: spring-websocket --- build.gradle | 12 +- deps/com.juick.server | 2 +- src/main/java/com/juick/jabber/ws/Main.java | 72 ------ .../java/com/juick/jabber/ws/SocketSubscribed.java | 51 ---- src/main/java/com/juick/jabber/ws/WSData.java | 277 --------------------- src/main/java/com/juick/jabber/ws/WSKeepAlive.java | 106 -------- .../java/com/juick/jabber/ws/XMPPConnection.java | 158 ------------ src/main/java/com/juick/ws/WebsocketComponent.java | 134 ++++++++++ src/main/java/com/juick/ws/XMPPConnection.java | 142 +++++++++++ .../ws/configuration/WebsocketConfiguration.java | 49 ++++ 10 files changed, 333 insertions(+), 670 deletions(-) delete mode 100644 src/main/java/com/juick/jabber/ws/Main.java delete mode 100644 src/main/java/com/juick/jabber/ws/SocketSubscribed.java delete mode 100644 src/main/java/com/juick/jabber/ws/WSData.java delete mode 100644 src/main/java/com/juick/jabber/ws/WSKeepAlive.java delete mode 100644 src/main/java/com/juick/jabber/ws/XMPPConnection.java create mode 100644 src/main/java/com/juick/ws/WebsocketComponent.java create mode 100644 src/main/java/com/juick/ws/XMPPConnection.java create mode 100644 src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java (limited to 'src') diff --git a/build.gradle b/build.gradle index 0eb80dd4..92b44bec 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,7 @@ subprojects { } apply plugin: 'java' -apply plugin: 'application' - -mainClassName = "com.juick.jabber.ws.Main" +apply plugin: 'war' repositories { mavenCentral() @@ -41,9 +39,13 @@ dependencies { compile server compile xmpp compile json - compile "org.springframework:spring-jdbc:4.3.0.RELEASE" + def springFrameworkVersion = '4.3.0.RELEASE' + compile "org.springframework:spring-jdbc:${springFrameworkVersion}" + compile "org.springframework:spring-websocket:${springFrameworkVersion}" + compile 'javax.inject:javax.inject:1' + compile 'org.apache.httpcomponents:httpclient:4.5.1' testCompile 'junit:junit:4.12' - runtime 'mysql:mysql-connector-java:5.1.37' + runtime 'mysql:mysql-connector-java:5.1.39' } compileJava.options.encoding = 'UTF-8' diff --git a/deps/com.juick.server b/deps/com.juick.server index bbc05316..87da42e3 160000 --- a/deps/com.juick.server +++ b/deps/com.juick.server @@ -1 +1 @@ -Subproject commit bbc05316ebbbd8da30ac12135791e110cacbebd5 +Subproject commit 87da42e3385e368f05d88ff125a78605a18f9cc3 diff --git a/src/main/java/com/juick/jabber/ws/Main.java b/src/main/java/com/juick/jabber/ws/Main.java deleted file mode 100644 index 6a397fff..00000000 --- a/src/main/java/com/juick/jabber/ws/Main.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.jabber.ws; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; - -import java.io.FileInputStream; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -/** - * - * @author Ugnich Anton - */ -public class Main { - - private static final Logger logger = Logger.getLogger("Websockets"); - - JdbcTemplate sql; - XMPPConnection xmpp; - public static String STATSFILE = null; - public final static List clients = Collections.synchronizedList(new ArrayList()); - - public static void main(String[] args) { - new Main().start(); - } - - public void start() { - try { - LogManager.getLogManager().readConfiguration(new FileInputStream("/etc/juick/ws_logging.properties")); - Properties conf = new Properties(); - conf.load(new FileInputStream("/etc/juick/ws.conf")); - - STATSFILE = conf.getProperty("statsfile"); - - setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url")); - xmpp = new XMPPConnection(sql, conf.getProperty("xmpp_password", "")); - new Thread(xmpp).start(); - - //new Thread(new WSConnections()).start(); - new Thread(new WSData(sql)).start(); - new Thread(new WSKeepAlive(sql)).start(); - } catch (Exception e) { - logger.log(Level.SEVERE, "websockets initialization error", e); - } - } - - public void setupSql(String driver, String url) { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(driver); - dataSource.setUrl(url); - sql = new JdbcTemplate(dataSource); - } -} \ No newline at end of file diff --git a/src/main/java/com/juick/jabber/ws/SocketSubscribed.java b/src/main/java/com/juick/jabber/ws/SocketSubscribed.java deleted file mode 100644 index 6144380c..00000000 --- a/src/main/java/com/juick/jabber/ws/SocketSubscribed.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.jabber.ws; - -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; - -/** - * - * @author ugnich - */ -public class SocketSubscribed { - - public SocketChannel sock = null; - public String clientName = null; - public int VUID = 0; - public int UID = 0; - public int MID = 0; - public boolean allMessages = false; - public boolean allReplies = false; - public long tsConnected; - public long tsLastData; - - public SocketSubscribed(SocketChannel sock, String clientName, int VUID) { - this.sock = sock; - this.clientName = clientName; - this.VUID = VUID; - tsConnected = tsLastData = System.currentTimeMillis(); - } - - public boolean sendByteBuffer(ByteBuffer bb) { - boolean ret = false; - bb.rewind(); - try { - sock.write(bb); - ret = true; - } catch (Exception e) { - close(); - } - return ret; - } - - public void close() { - try { - sock.socket().close(); - } catch (Exception e) { - } - try { - sock.close(); - } catch (Exception e) { - } - } -} diff --git a/src/main/java/com/juick/jabber/ws/WSData.java b/src/main/java/com/juick/jabber/ws/WSData.java deleted file mode 100644 index 9ae98bcf..00000000 --- a/src/main/java/com/juick/jabber/ws/WSData.java +++ /dev/null @@ -1,277 +0,0 @@ -package com.juick.jabber.ws; - -import com.juick.server.MessagesQueries; -import com.juick.server.UserQueries; -import com.juick.xmpp.utils.Base64; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class WSData implements Runnable { - - private static final Logger logger = Logger.getLogger("Websockets"); - static final String WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - JdbcTemplate sql; - public Selector sel; - - public WSData(JdbcTemplate sql) { - this.sql = sql; - } - - @Override - public void run() { - try { - sel = Selector.open(); - ServerSocketChannel listensock = ServerSocketChannel.open(); - listensock.configureBlocking(false); - listensock.socket().bind(new InetSocketAddress(8081)); - listensock.register(sel, SelectionKey.OP_ACCEPT); - - while (true) { - sel.select(); - Iterator it = sel.selectedKeys().iterator(); - while (it.hasNext()) { - SelectionKey selKey = it.next(); - it.remove(); - - if (selKey.isAcceptable()) { - ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel(); - SocketChannel sChannel = ssChannel.accept(); - sChannel.configureBlocking(false); - sChannel.register(sel, SelectionKey.OP_READ); - logger.info(sChannel.socket().getRemoteSocketAddress().toString() + " ACCEPTED"); - } else if (selKey.isReadable()) { - SocketChannel sChannel = (SocketChannel) selKey.channel(); - ByteBuffer buf = ByteBuffer.allocate(10240); - try { - int readbytes = sChannel.read(buf); - if (readbytes > 0) { - buf.flip(); - - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - buf.rewind(); - - switch (buf.get(0)) { - case (byte) 0x89: // PING - updateSocketTS(sChannel); - wsPing(sChannel); - break; - case (byte) 0x8A: // PONG - updateSocketTS(sChannel); - break; - case (byte) 0x81: // TEXT FRAME - updateSocketTS(sChannel); - wsTextFrame(sChannel, buf); - break; - case (byte) 'G': // HTTP - updateSocketTS(sChannel); - wsHandshake(sChannel, buf); - break; - case (byte) 0x88: // CONNECTION CLOSE - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " CONNECTION CLOSE"); - } - } else if (readbytes < 0) { - throw new IOException(sChannel.socket().getRemoteSocketAddress().toString() + " END OF STREAM"); - } - } catch (IOException e) { - logger.log(Level.SEVERE, "websocket exception", e); - sChannel.socket().close(); - sChannel.close(); - selKey.cancel(); - } - } - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "websocket exception", e); - } - } - - public void wsHandshake(SocketChannel sock, ByteBuffer buf) throws Exception { - String hOrigin = null; - String hHost = null; - String hLocation = null; - String hSecWebSocketKey = null; - String hSecWebSocketVersion = null; - String hXRealIP = null; - - buf.rewind(); - CharBuffer charbuf = Charset.forName("ISO-8859-1").decode(buf); - String headers[] = charbuf.toString().split("\r\n"); - for (int i = 0; i < headers.length; i++) { - String h[] = headers[i].split(" ", 2); - if (h.length == 2) { - if (h[0].equals("GET")) { - hLocation = headers[i].split(" ", 3)[1]; - } else if (h[0].equals("Origin:")) { - hOrigin = h[1]; - } else if (h[0].equals("Host:")) { - hHost = h[1]; - } else if (h[0].equals("Sec-WebSocket-Key:")) { - hSecWebSocketKey = h[1]; - } else if (h[0].equals("Sec-WebSocket-Version:")) { - hSecWebSocketVersion = h[1]; - } else if (h[0].equals("X-Real-IP:")) { - hXRealIP = h[1]; - } - } - } - - if (hOrigin == null || hHost == null || hLocation == null || hSecWebSocketKey == null || hSecWebSocketVersion == null || !hSecWebSocketVersion.equals("13")) { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " Invalid headers"); - } - - // Auth - int VUID = 0; - int hashloc = hLocation.indexOf("hash="); - if (hashloc > 0) { - String hash = hLocation.substring(hashloc + 5); - if (hash.indexOf('&') > 0) { - hash = hash.substring(0, hash.indexOf('&')); - } - if (hash.length() == 16) { - VUID = com.juick.server.UserQueries.getUIDbyHash(sql, hash); - } - } - - // URL - int hLocationQM = hLocation.indexOf('?'); - if (hLocationQM > 0) { - hLocation = hLocation.substring(0, hLocationQM); - } - - int MID = 0; - int responseCode = 404; - SocketSubscribed sockSubscr = null; - if (hLocation.equals("/") && VUID > 0) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - responseCode = 101; - } else if (hLocation.equals("/_all")) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.allMessages = true; - responseCode = 101; - } else if (hLocation.equals("/_replies")) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.allReplies = true; - responseCode = 101; - } else if (hLocation.matches("^/\\d+$")) { - try { - MID = Integer.parseInt(hLocation.substring(1)); - } catch (Exception e) { - } - if (MID > 0) { - if (MessagesQueries.canViewThread(sql, MID, VUID)) { - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.MID = MID; - responseCode = 101; - } else { - responseCode = 403; - } - } - } else if (hLocation.matches("^/[a-zA-Z0-9\\-]{2,16}/?$")) { - String uname; - if (hLocation.endsWith("/")) { - uname = hLocation.substring(1, hLocation.length() - 2); - } else { - uname = hLocation.substring(1); - } - - int UID = UserQueries.getUIDbyName(sql, uname); - if (UID > 0) { - // check access - sockSubscr = new SocketSubscribed(sock, hXRealIP, VUID); - sockSubscr.UID = UID; - responseCode = 101; - } - } - if (sockSubscr != null) { - synchronized (Main.clients) { - Main.clients.add(sockSubscr); - } - } - - // Response - String outstr; - if (responseCode == 101) { - outstr = "HTTP/1.1 101 Switching Protocols\r\n" - + "Upgrade: websocket\r\n" - + "Connection: Upgrade\r\n" - + "Sec-WebSocket-Accept: " + calcHeaderAccept(hSecWebSocketKey) + "\r\n" - + "\r\n"; - } else if (responseCode == 403) { - outstr = "HTTP/1.1 403 Forbidden\r\n\r\n"; - } else { - outstr = "HTTP/1.1 404 Not Found\r\n\r\n"; - } - ByteBuffer out = ByteBuffer.allocate(1024); - out.put(Charset.forName("ISO-8859-1").encode(outstr)); - out.flip(); - sock.write(out); - - if (responseCode == 101) { - logger.info(sock.socket().getRemoteSocketAddress().toString() + " HANDSHAKE (VUID = " + VUID + "; MID = " + MID + ")"); - } else { - throw new IOException(sock.socket().getRemoteSocketAddress().toString() + " " + responseCode); - } - } - - private String calcHeaderAccept(String key) { - String base = key + WEBSOCKET_GUID; - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - return Base64.encode(md.digest(base.getBytes())); - } catch (NoSuchAlgorithmException e) { - logger.severe("calcHeaderAccept: " + e); - } - return ""; - } - - public void wsPing(SocketChannel sock) throws Exception { - ByteBuffer out = ByteBuffer.allocate(2); - out.put((byte) 0x8A); // PONG FRAME - out.put((byte) 0x00); // 1 byte long - out.flip(); - out.rewind(); - sock.write(out); - } - - public void wsTextFrame(SocketChannel sock, ByteBuffer buf) throws Exception { - /* - ByteBuffer out = ByteBuffer.allocate(3); - out.put((byte) 0x81); // TEXT FRAME - out.put((byte) 0x01); // 1 byte long - out.put((byte) 0x20); // ' ' - out.flip(); - out.rewind(); - sock.write(out); - */ - } - - public void updateSocketTS(SocketChannel sock) { - synchronized (Main.clients) { - for (SocketSubscribed s : Main.clients) { - if (s.sock == sock) { - s.tsLastData = System.currentTimeMillis(); - break; - } - } - } - } -} diff --git a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java b/src/main/java/com/juick/jabber/ws/WSKeepAlive.java deleted file mode 100644 index 6244013d..00000000 --- a/src/main/java/com/juick/jabber/ws/WSKeepAlive.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.juick.jabber.ws; - -import org.springframework.jdbc.core.JdbcTemplate; - -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class WSKeepAlive implements Runnable { - private static final Logger logger = Logger.getLogger(WSKeepAlive.class.getName()); - - JdbcTemplate sql; - ByteBuffer pingBytes; - ByteBuffer closeBytes; - - public WSKeepAlive(JdbcTemplate sql) { - this.sql = sql; - - //pingBytes = ByteBuffer.allocate(2); - //pingBytes.put((byte) 0x8A); // PONG FRAME - //pingBytes.put((byte) 0x00); // 0 byte long - pingBytes = ByteBuffer.allocate(3); - pingBytes.put((byte) 0x81); // TEXT FRAME - pingBytes.put((byte) 0x01); // 1 byte long - pingBytes.put((byte) 0x20); // ' ' - pingBytes.flip(); - - closeBytes = ByteBuffer.allocate(2); - closeBytes.put((byte) 0x88); // CLOSE FRAME - closeBytes.put((byte) 0x00); // 0 byte long - closeBytes.flip(); - } - - @Override - public void run() { - while (true) { - PrintWriter statsFile = null; - - if (Main.STATSFILE != null) { - try { - statsFile = new PrintWriter(Main.STATSFILE, "UTF-8"); - } catch (Exception e) { - statsFile = null; - logger.severe("WSKeepAlive statsFile: " + e); - } - } - - long now = System.currentTimeMillis(); - - synchronized (Main.clients) { - if (statsFile != null) { - statsFile.write("

Connections (" + Main.clients.size() + ")

"); - } - - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - int inactive = (int) ((double) (now - s.tsLastData) / 1000.0); - - if (statsFile != null) { - try { - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - statsFile.print(""); - } catch (Exception e) { - logger.log(Level.SEVERE, "WSKeepAlive statsFile print", e); - } - } - - if (inactive > 180) { - s.sendByteBuffer(closeBytes); - s.close(); - i.remove(); - } else if (inactive > 60) { - if (!s.sendByteBuffer(pingBytes)) { - i.remove(); - } - } - } - } - - if (Main.STATSFILE != null) { - try { - statsFile.write("
IPinactiveVUIDUIDMIDallMallR
" + (s.clientName != null ? s.clientName : "?") + "" + inactive + "" + (s.VUID > 0 ? s.VUID : "") + "" + (s.UID > 0 ? s.UID : "") + "" + (s.MID > 0 ? s.MID : "") + "" + (s.allMessages ? "+" : "") + "" + (s.allReplies ? "+" : "") + "
"); - statsFile.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "WSKeepAlive statsFile close", e); - } - } - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - } - } - } -} diff --git a/src/main/java/com/juick/jabber/ws/XMPPConnection.java b/src/main/java/com/juick/jabber/ws/XMPPConnection.java deleted file mode 100644 index 8c187a36..00000000 --- a/src/main/java/com/juick/jabber/ws/XMPPConnection.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.juick.jabber.ws; - -import com.juick.User; -import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import com.juick.xmpp.extensions.JuickMessage; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.io.IOException; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.Iterator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -/** - * - * @author ugnich - */ -public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { - private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); - - JdbcTemplate sql; - Stream xmpp; - String xmppPassword; - MessageSerializer ms; - - public XMPPConnection(JdbcTemplate sql, String password) { - this.sql = sql; - xmppPassword = password; - ms = new MessageSerializer(); - } - - @Override - public void run() { - try { - Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); - xmpp.addChildParser(new JuickMessage()); - xmpp.addListener((Stream.StreamListener) this); - xmpp.addListener((Message.MessageListener) this); - xmpp.startParsing(); - } catch (IOException e) { - logger.log(Level.SEVERE, "XMPPConnection error", e); - } - } - - @Override - public void onStreamReady() { - logger.info("XMPP stream ready"); - } - - @Override - public void onStreamFail(String msg) { - logger.severe("XMPP stream failed: " + msg); - } - - @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); - if (jmsg != null) { - logger.info("got jmsg: " + ms.serialize(jmsg).toString()); - if (jmsg.getMID() == 0) { - int uid_to = 0; - try { - uid_to = Integer.parseInt(msg.to.Username); - } catch (Exception e) { - } - if (uid_to > 0) { - onJuickPM(uid_to, jmsg); - } - } else if (jmsg.getRID() == 0) { - onJuickMessagePost(jmsg); - } else { - onJuickMessageReply(jmsg); - } - } - } - - MessageSerializer messageSerializer = new MessageSerializer(); - - private void onJuickPM(int uid_to, com.juick.Message jmsg) { - String json = messageSerializer.serialize(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if (s.VUID == uid_to && s.MID == 0 && s.allMessages == false && s.allReplies == false) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private void onJuickMessagePost(com.juick.Message jmsg) { - String json = messageSerializer.serialize(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) - .stream().map(User::getUID).collect(Collectors.toList()); - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if (s.MID == 0 && s.allReplies == false && ((jmsg.Privacy >= 0 - && (s.allMessages || s.UID == jmsg.getUser().getUID())) || uids.contains(s.VUID))) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private void onJuickMessageReply(com.juick.Message jmsg) { - String json = messageSerializer.serialize(jmsg).toString(); - ByteBuffer bbMsg = buildTextFrame(json); - - int privacy = MessagesQueries.getMessagePrivacy(sql, jmsg.getMID()); - - synchronized (Main.clients) { - for (Iterator i = Main.clients.iterator(); i.hasNext();) { - SocketSubscribed s = i.next(); - if ((privacy >= 0 && s.allReplies) || s.MID == jmsg.getMID()) { - if (!s.sendByteBuffer(bbMsg)) { - i.remove(); - } - } - } - } - } - - private ByteBuffer buildTextFrame(String json) { - ByteBuffer jsonbytes = Charset.forName("UTF-8").encode(json); - ByteBuffer bbMsg = ByteBuffer.allocate(jsonbytes.limit() + 8); - bbMsg.put((byte) 0x81); - if (jsonbytes.limit() <= 125) { - bbMsg.put((byte) jsonbytes.limit()); - } else { - bbMsg.put((byte) 126); - bbMsg.putShort((short) jsonbytes.limit()); - } - bbMsg.put(jsonbytes); - bbMsg.flip(); - return bbMsg; - } -} diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java new file mode 100644 index 00000000..83e811a6 --- /dev/null +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -0,0 +1,134 @@ +package com.juick.ws; + +import com.juick.User; +import com.juick.server.MessagesQueries; +import com.juick.server.UserQueries; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Component +public class WebsocketComponent extends TextWebSocketHandler { + + @Inject + JdbcTemplate jdbc; + + private static final Logger logger = Logger.getLogger(WebsocketComponent.class.getName()); + final List clients = Collections.synchronizedList(new ArrayList()); + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + URI hLocation; + String hXRealIP = ""; + + hLocation = session.getUri(); + HttpHeaders headers = session.getHandshakeHeaders(); + hXRealIP = headers.getOrDefault("X-Real-IP", + Collections.singletonList(session.getRemoteAddress().toString())).get(0); + + // Auth + User visitor = new User(); + List params = URLEncodedUtils.parse(hLocation, "UTF-8"); + for (NameValuePair param : params) { + if (param.getName().equals("hash")) { + String hash = param.getValue(); + if (hash.length() == 16) { + visitor = UserQueries.getUserByHash(jdbc, hash); + } else { + try { + logger.info(String.format("wrong hash for %d from %s", visitor.getUID(), hXRealIP)); + session.close(new CloseStatus(403, "Forbidden")); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + } + break; + } + } + logger.info(String.format("user %d connected to %s from %s", visitor.getUID(), hLocation.getPath(), hXRealIP)); + + int MID = 0; + SocketSubscribed sockSubscr = null; + if (hLocation.getPath().equals("/") && visitor.getUID() > 0) { + logger.info(String.format("user %d connected", visitor.getUID())); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false); + } else if (hLocation.getPath().equals("/_all")) { + logger.info(String.format("user %d connected to legacy _all (%s)", visitor.getUID(), hLocation.getPath())); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.allMessages = true; + } else if (hLocation.getPath().equals("/_replies")) { + logger.info(String.format("user %d connected to legacy _replies (%s)", visitor.getUID(), hLocation.getPath())); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.allReplies = true; + } else if (hLocation.getPath().matches("/\\d+$")) { + try { + MID = Integer.parseInt(hLocation.getPath().substring(8)); + } catch (Exception e) { + } + if (MID > 0) { + if (MessagesQueries.canViewThread(jdbc, MID, visitor.getUID())) { + logger.info(String.format("user %d connected to legacy thread (%d) from %s", visitor.getUID(), MID, hXRealIP)); + sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); + sockSubscr.MID = MID; + } else { + try { + session.close(new CloseStatus(403, "Forbidden")); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + } + } + } + if (sockSubscr != null) { + synchronized (clients) { + clients.add(sockSubscr); + } + } + } + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { + synchronized (clients) { + clients.stream().filter(c -> c.session.equals(session)).forEach(c -> { + logger.info(String.format("session %s closed with status %s", c.clientName, status.getCode())); + clients.remove(c); + }); + } + } + class SocketSubscribed { + + WebSocketSession session; + String clientName; + User visitor; + int MID; + boolean allMessages; + boolean allReplies; + long tsConnected; + long tsLastData; + boolean legacy; + + public SocketSubscribed(WebSocketSession session, String clientName, User visitor, boolean legacy) { + this.session = session; + this.clientName = clientName; + this.visitor = visitor; + tsConnected = tsLastData = System.currentTimeMillis(); + this.legacy = legacy; + } + } +} diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java new file mode 100644 index 00000000..9ed3d0cd --- /dev/null +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -0,0 +1,142 @@ +package com.juick.ws; + +import com.juick.User; +import com.juick.json.MessageSerializer; +import com.juick.server.SubscriptionsQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.TextMessage; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.Socket; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * + * @author ugnich + */ +@Component +public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { + private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); + + JdbcTemplate sql; + Stream xmpp; + String xmppPassword; + MessageSerializer ms; + @Inject + WebsocketComponent ws; + + @Inject + public XMPPConnection(JdbcTemplate sql, String password) { + this.sql = sql; + xmppPassword = password; + ms = new MessageSerializer(); + new Thread(this).start(); + } + + @Override + public void run() { + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "ws.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((Message.MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, "XMPPConnection error", e); + } + } + + @Override + public void onStreamReady() { + logger.info("XMPP stream ready"); + } + + @Override + public void onStreamFail(String msg) { + logger.severe("XMPP stream failed: " + msg); + } + + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); + if (jmsg != null) { + logger.info("got jmsg: " + ms.serialize(jmsg).toString()); + if (jmsg.getMID() == 0) { + int uid_to = 0; + try { + uid_to = Integer.parseInt(msg.to.Username); + } catch (Exception e) { + } + if (uid_to > 0) { + onJuickPM(uid_to, jmsg); + } + } else if (jmsg.getRID() == 0) { + onJuickMessagePost(jmsg); + } else { + onJuickMessageReply(jmsg); + } + } + } + + MessageSerializer messageSerializer = new MessageSerializer(); + + private void onJuickPM(int uid_to, com.juick.Message jmsg) { + String json = messageSerializer.serialize(jmsg).toString(); + synchronized (ws.clients) { + ws.clients.stream().filter(c -> !c.legacy && c.visitor.getUID() == uid_to).forEach(c -> { + try { + logger.info("sending pm to " + c.visitor.getUID()); + c.session.sendMessage(new TextMessage(json)); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + }); + } + } + + private void onJuickMessagePost(com.juick.Message jmsg) { + String json = messageSerializer.serialize(jmsg).toString(); + List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) + .stream().map(User::getUID).collect(Collectors.toList()); + + synchronized (ws.clients) { + ws.clients.stream().filter(c -> !c.legacy && uids.contains(c.visitor.getUID())).forEach(c -> { + try { + logger.info("sending message to " + c.visitor.getUID()); + c.session.sendMessage(new TextMessage(json)); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + }); + } + } + + private void onJuickMessageReply(com.juick.Message jmsg) { + String json = messageSerializer.serialize(jmsg).toString(); + + List threadUsers = + SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()) + .stream().map(User::getUID).collect(Collectors.toList()); + synchronized (ws.clients) { + ws.clients.stream().filter(c -> !c.legacy && threadUsers.contains(c.visitor.getUID())).forEach(c -> { + try { + logger.info("sending reply to " + c.visitor.getUID()); + c.session.sendMessage(new TextMessage(json)); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + }); + } + } +} diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java new file mode 100644 index 00000000..a56e56f5 --- /dev/null +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -0,0 +1,49 @@ +package com.juick.ws.configuration; + +import com.juick.ws.WebsocketComponent; +import com.juick.ws.XMPPConnection; +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.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; + +import javax.inject.Inject; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@EnableWebSocket +@ComponentScan(basePackages = {"com.juick"}) +@PropertySource("classpath:juick.conf") +public class WebsocketConfiguration implements WebSocketConfigurer { + @Inject + Environment env; + + @Bean + WebSocketHandler wsHandler() { + return new WebsocketComponent(); + } + @Bean + JdbcTemplate jdbc() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(env.getProperty("datasource_url")); + return new JdbcTemplate(dataSource); + } + @Bean + XMPPConnection xmpp() { + return new XMPPConnection(jdbc(), env.getProperty("xmpp_password")); + } + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(wsHandler(), "/"); + } +} -- cgit v1.2.3 From a3b1c3f5ea8248ab9060bc773aa819ca4b090bee Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 11:09:57 +0300 Subject: configure webmvc --- build.gradle | 2 ++ .../ws/configuration/WebsocketConfiguration.java | 5 +++- .../ws/configuration/WebsocketInitializer.java | 33 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/juick/ws/configuration/WebsocketInitializer.java (limited to 'src') diff --git a/build.gradle b/build.gradle index 92b44bec..7f0bbe52 100644 --- a/build.gradle +++ b/build.gradle @@ -39,8 +39,10 @@ dependencies { compile server compile xmpp compile json + providedCompile 'javax.servlet:javax.servlet-api:3.1.0' def springFrameworkVersion = '4.3.0.RELEASE' compile "org.springframework:spring-jdbc:${springFrameworkVersion}" + compile "org.springframework:spring-webmvc:${springFrameworkVersion}" compile "org.springframework:spring-websocket:${springFrameworkVersion}" compile 'javax.inject:javax.inject:1' compile 'org.apache.httpcomponents:httpclient:4.5.1' diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index a56e56f5..a298d0a0 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -9,6 +9,8 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; @@ -20,10 +22,11 @@ import javax.inject.Inject; * Created by vitalyster on 28.06.2016. */ @Configuration +@EnableWebMvc @EnableWebSocket @ComponentScan(basePackages = {"com.juick"}) @PropertySource("classpath:juick.conf") -public class WebsocketConfiguration implements WebSocketConfigurer { +public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements WebSocketConfigurer { @Inject Environment env; diff --git a/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java b/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java new file mode 100644 index 00000000..89017f8b --- /dev/null +++ b/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java @@ -0,0 +1,33 @@ +package com.juick.ws.configuration; +import org.springframework.web.filter.CharacterEncodingFilter; +import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; + +import javax.servlet.Filter; + +/** + * Created by vt on 09/02/16. + */ +public class WebsocketInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + @Override + protected Class[] getRootConfigClasses() { + return new Class[] {WebsocketConfiguration.class}; + } + + @Override + protected Class[] getServletConfigClasses() { + return null; + } + + @Override + protected String[] getServletMappings() { + return new String[] { + "/" + }; + } + @Override + protected Filter[] getServletFilters() { + CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); + characterEncodingFilter.setEncoding("UTF-8"); + return new Filter[] { characterEncodingFilter}; + } +} -- cgit v1.2.3 From f2387bcc06dca0bf3368f32a99a6ec74a7381ccd Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 11:23:34 +0300 Subject: fix xmpp component wiring --- src/main/java/com/juick/ws/XMPPConnection.java | 5 ++--- src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 9ed3d0cd..5d4fbce9 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); + @Inject JdbcTemplate sql; Stream xmpp; String xmppPassword; @@ -35,9 +36,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. @Inject WebsocketComponent ws; - @Inject - public XMPPConnection(JdbcTemplate sql, String password) { - this.sql = sql; + public XMPPConnection(String password) { xmppPassword = password; ms = new MessageSerializer(); new Thread(this).start(); diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index a298d0a0..9fd75bb0 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -43,7 +43,7 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W } @Bean XMPPConnection xmpp() { - return new XMPPConnection(jdbc(), env.getProperty("xmpp_password")); + return new XMPPConnection(env.getProperty("xmpp_password")); } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { -- cgit v1.2.3 From a2faab09d3a8604ffe24dd337a3ab099f132e311 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 11:34:25 +0300 Subject: fix xmpp component wiring, take 2 --- src/main/java/com/juick/ws/XMPPConnection.java | 7 +++++-- .../java/com/juick/ws/configuration/WebsocketConfiguration.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 5d4fbce9..802271a0 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -8,6 +8,7 @@ import com.juick.xmpp.Message; import com.juick.xmpp.Stream; import com.juick.xmpp.StreamComponent; import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.web.socket.TextMessage; @@ -30,14 +31,16 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. @Inject JdbcTemplate sql; + @Inject + Environment env; Stream xmpp; String xmppPassword; MessageSerializer ms; @Inject WebsocketComponent ws; - public XMPPConnection(String password) { - xmppPassword = password; + public XMPPConnection() { + xmppPassword = env.getProperty("xmpp_password"); ms = new MessageSerializer(); new Thread(this).start(); } diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 9fd75bb0..46ef8486 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -43,7 +43,7 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W } @Bean XMPPConnection xmpp() { - return new XMPPConnection(env.getProperty("xmpp_password")); + return new XMPPConnection(); } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { -- cgit v1.2.3 From 9f2503cf924d79f00cb0956d37daf10e7a163163 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 14:40:55 +0300 Subject: gretty --- build.gradle | 17 +++++++++++++++++ src/main/java/com/juick/ws/XMPPConnection.java | 5 ++--- .../juick/ws/configuration/WebsocketConfiguration.java | 4 ---- 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/build.gradle b/build.gradle index 7f0bbe52..bea96871 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,19 @@ subprojects { mavenCentral() } } +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.akhikhl.gretty:gretty:+' + } +} + apply plugin: 'java' apply plugin: 'war' +apply plugin: 'org.akhikhl.gretty' repositories { mavenCentral() @@ -51,3 +61,10 @@ dependencies { } compileJava.options.encoding = 'UTF-8' + +gretty { + httpPort = 8080 + contextPath = '' + servletContainer = 'tomcat8' +} + diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 802271a0..8194862c 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -31,15 +31,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. @Inject JdbcTemplate sql; - @Inject - Environment env; Stream xmpp; String xmppPassword; MessageSerializer ms; @Inject WebsocketComponent ws; - public XMPPConnection() { + @Inject + public XMPPConnection(Environment env) { xmppPassword = env.getProperty("xmpp_password"); ms = new MessageSerializer(); new Thread(this).start(); diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 46ef8486..b0393038 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -41,10 +41,6 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W dataSource.setUrl(env.getProperty("datasource_url")); return new JdbcTemplate(dataSource); } - @Bean - XMPPConnection xmpp() { - return new XMPPConnection(); - } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), "/"); -- cgit v1.2.3 From f53538d230440f16948a12a34ffe587edcd0f245 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 14:53:37 +0300 Subject: fixes --- src/main/java/com/juick/ws/WebsocketComponent.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index 83e811a6..7399c298 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -97,19 +97,18 @@ public class WebsocketComponent extends TextWebSocketHandler { } } if (sockSubscr != null) { - synchronized (clients) { - clients.add(sockSubscr); - } + clients.add(sockSubscr); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { synchronized (clients) { - clients.stream().filter(c -> c.session.equals(session)).forEach(c -> { + clients.stream().filter(c -> c.session.getId().equals(session.getId())).forEach(c -> { logger.info(String.format("session %s closed with status %s", c.clientName, status.getCode())); clients.remove(c); }); } + } class SocketSubscribed { -- cgit v1.2.3 From 2d89e7882c09be7cbe82a4e5df09c23ab106be20 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 15:12:47 +0300 Subject: fix handler url mapping --- src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index b0393038..63ff12cb 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -43,6 +43,6 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(wsHandler(), "/"); + registry.addHandler(wsHandler(), "/**"); } } -- cgit v1.2.3 From c4948c141a68433b4ba9eca167622816dcd10fa1 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 15:25:08 +0300 Subject: fix session closing --- src/main/java/com/juick/ws/WebsocketComponent.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index 7399c298..8ccccb3b 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -103,9 +103,12 @@ public class WebsocketComponent extends TextWebSocketHandler { @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { synchronized (clients) { - clients.stream().filter(c -> c.session.getId().equals(session.getId())).forEach(c -> { - logger.info(String.format("session %s closed with status %s", c.clientName, status.getCode())); - clients.remove(c); + clients.removeIf(c -> { + if (c.session.equals(session)) { + logger.info(String.format("session from %s closed with status %d", c.clientName, status.getCode())); + return true; + } + return false; }); } -- cgit v1.2.3 From 0f907b3973e851970882a00da123f6b7a6a359fb Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 16:08:16 +0300 Subject: send messages to legacy urls --- src/main/java/com/juick/ws/XMPPConnection.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 8194862c..a6f4e2cb 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -120,6 +120,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); + ws.clients.stream().filter(c -> c.legacy && c.allMessages).forEach(c -> { + try { + logger.info("sending message to legacy client " + c.visitor.getUID()); + c.session.sendMessage(new TextMessage(json)); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + }); } } @@ -138,6 +146,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); + ws.clients.stream().filter(c -> (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMID())).forEach(c -> { + try { + logger.info("sending reply to legacy client " + c.visitor.getUID()); + c.session.sendMessage(new TextMessage(json)); + } catch (IOException e) { + logger.log(Level.WARNING, "ws error", e); + } + }); } } } -- cgit v1.2.3 From e8621033c919f0920a52463c01293349752aedec Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 28 Jun 2016 16:47:52 +0300 Subject: destroy xmpp service --- src/main/java/com/juick/ws/XMPPConnection.java | 6 +++++- .../ws/configuration/WebsocketConfiguration.java | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index a6f4e2cb..9ac22d45 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -17,6 +17,8 @@ import javax.inject.Inject; import java.io.IOException; import java.net.Socket; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -36,12 +38,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. MessageSerializer ms; @Inject WebsocketComponent ws; + @Inject + ExecutorService xmppThreadService; @Inject public XMPPConnection(Environment env) { xmppPassword = env.getProperty("xmpp_password"); ms = new MessageSerializer(); - new Thread(this).start(); + xmppThreadService.submit(this); } @Override diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 63ff12cb..27bfcf36 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -2,6 +2,7 @@ package com.juick.ws.configuration; import com.juick.ws.WebsocketComponent; import com.juick.ws.XMPPConnection; +import org.springframework.beans.factory.DisposableBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -17,6 +18,8 @@ import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Created by vitalyster on 28.06.2016. @@ -41,8 +44,25 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W dataSource.setUrl(env.getProperty("datasource_url")); return new JdbcTemplate(dataSource); } + @Bean + ExecutorService xmppThreadService() { + return Executors.newSingleThreadExecutor(); + } + @Bean + DestroyBean destroyBean() { + return new DestroyBean(); + } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), "/**"); } + class DestroyBean implements DisposableBean { + + @Inject + ExecutorService xmppThreadService; + @Override + public void destroy() throws Exception { + xmppThreadService.shutdownNow(); + } + } } -- cgit v1.2.3 From d131a99cb1641a4ea0c61b4caaee50a50c1ffd42 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 09:25:16 +0300 Subject: xmpp fix, next --- src/main/java/com/juick/ws/XMPPConnection.java | 3 +-- src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 9ac22d45..3122e715 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -38,8 +38,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. MessageSerializer ms; @Inject WebsocketComponent ws; - @Inject - ExecutorService xmppThreadService; + ExecutorService xmppThreadService = Executors.newSingleThreadExecutor(); @Inject public XMPPConnection(Environment env) { diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 27bfcf36..0a1b83d2 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -45,10 +45,6 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W return new JdbcTemplate(dataSource); } @Bean - ExecutorService xmppThreadService() { - return Executors.newSingleThreadExecutor(); - } - @Bean DestroyBean destroyBean() { return new DestroyBean(); } -- cgit v1.2.3 From 13642efd8bd1d70264392c1ee7b87f9b0c979066 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 09:41:02 +0300 Subject: xmpp fix, next --- .../juick/ws/configuration/WebsocketConfiguration.java | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 0a1b83d2..ecf2de46 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -1,8 +1,6 @@ package com.juick.ws.configuration; import com.juick.ws.WebsocketComponent; -import com.juick.ws.XMPPConnection; -import org.springframework.beans.factory.DisposableBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -18,8 +16,6 @@ import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import javax.inject.Inject; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * Created by vitalyster on 28.06.2016. @@ -44,21 +40,8 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W dataSource.setUrl(env.getProperty("datasource_url")); return new JdbcTemplate(dataSource); } - @Bean - DestroyBean destroyBean() { - return new DestroyBean(); - } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), "/**"); } - class DestroyBean implements DisposableBean { - - @Inject - ExecutorService xmppThreadService; - @Override - public void destroy() throws Exception { - xmppThreadService.shutdownNow(); - } - } } -- cgit v1.2.3 From 2e52796f2199e40deddff099830e40d1844f7793 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 10:50:13 +0300 Subject: allow * origins --- src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index ecf2de46..0bb293f4 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -42,6 +42,6 @@ public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements W } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(wsHandler(), "/**"); + registry.addHandler(wsHandler(), "/**").setAllowedOrigins("*"); } } -- cgit v1.2.3 From 614384564482c8a35a567dd092cf5674f853ebd3 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 11:04:41 +0300 Subject: ws debug --- src/main/java/com/juick/ws/XMPPConnection.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 3122e715..65008906 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -113,8 +113,10 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. String json = messageSerializer.serialize(jmsg).toString(); List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) .stream().map(User::getUID).collect(Collectors.toList()); - + logger.info(String.format("%d users subscribed to %d", uids.size(), jmsg.getUser().getUID())); synchronized (ws.clients) { + Long legacycount = ws.clients.stream().filter(c -> c.legacy && c.allMessages).count(); + logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); ws.clients.stream().filter(c -> !c.legacy && uids.contains(c.visitor.getUID())).forEach(c -> { try { logger.info("sending message to " + c.visitor.getUID()); @@ -136,11 +138,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickMessageReply(com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); - + logger.info("got reply: " + json); List threadUsers = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()) .stream().map(User::getUID).collect(Collectors.toList()); + logger.info(String.format("%d users subscribed to %d", threadUsers.size(), jmsg.getMID())); synchronized (ws.clients) { + Long legacycount = ws.clients.stream().filter(c -> c.legacy && c.allReplies).count(); + logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); ws.clients.stream().filter(c -> !c.legacy && threadUsers.contains(c.visitor.getUID())).forEach(c -> { try { logger.info("sending reply to " + c.visitor.getUID()); -- cgit v1.2.3 From 38040c8f5faaab4fe6d7055ed010533aa1860b07 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 11:26:33 +0300 Subject: ws injection fix --- src/main/java/com/juick/ws/XMPPConnection.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index 65008906..ae70d956 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -31,17 +31,17 @@ import java.util.stream.Collectors; public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); - @Inject JdbcTemplate sql; Stream xmpp; String xmppPassword; MessageSerializer ms; - @Inject WebsocketComponent ws; ExecutorService xmppThreadService = Executors.newSingleThreadExecutor(); @Inject - public XMPPConnection(Environment env) { + public XMPPConnection(Environment env, WebsocketComponent ws, JdbcTemplate sql) { + this.sql = sql; + this.ws = ws; xmppPassword = env.getProperty("xmpp_password"); ms = new MessageSerializer(); xmppThreadService.submit(this); -- cgit v1.2.3 From dbceb8cb379c44366c1014d282a73457762aabe1 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 11:46:44 +0300 Subject: ws injection fix, next --- src/main/java/com/juick/ws/XMPPConnection.java | 33 +++++++++------------- .../ws/configuration/WebsocketConfiguration.java | 14 +++++++-- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index ae70d956..d5cb4eba 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -17,8 +17,6 @@ import javax.inject.Inject; import java.io.IOException; import java.net.Socket; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -30,21 +28,18 @@ import java.util.stream.Collectors; @Component public class XMPPConnection implements Runnable, Stream.StreamListener, Message.MessageListener { private static final Logger logger = Logger.getLogger(XMPPConnection.class.getName()); - + @Inject JdbcTemplate sql; Stream xmpp; String xmppPassword; MessageSerializer ms; - WebsocketComponent ws; - ExecutorService xmppThreadService = Executors.newSingleThreadExecutor(); + WebsocketComponent wsHandler; @Inject - public XMPPConnection(Environment env, WebsocketComponent ws, JdbcTemplate sql) { - this.sql = sql; - this.ws = ws; + public XMPPConnection(Environment env, WebsocketComponent wsHandler) { + this.wsHandler = wsHandler; xmppPassword = env.getProperty("xmpp_password"); ms = new MessageSerializer(); - xmppThreadService.submit(this); } @Override @@ -97,8 +92,8 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickPM(int uid_to, com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); - synchronized (ws.clients) { - ws.clients.stream().filter(c -> !c.legacy && c.visitor.getUID() == uid_to).forEach(c -> { + synchronized (wsHandler.clients) { + wsHandler.clients.stream().filter(c -> !c.legacy && c.visitor.getUID() == uid_to).forEach(c -> { try { logger.info("sending pm to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -114,10 +109,10 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) .stream().map(User::getUID).collect(Collectors.toList()); logger.info(String.format("%d users subscribed to %d", uids.size(), jmsg.getUser().getUID())); - synchronized (ws.clients) { - Long legacycount = ws.clients.stream().filter(c -> c.legacy && c.allMessages).count(); + synchronized (wsHandler.clients) { + Long legacycount = wsHandler.clients.stream().filter(c -> c.legacy && c.allMessages).count(); logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); - ws.clients.stream().filter(c -> !c.legacy && uids.contains(c.visitor.getUID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> !c.legacy && uids.contains(c.visitor.getUID())).forEach(c -> { try { logger.info("sending message to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -125,7 +120,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); - ws.clients.stream().filter(c -> c.legacy && c.allMessages).forEach(c -> { + wsHandler.clients.stream().filter(c -> c.legacy && c.allMessages).forEach(c -> { try { logger.info("sending message to legacy client " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -143,10 +138,10 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()) .stream().map(User::getUID).collect(Collectors.toList()); logger.info(String.format("%d users subscribed to %d", threadUsers.size(), jmsg.getMID())); - synchronized (ws.clients) { - Long legacycount = ws.clients.stream().filter(c -> c.legacy && c.allReplies).count(); + synchronized (wsHandler.clients) { + Long legacycount = wsHandler.clients.stream().filter(c -> c.legacy && c.allReplies).count(); logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); - ws.clients.stream().filter(c -> !c.legacy && threadUsers.contains(c.visitor.getUID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> !c.legacy && threadUsers.contains(c.visitor.getUID())).forEach(c -> { try { logger.info("sending reply to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -154,7 +149,7 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); - ws.clients.stream().filter(c -> (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMID())).forEach(c -> { try { logger.info("sending reply to legacy client " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); diff --git a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java index 0bb293f4..223f8d63 100644 --- a/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ b/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java @@ -1,6 +1,7 @@ package com.juick.ws.configuration; import com.juick.ws.WebsocketComponent; +import com.juick.ws.XMPPConnection; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -10,12 +11,13 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * Created by vitalyster on 28.06.2016. @@ -28,12 +30,18 @@ import javax.inject.Inject; public class WebsocketConfiguration extends WebMvcConfigurerAdapter implements WebSocketConfigurer { @Inject Environment env; - + ExecutorService xmppThread = Executors.newSingleThreadExecutor(); @Bean - WebSocketHandler wsHandler() { + WebsocketComponent wsHandler() { return new WebsocketComponent(); } @Bean + XMPPConnection xmpp() { + XMPPConnection xmpp = new XMPPConnection(env, wsHandler()); + xmppThread.submit(xmpp); + return xmpp; + } + @Bean JdbcTemplate jdbc() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); -- cgit v1.2.3 From e3fdbbcd833e4be70b4e62e541cfd03b2f4d129a Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 11:57:55 +0300 Subject: update deps --- .gitignore | 1 + deps/com.juick.xmpp | 2 +- src/main/java/com/juick/ws/XMPPConnection.java | 4 +- .../com/juick/xmpp/extensions/JuickMessage.java | 183 +++++++++++++++++++++ .../java/com/juick/xmpp/extensions/JuickUser.java | 75 +++++++++ 5 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/juick/xmpp/extensions/JuickMessage.java create mode 100644 src/main/java/com/juick/xmpp/extensions/JuickUser.java (limited to 'src') diff --git a/.gitignore b/.gitignore index e3baf212..c26bb4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /dist/ /build/ .gradle/* +.idea/** \ No newline at end of file diff --git a/deps/com.juick.xmpp b/deps/com.juick.xmpp index a096ecc6..1d87d06b 160000 --- a/deps/com.juick.xmpp +++ b/deps/com.juick.xmpp @@ -1 +1 @@ -Subproject commit a096ecc6d011bf0f6c738b10d7d0a7319ffcf2dc +Subproject commit 1d87d06b72011cb26813cf084102a02e9dcd96e9 diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index d5cb4eba..d0bdc42f 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -62,8 +62,8 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. } @Override - public void onStreamFail(String msg) { - logger.severe("XMPP stream failed: " + msg); + public void onStreamFail(Exception ex) { + logger.log(Level.SEVERE, "XMPP stream failed", ex); } @Override diff --git a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java new file mode 100644 index 00000000..a7fd1e35 --- /dev/null +++ b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +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; + +/** + * + * @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")); + } + + public JuickMessage(com.juick.Message msg) { + super(msg); + 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.ReplyTo = Integer.parseInt(sReplyTo); + } + final String sPrivacy = parser.getAttributeValue(null, "privacy"); + if (sPrivacy != null) { + jmsg.Privacy = 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.setDate(df.parse(parser.getAttributeValue(null, "ts"))); + jmsg.AttachmentType = 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.Tags.add(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 (ReplyTo > 0) { + ret += " replyto=\"" + ReplyTo + "\""; + } + ret += " privacy=\"" + Privacy + "\""; + if (FriendsOnly) { + ret += " friendsonly=\"1\""; + } + if (ReadOnly) { + ret += " readonly=\"1\""; + } + if (getDate() != null) { + ret += " ts=\"" + df.format(getDate()) + "\""; + } + if (AttachmentType != null) { + ret += " attach=\"" + AttachmentType + "\""; + } + ret += ">"; + if (getUser() != null) { + ret += JuickUser.toString(getUser()); + } + if (getText() != null) { + ret += "" + XmlUtils.escape(getText()) + ""; + } + if (!Tags.isEmpty()) { + for (String Tag : Tags) { + ret += "" + XmlUtils.escape(Tag) + ""; + } + } + ret += ""; + + 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; + } +} diff --git a/src/main/java/com/juick/xmpp/extensions/JuickUser.java b/src/main/java/com/juick/xmpp/extensions/JuickUser.java new file mode 100644 index 00000000..edc6749a --- /dev/null +++ b/src/main/java/com/juick/xmpp/extensions/JuickUser.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ +package com.juick.xmpp.extensions; + +import com.juick.xmpp.utils.XmlUtils; +import com.juick.xmpp.*; +import java.io.IOException; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * + * @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() { + } + + public JuickUser(com.juick.User user) { + super(user); + } + + @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.setUName(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.getUName() != null && user.getUName().length() > 0) { + str += " uname='" + XmlUtils.escape(user.getUName()) + "'"; + } + str += "/>"; + return str; + } + + @Override + public String toString() { + return toString(this); + } +} -- cgit v1.2.3 From fb4aa5389924cb11fa8bf6489baf29ca8d7a6814 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 12:06:48 +0300 Subject: fix parsing date --- src/main/java/com/juick/xmpp/extensions/JuickMessage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java index a7fd1e35..ac45f3d2 100644 --- a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java +++ b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java @@ -83,7 +83,10 @@ public class JuickMessage extends com.juick.Message implements StanzaChild { jmsg.ReadOnly = true; } - jmsg.setDate(df.parse(parser.getAttributeValue(null, "ts"))); + String timestampString = parser.getAttributeValue(null, "ts"); + if (timestampString != null) { + jmsg.setDate(df.parse(timestampString)); + } jmsg.AttachmentType = parser.getAttributeValue(null, "attach"); while (parser.next() == XmlPullParser.START_TAG) { -- cgit v1.2.3 From 1f82412b053dce31d01e0c384bd18ad3b9e101ec Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 12:19:14 +0300 Subject: allow anonymous ws --- src/main/java/com/juick/ws/WebsocketComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index 8ccccb3b..d38f7b69 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -66,7 +66,7 @@ public class WebsocketComponent extends TextWebSocketHandler { int MID = 0; SocketSubscribed sockSubscr = null; - if (hLocation.getPath().equals("/") && visitor.getUID() > 0) { + if (hLocation.getPath().equals("/")) { logger.info(String.format("user %d connected", visitor.getUID())); sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false); } else if (hLocation.getPath().equals("/_all")) { -- cgit v1.2.3 From e0d4d97777962c6f1752d7772b57392ec53dfc21 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 12:29:38 +0300 Subject: allow anonymous replies --- src/main/java/com/juick/ws/WebsocketComponent.java | 6 +++++- src/main/java/com/juick/ws/XMPPConnection.java | 25 ++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index d38f7b69..daf17753 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -97,7 +97,10 @@ public class WebsocketComponent extends TextWebSocketHandler { } } if (sockSubscr != null) { - clients.add(sockSubscr); + synchronized (clients) { + clients.add(sockSubscr); + logger.info(clients.size() + " clients connected"); + } } } @Override @@ -110,6 +113,7 @@ public class WebsocketComponent extends TextWebSocketHandler { } return false; }); + logger.info(clients.size() + " clients connected"); } } diff --git a/src/main/java/com/juick/ws/XMPPConnection.java b/src/main/java/com/juick/ws/XMPPConnection.java index d0bdc42f..4a80eec5 100644 --- a/src/main/java/com/juick/ws/XMPPConnection.java +++ b/src/main/java/com/juick/ws/XMPPConnection.java @@ -108,11 +108,11 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. String json = messageSerializer.serialize(jmsg).toString(); List uids = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()) .stream().map(User::getUID).collect(Collectors.toList()); - logger.info(String.format("%d users subscribed to %d", uids.size(), jmsg.getUser().getUID())); synchronized (wsHandler.clients) { - Long legacycount = wsHandler.clients.stream().filter(c -> c.legacy && c.allMessages).count(); - logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); - wsHandler.clients.stream().filter(c -> !c.legacy && uids.contains(c.visitor.getUID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> + (!c.legacy && c.visitor.getUID() == 0) // anonymous users + || (!c.legacy && uids.contains(c.visitor.getUID()))) // subscriptions + .forEach(c -> { try { logger.info("sending message to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -120,7 +120,9 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); - wsHandler.clients.stream().filter(c -> c.legacy && c.allMessages).forEach(c -> { + wsHandler.clients.stream().filter(c -> + c.legacy && c.allMessages) // legacy all posts + .forEach(c -> { try { logger.info("sending message to legacy client " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -133,15 +135,14 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. private void onJuickMessageReply(com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); - logger.info("got reply: " + json); List threadUsers = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()) .stream().map(User::getUID).collect(Collectors.toList()); - logger.info(String.format("%d users subscribed to %d", threadUsers.size(), jmsg.getMID())); synchronized (wsHandler.clients) { - Long legacycount = wsHandler.clients.stream().filter(c -> c.legacy && c.allReplies).count(); - logger.info(String.format("%d legacy users watched %d", legacycount, jmsg.getMID())); - wsHandler.clients.stream().filter(c -> !c.legacy && threadUsers.contains(c.visitor.getUID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> + (!c.legacy && c.visitor.getUID() == 0) // anonymous users + || (!c.legacy && threadUsers.contains(c.visitor.getUID()))) // subscriptions + .forEach(c -> { try { logger.info("sending reply to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -149,7 +150,9 @@ public class XMPPConnection implements Runnable, Stream.StreamListener, Message. logger.log(Level.WARNING, "ws error", e); } }); - wsHandler.clients.stream().filter(c -> (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMID())).forEach(c -> { + wsHandler.clients.stream().filter(c -> + (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMID())) // legacy replies + .forEach(c -> { try { logger.info("sending reply to legacy client " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); -- cgit v1.2.3 From 4bc9b403ff3cac9ee115a788b18cffff520a9e3b Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 12:33:46 +0300 Subject: fix session close --- src/main/java/com/juick/ws/WebsocketComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index daf17753..5dfe8237 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -107,7 +107,7 @@ public class WebsocketComponent extends TextWebSocketHandler { public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { synchronized (clients) { clients.removeIf(c -> { - if (c.session.equals(session)) { + if (c.session.getId().equals(session.getId())) { logger.info(String.format("session from %s closed with status %d", c.clientName, status.getCode())); return true; } -- cgit v1.2.3 From d3a850e35880ae5493b3a197e0e8f58da99350f8 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 30 Jun 2016 13:08:52 +0300 Subject: fix thread subscription --- src/main/java/com/juick/ws/WebsocketComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/com/juick/ws/WebsocketComponent.java b/src/main/java/com/juick/ws/WebsocketComponent.java index 5dfe8237..e87b96a5 100644 --- a/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/src/main/java/com/juick/ws/WebsocketComponent.java @@ -79,7 +79,7 @@ public class WebsocketComponent extends TextWebSocketHandler { sockSubscr.allReplies = true; } else if (hLocation.getPath().matches("/\\d+$")) { try { - MID = Integer.parseInt(hLocation.getPath().substring(8)); + MID = Integer.parseInt(hLocation.getPath().substring(1)); } catch (Exception e) { } if (MID > 0) { -- cgit v1.2.3