Home | History | Annotate | Download | only in smack
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2003-2007 Jive Software.
      7  *
      8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *     http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 
     21 package org.jivesoftware.smack;
     22 
     23 import org.jivesoftware.smack.filter.PacketIDFilter;
     24 import org.jivesoftware.smack.packet.Authentication;
     25 import org.jivesoftware.smack.packet.IQ;
     26 
     27 import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
     28 import org.apache.harmony.javax.security.auth.callback.PasswordCallback;
     29 import org.apache.harmony.javax.security.auth.callback.Callback;
     30 
     31 /**
     32  * Implementation of JEP-0078: Non-SASL Authentication. Follow the following
     33  * <a href=http://www.jabber.org/jeps/jep-0078.html>link</a> to obtain more
     34  * information about the JEP.
     35  *
     36  * @author Gaston Dombiak
     37  */
     38 class NonSASLAuthentication implements UserAuthentication {
     39 
     40     private Connection connection;
     41 
     42     public NonSASLAuthentication(Connection connection) {
     43         super();
     44         this.connection = connection;
     45     }
     46 
     47     public String authenticate(String username, String resource, CallbackHandler cbh) throws XMPPException {
     48         //Use the callback handler to determine the password, and continue on.
     49         PasswordCallback pcb = new PasswordCallback("Password: ",false);
     50         try {
     51             cbh.handle(new Callback[]{pcb});
     52             return authenticate(username, String.valueOf(pcb.getPassword()),resource);
     53         } catch (Exception e) {
     54             throw new XMPPException("Unable to determine password.",e);
     55         }
     56     }
     57 
     58     public String authenticate(String username, String password, String resource) throws
     59             XMPPException {
     60         // If we send an authentication packet in "get" mode with just the username,
     61         // the server will return the list of authentication protocols it supports.
     62         Authentication discoveryAuth = new Authentication();
     63         discoveryAuth.setType(IQ.Type.GET);
     64         discoveryAuth.setUsername(username);
     65 
     66         PacketCollector collector =
     67             connection.createPacketCollector(new PacketIDFilter(discoveryAuth.getPacketID()));
     68         // Send the packet
     69         connection.sendPacket(discoveryAuth);
     70         // Wait up to a certain number of seconds for a response from the server.
     71         IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
     72         if (response == null) {
     73             throw new XMPPException("No response from the server.");
     74         }
     75         // If the server replied with an error, throw an exception.
     76         else if (response.getType() == IQ.Type.ERROR) {
     77             throw new XMPPException(response.getError());
     78         }
     79         // Otherwise, no error so continue processing.
     80         Authentication authTypes = (Authentication) response;
     81         collector.cancel();
     82 
     83         // Now, create the authentication packet we'll send to the server.
     84         Authentication auth = new Authentication();
     85         auth.setUsername(username);
     86 
     87         // Figure out if we should use digest or plain text authentication.
     88         if (authTypes.getDigest() != null) {
     89             auth.setDigest(connection.getConnectionID(), password);
     90         }
     91         else if (authTypes.getPassword() != null) {
     92             auth.setPassword(password);
     93         }
     94         else {
     95             throw new XMPPException("Server does not support compatible authentication mechanism.");
     96         }
     97 
     98         auth.setResource(resource);
     99 
    100         collector = connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
    101         // Send the packet.
    102         connection.sendPacket(auth);
    103         // Wait up to a certain number of seconds for a response from the server.
    104         response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    105         if (response == null) {
    106             throw new XMPPException("Authentication failed.");
    107         }
    108         else if (response.getType() == IQ.Type.ERROR) {
    109             throw new XMPPException(response.getError());
    110         }
    111         // We're done with the collector, so explicitly cancel it.
    112         collector.cancel();
    113 
    114         return response.getTo();
    115     }
    116 
    117     public String authenticateAnonymously() throws XMPPException {
    118         // Create the authentication packet we'll send to the server.
    119         Authentication auth = new Authentication();
    120 
    121         PacketCollector collector =
    122             connection.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
    123         // Send the packet.
    124         connection.sendPacket(auth);
    125         // Wait up to a certain number of seconds for a response from the server.
    126         IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
    127         if (response == null) {
    128             throw new XMPPException("Anonymous login failed.");
    129         }
    130         else if (response.getType() == IQ.Type.ERROR) {
    131             throw new XMPPException(response.getError());
    132         }
    133         // We're done with the collector, so explicitly cancel it.
    134         collector.cancel();
    135 
    136         if (response.getTo() != null) {
    137             return response.getTo();
    138         }
    139         else {
    140             return connection.getServiceName() + "/" + ((Authentication) response).getResource();
    141         }
    142     }
    143 }
    144