Android Open Source - AndroidMessenger A I M Client






From Project

Back to project page AndroidMessenger.

License

The source code is released under:

GNU General Public License

If you think the Android project AndroidMessenger listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*------------------------------------------------------------------------------
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL//*w  ww .  j a v  a  2s  .  co  m*/
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is levelonelabs.com code.
 * The Initial Developer of the Original Code is Level One Labs. Portions
 * created by the Initial Developer are Copyright (C) 2001 the Initial
 * Developer. All Rights Reserved.
 *
 *         Contributor(s):
 *             Scott Oster      (ostersc@alum.rpi.edu)
 *             Steve Zingelwicz (sez@po.cwru.edu)
 *             William Gorman   (willgorman@hotmail.com)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable
 * instead of those above. If you wish to allow use of your version of this
 * file only under the terms of either the GPL or the LGPL, and not to allow
 * others to use your version of this file under the terms of the NPL, indicate
 * your decision by deleting the provisions above and replace them with the
 * notice and other provisions required by the GPL or the LGPL. If you do not
 * delete the provisions above, a recipient may use your version of this file
 * under the terms of any one of the NPL, the GPL or the LGPL.
 *----------------------------------------------------------------------------*/

package com.levelonelabs.aim;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;


/**
 * Implements the AIM protocol
 * 
 * @author Scott Oster, Will Gorman
 * @created September 4, 2001
 */
public class AIMClient implements Runnable, AIMSender {
    // check connection ever "TIME_DELAY" milliseconds (5 mins)
    private static final long TIME_DELAY = 5 * 60 * 1000;
    private static final String PING = "PING";

    private AimConnectionCheck watchdogCheck;
    private AimConnectionCheck watchdogVerify;
    boolean connectionVerified = false;
    private Timer connectionCheck = new Timer();

    static Logger logger = Logger.getLogger(AIMClient.class.getName());

    // rate limiting
    private static final int MAX_POINTS = 10;

    private static final int RECOVER_RATE = 2200;

    // for TOC3 (using toc2_login)
    // private static final String REVISION = "\"TIC:\\Revision: 1.61 \" 160 US
    // \"\" \"\" 3 0 30303 -kentucky -utf8 94791632";
    private static final String REVISION = "\"TIC:TOC2\" 160";

    private String loginServer = "toc.oscar.aol.com";

    private int loginPort = 5190;

    private String authorizerServer = "login.oscar.aol.com";

    private int authorizerPort = 29999;

    private List aimListeners = new ArrayList();

    String name;

    private String pass;

    private String info;

    private String nonUserResponse;

    boolean online;

    private boolean autoAddUsers = false;

    // private final String ROAST = "Tic/Toc";
    private int seqNo;

    private Socket connection;

    private DataInputStream in;

    private DataOutputStream out;

    private Map<String,AIMBuddy> buddyHash;

    private int sendLimit = MAX_POINTS;

    private long lastFrameSendTime = System.currentTimeMillis();

    private int permitMode = PERMIT_ALL;

    private Set permitted;

    private Set denied;


    /**
     * Constructor for the AIMClient object
     * 
     * @param name
     * @param pass
     * @param info
     *            Description of the Parameter
     * @param response
     *            what to say to non-users when they message the bot (if
     *            autoaddUsers==false)
     * @param autoAddUsers
     */
    public AIMClient(String name, String pass, String info, String response, boolean autoAddUsers) {
        this.nonUserResponse = response;

        buddyHash = new HashMap<String,AIMBuddy>();
        permitted = new HashSet();
        denied = new HashSet();
        this.name = imNormalize(name);
        this.pass = pass;
        this.info = info;
        this.autoAddUsers = autoAddUsers;
        this.addBuddy(new AIMBuddy(name));
    }


    /**
     * Constructor for the AIMClient object
     * 
     * @param name
     * @param pass
     * @param info
     *            Description of the Parameter
     * @param autoAddUsers
     */
    public AIMClient(String name, String pass, String info, boolean autoAddUsers) {
        this(name, pass, info, "Sorry, you must be a user of this system to send requests.", autoAddUsers);
    }


    /**
     * Constructor for the AIMClient object
     * 
     * @param name
     * @param pass
     * @param info
     *            Description of the Parameter
     */
    public AIMClient(String name, String pass, String info) {
        this(name, pass, info, false);
    }


    /**
     * Constructor for the AIMClient object
     * 
     * @param name
     * @param pass
     */
    public AIMClient(String name, String pass) {
        this(name, pass, "No info", false);
    }


