Articles January 22, 2007 at 7:59 am

Global Buddy List for Jabber

I wrote this script for a client for whom I had set up an iChat server last spring. They asked if it would be possible for the server, in addition to its standard functions, to also maintain a buddy group containing all their local accounts (a feature my wife calls a “persistent buddy list”). From their point of view, having such a list would be extremely useful and having it be maintained by the server would guarantee it stayed up to date and accurate without wasting an employee’s time maintaining it. Unfortunately, neither the base Jabber server nor Apple’s modified version of it provide this feature, but it is possible to emulate it by directly modifying the files where the server stores its user information.

Each individual user’s information is stored in a relatively accessible XML file in the Jabber server’s spool directory. This script edits all the individual users’ .xml files on a Jabber server to create and manage a roster group containing all the local users. It’s possible that it would work on a vanilla Jabber server, but I’ve only used it with Apple’s modified “iChat” server.


Before I get into the setup details, I should mention that pretty much everything here must be done from a root-level command line shell. This is a dangerous combination. If you’re not comfortable in that environment then you should definitely run through this on a test machine before trying it on a production system because any typo could seriously disrupt the server.

The script uses the XML::Treebuilder module, which depends on a few Perl modules and a C package, some of which you will almost certainly need to install. They’re listed in the pre-requisites section below along with the version I’m using and links to copies of the original distribution files on our server plus the copies of the build directories from my install. If possible, you should download the source from a trusted site like CPAN and build/install the software yourself, but I’m providing these since you may need to refer to my builds for modifications or configuration.

