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}