/* * @(#)Packet.java 2010.01.16 at 07:05:30 GMT * * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.server; //~--- non-JDK imports -------------------------------------------------------- import tigase.util.TigaseStringprepException; import tigase.xml.Element; import tigase.xmpp.JID; import tigase.xmpp.StanzaType; //~--- JDK imports ------------------------------------------------------------ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; //~--- classes ---------------------------------------------------------------- /** * Objects of this class carry a single XMPP packet (stanza). * The XMPP stanza is carried as an XML element in DOM structure by the * Packet object which contains some extra information and convenience methods * to quickly access the most important stanza information.<p/> * The stanza is accessible directly through the <code>getElement()</code> method * and then it can be handles as an XML object. <br/> * <strong>Please note! Even though the <code>Packet</code> object and carried the * stanza <code>Element</code> is not unmodifiable it should be treated as such. This * particular <code>Packet</code> can be processed concurrently at the same time in * different components or plugins of the Tigase server. Modifying it may lead to * unexpected and hard to diagnoze behaviours. Every time you want to change or * update the object you should obtaina a copy of it using one of the utility methods: * <code>copyElementOnly()</code>, <code>swapFromTo(...)</code>, * <code>errorResult(...)</code>, <code>okResult(...)</code>, * <code>swapStanzaFromTo(...)</code></strong><p/> * There are no public constructors for the class, instead you have to use factory * methods: <code>packetInstance(...)</code> which return instance of one of the * classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. * While creating a new <code>Packet</code> instance JIDs are parsed and processed * through the stringprep. Hence some of the factory methods may throw * <code>TigaseStringprepException</code> exception. You can avoid this by using * the methods which accept preparsed JIDs. Reusing preparsed JIDs is highly * recommended. * <p/> * There are 3 kinds of addresses available from the <code>Packet</code> object: * <em>PacketFrom/To</em>, <em>StanzaFrom/To</em> and <em>From/To</em>.<br/> * <em>Stanza</em> addresses are the normal XMPP addresses parsed from the XML * stanza and as a convenience are available through methods as JID objects. This is * not only convenient to the developer but also this is important for performance * reasons as parsing JID and processing it through stringprep is quite expensive * operation so it is better to do it once and reuse the parsed objects. Please note * that any of them can be null. Note also. You should avoid parsing stanza JIDs * from the XML element in your code as this may impact the server performance. * Reuse the JIDs provided from the <code>Packet</code> methods.<br/> * <em>Packet</em> addresses are also JID objects but they may contain a different * values from the <em>Stanza</em> addresses. These are the Tigase internal * addresses used by the server and they usually contain Tigase component source * and destination address. In most cases they are used between connection managers * and session managers and can be ignored by other code. One advantage of setting * <code>PacketFrom</code> address to address of your component * (<code>getComponentId()</code>) address is that if there is a packet delivery problem * it will be returned back to the sender with apropriate error message.<br/> * <em>Simple From/To</em> addresses contains values following the logic: If * PacketFrom/To is not null then it contains PacketFrom/To values otherwise it * contains StanzaFrom/To values. This is because the Tigase server tries always * to deliver and process the <code>Packet</code> using PacketFrom/To addresses if * they are null then Stanza addresses are used instead. So these are just convenience * methods which allow avoiding extra <code>IFs</code> in the program code and also * save some CPU cycles. * * * Created: Tue Nov 22 07:07:11 2005 * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class Packet { private static final String ERROR_NS = "urn:ietf:params:xml:ns:xmpp-stanzas"; /** * The variable control whether the toStringSecure() hides all the CData information * from stanzas printed to logs or logs the full, detailed stanza content. By default the * variable is set to 'false' to protect users' privacy and not reveail chat content. * This is the value to be used in all production/live systems. For the debug purposes * on the test or development system it can be set to 'true' to help diagnose run-time * problems.<p/> * You can change value of the field by setting system property: * <code>'packet.debug.full'</code> to <code>'true'</code>. */ public static boolean FULL_DEBUG = Boolean.getBoolean("packet.debug.full"); public static final String CLIENT_XMLNS = "jabber:client"; //~--- fields --------------------------------------------------------------- ///** // * For internal Tigase use only. The session manager stores in stanza the old, original // * address while the packet is processd. This is sometimes necessary as the SM works // * for many virtual domains and the main SM address may be different from the address // * the user has put to the stanza. // */ //public static final String OLDFROM = "oldfrom"; // ///** // * Constant <code>OLDTO</code> is kind of hack to store old request address // * when the packet is processed by the session manager. The problem is that // * SessionManager may work for many virtual domains but has just one real // * address. So to forward the request to the SessionManager the 'to' address // * is replaced with the real SessionManager address. The response however // * needs to be sent with the 'from' address as the original request was 'to'. // * Therefore 'oldto' attribute temporarly stores the old 'to' address // * and after the packet processing is completed the 'from' attribute // * is replaced with original 'to' value. // * // */ //public static final String OLDTO = "oldto"; private JID packetFrom = null; private JID packetTo = null; private String packetToString = null; private String packetToStringSecure = null; private Set<String> processorsIds = new LinkedHashSet<String>(4, 0.9f); private JID stanzaFrom = null; private String stanzaId = null; private JID stanzaTo = null; private Priority priority = Priority.NORMAL; private Permissions permissions = Permissions.NONE; protected Element elem; private boolean routed; private StanzaType type; //~--- constructors --------------------------------------------------------- /** * A constructor creating the <code>Packet</code> instance. This is not part of * the public API, please use <code>packetInstance(...)</code> instead. * * @param elem is XML element with a single XMPP stanza. * @throws TigaseStringprepException exception is thrown if the stanza source or * destination address stringprep processing failed. */ protected Packet(final Element elem) throws TigaseStringprepException { setElem(elem); initVars(); } /** * A constructor creating the <code>Packet</code> instance. This is not part of * the public API, please use <code>packetInstance(...)</code> instead. * * @param elem is XML element with a single XMPP stanza. * @param stanzaFrom is a source JID address of the stanza passed as the * contructor parameter. * @param stanzaTo is a destination JID address of the stanza passed as the * constructor parameter. */ protected Packet(final Element elem, JID stanzaFrom, JID stanzaTo) { setElem(elem); initVars(stanzaFrom, stanzaTo); } //~--- methods -------------------------------------------------------------- /** * Method description * * * @param el * * @return */ public static String elemToString(Element el) { String elemData = el.toString(); int size = elemData.length(); if (size > 1024) { elemData = elemData.substring(0, 1024) + " ... "; } return elemData; } /** * Method description * * * @param el * * @return */ public static String elemToStringSecure(Element el) { String elemData = el.toStringSecure(); int size = elemData.length(); if (size > 1024) { elemData = elemData.substring(0, 1024) + " ... "; } return elemData; } /** * The method returns <code>Packet</code> instance. * More specificly it returns instance of one of the following classes: <code>Iq</code>, * <code>Message</code> or <code>Presence</code>. It takes stanza XML element * as an arguments, parses some the most commonly used data and created an object. * Preparsed information are: stanza from/to addresses, stanza id, type and presets * the <code>Packet</code> priority.<p/> * If there is a stringprep processing error for either the stanza source or destination * address <code>TigaseStringprepException</code> exception is thrown. * @param elem is a stanza XML <code>Element</code> * @return a <code>Packet</code> instance, more specificly instance of one of the * following classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. * @throws TigaseStringprepException if there is stanza from or to address parsing * error. */ public static Packet packetInstance(Element elem) throws TigaseStringprepException { if (elem.getName() == Message.ELEM_NAME) { return new Message(elem); } if (elem.getName() == Presence.ELEM_NAME) { return new Presence(elem); } if (elem.getName() == Iq.ELEM_NAME) { return new Iq(elem); } return new Packet(elem); } /** * The method returns <code>Packet</code> instance. * More specificly it returns instance of one of the following classes: <code>Iq</code>, * <code>Message</code> or <code>Presence</code>. It takes stanza XML element * as an arguments and preparsed stanza from and to addresses. The method * parses some other, the most commonly used data and created an object. * Preparsed information are: stanza id, type and presets the <code>Packet</code> * priority.<p/> * This method does not parses stanza from and stanza to address from the given XML * document, hence it does not throw <code>TigaseStringprepException</code>. Even * though reusing parsed from and to address is highly recommended an extra care * is needed to pass correct parameters as stanza JIDs or the packet may be * incorrectly routed or processed. * * @param elem is the stanza XML <code>Element</code> * @param stanzaFrom is a preparsed <code>JID</code> instance from the given stanza * XML element. * @param stanzaTo is a preparsed <code>JID</code> instance from the given stanza * XML element. * @return a <code>Packet</code> instance, more specificly instance of one of the * following classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. */ public static Packet packetInstance(Element elem, JID stanzaFrom, JID stanzaTo) { if (elem.getName() == Message.ELEM_NAME) { return new Message(elem, stanzaFrom, stanzaTo); } if (elem.getName() == Presence.ELEM_NAME) { return new Presence(elem, stanzaFrom, stanzaTo); } if (elem.getName() == Iq.ELEM_NAME) { return new Iq(elem, stanzaFrom, stanzaTo); } return new Packet(elem, stanzaFrom, stanzaTo); } /** * The method creates XML stanza from given parameters and returns * <code>Packet</code> instance for this XML stanza. * More specificly it returns instance of one of the following classes: <code>Iq</code>, * <code>Message</code> or <code>Presence</code>. <p/> * The method first builds an XML stanza from given parameters: element name, * from and to addresses and stanza type, then it creates a Packet instance for the * stanza. It also runs all the parsing and stringprep processing, hence it throws * an exception if any error is found. * @param el_name XML stanza elemen name as <code>String</code>. * @param from is the stanza <strong>from</strong> address as <code>String</code> * @param to is the stanza <strong>to</strong> address as <code>String</code>. * @param type is one of the stanza types: <strong>set</strong>, <strong>get</strong>, * <strong>result</strong>, .... as <code>StanzaType</code> instance. * @return a <code>Packet</code> instance, more specificly instance of one of the * following classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. * @throws TigaseStringprepException if there is stanza from or to address parsing * error. */ public static Packet packetInstance(String el_name, String from, String to, StanzaType type) throws TigaseStringprepException { Element elem = new Element(el_name, new String[] { "from", "to", "type" }, new String[] { from, to, type.toString() }); return packetInstance(elem); } /** * <code>copyElementOnly</code> method creates a copy of the packet with stanza * information copied only. The <code>Packet</code> specific information stays * blank (NULL): (packetFrom, packetTo, etc...).<p/> * This method should be used to obtain a copy of the packet without setting * packet specific fields (packetFrom or packetTo). The method reuses preparsed * stanza JIDs and does not throw any exception. * @return a new copy of the packet with packet specific fields set to NULL. */ public Packet copyElementOnly() { Element res_elem = elem.clone(); Packet result = packetInstance(res_elem, getStanzaFrom(), getStanzaTo()); result.setPriority(priority); return result; } /** * Method returns a string representation of all the data enclosed by the * <code>Packet</code> intance. All stanza XML element and all fields are converted * to the <code>String</code> representation for debugging. Please note, this may * be resources consuming process so use it only when experiencing problems with * <code>Packet</code> content. * * * @return <code>String</code> representation of the packet with all its fields. */ public String debug() { return toString() + ", stanzaFrom=" + stanzaFrom + ", stanzaTo=" + stanzaTo; } /** * Method returns a modified copy of the <code>Packet</code> with its stanza as * stanza error used for reporting errors. It is recommended not to use this * method directly as there is a utility class which makes generating error responses * much simpler. An example call (which uses this method underneath) looks like this * example: * <pre> * import tigase.xmpp.Authorization; * Authorization.BAD_REQUEST.getResponseMessage(packet, "Error message", true/false); * </pre> * This utility class and it's method acts not only as a convenience but also provides * some additional checking and control. * * * @param errorType is a <code>String</code> representation of the error type defined * in the XMPP RFC-3920. * @param errorCode is an integer error code defined in the XMPP RFC for backward * compatibility with old Jabber implementatons. * @param errorCondition is a <code>String</code> representation of the error condition * defined in the XMPP RFC-3920. * @param errorText human readable error message. * @param includeOriginalXML a boolean parameter indicating whether stanza top element * children should be included in the error message. * * @return a new <code>Packet</code> instance with an error type stanza which * is a response to this <code>Packet</code> instance. */ public Packet errorResult(final String errorType, final Integer errorCode, final String errorCondition, final String errorText, final boolean includeOriginalXML) { Element reply = new Element(elem.getName()); reply.setAttribute("type", StanzaType.error.toString()); // This is not needed anymore, initVars(...) takes care of that // if (getStanzaFrom() != null) { // reply.setAttribute("to", getStanzaFrom().toString()); // } // end of if (getElemFrom() != null) // // if (getStanzaTo() != null) { // reply.setAttribute("from", getStanzaTo().toString()); // } // end of if (getElemTo() != null) if (getStanzaId() != null) { reply.setAttribute("id", getStanzaId()); } // end of if (getElemId() != null) if (includeOriginalXML) { reply.addChildren(elem.getChildren()); } // end of if (includeOriginalXML) // if (getAttribute(OLDTO) != null) { // reply.setAttribute(OLDTO, getAttribute(OLDTO)); // } if (getXMLNS() != null) { reply.setXMLNS(getXMLNS()); } Element error = new Element("error"); if (errorCode != null) { error.setAttribute("code", errorCode.toString()); } error.setAttribute("type", errorType); Element cond = new Element(errorCondition); cond.setXMLNS(ERROR_NS); error.addChild(cond); if (errorText != null) { Element t = new Element("text", errorText, new String[] { "xml:lang", "xmlns" }, new String[] { "en", ERROR_NS }); error.addChild(t); } // end of if (text != null && text.length() > 0) reply.addChild(error); return swapFromTo(reply, getStanzaTo(), getStanzaFrom()); } //~--- get methods ---------------------------------------------------------- /** * A convenience method for accessing stanza top element attributes. This call is * equal to the call: * <pre> * packet.getElement().getAttribute(key); * </pre> * * * @param key is an attribute key. * * @return an attribute value or NULL if there is no such attribute. */ public String getAttribute(String key) { return elem.getAttribute(key); } /** * A convenience method for accessing stanza top level or any of it's children * attribute. This call is equal to the call: * <pre> * packet.getElement().getAttribute(xmlPath, key); * </pre> * * @param path is XML path for the stanza element or stanza child for which attribute * is retrieved. * @param key is an attribute key. * * @return */ public String getAttribute(String path, String key) { return elem.getAttribute(path, key); } /** * The method alwats returns NULL. It is overwritten in the <code>Iq</code> class * where it returns a command identifier if the <em>iq</code> stanza represnts an * ad-hoc command. It is provided here is a convenience so the developer does not * have to cast the packet to IQ before retrieving the command id. * * * @return the method always returns a NULL. */ public Command getCommand() { return null; } /** * Method returns character data from the enclosed stanza for a given stanza element * or child pointed by the <code>xmlPath</code> parameter. * This call is equal to the call: * <pre> * packet.getElement().getCData(xmlPath); * </pre> * * @param xmlPath is an XML path to the stanza element for which CData is retrieved. * * @return CData for a given element or NULL if the element does not exist or there is * no CData for the element. */ public String getElemCData(String xmlPath) { return elem.getCData(xmlPath); } /** * Method return character data for the stanza top element. * This call is equal to the call: * <pre> * packet.getElement().getCData(); * </pre> * * * @return CData or from the stanza top element or NULL if there is no CData for the * element. */ public String getElemCData() { return elem.getCData(); } /** * Method returns a list of all XML children from the enclosed stanza for a given * stanza element or child pointed by the <code>xmlPath</code> parameter. * This call is equal to the call: * <pre> * packet.getElement().getChildren(xmlPath); * </pre> * * @param xmlPath is an XML path to the stanza element for which children are * retrieved. * * @return children list for a given element or NULL if the element does not exist * or there is no children for the element. */ public List<Element> getElemChildren(String xmlPath) { return elem.getChildren(xmlPath); } /** * Method returns a <code>String</code> representation of the stanza source address. * Use of this method is not recommended, the API is depreciated in favor of API * operating on <code>JID</code> class. * @return a <code>String</code> representation of the stanza source address or NULL * if the source address has not been set. * @deprecated use getStanzaFrom() instead. */ @Deprecated public String getElemFrom() { return (stanzaFrom != null) ? stanzaFrom.toString() : null; } /** * Cnvenience method for retrieving the stanza top element name. * This call is equal to the call: * <pre> * packet.getElement().getName(); * </pre> * * @return the stanza top element name. */ public String getElemName() { return elem.getName(); } /** * Method returns a <code>String</code> representation of the stanza destination * address. * Use of this method is not recommended, the API is depreciated in favor of API * operating on <code>JID</code> class. * @return a <code>String</code> representation of the stanza destination address or * NULL if the destination address has not been set.. * @deprecated use getStanzaTo() instead */ @Deprecated public String getElemTo() { return (stanzaTo != null) ? stanzaTo.toString() : null; } /** * Method returns the stanza XML element in DOM format. * * * @return the stanza XML element in DOM format. */ public Element getElement() { return elem; } /** * Method parses the stanza and returns the error condition if there is any. * * * @return parsed stanza error condition or NULL if there is not error condition. */ public String getErrorCondition() { List<Element> children = elem.getChildren(elem.getName() + "/error"); if (children != null) { for (Element cond : children) { if ( !cond.getName().equals("text")) { return cond.getName(); } // end of if (!cond.getName().equals("text")) } // end of for (Element cond: children) } // end of if (children == null) else return null; } /** * Returns the packet source address. The method works as a following code: * <pre> * return (packetFrom != null) ? packetFrom : stanzaFrom; * </pre> * * @return a <code>JID</code> instance of the packet source address or NULL if * neither the packet source address is set nor the stanza source address is set. */ public JID getFrom() { return (packetFrom != null) ? packetFrom : stanzaFrom; } /** * Returns the packet internal source address. * * * @return a <code>JID>/code> instance of the packet internal source address or * NULL if the packet internal source address has not been set */ public JID getPacketFrom() { return this.packetFrom; } /** * Returns the packet internal destination address. * * * @return a <code>JID>/code> instance of the packet internal destination address or * NULL if the packet internal destination address has not been set. */ public JID getPacketTo() { return this.packetTo; } /** * Method returns permissions set of the user who has sent the packet. Some packets * carry ad-hoc commands which can change server parameters, configuration or * can contains other administration commands. Such commands are not executed if * the packet sender does not have enough permissions. * * * @return a sender permissions set. */ public Permissions getPermissions() { return permissions; } /** * Method returns the packet priority, * * * @return the packet priority. */ public Priority getPriority() { return priority; } /** * Method returns a set of all processor IDs which processed the packet. Each * session manager processor which handles the packet can mark the packet as * processed. This is used internally by the session manager to detect packets * which hasn't been processed by any processor, hence a default action is * applied to the apcket if possible. * * * @return a <code>Set</code> of stanza processor IDs which handled the packet. */ public Set<String> getProcessorsIds() { return processorsIds; } /** * Method returns source address of rhe stanza enclosed by this packet. * @return a <code>JID</code> instance of the stanza source address or NULL if the * source address has not been set for the stanza. */ public JID getStanzaFrom() { return stanzaFrom; } /** * Method returns the stanza ID if set. * * * @return a <code>String</code> representation of the stanza ID or NULL if the ID has * not been set for the stanza. */ public String getStanzaId() { return stanzaId; } /** * Method returns destinaion address of rhe stanza enclosed by this packet. * @return a <code>JID</code> instance of the stanza destination address or NULL if * the destination address has not been set for the stanza. */ public JID getStanzaTo() { return stanzaTo; } /** * Returns the packet destination address. The method works as a following code: * <pre> * return (packetTo != null) ? packetTo : stanzaTo; * </pre> * * @return a <code>JID</code> instance of the packet destination address or NULL if * neither the packet destination address is set nor the stanza destination address * is set. */ public JID getTo() { return (packetTo != null) ? packetTo : stanzaTo; } /** * Method returns the stanza type parsed from the top XML element of the enclosed * stanza. * * * @return a <code>StanzaType</code> instance of the stanza type parsed from the * top XML element of the enclosed stanza or NULL of the type has not been set. */ public StanzaType getType() { return type; } /** * Returns the enclosed stanza top element XMLNS. * This call is equal to the call: * <pre> * packet.getElement().getXMLNS(); * </pre> * * @return a <code>String</code> instance of the stanza top element XMLNS. */ public String getXMLNS() { return elem.getXMLNS(); } //~--- methods -------------------------------------------------------------- /** * The method allows for re-syncing stanza JIDs stored in the packet with the * attributes of the stanza if they have been changed for any reason. * <strong>Method mostly used internally only.</strong> Normally stanza carried by this * Packet instance * must not be changed, however there are rare occasions when it has to be changed. * RFC requires that the server adds missing <em>'from'</em> attribute to every * packet sent by the user. It would be highly inefficient to create a new instance * of the data just to add the missing from address. In such a case SM adds missing * attribute but then stanza preparsed JIDs stored in the packet are out of sync with * the enclosed stanza. This method allows for setting correct stanza JIDs for the * packet fields without a need to reparse the stanza. * * * @param stanzaFrom is a parsed source address JID from the stanza enclosed by this * packet. * @param stanzaTo is a parsed destination address JID from the stanza enclosed by * this packet. */ public void initVars(JID stanzaFrom, JID stanzaTo) { if (this.stanzaFrom != stanzaFrom) { this.stanzaFrom = stanzaFrom; if (stanzaFrom == null) { elem.removeAttribute("from"); } else { elem.setAttribute("from", stanzaFrom.toString()); } } if (this.stanzaTo != stanzaTo) { this.stanzaTo = stanzaTo; if (stanzaTo == null) { elem.removeAttribute("to"); } else { elem.setAttribute("to", stanzaTo.toString()); } } stanzaId = elem.getAttribute("id"); packetToString = null; packetToStringSecure = null; } /** * The method allows for resyncing/parsing stanza JIDs stored in the packet with the * attributes of the stanza if they have been changed for any reason. * <strong>Method mostly used internally only.</strong> Normally stanza carried by this * Packet instance * must not be changed, however there are rare occasions when it is needed. * RFC requires that the server adds missing <em>'from'</em> attribute to every * packet sent by the user. It would be highly inefficient to create a new instance * of the data just to add the missing from address. In such a case SM adds missing * attribute but then stanza preparsed JIDs stored in the packet are out of sync with * the enclosed stanza. This method causes stanza JIDs reparsing and setting the packet * variables. * * * @throws TigaseStringprepException if the stringprep error occurs during the stanza * JIDs parsing. */ public void initVars() throws TigaseStringprepException { String tmp = elem.getAttribute("to"); if (tmp != null) { stanzaTo = JID.jidInstance(tmp); } else { stanzaTo = null; } tmp = elem.getAttribute("from"); if (tmp != null) { stanzaFrom = JID.jidInstance(tmp); } else { stanzaFrom = null; } stanzaId = elem.getAttribute("id"); packetToString = null; packetToStringSecure = null; } //~--- get methods ---------------------------------------------------------- /** * The method checks whether the stanza enclosed by this <code>Packet</code> * instance is an ad-hoc command. * This is a generic method which in fact always returns <code>false</code>. It * is overwritten in the <code>Iq</code> class where the real checking is * performed. This class has been provided as a convenience method to perform * the check without a need for casting the <code>Packet</code> instance to * the <code>Iq</code> class. * * @return a <code>boolean</code> value <code>true</code> if the stanza is * an ad-hoc command and <code>false</code> otherwise. */ public boolean isCommand() { return false; } /** * The method checks wherher the enclosed stanza is a speciifc XML element. * That is, it checks whether the stanza element name and XMLNS is exactly * the same as given parameters. * This is a convenience method which logic is equal to the code below: * <pre> * return packet.getElement().getName() == name * && packet.getElement().getXMLNS() == xmlns; * </pre> * * @param name is a <code>String</code> representing the XML element name. * @param xmlns is a <code>String</code> representing the XML xmlns value. * * @return */ public boolean isElement(String name, String xmlns) { return (elem.getName() == name) && (xmlns == elem.getXMLNS()); } /** * Method determines whether the stanza represents so called <em>routed</em> * packet. * A routed packet is a packet created by a component responsible for * Communication with external components. In certain work mode it can send * over the link the whole packet information with all internal states and * addresses. Such a packet also encloses original stanza with all it's attributes. * * @return a <code>boolean</code> value of <code>true</code> if the packet is * routed and <code>false</code> otherwise. */ public boolean isRouted() { return routed; } /** * A convenience method which checks whether the enclosed stanza is a service * discovery query. * This is a generic method which in fact always returns <code>false</code>. It * is overwritten in the <code>Iq</code> class where the real checking is * performed. This class has been provided as a convenience method to perform * the check without a need for casting the <code>Packet</code> instance to * the <code>Iq</code> class. * * @return a <code>boolean</code> value <code>true</code> if the stanza is * a a service discovery query and <code>false</code> otherwise. */ public boolean isServiceDisco() { return false; } /** * The method checks whether the enclosed stanza contains an XML element and * XML child element for a given element path and xmlns. * The <code>elementPath</code> is directory path like string. * * @param elementPath is a <code>String</code> value which represents XML * element to a desired child element. * @param xmlns is a <code>String</code> value which represents XML XMLNS. * * @return */ public boolean isXMLNS(String elementPath, String xmlns) { String this_xmlns = elem.getXMLNS(elementPath); if (this_xmlns == xmlns) { return true; } return false; } public void setXMLNS(String xmlns) { elem.setXMLNS(xmlns); packetToString = null; packetToStringSecure = null; } //~--- methods -------------------------------------------------------------- /** * Method returns a modified copy of the <code>Packet</code> with its stanza as * stanza <code>result</code> used for reporting <em>IQ</em> stanza results. * The method preserves all the attributes of the original stanza, swaps stanza * source and destination addresses and can optionally add more child XML * elements and can preserve existing children elements up to given depth. * * @param includeXML is an XML content serialized to <code>String</code> or just * character data as <code>String</code> which has to be added to response * stanza. * @param originalXML parameter specified whether and if so to what depth the * original stanza child elements have to be preserved in the response packet. * @return a new <code>Packet</code> instance with an OK (result) type stanza * which is a response to this <code>Packet</code> instance. */ public Packet okResult(final String includeXML, final int originalXML) { Element reply = new Element(elem.getName()); if (getXMLNS() != null) { reply.setXMLNS(getXMLNS()); } reply.setAttribute("type", StanzaType.result.toString()); // Not needed anymore, initVars(...) takes care of that // if (getStanzaFrom() != null) { // reply.setAttribute("to", getStanzaFrom().toString()); // } // end of if (getElemFrom() != null) // // if (getStanzaTo() != null) { // reply.setAttribute("from", getStanzaTo().toString()); // } // end of if (getElemFrom() != null) // if (getStanzaId() != null) { reply.setAttribute("id", getStanzaId()); } // end of if (getElemId() != null) // if (getAttribute(OLDTO) != null) { // reply.setAttribute(OLDTO, getAttribute(OLDTO)); // } Element old_child = elem; Element new_child = reply; for (int i = 0; i < originalXML; i++) { old_child = old_child.getChildren().get(0); Element tmp = new Element(old_child.getName()); tmp.setXMLNS(old_child.getXMLNS()); new_child.addChild(tmp); new_child = tmp; } // end of for (int i = 0; i < originalXML; i++) if (includeXML != null) { new_child.setCData(includeXML); } // end of if (includeOriginalXML) Packet result = swapFromTo(reply, getStanzaTo(), getStanzaFrom()); result.setPriority(priority); return result; } /** * Method returns a modified copy of the <code>Packet</code> with its stanza as * stanza <code>result</code> used for reporting <em>IQ</em> stanza results. * The method preserves all the attributes of the original stanza, swaps stanza * source and destination addresses and can optionally add more child XML * elements and can preserve existing children elements up to given depth. * * @param includeXML is an XML content which has to be added to the response * stanza. * @param originalXML parameter specified whether and if so to what depth the * original stanza child elements have to be preserved in the response packet. * @return a new <code>Packet</code> instance with an OK (result) type stanza * which is a response to this <code>Packet</code> instance. */ public Packet okResult(Element includeXML, int originalXML) { Element reply = new Element(elem.getName()); if (getXMLNS() != null) { reply.setXMLNS(getXMLNS()); } reply.setAttribute("type", StanzaType.result.toString()); // Not needed anymore, initVars(...) takes care of that // if (getStanzaFrom() != null) { // reply.setAttribute("to", getStanzaFrom().toString()); // } // end of if (getElemFrom() != null) // // if (getStanzaTo() != null) { // reply.setAttribute("from", getStanzaTo().toString()); // } // end of if (getElemFrom() != null) // if (getStanzaId() != null) { reply.setAttribute("id", getStanzaId()); } // end of if (getElemId() != null) // if (getAttribute(OLDTO) != null) { // reply.setAttribute(OLDTO, getAttribute(OLDTO)); // } Element old_child = elem; Element new_child = reply; for (int i = 0; i < originalXML; i++) { old_child = old_child.getChildren().get(0); Element tmp = new Element(old_child.getName()); tmp.setXMLNS(old_child.getXMLNS()); new_child.addChild(tmp); new_child = tmp; } // end of for (int i = 0; i < originalXML; i++) if (includeXML != null) { new_child.addChild(includeXML); } // end of if (includeOriginalXML) Packet result = swapFromTo(reply, getStanzaTo(), getStanzaFrom()); result.setPriority(priority); return result; } /** * Returns a new <code>Packet</code> instance with stanza <em>routed</em> * which means an original stanza has been enclosed inside a <code>route</code> * XML element which contains additional information taken from * <code>Packet</code> packet instance internal attributes. * * @return a new <code>Packet</code> instance with <code>route</code> stanza. */ public Packet packRouted() { Element routedp = new Element("route", new String[] { "to", "from" }, new String[] { getTo().toString(), getFrom().toString() }); routedp.addChild(elem); return packetInstance(routedp, getFrom(), getTo()); } /** * The method marks that the packet has been processed by a packet processor * with a given ID. * * @param id is a <code>String</code> instance of the packet processer identifier. */ public void processedBy(String id) { processorsIds.add(id); } //~--- set methods ---------------------------------------------------------- /** * The method sets a source address for the <code>Packet</code> instance. * * * @param from is a <code>JID</code> instance of the packet new source address. */ public void setPacketFrom(JID from) { this.packetFrom = from; } /** * The method sets a destination address for the <code>Packet</code> instance. * * * @param to is a <code>JID</code> instance of the packet new destination * address. */ public void setPacketTo(JID to) { this.packetTo = to; } /** * The method sets permissions for the packet of a user who sent the stanza. * * * @param perm is <code>Permissions</code> instance of the stanza sender * permissions calculated by the session manager. */ public void setPermissions(Permissions perm) { packetToString = null; packetToStringSecure = null; permissions = perm; } /** * The method sets the packet priority. Depending on the priority the packet * is put to a queue with corresponding priority. This matter only on system * which experience overload and some packets may be delivered with a delay * if they are low priority packets. * * @param priority os a new <code>Priority</code> instance set for the packet. */ public void setPriority(Priority priority) { this.priority = priority; } //~--- methods -------------------------------------------------------------- /** * The method left for compatiblity with an old API reasons. Use * <code>swapStanzaFromTo()</code> instead. * * @return a new packet instance with a copy of the stanza element with * swapped source and destination addresses. * @deprecated Use <code>swapStanzaFromTo()</code> instead. */ @Deprecated public Packet swapElemFromTo() { return swapStanzaFromTo(); } /** * The method left for compatiblity with an old API reasons. Use * <code>swapStanzaFromTo()</code> instead. * * @param type a new stanza type which has to be set to the generated * stanza copy. * @return a new packet instance with a copy of the stanza element with * swapped source and destination addresses. * @deprecated Use <code>swapStanzaFromTo()</code> instead. */ @Deprecated public Packet swapElemFromTo(final StanzaType type) { return swapStanzaFromTo(type); } /** * The method creates a new instance of the <code>Packet</code> class with the * packet source and destination addresses swapped and sets the given stanza * element plus source and destination addresses for the new stanza. * This method gives you slightly more flexibility as you can set any source * and destination address for the new stanza. * This method is rarely used in packet processors which don't sent a simple * "ok result" response. Some data flow requires a completely new packet * to be send as a response to the original call, but the response has to be * delivered to the original sends. As an example are the SASL authentication * and TLS handshaking. * * @param el is an XML element set for the new packet. * @param stanzaFrom is the stanza source address * @param stanzaTo is the stanza destination address * * @return a new <code>Packet</code> instance. */ public Packet swapFromTo(Element el, JID stanzaFrom, JID stanzaTo) { Packet packet = packetInstance(el, stanzaFrom, stanzaTo); packet.setPacketTo(getFrom()); packet.setPacketFrom(getTo()); packet.setPriority(priority); return packet; } /** * Creates a new <code>Packet</code> instance with swapped packet source and * destination addresses. Please note the new packet contains unchanged copy of the * original stanza. Stanza source and destination addresses are no swapped. * * @return a new <code>Packet>/code> instance. */ public Packet swapFromTo() { Element el = elem.clone(); Packet packet = packetInstance(el, getStanzaFrom(), getStanzaTo()); packet.setPacketTo(getFrom()); packet.setPacketFrom(getTo()); packet.setPriority(priority); return packet; } /** * The method creates a new <code>Packet</code> instance with a stanza copy * with swapped source and destination addresses. The packet source and * destination addresses are set to null. * * * @return a new <code>Packet</code> instance. */ public Packet swapStanzaFromTo() { Element copy = elem.clone(); // Not needed anymore, initVars(...) takes care of that // copy.setAttribute("to", getStanzaFrom().toString()); // copy.setAttribute("from", getStanzaTo().toString()); Packet result = packetInstance(copy, getStanzaTo(), getStanzaFrom()); result.setPriority(priority); return result; } /** * The method creates a new <code>Packet</code> instance with a stanza copy * with swapped source and destination addresses and the given type set. * The packet source and destination addresses are set to null. * * * @param type is a new type for the stanza copy to set. * @return a new <code>Packet</code> instance. */ public Packet swapStanzaFromTo(final StanzaType type) { Element copy = elem.clone(); // Not needed anymore, initVars(...) takes care of that // copy.setAttribute("to", getStanzaFrom().toString()); // copy.setAttribute("from", getStanzaTo().toString()); copy.setAttribute("type", type.toString()); Packet result = packetInstance(copy, getStanzaTo(), getStanzaFrom()); result.setPriority(priority); return result; } /** * The method converts the <code>Packet</code> instance to a <code>String</code> * representation. The stanza XML element is presented as the string and all packet * attributes are also added to the string. * The method is for a debugging purposes to log the whole packet content to * the debug file for further analysis. It is recommended to use * <code>toStringSecure()</code> instead as it removes all the CData from the * stanza avoiding exposing user chat message content. The secure method * also preserves you from flooding your log files in case of a huge chunks of * data are sent in packets (user photos in vCards or files). * * @return a <code>String</code> representation of the packet instance. */ public String toStringFull() { if (packetToString == null) { String elemData = elemToString(elem); packetToString = calcToString(elemData); // ", DATA=" + elemData + ", SIZE=" + elem.toString().length() + ", XMLNS=" // + elem.getXMLNS() + ", PRIORITY=" + priority + ", PERMISSION=" + permissions; } return "from=" + packetFrom + ", to=" + packetTo + packetToString; } @Override public String toString() { return toString(FULL_DEBUG); } /** * Is a convenience method which allows you to call always the same method * but parametrize (configure) whether you want to get a secure packet string * representation or full representation. * * @param secure parameter specifies whether the secure packet representation * should be returned (<code>true</code> value) or the full one * (<code>false</code>). * * @return a <code>String</code> representation of the packet instance. */ public String toString(boolean secure) { String result; if (secure) { result = toStringSecure(); } else { result = toStringFull(); } return result; } /** * The method returns a <code>String</code> representation of the packet with * all CData content replaced with text: <em>"CData size: NN"</em>. This is a * preferable method to log the packets for debuging purposes. * * @return a <code>String</code> representation of the packet instance. */ public String toStringSecure() { if (FULL_DEBUG) { return toStringFull(); } else { if (packetToStringSecure == null) { String elemData = elemToStringSecure(elem); packetToStringSecure = calcToString(elemData); // ", DATA=" + elemData + ", SIZE=" + elem.toString().length() // + ", XMLNS=" + elem.getXMLNS() + ", PRIORITY=" + priority + ", PERMISSION=" // + permissions; } return "from=" + packetFrom + ", to=" + packetTo + packetToStringSecure; } } /** * The method unpacks the original packet and stanza from <code>route</code> * stanza. * This is the opposite action to the <code>packRouted()</code> method. * * * @return a new instance of the <code>Packet</code> class with unpacket * packet and stanza from <code>route</code> stanza. * * @throws TigaseStringprepException if there was a problem with addresses * stringprep processing. */ public Packet unpackRouted() throws TigaseStringprepException { Packet result = packetInstance(elem.getChildren().get(0)); result.setPacketTo(getTo()); result.setPacketFrom(getFrom()); return result; } /** * The method determines whether the packet has been processed by any of * the packet processors. * In fact it says whether there has been called method * <code>processedBy(...)</code> on the packet. * * @return a <code>boolean</code> value of <code>true</code> of the packet was * processed by any processor and <code>false</code> otherwise. */ public boolean wasProcessed() { return processorsIds.size() > 0; } /** * The method checks whether the packet has been processed by a * packet processor with the specified ID. * * @param id is a <code>String</code> instance of the packet processor identifier. * * @return a <code>boolean</code> value of <code>true</code> of the packet was * processed by a processor with specified ID and <code>false</code> otherwise. */ public boolean wasProcessedBy(String id) { return processorsIds.contains(id); } private String calcToString(String elemData) { return ", DATA=" + elemData + ", SIZE=" + elem.toString().length() + ", XMLNS=" + elem.getXMLNS() + ", PRIORITY=" + priority + ", PERMISSION=" + permissions + ", TYPE=" + type; } //~--- set methods ---------------------------------------------------------- private void setElem(Element elem) { if (elem == null) { throw new NullPointerException(); } // end of if (elem == null) this.elem = elem; if (elem.getAttribute("type") != null) { type = StanzaType.valueof(elem.getAttribute("type")); } else { type = null; } // end of if (elem.getAttribute("type") != null) else if (elem.getName() == "cluster") { setPriority(Priority.CLUSTER); } else { if ((elem.getName() == "presence") && ((type == null) || (type == StanzaType.available) || (type == StanzaType.unavailable) || (type == StanzaType.probe))) { setPriority(Priority.PRESENCE); } else { if (elem.getName() == "route") { routed = true; } else { routed = false; } // end of if (elem.getName().equals("route")) else } } } } //~ Formatted in Sun Code Convention //~ Formatted by Jindent --- http://www.jindent.com