    /**
     * Strip out HTML from a string
     * 
     * @param line * *
     * @return the string without HTML
     */
    public static String stripHTML(String line) {
        StringBuffer sb = new StringBuffer(line);
        String out = "";

        for (int i = 0; i < (sb.length() - 1); i++) {
            if (sb.charAt(i) == '<') {
                // Most tags
                if ((sb.charAt(i + 1) == '/') || ((sb.charAt(i + 1) >= 'a') && (sb.charAt(i + 1) <= 'z'))
                    || ((sb.charAt(i + 1) >= 'A') && (sb.charAt(i + 1) <= 'Z'))) {
                    for (int j = i + 1; j < sb.length(); j++) {
                        if (sb.charAt(j) == '>') {
                            sb = sb.replace(i, j + 1, "");
                            i--;
                            break;
                        }
                    }
                } else if (sb.charAt(i + 1) == '!') {
                    // Comments
                    for (int j = i + 1; j < sb.length(); j++) {
                        if ((sb.charAt(j) == '>') && (sb.charAt(j - 1) == '-') && (sb.charAt(j - 2) == '-')) {
                            sb = sb.replace(i, j + 1, "");
                            i--;
                            break;
                        }
                    }
                }
            }
        }

        out = sb.toString();
        return out;
    }


    /**
     * Protocol method
     * 
     * @para * *
     * @return roasted string
     */
    private static String imRoast(String pass) {
        String roast = "Tic/Toc";
        String out = "";
        String in = pass;
        String out2 = "0x";
        for (int i = 0; i < in.length(); i++) {
            out = java.lang.Long.toHexString(in.charAt(i) ^ roast.charAt(i % 7));
            if (out.length() < 2) {
                out2 = out2 + "0";
            }

            out2 = out2 + out;
        }

        return out2;
    }


