A quick joscar login tutorial

This "tutorial" was written by Keith as a Weblog entry once. It is by no means complete nor does it teach how to implement a robust login sequence. A full joscar tutorial will come in a future release. Check the Joust website for updates.

I have to admit that JoscarTester (and almost all of net.kano.joscartests.*) is some really awful code. It's not part of joscar, so I didn't worry too much about it. It's just a "client" I use to test new stuff. So that developers (like Adam :) don't have to parse through that garbage, I shall attempt to describe here how a typical client would function.

First a bit of background on how OSCAR works over TCP. A typical OSCAR "session" consists of at least two but up to six (or so) TCP connections. The first is to the "authorizer" server, login.oscar.aol.com, where you log in with your screenname and password and which redirects you to a "basic online service" (BOS) server. You disconnect from the authorizer and connect to the BOS server. From there you can do most things (IM, info, buddy list, and so on), but other "services" require connections to new servers. We'll get to this in a moment.

To connect to the authorizer, you could create your own TCP Socket or simply use my pre-prepared ClientConn class (I personally prefer asynchronous sockets, probably from my Tcl days :). In fact, there's a ClientConn subclass designed specifically for OSCAR connections! It's called ClientFlapConn and not only does it connect and whatnot, it also runs a FLAP event loop!

What is a FLAP, you may ask? Well it looks like you need to do some more research on the protocol! Briefly, however, FLAP is the protocol over which all client-server (and no client-client) OSCAR communication takes place. It's very simple, allowing for "login cookies," disconnection messages ("you have been disconnected because your screenname was signed onto from another computer"), and separate "channels" of data. It is over the FLAP protocol that OSCAR's ever-popular SNAC commands are sent (on the SNAC data channel).

So, to utilize this whole FLAP thing, first we need to set up a FLAP event handler (for incoming FLAP data packets) and a SNAC command handler (for incoming SNAC commands on the SNAC flap channel) for each connection you make to an OSCAR server. Setting up a FLAP connection is done like so:

Socket socket = createNewTcpConnectionHoweverYouWant();
FlapProcessor flapProcessor = new FlapProcessor(socket);

And then you can begin sending FLAP packets through the FLAP "processor." In addition, you might want to set up a loop to read incoming commands (note that I suggest something cleaner than this):

Runnable looper = new Runnable() {
    public void run() {
        while (true) {
            if (!flapProcessor.readNextFlap()) break;
        }
    }
};
new Thread(looper).start();

So now you've got the FLAP commands being read in from the socket. But where are they going?? If you want to be notified when a new FLAP packet comes in, you need to add a packet listener to the FLAP processor:

FlapPacketListener listener = new FlapPacketListener() {
    public void handlePacket(FlapPacketEvent e) {
        System.out.println("Got FLAP packet!!!");
    }
};
flapProcessor.addPacketListener(listener);

You should probably add an exception handler as well, but we won't get into that here. So now we're reading in FLAP packets. Yay. The problem is that we aren't that interested in FLAP-level data most of the time. We like SNACs; the majority of the interesting parts of the OSCAR protocol are SNAC-based. So let's add a SNAC processor too!

ClientSnacProcessor snacProcessor = new ClientSnacProcessor(flapProcessor);

Now packets on the SNAC channel of the FLAP connection we made will be converted into SnacCommands, and we can easily utilize SNACs' request-response system without any extra code on our part. Yay!

You'd notice, however, if you tried to run the above code, that the "Got FLAP packet!!!" message would not appear when SNAC commands were received. So where are they going? Well, we need to add a listener to the SNAC processor just like we did for the FLAP processor:

SnacPacketListener snacListener = new SnacPacketListener() {
    public void handlePacket(SnacPacketEvent e) {
        System.out.println("Got SNAC packet!");
    }
}
snacProcessor.addPacketListener(snacListener);

(Note that we don't need to add an exception handler to the SNAC processor; it just uses the FLAP processor's exception handlers.)

Alright! Now we're processing FLAPs and SNACs and whatnot. Now, however, comes the hard part: the actual protocol.

Here's how the connection to the authorizer goes:

See the javadoc for details on what each command should contain. The AuthResponse, if the login was successful, contains a host and maybe a port (use 5190 otherwise) for the BOS server. Here's how the connection to the BOS server goes:

So. That's the login process for you. Note that many of the commands sent in setting up the BOS connection are unnecessary, but it's good to send them even if you don't quite know what you're doing; the server sort of expects them and might behave differently without them, thinking you are an older or somehow broken client.

I said I would explain how to connect to other services, like chat rooms or the buddy icon server, but I've been writing this for two and a half hours now and I'd like to stop. :) Here's some sample code to go out on. (It might not compile as-is, but the ideas are there. Note that beginners will always want to include the two italicized lines.)

Socket socket = new Socket("login.oscar.aol.com", 5190);

FlapProcessor fp = new FlapProcessor(socket);
ClientSnacProcessor sp = new ClientSnacProcessor(fp);

sp.addPreprocessor(new FamilyVersionPreprocessor());
sp.setDefaultSnacCmdFactoryList(new DefaultClientFactoryList());

fp.addPacketListener(new FlapPacketListener() {
    public void handlePacket(FlapPacketEvent e) {
        FlapCommand flapCmd = e.getFlapCommand();

        if (flapCmd instanceof LoginFlapCmd) {
            fp.sendFlap(new LoginFlapCmd(LoginFlapCmd.VERSION_DEFAULT));
            sp.sendSnac(new KeyRequest("myscreenname"));
        } else {
            System.out.println("got unknown FLAP command: " + flapCmd);
        }
    }
});

sp.addPacketListener(new SnacPacketListener() {
    public void handlePacket(SnacPacketEvent e) {
        SnacCommand snacCmd = e.getSnacCommand();

        if (snacCmd instanceof KeyResponse) {
            sp.sendSnac(new AuthRequest("myscreenname", "mypassword",
                    new ClientVersionInfo("AOL Instant Messenger, 
                    version 5.1.3036/WIN32", 5, 1, 0, 3036), 
                    ((KeyResponse) snacCmd).getKey());
        } else if (snacCmd instanceof AuthResponse) {
            // ...
        }
    }
});

// this is so messy :)
new Thread() {
    public void run() {
        fp.runFlapLoop();
    }
}.start();

Enjoy.