PresenceRouter.java 10.1 KB
Newer Older
1 2 3 4 5
/**
 * $RCSfile: PresenceRouter.java,v $
 * $Revision: 3138 $
 * $Date: 2005-12-01 02:13:26 -0300 (Thu, 01 Dec 2005) $
 *
6
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
7
 *
8 9 10 11 12 13 14 15 16 17 18
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
19 20
 */

21
package org.jivesoftware.openfire;
22

23
import org.jivesoftware.openfire.container.BasicModule;
24
import org.jivesoftware.openfire.entitycaps.EntityCapabilitiesManager;
25 26 27 28 29 30
import org.jivesoftware.openfire.handler.PresenceSubscribeHandler;
import org.jivesoftware.openfire.handler.PresenceUpdateHandler;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.Session;
Gaston Dombiak's avatar
Gaston Dombiak committed
31
import org.jivesoftware.util.LocaleUtils;
32 33 34 35 36 37 38
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
39 40 41 42 43 44 45 46 47 48 49 50

/**
 * <p>Route presence packets throughout the server.</p>
 * <p>Routing is based on the recipient and sender addresses. The typical
 * packet will often be routed twice, once from the sender to some internal
 * server component for handling or processing, and then back to the router
 * to be delivered to it's final destination.</p>
 *
 * @author Iain Shigeoka
 */
public class PresenceRouter extends BasicModule {

51 52
	private static final Logger Log = LoggerFactory.getLogger(PresenceRouter.class);

53 54 55 56 57
    private RoutingTable routingTable;
    private PresenceUpdateHandler updateHandler;
    private PresenceSubscribeHandler subscribeHandler;
    private PresenceManager presenceManager;
    private SessionManager sessionManager;
58
    private EntityCapabilitiesManager entityCapsManager;
59
    private MulticastRouter multicastRouter;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    private String serverName;

    /**
     * Constructs a presence router.
     */
    public PresenceRouter() {
        super("XMPP Presence Router");
    }

    /**
     * Routes presence packets.
     *
     * @param packet the packet to route.
     * @throws NullPointerException if the packet is null.
     */
    public void route(Presence packet) {
        if (packet == null) {
            throw new NullPointerException();
        }
79
        ClientSession session = sessionManager.getSession(packet.getFrom());
80 81 82
        try {
            // Invoke the interceptors before we process the read packet
            InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
83
            if (session == null || session.getStatus() != Session.STATUS_CONNECTED) {
84 85 86 87 88 89 90 91 92 93
                handle(packet);
            }
            else {
                packet.setTo(session.getAddress());
                packet.setFrom((JID)null);
                packet.setError(PacketError.Condition.not_authorized);
                session.process(packet);
            }
            // Invoke the interceptors after we have processed the read packet
            InterceptorManager.getInstance().invokeInterceptors(packet, session, true, true);
94
        }
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
        catch (PacketRejectedException e) {
            if (session != null) {
                // An interceptor rejected this packet so answer a not_allowed error
                Presence reply = new Presence();
                reply.setID(packet.getID());
                reply.setTo(session.getAddress());
                reply.setFrom(packet.getTo());
                reply.setError(PacketError.Condition.not_allowed);
                session.process(reply);
                // Check if a message notifying the rejection should be sent
                if (e.getRejectionMessage() != null && e.getRejectionMessage().trim().length() > 0) {
                    // A message for the rejection will be sent to the sender of the rejected packet
                    Message notification = new Message();
                    notification.setTo(session.getAddress());
                    notification.setFrom(packet.getTo());
                    notification.setBody(e.getRejectionMessage());
                    session.process(notification);
                }
            }
114 115 116 117 118
        }
    }

