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 String toString() { 117 StringBuilder sb = new StringBuilder(); 118 sb.append("IQ Stanza ("); 119 sb.append(getChildElementName()).append(' ').append(getChildElementNamespace()); 120 sb.append(") ["); 121 logCommonAttributes(sb); 122 sb.append("type=").append(type).append(','); 123 sb.append(']'); 124 return sb.toString(); 125 } 126 127 @Override 128 public final XmlStringBuilder toXML() { 129 XmlStringBuilder buf = new XmlStringBuilder(); 130 buf.halfOpenElement(IQ_ELEMENT); 131 addCommonAttributes(buf); 132 if (type == null) { 133 buf.attribute("type", "get"); 134 } 135 else { 136 buf.attribute("type", type.toString()); 137 } 138 buf.rightAngleBracket(); 139 buf.append(getChildElementXML()); 140 buf.closeElement(IQ_ELEMENT); 141 return buf; 142 } 143 144 /** 145 * Returns the sub-element XML section of the IQ packet, or the empty String if there 146 * isn't one. 147 * 148 * @return the child element section of the IQ XML. 149 */ 150 public final XmlStringBuilder getChildElementXML() { 151 XmlStringBuilder xml = new XmlStringBuilder(); 152 if (type == Type.error) { 153 // Add the error sub-packet, if there is one. 154 appendErrorIfExists(xml); 155 } 156 else if (childElementName != null) { 157 // Add the query section if there is one. 158 IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this)); 159 if (iqChildElement != null) { 160 xml.append(iqChildElement); 161 XmlStringBuilder extensionsXml = getExtensionsXML(); 162 if (iqChildElement.isEmptyElement) { 163 if (extensionsXml.length() == 0) { 164 xml.closeEmptyElement(); 165 return xml; 166 } else { 167 xml.rightAngleBracket(); 168 } 169 } 170 xml.append(extensionsXml); 171 xml.closeElement(iqChildElement.element); 172 } 173 } 174 return xml; 175 } 176 177 /** 178 * This method must be overwritten by IQ subclasses to create their child content. It is important you don't use the builder 179 * <b>to add the final end tag</b>. This will be done automatically by {@link IQChildElementXmlStringBuilder} 180 * after eventual existing {@link ExtensionElement}s have been added. 181 * <p> 182 * For example to create an IQ with a extra attribute and an additional child element 183 * </p> 184 * <pre> 185 * {@code 186 * <iq to='foo@example.org' id='123'> 187 * <bar xmlns='example:bar' extraAttribute='blaz'> 188 * <extraElement>elementText</extraElement> 189 * </bar> 190 * </iq> 191 * } 192 * </pre> 193 * the body of the {@code getIQChildElementBuilder} looks like 194 * <pre> 195 * {@code 196 * // The builder 'xml' will already have the child element and the 'xmlns' attribute added 197 * // So the current builder state is "<bar xmlns='example:bar'" 198 * xml.attribute("extraAttribute", "blaz"); 199 * xml.rightAngleBracket(); 200 * xml.element("extraElement", "elementText"); 201 * // Do not close the 'bar' attribute by calling xml.closeElement('bar') 202 * } 203 * </pre> 204 * If your IQ only contains attributes and no child elements, i.e. it can be represented as empty element, then you 205 * can mark it as such. 206 * <pre> 207 * xml.attribute("myAttribute", "myAttributeValue"); 208 * xml.setEmptyElement(); 209 * </pre> 210 * If your IQ does not contain any attributes or child elements (besides {@link ExtensionElement}s), consider sub-classing 211 * {@link SimpleIQ} instead. 212 * 213 * @param xml a pre-created builder which already has the child element and the 'xmlns' attribute set. 214 * @return the build to create the IQ child content. 215 */ 216 protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml); 217 218 /** 219 * @deprecated use {@link #initializeAsResultFor(IQ)} instead. 220 */ 221 @Deprecated 222 protected final void initialzeAsResultFor(IQ request) { 223 initializeAsResultFor(request); 224 } 225 226 protected final void initializeAsResultFor(IQ request) { 227 if (!(request.getType() == Type.get || request.getType() == Type.set)) { 228 throw new IllegalArgumentException( 229 "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); 230 } 231 setStanzaId(request.getStanzaId()); 232 setFrom(request.getTo()); 233 setTo(request.getFrom()); 234 setType(Type.result); 235 } 236 237 /** 238 * Convenience method to create a new empty {@link Type#result IQ.Type.result} 239 * IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} 240 * IQ. The new stanza(/packet) will be initialized with:<ul> 241 * <li>The sender set to the recipient of the originating IQ. 242 * <li>The recipient set to the sender of the originating IQ. 243 * <li>The type set to {@link Type#result IQ.Type.result}. 244 * <li>The id set to the id of the originating IQ. 245 * <li>No child element of the IQ element. 246 * </ul> 247 * 248 * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet. 249 * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of 250 * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. 251 * @return a new {@link Type#result IQ.Type.result} IQ based on the originating IQ. 252 */ 253 public static IQ createResultIQ(final IQ request) { 254 return new EmptyResultIQ(request); 255 } 256 257 /** 258 * Convenience method to create a new {@link Type#error IQ.Type.error} IQ 259 * based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} 260 * IQ. The new stanza(/packet) will be initialized with:<ul> 261 * <li>The sender set to the recipient of the originating IQ. 262 * <li>The recipient set to the sender of the originating IQ. 263 * <li>The type set to {@link Type#error IQ.Type.error}. 264 * <li>The id set to the id of the originating IQ. 265 * <li>The child element contained in the associated originating IQ. 266 * <li>The provided {@link XMPPError XMPPError}. 267 * </ul> 268 * 269 * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet. 270 * @param error the error to associate with the created IQ packet. 271 * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of 272 * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. 273 * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. 274 */ 275 public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Builder error) { 276 if (!(request.getType() == Type.get || request.getType() == Type.set)) { 277 throw new IllegalArgumentException( 278 "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); 279 } 280 final ErrorIQ result = new ErrorIQ(error); 281 result.setStanzaId(request.getStanzaId()); 282 result.setFrom(request.getTo()); 283 result.setTo(request.getFrom()); 284 285 error.setStanza(result); 286 287 return result; 288 } 289 290 public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Condition condition) { 291 return createErrorResponse(request, XMPPError.getBuilder(condition)); 292 } 293 294 /** 295 * Convenience method to create a new {@link Type#error IQ.Type.error} IQ 296 * based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} 297 * IQ. The new stanza(/packet) will be initialized with:<ul> 298 * <li>The sender set to the recipient of the originating IQ. 299 * <li>The recipient set to the sender of the originating IQ. 300 * <li>The type set to {@link Type#error IQ.Type.error}. 301 * <li>The id set to the id of the originating IQ. 302 * <li>The child element contained in the associated originating IQ. 303 * <li>The provided {@link XMPPError XMPPError}. 304 * </ul> 305 * 306 * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet. 307 * @param error the error to associate with the created IQ packet. 308 * @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of 309 * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. 310 * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. 311 */ 312 public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) { 313 return createErrorResponse(request, XMPPError.getBuilder(error)); 314 } 315 316 /** 317 * A enum to represent the type of the IQ stanza. 318 */ 319 public enum Type { 320 321 /** 322 * The IQ stanza requests information, inquires about what data is needed in order to complete further operations, etc. 323 */ 324 get, 325 326 /** 327 * The IQ stanza provides data that is needed for an operation to be completed, sets new values, replaces existing values, etc. 328 */ 329 set, 330 331 /** 332 * The IQ stanza is a response to a successful get or set request. 333 */ 334 result, 335 336 /** 337 * The IQ stanza reports an error that has occurred regarding processing or delivery of a get or set request. 338 */ 339 error, 340 ; 341 342 /** 343 * Converts a String into the corresponding types. Valid String values 344 * that can be converted to types are: "get", "set", "result", and "error". 345 * 346 * @param string the String value to covert. 347 * @return the corresponding Type. 348 * @throws IllegalArgumentException when not able to parse the string parameter 349 * @throws NullPointerException if the string is null 350 */ 351 public static Type fromString(String string) { 352 return Type.valueOf(string.toLowerCase(Locale.US)); 353 } 354 } 355 356 public static class IQChildElementXmlStringBuilder extends XmlStringBuilder { 357 private final String element; 358 359 private boolean isEmptyElement; 360 361 private IQChildElementXmlStringBuilder(IQ iq) { 362 this(iq.getChildElementName(), iq.getChildElementNamespace()); 363 } 364 365 public IQChildElementXmlStringBuilder(ExtensionElement pe) { 366 this(pe.getElementName(), pe.getNamespace()); 367 } 368 369 private IQChildElementXmlStringBuilder(String element, String namespace) { 370 prelude(element, namespace); 371 this.element = element; 372 } 373 374 public void setEmptyElement() { 375 isEmptyElement = true; 376 } 377 } 378}