IQBindHandler.java 6.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
/**
 * $RCSfile$
 * $Revision: $
 * $Date: $
 *
 * Copyright (C) 2005 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.dom4j.Element;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.AuthToken;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
import org.jivesoftware.util.Log;
import org.xmpp.packet.IQ;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.StreamError;

/**
 * Binds a resource to the stream so that the client's address becomes a full JID. Once a resource
 * has been binded to the session the entity (i.e. client) is considered a "connected resource".<p>
 * <p/>
 * Clients may specify a desired resource but if none was specified then the server will create
 * a random resource for the session. The new resource should be in accordance with ResourcePrep.
 * The server will also verify if there are previous sessions from the same user that are already
 * using the resource specified by the user. Depending on the server configuration the old session
 * may be kicked or the new session may be rejected.
 *
 * @author Gaston Dombiak
 */
public class IQBindHandler extends IQHandler {

    private IQHandlerInfo info;
    private UserManager userManager;
    private XMPPServer localServer;

    public IQBindHandler() {
        super("Resource Binding handler");
        info = new IQHandlerInfo("bind", "urn:ietf:params:xml:ns:xmpp-bind");
    }

    public IQ handleIQ(IQ packet) throws UnauthorizedException {
        ClientSession session = sessionManager.getSession(packet.getFrom());
        // If no session was found then answer an error (if possible)
        if (session == null) {
            Log.error("Error during resource binding. Session not found in " +
                    sessionManager.getPreAuthenticatedKeys() +
                    " for key " +
                    packet.getFrom());
            // This error packet will probably won't make it through
            IQ reply = IQ.createResultIQ(packet);
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.internal_server_error);
            return reply;
        }

        IQ reply = IQ.createResultIQ(packet);
        Element child = reply.setChildElement("bind", "urn:ietf:params:xml:ns:xmpp-bind");
        // Check if the client specified a desired resource
        String resource = packet.getChildElement().elementTextTrim("resource");
        if (resource == null || resource.length() == 0) {
            // None was defined so use the random generated resource
            resource = session.getAddress().getResource();
        }
        else {
            // Check that the desired resource is valid
            try {
                resource = Stringprep.resourceprep(resource);
            }
            catch (StringprepException e) {
                reply.setChildElement(packet.getChildElement().createCopy());
                reply.setError(PacketError.Condition.jid_malformed);
                // Send the error directly since a route does not exist at this point.
                session.process(reply);
                return null;
            }
        }
        try {
            // Get the token that was generated during the SASL authentication
            AuthToken authToken = session.getAuthToken();
            if (authToken.isAnonymous()) {
                // User used ANONYMOUS SASL so initialize the session as an anonymous login
                session.setAnonymousAuth();
            }
            else {
                String username = authToken.getUsername();
                // If a session already exists with the requested JID, then check to see
                // if we should kick it off or refuse the new connection
                if (sessionManager.isActiveRoute(username, resource)) {
                    ClientSession oldSession = null;
                    try {
                        String domain = localServer.getServerInfo().getName();
                        oldSession = sessionManager.getSession(username, domain, resource);
                        oldSession.incrementConflictCount();
                        int conflictLimit = sessionManager.getConflictKickLimit();
                        if (conflictLimit != SessionManager.NEVER_KICK &&
                                oldSession.getConflictCount() > conflictLimit) {
                            Connection conn = oldSession.getConnection();
                            if (conn != null) {
                                // Kick out the old connection that is conflicting with the new one
                                StreamError error = new StreamError(StreamError.Condition.conflict);
                                conn.getWriter().write(error.toXML());
                                conn.close();
                            }
                        }
                        else {
                            reply.setChildElement(packet.getChildElement().createCopy());
                            reply.setError(PacketError.Condition.conflict);
                            // Send the error directly since a route does not exist at this point.
                            session.process(reply);
                            return null;
                        }
                    }
                    catch (Exception e) {
                        Log.error("Error during login", e);
                    }
                }
                // If the connection was not refused due to conflict, log the user in
                session.setAuthToken(authToken, userManager, resource);
            }
        }
        catch (UserNotFoundException e) {
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.not_authorized);
            // Send the error directly since a route does not exist at this point.
            session.process(reply);
            return null;
        }


        child.addElement("jid").setText(session.getAddress().toString());
        // Send the response directly since a route does not exist at this point.
        session.process(reply);
        return null;
    }

    public void initialize(XMPPServer server) {
        super.initialize(server);
        localServer = server;
        userManager = server.getUserManager();
    }

    public IQHandlerInfo getInfo() {
        return info;
    }
}