disocover stream hosts (xep-0065) cleaned up disco

Daniel Gultsch created

Change summary

src/eu/siacs/conversations/services/JingleConnectionManager.java    | 50 
src/eu/siacs/conversations/services/XmppConnectionService.java      |  5 
src/eu/siacs/conversations/xmpp/PacketReceived.java                 |  2 
src/eu/siacs/conversations/xmpp/XmppConnection.java                 | 94 
src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java        | 57 
src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java | 93 
src/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java  |  5 
src/eu/siacs/conversations/xmpp/jingle/OnPrimaryCanditateFound.java |  7 
src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java         |  2 
src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java    |  2 
src/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java          |  2 
11 files changed, 211 insertions(+), 108 deletions(-)

Detailed changes

src/eu/siacs/conversations/services/JingleConnectionManager.java 🔗

@@ -1,50 +0,0 @@
-package eu.siacs.conversations.services;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.JingleConnection;
-import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
-
-public class JingleConnectionManager {
-	
-	private XmppConnectionService xmppConnectionService;
-	
-	private ConcurrentHashMap<String, JingleConnection> connections = new ConcurrentHashMap<String, JingleConnection>();
-	
-	public JingleConnectionManager(XmppConnectionService service) {
-		this.xmppConnectionService = service;
-	}
-	
-	public void deliverPacket(Account account, JinglePacket packet) {
-		String id = generateInternalId(account.getJid(), packet.getFrom(), packet.getSessionId());
-	}
-	
-	public JingleConnection createNewConnection(Message message) {
-		Account account = message.getConversation().getAccount();
-		JingleConnection connection = new JingleConnection(this,account, message.getCounterpart());
-		String id = generateInternalId(account.getJid(), message.getCounterpart(), connection.getSessionId());
-		connection.init(message);
-		return connection;
-	}
-	
-	private String generateInternalId(String account, String counterpart, String sid) {
-		return account+"#"+counterpart+"#"+sid;
-		
-	}
-
-	public XmppConnectionService getXmppConnectionService() {
-		return this.xmppConnectionService;
-	}
-
-	public Element getPrimaryCanditate(String jid) {
-		Element canditate = new Element("canditate");
-		canditate.setAttribute("cid","122");
-		canditate.setAttribute("port","1234");
-		canditate.setAttribute("jid", jid);
-		canditate.setAttribute("type", "assisted");
-		return canditate;
-	}
-}

src/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -41,16 +41,17 @@ import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.OnBindListener;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
-import eu.siacs.conversations.xmpp.OnJinglePacketReceived;
 import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
 import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
 import eu.siacs.conversations.xmpp.OnStatusChanged;
 import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
 import eu.siacs.conversations.xmpp.XmppConnection;
+import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
+import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
+import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.Service;

src/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -16,9 +16,13 @@ import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocket;
@@ -44,11 +48,12 @@ import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Tag;
 import eu.siacs.conversations.xml.TagWriter;
 import eu.siacs.conversations.xml.XmlReader;
+import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
+import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
 import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
 import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
 import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
@@ -70,8 +75,7 @@ public class XmppConnection implements Runnable {
 	private boolean shouldBind = true;
 	private boolean shouldAuthenticate = true;
 	private Element streamFeatures;
-	private HashSet<String> discoFeatures = new HashSet<String>();
-	private List<String> discoItems = new ArrayList<String>();
+	private HashMap<String, List<String>> disco = new HashMap<String, List<String>>();
 	
 	private String streamId = null;
 	private int smVersion = 3;
@@ -644,8 +648,8 @@ public class XmppConnection implements Runnable {
 					tagWriter.writeStanzaAsync(enable);
 				}
 				sendInitialPresence();
-				sendServiceDiscoveryInfo();
-				sendServiceDiscoveryItems();
+				sendServiceDiscoveryInfo(account.getServer());
+				sendServiceDiscoveryItems(account.getServer());
 				if (bindListener !=null) {
 					bindListener.onBind(account);
 				}
@@ -654,42 +658,54 @@ public class XmppConnection implements Runnable {
 		});
 	}
 
