📄 presencesubscribehandler.java
字号:
/**
* $RCSfile: PresenceSubscribeHandler.java,v $
* $Revision: 3136 $
* $Date: 2005-12-01 02:06:16 -0300 (Thu, 01 Dec 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.handler;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.wildfire.roster.Roster;
import org.jivesoftware.wildfire.roster.RosterItem;
import org.jivesoftware.wildfire.roster.RosterManager;
import org.jivesoftware.wildfire.user.UserAlreadyExistsException;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* Implements the presence protocol. Clients use this protocol to
* update presence and roster information.
* <p/>
* The handler must properly detect the presence type, update the user's roster,
* and inform presence subscribers of the session's updated presence
* status. Presence serves many purposes in Jabber so this handler will
* likely be the most complex of all handlers in the server.
* <p/>
* There are four basic types of presence updates:
* <ul>
* <li>Simple presence updates - addressed to the server (or to address), these updates
* are properly addressed by the server, and multicast to
* interested subscribers on the user's roster. An empty, missing,
* or "unavailable" type attribute indicates a simple update (there
* is no "available" type although it should be accepted by the server.
* <li>Directed presence updates - addressed to particular jabber entities,
* these presence updates are properly addressed and directly delivered
* to the entity without broadcast to roster subscribers. Any update type
* is possible except those reserved for subscription requests.
* <li>Subscription requests - these updates request presence subscription
* status changes. Such requests always affect the roster. The server must:
* <ul>
* <li>update the roster with the proper subscriber info
* <li>push the roster changes to the user
* <li>forward the update to the correct parties.
* </ul>
* The valid types include "subscribe", "subscribed", "unsubscribed",
* and "unsubscribe".
* <li>XMPPServer probes - Provides a mechanism for servers to query the presence
* status of users on another server. This allows users to immediately
* know the presence status of users when they come online rather than way
* for a presence update broadcast from the other server or tracking them
* as they are received. Requires S2S capabilities.
* </ul>
* <p/>
* <h2>Warning</h2>
* There should be a way of determining whether a session has
* authorization to access this feature. I'm not sure it is a good
* idea to do authorization in each handler. It would be nice if
* the framework could assert authorization policies across channels.
*
* @author Iain Shigeoka
*/
public class PresenceSubscribeHandler extends BasicModule implements ChannelHandler {
private RoutingTable routingTable;
private XMPPServer localServer;
private String serverName;
private PacketDeliverer deliverer;
private PresenceManager presenceManager;
private RosterManager rosterManager;
private UserManager userManager;
public PresenceSubscribeHandler() {
super("Presence subscription handler");
}
public void process(Packet xmppPacket) throws PacketException {
Presence presence = (Presence) xmppPacket;
try {
JID senderJID = presence.getFrom();
JID recipientJID = presence.getTo();
Presence.Type type = presence.getType();
// Reject presence subscription requests sent to the local server itself.
if (recipientJID == null || recipientJID.toString().equals(serverName)) {
if (type == Presence.Type.subscribe) {
Presence reply = new Presence();
reply.setTo(senderJID);
reply.setFrom(recipientJID);
reply.setType(Presence.Type.unsubscribed);
deliverer.deliver(reply);
}
return;
}
try {
Roster senderRoster = getRoster(senderJID);
if (senderRoster != null) {
manageSub(recipientJID, true, type, senderRoster);
}
Roster recipientRoster = getRoster(recipientJID);
boolean recipientSubChanged = false;
if (recipientRoster != null) {
recipientSubChanged = manageSub(senderJID, false, type, recipientRoster);
}
// Do not forward the packet to the recipient if the presence is of type subscribed
// and the recipient user has not changed its subscription state.
if (!(type == Presence.Type.subscribed && recipientRoster != null &&
!recipientSubChanged)) {
// If the user is already subscribed to the *local* user's presence then do not
// forward the subscription request and instead send an auto-reply on behalf
// of the user
if (type == Presence.Type.subscribe && recipientRoster != null &&
!recipientSubChanged) {
try {
RosterItem.SubType subType = recipientRoster.getRosterItem(senderJID)
.getSubStatus();
if (subType == RosterItem.SUB_FROM || subType == RosterItem.SUB_BOTH) {
// auto-reply by sending a presence stanza of type "subscribed"
// to the contact on behalf of the user
Presence reply = new Presence();
reply.setTo(senderJID);
reply.setFrom(recipientJID);
reply.setType(Presence.Type.subscribed);
deliverer.deliver(reply);
return;
}
}
catch (UserNotFoundException e) {
// Weird case: Roster item does not exist. Should never happen
}
}
// Try to obtain a handler for the packet based on the routes. If the handler is
// a module, the module will be able to handle the packet. If the handler is a
// Session the packet will be routed to the client. If a route cannot be found
// then the packet will be delivered based on its recipient and sender.
List<ChannelHandler> handlers = routingTable.getRoutes(recipientJID);
if (!handlers.isEmpty()) {
for (ChannelHandler handler : handlers) {
Presence presenteToSend = presence.createCopy();
// Stamp the presence with the user's bare JID as the 'from' address
presenteToSend.setFrom(senderJID.toBareJID());
handler.process(presenteToSend);
}
}
else {
deliverer.deliver(presence.createCopy());
}
if (type == Presence.Type.subscribed) {
// Send the presence of the local user to the remote user. The remote user
// subscribed to the presence of the local user and the local user accepted
JID prober = localServer.isLocal(recipientJID) ?
new JID(recipientJID.toBareJID()) : recipientJID;
presenceManager.probePresence(prober, senderJID);
}
}
if (type == Presence.Type.unsubscribed) {
// Send unavailable presence from all of the local user's available resources
// to the remote user
presenceManager.sendUnavailableFromSessions(recipientJID, senderJID);
}
}
catch (SharedGroupException e) {
Presence result = presence.createCopy();
JID sender = result.getFrom();
result.setFrom(presence.getTo());
result.setTo(sender);
result.setError(PacketError.Condition.not_acceptable);
deliverer.deliver(result);
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
/**
* <p>Obtain the roster for the given address or null if the address doesn't have a roster.</p>
*
* @param address The address to check
* @return The roster or null if the address is not managed on the server
*/
private Roster getRoster(JID address) {
String username;
Roster roster = null;
if (localServer.isLocal(address) && userManager.isRegisteredUser(address.getNode())) {
username = address.getNode();
try {
roster = rosterManager.getRoster(username);
}
catch (UserNotFoundException e) {
// Do nothing
}
}
return roster;
}
/**
* Manage the subscription request. This method retrieves a user's roster
* and updates it's state, storing any changes made, and updating the roster
* owner if changes occured.
*
* @param target The roster target's jid (the item's jid to be changed)
* @param isSending True if the request is being sent by the owner
* @param type The subscription change type (subscribe, unsubscribe, etc.)
* @return true if the subscription state has changed.
*/
private boolean manageSub(JID target, boolean isSending, Presence.Type type, Roster roster)
throws UserAlreadyExistsException, SharedGroupException
{
RosterItem item = null;
RosterItem.AskType oldAsk;
RosterItem.SubType oldSub = null;
RosterItem.RecvType oldRecv;
boolean newItem = false;
try {
if (roster.isRosterItem(target)) {
item = roster.getRosterItem(target);
}
else {
if (Presence.Type.unsubscribed == type || Presence.Type.unsubscribe == type) {
// Do not create a roster item when processing a confirmation of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -