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