@@ 1,7 1,9 @@
package eu.siacs.conversations.xmpp.jingle;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ 236,6 238,23 @@ public class RtpContentMap {
throw new IllegalStateException("Content map doesn't have distinct DTLS setup");
}
+ private DTLS getDistinctDtls() {
+ final Set<DTLS> dtlsSet =
+ ImmutableSet.copyOf(
+ Collections2.transform(
+ contents.values(),
+ dt -> {
+ final IceUdpTransportInfo.Fingerprint fp =
+ dt.transport.getFingerprint();
+ return new DTLS(fp.getHash(), fp.getSetup(), fp.getContent());
+ }));
+ final DTLS dtls = Iterables.getFirst(dtlsSet, null);
+ if (dtlsSet.size() == 1 && dtls != null) {
+ return dtls;
+ }
+ throw new IllegalStateException("Content map doesn't have distinct DTLS setup");
+ }
+
public boolean emptyCandidates() {
int count = 0;
for (DescriptionTransport descriptionTransport : contents.values()) {
@@ 262,12 281,22 @@ public class RtpContentMap {
return new RtpContentMap(this.group, contentMapBuilder.build());
}
+ public RtpContentMap toContentModification(final Collection<String> modifications) {
+ return new RtpContentMap(
+ this.group,
+ Maps.transformValues(
+ Maps.filterKeys(contents, Predicates.in(modifications)),
+ dt ->
+ new DescriptionTransport(
+ dt.senders, dt.description, IceUdpTransportInfo.STUB)));
+ }
+
public Diff diff(final RtpContentMap rtpContentMap) {
final Set<String> existingContentIds = this.contents.keySet();
final Set<String> newContentIds = rtpContentMap.contents.keySet();
return new Diff(
- Sets.difference(newContentIds, existingContentIds),
- Sets.difference(existingContentIds, newContentIds));
+ ImmutableSet.copyOf(Sets.difference(newContentIds, existingContentIds)),
+ ImmutableSet.copyOf(Sets.difference(existingContentIds, newContentIds)));
}
public boolean iceRestart(final RtpContentMap rtpContentMap) {
@@ 278,6 307,26 @@ public class RtpContentMap {
}
}
+ public RtpContentMap addContent(final RtpContentMap modification) {
+ final IceUdpTransportInfo.Credentials credentials = getDistinctCredentials();
+ final DTLS dtls = getDistinctDtls();
+ final IceUdpTransportInfo iceUdpTransportInfo =
+ IceUdpTransportInfo.of(credentials, dtls.setup, dtls.hash, dtls.fingerprint);
+ final Map<String, DescriptionTransport> combined =
+ new ImmutableMap.Builder<String, DescriptionTransport>()
+ .putAll(contents)
+ .putAll(
+ Maps.transformValues(
+ modification.contents,
+ dt ->
+ new DescriptionTransport(
+ dt.senders,
+ dt.description,
+ iceUdpTransportInfo)))
+ .build();
+ return new RtpContentMap(modification.group, combined);
+ }
+
public static class DescriptionTransport {
public final Content.Senders senders;
public final RtpDescription description;
@@ 370,4 419,31 @@ public class RtpContentMap {
.toString();
}
}
+
+ public static final class DTLS {
+ public final String hash;
+ public final IceUdpTransportInfo.Setup setup;
+ public final String fingerprint;
+
+ private DTLS(String hash, IceUdpTransportInfo.Setup setup, String fingerprint) {
+ this.hash = hash;
+ this.setup = setup;
+ this.fingerprint = fingerprint;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DTLS dtls = (DTLS) o;
+ return Objects.equal(hash, dtls.hash)
+ && setup == dtls.setup
+ && Objects.equal(fingerprint, dtls.fingerprint);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(hash, setup, fingerprint);
+ }
+ }
}
@@ 12,8 12,6 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
@@ 28,23 26,29 @@ import eu.siacs.conversations.xmpp.jingle.SessionDescription;
public class IceUdpTransportInfo extends GenericTransportInfo {
+ public static final IceUdpTransportInfo STUB = new IceUdpTransportInfo();
+
public IceUdpTransportInfo() {
super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP);
}
public static IceUdpTransportInfo upgrade(final Element element) {
- Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
- Preconditions.checkArgument(Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()), "Element does not match ice-udp transport namespace");
+ Preconditions.checkArgument(
+ "transport".equals(element.getName()), "Name of provided element is not transport");
+ Preconditions.checkArgument(
+ Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()),
+ "Element does not match ice-udp transport namespace");
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren());
return transportInfo;
}
- public static IceUdpTransportInfo of(SessionDescription sessionDescription, SessionDescription.Media media) {
+ public static IceUdpTransportInfo of(
+ SessionDescription sessionDescription, SessionDescription.Media media) {
final String ufrag = Iterables.getFirst(media.attributes.get("ice-ufrag"), null);
final String pwd = Iterables.getFirst(media.attributes.get("ice-pwd"), null);
- IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
+ final IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
if (ufrag != null) {
iceUdpTransportInfo.setAttribute("ufrag", ufrag);
}
@@ 56,7 60,15 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
iceUdpTransportInfo.addChild(fingerprint);
}
return iceUdpTransportInfo;
+ }
+ public static IceUdpTransportInfo of(
+ final Credentials credentials, final Setup setup, final String hash, final String fingerprint) {
+ final IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
+ iceUdpTransportInfo.addChild(Fingerprint.of(setup, hash, fingerprint));
+ iceUdpTransportInfo.setAttribute("ufrag", credentials.ufrag);
+ iceUdpTransportInfo.setAttribute("pwd", credentials.password);
+ return iceUdpTransportInfo;
}
public Fingerprint getFingerprint() {
@@ 91,7 103,8 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
transportInfo.setAttribute("ufrag", credentials.ufrag);
transportInfo.setAttribute("pwd", credentials.password);
for (final Element child : getChildren()) {
- if (child.getName().equals("fingerprint") && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
+ if (child.getName().equals("fingerprint")
+ && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
final Fingerprint fingerprint = new Fingerprint();
fingerprint.setAttributes(new Hashtable<>(child.getAttributes()));
fingerprint.setContent(child.getContent());
@@ 231,7 244,7 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return getAttributeAsInt("rel-port");
}
- public String getType() { //TODO might be converted to enum
+ public String getType() { // TODO might be converted to enum
return getAttribute("type");
}
@@ 256,7 269,8 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
checkNotNullNoWhitespace(protocol, "protocol");
final String transport = protocol.toLowerCase(Locale.ROOT);
if (!"udp".equals(transport)) {
- throw new IllegalArgumentException(String.format("'%s' is not a supported protocol", transport));
+ throw new IllegalArgumentException(
+ String.format("'%s' is not a supported protocol", transport));
}
final String priority = this.getAttribute("priority");
checkNotNullNoWhitespace(priority, "priority");
@@ 284,7 298,15 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
if (ufrag != null) {
additionalParameter.put("ufrag", ufrag);
}
- final String parametersString = Joiner.on(' ').join(Collections2.transform(additionalParameter.entrySet(), input -> String.format("%s %s", input.getKey(), input.getValue())));
+ final String parametersString =
+ Joiner.on(' ')
+ .join(
+ Collections2.transform(
+ additionalParameter.entrySet(),
+ input ->
+ String.format(
+ "%s %s",
+ input.getKey(), input.getValue())));
return String.format(
"candidate:%s %s %s %s %s %s %s",
foundation,
@@ 293,20 315,19 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
priority,
connectionAddress,
port,
- parametersString
-
- );
+ parametersString);
}
}
private static void checkNotNullNoWhitespace(final String value, final String name) {
if (Strings.isNullOrEmpty(value)) {
- throw new IllegalArgumentException(String.format("Parameter %s is missing or empty", name));
+ throw new IllegalArgumentException(
+ String.format("Parameter %s is missing or empty", name));
}
- SessionDescription.checkNoWhitespace(value, String.format("Parameter %s contains white spaces", name));
+ SessionDescription.checkNoWhitespace(
+ value, String.format("Parameter %s contains white spaces", name));
}
-
public static class Fingerprint extends Element {
private Fingerprint() {
@@ 340,11 361,20 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return null;
}
- public static Fingerprint of(final SessionDescription sessionDescription, final SessionDescription.Media media) {
+ public static Fingerprint of(
+ final SessionDescription sessionDescription, final SessionDescription.Media media) {
final Fingerprint fingerprint = of(media.attributes);
return fingerprint == null ? of(sessionDescription.attributes) : fingerprint;
}
+ private static Fingerprint of(final Setup setup, final String hash, final String content) {
+ final Fingerprint fingerprint = new Fingerprint();
+ fingerprint.setContent(content);
+ fingerprint.setAttribute("hash", hash);
+ fingerprint.setAttribute("setup", setup.toString().toLowerCase(Locale.ROOT));
+ return fingerprint;
+ }
+
public String getHash() {
return this.getAttribute("hash");
}
@@ 356,7 386,9 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
}
public enum Setup {
- ACTPASS, PASSIVE, ACTIVE;
+ ACTPASS,
+ PASSIVE,
+ ACTIVE;
public static Setup of(String setup) {
try {
@@ 373,7 405,7 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
if (this == ACTIVE) {
return PASSIVE;
}
- throw new IllegalStateException(this.name()+" can not be flipped");
+ throw new IllegalStateException(this.name() + " can not be flipped");
}
}
}