IceUdpTransportInfo.java

  1package eu.siacs.conversations.xmpp.jingle.stanzas;
  2
  3import android.util.Log;
  4
  5import com.google.common.base.Function;
  6import com.google.common.base.Joiner;
  7import com.google.common.base.Preconditions;
  8import com.google.common.collect.ArrayListMultimap;
  9import com.google.common.collect.Collections2;
 10import com.google.common.collect.ImmutableList;
 11import com.google.common.collect.Iterables;
 12import com.google.common.collect.Lists;
 13
 14import org.checkerframework.checker.nullness.compatqual.NullableDecl;
 15
 16import java.util.Collection;
 17import java.util.HashMap;
 18import java.util.Hashtable;
 19import java.util.List;
 20import java.util.Map;
 21
 22import eu.siacs.conversations.Config;
 23import eu.siacs.conversations.xml.Element;
 24import eu.siacs.conversations.xml.Namespace;
 25import eu.siacs.conversations.xmpp.jingle.SessionDescription;
 26
 27public class IceUdpTransportInfo extends GenericTransportInfo {
 28
 29    private IceUdpTransportInfo() {
 30        super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP);
 31    }
 32
 33    public Fingerprint getFingerprint() {
 34        final Element fingerprint = this.findChild("fingerprint", Namespace.JINGLE_APPS_DTLS);
 35        return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
 36    }
 37
 38    public List<Candidate> getCandidates() {
 39        final ImmutableList.Builder<Candidate> builder = new ImmutableList.Builder<>();
 40        for (final Element child : getChildren()) {
 41            if ("candidate".equals(child.getName())) {
 42                builder.add(Candidate.upgrade(child));
 43            }
 44        }
 45        return builder.build();
 46    }
 47
 48    public static IceUdpTransportInfo upgrade(final Element element) {
 49        Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
 50        Preconditions.checkArgument(Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()), "Element does not match ice-udp transport namespace");
 51        final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
 52        transportInfo.setAttributes(element.getAttributes());
 53        transportInfo.setChildren(element.getChildren());
 54        return transportInfo;
 55    }
 56
 57    public IceUdpTransportInfo cloneWrapper() {
 58        final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
 59        transportInfo.setAttributes(new Hashtable<>(getAttributes()));
 60        return transportInfo;
 61    }
 62
 63    public static IceUdpTransportInfo of(SessionDescription sessionDescription, SessionDescription.Media media) {
 64        final String ufrag = Iterables.getFirst(media.attributes.get("ice-ufrag"), null);
 65        final String pwd = Iterables.getFirst(media.attributes.get("ice-pwd"), null);
 66        IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
 67        if (ufrag != null) {
 68            iceUdpTransportInfo.setAttribute("ufrag", ufrag);
 69        }
 70        if (pwd != null) {
 71            iceUdpTransportInfo.setAttribute("pwd", pwd);
 72        }
 73        final Fingerprint fingerprint = Fingerprint.of(sessionDescription, media);
 74        if (fingerprint != null) {
 75            iceUdpTransportInfo.addChild(fingerprint);
 76        }
 77        return iceUdpTransportInfo;
 78
 79    }
 80
 81    public static class Candidate extends Element {
 82
 83        private Candidate() {
 84            super("candidate");
 85        }
 86
 87        public int getComponent() {
 88            return getAttributeAsInt("component");
 89        }
 90
 91        public int getFoundation() {
 92            return getAttributeAsInt("foundation");
 93        }
 94
 95        public int getGeneration() {
 96            return getAttributeAsInt("generation");
 97        }
 98
 99        public String getId() {
100            return getAttribute("id");
101        }
102
103        public String getIp() {
104            return getAttribute("ip");
105        }
106
107        public int getNetwork() {
108            return getAttributeAsInt("network");
109        }
110
111        public int getPort() {
112            return getAttributeAsInt("port");
113        }
114
115        public int getPriority() {
116            return getAttributeAsInt("priority");
117        }
118
119        public String getProtocol() {
120            return getAttribute("protocol");
121        }
122
123        public String getRelAddr() {
124            return getAttribute("rel-addr");
125        }
126
127        public int getRelPort() {
128            return getAttributeAsInt("rel-port");
129        }
130
131        public String getType() { //TODO might be converted to enum
132            return getAttribute("type");
133        }
134
135        private int getAttributeAsInt(final String name) {
136            final String value = this.getAttribute(name);
137            if (value == null) {
138                return 0;
139            }
140            try {
141                return Integer.parseInt(value);
142            } catch (NumberFormatException e) {
143                return 0;
144            }
145        }
146
147        public static Candidate upgrade(final Element element) {
148            Preconditions.checkArgument("candidate".equals(element.getName()));
149            final Candidate candidate = new Candidate();
150            candidate.setAttributes(element.getAttributes());
151            candidate.setChildren(element.getChildren());
152            return candidate;
153        }
154
155        public String toSdpAttribute(final String ufrag) {
156            final String foundation = this.getAttribute("foundation");
157            final String component = this.getAttribute("component");
158            final String transport = this.getAttribute("protocol");
159            final String priority = this.getAttribute("priority");
160            final String connectionAddress = this.getAttribute("ip");
161            final String port = this.getAttribute("port");
162            final Map<String,String> additionalParameter = new HashMap<>();
163            final String relAddr = this.getAttribute("rel-addr");
164            if (relAddr != null) {
165                additionalParameter.put("raddr",relAddr);
166            }
167            final String relPort = this.getAttribute("rel-port");
168            if (relPort != null) {
169                additionalParameter.put("rport", relPort);
170            }
171            final String generation = this.getAttribute("generation");
172            if (generation != null) {
173                additionalParameter.put("generation", generation);
174            }
175            if (ufrag != null) {
176                additionalParameter.put("ufrag", ufrag);
177            }
178            final String parametersString = Joiner.on(' ').join(Collections2.transform(additionalParameter.entrySet(), input -> String.format("%s %s",input.getKey(),input.getValue())));
179            return String.format(
180                    "candidate:%s %s %s %s %s %s %s",
181                    foundation,
182                    component,
183                    transport,
184                    priority,
185                    connectionAddress,
186                    port,
187                    parametersString
188
189            );
190        }
191
192        // https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.1
193        public static Candidate fromSdpAttribute(final String attribute) {
194            final String[] pair = attribute.split(":", 2);
195            if (pair.length == 2 && "candidate".equals(pair[0])) {
196                final String[] segments = pair[1].split(" ");
197                if (segments.length >= 6) {
198                    final String foundation = segments[0];
199                    final String component = segments[1];
200                    final String transport = segments[2];
201                    final String priority = segments[3];
202                    final String connectionAddress = segments[4];
203                    final String port = segments[5];
204                    final HashMap<String, String> additional = new HashMap<>();
205                    for (int i = 6; i < segments.length - 1; i = i + 2) {
206                        additional.put(segments[i], segments[i + 1]);
207                    }
208                    final Candidate candidate = new Candidate();
209                    candidate.setAttribute("component", component);
210                    candidate.setAttribute("foundation", foundation);
211                    candidate.setAttribute("generation", additional.get("generation"));
212                    candidate.setAttribute("rel-addr", additional.get("raddr"));
213                    candidate.setAttribute("rel-port", additional.get("rport"));
214                    candidate.setAttribute("ip", connectionAddress);
215                    candidate.setAttribute("port", port);
216                    candidate.setAttribute("priority", priority);
217                    candidate.setAttribute("protocol", transport);
218                    candidate.setAttribute("type", additional.get("typ"));
219                    return candidate;
220                }
221            }
222            return null;
223        }
224    }
225
226
227    public static class Fingerprint extends Element {
228
229        public String getHash() {
230            return this.getAttribute("hash");
231        }
232
233        public String getSetup() {
234            return this.getAttribute("setup");
235        }
236
237        private Fingerprint() {
238            super("fingerprint", Namespace.JINGLE_APPS_DTLS);
239        }
240
241        public static Fingerprint upgrade(final Element element) {
242            Preconditions.checkArgument("fingerprint".equals(element.getName()));
243            Preconditions.checkArgument(Namespace.JINGLE_APPS_DTLS.equals(element.getNamespace()));
244            final Fingerprint fingerprint = new Fingerprint();
245            fingerprint.setAttributes(element.getAttributes());
246            fingerprint.setContent(element.getContent());
247            return fingerprint;
248        }
249
250        private static Fingerprint of(ArrayListMultimap<String, String> attributes) {
251            final String fingerprint = Iterables.getFirst(attributes.get("fingerprint"), null);
252            final String setup = Iterables.getFirst(attributes.get("setup"), null);
253            if (setup != null && fingerprint != null) {
254                final String[] fingerprintParts = fingerprint.split(" ", 2);
255                if (fingerprintParts.length == 2) {
256                    final String hash = fingerprintParts[0];
257                    final String actualFingerprint = fingerprintParts[1];
258                    final Fingerprint element = new Fingerprint();
259                    element.setAttribute("hash", hash);
260                    element.setAttribute("setup", setup);
261                    element.setContent(actualFingerprint);
262                    return element;
263                }
264            }
265            return null;
266        }
267
268        public static Fingerprint of(final SessionDescription sessionDescription, final SessionDescription.Media media) {
269            final Fingerprint fingerprint = of(media.attributes);
270            return fingerprint == null ? of(sessionDescription.attributes) : fingerprint;
271        }
272    }
273}