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