    private void handle(Presence packet) {
        JID recipientJID = packet.getTo();
119
        JID senderJID = packet.getFrom();
120 121 122 123 124 125 126 127 128 129
        // Check if the packet was sent to the server hostname
        if (recipientJID != null && recipientJID.getNode() == null &&
                recipientJID.getResource() == null && serverName.equals(recipientJID.getDomain())) {
            if (packet.getElement().element("addresses") != null) {
                // Presence includes multicast processing instructions. Ask the multicastRouter
                // to route this packet
                multicastRouter.route(packet);
                return;
            }
        }
130
        try {
131 132 133 134 135 136 137 138
            // Presences sent between components are just routed to the component
            if (recipientJID != null && !XMPPServer.getInstance().isLocal(recipientJID) &&
                    !XMPPServer.getInstance().isLocal(senderJID)) {
                // Route the packet
                routingTable.routePacket(recipientJID, packet, false);
                return;
            }

139 140 141 142 143 144 145 146
            Presence.Type type = packet.getType();
            // Presence updates (null is 'available')
            if (type == null || Presence.Type.unavailable == type) {
                // check for local server target
                if (recipientJID == null || recipientJID.getDomain() == null ||
                        "".equals(recipientJID.getDomain()) || (recipientJID.getNode() == null &&
                        recipientJID.getResource() == null) &&
                        serverName.equals(recipientJID.getDomain())) {
147
                    entityCapsManager.process(packet);
148 149 150
                    updateHandler.process(packet);
                }
                else {
151 152 153
                    // Trigger events for presences of remote users
                    if (senderJID != null && !serverName.equals(senderJID.getDomain()) &&
                            !routingTable.hasComponentRoute(senderJID)) {
154
                        entityCapsManager.process(packet);
155 156
                    }
                    
157
                    // Check that sender session is still active (let unavailable presence go through)
158
                    Session session = sessionManager.getSession(packet.getFrom());
159
                    if (session != null && session.getStatus() == Session.STATUS_CLOSED && type == null) {
160 161 162 163
                        Log.warn("Rejected available presence: " + packet + " - " + session);
                        return;
                    }

164
                    // The user sent a directed presence to an entity
165
                    // Broadcast it to all connected resources
166
                    for (JID jid : routingTable.getRoutes(recipientJID, senderJID)) {
167
                        // Register the sent directed presence
Gaston Dombiak's avatar
Gaston Dombiak committed
168
                        updateHandler.directedPresenceSent(packet, jid, recipientJID.toString());
169
                        // Route the packet
Gaston Dombiak's avatar
Gaston Dombiak committed
170
                        routingTable.routePacket(jid, packet, false);
171 172 173 174 175 176 177 178 179 180 181 182 183
                    }
                }

            }
            else if (Presence.Type.subscribe == type // presence subscriptions
                    || Presence.Type.unsubscribe == type
                    || Presence.Type.subscribed == type
                    || Presence.Type.unsubscribed == type)
            {
                subscribeHandler.process(packet);
            }
            else if (Presence.Type.probe == type) {
                // Handle a presence probe sent by a remote server
184
                if (!XMPPServer.getInstance().isLocal(recipientJID)) {
Gaston Dombiak's avatar
Gaston Dombiak committed
185
                    routingTable.routePacket(recipientJID, packet, false);
186 187 188 189 190
                }
                else {
                    // Handle probe to a local user
                    presenceManager.handleProbe(packet);
                }
191 192 193 194
            }
            else {
                // It's an unknown or ERROR type, just deliver it because there's nothing
                // else to do with it
Gaston Dombiak's avatar
Gaston Dombiak committed
195
                routingTable.routePacket(recipientJID, packet, false);
196 197 198 199 200 201 202
            }

        }
        catch (Exception e) {
            Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
            Session session = sessionManager.getSession(packet.getFrom());
            if (session != null) {
203
                session.close();
204 205 206 207
            }
        }
    }

208 209
    @Override
	public void initialize(XMPPServer server) {
210
        super.initialize(server);
211
        serverName = server.getServerInfo().getXMPPDomain();
212 213 214 215
        routingTable = server.getRoutingTable();
        updateHandler = server.getPresenceUpdateHandler();
        subscribeHandler = server.getPresenceSubscribeHandler();
        presenceManager = server.getPresenceManager();
216
        multicastRouter = server.getMulticastRouter();
217
        sessionManager = server.getSessionManager();
218
        entityCapsManager = EntityCapabilitiesManager.getInstance();
219
    }
Gaston Dombiak's avatar
Gaston Dombiak committed
220 221 222 223

    /**
     * Notification message indicating that a packet has failed to be routed to the receipient.
     *
224
     * @param receipient address of the entity that failed to receive the packet.
Gaston Dombiak's avatar
Gaston Dombiak committed
225 226
     * @param packet Presence packet that failed to be sent to the receipient.
     */
227
    public void routingFailed(JID receipient, Packet packet) {
Gaston Dombiak's avatar
Gaston Dombiak committed
228 229
        // presence packets are dropped silently
    }
230
}