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}