disocover stream hosts (xep-0065) cleaned up disco

This commit is contained in:
Daniel Gultsch 2014-04-08 23:15:55 +02:00
parent 92dcf85701
commit 298c7adcd1
11 changed files with 211 additions and 108 deletions

View file

@ -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;
}
}

View file

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

View file

@ -1,5 +1,5 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp;
abstract interface PacketReceived { public abstract interface PacketReceived {
} }

View file

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

View file

@ -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.ArrayList;
import java.util.List; import java.util.List;
@ -9,30 +7,37 @@ import android.util.Log;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.JingleConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.stanzas.jingle.Content; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket; 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 { public class JingleConnection {
private JingleConnectionManager mJingleConnectionManager; private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService; private XmppConnectionService mXmppConnectionService;
private Message message;
private String sessionId; private String sessionId;
private Account account; private Account account;
private String counterpart; private String initiator;
private String responder;
private List<Element> canditates = new ArrayList<Element>(); 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.mJingleConnectionManager = mJingleConnectionManager;
this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService(); this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
this.account = account; this.sessionId = this.mJingleConnectionManager.nextRandomId();
this.counterpart = counterpart;
SecureRandom random = new SecureRandom();
sessionId = new BigInteger(100, random).toString(32);
this.canditates.add(this.mJingleConnectionManager.getPrimaryCanditate(account.getJid()));
} }
public String getSessionId() { public String getSessionId() {
@ -40,26 +45,46 @@ public class JingleConnection {
} }
public void init(Message message) { 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(); JinglePacket packet = this.bootstrapPacket();
packet.setAction("session-initiate"); packet.setAction("session-initiate");
packet.setInitiator(this.account.getFullJid()); packet.setInitiator(this.account.getFullJid());
Content content = new Content(); Content content = new Content();
if (message.getType() == Message.TYPE_IMAGE) { if (message.getType() == Message.TYPE_IMAGE) {
//creator='initiator' name='a-file-offer'
content.setAttribute("creator", "initiator"); content.setAttribute("creator", "initiator");
content.setAttribute("name", "a-file-offer"); content.setAttribute("name", "a-file-offer");
content.offerFile(this.mXmppConnectionService.getFileBackend().getImageFile(message)); content.offerFile(this.mXmppConnectionService.getFileBackend().getImageFile(message));
content.setCanditates(this.canditates); content.setCanditates(this.canditates);
packet.setContent(content); packet.setContent(content);
Log.d("xmppService",packet.toString()); Log.d("xmppService",packet.toString());
account.getXmppConnection().sendIqPacket(packet, null); account.getXmppConnection().sendIqPacket(packet, this.responseListener);
} }
} }
private JinglePacket bootstrapPacket() { private JinglePacket bootstrapPacket() {
JinglePacket packet = new JinglePacket(); JinglePacket packet = new JinglePacket();
packet.setFrom(account.getFullJid()); 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); packet.setSessionId(this.sessionId);
return packet; return packet;
} }

View file

@ -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);
}
}

View file

@ -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.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 interface OnJinglePacketReceived extends PacketReceived {
public void onJinglePacketReceived(Account account, JinglePacket packet); public void onJinglePacketReceived(Account account, JinglePacket packet);

View file

@ -0,0 +1,7 @@
package eu.siacs.conversations.xmpp.jingle;
import eu.siacs.conversations.xml.Element;
public interface OnPrimaryCanditateFound {
public void onPrimaryCanditateFound(boolean success, Element canditate);
}

View file

@ -1,4 +1,4 @@
package eu.siacs.conversations.xmpp.stanzas.jingle; package eu.siacs.conversations.xmpp.jingle.stanzas;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;

View file

@ -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.xml.Element;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;

View file

@ -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.xml.Element;