    /**
     * Protocol method * in
     * 
     * @return normalized string
     */
    private static String imNormalize(String in) {
        String out = "";
        in = in.toLowerCase();
        char[] arr = in.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] != ' ') {
                out = out + "" + arr[i];
            }
        }

        return out;
    }


    /**
     * Retrieve a buddy from the list
     * 
     * @param buddyName
     * @return The buddy
     */
    public AIMBuddy getBuddy(String buddyName) {
        return (AIMBuddy) buddyHash.get(imNormalize(buddyName));
    }


    /**
     * Get an iterator for all the current buddy names
     * 
     * @return iterator
     */
    public Iterator getBuddyNames() {
        return Arrays.asList(buddyHash.keySet().toArray()).iterator();
    }


    /**
     * Sign on to aim server
     */
    public void signOn() {
        new Thread(this).start();

        // check the connection
        watchdogCheck = new AimConnectionCheck(this, true);

        // verify the message was received 5 secs later
        watchdogVerify = new AimConnectionCheck(this, false);
        connectionCheck.scheduleAtFixedRate(watchdogCheck, TIME_DELAY, TIME_DELAY);
        connectionCheck.scheduleAtFixedRate(watchdogVerify, TIME_DELAY + 5000, TIME_DELAY);

        // give the server time to log us on before returning flow to the user
        // check for success once every 2 secs, up to 20 secs
        // true connection comes from the handledConnected call back
        for (int i = 0; i < 10; i++) {
            if (!this.online) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                return;
            }
        }
    }


    /**
     * Sign off from aim server
     */
    public void signOff() {
        // cancel the ping until signon is called again
      if(watchdogCheck != null)
        watchdogCheck.cancel();
      if(watchdogVerify != null)
        watchdogVerify.cancel();
        signoff("User request");
    }

    /**
     * A hack to get around skip throwing IOExceptions.. anrdoid still is kinda broken
     * @throws IOException 
     */
    public void skipBytes(DataInputStream i, int bytes) throws IOException{
      for(int x=0; x<bytes; x++)
        i.readByte();
    }

    /**
     * Main processing method for the AIMClient object
     */
    public void run() {
        int length;
        seqNo = (int) Math.floor(Math.random() * 65535.0);

        // AOL likes to have a bunch of bogus IPs for some reason, so lets try
        // them all until one works
        //Problem is for some reason this crashes on android!  Commenting the string array going to just use 1 string
        //InetAddress[] loginIPs = null;
        InetAddress loginIP = null;
        try {
            //loginIPs = InetAddress.getAllByName(loginServer);
          loginIP = InetAddress.getByName(loginServer);
        } catch (UnknownHostException e) {
            signoff("0");
            generateError("Signon err", e.getMessage());
            return;
        } catch (OutOfMemoryError e) {
          
        }

     //   for (int i = 0; i < loginIPs.length; i++) {
            try {
              //  logger.info("Attempting to logon using IP:" + loginIPs[i]);
                logger.info("Attempting to logon using IP:" + loginIP);
                // * Client connects to TOC
                //connection = new Socket(loginIPs[i], loginPort);
                connection = new Socket(loginIP, loginPort);
                connection.setSoTimeout(10000);
                in = new DataInputStream(connection.getInputStream());
                out = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream(),512));
               // logger.info("Successfully connected using IP:" + loginIPs[i]);
                logger.info("Successfully connected using IP:" + loginIP);
               // break;
            } catch (Exception e) {
                // try the next one
            }
 //       }

        if (connection == null || in == null || out == null) {
            signoff("1");
            generateError("Signon err", "Unable to establish connection to logon server.");
            return;
        }

        logger.info("*** Starting AIM CLIENT (SEQNO:" + seqNo + ") ***");
        try {
            // * Client sends "FLAPON\r\n\r\n"
            out.writeBytes("FLAPON\r\n\r\n");
            out.flush();
            // 6 byte header, plus 4 FLAP version (1)
            byte[] signon = new byte[10];
            // * TOC sends Client FLAP SIGNON
            in.readFully(signon);
            // * Client sends TOC FLAP SIGNON
            out.writeByte(42);// *
            
            logger.info("FLAP SIGNIN RECIEVED");
            
            
            out.writeByte(1); // SIGNON TYPE
            out.writeShort(seqNo); // SEQ NO
            seqNo = (seqNo + 1) & 65535;
            out.writeShort(name.length() + 8); // data length = username length
            // + SIGNON DATA
            out.writeInt(1); // FLAP VERSION
            out.writeShort(1); // TLF TAG
            out.writeShort(name.length()); // username length
            out.writeBytes(name); // usename
            out.flush();
            logger.info("LOGIN INFO SENT");
            // * Client sends TOC "toc_signon" message
            frameSend("toc2_signon " + authorizerServer + " " + authorizerPort + " " + name + " " + imRoast(pass)
                + " English " + REVISION + " " + toc2MagicNumber(name, pass) + "\0");
            logger.info("FRAME SENT?");
            // * if login fails TOC drops client's connection
            // else TOC sends client SIGN_ON reply
            logger.info("After frame sent, we have # available:" + in.available());
            try{
              skipBytes(in, 4);
            }catch(IOException e){
              //this sometimes crashes; not sure?
              logger.info("SKIP 4 THREW IOEXCEPTION " + e.getMessage());
            }
            logger.info("skipping 4?" + in.available());
            length = in.readShort(); // data length
            logger.info("Tried to read something..." + length + in.available());
            signon = new byte[length];
            in.readFully(signon); // data
            logger.info("Got signon packets, analyzing...");
            if (new String(signon).startsWith("ERROR")) {
                fromAIM(signon);
                logger.severe("Signon error");
                signoff("2");
                return;
            }
            logger.info("LOGIN OK?");
            skipBytes(in, 4); // seq num
            logger.info("Another problem with skip?");
            length = in.readShort(); // data length
            signon = new byte[length];
            in.readFully(signon); // data
            // * Client sends TOC toc_init_done message
            frameSend("toc_init_done\0");
            online = true;
            generateConnected();
            frameSend("toc_set_info \"" + info + "\"\0");
            logger.info("Done with AIM logon");
            connection.setSoTimeout(3000);
        } catch (InterruptedIOException e) {
            signoff("2.25");
        } catch (IOException e) {
            signoff("3");
        }

        byte[] data;
        while (true) {
            try {
              skipBytes(in, 4);
                length = in.readShort();
                data = new byte[length];
                in.readFully(data);
                fromAIM(data);
                //logger.info("SEQNO:"+seqNo);
            } catch (InterruptedIOException e) {
                // This is normal; read times out when we don't read anything.
                 //logger.warning("*** AIM ERROR: " + e + " ***");
            } catch (IOException e) {
                logger.severe("*** AIM IO ERROR: " + e + "," + e.getMessage() +" ***");
                generateDisconnected();
                break;
            }
        }
       signoff("Connection reset.");
    }


    /**
     * @param name2
     * @param pass2
     * @return
     */
    private static int toc2MagicNumber(String username, String password) {
        int sn = username.charAt(0) - 96;
        int pw = password.charAt(0) - 96;

        int a = sn * 7696 + 738816;
        int b = sn * 746512;
        int c = pw * a;

        return c - a + b + 71665152;
    }


    /**
     * Register a listener to recieve aim events
     * 
     * @param listener
     *            The listener
     */
    public void addAIMListener(AIMListener listener) {
        aimListeners.add(listener);
    }


    /**
     * Send a message to a buddy
     * 
     * @param buddy
     * @param text
     */
    public void sendMessage(AIMBuddy buddy, String text) {
        if ((buddy == null) || buddy.isBanned()) {
            return;
        }

        if (buddy.isOnline()) {
            sendMesg(buddy.getName(), text);
        } else {
            // for some reason we are sending a message to an offline buddy
            // this will generate a status request for them (this message will
            // be lost, but if they are online, we should get an update)
            try {
                frameSend("toc_get_status " + imNormalize(buddy.getName()) + "\0");
            } catch (IOException e) {
                logger.severe("Error sending status request for offline buddy: " + e.getMessage());
            }
        }
    }


    /**
     * Add a single budy
     * 
     * @param buddy
     *            The buddy to add
     */
    public void addBuddy(AIMBuddy buddy) {
        if (buddy == null) {
            return;
        }

        if (getBuddy(buddy.getName()) != null) {
            return;
        }

        if (this.online) {
            String toBeSent = "toc2_new_buddies {g:" + buddy.getGroup() + "\nb:" + imNormalize(buddy.getName()) + "\n}";
            try {
                frameSend(toBeSent + "\0");
            } catch (IOException e) {
                logger.severe(e.toString());
                signoff("Error adding buddy");
            }
        }

        // logger.info("Added buddy to hash");
        buddyHash.put(imNormalize(buddy.getName()), buddy);
    }


    /**
     * Convience method for adding multiple buddies
     * 
     * @param buddyList
     *            List of AIMBuddy
     */
    public void addBuddies(List buddyList) {
        // make a list of buddys for each "group"
        Map groupMap = createGroupMap(buddyList);

        // iterate over the groups and send the buddies
        Iterator groupIter = groupMap.keySet().iterator();
        while (groupIter.hasNext()) {
            String group = (String) groupIter.next();
            String currentlist = "toc2_new_buddies {g:" + group + "\n";
            List groupList = (List) groupMap.get(group);
            for (int i = 0; i < groupList.size(); i++) {
                AIMBuddy buddy = (AIMBuddy) groupList.get(i);
                buddyHash.put(imNormalize(buddy.getName()), buddy);
                currentlist += "b:" + imNormalize(buddy.getName()) + "\n";
                if (currentlist.length() > 1800) {
                    try {
                        frameSend(currentlist + "}\0");
                        currentlist = "toc2_new_buddies {g:" + group + "\n";
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.severe("ERROR adding buddies.");
                    }
                }
            }
            // send the left overs (if any)
            if (currentlist.length() > ("toc2_new_buddies {g:" + group + "\n").length()) {
                try {
                    frameSend(currentlist + "}\0");
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.severe("ERROR adding buddies.");
                }
            }

        }
    }


    /**
     * Create a Map of List of buddies in the same group
     * 
     * @param buddyList
     *            a list of buddies
     * @return a Map <String, List> keyed with group name with value a list of
     *         buddies in that group
     */
    private Map createGroupMap(List buddyList) {
        // <group name,List of buddy>
        Map groupMap = new HashMap();

        // iterate the buddies and group them by group name
        for (Iterator iter = buddyList.iterator(); iter.hasNext();) {
            Object obj = iter.next();
            if (obj instanceof AIMBuddy) {
                AIMBuddy buddy = (AIMBuddy) obj;
                String group = buddy.getGroup();
                // pull the list of buddies in this buddy's group
                List groupList = (List) groupMap.get(group);
                if (groupList == null) {
                    // first buddy in this group, make a new list
                    groupList = new ArrayList();
                    groupMap.put(group, groupList);
                }
                // add the buddy to the list of buddies in this group
                groupList.add(buddy);
            }
        }
        return groupMap;
    }


    /**
     * Remove a single budy
     * 
     * @param buddy
     *            The buddy to add
     */
    public void removeBuddy(AIMBuddy buddy) {
        if (buddy == null) {
            return;
        }

        if (getBuddy(buddy.getName()) == null) {
            return;
        }

        String buddyname = imNormalize(buddy.getName());

        String toBeSent = "toc2_remove_buddy";
        try {
            frameSend(toBeSent + " " + buddyname + " " + buddy.getGroup() + "\0");
        } catch (IOException e) {
            logger.severe(e.toString());
            signoff("Error removing buddy.");
        }

        // logger.info("Removed buddy from hash");
        buddyHash.remove(imNormalize(buddy.getName()));
    }


    /**
     * Convience method for removing multiple buddies
     * 
     * @param buddyList
     *            List of AIMBuddy
     */
    public void removeBuddies(List buddyList) {
        // make a list of buddys for each "group"
        Map groupMap = createGroupMap(buddyList);

        // iterate over the groups and remove the buddies
        Iterator groupIter = groupMap.keySet().iterator();
        while (groupIter.hasNext()) {
            String group = (String) groupIter.next();
            String currentlist = "toc2_remove_buddy";
            List groupList = (List) groupMap.get(group);
            for (int i = 0; i < groupList.size(); i++) {
                AIMBuddy buddy = (AIMBuddy) groupList.get(i);
                buddyHash.remove(imNormalize(buddy.getName()));
                currentlist += " " + imNormalize(buddy.getName());
                if (currentlist.length() > 1800) {
                    try {
                        frameSend(currentlist + " " + group + "\0");
                        currentlist = "toc2_remove_buddy";
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.severe("ERROR removing buddies.");
                    }
                }
            }
            // remove the left overs (if any)
            if (currentlist.length() > "toc2_remove_buddy".length()) {
                try {
                    frameSend(currentlist + " " + group + "\0");
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.severe("ERROR adding buddies.");
                }
            }

        }
    }


    /**
     * Warn a buddy
     * 
     * @param buddy
     */
    public void sendWarning(AIMBuddy buddy) {
        if (buddy == null) {
            return;
        }

        logger.info("Attempting to warn: " + buddy.getName() + ".");

        String work = "toc_evil ";
        work = work.concat(imNormalize(buddy.getName()));
        work = work.concat(" norm \0");
        // logger.info(work);
        try {
            frameSend(work);
        } catch (IOException e) {
            signoff("9");
        }
    }


    /**
     * tell aim to ignore a buddy
     * 
     * @param buddy
     */
    public void banBuddy(AIMBuddy buddy) {
        if ((buddy == null) || (buddy.getName().length() == 0)) {
            return;
        }

        if (getBuddy(buddy.getName()) == null) {
            return;
        }
        buddy.setBanned(true);
        sendDeny(imNormalize(buddy.getName()));
    }


    /**
     * tell aim to ignore a buddy
     * 
     * @param buddyname
     */
    private void sendDeny(String buddyname) {
        if (buddyname.length() == 0) {
            logger.info("Attempting to permit all.");
        } else {
            logger.info("Attempting to deny: " + buddyname + ".");
        }

        String toBeSent = "toc2_add_deny";
        try {
            frameSend(toBeSent + " " + buddyname + "\0");
        } catch (IOException e) {
            logger.severe(e.toString());
            signoff("7.75");
        }
    }


    /**
     * tell aim to permit a buddy
     * 
     * @param buddyname
     */
    private void sendPermit(String buddyname) {
        logger.info("Attempting to permit: " + buddyname + ".");

        String toBeSent = "toc2_add_permit";
        try {
            frameSend(toBeSent + " " + buddyname + "\0");
        } catch (IOException e) {
            logger.severe(e.getMessage());
            signoff("7.875");
        }
    }


    /**
     * protocol methods *
     * 
     * @param toBeSent
     * @exception IOException
     *                Description of Exception
     */
    private void frameSend(String toBeSent) throws IOException {
        if (sendLimit < MAX_POINTS) {
            sendLimit += ((System.currentTimeMillis() - lastFrameSendTime) / RECOVER_RATE);
            // never let the limit exceed the max, else this code won't work
            // right
            sendLimit = Math.min(MAX_POINTS, sendLimit);
            if (sendLimit < MAX_POINTS) {
                // sendLimit could be less than 0, this still works properly
                logger.info("Current send limit=" + sendLimit + " out of " + MAX_POINTS);
               /* try {
                    // this will wait for every point below the max
                    int waitAmount = MAX_POINTS - sendLimit;
                    logger.info("Delaying send " + waitAmount + " units");
                    //Thread.sleep(RECOVER_RATE * waitAmount);
                    sendLimit += waitAmount;
                } catch (InterruptedException ie) {
                }*/
            }
        }
        out.writeByte(42); // *
        out.writeByte(2); // DATA
        out.writeShort(seqNo); // SEQ NO
        seqNo = (seqNo + 1) & 65535;
        out.writeShort(toBeSent.length()); // DATA SIZE
        out.writeBytes(toBeSent); // DATA
        out.flush();

        // sending is more expensive the higher our warning level
        // this should decrement between 1 and 10 points (exponentially)
        int warnAmount = getBuddy(this.name).getWarningAmount();
        sendLimit -= (1 + Math.pow((3 * warnAmount) / 100, 2));
        lastFrameSendTime = System.currentTimeMillis();
    }


    /**
     * Send message event to all listeners.
     * 
     * @param from
     * @param request
     */
    private void generateMessage(String from, String request) {
        AIMBuddy aimbud = getBuddy(from);
        if (aimbud == null) {
            if (autoAddUsers) {
                aimbud = new AIMBuddy(from);
                addBuddy(aimbud);
                aimbud.setOnline(true);
            } else {
                logger.info("MESSAGE FROM A NON BUDDY(" + from + ")");
                // only send a response if a non-empty one is configured
                if ((nonUserResponse != null) && !nonUserResponse.equals("")) {
                    sendMesg(from, nonUserResponse);
                }
                return;
            }
        }

        if (aimbud.isBanned()) {
            logger.info("Ignoring message from banned user (" + from + "):" + request);
        } else {
            for (int i = 0; i < aimListeners.size(); i++) {
                try {
                    ((AIMListener) aimListeners.get(i)).handleMessage(aimbud, request);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * Send warning event to all listeners.
     * 
     * @param from
     * @param amount
     *            of warning
     */
    private void generateWarning(String from, int amount) {
        AIMBuddy aimbud = getBuddy(from);
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleWarning(aimbud, amount);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send connected event to all listeners.
     */
    private void generateConfigReady(Map<String, AIMBuddy> buddyHash2) {
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleConfigReady(buddyHash2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Send connected event to all listeners.
     */
    private void generateConnected() {
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleConnected();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send disconnected event to all listeners.
     */
    private void generateDisconnected() {
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleDisconnected();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send error event to all listeners.
     * 
     * @param error
     *            code
     * @param message
     */
    private void generateError(String error, String message) {
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleError(error, message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send buddy sign on event to all listeners.
     * 
     * @param buddy
     *            that signed on
     * @param message
     */
    private void generateBuddySignOn(String buddy, String message) {
        AIMBuddy aimbud = getBuddy(buddy);
        if (aimbud == null) {
          //aimbud = new AIMBuddy(buddy);
          //addBuddy(aimbud);
            logger.severe("ERROR:  NOTIFICATION ABOUT NON BUDDY SIGN ON(" + buddy + ")");
            return;
        }
        logger.warning("AIM SIGN ON " + buddy);
        if (!aimbud.isOnline()) {
            aimbud.setOnline(true);
            for (int i = 0; i < aimListeners.size(); i++) {
                try {
                  logger.severe("SENDING HNDLE BUDDY SIGN ON");
                    ((AIMListener) aimListeners.get(i)).handleBuddySignOn(aimbud, message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * Send buddy sign off event to all listeners.
     * 
     * @param buddy
     *            that signed off
     * @param message
     */
    private void generateBuddySignOff(String buddy, String message) {
        AIMBuddy aimbud = getBuddy(buddy);
        if (aimbud == null) {
            logger.severe("ERROR:  NOTIFICATION ABOUT NON BUDDY SIGN OFF(" + buddy + ")");
            return;
        }

        // logger.info("XML = \n" + aimbud.toXML());
        aimbud.setOnline(false);
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleBuddySignOff(aimbud, message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send buddy is available event to all listeners.
     * 
     * @param buddy
     *            The subject of the change.
     * @param message
     *            DOCUMENT ME!
     */
    private void generateBuddyAvailable(String buddy, String message) {
        AIMBuddy aimbud = getBuddy(buddy);
        if (aimbud == null) {
            logger.severe("ERROR:  NOTIFICATION ABOUT NON BUDDY AVAILABLE(" + buddy + ")");
            return;
        }
        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleBuddyAvailable(aimbud, message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * Send buddy is unavailable event to all listeners.
     * 
     * @param buddy
     *            The subject of the change.
     * @param message
     *            DOCUMENT ME!
     */
    private void generateBuddyUnavailable(String buddy, String message) {
        AIMBuddy aimbud = getBuddy(buddy);
        if (aimbud == null) {
            logger.severe("ERROR:  NOTIFICATION ABOUT NON BUDDY UNAVAILABLE(" + buddy + ")");
            return;
        }

        for (int i = 0; i < aimListeners.size(); i++) {
            try {
                ((AIMListener) aimListeners.get(i)).handleBuddyUnavailable(aimbud, message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * message recieved from aim
     * 
     * @param buffer
     */
    private void fromAIM(byte[] buffer) {
        try {
            String inString = new String(buffer);

            logger.info("*** AIM: " + inString + " ***");
            StringTokenizer inToken = new StringTokenizer(inString, ":");
            String command = inToken.nextToken();
            if (command.equals("IM_IN2")) {
                // treat every message received as verification
                this.connectionVerified = true;

                String from = imNormalize(inToken.nextToken());
                // whats this?
                inToken.nextToken();
                // whats this?
                inToken.nextToken();
                String mesg = inToken.nextToken();
                while (inToken.hasMoreTokens()) {
                    mesg = mesg + ":" + inToken.nextToken();
                }

                String request = stripHTML(mesg);

                if ((from.equalsIgnoreCase(this.name)) && (request.equals(AIMClient.PING))) {
                    logger.info("AIM CONNECTION VERIFIED(" + new Date() + ").");
                    return;
                }

                logger.info("*** AIM MESSAGE: " + from + " > " + request + " ***");

                // CALL ALL LISTENERS HERE
                generateMessage(from, request.trim());
                return;
            }

            if (command.equals("CONFIG2")) {
                if (inToken.hasMoreElements()) {
                    String config = inToken.nextToken();
                    while (inToken.hasMoreTokens()) {
                        config = config + ":" + inToken.nextToken();
                    }
                    processConfig(config);
                    logger.info("*** AIM CONFIG RECEIVED ***");
                } else {
                    setPermitMode(PERMIT_ALL);
                    logger.info("*** AIM NO CONFIG RECEIVED ***");
                }
                return;
            }

            if (command.equals("EVILED")) {
                int amount = Integer.parseInt(inToken.nextToken());
                String from = "anonymous";
                if (inToken.hasMoreElements()) {
                    from = imNormalize(inToken.nextToken());
                }

                // if what we have is less than what the server just sent, its
                // a warning
                // otherwise it was just a server decrement update
                if (getBuddy(name).getWarningAmount() < amount) {
                    generateWarning(from, amount);
                }

                return;
            }

            if (command.equals("UPDATE_BUDDY2")) {
                String bname = imNormalize(inToken.nextToken());
                AIMBuddy aimbud = getBuddy(bname);
                if (aimbud == null) {
                  aimbud = new AIMBuddy(bname);
                }
                String stat = inToken.nextToken();
                if (stat.equals("T")) {
                    generateBuddySignOn(bname, "INFO");
                     logger.info("Buddy:" + bname + " just signed on.");
                } else if (stat.equals("F")) {
                    generateBuddySignOff(bname, "INFO");
                    logger.info("Buddy:" + bname + " just signed off.");
                }
                int evilAmount = Integer.parseInt(inToken.nextToken());
                aimbud.setWarningAmount(evilAmount);
                if (stat.equals("T")) { // See whether user is available.
                    String signOnTime = inToken.nextToken();

                    // TODO: what is the format of this?
                    // System.err.println(bname+" signon="+signOnTime);
                    String idleTime = inToken.nextToken();
                    // System.err.println(bname+"
                    // idle="+Integer.valueOf(idleTime).intValue()+" mins");
                    if (-1 != inToken.nextToken().indexOf('U')) {
                        generateBuddyUnavailable(bname, "INFO");
                    } else {
                        generateBuddyAvailable(bname, "INFO");
                    }
                }

                return;
            }

            if (command.equals("ERROR")) {
                String error = inToken.nextToken();
                logger.severe("*** AIM ERROR: " + error + " ***");
                if (error.equals("901")) {
                    generateError(error, "Not currently available");
                    // logger.info("Not currently available");
                    return;
                }

                if (error.equals("902")) {
                    generateError(error, "Warning not currently available");
                    // logger.info("Warning not currently available");
                    return;
                }

                if (error.equals("903")) {
                    generateError(error, "Message dropped, sending too fast");
                    // logger.info("Message dropped, sending too fast");
                    return;
                }

                if (error.equals("960")) {
                    String person = inToken.nextToken();
                    generateError(error, "Sending messages too fast to " + person);
                    // logger.info("Sending messages too fast to " + person);
                    return;
                }

                if (error.equals("961")) {
                    String person = inToken.nextToken();
                    generateError(error, person + " sent you too big a message");
                    // logger.info(person + " sent you too big a message");
                    return;
                }

                if (error.equals("962")) {
                    String person = inToken.nextToken();
                    generateError(error, person + " sent you a message too fast");
                    // logger.info(person + " sent you a message too fast");
                    return;
                }

                if (error.equals("980")) {
                    generateError(error, "Invalid login information!");
                    logger.severe("Invalid login information");
                    return;
                }
                if (error.equals("983")) {
                    generateError(error, "Too many login attempts!");
                    logger.severe("Logging on too fast");
                    return;
                }

                if (error.equals("Signon err")) {
                    String text = inToken.nextToken();
                    generateError(error, "AIM Signon failure: " + text);

                    // logger.info("AIM Signon failure: " + text);
                    signoff("5");
                }

                return;
            }
        } catch (Exception e) {
            logger.severe("ERROR: failed to handle aim protocol properly");
            e.printStackTrace();
        }
    }


    /**
     * Processes AIM server-passed config string
     * 
     * @param config
     *            A properly formated TOC configuration.
     */
    private void processConfig(String config) {
        int new_permit_mode = PERMIT_ALL;
        BufferedReader br = new BufferedReader(new StringReader(config),512);
        try {
            String current_group = DEFAULT_GROUP;
            String line;
            while (null != (line = br.readLine())) {
                if (line.equals("done")) {
                    break;
                }
                char type = line.charAt(0);
                
                String arg = line.substring(2);
                switch (type) {
                    case 'g' :
                        current_group = arg;
                        break;
                    case 'b' :
                        // make a new buddy if they dont exist locally
                        AIMBuddy buddy = null;
                        int x = arg.indexOf(":");
                        String alias ="";
                        if(x != -1){ //this person has an alias
                          alias = arg.substring(x+1);
                          arg   = arg.substring(0,x);
                        }else{
                          alias = arg;
                        }
                        buddy = (AIMBuddy) buddyHash.get(imNormalize(arg));
                        if (buddy == null) {
                            buddy = new AIMBuddy(arg, current_group,alias);
                            buddyHash.put(imNormalize(arg), buddy);
                        } else {
                            // they already exist, so just take the server's
                            // word
                            // for the group they belong in
                            buddy.setGroup(current_group);
                        }
                        break;
                    case 'p' :
                        permitted.add(imNormalize(arg));
                        break;
                    case 'd' :
                        denied.add(imNormalize(arg));
                        break;
                    case 'm' :
                        new_permit_mode = Integer.parseInt(arg);
                        break;
                }
            }
        } catch (IOException e) {
            logger.warning("Error reading configuration.");
            signoff("2.25");
            return;
        }
        logger.warning("Generating ConfigReady Handlevent");
        generateConfigReady(buddyHash);
        // this will "readd" existing buddies, but thats ok
        addBuddies(new ArrayList(buddyHash.values()));
        setPermitMode(new_permit_mode);
    }


    /**
     * internal method to send message to aim
     * 
     * @param to
     * @param text
     *            to send
     */
    void sendMesg(String to, String text) {
        if (text.length() >= 1024) {
            text = text.substring(0, 1024);
        }
        logger.info("Sending Message " + to + " > " + text);

        String work = "toc2_send_im ";
        work = work.concat(to);
        work = work.concat(" \"");
        for (int i = 0; i < text.length(); i++) {
            switch (text.charAt(i)) {
                case '$' :
                case '{' :
                case '}' :
                case '[' :
                case ']' :
                case '(' :
                case ')' :
                case '\"' :
                case '\\' :
                    work = work.concat("\\" + text.charAt(i));
                    break;
                default :
                    work = work.concat("" + text.charAt(i));
                    break;
            }
        }

        work = work.concat("\"\0");
        // logger.info(work);
        try {
            frameSend(work);
        } catch (IOException e) {
            logger.severe("*** AIM ERROR: sending message.");
            e.printStackTrace();
            signoff("9");
        }
    }


    /**
     * Change availability. If the reason is the empty String, the user will be
     * made avaiable. Otherwise, it will be made away.
     * 
     * @param reason
     *            The reason explaining why the user is not avaiable.
     */
    private void sendAway(String reason) {
        final String work = "toc_set_away \"" + reason + "\"\0";

        try {
            frameSend(work);
        } catch (IOException e) {
            signoff("10");
        }
    }


    /**
     * sign off
     * 
     * @param place
     */
    private void signoff(String place) {
        online = false;
        logger.info("Trying to close IM (" + place + ").....");
        try {
            if (null != out) {
                out.close();
            }
            if (null != in) {
                in.close();
            }
            if (null != connection) {
                connection.close();
            }
        } catch (IOException e) {
            logger.severe(e.toString());
        }

        generateDisconnected();
        logger.info("*** AIM CLIENT SIGNED OFF.");
    }


    /**
     * Add a buddy to the denied list
     * 
     * @param buddy
     */
    public void denyBuddy(AIMBuddy buddy) {
        String bname = imNormalize(buddy.getName());
        permitted.remove(bname);
        denied.add(bname);
        sendDeny(bname);
    }


    /**
     * Add a buddy to the permitted list
     * 
     * @param buddy
     */
    public void permitBuddy(AIMBuddy buddy) {
        String bname = imNormalize(buddy.getName());
        denied.remove(bname);
        permitted.add(bname);
        sendPermit(bname);
    }


    /**
     * Gets the permit mode that is set on the server.
     * 
     * @return int representation (see public statics) of current permit mode.
     */
    public int getPermitMode() {
        return permitMode;
    }


    /**
     * Sets the permit mode on the server. (Use constants from AIMSender)
     * 
     * @param mode
     */
    public void setPermitMode(int mode) {
        if (mode < 1 || mode > 5) {
            logger.info("Invalid permit mode, ignoring:" + mode);
            return;
        } else if (mode == DENY_SOME && this.denied.size() == 0) {
            logger.info("Attempting to deny some, and none are denied, ignoring.");
            return;
        } else if (mode == PERMIT_SOME && this.permitted.size() == 0) {
            logger.info("Attempting to permit some, and none are permitted, ignoring.");
            return;
        }

        logger.info("Setting permit mode to:" + mode);
        permitMode = mode;
        try {
            frameSend("toc2_set_pdmode " + permitMode + "\0");
        } catch (IOException e) {
            e.printStackTrace();
            logger.severe("ERROR setting permit mode!");
        }
    }


    /**
     * Clear unvailable message
     */
    public void setAvailable() {
        sendAway("");
    }


    /**
     * Set unvailable message
     * 
     * @param reason
     */
    public void setUnavailable(String reason) {
        sendAway(reason);
    }


    /**
     * Try to verify our connections by messaging ourselves. Takes 2 instances
     * to check. One to send the message (sender=true), One to check the result
     * (sender=false)
     * 
     * @author Scott Oster
     * @created February 28, 2002
     */
    static class AimConnectionCheck extends TimerTask {
        AIMClient aim;
        private boolean sender;


        /**
         * Constructor for the AimConnectionCheck object
         * 
         * @param aim
         *            handle to aim
         * @param sender
         *            which instance it is
         */
        public AimConnectionCheck(AIMClient aim, boolean sender) {
            this.aim = aim;
            this.sender = sender;
        }


        /**
         * Main processing method for the AimConnectionCheck object
         */
        public void run() {
            try {
                if (sender) {
                    aim.connectionVerified = false;
                    // only message if we are online (when we get reconnected
                    // online will be true)
                    if (aim.online) {
                        aim.sendMesg(aim.name, AIMClient.PING);
                    }
                } else {
                    // need to see if we got a response
                    if (!aim.connectionVerified) {
                        // restart the connection if we didnt see the message
                        logger.info("*** AIM -- CONNECTION PROBLEM(" + new Date() + "): Connection was not verified!");
                        logger.info("****** Assuming it was dropped, issuing restart.");
                        aim.signoff("Connection Dropped!");
                        new Thread(aim).start();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}




Java Source Code List

com.levelonelabs.aim.AIMAdapter.java
com.levelonelabs.aim.AIMBuddy.java
com.levelonelabs.aim.AIMClient.java
com.levelonelabs.aim.AIMGroup.java
com.levelonelabs.aim.AIMListener.java
com.levelonelabs.aim.AIMSender.java
com.levelonelabs.aim.XMLizable.java
com.levelonelabs.aim.example.AIMReceptionist.java
com.levelonelabs.aim.example.EchoExample.java
com.penn.cis121.androidmessenger.AMProtocolMapper.java
com.penn.cis121.androidmessenger.AccountEditor.java
com.penn.cis121.androidmessenger.AndroidMessenger.java
com.penn.cis121.androidmessenger.ConversationContainer.java
com.penn.cis121.androidmessenger.accountprovider.AccountInfo.java
com.penn.cis121.androidmessenger.accountprovider.AccountProvider.java
com.penn.cis121.androidmessenger.protocols.Aim.java