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