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