001/** 002 * 003 * Copyright 2003-2007 Jive Software. 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 */ 017 018package org.jivesoftware.smack.packet; 019 020import java.util.Locale; 021 022import org.jivesoftware.smack.util.Objects; 023import org.jivesoftware.smack.util.XmlStringBuilder; 024 025/** 026 * The base IQ (Info/Query) packet. IQ packets are used to get and set information 027 * on the server, including authentication, roster operations, and creating 028 * accounts. Each IQ stanza(/packet) has a specific type that indicates what type of action 029 * is being taken: "get", "set", "result", or "error".<p> 030 * 031 * IQ packets can contain a single child element that exists in a specific XML 032 * namespace. The combination of the element name and namespace determines what 033 * type of IQ stanza(/packet) it is. Some example IQ subpacket snippets:<ul> 034 * 035 * <li><query xmlns="jabber:iq:auth"> -- an authentication IQ. 036 * <li><query xmlns="jabber:iq:private"> -- a private storage IQ. 037 * <li><pubsub xmlns="http://jabber.org/protocol/pubsub"> -- a pubsub IQ. 038 * </ul> 039 * 040 * @author Matt Tucker 041 */ 042public abstract class IQ extends Stanza { 043 044 // Don't name this field 'ELEMENT'. When it comes to IQ, ELEMENT is the child element! 045 public static final String IQ_ELEMENT = "iq"; 046 public static final String QUERY_ELEMENT = "query"; 047 048 private final String childElementName; 049 private final String childElementNamespace; 050 051 private Type type = Type.get; 052 053 public IQ(IQ iq) { 054 super(iq); 055 type = iq.getType(); 056 this.childElementName = iq.childElementName; 057 this.childElementNamespace = iq.childElementNamespace; 058 } 059 060 protected IQ(String childElementName) { 061 this(childElementName, null); 062 } 063 064 protected IQ(String childElementName, String childElementNamespace) { 065 this.childElementName = childElementName; 066 this.childElementNamespace = childElementNamespace; 067 } 068 069 /** 070 * Returns the type of the IQ packet. 071 * 072 * @return the type of the IQ packet. 073 */ 074 public Type getType() { 075 return type; 076 } 077 078 /** 079 * Sets the type of the IQ packet. 080 * <p> 081 * Since the type of an IQ must present, an IllegalArgmentException will be thrown when type is 082 * <code>null</code>. 083 * </p> 084 * 085 * @param type the type of the IQ packet. 086 */ 087 public void setType(Type type) { 088 this.type = Objects.requireNonNull(type, "type must not be null"); 089 } 090 091 /** 092 * Return true if this IQ is a request IQ, i.e. an IQ of type {@link Type#get} or {@link Type#set}. 093 * 094 * @return true if IQ type is 'get' or 'set', false otherwise. 095 * @since 4.1 096 */ 097 public boolean isRequestIQ() { 098 switch (type) { 099 case get: 100 case set: 101 return true; 102 default: 103 return false; 104 } 105 } 106 107 public final String getChildElementName() { 108 return childElementName; 109 } 110 111 public final String getChildElementNamespace() { 112 return childElementNamespace; 113 } 114 115 @Override 116 public final XmlStringBuilder toXML() { 117 XmlStringBuilder buf = new XmlStringBuilder(); 118 buf.halfOpenElement(IQ_ELEMENT); 119 addCommonAttributes(buf); 120 if (type == null) { 121 buf.attribute("type", "get"); 122 } 123 else { 124 buf.attribute("type", type.toString()); 125 } 126 buf.rightAngleBracket(); 127 buf.append(getChildElementXML()); 128 buf.closeElement(IQ_ELEMENT); 129 return buf; 130 } 131 132 /** 133 * Returns the sub-element XML section of the IQ packet, or the empty String if there 134 * isn't one. 135 * 136 * @return the child element section of the IQ XML. 137 */ 138 public final XmlStringBuilder getChildElementXML() { 139 XmlStringBuilder xml = new XmlStringBuilder(); 140 if (type == Type.error) { 141 // Add the error sub-packet, if there is one. 142 appendErrorIfExists(xml); 143 } 144 else if (childElementName != null) { 145 // Add the query section if there is one. 146 IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); 147 if (iqChildElement != null) { 148 xml.append(iqChildElement); 149 XmlStringBuilder extensionsXml = getExtensionsXML(); 150 if (iqChildElement.isEmptyElement) { 151 if (extensionsXml.length() == 0) { 152 xml.closeEmptyElement(); 153 return xml; 154 } else { 155 xml.rightAngleBracket(); 156 } 157 } 158 xml.append(extensionsXml); 159 xml.closeElement(iqChildElement.element); 160 } 161 } 162 return xml; 163 } 164 165 /** 166 * This method must be overwritten by IQ subclasses to create their child content. It is important that the builder 167 * <b>does not include the final end element</b>. This will be done automatically by IQChildelementXmlStringBuilder 168 * after eventual existing stanza(/packet) extensions have been added. 169 * <p> 170 * For example to create an IQ with a extra attribute and an additional child element 171 * </p> 172 * <pre> 173 * {@code 174 * <iq to='foo@example.org' id='123'> 175 * <bar xmlns='example:bar' extraAttribute='blaz'> 176 * <extraElement>elementText</extraElement> 177 * </bar> 178 * </iq> 179 * } 180 * </pre> 181 * the body of the {@code getIQChildElementBuilder} looks like 182 * <pre> 183 * {@code 184 * // The builder 'xml' will already have the child element and the 'xmlns' attribute added 185 * // So the current builder state is "<bar xmlns='example:bar'" 186 * xml.attribute("extraAttribute", "blaz"); 187 * xml.rightAngleBracket(); 188 * xml.element("extraElement", "elementText"); 189 * // Do not close the 'bar' attribute by calling xml.closeElement('bar') 190 * } 191 * </pre> 192 * If your IQ only contains attributes and no child elements, i.e. it can be represented as empty element, then you 193 * can mark it as such. 194 * <pre> 195 * xml.attribute("myAttribute", "myAttributeValue"); 196 * xml.setEmptyElement(); 197 * </pre> 198 * If your IQ does not contain any attributes or child elements (besides stanza(/packet) extensions), consider sub-classing 199 * {@link SimpleIQ} instead. 200 * 201 * @param xml a pre-created builder which already has the child element and the 'xmlns' attribute set. 202 * @return the build to create the IQ child content. 203 */ 204 protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml); 205 206 /** 207 * Convenience method to create a new empty {@link Type#result IQ.Type.result} 208 * IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} 209 * IQ. The new stanza(/packet) will be initialized with:<ul> 210 * <li>The sender set to the recipient of the originating IQ. 211 * <li>The recipient set to the sender of the originating IQ. 212 * <li>The type set to {@link Type#result IQ.Type.result}. 213 * <li>The id set to the id of the originating IQ. 214 * <li>No child element of the IQ element. 215 * </ul> 216 * 217 * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet. 218 * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of 219 * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. 220 * @return a new {@link Type#result IQ.Type.result} IQ based on the originating IQ. 221 */ 222 public static IQ createResultIQ(final IQ request) { 223 return new EmptyResultIQ(request); 224 } 225 226 /** 227 * Convenience method to create a new {@link Type#error IQ.Type.error} IQ 228 * based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} 229 * IQ. The new stanza(/packet) will be initialized with:<ul> 230 * <li>The sender set to the recipient of the originating IQ. 231 * <li>The recipient set to the sender of the originating IQ. 232 * <li>The type set to {@link Type#error IQ.Type.error}. 233 * <li>The id set to the id of the originating IQ. 234 * <li>The child element contained in the associated originating IQ. 235 * <li>The provided {@link XMPPError XMPPError}. 236 * </ul> 237 * 238 * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet. 239 * @param error the error to associate with the created IQ packet. 240 * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of 241 * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. 242 * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. 243 */ 244 public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) { 245 if (!(request.getType() == Type.get || request.getType() == Type.set)) { 246 throw new IllegalArgumentException( 247 "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); 248 } 249 final ErrorIQ result = new ErrorIQ(error); 250 result.setStanzaId(request.getStanzaId()); 251 result.setFrom(request.getTo()); 252 result.setTo(request.getFrom()); 253 return result; 254 } 255 256 /** 257 * A enum to represent the type of the IQ stanza. 258 */ 259 public enum Type { 260 261 /** 262 * The IQ stanza requests information, inquires about what data is needed in order to complete further operations, etc. 263 */ 264 get, 265 266 /** 267 * The IQ stanza provides data that is needed for an operation to be completed, sets new values, replaces existing values, etc. 268 */ 269 set, 270 271 /** 272 * The IQ stanza is a response to a successful get or set request. 273 */ 274 result, 275 276 /** 277 * The IQ stanza reports an error that has occurred regarding processing or delivery of a get or set request. 278 */ 279 error, 280 ; 281 282 /** 283 * Converts a String into the corresponding types. Valid String values 284 * that can be converted to types are: "get", "set", "result", and "error". 285 * 286 * @param string the String value to covert. 287 * @return the corresponding Type. 288 * @throws IllegalArgumentException when not able to parse the string parameter 289 * @throws NullPointerException if the string is null 290 */ 291 public static Type fromString(String string) { 292 return Type.valueOf(string.toLowerCase(Locale.US)); 293 } 294 } 295 296 public static class IQChildElementXmlStringBuilder extends XmlStringBuilder { 297 private final String element; 298 299 private boolean isEmptyElement; 300 301 private IQChildElementXmlStringBuilder(IQ iq) { 302 this(iq.getChildElementName(), iq.getChildElementNamespace()); 303 } 304 305 public IQChildElementXmlStringBuilder(ExtensionElement pe) { 306 this(pe.getElementName(), pe.getNamespace()); 307 } 308 309 private IQChildElementXmlStringBuilder(String element, String namespace) { 310 prelude(element, namespace); 311 this.element = element; 312 } 313 314 public void setEmptyElement() { 315 isEmptyElement = true; 316 } 317 } 318}