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