001/**
002 *
003 * Copyright the original author or authors
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.smackx.bytestreams.ibb.packet;
018
019import org.jivesoftware.smack.packet.PacketExtension;
020import org.jivesoftware.smack.util.StringUtils;
021import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
022
023/**
024 * Represents a chunk of data of an In-Band Bytestream within an IQ stanza or a
025 * message stanza
026 * 
027 * @author Henning Staib
028 */
029public class DataPacketExtension implements PacketExtension {
030
031    /**
032     * The element name of the data packet extension.
033     */
034    public final static String ELEMENT_NAME = "data";
035
036    /* unique session ID identifying this In-Band Bytestream */
037    private final String sessionID;
038
039    /* sequence of this packet in regard to the other data packets */
040    private final long seq;
041
042    /* the data contained in this packet */
043    private final String data;
044
045    private byte[] decodedData;
046
047    /**
048     * Creates a new In-Band Bytestream data packet.
049     * 
050     * @param sessionID unique session ID identifying this In-Band Bytestream
051     * @param seq sequence of this packet in regard to the other data packets
052     * @param data the base64 encoded data contained in this packet
053     */
054    public DataPacketExtension(String sessionID, long seq, String data) {
055        if (sessionID == null || "".equals(sessionID)) {
056            throw new IllegalArgumentException("Session ID must not be null or empty");
057        }
058        if (seq < 0 || seq > 65535) {
059            throw new IllegalArgumentException("Sequence must not be between 0 and 65535");
060        }
061        if (data == null) {
062            throw new IllegalArgumentException("Data must not be null");
063        }
064        this.sessionID = sessionID;
065        this.seq = seq;
066        this.data = data;
067    }
068
069    /**
070     * Returns the unique session ID identifying this In-Band Bytestream.
071     * 
072     * @return the unique session ID identifying this In-Band Bytestream
073     */
074    public String getSessionID() {
075        return sessionID;
076    }
077
078    /**
079     * Returns the sequence of this packet in regard to the other data packets.
080     * 
081     * @return the sequence of this packet in regard to the other data packets.
082     */
083    public long getSeq() {
084        return seq;
085    }
086
087    /**
088     * Returns the data contained in this packet.
089     * 
090     * @return the data contained in this packet.
091     */
092    public String getData() {
093        return data;
094    }
095
096    /**
097     * Returns the decoded data or null if data could not be decoded.
098     * <p>
099     * The encoded data is invalid if it contains bad Base64 input characters or
100     * if it contains the pad ('=') character on a position other than the last
101     * character(s) of the data. See <a
102     * href="http://xmpp.org/extensions/xep-0047.html#sec">XEP-0047</a> Section
103     * 6.
104     * 
105     * @return the decoded data
106     */
107    public byte[] getDecodedData() {
108        // return cached decoded data
109        if (this.decodedData != null) {
110            return this.decodedData;
111        }
112
113        // data must not contain the pad (=) other than end of data
114        if (data.matches(".*={1,2}+.+")) {
115            return null;
116        }
117
118        // decodeBase64 will return null if bad characters are included
119        this.decodedData = StringUtils.decodeBase64(data);
120        return this.decodedData;
121    }
122
123    public String getElementName() {
124        return ELEMENT_NAME;
125    }
126
127    public String getNamespace() {
128        return InBandBytestreamManager.NAMESPACE;
129    }
130
131    public String toXML() {
132        StringBuilder buf = new StringBuilder();
133        buf.append("<");
134        buf.append(getElementName());
135        buf.append(" ");
136        buf.append("xmlns=\"");
137        buf.append(InBandBytestreamManager.NAMESPACE);
138        buf.append("\" ");
139        buf.append("seq=\"");
140        buf.append(seq);
141        buf.append("\" ");
142        buf.append("sid=\"");
143        buf.append(sessionID);
144        buf.append("\">");
145        buf.append(data);
146        buf.append("</");
147        buf.append(getElementName());
148        buf.append(">");
149        return buf.toString();
150    }
151
152}