-	private void sendServiceDiscoveryInfo() {
+	private void sendServiceDiscoveryInfo(final String server) {
 		IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
-		iq.setTo(account.getServer());
+		iq.setTo(server);
 		iq.query("http://jabber.org/protocol/disco#info");
 		this.sendIqPacket(iq, new OnIqPacketReceived() {
 
 			@Override
 			public void onIqPacketReceived(Account account, IqPacket packet) {
-					List<Element> elements = packet.query().getChildren();
-					for (int i = 0; i < elements.size(); ++i) {
-						if (elements.get(i).getName().equals("feature")) {
-							discoFeatures.add(elements.get(i).getAttribute(
-									"var"));
-						}
+				List<Element> elements = packet.query().getChildren();
+				List<String> features = new ArrayList<String>();
+				for (int i = 0; i < elements.size(); ++i) {
+					if (elements.get(i).getName().equals("feature")) {
+						features.add(elements.get(i).getAttribute(
+								"var"));
 					}
-				if (discoFeatures.contains("urn:xmpp:carbons:2")) {
-					sendEnableCarbons();
+				}
+				Log.d(LOGTAG,"put "+server+" "+features.toString());
+				disco.put(server, features);
+				
+				if (account.getServer().equals(server)) {
+					enableAdvancedStreamFeatures();
 				}
 			}
 		});
 	}
-	private void sendServiceDiscoveryItems() {
+	
+	private void enableAdvancedStreamFeatures() {
+		if (hasFeaturesCarbon()) {
+			sendEnableCarbons();
+		}
+	}
+	
+	private void sendServiceDiscoveryItems(final String server) {
 		IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
-		iq.setTo(account.getServer());
+		iq.setTo(server);
 		iq.query("http://jabber.org/protocol/disco#items");
 		this.sendIqPacket(iq, new OnIqPacketReceived() {
 
 			@Override
 			public void onIqPacketReceived(Account account, IqPacket packet) {
-					List<Element> elements = packet.query().getChildren();
-					for (int i = 0; i < elements.size(); ++i) {
-						if (elements.get(i).getName().equals("item")) {
-							discoItems.add(elements.get(i).getAttribute(
-									"jid"));
-						}
+				List<Element> elements = packet.query().getChildren();
+				for (int i = 0; i < elements.size(); ++i) {
+					if (elements.get(i).getName().equals("item")) {
+						String jid = elements.get(i).getAttribute(
+								"jid");
+						sendServiceDiscoveryInfo(jid);
 					}
+				}
 			}
 		});
 	}
@@ -850,7 +866,26 @@ public class XmppConnection implements Runnable {
 	}
 	
 	public boolean hasFeaturesCarbon() {
-		return discoFeatures.contains("urn:xmpp:carbons:2");
+		return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
+	}
+	
+	public boolean hasDiscoFeature(String server, String feature) {
+		if (!disco.containsKey(server)) {
+			return false;
+		}
+		return disco.get(server).contains(feature);
+	}
+	
+	public String findDiscoItemByFeature(String feature) {
+		Iterator<Entry<String, List<String>>> it = this.disco.entrySet().iterator();
+	    while (it.hasNext()) {
+	    	Entry<String, List<String>> pairs = it.next();
+	        if (pairs.getValue().contains(feature)) {
+	        	return pairs.getKey();
+	        }
+	        it.remove();
+	    }
+		return null;
 	}
 
 	public void r() {
@@ -866,15 +901,6 @@ public class XmppConnection implements Runnable {
 	}
 
 	public String getMucServer() {
-		for(int i = 0; i < discoItems.size(); ++i) {
-			if (discoItems.get(i).contains("conference.")) {
-				return discoItems.get(i);
-			} else if (discoItems.get(i).contains("conf.")) {
-				return discoItems.get(i);
-			} else if (discoItems.get(i).contains("muc.")) {
-				return discoItems.get(i);
-			}
-		}
-		return null;
+		return findDiscoItemByFeature("http://jabber.org/protocol/muc");
 	}
 }

src/eu/siacs/conversations/xmpp/JingleConnection.java → src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java 🔗

@@ -1,7 +1,5 @@
-package eu.siacs.conversations.xmpp;
+package eu.siacs.conversations.xmpp.jingle;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -9,30 +7,37 @@ import android.util.Log;
 
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.JingleConnectionManager;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.stanzas.jingle.Content;
-import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
+import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 
 public class JingleConnection {
 
 	private JingleConnectionManager mJingleConnectionManager;
 	private XmppConnectionService mXmppConnectionService;
 	
+	private Message message;
 	private String sessionId;
 	private Account account;
-	private String counterpart;
+	private String initiator;
+	private String responder;
 	private List<Element> canditates = new ArrayList<Element>();
 	
-	public JingleConnection(JingleConnectionManager mJingleConnectionManager, Account account, String counterpart) {
+	private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
+		
+		@Override
+		public void onIqPacketReceived(Account account, IqPacket packet) {
+			Log.d("xmppService",packet.toString());
+		}
+	};
+	
+	public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
 		this.mJingleConnectionManager = mJingleConnectionManager;
 		this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
-		this.account = account;
-		this.counterpart = counterpart;
-		SecureRandom random = new SecureRandom();
-		sessionId = new BigInteger(100, random).toString(32);
-		this.canditates.add(this.mJingleConnectionManager.getPrimaryCanditate(account.getJid()));
+		this.sessionId = this.mJingleConnectionManager.nextRandomId();
 	}
 	
 	public String getSessionId() {
@@ -40,26 +45,46 @@ public class JingleConnection {
 	}
 	
 	public void init(Message message) {
+		this.message = message;
+		this.account = message.getConversation().getAccount();
+		this.initiator = this.account.getFullJid();
+		if (this.canditates.size() > 0) {
+			this.sendInitRequest();
+		} else {
+			this.mJingleConnectionManager.getPrimaryCanditate(account, new OnPrimaryCanditateFound() {
+				
+				@Override
+				public void onPrimaryCanditateFound(boolean success, Element canditate) {
+					if (success) {
+						canditates.add(canditate);
+					}
+					sendInitRequest();
+				}
+			});
+		}
+		
+	}
+	
+	private void sendInitRequest() {
 		JinglePacket packet = this.bootstrapPacket();
 		packet.setAction("session-initiate");
 		packet.setInitiator(this.account.getFullJid());
 		Content content = new Content();
 		if (message.getType() == Message.TYPE_IMAGE) {
-			//creator='initiator' name='a-file-offer'
 			content.setAttribute("creator", "initiator");
 			content.setAttribute("name", "a-file-offer");
 			content.offerFile(this.mXmppConnectionService.getFileBackend().getImageFile(message));
 			content.setCanditates(this.canditates);
 			packet.setContent(content);
 			Log.d("xmppService",packet.toString());
-			account.getXmppConnection().sendIqPacket(packet, null);
+			account.getXmppConnection().sendIqPacket(packet, this.responseListener);
 		}
 	}
 	
 	private JinglePacket bootstrapPacket() {
 		JinglePacket packet = new JinglePacket();
 		packet.setFrom(account.getFullJid());
-		packet.setTo(this.counterpart+"/Gajim");
+		packet.setTo(this.message.getCounterpart()+"/Gajim"); //fixme, not right in all cases;
 		packet.setSessionId(this.sessionId);
 		return packet;
 	}

src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java 🔗

@@ -0,0 +1,93 @@
+package eu.siacs.conversations.xmpp.jingle;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import android.util.Log;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class JingleConnectionManager {
+	
+	private XmppConnectionService xmppConnectionService;
+	
+	private List<JingleConnection> connections = new ArrayList<JingleConnection>(); //make concurrent
+	
+	private ConcurrentHashMap<String, Element> primaryCanditates = new ConcurrentHashMap<String, Element>();
+	
+	private SecureRandom random = new SecureRandom();
+	
+	public JingleConnectionManager(XmppConnectionService service) {
+		this.xmppConnectionService = service;
+	}
+	
+	public void deliverPacket(Account account, JinglePacket packet) {
+		String id = generateInternalId(account.getJid(), packet.getFrom(), packet.getSessionId());
+	}
+	
+	public JingleConnection createNewConnection(Message message) {
+		Account account = message.getConversation().getAccount();
+		JingleConnection connection = new JingleConnection(this);
+		String id = generateInternalId(account.getJid(), message.getCounterpart(), connection.getSessionId());
+		connection.init(message);
+		return connection;
+	}
+	
+	private String generateInternalId(String account, String counterpart, String sid) {
+		return account+"#"+counterpart+"#"+sid;
+		
+	}
+
+	public XmppConnectionService getXmppConnectionService() {
+		return this.xmppConnectionService;
+	}
+
+	public void getPrimaryCanditate(Account account, final OnPrimaryCanditateFound listener) {
+		if (!this.primaryCanditates.containsKey(account.getJid())) {
+			String xmlns = "http://jabber.org/protocol/bytestreams";
+			String proxy = account.getXmppConnection().findDiscoItemByFeature(xmlns);
+			if (proxy!=null) {
+				IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
+				iq.setTo(proxy);
+				iq.query(xmlns);
+				account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() {
+					
+					@Override
+					public void onIqPacketReceived(Account account, IqPacket packet) {
+						Element streamhost = packet.query().findChild("streamhost","http://jabber.org/protocol/bytestreams");
+						if (streamhost!=null) {
+							Log.d("xmppService","streamhost found "+streamhost.toString());
+							Element canditate = new Element("canditate");
+							canditate.setAttribute("cid",nextRandomId());
+							canditate.setAttribute("host", streamhost.getAttribute("host"));
+							canditate.setAttribute("port",streamhost.getAttribute("port"));
+							canditate.setAttribute("type", "proxy");
+							primaryCanditates.put(account.getJid(), canditate);
+							listener.onPrimaryCanditateFound(true, canditate);
+						} else {
+							listener.onPrimaryCanditateFound(false, null);
+						}
+					}
+				});
+			} else {
+				listener.onPrimaryCanditateFound(false, null);
+			}
+			
+		} else {
+			listener.onPrimaryCanditateFound(true, this.primaryCanditates.get(account.getJid()));
+		}
+	}
+	
+	public String nextRandomId() {
+		return new BigInteger(50, random).toString(32);
+	}
+}

src/eu/siacs/conversations/xmpp/OnJinglePacketReceived.java → src/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java 🔗

@@ -1,7 +1,8 @@
-package eu.siacs.conversations.xmpp;
+package eu.siacs.conversations.xmpp.jingle;
 
 import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
+import eu.siacs.conversations.xmpp.PacketReceived;
+import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 
 public interface OnJinglePacketReceived extends PacketReceived {
 	public void onJinglePacketReceived(Account account, JinglePacket packet);

src/eu/siacs/conversations/xmpp/stanzas/jingle/JinglePacket.java → src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java 🔗

@@ -1,4 +1,4 @@
-package eu.siacs.conversations.xmpp.stanzas.jingle;
+package eu.siacs.conversations.xmpp.jingle.stanzas;
 
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;