aboutsummaryrefslogtreecommitdiff
path: root/src/com/juick/jabber/ws/WSData.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/juick/jabber/ws/WSData.java')
-rw-r--r--src/com/juick/jabber/ws/WSData.java233
1 files changed, 233 insertions, 0 deletions
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<SelectionKey> 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 + "'");
+ }
+ }
+}