Package hamsam.protocol.aim.command

Source Code of hamsam.protocol.aim.command.AuthCommandHandler

/*
*
* Hamsam - Instant Messaging API
*
* Copyright (C) 2003 Mike Miller <mikemil@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/

package hamsam.protocol.aim.command;

import java.io.IOException;
import java.util.*;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Logger;


import hamsam.api.*;
import hamsam.api.Buddy;
import hamsam.api.Message;
import hamsam.api.MessageComponent;
import hamsam.api.TextComponent;
import hamsam.exception.IllegalStateException;

import hamsam.net.Connection;
import hamsam.net.DirectConnection;
import hamsam.net.HttpConnection;
import hamsam.net.ProxyInfo;
import hamsam.net.SocksConnection;

import hamsam.util.log.LogManager;

import hamsam.protocol.Protocol;

import hamsam.protocol.aim.AIMReaderThread;
import hamsam.protocol.aim.AIMWriterThread;

import hamsam.protocol.aim.flap.FlapConstants;
import hamsam.protocol.aim.snac.SNACConstants;

import hamsam.protocol.aim.util.ByteUtils;
import hamsam.protocol.aim.util.TLV;
import hamsam.protocol.aim.util.TLVConstants;

/**
* @author mikem
*
*/
public class AuthCommandHandler implements CommandHandler, ConnectionHandler, Runnable {

    //~ Static fields/initializers -----------------------------------------------------------------

    Logger log = LogManager.getLogger();

    /* command handler states */
    private static final int DISCONNECTED = 0;
    private static final int CONNECTING = 1; // sending client ident
    private static final int DISCONNECTING = 3; // disconnecting from authentication server
    private static final int BOS_CONNECTING = 4; // next we connect to the BOS server wtih cookie     
    private static final int BOS_CONNECTED = 5;
    private static final int BOS_SERVICE_LIST = 6;
    private static final int PROTOCOL_NEGOTIATION = 7;
    private static final int ACK_CONNECT_RATES = 8;
    private static final int CLIENT_READY = 9;

    //~ Instance fields ----------------------------------------------------------------------------
    /** This thread checks for incoming commands from AIM    */
    private AIMReaderThread reader;
    /** This thread handles all outgoing commands to AIM.    */
    private AIMWriterThread writer;
    /** This is a buffer for holding all incoming commands. Access to this buffer must be synchronized.  */
    private Vector readBuffer;
    /** This is a buffer for holding all outgoing commands. Access to this buffer must be synchronized.  */
    private Vector writeBuffer;
    /** internal state */
    private int state;
    /** connection type */
    private ProxyInfo proxyInfo;
    /** users screen name*/
    private String screenName;
    /** user's password */
    private String password;
    /** connection object */
    private Connection conn;
    /** AIM protocol instance */
    private Protocol protocol;
    private IMListener listener;
    private boolean quit = false;

    private int seqNum = 11;

    //~ Constructors -------------------------------------------------------------------------------
    /**
     * Default Constructor
     * @param protocol active protocol object
     */
    public AuthCommandHandler(Protocol protocol) {
        this.protocol = protocol;
        state = DISCONNECTED;
        readBuffer = new Vector();
        writeBuffer = new Vector();
        Thread thread = new Thread(this);
        thread.setDaemon(true);
        thread.setName("AuthCommandHandler");
        thread.start();
    }

    //~ Methods ------------------------------------------------------------------------------------
    /**
     * Sends the initial login request to AIM
     * @param info ProxyInfo object containing connection related information
     * @param host host name of the server to connect to
     * @param port port number to connect to
     * @param user user's screen name
     * @param password users password
     */
    public void connect(ProxyInfo info, String host, int port, String user, String password)
        throws IllegalStateException {
        proxyInfo = info;
        screenName = user;
        this.password = password;

        if (listener != null) {
            listener.connecting(protocol);
        }

        // create the conection
        if (connectToHost(host, port, CONNECTING)) {
            startThreads();
            sendToWriterThread(new LoginCmd(screenName, password));
        } else {
            log.severe("error connecting to " + host + " on port " + port);
        }
    }

