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