(You should be able to install directly from my build directories, if you need to, but you probably have no idea who I am and thus have no particular reason to trust a source kit I provide. Standard best practices would therefore suggest that, if you can, building fresh from CPAN’s files would be preferable. 😉


For those of you who may be unfamiliar with common Unix software building and installing procedures, here are a few things to keep in mind:

  • This is going to look like a lot of work to get a 194-line (including comments) Perl script to work, but this isn’t uncommon. One philosophy of software development holds that rather than writing a large program, the ideal should be to extend the capabilities of the language until your program can be written in a single line of code. While this situation is far from that ideal, the pre-requisites below are extensions to Perl that allow this script to be as short as it is.
  • Most of this software will not have been tested on MacOS X by its authors, so the documentation may be inaccurate (especially on pre-requisites, since many will assume a complete GNU development toolkit as a matter of course).
  • It is common when building/installing software on a new Unix to occasionally have to modify build scripts to change things like the ‘include’ and ‘library’ directories or the names of C header files. You shouldn’t need to do any of this if you use my build materials (since I will have already done it) but consider the discussion above on trusting downloaded source code before deciding whether to do that.
  • Most packages will include a file called “README” or “INSTALL” which will list anything you’ll need to have in place before beginning and describe the steps you’ll need to follow.
  • Most C-based packages will be designed to install using the following sequence of commands (to be run, generally as root, from the distribution’s directory):
    • ./configure
    • make
    • make install
  • Most Perl-based packages will be designed to install using the following sequence of commands (to be run, generally as root, from the distribution’s directory):
    • perl Makefile.PL
    • make
    • make test
    • make install

This is not intended to frighten you off. In fact, though many (possibly most) of the pre-requisites below can probably be installed via fink or darwinports, I would specifically encourage folks who are new to this but want to get experience to build this set manually. If I remember correctly from when I first built these, the versions of the packages listed below required no significant modification to build and install correctly, so they should serve as a good introduction.


Please note: These are listed in the order in which they should be installed. This is important, since they represent a chain of dependencies, rather than a list of independent packages.

  • MacOS X Server 10.4: the iChat server was introduced with 10.4, and 10.5 has not yet been released, so this one should be pretty obvious; it’s currently running on a 10.4.6 server, but there should be no relevant differences between the various 10.4 revisions.
  • HTML::Parser: this should already be installed, as I haven’t found a MacOS X Server install without it. It’s a pre-requisite for HTML-Tree, though, so you may have to go looking for it if that build claims it’s missing.
  • XCode: since you will need an ‘expat’ version higher than 1.95.0 installed, you’ll probably need to build and install it yourself. This will require a C compiler and several support utilities, and the best way to get those onto your server is to install the Apple development toolkit.
  • expat, 2.0.0: the distribution, my build directory
  • XML::Parser, 2.34: the distribution, my build directory
  • HTML::Tagset, 3.10: the distribution, my build directory
  • HTML::Tree, 3.1901: the distribution, my build directory
  • XML::Treebuilder, 3.09: the distribution, my build directory


Before running the script, you’ll need to edit it to change at least two variables (‘staffgroup’ and ‘localserver’) to reflect your local setup; you’ll probably also need to change the ‘stopcmd’ and ‘startcmd’ arrays, since you may not be running as many different Jabber instances as the client for whom I wrote this is.

(The additional Jabber processes this script references in the ‘stopcmd’ and ‘startcmd’ arrays are the Bandersnatch Jabber monitor and the AIM and Yahoo transports. I will be posting articles on those as soon as I can get a clean server on which to test my instructions, and hopefully including binaries you can install since actually compiling them involves several hundred megs of pre-requisite software much of which also has to be modified to work on MacOS X. You will need to remove those entries if you aren’t running these additional services yet.)

The script takes no arguments; simply execute it from a root command-line shell and, if everything here has been done correctly, it should just do its thing. Its thing, in a nutshell, is as follows:

  • Stop the running Jabber processes; all users must be logged out anyway, since we need their clients to reload the roster after the script makes its changes.
  • Read the ‘spooldir’ to build a list of users registered with this Jabber server.
  • For each registered user:
    • read the .xml file and find the ‘roster’ element (which is the term used by Jabber for what most IM clients call a “buddy list”).
    • build a list of all roster ‘item’ elements which point to local users.
    • for every user in the list from above for which ‘staffgroup’ is not their group, set it to be so.
    • add every local user not in the list from above to the roster with the ‘staffgroup’ as their group.
    • write out the user’s .xml file.
  • Re-start the Jabber processes.

BE CAREFUL: make a backup of your ‘spooldir’ before running this (at least the first couple of times, until you’re comfortable that it works well on your server) and if any of your Jabber users do not have local system or Open Directory accounts be aware that this script will delete their .xml files.


Hmm, that should probably be listed as the first bug: the script should make a backup copy of any files it’s going to remove or modify. Not only would this allow the human operator to fix any mistakes it makes fairly quickly, it would also let me use ‘diff’ to identify which changes it made in a file (which can currently be a tad difficult since the XML library tends to have an ambiguous relationship with line breaks).

Since the script (as it is currently written) cannot actually create the base ‘roster’ element itself, this does bring up a limitation of which you should be aware prior to running it. The ‘roster’ element is not created by the Jabber server until a client tells the server that it’s needed. The script will only work for Jabber accounts which have already been created, and they must have at least one buddy in their buddy list before the script is run.

There also seems to be a problem in dealing with user accounts which have multiple “short name” entries, in that they are only recognized correctly if their Jabber account matches the first “short name” entry associated with the account.

Getting Help:

If you have trouble with this, please feel free to send me a message through this site or post in the comments here on this article.


No Comments

  • I can’t find any reference to Open Directory on the Wildfire site you linked to, so I’m guessing it can’t use Open Directory authentication?

    My client already had an Open Directory setup managing authentication for their file servers, email accounts and wireless network access. As you can probably guess, the Open Directory integration Apple provided with their iChat variant of the Jabberd 1.4 server was pretty key in their decision. 😉


    • It’s been a while since I played with Wildfire, but I’m pretty sure you could do
      authentication with LDAP binds and that it worked with my OD setup.

      Awesome script though. Nice work.

    • Wildfire runs on more than just Macs, so they talk about LDAP support, not
      Open Directory. It most definitely supports LDAP – I wouldn’t have bothered
      with it otherwise.

Leave a reply

You must be logged in to post a comment.