Using Messages and Presence

Now that we have a decent grounding in the
Jabber protocol and technology, let's put it to work for us. This
chapter fits Jabber into solutions for two or three common problems and
shows how the technology and features lend themselves very well to
application-to-person (A2P) scenarios.

By way of introduction, we'll have a look at constructing and sending
simple Jabber messages, to effect an "in-your-face" notification
mechanism for a version control system. We'll also introduce a usage of
the <presence/> element as an availability indicator for
connecting systems. Finally we'll combine the two features
(<message/> and the concept of availability) to make
the notification mechanism "sensitive" to the presence of the person
being notified.

CVS Notification via Jabber

CVS—the Concurrent Versions
System—allows you to comfortably create and manage versions of the
sources of your project. The most common use for CVS is to create and
manage versions of program source code, but it can be readily used for
any text files. For example, this book was written using DocBook SGML
(http://www.docbook.org), and a CVS repository was used to manage
different versions of the manuscript throughout the writing and editing
process. CVS allowed us to maintain the original source files for the
chapters, to compare those versions against edited files, and served as
a place from which older versions could be retrieved. You can find out
more about CVS at http://www.cvshome.org.

That's the "Versions System" part of CVS's name. The "Concurrent" part
means that this facility is given an extra dimension in the form of
group collaboration. With CVS, more than one person can share work on a
project, and the various chunks of work carried out by each participant
are coordinated—automatically, to a large extent—by CVS. Multiple
changes by different people to the same file can be merged by CVS; any
unresolvable conflicts (which may for example arise when more than one
person changes exactly the same line of source code) are flagged and
must be resolved by the participants involved.

The general idea is that you can create a project containing files and
directories and have it stored centrally in a CVS repository. Depending
on what sort of access is granted to this repository, other project
participants can pull down a copy of the project—those files and
directories—and work on it independently. In this way, each
participant's work is isolated (in time and space) from the others. When
the work is done, the work can be sent back to the repository and the
changes will be merged into the central copy. After that, those merged
changes are available to the rest of the participants.

CVS Watches and Notification

While CVS automatically handles
most of the tedious merging process that comes about when more than one
person works on a project, it also offers a facility that allows you to
set a "watch" on one or more files in the project and be alerted when
someone else starts to work on those watched files. This is useful if
you wish to preempt any automatic merging process by contacting the
other participant and coordinating your editing efforts with him.

There are two CVS commands involved in setting up watches and
notifications. There are also a couple of CVS administrative files that
determine how the notifications are carried out. Let's look at these
commands and files in turn.

CVS commands

The CVS commands cvs watch and cvs notify are
used, usually in combination, by project participants to set up the
notification mechanism:

cvs watch on|off

Assuming we have a CVS-controlled project called proj1 and we're

currently inside a local checked-out copy of the project's files, we

first use cvs watch to tell CVS to watch a file ("turn a watch

on") that we're interested in, which is file4 in this example: :

yak:~/projects/proj1$ cvs watch on file4

This causes CVS to mark file4 as "watched," which means any time a

project participant checks out the file from the central repository,

the checked-out working copy is created with read-only attributes.

This means the participant is (initially) prevented from saving any

changes to that working copy. It is, in effect, a reminder to that

participant to use the CVS command cvs edit, specifying file4, before

commencing the edit session. Using cvs edit causes CVS to: # Remove

the read-only attribute for the file

Send out notifications (to those who have requested them with the cvs

watch add) that the participant has commenced editing it

cvs watch add|remove

While running cvs watch on against a file will set a marker causing

the file to be replicated with the read-only attribute when checked

out (which has the effect of "suggesting" to the participant editing

the file that he use the cvs edit command to signal that he's to

start editing), the actual determination of the notification

recipients is set up using the cvs watch add command. : Running the

command: :

yak:~/projects/proj1$ cvs watch add file4

will arrange for the CVS notification to be sent to us when

someone else signals their intention (via cvs edit) to edit file4.

CVS administrative files

A number of administrative files used
to control how CVS works are kept in the central CVS repository. Two of
these files, notify and users, are used to manage the watch-based
notification process:

notify

The standard notify file contains a line like this: :

ALL mail %s -s "CVS notification"

The ALL causes the formula described here to be used for any

notification requirements (an alternative to ALL is a

regular expression to match the directory name in which the edit

causing the notification is being carried out). : The rest of the line

is the formula to use to send the notification. It is a simple

invocation of the mail command, specifying a subject line (-s

"CVS notification"). The %s is a placeholder that CVS

replaces with the address of the notification's intended recipient.

The actual notification text, generated by CVS, is piped into the mail

==== The notification ==== If the contents of the notify and users files
have been set up correctly, a typical notification, set up by DJ using
the cvs watch on file4 and cvs watch add file4 commands, and triggered
by Piers using the cvs edit file4 command, will be received in DJ's
inbox looking like the one shown in Example 8-1.

CVS Notifications via Jabber

While email-based notifications are
useful, we can add value to this process by using a more immediate (and
penetrating) form of communication: Jabber. Although mail clients can be
configured to check for mail automatically on a regular basis, using an
IM-style client has a number of immediately obvious advantages:

It's likely to take up less screen real estate. * No amount of

tweaking of the mail client's autocheck frequency (which, if available,
will log in, check for, and pull emails from the mail server) will
match the immediacy of IM-style message push. * In extreme cases,
the higher the autocheck frequency of the mail client, the higher the
effect on overall system performance. * Depending on the configuration,
an incoming Jabber message can be made to pop up, with greater effect. *
A Jabber user is more likely to have a Jabber client running permanently
than an email client. * It's more fun! The design of CVS's notification
mechanism is simple and abstract enough for us to put an alternative
notification system in place. If we substitute the formula in the notify
configuration file with something that will call a Jabber script, we
might end up with something like:

ALL python cvsmsg %s

Like the previous formula, it will be invoked by CVS to send the
notification, and the %s will be substituted by the recipient's
address determined from the users file. In this case, the Python script
cvsmsg is called. However, now that we're sending a notification
via Jabber, we need a Jabber address—a JID—instead of an email address.
No problem, just edit the users file to reflect the new addresses.
Example 8-2 shows what the users file might contain if we were to use
JIDs instead of email addresses.

As Jabber user JIDs in their most basic form (i.e., without a
resource suffix) resemble email IDs, there doesn't appear to be that
much difference. In any case, CVS doesn't really care, and it takes the
portion following the colon separator and simply passes it to the
formula in the notify file.

The cvsmsg Script

Let's now have a look at the script, called
cvsmsg. It has to send a notification message, which it receives on
STDIN, to a JID, which it receives as an argument passed to the script,
as shown in Example 8-3.

Specified here are the connection and authentication details for the
cvsmsg script itself. If it's to send a message via Jabber, it
must itself connect to Jabber. The Server variable specifies
which Jabber server to connect to, and the Username,
Password, and Resource variables contain the rest of
the information for the script's own JID
(cvsmsg@gnu.pipetree.com/cvsmsg) and password.

cvsuser = sys.argv[1] message =

for line in sys.stdin.readlines(): message = message + line

The sys.argv[1] refers to the notification recipient's JID,
which will be specified by the CVS notification mechanism, as it is
substituted for the %s in the notify file's formula. This is
saved in the cvsuser variable. We then build up the content of
our message body we're going to send via Jabber by reading what's
available on STDIN. Typically this will look like what we saw in the
email message body in Example 8-1:

Another Jabberpy module, xmlstream, handles the
connection to the Jabber server. We don't have to use that module
explicitly, however; the jabber module wraps and uses it,
shielding us from the details—hence the call to instantiate a new
jabber.Client object into con, to lay the way for our
connection to the host specified in our Server variable:
gnu.pipetree.com. If no port is explicitly specified, the
standard port (5222), on which the c2s service listens, is
assumed.

The instantiation causes a number of parameters and variables to be
initialized, and internally an xmlstream.Client object is
instantiated; various parameters are passed through from the
jabber.Client object (for example, for logging and debugging
purposes), and an XML parser object is instantiated. This will be used
to parse fragments of XML that come in over the XML stream.

A connection is attempted with the connect() method of the
connection object in con. This is serviced by the
xmlstream.Client object and an XML stream header, as described
in Section 5.3, is sent to gnu.pipetree.com:5222 in an attempt
to establish a client connection. An IOError exception is
raised if the connection cannot be established; we trap this, after a
fashion, with the try: ... except as shown.

Once connected (meaning the client has successfully exchanged XML stream
headers with the server) we need to authenticate:

con.auth(Username,Password,Resource)

The auth method of the jabber.Client object provides
us with a simple way of carrying out the authentication negotiation,
qualified with the jabber:iq:auth namespace and described in
detail in Section 7.3. Although we supply our password here in the
script in plaintext (secret), the auth method will use
the IQ-get (<iq type='get'...>) to retrieve a list of
authentication methods supported by the server. It will try to use the
most secure, "gracefully degrading" to the least, until it finds one
that is supported. This is shown in Figure 8-1.

</code> Note the presence of
Resource in the call. This is required for a successful client
authentication regardless of the authentication method. Sending an
IQ-set (<iq type='set'...>) in the
jabber:iq:auth namespace without specifying a value in a
<resource/> tag results in a "Not Acceptable" error 406;
see Table 5-3 for a list of standard error codes and texts.

We're connected and authenticated. "The world is now our lobster," as an
old friend used to say. We're not necessarily expecting to receive
anything at this stage, and even if we did, we wouldn't really want to
do anything with what we received anyway. So we don't bother setting up
any mechanism for handling elements that might appear on the stream.

con.send(jabber.Message(cvsuser, message, subject="CVS Watch
Alarm"))

The next step is to send the notification message (in message)
to the user (in cvsuser). There are actually two calls here.
The innermost call, jabber.Message(), creates a simple message
element that looks like this:

It takes two positional (and required) parameters; any other information
to be passed (such as the subject in this example) must be
supplied as key=value pairs. The outermost call,
con.send(), sends whatever it is given over the XML stream that
the jabber.Client object con represents. In the case
of the jabber.Message call, this is the string representation
of the object so created (i.e., the <message/> element).

Once the notification message has been sent, the script's work is done.
We can therefore disconnect from the server before exiting the script:

con.disconnect()

Calling the disconnect() method of the jabber.Client
sends an unavailable presence element to the server on behalf of
the user who is connected:

<presence type='unavailable'/>

This is sent regardless of whether a <presence/> element
was sent during the conversation but does no harm if one wasn't.

After sending the unavailable presence information, the XML stream is
closed by sending the stream's closing tag:

</stream:stream>

This signifes to the server that the client wishes to end the
conversation. Finally, the socket is closed.

Dialup System Watch

These days, it's becoming increasingly common
to have a server at home with a dialup connection to the Internet. Your
data, your latest developments, and your mail are stored on there. This
works really well when you're telecommuting and pulling those late night
hacking sessions at home; you have access to all your information and
can connect to the Net.

For many people, however, the reality is that it's not just at home
where the work gets done. Consultants, freelancers, and people with many
customers have their work cut out for them in traveling to different
sites to complete jobs. One of the biggest issues in this respect,
especially in Europe where dialup and pay-per-minute connections still
outweigh fixed or flat-rate connections, is the accessibility of the
information on the server at home, sitting behind a modem. In a lot of
cases, the expense of leaving the server dialed up for the duration of
the trip is far too great to be justified.

One solution is to have the server dial up and connect to the Internet
at regular intervals, say, every hour or two, and remain connected for
5 or 10 minutes. If you need access to the information or need to log on
to your server and run a few tests, you can hold the connection open,
once you've connected to it, by running a ping, for example.

The problem here, though, is timing. Due to the inevitable
synchronization problems between wristwatch and PC clock, eddies in the
space-time continuum, and the fact that people simply forget to check
the time, the online window of the server's dialup is often missed.

The essence of this problem is a presence thing. We need to know
about the presence, the availability, of our server at home, with
respect to the Internet.

Using Jabber as your IM mechanism at work, it's likely that you'll have
a Jabber client of some sort on your laptop or desktop at the customer
sites. Whether it's WinJab on Windows, Jarl in Command Line Interface
(CLI) mode on a remote server over an SSH connection, or any other type
of Jabber client and connection, the point is that the client turns out
to be an ideal ready-made component for solving the dialup timing
problem. Here's how it works:

Get the server to dial up and connect to the Internet regularly. * On

connection, start a script that sends Jabber presence to you. * On
disconnection, get the script to end. If you add to your roster a JID
that represents the server at home, it would be possible to subscribe to
the server's presence and know when it was available—connected to the
Internet—and when it wasn't.

The script we're going to write to send Jabber presence is called
HostAlive.

Making Preparations for Execution

Before diving into the script,
it's necessary to do a bit of preparation. We're going to be using the
presence subscription concept, which was described in Chapter 5 and
is covered in more detail in the next section in this chapter. We're
also going to have to get the script to run, and stay running, when the
dialup connection is made and have it stop when the dialup connection is
ended.

Presence

Rather than get involved in the nitty-gritty of
presence subcriptions right now, let's use the tools that are around us
to get things set up. In order for this to work, we need to be
subscribed to the presence of the script that will be invoked when the
server dials up and connects to the Internet. The script will connect to
the Jabber server using a JID with a username that represents the Linux
server: myserver@gnu.pipetree.com. My JID in this case is
dj@gnu.pipetree.com, so we just use whatever Jabber client happens
to be at hand, say, Jabber Instant Messenger (JIM), to effect both sides
of the subscription.

Step 1

Create JID myserver@gnu.pipetree.com

We need to create the script's JID if it doesn't already exist. We can

send a presence subscription request to the JID. Adding the JID to the

roster using JIM is shown in Figure 8-2.

Step 3

Accept presence subscription as myserver

Using the JIM client, we reconnect with the myserver JID and

accept the presence subscription request from Step 2, so that

dj@gnu.pipetree.com will automatically receive

myserver@gnu.pipetree.com's availability information. Whether or

not myserver subscribes to dj's presence is irrelevant in this

case, as the script itself is not interested in the availability of

anyone at all.

</code> At this stage, the entry in
dj@gnu.pipetree.com's roster that represents the Linux server will
indicate whether the script run at dialup time is active. If we continue
to use the JIM client, we will see that active status is shown by a
yellow bulb and inactive by no icon at all.

Starting and stopping the script

The dialup connection is set
up using the Point-to-Point Protocol daemon pppd. This uses a program
such as chat to talk to the modem and get it to dial the ISP. The pppd
mechanism affords us an ideal way to start and stop a script on the
respective connection and disconnection of the line. When the
connection has been made, the script /etc/ppp/ip-up is invoked and
passed a number of connection-related parameters. Similarly
/etc/ppp/ip-down is invoked when the connection is closed.

Some implementations of pppd also offer /etc/ppp/ip-up.local and
/etc/ppp/ip-down.local, which should be used in place of the ip-up and
ip-down scripts if they exist. These .local versions are
intended to separate out system-specific connection-related activities
from general connection-related activities, in a similar way to how the
rc.local file allows system-specific startup activities to be defined in
the /etc/rc.d/ Unix System V set of runlevel directories.

So what we want to do is start HostAlive with
ip-up[.local] and stop it with ip-down[.local]. What
these starter and stopper scripts might look like is shown in Example
8-4 and Example 8-5. They are simply shell scripts that share the
process ID (PID) of the Jabber script via a temporary file. The starter
starts the Jabber script and writes the PID of that script to a file.
The stopper kills the script using the PID.

Example 8-4 shows that we're passing through one of the parameters that
pppd gives to the ip-up script: the remote IP address—by which the
server is known during its temporary connection to the Internet—in the
$5 variable.[1] This IP address can be passed along as part
of the availability information in the <presence/>
element, so that the recipient (dj) can see what IP address has been
assigned to the server.

The HostAlive Script

As you might have guessed from looking at
Example 8-4, we're going to write HostAlive in Java, shown in
Example 8-6. We'll use the JabberBeans library; see Section P.4
in the Preface for details of where to get this library and what the
requirements are.

The JabberBeans library is highly modular and designed so we
can pick only the features that we need; in this case, however, we're
just going to import the whole set of classes within the
org.jabber.jabberbeans and
org.jabber.jabberbeans.Extension packages, for simplicity.

We're also going to be manipulating the Jabber server's hostname, so we
pull in the InetAddress class for convenience.

The script must connect to the Jabber server on gnu.pipetree.com as
the myserver user. We define some constants for this:

In the same way as with the Python-based CVS notification script earlier
in this chapter, we also start off by building a connection to the
Jabber server. As before, it's a two-stage process. The first stage is
to create the connection object:

public static void main(String argv[]) {

ConnectionBean cb=new ConnectionBean();

A ConnectionBean object represents the connection between the
script and the Jabber server. All XML fragments (Jabber elements) pass
through this object.

Then it's time to attempt the socket connection and the exchange of XML
stream headers:

We create an Internet address object in addr from the hostname
assigned to the SERVER constant. As the creation of the
addr instance may throw an exception (Unknown Host), we
combine the instantiation with the connection() call on the
ConnectionBean object, which may also throw an exception of its
own—if there is a problem connecting.

At this stage, we're connected and have successfully exchanged the XML
stream headers with the Jabber server. So now we must authenticate:

Figure 8-3 shows how the objects in this section of code interrelate
and represent various parts of what we're trying to do—which is to
construct an authorization packet. This takes the form of an IQ-set
containing a <query/> tag qualified by the
jabber:iq:auth namespace like this:[2]

Constructing Jabber elements with the JabberBeans library uses
so-called builders that allow individual element components to be
created separately and then fused together into a final structure. In
the code, we use two builders: an InfoQueryBuilder to construct
the <iq/> envelope and an IQAuthBuilder to
construct the <query/> content.

Taking the code step by step, we create or declare each of the three
things, iqb, iq, and iqAuthb:

Once we've constructed the authorization element, now held as the
iq object, we can send it down the stream to the Jabber server
with the send() method of the ConnectionBean object
cb:

cb.send(iq);

Finally, once we've authenticated, we can construct the presence packet
and send it using the same technique as before.[3]

We construct a new object to represent the presence packet denoting
general availability—<presence/>:

PresenceBuilder pb=new PresenceBuilder();

In this case, there are no namespace-qualified extensions to add to the
<presence/> element, but we do want to add the IP
address that was passed into the script and available in
argv[0]. We can use the setStatus() method on the
presence object to set the optional <status/> to contain
that IP address:

pb.setStatus(argv[0]);

After this, we can go ahead and generate the element, which will look
like this:

<presence> <status>123.45.67.89</status>
</presence>

After the generation with the build() call, we send it down the
stream in the same way as the authorization <iq/>
element:

As for each of the build() calls, we must trap a possible
exception that build() throws if it can't complete (for
example, due to lack of information). This is the
InstantiationException.

We can see the results of myserver sending such an information-laden
<presence/> element to dj in Figure 8-4. As the
server connects to the Internet, the Java script is started via the
ip-up script, and it relays the assigned IP address, which is shown
in Jarl's status bar as the availability information reaches dj's
client.

</code> All that remains for the script

to do now is to hang around. While the XML stream to the Jabber server
remains, and the connection is not broken, its availability will remain
as it was as described by the simple <presence/> element
we sent. So we simply go into a sort of hibernation. We have no hope of
escaping, but it should be taken care of by the ip-down script as
described earlier.

In fact, when the ip-down script kills the script, the socket
connection will be closed, but there was no clean disconnect—no
<presence type='unavailable'/> was sent by the script to
the Jabber server. In this case, the Jabber server will notice that the
socket was closed and generate an unavailable
<presence/> element on behalf of the client.

Presence-Sensitive CVS Notification

In Section 8.1 early in this
chapter, we replaced the email-based CVS notification mechanism with a
Jabber-based one. The script used was extremely simple—it connected to
the Jabber server specified, authenticated, and sent off the
notification message to the recipient JID.

What if we wanted to make the script "sensitive"? Jabber's presence
concept could help us here; if we extended the mechanism to allow for
the building of presence-based relationships between the notification
script and the notification recipients, we can make the sending of the
notification message dependent on the recipient's availability.
"Presence-based relationships" refers to the presence subscription
mechanism described in Section 5.4.2.3.

("cvsmsg-sensitive"), on receipt of the presence subscription from a
recipient, accepts the request and reciprocates by sending a
subscription request back to that recipient.

On receipt of the

presence subscription from the notification script, the recipient
accepts the request.

When the notification script starts up to send a

message, it announces its own availability with a
<presence/> element, which causes the availability of
the JIDs to which it has a presence subscription to be sent to it. Based
on these <presence/> packets received, it can make a
decision as to whether to send the notification message or not.

The

decision we're going to use here is an arbitrary one: if the recipient
is online, we'll send the message, unless he's specified that he doesn't
want to be disturbed, with the <show>dnd</show>
element.

Subscription Relationships

This method will result in "balanced"
subscription relationships between script and recipients. In other
words, the script is subscribed to a recipient's presence, and vice
versa.

Of the two presence subscription "directions," the one where the
notification script subscribes to the recipient's presence (as
opposed to the one where the recipient subscribes to the
notification script's presence) is by far the most important. While
it's not critical that the recipients know when the notification script
is connected and active, it's essential that the notification script
know about a recipient's availability at the time it wants to send a
message.

So would it be more appropriate to create "unbalanced" subscription
relationships?

An unbalanced relationship is one where one party knows about the other
party's availability but not vice versa. The idea for sensitizing
the notification script will work as long as the script can know about
the availability of the recipients. Whether or not the opposite is true
is largely irrelevant.

Nevertheless, it's worth basing the interaction on balanced, or
reciprocal, presence subscriptions, primarily for simplicity's sake and
also for the fact that most Jabber clients (and most users of these
clients) tend to cope well and consistently with balanced subscriptions,
whereby the representation and interpretation of unbalanced
relationships is dealt with and understood in different manners. Some
clients use a lurker group to classify one-way presence
subscriptions from other JIDs (a "lurker" being one that can see you
while you can't see it).

Far from being nebulous concepts, balanced and unbalanced subscription
relationships are characterized technically by values of a certain
attribute specified in each item—each JID—in a roster: the
subscription attribute of the <item/> tags
within the roster. As we progress through the extensions to the CVS
notification script,we'll be examining these values at various stages in
this recipe description in Section 8.3.3.

{{Sidebar|AnthropomorphismIt's worth pointing out at this stage that
adding a JID that's used by a script to connect to Jabber is
slightly symbolic of the extension of the instant messaging world into
the wider arena of A2P messaging. Adding a service JID to your
roster and sharing presence information with that service immediately
widens the scope of what's possible with a humble instant messaging
client, and blurs the boundaries between people and applications.

</code>

The cvsmsg-s Script

The script, as it stands in Section 8.1.3,
is what we want to extend and make sensitive to presence. Example 8-7
looks at the extended script, cvsmsg-s, and then walks through the
additions.

Presence callback

The next addition to the script is a
callback to handle <presence/> elements. The callback in
this script takes the form of a subroutine called presenceCB()
("presence callback"). Callbacks, in relation to programming with
Jabber, are explained in Section 8.3.4.

This is what the callback for handling <presence/>
elements looks like:

Phew! Let's take it a bit at a time. The first thing to note is what's
specified in the subroutine declaration:

def presenceCB(con, prs):

As a handler, the subroutine presenceCB() will be passed the
connection object in con, and the presence node in
prs. con is the same connection object that is created
later in the script (con = jabber.Client(host=Server)) and is
passed in for convenience, as it's quite likely we're going to want to
use it, say, to send something back over the stream.

The presence node in prs is an object representation of the XML
fragment that came in over the stream and was parsed into its component
parts. The object is an instance of the jabber.Presence class,
which is simply a specialization of the more generic
jabber.Protocol class, as are the other classes that represent
the other two Jabber protocol elements that are to be expected:
jabber.Message and jabber.Iq. The
jabber.Protocol class represents protocol elements in general.

As such, there are a number of <presence/>
element-specific methods we can call on the prs object, such as
getShow() and getStatus() (which return the values of
the <show/> and <status/> tags—children
of the <presence/> element—respectively) and general
element methods such as getID, which returns the value of any
id attribute assigned to the element, and setTo(),
which can be used to address the element—to set the value of the
to attribute.

The first thing the handler does is to call a few of these element
methods to determine the type of <presence/> element
(presence types are described in Section 5.4.2), and who it's coming
from:

type = prs.getType() parts = split(prs.getFrom(), '/') who =
parts[0]

When the notification script is called, the JID found in the CVS users
file is substituted for the %s in the formula contained in the
CVS notify file. So if the user dj were to be notified, the JID
passed to the script would be dj@gnu.pipetree.com.

The way JIDs are passed around independently of the context of a
Jabber session is usually in the simpler
form—username@hostname, that is, without the resource
suffix—username@hostname/resource. As
described in Chapter 5, the resource is primarily used to distinguish
individual sessions belonging to one Jabber user.

But when the Jabber library—and subsequently a handler subroutine in the
script—receives an element, it contains a from attribute whose
value has been stamped by the Jabber server as it passes through. The
value represents the session, the connection, from which the
<presence/> element was sent and, as such, includes a
resource suffix. So in order to properly match up the source JID for any
incoming <presence/> element with the JID specified when
the script was invoked (contained in the cvsuser variable), we
need to strip off this resource suffix. The remaining
username@hostname part is captured in the who
variable.

There's one more step to determine the presence type. The type
attribute is optional; its absence signifies the default presence
type, which is available. So we effect this default
substitution here to make the subsequent code clearer:

if type == None: type = 'available'

At this stage, we want to take different actions depending on what sort
of presence information has arrived. Recalling the sequence of events in
the reciprocal presence subscription exchange described earlier in this
chapter, one of the activities is for a potential notification recipient
to subscribe to the presence of the script's JID.

This subscription request is carried in a <presence/>
element, with a type of subscribe. Example 8-8 shows what a
typical subscription request would look like.

At this stage, dj@gnu.pipetree.com has just sent a request to
subscribe to the script's presence. The subscription relationship
between the two parties is nondescript, and this is reflected in the
details of the item in dj's roster that relates to the script's JID:

Each call to the jabber.Presence class constructor creates a
node representing a <presence/> element. The two
parameters passed in the call are fairly self-explanatory: we specify
to whom the <presence/> element should be sent, and
the type.

If the presence subscription request came in from the JID
dj@gnu.pipetree.com, then the XML represented by the node created in
the first call here (specifying a presence type of subscribed)
would look something like that in Example 8-9.

Acceptance of a presence subscription request from
dj@gnu.pipetree.com

In the first case, we use a to attribute, because our
conversation is one-to-one. In the second, we don't; our unaddressed
availability information is caught by the server and in turn sent on to
those entities that are subscribed to your presence.

Although you can send <presence/> elements that
convey availability information directly to a JID, it's not normal.
However, explicitly addressing the elements in a subscription scenario
is essential.

There's another situation in which such "directed" (explicitly
addressed) <presence/> elements are used—to partake of
the services of the availability tracker. This is described in the
Section 5.4.2.4.

</code>
Once constructed, each of the jabber.Presence nodes is sent
back along the stream with the con.send() calls.

Now that the script has accepted djs subscription request, djs
roster item for the script reflects the new relationship:

<item jid='cvsmsg@gnu.pipetree.com' subscription='to'/>

subscription='to' denotes that the subscription relationship is
currently one way—dj has a subscription to the script. There's no
ask attribute as there's no current request going from dj
to the script.

While dj's roster item for the script shows a subscription
value of to, the script's roster item for dj shows a
subscription value of from:

<item jid='dj@gnu.pipetree.com' subscription='from'
ask='subscribe'/>

which shows that the script has a subscription from'dj.

Furthermore, remember that the script not only accepts dj's
subscription request, it sends a reciprocal one of its own. (Hence the
ask="subscribe' status in the item.) When dj accepts this
request, the roster item changes yet again to reflect the balanced
relationship:

<item jid='cvsmsg@gnu.pipetree.com' subscription='both'/>

We want the script to handle requests to unsubscribe from its
presence in the same way:

The only difference between this section and the previous one is that it
deals with requests to unsubscribe as opposed to
subscribe to presence. Otherwise it works in exactly the same
way. A sequence of <presence/> elements used in an
"unsubscription conversation" between dj and the script, and the
changes to the roster <item/> tags on each side, is shown
in Figure 8-5.

</code> While we
must take action on presence types subscribe and
unsubscribe, we don't really need to do anything for their
acknowledgment counterparts: subscribed and
unsubscribed ("I have accepted your request, and you are now
subscribed/unsubscribed to my presence").

Nevertheless, just for illustration purposes, we'll include a couple of
conditions to show what's going on when the script runs:

This presenceCB() subroutine is set up to handle
<presence/> elements. In a typical execution scenario,
where the script is subscribed to the presence of many potential CVS
notification recipients, the subroutine is going to be called to handle
the availability information of all recipients who happen to be
connected to Jabber at the moment of notification. We're interested in
the availability information of only one particular recipient (who
== cvsuser), and we want to check on the contents of the
<show/> tag.

If we get a match, we can send the notification message by creating a
jabber.Message node that will look like this:

As in the cvsmsg script, once created, the node can be sent with the
con.send() method call.

Like the conditions for the presence subscription and unsubscription
acknowledgments, we're including a final condition to deal with the case
where a recipient disconnects from the Jabber server during the
execution of the script: an
unavailable<presence/> element will be sent:

elif type == 'unavailable': print "%s is unavailable" % (who)

We're simply logging such an event for illustration purposes.

Connection and authentication

Most of the main part of the
script is the same as the nonsensitive version from Section 8.1: reading
in the notification message, preparing a connection to the Jabber
server, and trying to connect:

Registration of <presence/> handler

While we've
defined the presenceCB() subroutine to handle
<presence/> packets, we haven't actually told the Jabber
library about it. The call to the setPresenceHandler() method
of the connection object does this for us, performing the "Register
handler" step shown in Figure 8-6. The steps shown in Figure 8-6 are
described in Section 8.3.4.

con.setPresenceHandler(presenceCB)

</code>

Request for roster

It's easy to guess what the next method
call does:

con.requestRoster()

It makes a request for the roster by sending an IQ-get with a query
qualified by the jabber:iq:roster namespace:

However, as there are no explicit references to the roster anywhere in
the script, it's not as easy to guess why we request the roster in the
first place. We know that the client-side copy is merely a "slave" copy,
and, even more relevant here, we know that subscription
information in the roster <item/> tags is managed by the
server—we as a client don't need to (in fact, shouldn't) do anything
to maintain the subscription and ask attributes and
keep them up to date.

So why do we request it? Basically, it's because there's a fundamental
difference between <presence/> elements used to convey
availability information and <presence/> elements used to
convey presence subscription information. If John sends Jim availability
information in a <presence/> element, whether directly
(with a to attribute) or indirectly (through the distribution
of that element by the server to Jim as a subscriber to John's
presence), and Jim's offline on holiday, it doesn't make sense to store
and forward the message to him when he next connects:

availability information are not stored and forwarded if they can't
be delivered because the intended recipient is offline. What would be
the point?

However, <presence/> elements that convey subscription
information are a different kettle of fish. While it's not important
that a user is sent out of date availability information when he next
connects to his Jabber client, any subscription (or unsubscription)
requests or confirmations that were sent to him are important. So
they need to be stored and forwarded.

As we've already seen, the presence subscription mechanism and rosters
are inextricably linked. And if we look briefly under the covers, we see
how this is so. When a presence subscription request is sent to a user,
it runs the gauntlet of modules in the JSM (see Section 4.4.4 for
details on what these modules are). The roster-handling module
mod_roster grabs this request, and, just in case the recipient turns
out not to be connected, stores it.

And here's how intertwined the presence subscription mechanism and
rosters really are: the request is stored as a cluster of attribute
details within an <item/> tag in the roster belonging to
the recipient of the presence subscription request. It looks like
this:

<item jid='user@hostname' subscription='none' subscribe=
hidden=/>

On receipt of a presence subscription request, the mod_roster module
will create the roster item if it doesn't exist already and then assign
the attributes related to presence
subscription—subscription="none' and subscribe="'—to
it. There's no ask attribute, as this is assigned only to the
item on the roster belonging to the sender, not the one belonging to
the receiver, of the subscription request.

The subscribe attribute is used to store the reason for the
request, that, if specified, is carried in the <status/>
tag of the <presence/> element that conveys the request.
If no reason is given, the value for the attribute is empty, as shown
here. Otherwise, it will contain what was stored in the
<status/> tag. Example 8-10 shows a presence subscription
request that carries a reason.

is used internally by mod_roster to mark the item as nondisplayable;
it effectively is a pseudo <item/> that, when brought to
life, actually turns out to be a <presence/> element. So
when a request for the roster is made, mod_roster makes sure that it
doesn't send these "hidden" items. The hidden attribute always
has an empty value, as shown here.

After storing the subscription request, mod_roster will actually
send the original <presence/> element that conveyed that
request to the recipient—that is, if the recipient is online and if
the recipient has already made a request for his roster. As sending an
availability presence packet:

<presence/>

causes the mod_offline module to forward any messages stored
offline in that user's absence, so requesting the roster:

<iq type='get'><query
xmlns='jabber:iq:roster'/></iq>

causes the mod_roster module to forward any subscription requests
stored offline in that user's absence.

Sending of availability information

OK. We've connected,
authenticated, defined, and registered the callback to handle
<presence/> elements, and requested the roster, so
mod_roster will send us any presence subscription (or
unsubscription) requests. Now we need to make an availability
announcement in the form of a simple <presence/> element:

<presence/>

We can do this by calling the sendInitPresence() method on the
connection object:

con.sendInitPresence()

This availability information will be distributed to all the entities
that are subscribed to the script's presence and are online at that
moment. It will also signify to the Jabber server that we are properly
online—in which case it can forward to us any messages that had been
stored up in our absence.

We're not really expecting any <message/> elements;
indeed, we haven't set up any subroutine to handle them, so they'd just
be thrown away by the library anyway. The real reason for sending
presence is so that the server will actively go and probe those in a
presence subscription relationship with the script and report back on
those who are available (who have themselves sent their presence during
their current session). This causes <presence/> elements
to arrive on the stream and make their way to the presenceCB()
handler.

Waiting for packets

Once everything is set up, and the script
has announced its presence, it really just needs to sit back and listen
to the <presence/> elements that come in. If one of
these is from the intended notification recipient, and the availability
state is right (i.e., not in dnd mode), we know that the
circumstances are appropriate for sending the notification.

But the elements being sent over the stream from the server don't
spontaneously get received, parsed, and dispatched; we can control when
that happens from the script. This is the nub of the symbiosis between
the element events and the procedural routines, and it's name is
process().

Calling process() will check on the stream to see if any XML
fragments have arrived and are waiting to be picked up. If there are
any, Steps 3 through 5, shown in Figure 8-6 and described in Section
8.3.4, are executed. The numeric value specified in the call to
process() is the number of seconds to wait for incoming
fragments if none is currently waiting to be picked up. Specifying no
value (or 0) means that the method won't hang around if nothing has
arrived. Specifying a value of 30 means that it will wait up to half a
minute. We really want something in between, and it turns out that
waiting for up to a second for fragments in a finite loop like this:

for i in range(5): con.process(1)

will allow for a slightly stuttered arrival of the
<presence/> elements that are sent to the script as a
result of the server-initiated probes.

Finishing up

We're just about done. The
<presence/> elements that arrive and find their way to
the callback are examined, and the CVS notification message is sent off
if appropriate. Once the process() calls have finished, and,
implicitly, the (potentially) multiple calls to presenceCB,
there's nothing left to do. So we simply disconnect from the Jabber
server, as before:

con.disconnect()

Jabber Programming and Callbacks

When programming all but the
simplest Jabber scripts, you're going to be using callbacks, as
we've seen in this recipe. Callbacks are also known as handlers.

Rather than purely procedural programming ("do this, then do that, then
do the other"), we need a different model to cope with the
event-based nature of Jabber or, more precisely, the event-based
nature of how we converse using the Jabber protocol over an XML stream.
Although we control what we send over the XML stream connection
that we've established with the Jabber server, we can't control what we
receive, and more importantly, we can't control when we receive
it. We need an event-based programming model to be able to handle
the protocol elements as they arrive.

The libraries available for programming with Jabber offer callback
mechanisms. With these callback mechanisms, we can register subroutines
with the part of the library that's handling the reception of XML
document stream fragments. Then, whenever an element appears on the
incoming stream (a fragment in the stream document that the Jabber
server is sending to us), the library can pass it to the appropriate
subroutine in the script for us to act upon—to be "handled." This
passing of elements to be handled by callbacks is referred to as
dispatching.

Figure 8-6 shows the relationship between the library and script, and
the sequence of events surrounding registering a handler and having it
called. Here are the steps shown: