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 (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
68 return RtpDescription.upgrade(description);
69 } else {
70 return GenericDescription.upgrade(description);
71 }
72 }
73
74 public void setDescription(final GenericDescription description) {
75 Preconditions.checkNotNull(description);
76 this.addChild(description);
77 }
78
79 public String getDescriptionNamespace() {
80 final Element description = this.findChild("description");
81 return description == null ? null : description.getNamespace();
82 }
83
84 public GenericTransportInfo getTransport() {
85 final Element transport = this.findChild("transport");
86 final String namespace = transport == null ? null : transport.getNamespace();
87 if (Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(namespace)) {
88 return IceUdpTransportInfo.upgrade(transport);
89 } else if (transport != null) {
90 return GenericTransportInfo.upgrade(transport);
91 } else {
92 return null;
93 }
94 }
95
96 public void setTransport(GenericTransportInfo transportInfo) {
97 this.addChild(transportInfo);
98 }
99
100 public enum Creator {
101 INITIATOR,
102 RESPONDER;
103
104 public static Creator of(final String value) {
105 return Creator.valueOf(value.toUpperCase(Locale.ROOT));
106 }
107
108 @Override
109 @NonNull
110 public String toString() {
111 return super.toString().toLowerCase(Locale.ROOT);
112 }
113 }
114
115 public enum Senders {
116 BOTH,
117 INITIATOR,
118 NONE,
119 RESPONDER;
120
121 public static Senders of(final String value) {
122 return Senders.valueOf(value.toUpperCase(Locale.ROOT));
123 }
124
125 public static Senders of(final SessionDescription.Media media, final boolean initiator) {
126 final Set<String> attributes = media.attributes.keySet();
127 if (attributes.contains("sendrecv")) {
128 return BOTH;
129 } else if (attributes.contains("inactive")) {
130 return NONE;
131 } else if (attributes.contains("sendonly")) {
132 return initiator ? INITIATOR : RESPONDER;
133 } else if (attributes.contains("recvonly")) {
134 return initiator ? RESPONDER : INITIATOR;
135 }
136 Log.w(Config.LOGTAG,"assuming default value for senders");
137 // If none of the attributes "sendonly", "recvonly", "inactive", and "sendrecv" is
138 // present, "sendrecv" SHOULD be assumed as the default
139 // https://www.rfc-editor.org/rfc/rfc4566
140 return BOTH;
141 }
142
143 @Override
144 @NonNull
145 public String toString() {
146 return super.toString().toLowerCase(Locale.ROOT);
147 }
148
149 public String asMediaAttribute(final boolean initiator) {
150 final boolean responder = !initiator;
151 if (this == Content.Senders.BOTH) {
152 return "sendrecv";
153 } else if (this == Content.Senders.NONE) {
154 return "inactive";
155 } else if ((initiator && this == Content.Senders.INITIATOR)
156 || (responder && this == Content.Senders.RESPONDER)) {
157 return "sendonly";
158 } else if ((initiator && this == Content.Senders.RESPONDER)
159 || (responder && this == Content.Senders.INITIATOR)) {
160 return "recvonly";
161 } else {
162 throw new IllegalStateException(
163 String.format(
164 "illegal combination of initiator=%s and %s", initiator, this));
165 }
166 }
167 }
168}