/* * 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, either 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.xmppsession; import tigase.db.NonAuthUserRepository; import tigase.server.Packet; import tigase.sys.TigaseRuntime; import tigase.xmpp.Authorization; import tigase.xmpp.BareJID; import tigase.xmpp.JID; import tigase.xmpp.NoConnectionIdException; import tigase.xmpp.NotAuthorizedException; import tigase.xmpp.PacketErrorTypeException; import tigase.xmpp.StanzaType; import tigase.xmpp.XMPPResourceConnection; import tigase.xmpp.XMPPSession; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; /** * Describe class PacketDefaultHandler here. * * * Created: Fri Feb 2 15:08:58 2007 * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class PacketDefaultHandler { /** * Variable <code>log</code> is a class logger. */ private static final Logger log = Logger .getLogger("tigase.server.xmppsession.PacketFilter"); private static TigaseRuntime runtime = TigaseRuntime.getTigaseRuntime(); // ~--- fields --------------------------------------------------------------- // private RosterAbstract roster_util = // RosterFactory.getRosterImplementation(true); private String[] AUTH_ONLY_ELEMS = { "message", "presence" }; private String[] IGNORE_PACKETS = { "stream:features" }; private StanzaType[] IGNORE_TYPES = { StanzaType.error }; // ~--- constructors --------------------------------------------------------- /** * Creates a new <code>PacketDefaultHandler</code> instance. * */ public PacketDefaultHandler() { } // ~--- methods -------------------------------------------------------------- /** * Method description * * * @param packet * @param session * * @return */ public boolean canHandle(Packet packet, XMPPResourceConnection session) { if (session == null) { return false; } // end of if (session == null) // Cannot forward packet if there is no destination address if (packet.getStanzaTo() == null) { // If this is simple <iq type="result"/> then ignore it // and consider it OK if ((packet.getElemName() == "iq") && (packet.getType() == StanzaType.result)) { // Nothing to do.... return true; } if (log.isLoggable(Level.INFO)) { log.log(Level.INFO, "No ''to'' address, can''t deliver packet: {0}", packet); } return false; } return true; } /** * Method description * * * @param packet * @param session * @param repo * @param results * * @return */ public boolean forward(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo, final Queue<Packet> results) { // Processing of the packets which needs to be processed as quickly // as possible, direct presences from unsubscribed entities apparently // have high priority as they may come from MUC and must be delivered // before room history // if (packet.getElemName() == "presence") { // PresenceType pres_type = roster_util.getPresenceType(session, packet); // if ((prese_type == PresenceType.in_initial) // && (packet.getElemFrom() != null) // && (roster_util.isSubscribedTo(session, packet.getElemFrom()) // || (DynamicRoster.getBuddyItem(session, settings, // packet.getElemFrom()) != null))) { // } // } return false; } /** * Method description * * * @param packet * @param session * @param repo * @param results * * @return */ public boolean preprocess(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo, final Queue<Packet> results) { if (session != null) { session.incPacketsCounter(); XMPPSession parent = session.getParentSession(); if (parent != null) { parent.incPacketsCounter(); } } for (int i = 0; i < IGNORE_PACKETS.length; i++) { if ((packet.getElemName() == IGNORE_PACKETS[i]) && (packet.getType() == IGNORE_TYPES[i])) { return true; } } if ((session == null) || session.isServerSession()) { return false; } // end of if (session == null) try { // For all messages coming from the owner of this account set // proper 'from' attribute. This is actually needed for the case // when the user sends a message to himself. if (session.getConnectionId().equals(packet.getPacketFrom())) { if (!session.isAuthorized()) { // We allow only certain packets here... // For now it is simpler to disallow all messages and presences // packets, the rest should be bounced back anyway for (String elem : AUTH_ONLY_ELEMS) { if (packet.getElemName() == elem) { results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, "You must authenticate session first, before you" + " can send any message or presence packet.", true)); if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Packet received before the session has been authenticated." + "Session details: connectionId=" + "{0}, sessionId={1}, packet={2}", new Object[] { session.getConnectionId(), session.getSessionId(), packet.toStringSecure() }); } return true; } } return false; } // After authentication we require resource binding packet and // nothing else: // actually according to XEP-0170: // http://xmpp.org/extensions/xep-0170.html // stream compression might occur between authentication and resource // binding if (session.isResourceSet() || packet.isXMLNS("/iq/bind", "urn:ietf:params:xml:ns:xmpp-bind") || packet.isXMLNS("compress", "http://jabber.org/protocol/compress")) { JID from_jid = session.getJID(); if (from_jid != null) { // Do not replace current settings if there is at least correct // BareJID // already set. if ((packet.getStanzaFrom() == null) || !from_jid.getBareJID().equals(packet.getStanzaFrom().getBareJID())) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Setting correct from attribute: {0}", from_jid); } // No need for the line below, initVars(...) takes care of that // packet.getElement().setAttribute("from", from_jid.toString()); packet.initVars(from_jid, packet.getStanzaTo()); } else { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Skipping setting correct from attribute: {0}, is already correct.", from_jid); } } } else { log.log(Level.WARNING, "Session is authenticated but session.getJid() is empty: {0}", packet.toStringSecure()); } } else { // We do not accept anything without resource binding.... results .offer(Authorization.NOT_AUTHORIZED .getResponseMessage( packet, "You must bind the resource first: http://www.xmpp.org/rfcs/rfc3920.html#bind", true)); if (log.isLoggable(Level.INFO)) { log.log( Level.INFO, "Session details: connectionId={0}, sessionId={1}", new Object[] { session.getConnectionId(), session.getSessionId()}); } if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Session more detais: JID={0}", session.getjid()); } return true; } } } catch (PacketErrorTypeException e) { // Ignore this packet if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Ignoring packet with an error to non-existen user session: {0}", packet.toStringSecure()); } } catch (Exception e) { log.log(Level.FINEST, "Packet preprocessing exception: ", e); return false; } // end of try-catch return false; } /** * Method description * * * @param packet * @param session * @param repo * @param results * */ public void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Processing packet: {0}", packet.toStringSecure()); } try { // Already done in forward method.... // No need to repeat this (performance - everything counts...) // // For all messages coming from the owner of this account set // // proper 'from' attribute. This is actually needed for the case // // when the user sends a message to himself. // if (packet.getFrom() != null // && packet.getFrom().equals(session.getConnectionId())) { // packet.getElement().setAttribute("from", session.getJID()); // log.finest("Setting correct from attribute: " + session.getJID()); // } // end of if (packet.getFrom().equals(session.getConnectionId())) // String id = null; JID to = packet.getStanzaTo(); // If this is simple <iq type="result"/> then ignore it // and consider it OK if ((to == null) && (packet.getElemName() == "iq") && (packet.getType() == StanzaType.result)) { // Nothing to do.... return; } if (session.isUserId(to.getBareJID())) { // Yes this is message to 'this' client if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Yes, this is packet to ''this'' client: {0}", to); } // Element elem = packet.getElement().clone(); // Packet result = new Packet(elem); // result.setTo(session.getConnectionId(packet.getElemTo())); // if (log.isLoggable(Level.FINEST)) { // log.finest("Setting to: " + result.getTo()); // } // result.setFrom(packet.getTo()); Packet result = packet.copyElementOnly(); result.setPacketFrom(packet.getTo()); try { result.setPacketTo(session.getConnectionId(packet.getStanzaTo())); results.offer(result); } catch (NoConnectionIdException ex) { log.log(Level.WARNING, "Packet to the server which hasn't been properly processed: {0}", packet); } return; } // end of else if (packet.getStanzaFrom() != null) { BareJID from = packet.getStanzaFrom().getBareJID(); if (session.isUserId(from)) { Packet result = packet.copyElementOnly(); // String[] connIds = runtime.getConnectionIdsForJid(to); // if (connIds != null && connIds.length > 0) { // result.setTo(connIds[0]); // } results.offer(result); return; } } } catch (NotAuthorizedException e) { try { results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, "You must authorize session first.", true)); log.log(Level.INFO, "NotAuthorizedException for packet: {0}", packet.toString()); } catch (PacketErrorTypeException e2) { log.log(Level.INFO, "Packet processing exception: {0}", e2); } } // end of try-catch } } // ~ Formatted in Sun Code Convention // ~ Formatted by Jindent --- http://www.jindent.com