support jingle ft:4 to be compatible with swift
Conversations and Gajim both have an implementation bug that sends the jingle session id instead of the transport id (compare XEP-260 2.2). This commit has a work around for this that remains buggy when using ft:3. If gajim is ever to fix this we will be incompatbile. gajim should implement ft:4 instead. (gajim to gajim is broken as well)
This commit is contained in:
		
							parent
							
								
									b5caa8fa35
								
							
						
					
					
						commit
						1d79a677c8
					
				| @ -16,11 +16,13 @@ import eu.siacs.conversations.Config; | ||||
| import eu.siacs.conversations.crypto.axolotl.AxolotlService; | ||||
| import eu.siacs.conversations.services.XmppConnectionService; | ||||
| import eu.siacs.conversations.utils.PhoneHelper; | ||||
| import eu.siacs.conversations.xmpp.jingle.stanzas.Content; | ||||
| 
 | ||||
| public abstract class AbstractGenerator { | ||||
| 	private final String[] FEATURES = { | ||||
| 			"urn:xmpp:jingle:1", | ||||
| 			"urn:xmpp:jingle:apps:file-transfer:3", | ||||
| 			Content.Version.FT_3.getNamespace(), | ||||
| 			Content.Version.FT_4.getNamespace(), | ||||
| 			"urn:xmpp:jingle:transports:s5b:1", | ||||
| 			"urn:xmpp:jingle:transports:ibb:1", | ||||
| 			"http://jabber.org/protocol/muc", | ||||
|  | ||||
| @ -21,6 +21,7 @@ import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.entities.Conversation; | ||||
| import eu.siacs.conversations.entities.DownloadableFile; | ||||
| import eu.siacs.conversations.entities.Message; | ||||
| import eu.siacs.conversations.entities.Presence; | ||||
| import eu.siacs.conversations.entities.Transferable; | ||||
| import eu.siacs.conversations.entities.TransferablePlaceholder; | ||||
| import eu.siacs.conversations.persistance.FileBackend; | ||||
| @ -45,6 +46,8 @@ public class JingleConnection implements Transferable { | ||||
| 	protected static final int JINGLE_STATUS_TRANSMITTING = 5; | ||||
| 	protected static final int JINGLE_STATUS_FAILED = 99; | ||||
| 
 | ||||
| 	private Content.Version ftVersion = Content.Version.FT_3; | ||||
| 
 | ||||
| 	private int ibbBlockSize = 8192; | ||||
| 
 | ||||
| 	private int mJingleStatus = -1; | ||||
| @ -238,12 +241,14 @@ public class JingleConnection implements Transferable { | ||||
| 		this.contentCreator = "initiator"; | ||||
| 		this.contentName = this.mJingleConnectionManager.nextRandomId(); | ||||
| 		this.message = message; | ||||
| 		this.account = message.getConversation().getAccount(); | ||||
| 		upgradeNamespace(); | ||||
| 		this.message.setTransferable(this); | ||||
| 		this.mStatus = Transferable.STATUS_UPLOADING; | ||||
| 		this.account = message.getConversation().getAccount(); | ||||
| 		this.initiator = this.account.getJid(); | ||||
| 		this.responder = this.message.getCounterpart(); | ||||
| 		this.sessionId = this.mJingleConnectionManager.nextRandomId(); | ||||
| 		this.transportId = this.mJingleConnectionManager.nextRandomId(); | ||||
| 		if (this.candidates.size() > 0) { | ||||
| 			this.sendInitRequest(); | ||||
| 		} else { | ||||
| @ -287,6 +292,20 @@ public class JingleConnection implements Transferable { | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	private void upgradeNamespace() { | ||||
| 		Jid jid = this.message.getCounterpart(); | ||||
| 		String resource = jid != null ?jid.getResourcepart() : null; | ||||
| 		if (resource != null) { | ||||
| 			Presence presence = this.account.getRoster().getContact(jid).getPresences().getPresences().get(resource); | ||||
| 			if (presence != null) { | ||||
| 				List<String> features = presence.getServiceDiscoveryResult().getFeatures(); | ||||
| 				if (features.contains(Content.Version.FT_4.getNamespace())) { | ||||
| 					this.ftVersion = Content.Version.FT_4; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public void init(Account account, JinglePacket packet) { | ||||
| 		this.mJingleStatus = JINGLE_STATUS_INITIATED; | ||||
| 		Conversation conversation = this.mXmppConnectionService | ||||
| @ -307,7 +326,13 @@ public class JingleConnection implements Transferable { | ||||
| 		this.contentName = content.getAttribute("name"); | ||||
| 		this.transportId = content.getTransportId(); | ||||
| 		this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren())); | ||||
| 		this.fileOffer = packet.getJingleContent().getFileOffer(); | ||||
| 		this.ftVersion = content.getVersion(); | ||||
| 		if (ftVersion == null) { | ||||
| 			this.sendCancel(); | ||||
| 			this.fail(); | ||||
| 			return; | ||||
| 		} | ||||
| 		this.fileOffer = content.getFileOffer(this.ftVersion); | ||||
| 
 | ||||
| 		mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null); | ||||
| 
 | ||||
| @ -431,24 +456,23 @@ public class JingleConnection implements Transferable { | ||||
| 					this.file.setKeyAndIv(conversation.getSymmetricKey()); | ||||
| 					pair = AbstractConnectionManager.createInputStream(this.file, false); | ||||
| 					this.file.setExpectedSize(pair.second); | ||||
| 					content.setFileOffer(this.file, true); | ||||
| 					content.setFileOffer(this.file, true, this.ftVersion); | ||||
| 				} else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { | ||||
| 					this.file.setKey(mXmppAxolotlMessage.getInnerKey()); | ||||
| 					this.file.setIv(mXmppAxolotlMessage.getIV()); | ||||
| 					pair = AbstractConnectionManager.createInputStream(this.file, true); | ||||
| 					this.file.setExpectedSize(pair.second); | ||||
| 					content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement()); | ||||
| 					content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); | ||||
| 				} else { | ||||
| 					pair = AbstractConnectionManager.createInputStream(this.file, false); | ||||
| 					this.file.setExpectedSize(pair.second); | ||||
| 					content.setFileOffer(this.file, false); | ||||
| 					content.setFileOffer(this.file, false, this.ftVersion); | ||||
| 				} | ||||
| 			} catch (FileNotFoundException e) { | ||||
| 				cancel(); | ||||
| 				return; | ||||
| 			} | ||||
| 			this.mFileInputStream = pair.first; | ||||
| 			this.transportId = this.mJingleConnectionManager.nextRandomId(); | ||||
| 			content.setTransportId(this.transportId); | ||||
| 			content.socks5transport().setChildren(getCandidatesAsElements()); | ||||
| 			packet.setContent(content); | ||||
| @ -488,7 +512,7 @@ public class JingleConnection implements Transferable { | ||||
| 			public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) { | ||||
| 				final JinglePacket packet = bootstrapPacket("session-accept"); | ||||
| 				final Content content = new Content(contentCreator,contentName); | ||||
| 				content.setFileOffer(fileOffer); | ||||
| 				content.setFileOffer(fileOffer, ftVersion); | ||||
| 				content.setTransportId(transportId); | ||||
| 				if (success && candidate != null && !equalCandidateExists(candidate)) { | ||||
| 					final JingleSocks5Transport socksConnection = new JingleSocks5Transport( | ||||
| @ -626,13 +650,20 @@ public class JingleConnection implements Transferable { | ||||
| 			this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; | ||||
| 			if (connection.needsActivation()) { | ||||
| 				if (connection.getCandidate().isOurs()) { | ||||
| 					final String sid; | ||||
| 					if (ftVersion == Content.Version.FT_3) { | ||||
| 						Log.d(Config.LOGTAG,account.getJid().toBareJid()+": use session ID instead of transport ID to activate proxy"); | ||||
| 						sid = getSessionId(); | ||||
| 					} else { | ||||
| 						sid = getTransportId(); | ||||
| 					} | ||||
| 					Log.d(Config.LOGTAG, "candidate " | ||||
| 							+ connection.getCandidate().getCid() | ||||
| 							+ " was our proxy. going to activate"); | ||||
| 					IqPacket activation = new IqPacket(IqPacket.TYPE.SET); | ||||
| 					activation.setTo(connection.getCandidate().getJid()); | ||||
| 					activation.query("http://jabber.org/protocol/bytestreams") | ||||
| 							.setAttribute("sid", this.getSessionId()); | ||||
| 							.setAttribute("sid", sid); | ||||
| 					activation.query().addChild("activate") | ||||
| 							.setContent(this.getCounterPart().toString()); | ||||
| 					mXmppConnectionService.sendIqPacket(account,activation, | ||||
| @ -975,6 +1006,14 @@ public class JingleConnection implements Transferable { | ||||
| 		mXmppConnectionService.updateConversationUi(); | ||||
| 	} | ||||
| 
 | ||||
| 	public String getTransportId() { | ||||
| 		return this.transportId; | ||||
| 	} | ||||
| 
 | ||||
| 	public Content.Version getFtVersion() { | ||||
| 		return this.ftVersion; | ||||
| 	} | ||||
| 
 | ||||
| 	interface OnProxyActivated { | ||||
| 		public void success(); | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ import eu.siacs.conversations.entities.DownloadableFile; | ||||
| import eu.siacs.conversations.persistance.FileBackend; | ||||
| import eu.siacs.conversations.utils.CryptoHelper; | ||||
| import eu.siacs.conversations.utils.SocksSocketFactory; | ||||
| import eu.siacs.conversations.xmpp.jingle.stanzas.Content; | ||||
| 
 | ||||
| public class JingleSocks5Transport extends JingleTransport { | ||||
| 	private JingleCandidate candidate; | ||||
| @ -40,7 +41,12 @@ public class JingleSocks5Transport extends JingleTransport { | ||||
| 		try { | ||||
| 			MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); | ||||
| 			StringBuilder destBuilder = new StringBuilder(); | ||||
| 			destBuilder.append(jingleConnection.getSessionId()); | ||||
| 			if (jingleConnection.getFtVersion() == Content.Version.FT_3) { | ||||
| 				Log.d(Config.LOGTAG,this.connection.getAccount().getJid().toBareJid()+": using session Id instead of transport Id for proxy destination"); | ||||
| 				destBuilder.append(jingleConnection.getSessionId()); | ||||
| 			} else { | ||||
| 				destBuilder.append(jingleConnection.getTransportId()); | ||||
| 			} | ||||
| 			if (candidate.isOurs()) { | ||||
| 				destBuilder.append(jingleConnection.getAccount().getJid()); | ||||
| 				destBuilder.append(jingleConnection.getCounterPart()); | ||||
|  | ||||
| @ -5,12 +5,23 @@ import eu.siacs.conversations.xml.Element; | ||||
| 
 | ||||
| public class Content extends Element { | ||||
| 
 | ||||
| 	private String transportId; | ||||
| 	public enum Version { | ||||
| 		FT_3("urn:xmpp:jingle:apps:file-transfer:3"), | ||||
| 		FT_4("urn:xmpp:jingle:apps:file-transfer:4"); | ||||
| 
 | ||||
| 	private Content(String name) { | ||||
| 		super(name); | ||||
| 		private final String namespace; | ||||
| 
 | ||||
| 		Version(String namespace) { | ||||
| 			this.namespace = namespace; | ||||
| 		} | ||||
| 
 | ||||
| 		public String getNamespace() { | ||||
| 			return namespace; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private String transportId; | ||||
| 
 | ||||
| 	public Content() { | ||||
| 		super("content"); | ||||
| 	} | ||||
| @ -21,15 +32,28 @@ public class Content extends Element { | ||||
| 		this.setAttribute("name", name); | ||||
| 	} | ||||
| 
 | ||||
| 	public Version getVersion() { | ||||
| 		if (hasChild("description", Version.FT_3.namespace)) { | ||||
| 			return Version.FT_3; | ||||
| 		} else if (hasChild("description" , Version.FT_4.namespace)) { | ||||
| 			return Version.FT_4; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	public void setTransportId(String sid) { | ||||
| 		this.transportId = sid; | ||||
| 	} | ||||
| 
 | ||||
| 	public Element setFileOffer(DownloadableFile actualFile, boolean otr) { | ||||
| 		Element description = this.addChild("description", | ||||
| 				"urn:xmpp:jingle:apps:file-transfer:3"); | ||||
| 		Element offer = description.addChild("offer"); | ||||
| 		Element file = offer.addChild("file"); | ||||
| 	public Element setFileOffer(DownloadableFile actualFile, boolean otr, Version version) { | ||||
| 		Element description = this.addChild("description", version.namespace); | ||||
| 		Element file; | ||||
| 		if (version == Version.FT_3) { | ||||
| 			Element offer = description.addChild("offer"); | ||||
| 			file = offer.addChild("file"); | ||||
| 		} else { | ||||
| 			file = description.addChild("file"); | ||||
| 		} | ||||
| 		file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize())); | ||||
| 		if (otr) { | ||||
| 			file.addChild("name").setContent(actualFile.getName() + ".otr"); | ||||
| @ -39,27 +63,29 @@ public class Content extends Element { | ||||
| 		return file; | ||||
| 	} | ||||
| 
 | ||||
| 	public Element getFileOffer() { | ||||
| 		Element description = this.findChild("description", | ||||
| 				"urn:xmpp:jingle:apps:file-transfer:3"); | ||||
| 	public Element getFileOffer(Version version) { | ||||
| 		Element description = this.findChild("description", version.namespace); | ||||
| 		if (description == null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		Element offer = description.findChild("offer"); | ||||
| 		if (offer == null) { | ||||
| 			return null; | ||||
| 		if (version == Version.FT_3) { | ||||
| 			Element offer = description.findChild("offer"); | ||||
| 			if (offer == null) { | ||||
| 				return null; | ||||
| 			} | ||||
| 			return offer.findChild("file"); | ||||
| 		} else { | ||||
| 			return description.findChild("file"); | ||||
| 		} | ||||
| 		return offer.findChild("file"); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setFileOffer(Element fileOffer) { | ||||
| 		Element description = this.findChild("description", | ||||
| 				"urn:xmpp:jingle:apps:file-transfer:3"); | ||||
| 		if (description == null) { | ||||
| 			description = this.addChild("description", | ||||
| 					"urn:xmpp:jingle:apps:file-transfer:3"); | ||||
| 	public void setFileOffer(Element fileOffer, Version version) { | ||||
| 		Element description = this.addChild("description", version.namespace); | ||||
| 		if (version == Version.FT_3) { | ||||
| 			description.addChild("offer").addChild(fileOffer); | ||||
| 		} else { | ||||
| 			description.addChild(fileOffer); | ||||
| 		} | ||||
| 		description.addChild(fileOffer); | ||||
| 	} | ||||
| 
 | ||||
| 	public String getTransportId() { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch