001/**
002 *
003 * Copyright © 2014-2021 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.sm.packet;
018
019import java.util.Collections;
020import java.util.List;
021
022import javax.xml.namespace.QName;
023
024import org.jivesoftware.smack.packet.ExtensionElement;
025import org.jivesoftware.smack.packet.Nonza;
026import org.jivesoftware.smack.packet.StanzaError;
027import org.jivesoftware.smack.packet.StanzaErrorTextElement;
028import org.jivesoftware.smack.util.XmlStringBuilder;
029
030public class StreamManagement {
031    public static final String NAMESPACE = "urn:xmpp:sm:3";
032
033    public static final class StreamManagementFeature implements ExtensionElement {
034
035        public static final String ELEMENT = "sm";
036        public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
037
038        public static final StreamManagementFeature INSTANCE = new StreamManagementFeature();
039
040        private StreamManagementFeature() {
041        }
042
043        @Override
044        public String getElementName() {
045            return QNAME.getLocalPart();
046        }
047
048        @Override
049        public String getNamespace() {
050            return QNAME.getNamespaceURI();
051        }
052
053        @Override
054        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
055            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
056            xml.closeEmptyElement();
057            return xml;
058        }
059    }
060
061    private abstract static class AbstractEnable implements Nonza {
062
063        /**
064         * Preferred maximum resumption time in seconds (optional).
065         */
066        protected int max = -1;
067
068        protected boolean resume = false;
069
070        protected void maybeAddResumeAttributeTo(XmlStringBuilder xml) {
071            if (resume) {
072                // XEP 198 never mentions the case where resume='false', it's either set to true or
073                // not set at all. We reflect this in this code part
074                xml.attribute("resume", "true");
075            }
076        }
077
078        protected void maybeAddMaxAttributeTo(XmlStringBuilder xml) {
079            if (max > 0) {
080                xml.attribute("max", Integer.toString(max));
081            }
082        }
083
084        public boolean isResumeSet() {
085            return resume;
086        }
087
088        /**
089         * Return the max resumption time in seconds.
090         * @return the max resumption time in seconds
091         */
092        public int getMaxResumptionTime() {
093            return max;
094        }
095
096        @Override
097        public final String getNamespace() {
098            return NAMESPACE;
099        }
100    }
101
102    public static class Enable extends AbstractEnable {
103        public static final String ELEMENT = "enable";
104
105        public static final Enable INSTANCE = new Enable();
106
107        private Enable() {
108        }
109
110        public Enable(boolean resume) {
111            this.resume = resume;
112        }
113
114        public Enable(boolean resume, int max) {
115            this(resume);
116            this.max = max;
117        }
118
119        @Override
120        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
121            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
122            maybeAddResumeAttributeTo(xml);
123            maybeAddMaxAttributeTo(xml);
124            xml.closeEmptyElement();
125            return xml;
126        }
127
128        @Override
129        public String getElementName() {
130            return ELEMENT;
131        }
132    }
133
134    /**
135     * A Stream Management 'enabled' element.
136     * <p>
137     * Here is a full example, all attributes besides 'xmlns' are optional.
138     * </p>
139     * <pre>
140     * {@code
141     * <enabled xmlns='urn:xmpp:sm:3'
142     *      id='some-long-sm-id'
143     *      location='[2001:41D0:1:A49b::1]:9222'
144     *      resume='true'/>
145     * }
146     * </pre>
147     */
148    public static class Enabled extends AbstractEnable {
149        public static final String ELEMENT = "enabled";
150
151        /**
152         * The stream id ("SM-ID")
153         */
154        private final String id;
155
156        /**
157         * The location where the server prefers reconnection.
158         */
159        private final String location;
160
161        public Enabled(String id, boolean resume) {
162            this(id, resume, null, -1);
163        }
164
165        public Enabled(String id, boolean resume, String location, int max) {
166            this.id = id;
167            this.resume = resume;
168            this.location = location;
169            this.max = max;
170        }
171
172        public String getId() {
173            return id;
174        }
175
176        public String getLocation() {
177            return location;
178        }
179
180        @Override
181        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
182            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
183            xml.optAttribute("id", id);
184            maybeAddResumeAttributeTo(xml);
185            xml.optAttribute("location", location);
186            maybeAddMaxAttributeTo(xml);
187            xml.closeEmptyElement();
188            return xml;
189        }
190
191        @Override
192        public String getElementName() {
193            return ELEMENT;
194        }
195    }
196
197    public static class Failed implements Nonza {
198        public static final String ELEMENT = "failed";
199
200        private final StanzaError.Condition condition;
201
202        private final List<StanzaErrorTextElement> textElements;
203
204
205        public Failed() {
206            this(null, null);
207        }
208
209        public Failed(StanzaError.Condition condition, List<StanzaErrorTextElement> textElements) {
210            this.condition = condition;
211            if (textElements == null) {
212                this.textElements = Collections.emptyList();
213            } else {
214                this.textElements = Collections.unmodifiableList(textElements);
215            }
216        }
217
218        public StanzaError.Condition getStanzaErrorCondition() {
219            return condition;
220        }
221
222        public List<StanzaErrorTextElement> getTextElements() {
223            return textElements;
224        }
225
226        @Override
227        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
228            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
229            if (condition == null && textElements.isEmpty()) {
230                xml.closeEmptyElement();
231            } else {
232                if (condition != null) {
233                    xml.rightAngleBracket();
234                    xml.append(condition.toString());
235                    xml.xmlnsAttribute(StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE);
236                    xml.closeEmptyElement();
237                }
238                xml.append(textElements);
239                xml.closeElement(ELEMENT);
240            }
241            return xml;
242        }
243
244        @Override
245        public String getNamespace() {
246            return NAMESPACE;
247        }
248
249        @Override
250        public String getElementName() {
251            return ELEMENT;
252        }
253
254    }
255
256    private abstract static class AbstractResume implements Nonza {
257
258        private final long handledCount;
259        private final String previd;
260
261        private AbstractResume(long handledCount, String previd) {
262            this.handledCount = handledCount;
263            this.previd = previd;
264        }
265
266        public long getHandledCount() {
267            return handledCount;
268        }
269
270        public String getPrevId() {
271            return previd;
272        }
273
274        @Override
275        public final String getNamespace() {
276            return NAMESPACE;
277        }
278
279        @Override
280        public final XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
281            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
282            xml.attribute("h", Long.toString(handledCount));
283            xml.attribute("previd", previd);
284            xml.closeEmptyElement();
285            return xml;
286        }
287    }
288
289    public static class Resume extends AbstractResume {
290        public static final String ELEMENT = "resume";
291
292        public Resume(long handledCount, String previd) {
293            super(handledCount, previd);
294        }
295
296        @Override
297        public String getElementName() {
298            return ELEMENT;
299        }
300    }
301
302    public static class Resumed extends AbstractResume {
303        public static final String ELEMENT = "resumed";
304
305        public Resumed(long handledCount, String previd) {
306            super(handledCount, previd);
307        }
308
309        @Override
310        public String getElementName() {
311            return ELEMENT;
312        }
313    }
314
315    public static class AckAnswer implements Nonza {
316        public static final String ELEMENT = "a";
317
318        private final long handledCount;
319
320        public AckAnswer(long handledCount) {
321            this.handledCount = handledCount;
322        }
323
324        public long getHandledCount() {
325            return handledCount;
326        }
327
328        @Override
329        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
330            XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
331            xml.attribute("h", Long.toString(handledCount));
332            xml.closeEmptyElement();
333            return xml;
334        }
335
336        @Override
337        public String getNamespace() {
338            return NAMESPACE;
339        }
340
341        @Override
342        public String getElementName() {
343            return ELEMENT;
344        }
345    }
346
347    public static final class AckRequest implements Nonza {
348        public static final String ELEMENT = "r";
349        public static final AckRequest INSTANCE = new AckRequest();
350
351        private AckRequest() {
352        }
353
354        @Override
355        public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
356            return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
357        }
358
359        @Override
360        public String getNamespace() {
361            return NAMESPACE;
362        }
363
364        @Override
365        public String getElementName() {
366            return ELEMENT;
367        }
368    }
369}