1package eu.siacs.conversations.xmpp.jingle.stanzas;
2
3import android.util.Log;
4
5import androidx.annotation.NonNull;
6
7import com.google.common.base.Preconditions;
8import com.google.common.base.Strings;
9import com.google.common.collect.ImmutableSet;
10
11import java.util.Locale;
12import java.util.Set;
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 Content extends Element {
20
21 public Content(final Creator creator, final Senders senders, final String name) {
22 super("content", Namespace.JINGLE);
23 this.setAttribute("creator", creator.toString());
24 this.setAttribute("name", name);
25 this.setSenders(senders);
26 }
27
28 private Content() {
29 super("content", Namespace.JINGLE);
30 }
31
32 public static Content upgrade(final Element element) {
33 Preconditions.checkArgument("content".equals(element.getName()));
34 final Content content = new Content();
35 content.setAttributes(element.getAttributes());
36 content.setChildren(element.getChildren());
37 return content;
38 }
39
40 public String getContentName() {
41 return this.getAttribute("name");
42 }
43
44 public Creator getCreator() {
45 return Creator.of(getAttribute("creator"));
46 }
47
48 public Senders getSenders() {
49 final String attribute = getAttribute("senders");
50 if (Strings.isNullOrEmpty(attribute)) {
51 return Senders.BOTH;
52 }
53 return Senders.of(getAttribute("senders"));
54 }
55
56 public void setSenders(final Senders senders) {
57 if (senders != null && senders != Senders.BOTH) {
58 this.setAttribute("senders", senders.toString());
59 }
60 }
61
62 public GenericDescription getDescription() {
63 final Element description = this.findChild("description");
64 if (description == null) {
65 return null;
66 }
67 final String namespace = description.getNamespace();
68 if (FileTransferDescription.NAMESPACES.contains(namespace)) {
69 return FileTransferDescription.upgrade(description);
70 } else if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
71 return RtpDescription.upgrade(description);
72 } else {
73 return GenericDescription.upgrade(description);
74 }
75 }
76
77 public void setDescription(final GenericDescription description) {
78 Preconditions.checkNotNull(description);
79 this.addChild(description);
80 }
81
82 public String getDescriptionNamespace() {
83 final Element description = this.findChild("description");
84 return description == null ? null : description.getNamespace();
85 }
86
87 public GenericTransportInfo getTransport() {
88 final Element transport = this.findChild("transport");
89 final String namespace = transport == null ? null : transport.getNamespace();
90 if (Namespace.JINGLE_TRANSPORTS_IBB.equals(namespace)) {
91 return IbbTransportInfo.upgrade(transport);
92 } else if (Namespace.JINGLE_TRANSPORTS_S5B.equals(namespace)) {
93 return S5BTransportInfo.upgrade(transport);
94 } else if (Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(namespace)) {
95 return IceUdpTransportInfo.upgrade(transport);
96 } else if (transport != null) {
97 return GenericTransportInfo.upgrade(transport);
98 } else {
99 return null;
100 }
101 }
102
103
104 public void setTransport(GenericTransportInfo transportInfo) {
105 this.addChild(transportInfo);
106 }
107
108 public enum Creator {
109 INITIATOR,
110 RESPONDER;
111
112 public static Creator of(final String value) {
113 return Creator.valueOf(value.toUpperCase(Locale.ROOT));
114 }
115
116 @Override
117 @NonNull
118 public String toString() {
119 return super.toString().toLowerCase(Locale.ROOT);
120 }
121 }
122
123 public enum Senders {
124 BOTH,
125 INITIATOR,
126 NONE,
127 RESPONDER;
128
129 public static Senders of(final String value) {
130 return Senders.valueOf(value.toUpperCase(Locale.ROOT));
131 }
132
133 public static Senders of(final SessionDescription.Media media, final boolean initiator) {
134 final Set<String> attributes = media.attributes.keySet();
135 if (attributes.contains("sendrecv")) {
136 return BOTH;
137 } else if (attributes.contains("inactive")) {
138 return NONE;
139 } else if (attributes.contains("sendonly")) {
140 return initiator ? INITIATOR : RESPONDER;
141 } else if (attributes.contains("recvonly")) {
142 return initiator ? RESPONDER : INITIATOR;
143 }
144 Log.w(Config.LOGTAG,"assuming default value for senders");
145 // If none of the attributes "sendonly", "recvonly", "inactive", and "sendrecv" is
146 // present, "sendrecv" SHOULD be assumed as the default
147 // https://www.rfc-editor.org/rfc/rfc4566
148 return BOTH;
149 }
150
151 public static Set<Senders> receiveOnly(final boolean initiator) {
152 return ImmutableSet.of(initiator ? RESPONDER : INITIATOR);
153 }
154
155 @Override
156 @NonNull
157 public String toString() {
158 return super.toString().toLowerCase(Locale.ROOT);
159 }
160
161 public String asMediaAttribute(final boolean initiator) {
162 final boolean responder = !initiator;
163 if (this == Content.Senders.BOTH) {
164 return "sendrecv";
165 } else if (this == Content.Senders.NONE) {
166 return "inactive";
167 } else if ((initiator && this == Content.Senders.INITIATOR)
168 || (responder && this == Content.Senders.RESPONDER)) {
169 return "sendonly";
170 } else if ((initiator && this == Content.Senders.RESPONDER)
171 || (responder && this == Content.Senders.INITIATOR)) {
172 return "recvonly";
173 } else {
174 throw new IllegalStateException(
175 String.format(
176 "illegal combination of initiator=%s and %s", initiator, this));
177 }
178 }
179 }
180}