1/**
2 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 *     http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14package org.jivesoftware.smackx.bytestreams.ibb.packet;
15
16import org.jivesoftware.smack.packet.PacketExtension;
17import org.jivesoftware.smack.util.StringUtils;
18import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
19
20/**
21 * Represents a chunk of data of an In-Band Bytestream within an IQ stanza or a
22 * message stanza
23 *
24 * @author Henning Staib
25 */
26public class DataPacketExtension implements PacketExtension {
27
28    /**
29     * The element name of the data packet extension.
30     */
31    public final static String ELEMENT_NAME = "data";
32
33    /* unique session ID identifying this In-Band Bytestream */
34    private final String sessionID;
35
36    /* sequence of this packet in regard to the other data packets */
37    private final long seq;
38
39    /* the data contained in this packet */
40    private final String data;
41
42    private byte[] decodedData;
43
44    /**
45     * Creates a new In-Band Bytestream data packet.
46     *
47     * @param sessionID unique session ID identifying this In-Band Bytestream
48     * @param seq sequence of this packet in regard to the other data packets
49     * @param data the base64 encoded data contained in this packet
50     */
51    public DataPacketExtension(String sessionID, long seq, String data) {
52        if (sessionID == null || "".equals(sessionID)) {
53            throw new IllegalArgumentException("Session ID must not be null or empty");
54        }
55        if (seq < 0 || seq > 65535) {
56            throw new IllegalArgumentException("Sequence must not be between 0 and 65535");
57        }
58        if (data == null) {
59            throw new IllegalArgumentException("Data must not be null");
60        }
61        this.sessionID = sessionID;
62        this.seq = seq;
63        this.data = data;
64    }
65
66    /**
67     * Returns the unique session ID identifying this In-Band Bytestream.
68     *
69     * @return the unique session ID identifying this In-Band Bytestream
70     */
71    public String getSessionID() {
72        return sessionID;
73    }
74
75    /**
76     * Returns the sequence of this packet in regard to the other data packets.
77     *
78     * @return the sequence of this packet in regard to the other data packets.
79     */
80    public long getSeq() {
81        return seq;
82    }
83
84    /**
85     * Returns the data contained in this packet.
86     *
87     * @return the data contained in this packet.
88     */
89    public String getData() {
90        return data;
91    }
92
93    /**
94     * Returns the decoded data or null if data could not be decoded.
95     * <p>
96     * The encoded data is invalid if it contains bad Base64 input characters or
97     * if it contains the pad ('=') character on a position other than the last
98     * character(s) of the data. See <a
99     * href="http://xmpp.org/extensions/xep-0047.html#sec">XEP-0047</a> Section
100     * 6.
101     *
102     * @return the decoded data
103     */
104    public byte[] getDecodedData() {
105        // return cached decoded data
106        if (this.decodedData != null) {
107            return this.decodedData;
108        }
109
110        // data must not contain the pad (=) other than end of data
111        if (data.matches(".*={1,2}+.+")) {
112            return null;
113        }
114
115        // decodeBase64 will return null if bad characters are included
116        this.decodedData = StringUtils.decodeBase64(data);
117        return this.decodedData;
118    }
119
120    public String getElementName() {
121        return ELEMENT_NAME;
122    }
123
124    public String getNamespace() {
125        return InBandBytestreamManager.NAMESPACE;
126    }
127
128    public String toXML() {
129        StringBuilder buf = new StringBuilder();
130        buf.append("<");
131        buf.append(getElementName());
132        buf.append(" ");
133        buf.append("xmlns=\"");
134        buf.append(InBandBytestreamManager.NAMESPACE);
135        buf.append("\" ");
136        buf.append("seq=\"");
137        buf.append(seq);
138        buf.append("\" ");
139        buf.append("sid=\"");
140        buf.append(sessionID);
141        buf.append("\">");
142        buf.append(data);
143        buf.append("</");
144        buf.append(getElementName());
145        buf.append(">");
146        return buf.toString();
147    }
148
149}
150