/** * license (MIT) Copyright Nubisa Inc. 2014 */ package jxm; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.NoRouteToHostException; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.Date; public class PListen extends Thread { public static final String ENCODING = "UTF8"; public final static String CreateText(Client client, String mess, boolean in) { if (client.getEncrypted()) { if(in){ mess = new String(Base64.decode(mess)); } } if (in) { // server sends unescaped // mess = unescape(mess); mess = mess.replace("\r", "\\r").replace("\n", "\\n"); } else { mess = mess.replace("\r", "\\r").replace("\n", "\\n"); // escape() is not really compatible //mess = escape(mess); mess = encodeUriComponent(mess); } if (client.getEncrypted()) { if(!in){ mess = Base64.encode(mess.getBytes()); } } return mess; } public static String escape(String str1) { final String str2 = "0123456789ABCDEF"; final int length = str1.length(); StringBuilder stringBuilder = new StringBuilder(length * 2); int index = -1; while (++index < length) { char ch = str1.charAt(index); int num = (int) ch; if ((97 > num || num > 122) && (65 > num || num > 90) && ((48 > num || num > 57) && ((int) ch != 64 && (int) ch != 42)) && ((int) ch != 95 && (int) ch != 43 && ((int) ch != 45 && (int) ch != 46) && (int) ch != 47)) { stringBuilder.append('%'); if (num < 256) { stringBuilder.append(str2.charAt(num / 16)); ch = str2.charAt(num % 16); } else { stringBuilder.append('u'); stringBuilder.append(str2.charAt((num >> 12) % 16)); stringBuilder.append(str2.charAt((num >> 8) % 16)); stringBuilder.append(str2.charAt((num >> 4) % 16)); ch = str2.charAt(num % 16); } } stringBuilder.append(ch); } return stringBuilder.toString(); } private static int HexDigit(char c) { if ((int) c >= 48 && (int) c <= 57) return (int) c - 48; if ((int) c >= 65 && (int) c <= 70) return 10 + (int) c - 65; if ((int) c >= 97 && (int) c <= 102) return 10 + (int) c - 97; return -1; } public static String encodeUriComponent(String string) { String result = null; try { result = URLEncoder.encode(string, "UTF-8"); String[] src = { "\\+", "\\%21", "\\%27", "\\%28", "\\%29", "\\%7E" }; String[] dst = { "%20", "!", "'", "(", ")", "~" }; int ln = src.length; for (int i = 0; i < ln; i++) { result = result.replaceAll(src[i], dst[i]); } } catch (Exception ex) { result = string; } return result; } public static String unescape(String str) { int length = str.length(); StringBuilder stringBuilder = new StringBuilder(length); int index = -1; while (++index < length) { char ch = str.charAt(index); if ((int) ch == 37) { int num1; int num2; int num3; int num4; if (index + 5 < length && (int) str.charAt(index + 1) == 117 && ((num1 = HexDigit(str.charAt(index + 2))) != -1 && (num2 = HexDigit(str.charAt(index + 3))) != -1) && ((num3 = HexDigit(str.charAt(index + 4))) != -1 && (num4 = HexDigit(str.charAt(index + 5))) != -1)) { ch = (char) ((num1 << 12) + (num2 << 8) + (num3 << 4) + num4); index += 5; } else { int num5; int num6; if (index + 2 < length && (num5 = HexDigit(str.charAt(index + 1))) != -1 && (num6 = HexDigit(str.charAt(index + 2))) != -1) { ch = (char) ((num5 << 4) + num6); index += 2; } } } stringBuilder.append(ch); } return stringBuilder.toString(); } private InputStream activeStream = null; private Client client; private String errorMessage = ""; public boolean exit = false; private boolean isDisposing = false; public PEvents notifier = null; private Socketer socketConn = null; private boolean socketConnected = false; private boolean socketConnecting = false; public boolean socketDisabled = false; public PListen(final String name, final Client pClient) { super(pClient.GetClientId() + name); client = pClient; } public void Dispose() { client.fireLog("Disposing Listener", LogLevel.Critical); exit = true; if (socketConnected || socketConnecting) { try { socketConn.close(); } catch (Exception e) { } } socketConnected = false; socketDisabled = false; socketConnecting = false; isDisposing = true; try { activeStream.close(); } catch (Exception e) { } } public String downloadString(final String connStr) { InputStream stream = null; try { URL url = new URL(connStr); stream = url.openStream(); activeStream = stream; } catch (Exception e) { errorMessage = e.getClass().getName() + " : " + e.getMessage(); fire(null, true); client.goClose(); return null; } InputStreamReader isr = new InputStreamReader(stream); BufferedReader rs = new BufferedReader(isr); StringBuilder str = new StringBuilder(); try { String st; while ((st = rs.readLine()) != null) { byte[] data = st.getBytes(); str.append(new String(data, 0, data.length, ENCODING)); } stream.close(); } catch (Exception ex) { if (isDisposing) return ""; errorMessage = "Error during download:" + ex.getMessage(); fire(null, true); return null; } return str.toString(); } private static String uid = null; private static long uidc = 0; public static String getUID(boolean reset) { //TODO: update here from server side if (uid == null || reset) uid = java.util.UUID.randomUUID().toString().replace("-","") + (uidc++); return uid; } public String downloadString(final String connStr, final String params) { URL url; URLConnection conn; try { url = new URL(connStr); conn = url.openConnection(); conn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); writer.write(params); writer.flush(); InputStreamReader irs = new InputStreamReader(conn.getInputStream()); BufferedReader in = new BufferedReader(irs); StringBuilder str = new StringBuilder(); String st; while ((st = in.readLine()) != null) { byte[] data = st.getBytes(); str.append(new String(data, 0, data.length, ENCODING)); } writer.close(); in.close(); return str.toString(); } catch (NoRouteToHostException ne) { errorMessage = "connection to host is lost"; fire(null, true); notifier.UpdateIsConnected(false); return null; } catch (Exception e) { errorMessage = e.getClass().getName() + " : " + e.getMessage(); fire(null, true); return null; } } private void fire(String msg, boolean isError) { if (notifier != null) { if (isError) { notifier.ErrorReceived(errorMessage); } else { notifier.MessageReceived(msg); } } } public String getErrorMessage() { return errorMessage; } public boolean getSocketConnected() { return socketConnected; } public void ListenSocket() throws Exception { if (socketConnected || socketConnecting) return; int port = client.getSocketPort(); if(client.getSecuredKey() == null) return; String url = "ws://" + client.getSocketURL() + ":" + port + "/" + client.getApplicationPath()+ "/jx?c=" + client.GetClientId() +"&sid="+client.getSecuredKey()+"&de=1"; URI uri = new URI(url); socketConn = new Socketer(uri); socketConnecting = true; socketConn.events = new SocketEvents() { @Override public void close() { socketConnected = false; socketConnecting = false; client.fireLog("Socket Closed", LogLevel.Critical); client.goClose(); } @Override public void error(Exception e) { errorMessage = "SocketError:" + e.getMessage(); fire(null, true); //client.Call("71n9", null, null); } @Override public void message(String message) { client.fireLog("Socket Received:" + message, LogLevel.Informative); message = PListen.CreateText(client, message, true); fire(message, false); } @Override public void open() { socketConnected = true; socketConnecting = false; client.fireLog("Socket Connected", LogLevel.Informative); if(client.Events!=null){ client.Events.OnConnect(client); } } }; socketConn.connect(); } public void Reload() { client.fireLog("Reloading Client (not suggested! instead recreate DesktopClient instance)", LogLevel.Critical); isDisposing = false; } @Override public void run() { isDisposing = false; exit = false; while (!exit) { try { Thread.sleep(25); } catch (Exception e) { break; } if (this.socketConnected || this.isDisposing) { continue; } boolean hasListen = false; if (client.getSocketURL() != null && !socketDisabled && !isDisposing) { hasListen = true; } else{ String connStr = client.getSocketURL().concat(":" + client.getSocketPort() + "/" + client.getApplicationPath() + "/jx?"); if(client.getIsSecure()){ connStr = "https://" + connStr; }else{ connStr = "http://" + connStr; } if(client.getSecuredKey() == null) return; connStr = connStr.concat("c=" + client.GetClientId() + "&de=1&sid="+client.getSecuredKey()+"&co=" + ((Long) (new Date().getTime())).toString()); //client.fireLog("HTTP DownloadStart "+connStr, LogLevel.Informative); String message = downloadString(connStr); // client.fireLog("HTTP DownladEnd", LogLevel.Informative); if (message == null) { continue; } client.fireLog("HTTP Received:" + message, LogLevel.Informative); if (isDisposing) { continue; } if (!client.getIsConnected() && !isDisposing) { continue; } String[] messages; if (client.getEncrypted()) { messages = message.split(";"); final int ln = messages.length; for (int i = 0; i < ln; i++) { if (messages[i] != null) { try { messages[i] = PListen.CreateText(client, messages[i].trim(), true); } catch (Exception e) { } } } } else { messages = new String[1]; try { messages[0] = PListen.CreateText(client, message.trim(), true); } catch (Exception e) { } } for (String mess : messages) { if (mess == null) continue; if (!hasListen) { hasListen = mess.contains("jxcore.Listen"); } fire(mess, false); } } if (hasListen) { if (client.getSocketURL() != null && !socketDisabled && !isDisposing) { try { if (!socketEnabled()) this.ListenSocket(); } catch (Exception e) { socketDisabled = true; errorMessage = "Socket Connection Error (" + e.getMessage() + ") Socket connection disabled."; fire(null, true); } } continue; } else { if (socketConnected || socketConnecting){}else{ notifier.UpdateIsConnected(false); break; } } } } public boolean socketEnabled() { return (this.socketConnected); } public boolean socketSend(String msg) { if (socketConn == null) return false; try { socketConn.send(msg); } catch (Exception e) { errorMessage = "socketSend Error:" + e.getMessage(); fire(null, true); return false; } return true; } }