1package eu.siacs.conversations.xmpp.jingle.stanzas;
2
3import androidx.annotation.NonNull;
4
5import com.google.common.base.CaseFormat;
6import com.google.common.base.Preconditions;
7import com.google.common.base.Strings;
8import com.google.common.collect.ImmutableMap;
9
10import eu.siacs.conversations.xml.Element;
11import eu.siacs.conversations.xml.Namespace;
12import eu.siacs.conversations.xmpp.Jid;
13import eu.siacs.conversations.xmpp.stanzas.IqPacket;
14
15import java.util.Map;
16
17public class JinglePacket extends IqPacket {
18
19 private JinglePacket() {
20 super();
21 }
22
23 public JinglePacket(final Action action, final String sessionId) {
24 super(TYPE.SET);
25 final Element jingle = addChild("jingle", Namespace.JINGLE);
26 jingle.setAttribute("sid", sessionId);
27 jingle.setAttribute("action", action.toString());
28 }
29
30 public static JinglePacket upgrade(final IqPacket iqPacket) {
31 Preconditions.checkArgument(iqPacket.hasChild("jingle", Namespace.JINGLE));
32 Preconditions.checkArgument(iqPacket.getType() == TYPE.SET);
33 final JinglePacket jinglePacket = new JinglePacket();
34 jinglePacket.setAttributes(iqPacket.getAttributes());
35 jinglePacket.setChildren(iqPacket.getChildren());
36 return jinglePacket;
37 }
38
39 // TODO deprecate this somehow and make file transfer fail if there are multiple (or something)
40 public Content getJingleContent() {
41 final Element content = getJingleChild("content");
42 return content == null ? null : Content.upgrade(content);
43 }
44
45 public Group getGroup() {
46 final Element jingle = findChild("jingle", Namespace.JINGLE);
47 final Element group = jingle.findChild("group", Namespace.JINGLE_APPS_GROUPING);
48 return group == null ? null : Group.upgrade(group);
49 }
50
51 public void addGroup(final Group group) {
52 this.addJingleChild(group);
53 }
54
55 public Map<String, Content> getJingleContents() {
56 final Element jingle = findChild("jingle", Namespace.JINGLE);
57 ImmutableMap.Builder<String, Content> builder = new ImmutableMap.Builder<>();
58 for (final Element child : jingle.getChildren()) {
59 if ("content".equals(child.getName())) {
60 final Content content = Content.upgrade(child);
61 builder.put(content.getContentName(), content);
62 }
63 }
64 return builder.build();
65 }
66
67 public void addJingleContent(final Content content) { // take content interface
68 addJingleChild(content);
69 }
70
71 public ReasonWrapper getReason() {
72 final Element reasonElement = getJingleChild("reason");
73 if (reasonElement == null) {
74 return new ReasonWrapper(Reason.UNKNOWN, null);
75 }
76 String text = null;
77 Reason reason = Reason.UNKNOWN;
78 for (Element child : reasonElement.getChildren()) {
79 if ("text".equals(child.getName())) {
80 text = child.getContent();
81 } else {
82 reason = Reason.of(child.getName());
83 }
84 }
85 return new ReasonWrapper(reason, text);
86 }
87
88 public void setReason(final Reason reason, final String text) {
89 final Element jingle = findChild("jingle", Namespace.JINGLE);
90 final Element reasonElement = jingle.addChild("reason");
91 reasonElement.addChild(reason.toString());
92 if (!Strings.isNullOrEmpty(text)) {
93 reasonElement.addChild("text").setContent(text);
94 }
95 }
96
97 // RECOMMENDED for session-initiate, NOT RECOMMENDED otherwise
98 public void setInitiator(final Jid initiator) {
99 Preconditions.checkArgument(initiator.isFullJid(), "initiator should be a full JID");
100 findChild("jingle", Namespace.JINGLE).setAttribute("initiator", initiator);
101 }
102
103 // RECOMMENDED for session-accept, NOT RECOMMENDED otherwise
104 public void setResponder(Jid responder) {
105 Preconditions.checkArgument(responder.isFullJid(), "responder should be a full JID");
106 findChild("jingle", Namespace.JINGLE).setAttribute("responder", responder);
107 }
108
109 public Element getJingleChild(final String name) {
110 final Element jingle = findChild("jingle", Namespace.JINGLE);
111 return jingle == null ? null : jingle.findChild(name);
112 }
113
114 public void addJingleChild(final Element child) {
115 final Element jingle = findChild("jingle", Namespace.JINGLE);
116 jingle.addChild(child);
117 }
118
119 public String getSessionId() {
120 return findChild("jingle", Namespace.JINGLE).getAttribute("sid");
121 }
122
123 public Action getAction() {
124 return Action.of(findChild("jingle", Namespace.JINGLE).getAttribute("action"));
125 }
126
127 public enum Action {
128 CONTENT_ACCEPT,
129 CONTENT_ADD,
130 CONTENT_MODIFY,
131 CONTENT_REJECT,
132 CONTENT_REMOVE,
133 DESCRIPTION_INFO,
134 SECURITY_INFO,
135 SESSION_ACCEPT,
136 SESSION_INFO,
137 SESSION_INITIATE,
138 SESSION_TERMINATE,
139 TRANSPORT_ACCEPT,
140 TRANSPORT_INFO,
141 TRANSPORT_REJECT,
142 TRANSPORT_REPLACE;
143
144 public static Action of(final String value) {
145 // TODO handle invalid
146 return Action.valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, value));
147 }
148
149 @Override
150 @NonNull
151 public String toString() {
152 return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString());
153 }
154 }
155
156 public static class ReasonWrapper {
157 public final Reason reason;
158 public final String text;
159
160 public ReasonWrapper(Reason reason, String text) {
161 this.reason = reason;
162 this.text = text;
163 }
164 }
165}