Extending the Jabber Architecture: Modules

This document will provide a general overview of how to create modules to extend the Jabber Transport.

WORK IN PROGRESS

Modules do the work of maintaining Jabber clients and users, providing authentication, roster management, logging, message history, and similiar functions. They live within the Jabber Transport, and are the end result logic for most of the functionality. When a client connects to the server, all of the pieces of the packet are decoded, identified, processed, and a module is called to either handle the request, decide how it should be handled, or be notified of the change. The server maintains most of the data and does the difficult parts of the processing, leaving only the simplest part of the end-user logic handling to the modules.

Configuration

Before we jump into the module operation and details, we need to understand how the server module configuration is done. Internally to the server, every user has a virtual "group" associated with them. This group identifies which modules handle the different pieces of functionality for this user and is very flexible. Note: determining which users belong in which groups is a special module call, more on this later. Let's take a look at a simple configuration:

        <groups>
                <group id="testing">
                        <roster>mysql</roster>
                        <messages>file</messages>
                        <info>mysql</info>
                </group>
                <group id="dbusers" default="mysql"/>
        </groups>

The format isn't too complex: Each "group" is identified as a node in , and each "function" is identified as a named node within the group. The server stores this configuration and goes through a resolution process when the server is processing a new request:

As seen, the configuration isn't too complex and its use is fairly straight-forward. It can be confusing though once you start authoring a module and interacting with the module, other modules, server, configuration, and connected users.

Code

The modularization is broken into different "functional" areas as well as some general global functions. We'll take a look at the overview of how a module interacts with the server and then look at each functional area specifically.

Initialization

more coming soon, code sample for now:

void mod_test_init(xpt *config)
{
	char *pass;
	xpt *next;
	jpair *j;

	module_set_group("test", mod_test_group);
	module_set_auth("test", mod_test_auth);
	module_set_log("test", mod_test_login, mod_test_logout);
	module_set_status("test", mod_test_statusin, mod_test_statusout);
	module_set_message("test", NULL, mod_test_messageout);
	module_set_roster("test", mod_test_roster);
}

module_set_xxxx("internal module id", handler_function) can be called multiple times for each phase or just once... multiple id's can be used, etc

Functional Groupings

Authentication

Coming...

int mod_test_auth(char *user, char *pass)
{
	/* Authentication handler, returns PASS only if the username/password check out */
	return PASS;
}

Messages and Login/Logout

Coming...

void mod_test_login(char *user, char *nick)
{
	/* Just to notify the module that the user logged in, general purpose
	 */
}

void mod_test_logout(char *user, char *nick)
{
	/* Just to notify the module that the user logged out, general purpose
	 */
}

int mod_test_messagein(char *localuser, char *localnick, char *otheruser, char *othernick, char *subject, char *say, char *thread, char *ext, int priority, char *type)
{
	/* Just to notify the module that the user is recieving a message
	 * SPECIAL: if this user isn't online(based on login/logout handlers) store it for them, offline message
	 * -- return SEND/FAIL to tell the server to actually deliver the message
	 */
	return SEND;
}

int mod_test_messageout(char *localuser, char *localnick, char *otheruser, char *othernick, char *subject, char *say, char *thread, char *ext, int priority, char *type)
{
	/* Just to notify the module that the user sent an outgoing message to another user
	 * -- return SEND/FAIL to tell the server to actually deliver the message
	 */
	return SEND;
}

Statuses and Rosters

Coming...

int mod_test_statusin(char *to, char *user, char *nick, char *say, int type, int priority, char *ext)
{
	/* Called whenever the user is receiving a status change from another user.
	 * to = the local user that will be receiving the status change
	 * user/nick = the ID of the user sending the status change
	 * -- return SEND to tell the server to actually deliver the status change
	 * 	(if it's a type=STATUS_ONLINE the server will also send back this users status)
	 * -- return FAIL to completly ignore this status change
	 * -- return PRIVATE to ONLY deliver it to the user(don't respond to the sender for online status)
	 */
	return SEND;
}

jpair *mod_test_statusout(char *user, char *nick, char *say, int type, int priority, char *ext)
{
	/* Called whenever the user is changing their status and sending the change.
	 * user/nick = the ID of the user sending the status change
	 * -- return a jpair list containing the ID's of users to deliver the status change to
	 */
	return NULL;
}

jpair *mod_test_roster(char *localuser, int type, char *group, char *user)
{
	/* Called to add/del and fetch roster entries for this user
	 * -- return a jpair list containing the group name and user id for each roster entry, or NULL
	 */
	if(type == ROSTER_GET)
	{
		return jpair_new(NULL, "testgroup", "myfriend", 0);
	}
	return NULL;
}

Querying

Coming...

User/Group Resoulution

Coming...

char *mod_test_group(char *user)
{
	/* Returns the "group" that the user belongs to, only if we "know" about this user */
	return strdup("test");
}

Logging

Coming...