package com.juick.xmpp.s2s; import com.juick.xmpp.Iq; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Presence; import com.juick.xmpp.utils.XmlUtils; import org.xmlpull.v1.XmlPullParser; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.Channels; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author ugnich */ public class ConnectionIn extends Connection implements Runnable { private static final Logger LOGGER = Logger.getLogger(ConnectionIn.class.getName()); final public List from = new ArrayList<>(); public long tsRemoteData = 0; public long packetsRemote = 0; public ConnectionIn(AsynchronousSocketChannel socket) { super(); this.socket = socket; streamID = UUID.randomUUID().toString(); } @Override public void run() { LOGGER.info("STREAM FROM ? " + streamID + " START"); try { parser.setInput(new InputStreamReader(Channels.newInputStream(socket))); writer = new OutputStreamWriter(Channels.newOutputStream(socket)); parser.next(); // stream:stream updateTsRemoteData(); if (!parser.getName().equals("stream:stream") || !parser.getAttributeValue(null, "xmlns").equals("jabber:server") || !parser.getAttributeValue(null, "xmlns:stream").equals("http://etherx.jabber.org/streams") || !parser.getAttributeValue(null, "xmlns:db").equals("jabber:server:dialback")) { // || !parser.getAttributeValue(null, "version").equals("1.0") // || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) { throw new Exception("STREAM FROM ? " + streamID + " INVALID FIRST PACKET"); } boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; String openStream = ""; if (xmppversionnew) { openStream += ""; } sendStanza(openStream); while (parser.next() != XmlPullParser.END_DOCUMENT) { updateTsRemoteData(); if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } logParser(); packetsRemote++; String tag = parser.getName(); if (tag.equals("db:result")) { String dfrom = parser.getAttributeValue(null, "from"); String to = parser.getAttributeValue(null, "to"); LOGGER.info("STREAM FROM " + dfrom + " TO " + to + " " + streamID + " ASKING FOR DIALBACK"); if (dfrom.endsWith(XMPPComponent.HOSTNAME) && (dfrom.equals(XMPPComponent.HOSTNAME) || dfrom.endsWith("." + XMPPComponent.HOSTNAME))) { break; } if (dfrom != null && to != null && to.equals(XMPPComponent.HOSTNAME)) { String dbKey = XmlUtils.getTagText(parser); updateTsRemoteData(); ConnectionOut c = XMPPComponent.getConnectionOut(dfrom, false); if (c != null) { c.sendDialbackVerify(streamID, dbKey); } else { c = new ConnectionOut(dfrom, streamID, dbKey); new Thread(c).start(); } } else { throw new Exception("STREAM FROM " + dfrom + " " + streamID + " DIALBACK RESULT FAIL"); } } else if (tag.equals("db:verify")) { String vfrom = parser.getAttributeValue(null, "from"); String vto = parser.getAttributeValue(null, "to"); String vid = parser.getAttributeValue(null, "id"); String vkey = XmlUtils.getTagText(parser); updateTsRemoteData(); boolean valid = false; if (vfrom != null && vto != null && vid != null && vkey != null) { String vkey2 = generateDialbackKey(vfrom, vto, vid); valid = vkey.equals(vkey2); } if (valid) { sendStanza(""); LOGGER.info("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY VALID"); } else { sendStanza(""); LOGGER.warning("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY INVALID"); } } else if (tag.equals("presence") && checkFromTo(parser)) { Presence p = Presence.parse(parser, null); if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { JuickBot.incomingPresence(p); } } else if (tag.equals("message") && checkFromTo(parser)) { updateTsRemoteData(); Message msg = Message.parse(parser, XMPPComponent.childParsers); if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { LOGGER.info("STREAM " + streamID + ": " + msg.toString()); if (!JuickBot.incomingMessage(msg)) { XMPPComponent.connRouter.sendStanza(msg.toString()); } } } else if (tag.equals("iq") && checkFromTo(parser)) { updateTsRemoteData(); String type = parser.getAttributeValue(null, "type"); String xml = XmlUtils.parseToString(parser, true); if (type == null || !type.equals(Iq.Type.error)) { LOGGER.info("STREAM " + streamID + ": " + xml); XMPPComponent.connRouter.sendStanza(xml); } } else { LOGGER.info("STREAM " + streamID + ": " + XmlUtils.parseToString(parser, true)); } } LOGGER.warning("STREAM " + streamID + " FINISHED"); XMPPComponent.removeConnectionIn(this); closeConnection(); } catch (EOFException ex) { LOGGER.info(String.format("STREAM %s CLOSED (dirty)", streamID)); XMPPComponent.removeConnectionIn(this); closeConnection(); } catch (Exception e) { LOGGER.log(Level.WARNING, "STREAM " + streamID + " ERROR", e); XMPPComponent.removeConnectionIn(this); closeConnection(); } } void updateTsRemoteData() { tsRemoteData = System.currentTimeMillis(); } public void sendDialbackResult(String sfrom, String type) { try { sendStanza(""); if (type.equals("valid")) { from.add(sfrom); LOGGER.info("STREAM FROM " + sfrom + " " + streamID + " READY"); } } catch (IOException e) { LOGGER.warning("STREAM FROM " + sfrom + " " + streamID + " ERROR: " + e.toString()); } } boolean checkFromTo(XmlPullParser parser) throws Exception { String cfrom = parser.getAttributeValue(null, "from"); String cto = parser.getAttributeValue(null, "to"); if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { JID jidto = new JID(cto); if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(XMPPComponent.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { JID jidfrom = new JID(cfrom); int size = from.size(); for (int i = 0; i < size; i++) { if (from.get(i).equals(jidfrom.Host)) { return true; } } } } return false; } }