Back to project page android_smsoverxmpp.
The source code is released under:
GNU General Public License
If you think the Android project android_smsoverxmpp listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.smorra.smsoverxmpp; //from www . j a va 2 s .c o m import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.util.Base64; import android.widget.Toast; import com.smorra.asyncsocket.TcpServerClient; import com.smorra.asyncsocket.TcpServerClientCallback; public class Client extends Handler implements TcpServerClientCallback { TcpServerClient tsc; byte[] buffer = new byte[0]; byte[] decodedBuffer = new byte[0]; boolean isClosed; Server server; boolean isAuthed = false; ArrayList<String> usedIds = new ArrayList<String>(); boolean isStreamClosed; enum State { BEFORE_HANDSHAKE, HANDSHAKING, AFTER_HANDSHAKE } State state = State.BEFORE_HANDSHAKE; String to = null; boolean isSslStreamRead = false; SSLEngine engine; String bindId; Context context; public Client(TcpServerClient tsc, Server server, Context context) throws IOException, InterruptedException { this.server = server; this.tsc = tsc; this.context = context; tsc.setCallbackInterface(this); // tsc.write("<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='jabber.b4ne.de' id='6d5778d5-39ad-4d48-b01d-4a6bb52453da' xml:lang='en' xmlns='jabber:client'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>SCRAM-SHA-1</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>".getBytes()); } public void close() throws IOException { tsc.close(); } @Override public void onRead(TcpServerClient tc, byte[] bytes) { try { System.out.println("onread " + new String(bytes, "UTF-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } byte[] new_buffer = new byte[bytes.length + buffer.length]; System.arraycopy(buffer, 0, new_buffer, 0, buffer.length); System.arraycopy(bytes, 0, new_buffer, buffer.length, bytes.length); buffer = new_buffer; while (state == State.BEFORE_HANDSHAKE) { try { System.out.println("BUFFER IS " + new String(buffer, "UTF-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } int i; for (i = 0; i < buffer.length; i++) { if (buffer[i] == '>') { byte[] cmd = new byte[i + 1]; System.arraycopy(buffer, 0, cmd, 0, i + 1); new_buffer = new byte[buffer.length - i - 1]; System.arraycopy(buffer, i + 1, new_buffer, 0, new_buffer.length); buffer = new_buffer; try { System.out.println("HANDLESTREAM BEGIN \"" + new String(cmd, "UTF-8") + "\""); handleStreamBegin(cmd); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } } if (i == buffer.length) break; } boolean enoughBytesForUnwrap = true; while (state == State.HANDSHAKING) { if (engine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { if (!enoughBytesForUnwrap) break; int bufSize = engine.getSession().getApplicationBufferSize(); System.out.println("ENTER LOOP"); while (true) { ByteBuffer dst = ByteBuffer.allocate(bufSize); ByteBuffer src = ByteBuffer.wrap(buffer); SSLEngineResult res = null; try { res = engine.unwrap(src, dst); } catch (Exception e) { e.printStackTrace(); } // process produced bytes byte[] decoded = new byte[res.bytesProduced()]; System.arraycopy(dst.array(), 0, decoded, 0, decoded.length); try { System.out.println("CALLIGN FROM LINE XYZ2"); doReadDecoded(decoded); } catch (Exception e) { e.printStackTrace(); } new_buffer = new byte[src.capacity() - src.position()]; System.arraycopy(buffer, src.position(), new_buffer, 0, new_buffer.length); buffer = new_buffer; if (res.getStatus() == Status.BUFFER_OVERFLOW) { System.out.println("BUFFER OVERFLOW"); bufSize += 1024; } else if (res.getStatus() == Status.BUFFER_UNDERFLOW || res.getStatus() == Status.CLOSED) { System.out.println("BUFFER UNDERFLOW or CLOSED"); enoughBytesForUnwrap = false; break; } else if (res.getStatus() == Status.OK) { // do it again if (res.bytesConsumed() == 0) { enoughBytesForUnwrap = false; break; } bufSize = engine.getSession().getApplicationBufferSize(); } } } else if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { Runnable runnable = engine.getDelegatedTask(); runnable.run(); } else if (engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { int bufSize = engine.getSession().getPacketBufferSize(); while (engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { System.out.println("IN OOOOOOP"); ByteBuffer src = ByteBuffer.allocate(0); ByteBuffer dst = ByteBuffer.allocate(bufSize); SSLEngineResult res = null; try { res = engine.wrap(src, dst); } catch (SSLException e) { // TODO Auto-generated catch block e.printStackTrace(); Toast.makeText(context, "Client incompatible. Please try a different client.", Toast.LENGTH_LONG).show(); try { handleStreamClose(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return; } // process produced bytes byte[] produced = new byte[res.bytesProduced()]; System.arraycopy(dst.array(), 0, produced, 0, produced.length); try { write(produced); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("RESULT IS " + res.getStatus()); if (res.getStatus() == Status.BUFFER_OVERFLOW) { bufSize += 1024; } else if (res.getStatus() == Status.OK) { bufSize = engine.getSession().getApplicationBufferSize(); } else if (res.getStatus() == Status.CLOSED) { System.out.println("WHAT TO DO?"); } else if (res.getStatus() == Status.BUFFER_UNDERFLOW) { System.out.println("BUFFER_UNDERFLOW while wrapping?"); } } } else { state = State.AFTER_HANDSHAKE; System.out.println("UNKNOWN STATE " + engine.getHandshakeStatus()); break; } } if (state == State.AFTER_HANDSHAKE) { int bufSize = engine.getSession().getApplicationBufferSize(); while (true) { System.out.println("IN LOOP AFTER"); ByteBuffer dst = ByteBuffer.allocate(bufSize); ByteBuffer src = ByteBuffer.wrap(buffer); SSLEngineResult res = null; try { res = engine.unwrap(src, dst); } catch (SSLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // process produced bytes byte[] decoded = new byte[res.bytesProduced()]; System.arraycopy(dst.array(), 0, decoded, 0, decoded.length); try { System.out.println("CALLIGN FROM LINE XY"); doReadDecoded(decoded); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // refresh buffer new_buffer = new byte[buffer.length - res.bytesConsumed()]; System.arraycopy(buffer, res.bytesConsumed(), new_buffer, 0, new_buffer.length); buffer = new_buffer; if (res.getStatus() == Status.BUFFER_OVERFLOW) { bufSize += 1024; } else if (res.getStatus() == Status.BUFFER_UNDERFLOW || res.getStatus() == Status.CLOSED) { break; } else if (res.getStatus() == Status.OK) { if (res.bytesConsumed() == 0) break; // do it again bufSize = engine.getSession().getApplicationBufferSize(); } } } } private void doReadDecoded(byte[] dat) throws IOException, ParserConfigurationException, SAXException { if (isStreamClosed) return; try { System.out.println("READING DECODED: " + new String(dat, "UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("CALL TO doReadDecoded"); byte[] new_decodedBuffer = new byte[decodedBuffer.length + dat.length]; System.arraycopy(decodedBuffer, 0, new_decodedBuffer, 0, decodedBuffer.length); System.arraycopy(dat, 0, new_decodedBuffer, decodedBuffer.length, dat.length); decodedBuffer = new_decodedBuffer; boolean found = true; while (found) { found = false; for (int i = 0; i < decodedBuffer.length; i++) { if (decodedBuffer[i] == '>') { byte[] cmd = new byte[i + 1]; System.arraycopy(decodedBuffer, 0, cmd, 0, cmd.length); cmd = unpadCmd(cmd); if (new String(cmd, "UTF-8").startsWith("</stream:stream>")) { handleStreamClose(); break; } if (new String(cmd, "UTF-8").startsWith("<stream:stream ") || new String(cmd, "UTF-8").startsWith("<?xml ")) { byte[] new_decodeBuffer = new byte[decodedBuffer.length - i - 1]; System.arraycopy(decodedBuffer, i + 1, new_decodeBuffer, 0, new_decodeBuffer.length); decodedBuffer = new_decodeBuffer; handleStreamSsl(cmd); found = true; break; } try { String test = new String(cmd, "UTF-8"); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(test)); db.parse(is); found = true; byte[] new_decodeBuffer = new byte[decodedBuffer.length - i - 1]; System.arraycopy(decodedBuffer, i + 1, new_decodeBuffer, 0, new_decodeBuffer.length); decodedBuffer = new_decodeBuffer; try { handleCommandSsl(test); } catch (Exception e) { e.printStackTrace(); } break; } catch (Exception e) { } } } System.out.println("DOREADDECODED " + dat.length); } } private byte[] unpadCmd(byte[] cmd) { int i = 0; for (; i < cmd.length; i++) { if (cmd[i] == '<') break; } byte[] cmd_new = new byte[cmd.length - i]; System.arraycopy(cmd, i, cmd_new, 0, cmd_new.length); return cmd_new; } private void handleStreamSsl(byte[] cmd) throws IOException, ParserConfigurationException, SAXException { String cmd_ = new String(cmd, "UTF-8"); System.out.println("handleStreamSsl " + cmd_); if (cmd_.startsWith("<stream:stream ")) { Document doc = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(cmd_ + "</stream:stream>")); doc = db.parse(is); to = ((Element) doc.getFirstChild()).getAttributeNode("to").getValue(); writeSsl("<?xml version='1.0'?>".getBytes()); if (isAuthed) { writeSsl(("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='" + to + "' id='" + generateId() + "' xml:lang='en' xmlns='jabber:client'>)").getBytes()); writeSsl("<stream:features>".getBytes()); writeSsl("<ver xmlns='urn:xmpp:features:rosterver'/>".getBytes()); writeSsl("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><required/></bind>".getBytes()); writeSsl("<session xmlns='urn:ietf:params:xml:ns:xmpp-session'><optional/></session>".getBytes()); writeSsl("</stream:features>".getBytes()); } else { writeSsl(("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='" + to + "' id='" + generateId() + "' xml:lang='en' xmlns='jabber:client'>").getBytes()); writeSsl("<stream:features>".getBytes()); writeSsl("<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>".getBytes()); writeSsl("<mechanism>PLAIN</mechanism>".getBytes()); writeSsl("</mechanisms>".getBytes()); writeSsl("</stream:features>".getBytes()); } isSslStreamRead = true; } else if (cmd_.startsWith("<?xml ")) { } } private String generateId() { String candidate = Util.generateHex(8) + "-" + Util.generateHex(4) + "-" + Util.generateHex(4) + "-" + Util.generateHex(8); for (String str : usedIds) { if (str.equals(candidate)) return generateId(); } usedIds.add(candidate); return candidate; } private void handleStreamClose() throws IOException { isStreamClosed = true; System.out.println("HANDLE STREAM CLOSE"); tsc.close(); server.removeClient(this); } private void handleCommandSsl(String s) throws ParserConfigurationException, SAXException, IOException { System.out.println("handleCommandSsl: " + s); Document doc = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(s)); doc = db.parse(is); Element first = (Element) doc.getFirstChild(); if (first.getNodeName().equals("presence")) { return; } if (first.getNodeName().equals("auth")) { String str = first.getTextContent(); byte[] userpw = Base64.decode(str, Base64.DEFAULT); String user = "", pw = ""; for (int i = 1; i < userpw.length; i++) { if (userpw[i] == 0) { user = new String(userpw, 1, i - 1); pw = new String(userpw, i + 1, userpw.length - i - 1); System.out.println("PW: " + pw); System.out.println("user: " + user); break; } } if (user.equals("android") && pw.equals(Util.getPw(context))) { isAuthed = true; writeSsl("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></success>".getBytes()); return; } else { writeSsl("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/><text>Unable to authorize you with the authentication credentials you've sent.</text></failure>".getBytes()); System.out.println("Wrong login. User: " + user + " Password: " + pw); return; } } else if (first.getNodeName().equals("message")) { Node type = first.getAttributes().getNamedItem("type"); Node to = first.getAttributes().getNamedItem("to"); if (to != null && type != null && type.getNodeValue().equals("chat")) { Node body = first.getElementsByTagName("body").item(0); if (body != null) { String body_ = body.getTextContent(); String to_ = to.getNodeValue(); String number = to_.substring(0, to_.indexOf('@')); Util.sendSms(number, body_); ContentValues content = new ContentValues(); content.put("address", number); content.put("body", body_); context.getContentResolver().insert(Uri.parse("content://sms/sent"), content); } } return; } else if (first.getNodeName().equals("iq")) { Element second = (Element) doc.getFirstChild().getFirstChild(); String id = first.getAttributes().getNamedItem("id").getNodeValue(); Node toAttr = first.getAttributes().getNamedItem("to"); String toStr = toAttr == null ? to : toAttr.getNodeValue(); if (first.getAttributes().getNamedItem("type").getNodeValue().equals("set")) { if (second.getNodeName().equals("bind")) { bindId = generateId(); writeSsl(("<iq id='" + id + "' type='result'>").getBytes()); writeSsl("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>".getBytes()); writeSsl(("<jid>android@" + to + "/" + bindId + "</jid>").getBytes()); writeSsl("</bind>".getBytes()); writeSsl("</iq>".getBytes()); sendUnreadSms(); ContentValues values = new ContentValues(); values.put("read", 1); context.getContentResolver().update(Uri.parse("content://sms/inbox"), values, null, null); return; } else if (second.getNodeName().equals("session")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + generateId() + "'/>").getBytes()); return; } } else if (first.getAttributes().getNamedItem("type").getNodeValue().equals("get")) { if (second.getNodeName().equals("query")) { String xmlns = second.getAttributes().getNamedItem("xmlns").getNodeValue(); if (xmlns.equals("http://jabber.org/protocol/disco#items")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + bindId + "' from='" + toStr + "'>").getBytes()); writeSsl("<query xmlns='http://jabber.org/protocol/disco#items'/>".getBytes()); writeSsl("</iq>".getBytes()); return; } else if (xmlns.equals("http://jabber.org/protocol/disco#info")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + bindId + "' from='" + toStr + "'>").getBytes()); writeSsl("<query xmlns='http://jabber.org/protocol/disco#info'>".getBytes()); writeSsl("<feature var='jabber:iq:roster'/>".getBytes()); writeSsl("</query>".getBytes()); writeSsl("</iq>".getBytes()); return; } else if (xmlns.equals("jabber:iq:roster")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + bindId + "'>").getBytes()); writeSsl("<query xmlns='jabber:iq:roster'>".getBytes()); ArrayList<Contact> contacts = Util.getContacts(context); for (Contact contact : contacts) { writeSsl(("<item jid='" + contact.number + "@" + to + "' subscription='both' name='" + contact.name + "'></item>").getBytes()); } writeSsl("</query>".getBytes()); writeSsl("</iq>".getBytes()); for (Contact contact : contacts) { writeSsl(("<presence from='" + contact.number + "@" + to + "/SMS' to='andriod@" + to + "'/>").getBytes()); } return; } } else if (second.getNodeName().equals("vCard")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + bindId + "' from='" + toStr + "'>").getBytes()); writeSsl(("<vCard xmlns='vcard-temp'><NICKNAME>android@" + to + "</NICKNAME></vCard>").getBytes()); writeSsl("</iq>".getBytes()); return; } else if (second.getNodeName().equals("ping")) { writeSsl(("<iq id='" + id + "' type='result' to='android@" + to + "/" + bindId + "'/>").getBytes()); return; } } if (toStr != null) writeSsl(("<iq from='" + toStr + "' id='" + id + "' type='error' to='android@" + to + "/" + generateId() + "'/>").getBytes()); return; } System.out.println("WARNING ! COMMAND NOT HANDLED"); } private void writeSsl(byte[] bytes) throws IOException { int bufSize = engine.getSession().getApplicationBufferSize(); while (bytes.length != 0) { ByteBuffer src = ByteBuffer.wrap(bytes); ByteBuffer dst = ByteBuffer.allocate(bufSize); SSLEngineResult res = engine.wrap(src, dst); byte[] produced = new byte[res.bytesProduced()]; System.arraycopy(dst.array(), 0, produced, 0, res.bytesProduced()); tsc.write(produced); byte[] new_bytes = new byte[bytes.length - res.bytesConsumed()]; System.arraycopy(bytes, res.bytesConsumed(), new_bytes, 0, new_bytes.length); bytes = new_bytes; if (res.getStatus() == Status.BUFFER_OVERFLOW) bufSize += 1024; } } public boolean sendUnreadSms() throws IOException { Cursor c = context.getContentResolver().query(Uri.parse("content://sms/inbox"), null, "read = 0", null, null); boolean hasNew = false; while (c.moveToNext()) { String findNumber = Util.findNumber(c.getString(c.getColumnIndex("address")), context); if (findNumber == null) findNumber = c.getString(c.getColumnIndex("address")); writeSsl(("<message id='some_id' type='chat' from='" + findNumber + "@" + to + "/" + bindId + "'>").getBytes()); writeSsl(("<body>" + Util.xmlEncode(c.getString(c.getColumnIndex("body"))) + "</body>").getBytes()); writeSsl("</message>".getBytes()); hasNew = true; } c.close(); return hasNew; } private void handleStreamBegin(byte[] cmd) throws ParserConfigurationException, SAXException, IOException, NoSuchAlgorithmException { cmd = unpadCmd(cmd); String cmd_ = new String(cmd, "UTF-8"); if (cmd_.startsWith("<?xml ")) return; if (cmd_.startsWith("<stream:stream ")) { Document doc = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(cmd_ + "</stream:stream>")); doc = db.parse(is); to = ((Element) doc.getFirstChild()).getAttributeNode("to").getValue(); write("<?xml version='1.0'?>".getBytes()); write(("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='" + to + "' id='" + generateId() + "' xml:lang='en' xmlns='jabber:client'>").getBytes()); write("<stream:features>".getBytes()); write("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls>".getBytes()); write("</stream:features>".getBytes()); System.out.println("COMMAND " + cmd_); } else if (cmd_.startsWith("<starttls ")) { write("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>".getBytes()); SSLContext sslc = null; sslc = SSLContext.getInstance("TLS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); try { kmf.init(Service_.ks, "pw".toCharArray()); sslc.init(kmf.getKeyManagers(), null, null); } catch (Exception e) { e.printStackTrace(); } engine = sslc.createSSLEngine(); engine.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" }); // engine.setEnabledCipherSuites(new String[] { // "SSL_RSA_WITH_RC4_128_SHA" }); engine.setUseClientMode(false); engine.beginHandshake(); state = State.HANDSHAKING; if (engine.getHandshakeStatus() != HandshakeStatus.NEED_UNWRAP) { throw new IOException("STATUS SHOULD BE NEED_UNWRAP"); } } } private void write(byte[] bytes) throws IOException { System.out.println("writing: " + new String(bytes, "UTF-8")); tsc.write(bytes); } @Override public void onDisconnect(TcpServerClient tc) { System.out.println("ON DISCONNECT"); isClosed = true; server.removeClient(this); } @Override public void onWritten(TcpServerClient tcpClient) { } public boolean onSmsReceived() throws IOException { if (!isAuthed) return false; return sendUnreadSms(); } }