    /*
     * Start the reader & writer threads
     */
    private void startThreads() {

        if (reader == null) {
            reader = new AIMReaderThread(conn, readBuffer);
            reader.start();
        }

        if (writer == null) {
            writer = new AIMWriterThread(conn, writeBuffer);
            writer.start();
        }
    }

    /**
     * Reconnect to host - used during the OSCAR authentication processing
     * @param hostName
     * @param port
     * @throws IllegalStateException
     */
    public void reconnect(String hostName, int port) throws IllegalStateException {
        log.info("disconnecting from auth server/connecting to BOS server...");

        // disconnect from authentication server / connect to BOS
        disconnect();
        if (connectToHost(hostName, port, BOS_CONNECTING)) {
            startThreads();
        }
    }

    /**
     * Cleans up the connection info in the handler
     */
    public void disconnect() {
        screenName = null;
        password = null;
        state = DISCONNECTING;

        writer.stopWriting();
        reader.stopReading();

        try {
            conn.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        conn = null;
        writer = null;
        reader = null;
        state = DISCONNECTED;
    }

    /** Request protocol shutdown
     *
     */
    public void shutdown() {

        quit = true;
        if (listener != null) {
            listener.disconnected(protocol);
        }
    }

    /**
     * Handles the authentication related
     * @see hamsam.protocol.aim.command.CommandHandler#handleCommand(hamsam.protocol.aim.command.CommandEvent)
     */
    public void handleCommand(CommandEvent evt) {
        log.info("incoming event <--- " + evt.toString());

        Command cmd = evt.getCommand();

        switch (state) {
            case CONNECTING :

                if (cmd.getChannel() == FlapConstants.FLAP_CHANNEL_DISCONNECT) {

                    // get the server and cookie from the command tlv
                    TLV tlvServer = cmd.getTLV(TLVConstants.TLV_TYPE_SERVER);
                    TLV tlvCookie = cmd.getTLV(TLVConstants.TLV_TYPE_COOKIE);
                    TLV tlvError = cmd.getTLV(TLVConstants.TLV_TYPE_ERROR_CODE);

                    if ((tlvServer != null) && (tlvCookie != null)) {
                        // hack the port to 22 now that work firewall blocks real port
                        int port =  5190;// 22;

                        try {
                            String server = null;
                            String serverCookie = tlvServer.getStringValue();
                            int pos = serverCookie.indexOf(':');

                            if (pos != -1) {
                                server = serverCookie.substring(0, pos);
                                port = Integer.parseInt(serverCookie.substring(pos+1));
                            } else {
                                server = serverCookie;
                            }

                            reconnect(server, port);
                        } catch (IllegalStateException e) {
                            e.printStackTrace();
                        }

                        // Login to BOS Server with Cookie
                        state = BOS_CONNECTING;
                        sendToWriterThread(new BOSLoginCmd(tlvCookie.getValue()));
                    } else if (tlvError != null) {
                        log.severe("Authentication error - error code=0x" + ByteUtils.toHexString(tlvError.getValue()));
                    }
                }
                break;

            case BOS_CONNECTING :
                if (cmd.getChannel() == FlapConstants.FLAP_CHANNEL_CONNECT) {
                    state = BOS_CONNECTED;
                    if (listener != null) {
                        listener.connected(protocol);
                    }
                }
                break;

            case BOS_CONNECTED :
                if (cmd.getChannel() == FlapConstants.FLAP_CHANNEL_SNAC) {
                    if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_GENERIC_SERVICE_CONTROLS
                        && cmd.getSubType() == SNACConstants.FAMILY_LIST) {
                        state = BOS_SERVICE_LIST;
                        sendToWriterThread(new ClientFamilyVersionCmd());
                    }

                }
                break;

            case BOS_SERVICE_LIST :
                if (cmd.getChannel() == FlapConstants.FLAP_CHANNEL_SNAC) {
                    if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_GENERIC_SERVICE_CONTROLS) {
                        if (cmd.getSubType() == SNACConstants.MSG_OF_THE_DAY) {
                            log.info("ignoring the MSG OF THE DAY");
                            break;
                        } else if (cmd.getSubType() == SNACConstants.SRV_SVC_VERSIONS) {
                            state = PROTOCOL_NEGOTIATION;
                            sendToWriterThread(new RequestServerRateCmd());
                        }
                        break;
                    }
                }
                break;
            case PROTOCOL_NEGOTIATION :
                if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_GENERIC_SERVICE_CONTROLS) {
                    if (cmd.getSubType() == SNACConstants.SERVER_RATE_INFO) {
                        state = ACK_CONNECT_RATES;
                        sendToWriterThread(new AckServerRateInfo());
                        // send location info including capabilities
                        sendToWriterThread(new ClientSetLocationInfoCmd());

                        // send the 'activate SSI' which starts the buddy notifications (on/off line) flowing
                        //  sendToWriterThread(
                        //      new BaseCmd(
                        //          7,
                        //          SNACConstants.SNAC_FAMILY_SERVER_STORED_INFO,
                        //          SNACConstants.ACTIVATE_SERVER_SSI));

                        sendToWriterThread(
                            new BaseCmd(
                                7,
                                SNACConstants.SNAC_FAMILY_SERVER_STORED_INFO,
                                SNACConstants.REQUEST_CONTACT_LIST));
                        // now short-cut directly to Client-Ready cmd
                        // sendToWriterThread(new ClientReadyCmd());
                        // state = CLIENT_READY;
                        // sendToWriterThread(new RequestICBMParmCmd(seqNum++));
                    }
                }
                break;
            case ACK_CONNECT_RATES :
                if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_SERVER_STORED_INFO) {
                    switch (cmd.getSubType()) {
                        case SNACConstants.CONTACT_LIST_RESPONSE :
                            ContactListResponseCmd contactListResponse = new ContactListResponseCmd(cmd);
                            buddyListReceived(contactListResponse.getGroups());

                            // send the 'activate SSI' which starts the buddy notifications (on/off line) flowing
                            sendToWriterThread(
                                new BaseCmd(
                                    8,
                                    SNACConstants.SNAC_FAMILY_SERVER_STORED_INFO,
                                    SNACConstants.ACTIVATE_SERVER_SSI));

                            sendToWriterThread(new ClientSetStatusCmd(9, 0));

                            //now short-cut directly to Client-Ready cmd
                            sendToWriterThread(new ClientReadyCmd());
                            state = CLIENT_READY;
                            sendToWriterThread(new RequestICBMParmCmd(seqNum++));
                            break;
                    }
                }
                break;

            case CLIENT_READY :
                if (cmd.getSubType() == SNACConstants.SNAC_SUBTYPE_ERROR) {
                    log.severe("error code=" + ByteUtils.toHexString(cmd.getSNACData()));
                } else if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_MESSAGING) {
                    switch (cmd.getSubType()) {
                        case SNACConstants.SRV_ICBM_ERROR :
                            //System.out.println("ICMB error : ");
                            //ByteUtils.dump(cmd.getSNACData());
                            break;
                        case SNACConstants.CLIENT_RECV_ICBM :
                            ServerICBM serverICBM = new ServerICBM(cmd);
                            log.info("ICMB text=" + serverICBM.getMessageText());
                            break;
                        case SNACConstants.RESPONSE_ICBM_PARMINFO :
                            byte[] snacData = cmd.getSNACData();
                            log.fine("ICBM parm info response" + ByteUtils.toHexString(snacData));

                            // bit4 should be set to 1 for mini typing notifications
                            long flags = ByteUtils.getUInt(snacData, 2);
                            flags |= 8;
                            sendToWriterThread(new ClientSetICBMParmsCmd(seqNum++, (int) flags, snacData));
                            break;

                            // test to see if this is how I receive typing notifications!
                            // none received as of yet!!!
                        case SNACConstants.SNAC_SUBTYPE_MINI_TYPING_NOTIFICATION :
                            //System.out.println("Typing notification received!");
                            log.fine("Typing notification received");
                            //todo: figure out if typing started or stopped and
                            //       call the appropriate listener method
                            break;

                    }
                } else if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_BUDDY_LIST_MANAGEMENT) {
                    switch (cmd.getSubType()) {
                        case SNACConstants.SNAC_SUBTYPE_USER_ONLINE :
                        case SNACConstants.SNAC_SUBTYPE_USER_OFFLINE :
                            byte[] data = cmd.getSNACData();
                            int nameLen = data[0];
                            String name = ByteUtils.toString(data, 1, nameLen);
                            String status =
                                cmd.getSubType() == SNACConstants.SNAC_SUBTYPE_USER_ONLINE ? " online" : " offline";
                            log.info("User " + name + status);
                            break;
                    }
                } else if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_SERVER_STORED_INFO) {
                    switch (cmd.getSubType()) {
                        case SNACConstants.CONTACT_LIST_RESPONSE :
                            ContactListResponseCmd contactListResponse = new ContactListResponseCmd(cmd);
                            buddyListReceived(contactListResponse.getGroups());
                            break;
                    }
                } else if (cmd.getFamily() == SNACConstants.SNAC_FAMILY_GENERIC_SERVICE_CONTROLS) {
                    switch (cmd.getSubType()) {
                        case SNACConstants.SRV_ONLINE_INFO :
                             System.out.println("Server Online Info: ");
                             ByteUtils.dump(cmd.getSNACData());
                             break;
                    }
                }  // todo - include handling for snac(04/0c) - ICBM send ACK
                break;
        }
    }

    /*
     *  Connects to a server
     */
    private boolean connectToHost(String host, int port, int newState) throws IllegalStateException {
        log.info("creating connection to host " + host + " port " + port);
        state = newState;

        //      Create a connection object
        try {

            switch (proxyInfo.getProxyType()) {

                case ProxyInfo.DIRECT :
                    this.conn = new DirectConnection(host, port);
                    break;

                case ProxyInfo.SOCKS4 :

                    String proxyHost = proxyInfo.getServerName();
                    int proxyPort = proxyInfo.getServerPort();
                    this.conn = new SocksConnection(proxyHost, proxyPort, host, port);
                    break;

                case ProxyInfo.SOCKS5 :
                    proxyHost = proxyInfo.getServerName();
                    proxyPort = proxyInfo.getServerPort();

                    String proxyUsername = proxyInfo.getUsername();
                    String proxyPassword = proxyInfo.getPassword();
                    this.conn = new SocksConnection(proxyHost, proxyPort, proxyUsername, proxyPassword, host, port);
                    break;

                case ProxyInfo.HTTP :
                    proxyHost = proxyInfo.getServerName();
                    proxyPort = proxyInfo.getServerPort();
                    proxyUsername = proxyInfo.getUsername();
                    proxyPassword = proxyInfo.getPassword();

                    if ((proxyUsername == null) || (proxyPassword == null)) {
                        this.conn = new HttpConnection(proxyHost, proxyPort, host, port);
                    } else {
                        this.conn = new HttpConnection(proxyHost, proxyPort, proxyUsername, proxyPassword, host, port);
                    }

                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    /** Send a buddy an instant message
     * @param buddy Buddy
     * @param message Message
     * @see hamsam.protocol.aim.command.CommandHandler#sendInstantMessage(hamsam.api.Buddy, hamsam.api.Message)
     */
    public void sendInstantMessage(Buddy buddy, Message message) {

        StringBuffer sb = new StringBuffer();
        Enumeration e = message.getComponents();
        while (e.hasMoreElements()) {
            MessageComponent comp = (MessageComponent) e.nextElement();
            if (comp instanceof TextComponent) {
                sb.append(((TextComponent) comp).getSequence());
            }
            //else if(comp instanceof SmileyComponent)
            //{
            //    // handle smiley component
            //}
            //else if(comp instanceof URLComponent)
            //{
            //   // handle URL component
            //}
        }

        sendToWriterThread(new ClientICMB(seqNum++, buddy.getUsername(), sb.toString()));
    }

    /*
     * Send a packet to the writer thread for despatch to server.
     */
    private void sendToWriterThread(Command cmd) {

        synchronized (writeBuffer) {
            writeBuffer.add(cmd);
            writeBuffer.notify();
        }
    }

    /**
     * Processes the incoming events from the reader thread
     * @see java.lang.Runnable#run()
     */
    public void run() {

        CommandEvent evt = null;

        while (!quit) {

            synchronized (readBuffer) {
                while (readBuffer.isEmpty()) {
                    try {
                        readBuffer.wait();
                    } catch (InterruptedException e) {
                    }
                }
                if (readBuffer.isEmpty()) {
                    continue;
                }
                evt = (CommandEvent) readBuffer.remove(0);
            }

            handleCommand(evt);
            evt = null;
        }
    }

    /*
     * Build list of Buddy objects to past to the listener.  We call this
     * as the last part of login.
     */
    private void buddyListReceived(Map map) {
        List buddyList = new LinkedList();
        if (map != null) {
            for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
                String groupName = (String) iter.next();
                List list = (List) map.get(groupName);

                for (Iterator iterator = list.iterator(); iterator.hasNext();) {
                    String buddy = (String) iterator.next();
                    buddyList.add(new Buddy(protocol, buddy, groupName));
                }
            }
        }

        Buddy[] buddies = new Buddy[buddyList.size()];
        buddies = (Buddy[]) buddyList.toArray(buddies);

        if (listener != null) {
            listener.buddyListReceived(protocol, buddies);
        }
    }

    /** Add a buddy to the Buddy List
     * @param buddy Buddy to add to Buddy List
     * @see hamsam.protocol.aim.command.CommandHandler#addToBuddyList(hamsam.api.Buddy)
     */
    public void addToBuddyList(Buddy buddy) {
        sendToWriterThread(new AddBuddyCmd(seqNum++, buddy));
    }

    /** Delete a buddy from the Buddy List
     * @param buddy to delete from Buddy List
     * @see hamsam.protocol.aim.command.CommandHandler#deleteFromBuddyList(hamsam.api.Buddy)
     */
    public void deleteFromBuddyList(Buddy buddy) {
        sendToWriterThread(new DeleteBuddyCmd(seqNum++, buddy));
    }

    /** Ignore a buddy
     * @param buddy Buddy to ignore
     * @see hamsam.protocol.aim.command.CommandHandler#ignoreBuddy(hamsam.api.Buddy)
     */
    public void ignoreBuddy(Buddy buddy) {
        sendToWriterThread(new IgnoreBuddyCmd(seqNum++, buddy));
    }

    /** Unignore a buddy
     * @param buddy Buddy to unignore
     * @see hamsam.protocol.aim.command.CommandHandler#unIgnoreBuddy(hamsam.api.Buddy)
     */
    public void unIgnoreBuddy(Buddy buddy) {
        sendToWriterThread(new UnignoreBuddyCmd(seqNum++, buddy));
    }

    /** Send typing started notification to a Buddy
     * @param buddy Buddy that you are typing a message to.
     * @see hamsam.protocol.aim.command.CommandHandler#typingStarted(hamsam.api.Buddy)
     */
    public void typingStarted(Buddy buddy) {
        sendToWriterThread(new TypingNotificationCmd(seqNum++, buddy, SNACConstants.MTN_TYPING_STARTED));
    }

    /** Send typing stopped notification to a Buddy
     * @param buddy Buddy that you stopped typing a message to.
     * @see hamsam.protocol.aim.command.CommandHandler#typingStopped(hamsam.api.Buddy)
     */
    public void typingStopped(Buddy buddy) {
        sendToWriterThread(new TypingNotificationCmd(seqNum++, buddy, SNACConstants.MTN_TYPING_FINISHED));
    }

    /* (non-Javadoc)
     * @see hamsam.protocol.aim.command.CommandHandler#setListener(hamsam.api.IMListener)
     */
    public void setListener(IMListener listener) {
        this.listener = listener;
    }

    /* (non-Javadoc)
     * @see hamsam.protocol.aim.command.CommandHandler#changeStatus(java.lang.Integer)
     */
    public void changeStatus(Integer statusFlag) {
        // this is how ICQ would do it
        //sendToWriterThread( new ClientSetStatusCmd(seqNum++, statusFlag.intValue()));
        sendToWriterThread( new ClientSetLocationInfoCmd(seqNum++, statusFlag.intValue()));       
    }
}
TOP

Related Classes of hamsam.protocol.aim.command.AuthCommandHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.