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