Index: perl/modules/Jabber/lib/Net/XMPP/Client.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Client.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Client.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,250 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Client;
+
+=head1 NAME
+
+Net::XMPP::Client - XMPP Client Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Client is a module that provides a developer easy access
+  to the Extensible Messaging and Presence Protocol (XMPP).
+
+=head1 DESCRIPTION
+
+  Client.pm uses Protocol.pm to provide enough high level APIs and
+  automation of the low level APIs that writing an XMPP Client in
+  Perl is trivial.  For those that wish to work with the low level
+  you can do that too, but those functions are covered in the
+  documentation for each module.
+
+  Net::XMPP::Client provides functions to connect to an XMPP server,
+  login, send and receive messages, set personal information, create
+  a new user account, manage the roster, and disconnect.  You can use
+  all or none of the functions, there is no requirement.
+
+  For more information on how the details for how Net::XMPP is written
+  please see the help for Net::XMPP itself.
+
+  For a full list of high level functions available please see
+  Net::XMPP::Protocol.
+
+=head2 Basic Functions
+
+    use Net::XMPP;
+
+    $Con = Net::XMPP::Client->new();
+
+    $Con->SetCallbacks(...);
+
+    $Con->Execute(hostname=>"jabber.org",
+                  username=>"bob",
+                  password=>"XXXX",
+                  resource=>"Work'
+                 );
+
+    #
+    # For the list of available functions see Net::XMPP::Protocol.
+    #
+
+    $Con->Disconnect();
+
+=head1 METHODS
+
+=head2 Basic Functions
+
+    new(debuglevel=>0|1|2, - creates the Client object.  debugfile
+        debugfile=>string,   should be set to the path for the debug
+        debugtime=>0|1)      log to be written.  If set to "stdout"
+                             then the debug will go there.  debuglevel
+                             controls the amount of debug.  For more
+                             information about the valid setting for
+                             debuglevel, debugfile, and debugtime see
+                             Net::XMPP::Debug.
+
+    Connect(hostname=>string,      - opens a connection to the server
+            port=>integer,           listed in the hostname (default
+            timeout=>int             localhost), on the port (default
+            connectiontype=>string,  5222) listed, using the
+            tls=>0|1)                connectiontype listed (default
+                                     tcpip).  The two connection types
+                                     available are:
+                                       tcpip  standard TCP socket
+                                       http   TCP socket, but with the
+                                              headers needed to talk
+                                              through a web proxy
+                                     If you specify tls, then it TLS
+                                     will be used if it is available
+                                     as a feature.
+
+    Execute(hostname=>string,       - Generic inner loop to handle
+            port=>int,                connecting to the server, calling
+            tls=>0|1,                 Process, and reconnecting if the
+            username=>string,         connection is lost.  There are
+            password=>string,         five callbacks available that are
+            resource=>string,         called at various places:
+            register=>0|1,              onconnect - when the client has
+            connectiontype=>string,                 made a connection.
+            connecttimeout=>string,     onauth - when the connection is
+            connectattempts=>int,                made and user has been
+            connectsleep=>int,                   authed.  Essentially,
+            processtimeout=>int)                 this is when you can
+                                                 start doing things
+                                                 as a Client.  Like
+                                                 send presence, get your
+                                                 roster, etc...
+                                        onprocess - this is the most
+                                                    inner loop and so
+                                                    gets called the most.
+                                                    Be very very careful
+                                                    what you put here
+                                                    since it can
+                                                    *DRASTICALLY* affect
+                                                    performance.
+                                        ondisconnect - when the client
+                                                       disconnects from
+                                                       the server.
+                                        onexit - when the function gives
+                                                 up trying to connect and
+                                                 exits.
+                                      The arguments are passed straight
+                                      on to the Connect function, except
+                                      for connectattempts and connectsleep.
+                                      connectattempts is the number of
+                                      times that the Component should try
+                                      to connect before giving up.  -1
+                                      means try forever.  The default is
+                                      -1. connectsleep is the number of
+                                      seconds to sleep between each
+                                      connection attempt.
+
+                                      If you specify register=>1, then the
+                                      Client will attempt to register the
+                                      sepecified account for you, if it
+                                      does not exist.
+            
+    Process(integer) - takes the timeout period as an argument.  If no
+                       timeout is listed then the function blocks until
+                       a packet is received.  Otherwise it waits that
+                       number of seconds and then exits so your program
+                       can continue doing useful things.  NOTE: This is
+                       important for GUIs.  You need to leave time to
+                       process GUI commands even if you are waiting for
+                       packets.  The following are the possible return
+                       values, and what they mean:
+
+                           1   - Status ok, data received.
+                           0   - Status ok, no data received.
+                         undef - Status not ok, stop processing.
+                       
+                       IMPORTANT: You need to check the output of every
+                       Process.  If you get an undef then the connection
+                       died and you should behave accordingly.
+
+    Disconnect() - closes the connection to the server.
+
+    Connected() - returns 1 if the Transport is connected to the server,
+                  and 0 if not.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use Carp;
+use Net::XMPP::Connection;
+use base qw( Net::XMPP::Connection );
+
+sub new
+{
+    my $proto = shift;
+    my $self = { };
+
+    bless($self, $proto);
+    $self->init(@_);
+    
+    $self->{SERVER}->{port} = 5222;
+    $self->{SERVER}->{namespace} = "jabber:client";
+    $self->{SERVER}->{allow_register} = 1;
+
+    return $self;
+}
+
+
+sub _auth
+{
+    my $self = shift;
+
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my %auth;
+    $auth{username} = $args{username};
+    $auth{password} = $args{password};
+    $auth{resource} = $args{resource} if exists($args{resource});
+    
+    return $self->AuthSend(%auth);
+}
+
+
+sub _connection_args
+{
+    my $self = shift;
+    my (%args) = @_;
+    
+    my %connect;
+    $connect{hostname}       = $args{hostname};
+    $connect{port}           = $args{port}           if exists($args{port});
+    $connect{connectiontype} = $args{connectiontype} if exists($args{connectiontype});
+    $connect{timeout}        = $args{connecttimeout} if exists($args{connecttimeout});
+    $connect{tls}            = $args{tls}            if exists($args{tls});
+
+    return %connect;
+}
+
+
+sub _register
+{
+    my $self = shift;
+
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my %fields = $self->RegisterRequest();
+
+    $fields{username} = $args{username};
+    $fields{password} = $args{password};
+
+    $self->RegisterSend(%fields);
+
+    return $self->_auth(%args);
+}
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Connection.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Connection.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Connection.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,468 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Connection;
+
+=head1 NAME
+
+Net::XMPP::Connection - XMPP Connection Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Connection is a private package that serves as a basis
+  for anything wanting to open a socket connection to a server.
+
+=head1 DESCRIPTION
+
+  This module is not meant to be used directly.  You should be using
+  either Net::XMPP::Client, or another package that inherits from
+  Net::XMPP::Connection.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use Carp;
+use base qw( Net::XMPP::Protocol );
+
+
+sub new
+{
+    my $proto = shift;
+    my $self = { };
+
+    bless($self, $proto);
+    
+    $self->init(@_);
+    
+    $self->{SERVER}->{namespace} = "unknown";
+
+    return $self;
+}
+
+
+##############################################################################
+#
+# init - do all of the heavy lifting for a generic connection.
+#
+##############################################################################
+sub init
+{
+    my $self = shift;
+    
+    $self->{ARGS} = {};
+    while($#_ >= 0) { $self->{ARGS}->{ lc(pop(@_)) } = pop(@_); }
+
+    $self->{DEBUG} =
+        Net::XMPP::Debug->new(level      => $self->_arg("debuglevel",-1),
+                              file       => $self->_arg("debugfile","stdout"),
+                              time       => $self->_arg("debugtime",0),
+                              setdefault => 1,
+                              header     => "XMPP::Conn"
+                             );
+
+    $self->{SERVER} = {};
+    $self->{SERVER}->{hostname} = "localhost";
+    $self->{SERVER}->{tls} = $self->_arg("tls",0);
+    $self->{SERVER}->{ssl} = $self->_arg("ssl",0);
+    $self->{SERVER}->{connectiontype} = $self->_arg("connectiontype","tcpip");
+
+    $self->{CONNECTED} = 0;
+    $self->{DISCONNECTED} = 0;
+
+    $self->{STREAM} =
+        XML::Stream->new(style      => "node",
+                         debugfh    => $self->{DEBUG}->GetHandle(),
+                         debuglevel => $self->{DEBUG}->GetLevel(),
+                         debugtime  => $self->{DEBUG}->GetTime(),
+                        );
+    
+    $self->{RCVDB}->{currentID} = 0;
+
+    $self->callbackInit();
+
+    return $self;
+}
+
+
+##############################################################################
+#
+# Connect - Takes a has and opens the connection to the specified server.
+#           Registers CallBack as the main callback for all packets from
+#           the server.
+#
+#           NOTE:  Need to add some error handling if the connection is
+#           not made because the server hostname is wrong or whatnot.
+#
+##############################################################################
+sub Connect
+{
+    my $self = shift;
+
+    while($#_ >= 0) { $self->{SERVER}{ lc pop(@_) } = pop(@_); }
+
+    $self->{SERVER}->{timeout} = 10 unless exists($self->{SERVER}->{timeout});
+
+    $self->{DEBUG}->Log1("Connect: host($self->{SERVER}->{hostname}:$self->{SERVER}->{port}) namespace($self->{SERVER}->{namespace})");
+    $self->{DEBUG}->Log1("Connect: timeout($self->{SERVER}->{timeout})");
+    
+    delete($self->{SESSION});
+    $self->{SESSION} =
+        $self->{STREAM}->
+            Connect(hostname       => $self->{SERVER}->{hostname},
+                    port           => $self->{SERVER}->{port},
+                    namespace      => $self->{SERVER}->{namespace},
+                    connectiontype => $self->{SERVER}->{connectiontype},
+                    timeout        => $self->{SERVER}->{timeout},
+                    ssl            => $self->{SERVER}->{ssl}, #LEGACY
+                    (defined($self->{SERVER}->{componentname}) ?
+                     (to => $self->{SERVER}->{componentname}) :
+                     ()
+                    ),
+                   );
+
+    if ($self->{SESSION})
+    {
+        $self->{DEBUG}->Log1("Connect: connection made");
+
+        $self->{STREAM}->SetCallBacks(node=>sub{ $self->CallBack(@_) });
+        $self->{CONNECTED} = 1;
+
+        if (exists($self->{SESSION}->{version}) &&
+            ($self->{SESSION}->{version} ne ""))
+        {
+            my $tls = $self->GetStreamFeature("xmpp-tls");
+            if (defined($tls) && $self->{SERVER}->{tls})
+            {
+                $self->{SESSION} =
+                    $self->{STREAM}->StartTLS(
+                        $self->{SESSION}->{id},
+                        $self->{SERVER}->{timeout},
+                    );
+            }
+            elsif (defined($tls) && ($tls eq "required"))
+            {
+                $self->SetErrorCode("The server requires us to use TLS, but you did not specify that\nTLS was an option.");
+                return;
+            }
+        }
+
+        return 1;
+    }
+    else
+    {
+        $self->SetErrorCode($self->{STREAM}->GetErrorCode());
+        return;
+    }
+}
+
+
+##############################################################################
+#
+# Connected - returns 1 if the Transport is connected to the server, 0
+#             otherwise.
+#
+##############################################################################
+sub Connected
+{
+    my $self = shift;
+
+    $self->{DEBUG}->Log1("Connected: ($self->{CONNECTED})");
+    return $self->{CONNECTED};
+}
+
+
+##############################################################################
+#
+# Disconnect - Sends the string to close the connection cleanly.
+#
+##############################################################################
+sub Disconnect
+{
+    my $self = shift;
+
+    $self->{STREAM}->Disconnect($self->{SESSION}->{id})
+        if ($self->{CONNECTED} == 1);
+    $self->{STREAM}->SetCallBacks(node=>undef);    
+    $self->{CONNECTED} = 0;
+    $self->{DISCONNECTED} = 1;
+    $self->{DEBUG}->Log1("Disconnect: bye bye");
+}
+
+
+##############################################################################
+#
+# Execute - generic inner loop to listen for incoming messages, stay
+#           connected to the server, and do all the right things.  It
+#           calls a couple of callbacks for the user to put hooks into
+#           place if they choose to.
+#
+##############################################################################
+sub Execute
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    $args{connectiontype} = "tcpip" unless exists($args{connectiontype});
+    $args{connectattempts} = -1 unless exists($args{connectattempts});
+    $args{connectsleep} = 5 unless exists($args{connectsleep});
+    $args{register} = 0 unless exists($args{register});
+
+    my %connect = $self->_connect_args(%args);
+
+    $self->{DEBUG}->Log1("Execute: begin");
+
+    my $connectAttempt = $args{connectattempts};
+
+    while(($connectAttempt == -1) || ($connectAttempt > 0))
+    {
+
+        $self->{DEBUG}->Log1("Execute: Attempt to connect ($connectAttempt)");
+
+        my $status = $self->Connect(%connect);
+
+        if (!(defined($status)))
+        {
+            $self->{DEBUG}->Log1("Execute: Server is not answering.  (".$self->GetErrorCode().")");
+            $self->{CONNECTED} = 0;
+
+            $connectAttempt-- unless ($connectAttempt == -1);
+            sleep($args{connectsleep});
+            next;
+        }
+
+        $self->{DEBUG}->Log1("Execute: Connected...");
+        &{$self->{CB}->{onconnect}}() if exists($self->{CB}->{onconnect});
+
+        my @result = $self->_auth(%args);
+
+        if (@result && $result[0] ne "ok")
+        {
+            $self->{DEBUG}->Log1("Execute: Could not auth with server: ($result[0]: $result[1])");
+            &{$self->{CB}->{onauthfail}}()
+                if exists($self->{CB}->{onauthfail});
+            
+            if (!$self->{SERVER}->{allow_register} || $args{register} == 0)
+            {
+                $self->{DEBUG}->Log1("Execute: Register turned off.  Exiting.");
+                $self->Disconnect();
+                &{$self->{CB}->{ondisconnect}}()
+                    if exists($self->{CB}->{ondisconnect});
+                $connectAttempt = 0;
+            }
+            else
+            {
+                @result = $self->_register(%args);
+
+                if ($result[0] ne "ok")
+                {
+                    $self->{DEBUG}->Log1("Execute: Register failed.  Exiting.");
+                    &{$self->{CB}->{onregisterfail}}()
+                        if exists($self->{CB}->{onregisterfail});
+            
+                    $self->Disconnect();
+                    &{$self->{CB}->{ondisconnect}}()
+                        if exists($self->{CB}->{ondisconnect});
+                    $connectAttempt = 0;
+                }
+                else
+                {
+                    &{$self->{CB}->{onauth}}()
+                        if exists($self->{CB}->{onauth});
+                }
+            }
+        }
+        else
+        {
+            &{$self->{CB}->{onauth}}()
+                if exists($self->{CB}->{onauth});
+        }
+ 
+        while($self->Connected())
+        {
+
+            while(defined($status = $self->Process($args{processtimeout})))
+            {
+                &{$self->{CB}->{onprocess}}()
+                    if exists($self->{CB}->{onprocess});
+            }
+
+            if (!defined($status))
+            {
+                $self->Disconnect();
+                $self->{DEBUG}->Log1("Execute: Connection to server lost...");
+                &{$self->{CB}->{ondisconnect}}()
+                    if exists($self->{CB}->{ondisconnect});
+
+                $connectAttempt = $args{connectattempts};
+                next;
+            }
+        }
+
+        last if $self->{DISCONNECTED};
+    }
+
+    $self->{DEBUG}->Log1("Execute: end");
+    &{$self->{CB}->{onexit}}() if exists($self->{CB}->{onexit});
+}
+
+
+###############################################################################
+#
+#  Process - If a timeout value is specified then the function will wait
+#            that long before returning.  This is useful for apps that
+#            need to handle other processing while still waiting for
+#            packets.  If no timeout is listed then the function waits
+#            until a packet is returned.  Either way the function exits
+#            as soon as a packet is returned.
+#
+###############################################################################
+sub Process
+{
+    my $self = shift;
+    my ($timeout) = @_;
+    my %status;
+
+    if (exists($self->{PROCESSERROR}) && ($self->{PROCESSERROR} == 1))
+    {
+        croak("There was an error in the last call to Process that you did not check for and\nhandle.  You should always check the output of the Process call.  If it was\nundef then there was a fatal error that you need to check.  There is an error\nin your program");
+    }
+
+    $self->{DEBUG}->Log5("Process: timeout($timeout)") if defined($timeout);
+
+    if (!defined($timeout) || ($timeout eq ""))
+    {
+        while(1)
+        {
+            %status = $self->{STREAM}->Process();
+            $self->{DEBUG}->Log1("Process: status($status{$self->{SESSION}->{id}})");
+            last if ($status{$self->{SESSION}->{id}} != 0);
+            select(undef,undef,undef,.25);
+        }
+        $self->{DEBUG}->Log1("Process: return($status{$self->{SESSION}->{id}})");
+        if ($status{$self->{SESSION}->{id}} == -1)
+        {
+            $self->{PROCESSERROR} = 1;
+            return;
+        }
+        else
+        {
+            return $status{$self->{SESSION}->{id}};
+        }
+    }
+    else
+    {
+        %status = $self->{STREAM}->Process($timeout);
+        if ($status{$self->{SESSION}->{id}} == -1)
+        {
+            $self->{PROCESSERROR} = 1;
+            return;
+        }
+        else
+        {
+            return $status{$self->{SESSION}->{id}};
+        }
+    }
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Overloadable Methods
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+##############################################################################
+#
+# _auth - Overload this method to provide the authentication method for your
+#         type of connection.
+#
+##############################################################################
+sub _auth
+{
+    my $self = shift;
+    croak("You must override the _auth method.");
+}
+
+
+##############################################################################
+#
+# _connect_args - The Connect function that the Execute loop uses needs
+#                 certain args.  This method lets you map the Execute args
+#                 into the Connect args for your Connection type.
+#
+##############################################################################
+sub _connect_args
+{
+    my $self = shift;
+    my (%args) = @_;
+
+    return %args;
+}
+
+
+##############################################################################
+#
+# _register - overload this method if you need your connection to register
+#             with the server.
+#
+##############################################################################
+sub _register
+{
+    my $self = shift;
+    return ( "ok" ,"" );
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Private Helpers
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+sub _arg
+{
+    my $self = shift;
+    my $arg = shift;
+    my $default = shift;
+
+    return exists($self->{ARGS}->{$arg}) ? $self->{ARGS}->{$arg} : $default;
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Debug.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Debug.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Debug.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,345 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Debug;
+
+=head1 NAME
+
+Net::XMPP::Debug - XMPP Debug Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Debug is a module that provides a developer easy access
+  to logging debug information.
+
+=head1 DESCRIPTION
+
+  Debug is a helper module for the Net::XMPP modules.  It provides
+  the Net::XMPP modules with an object to control where, how, and
+  what is logged.
+
+=head2 Basic Functions
+
+    $Debug = Net::XMPP::Debug->new();
+
+    $Debug->Init(level=>2,
+	             file=>"stdout",
+  	             header=>"MyScript");
+
+    $Debug->Log0("Connection established");
+
+=head1 METHODS
+
+=head2 Basic Functions
+
+    new(hash) - creates the Debug object.  The hash argument is passed
+                to the Init function.  See that function description
+                below for the valid settings.
+
+    Init(level=>integer,  - initializes the debug object.  The level
+         file=>string,      determines the maximum level of debug
+         header=>string,    messages to log:
+         setdefault=>0|1,     0 - Base level Output (default)
+         usedefault=>0|1,     1 - High level API calls
+         time=>0|1)           2 - Low level API calls
+                              ...
+                              N - Whatever you want....
+                            The file determines where the debug log
+                            goes.  You can either specify a path to
+                            a file, or "stdout" (the default).  "stdout"
+                            tells Debug to send all of the debug info
+                            sent to this object to go to stdout.
+                            header is a string that will preappended
+                            to the beginning of all log entries.  This
+                            makes it easier to see what generated the
+                            log entry (default is "Debug").
+                            setdefault saves the current filehandle
+                            and makes it available for other Debug
+                            objects to use.  To use the default set
+                            usedefault to 1.  The time parameter
+                            specifies whether or not to add a timestamp
+                            to the beginning of each logged line.
+
+    LogN(array) - Logs the elements of the array at the corresponding
+                  debug level N.  If you pass in a reference to an
+                  array or hash then they are printed in a readable
+                  way.  (ie... Log0, Log2, Log100, etc...)
+
+=head1 EXAMPLE
+
+  $Debug = Net::XMPP:Debug->new(level=>2,
+                               header=>"Example");
+
+    $Debug->Log0("test");
+
+    $Debug->Log2("level 2 test");
+
+    $hash{a} = "atest";
+    $hash{b} = "btest";
+
+    $Debug->Log1("hashtest",\%hash);
+
+  You would get the following log:
+
+    Example: test
+    Example: level 2 test
+    Example: hashtest { a=>"atest" b=>"btest" }
+
+  If you had set the level to 1 instead of 2 you would get:
+
+    Example: test
+    Example: hashtest { a=>"atest" b=>"btest" }
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+require 5.003;
+use strict;
+use FileHandle;
+use Carp;
+use vars qw( %HANDLES $DEFAULT $DEFAULTLEVEL $DEFAULTTIME $AUTOLOAD );
+
+$DEFAULTLEVEL = -1;
+
+sub new
+{
+    my $proto = shift;
+    my $self = { };
+    bless($self, $proto);
+
+    $self->Init(@_);
+
+    return $self;
+}
+
+
+##############################################################################
+#
+# Init - opens the fielhandle and initializes the Debug object.
+#
+##############################################################################
+sub Init
+{
+    my $self = shift;
+
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+    
+    delete($args{file}) if (lc($args{file}) eq "stdout");
+
+    $args{time} = 0 if !exists($args{time});
+    $args{setdefault} = 0 if !exists($args{setdefault});
+    $args{usedefault} = 0 if !exists($args{usedefault});
+
+    $self->{TIME} = $args{time};
+
+    if ($args{usedefault} == 1)
+    {
+        $args{setdefault} = 0;
+        $self->{USEDEFAULT} = 1;
+    }
+    else
+    {
+        $self->{LEVEL} = 0;
+        $self->{LEVEL} = $args{level} if exists($args{level});
+
+        $self->{HANDLE} = new FileHandle(">&STDERR");
+        $self->{HANDLE}->autoflush(1);
+        if (exists($args{file}))
+        {
+            if (exists($Net::XMPP::Debug::HANDLES{$args{file}}))
+            {
+                $self->{HANDLE} = $Net::XMPP::Debug::HANDLES{$args{file}};
+                $self->{HANDLE}->autoflush(1);
+            }
+            else
+            {
+                if (-e $args{file})
+                {
+                    if (-w $args{file})
+                    {
+                        $self->{HANDLE} = new FileHandle(">$args{file}");
+                        if (defined($self->{HANDLE}))
+                        {
+                            $self->{HANDLE}->autoflush(1);
+                            $Net::XMPP::Debug::HANDLES{$args{file}} = $self->{HANDLE};
+                        }
+                        else
+                        {
+                            print STDERR "ERROR: Debug filehandle could not be opened.\n";
+                            print STDERR"        Debugging disabled.\n";
+                            print STDERR "       ($!)\n";
+                            $self->{LEVEL} = -1;
+                        }
+                    }
+                    else
+                    {
+                        print STDERR "ERROR: You do not have permission to write to $args{file}.\n";
+                        print STDERR"        Debugging disabled.\n";
+                        $self->{LEVEL} = -1;
+                    }
+                }
+                else
+                {
+                    $self->{HANDLE} = new FileHandle(">$args{file}");
+                    if (defined($self->{HANDLE}))
+                    {
+                        $self->{HANDLE}->autoflush(1);
+                        $Net::XMPP::Debug::HANDLES{$args{file}} = $self->{HANDLE};
+                    }
+                    else
+                    {
+                        print STDERR "ERROR: Debug filehandle could not be opened.\n";
+                        print STDERR"        Debugging disabled.\n";
+                        print STDERR "       ($!)\n";
+                        $self->{LEVEL} = -1;
+                    }
+                }
+            }
+        }
+    }
+    if ($args{setdefault} == 1)
+    {
+        $Net::XMPP::Debug::DEFAULT = $self->{HANDLE};
+        $Net::XMPP::Debug::DEFAULTLEVEL = $self->{LEVEL};
+        $Net::XMPP::Debug::DEFAULTTIME = $self->{TIME};
+    }
+
+    $self->{HEADER} = "Debug";
+    $self->{HEADER} = $args{header} if exists($args{header});
+}
+
+
+##############################################################################
+#
+# Log - takes the limit and the array to log and logs them
+#
+##############################################################################
+sub Log
+{
+    my $self = shift;
+    my (@args) = @_;
+
+    my $fh = $self->{HANDLE};
+    $fh = $Net::XMPP::Debug::DEFAULT if exists($self->{USEDEFAULT});
+
+    my $string = "";
+
+    my $testTime = $self->{TIME};
+    $testTime = $Net::XMPP::Debug::DEFAULTTIME if exists($self->{USEDEFAULT});
+
+    $string .= "[".&Net::XMPP::GetTimeStamp("local",time,"short")."] "
+        if ($testTime == 1);
+    $string .= $self->{HEADER}.": ";
+
+    my $arg;
+
+    foreach $arg (@args)
+    {
+        if (ref($arg) eq "HASH")
+        {
+            $string .= " {";
+            my $key;
+            foreach $key (sort {$a cmp $b} keys(%{$arg}))
+            {
+                $string .= " ".$key."=>'".$arg->{$key}."'";
+            }
+            $string .= " }";
+        }
+        else
+        {
+            if (ref($arg) eq "ARRAY")
+            {
+                $string .= " [ ".join(" ",@{$arg})." ]";
+            }  else {
+                $string .= $arg;
+            }
+        }
+    }
+    print $fh "$string\n";
+    return 1;
+}
+
+
+##############################################################################
+#
+# AUTOLOAD - if a function is called that is not defined then this function
+#            will examine the function name and either give an error or call
+#            the appropriate function.
+#
+##############################################################################
+sub AUTOLOAD
+{
+    my $self = shift;
+    return if ($AUTOLOAD =~ /::DESTROY$/);
+    my ($function) = ($AUTOLOAD =~ /\:\:(.*)$/);
+    croak("$function not defined") if !($function =~ /Log\d+/);
+    my ($level) = ($function =~ /Log(\d+)/);
+    return 0 if ($level > (exists($self->{USEDEFAULT}) ? $Net::XMPP::Debug::DEFAULTLEVEL : $self->{LEVEL}));
+    $self->Log(@_);
+}
+
+
+##############################################################################
+#
+# GetHandle - returns the filehandle being used by this object.
+#
+##############################################################################
+sub GetHandle
+{
+    my $self = shift;
+    return $self->{HANDLE};
+}
+
+
+##############################################################################
+#
+# GetLevel - returns the debug level used by this object.
+#
+##############################################################################
+sub GetLevel
+{
+    my $self = shift;
+    return $self->{LEVEL};
+}
+
+
+##############################################################################
+#
+# GetTime - returns the debug time used by this object.
+#
+##############################################################################
+sub GetTime
+{
+    my $self = shift;
+    return $self->{TIME};
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/IQ.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/IQ.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/IQ.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,367 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::IQ;
+
+=head1 NAME
+
+Net::XMPP::IQ - XMPP Info/Query Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::IQ is a companion to the Net::XMPP module. It
+  provides the user a simple interface to set and retrieve all
+  parts of an XMPP IQ.
+
+=head1 DESCRIPTION
+
+  Net::XMPP::IQ differs from the other Net::XMPP::* modules in that
+  the XMLNS of the query is split out into a submodule under
+  IQ.  For specifics on each module please view the documentation
+  for the Net::XMPP::Namespaces module.
+
+  A Net::XMPP::IQ object is passed to the callback function for the
+  message.  Also, the first argument to the callback functions is the
+  session ID from XML::Stream.  There are some cases where you might
+  want this information, like if you created a Client that connects to
+  two servers at once, or for writing a mini server.
+
+    use Net::XMPP;
+
+    sub iq {
+      my ($sid,$IQ) = @_;
+      .
+      .
+      my $reply = $IQ->Reply();
+      my $replyQuery->GetQuery();
+      .
+    }
+
+  You now have access to all of the retrieval functions available.
+
+  To create a new iq to send to the server:
+
+    use Net::XMPP;
+
+    $IQ = Net::XMPP::IQ->new();
+    $IQType = $IQ->NewChild( type );
+    $IQType->SetXXXXX("yyyyy");
+
+  Now you can call the creation functions for the IQ, and for the
+  <query/> on the new query object itself.  See below for the <iq/>
+  functions, and in each query module for those functions.
+
+=head1 METHODS
+
+=head2 General functions
+
+  Reply(%args) - Creates a return <iq/> with the to and from
+                 filled in correctly, and a query object already
+                 added in.  The %args that you pass are passed
+                 to SetIQ() and will overwrite the IQ settings
+                 that Reply sets.
+
+=head2 Retrieval functions
+
+  GetTo()      - returns either a string with the JID, or a
+  GetTo("jid")   Net::XMPP::JID object for the person who is
+                 going to receive the <iq/>.  To get the JID
+                 object set the string to "jid", otherwise leave
+                 blank for the text string.
+
+                 $to    = $IQ->GetTo();
+                 $toJID = $IQ->GetTo("jid");
+
+  GetFrom()      -  returns either a string with the JID, or a
+  GetFrom("jid")    Net::XMPP::JID object for the person who
+                    sent the <iq/>.  To get the JID object set
+                    the string to "jid", otherwise leave blank for the
+                    text string.
+
+                    $from    = $IQ->GetFrom();
+                    $fromJID = $IQ->GetFrom("jid");
+
+  GetType() - returns a string with the type <iq/> this is.
+
+              $type = $IQ->GetType();
+
+  GetID() - returns an integer with the id of the <iq/>.
+
+            $id = $IQ->GetID();
+
+  GetError() - returns a string with the text description of the error.
+
+               $error = $IQ->GetError();
+
+  GetErrorCode() - returns a string with the code of error.
+
+                   $errorCode = $IQ->GetErrorCode();
+
+  GetQuery() - returns a Net::XMPP::Stanza object that contains the data
+               in the query of the <iq/>.  Basically, it returns the
+               first child in the <iq/>.
+
+               $query = $IQ->GetQuery();
+
+  GetQueryXMLNS() - returns a string with the namespace of the query
+                    for this <iq/>, if one exists.
+
+                    $xmlns = $IQ->GetQueryXMLNS();
+
+=head2 Creation functions
+
+  SetIQ(to=>string|JID,    - set multiple fields in the <iq/> at one
+        from=>string|JID,    time.  This is a cumulative and over
+        id=>string,          writing action.  If you set the "to"
+        type=>string,        attribute twice, the second setting is
+        errorcode=>string,   what is used.  If you set the status, and
+        error=>string)       then set the priority then both will be in
+                             the <iq/> tag.  For valid settings read the
+                             specific Set functions below.
+
+                             $IQ->SetIQ(type=>"get",
+                                        to=>"bob\@jabber.org");
+
+                             $IQ->SetIQ(to=>"bob\@jabber.org",
+                                        errorcode=>403,
+                                        error=>"Permission Denied");
+
+  SetTo(string) - sets the to attribute.  You can either pass a string
+  SetTo(JID)      or a JID object.  They must be a valid Jabber
+                  Identifiers or the server will return an error message.
+                  (ie.  bob@jabber.org, etc...)
+
+                  $IQ->SetTo("bob\@jabber.org");
+
+  SetFrom(string) - sets the from attribute.  You can either pass a
+  SetFrom(JID)      string or a JID object.  They must be a valid JIDs
+                    or the server will return an error message.
+                    (ie.  bob@jabber.org, etc...)
+
+                    $IQ->SetFrom("me\@jabber.org");
+
+  SetType(string) - sets the type attribute.  Valid settings are:
+
+                    get      request information
+                    set      set information
+                    result   results of a get
+                    error    there was an error
+
+                    $IQ->SetType("set");
+
+  SetErrorCode(string) - sets the error code of the <iq/>.
+
+                         $IQ->SetErrorCode(403);
+
+  SetError(string) - sets the error string of the <iq/>.
+
+                     $IQ->SetError("Permission Denied");
+
+  NewChild(string) - creates a new Net::XMPP::Stanza object with the
+                     namespace in the string.  In order for this
+                     function to work with a custom namespace, you
+                     must define and register that namespace with the
+                     IQ module.  For more information please read the
+                     documentation for Net::XMPP::Stanza.
+
+                     $queryObj = $IQ->NewChild("jabber:iq:auth");
+                     $queryObj = $IQ->NewChild("jabber:iq:roster");
+
+  Reply(hash) - creates a new IQ object and populates the to/from
+                fields.  If you specify a hash the same as with SetIQ
+                then those values will override the Reply values.
+
+                $iqReply = $IQ->Reply();
+                $iqReply = $IQ->Reply(type=>"result");
+
+=head2 Removal functions
+
+  RemoveTo() - removes the to attribute from the <iq/>.
+
+               $IQ->RemoveTo();
+  
+  RemoveFrom() - removes the from attribute from the <iq/>.
+
+                 $IQ->RemoveFrom();
+  
+  RemoveID() - removes the id attribute from the <iq/>.
+
+               $IQ->RemoveID();
+  
+  RemoveType() - removes the type attribute from the <iq/>.
+
+                 $IQ->RemoveType();
+  
+  RemoveError() - removes the <error/> element from the <iq/>.
+
+                  $IQ->RemoveError();
+  
+  RemoveErrorCode() - removes the code attribute from the <error/>
+                      element in the <iq/>.
+
+                      $IQ->RemoveErrorCode();
+
+=head2 Test functions
+
+  DefinedTo() - returns 1 if the to attribute is defined in the <iq/>, 
+                0 otherwise.
+
+                $test = $IQ->DefinedTo();
+
+  DefinedFrom() - returns 1 if the from attribute is defined in the
+                  <iq/>, 0 otherwise.
+
+                  $test = $IQ->DefinedFrom();
+
+  DefinedID() - returns 1 if the id attribute is defined in the <iq/>, 
+                0 otherwise.
+
+                $test = $IQ->DefinedID();
+
+  DefinedType() - returns 1 if the type attribute is defined in the
+                  <iq/>, 0 otherwise.
+
+                  $test = $IQ->DefinedType();
+
+  DefinedError() - returns 1 if <error/> is defined in the <iq/>, 
+                   0 otherwise.
+
+                   $test = $IQ->DefinedError();
+
+  DefinedErrorCode() - returns 1 if the code attribute is defined in
+                       <error/>, 0 otherwise.
+
+                       $test = $IQ->DefinedErrorCode();
+
+  DefinedQuery() - returns 1 if there is at least one namespaced
+                   child in the object.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+require 5.003;
+use strict;
+use Carp;
+use vars qw( %FUNCTIONS );
+use Net::XMPP::Stanza;
+use base qw( Net::XMPP::Stanza );
+
+sub new
+{
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self = { };
+
+    bless($self, $proto);
+
+    $self->{DEBUGHEADER} = "IQ";
+    $self->{TAG} = "iq";
+
+    $self->{FUNCS} = \%FUNCTIONS;
+    
+    $self->_init(@_);
+
+    return $self;
+}
+
+sub _iq { my $self = shift; return Net::XMPP::IQ->new(); }
+
+$FUNCTIONS{Error}->{path} = 'error/text()';
+
+$FUNCTIONS{ErrorCode}->{path} = 'error/@code';
+
+$FUNCTIONS{From}->{type} = 'jid';
+$FUNCTIONS{From}->{path} = '@from';
+
+$FUNCTIONS{ID}->{path} = '@id';
+
+$FUNCTIONS{To}->{type} = 'jid';
+$FUNCTIONS{To}->{path} = '@to';
+
+$FUNCTIONS{Type}->{path} = '@type';
+
+$FUNCTIONS{XMLNS}->{path} = '@xmlns';
+
+$FUNCTIONS{IQ}->{type}  = 'master';
+
+$FUNCTIONS{Child}->{type}  = 'child';
+$FUNCTIONS{Child}->{path}  = '*[@xmlns]';
+$FUNCTIONS{Child}->{child} = { };
+
+$FUNCTIONS{Query}->{type}  = 'child';
+$FUNCTIONS{Query}->{path}  = '*[@xmlns][0]';
+$FUNCTIONS{Query}->{child} = { child_index=>0 };
+
+##############################################################################
+#
+# GetQueryXMLNS - returns the xmlns of the first child
+#
+##############################################################################
+sub GetQueryXMLNS
+{
+    my $self = shift;
+    return $self->{CHILDREN}->[0]->GetXMLNS() if ($#{$self->{CHILDREN}} > -1);
+}
+
+
+##############################################################################
+#
+# Reply - returns a Net::XMPP::IQ object with the proper fields
+#         already populated for you.
+#
+##############################################################################
+sub Reply
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $reply = $self->_iq();
+
+    $reply->SetID($self->GetID()) if ($self->GetID() ne "");
+    $reply->SetType("result");
+
+    $reply->NewChild($self->GetQueryXMLNS());
+
+    $reply->SetIQ((($self->GetFrom() ne "") ?
+                   (to=>$self->GetFrom()) :
+                   ()
+                  ),
+                  (($self->GetTo() ne "") ?
+                   (from=>$self->GetTo()) :
+                   ()
+                  ),
+                 );
+    $reply->SetIQ(%args);
+
+    return $reply;
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/JID.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/JID.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/JID.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,358 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::JID;
+
+=head1 NAME
+
+Net::XMPP::JID - XMPP JID Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::JID is a companion to the Net::XMPP module.
+  It provides the user a simple interface to set and retrieve all 
+  parts of a Jabber ID (userid on a server).
+
+=head1 DESCRIPTION
+
+  To initialize the JID you must pass it the string that represents the
+  jid from the XML packet.  Inside the XMPP modules this is done
+  automatically and the JID object is returned instead of a string.
+  For example, in the callback function for the XMPP object foo:
+
+    use Net::XMPP;
+
+    sub foo {
+      my $foo = Net::XMPP::Foo->new(@_);
+      my $from = $foo->GetFrom();
+      my $JID = Net::XMPP::JID->new($from);
+      .
+      .
+      .
+    }
+
+  You now have access to all of the retrieval functions available.
+
+  To create a new JID to send to the server:
+
+    use Net::XMPP;
+
+    $JID = Net::XMPP::JID->new();
+
+  Now you can call the creation functions below to populate the tag
+  before sending it.
+
+=head2 Retrieval functions
+
+    $userid   = $JID->GetUserID();
+    $server   = $JID->GetServer();
+    $resource = $JID->GetResource();
+
+    $JID      = $JID->GetJID();
+    $fullJID  = $JID->GetJID("full");
+    $baseJID  = $JID->GetJID("base");
+
+=head2 Creation functions
+
+    $JID->SetJID(userid=>"bob",
+                 server=>"jabber.org",
+                 resource=>"Work");
+
+    $JID->SetJID('blue@moon.org/Home');
+
+    $JID->SetUserID("foo");
+    $JID->SetServer("bar.net");
+    $JID->SetResource("Foo Bar");
+
+=head1 METHODS
+
+=head2 Retrieval functions
+
+  GetUserID() - returns a string with the userid of the JID.
+                If the string is an address (bob%jabber.org) then
+                the function will return it as an address 
+                (bob@jabber.org).
+
+  GetServer() - returns a string with the server of the JID.
+
+  GetResource() - returns a string with the resource of the JID. 
+
+  GetJID()       - returns a string that represents the JID stored
+  GetJID("full")   within.  If the "full" string is specified, then
+  GetJID("base")   you get the full JID, including Resource, which
+                   should be used to send to the server.  If the "base",
+                   string is specified, then you will just get
+                   user@server, or the base JID.
+
+=head2 Creation functions
+
+  SetJID(userid=>string,   - set multiple fields in the jid at
+         server=>string,     one time.  This is a cumulative
+         resource=>string)   and over writing action.  If you set
+  SetJID(string)             the "userid" attribute twice, the second
+                             setting is what is used.  If you set
+                             the server, and then set the resource
+                             then both will be in the jid.  If all
+                             you pass is a string, then that string
+                             is used as the JID.  For valid settings 
+                             read the specific Set functions below.
+
+  SetUserID(string) - sets the userid.  Must be a valid userid or the
+                      server will complain if you try to use this JID
+                      to talk to the server.  If the string is an 
+                      address then it will be converted to the %
+                      form suitable for using as a User ID.
+
+  SetServer(string) - sets the server.  Must be a valid host on the 
+                      network or the server will not be able to talk
+                      to it.
+
+  SetResource(string) - sets the resource of the userid to talk to.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+require 5.003;
+use strict;
+use Carp;
+
+sub new
+{
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self = { };
+
+    bless($self, $proto);
+
+    if ("@_" ne (""))
+    {
+        my ($jid) = @_;
+        return $jid if ((ref($jid) ne "") && ($jid->isa("Net::XMPP::JID")));
+        $self->{JID} = $jid;
+    }
+    else
+    {
+        $self->{JID} = "";
+    }
+    $self->ParseJID();
+
+    return $self;
+}
+
+
+##############################################################################
+#
+# ParseJID - private helper function that takes the JID and sets the
+#            the three parts of it.
+#
+##############################################################################
+sub ParseJID
+{
+    my $self = shift;
+
+    my $userid;
+    my $server;
+    my $resource;
+
+    ($userid,$server,$resource) =
+        ($self->{JID} =~ /^([^\@\/'"&:<>]*)\@([A-Za-z0-9\.\-\_]+)\/?(.*?)$/);
+    if (!defined($server))
+    {
+        ($server,$resource) =
+            ($self->{JID} =~ /^([A-Za-z0-9\.\-\_]+)\/?(.*?)$/);
+    }
+
+    $userid = "" unless defined($userid);
+    $server = "" unless defined($server);
+    $resource = "" unless defined($resource);
+
+    $self->{USERID} = $userid;
+    $self->{SERVER} = $server;
+    $self->{RESOURCE} = $resource;
+}
+
+
+##############################################################################
+#
+# BuildJID - private helper function that takes the three parts and sets the
+#            JID from them.
+#
+##############################################################################
+sub BuildJID
+{
+    my $self = shift;
+    $self->{JID} = $self->{USERID};
+    $self->{JID} .= "\@" if ($self->{USERID} ne "");
+    $self->{JID} .= $self->{SERVER} if (exists($self->{SERVER}) &&
+                        defined($self->{SERVER}));
+    $self->{JID} .= "/".$self->{RESOURCE} if (exists($self->{RESOURCE}) &&
+                        defined($self->{RESOURCE}) &&
+                        ($self->{RESOURCE} ne ""));
+}
+
+
+##############################################################################
+#
+# GetUserID - returns the userid of the JID.
+#
+##############################################################################
+sub GetUserID
+{
+    my $self = shift;
+    my $userid = $self->{USERID};
+    $userid =~ s/\%/\@/;
+    return $userid;
+}
+
+
+##############################################################################
+#
+# GetServer - returns the server of the JID.
+#
+##############################################################################
+sub GetServer
+{
+    my $self = shift;
+    return $self->{SERVER};
+}
+
+
+##############################################################################
+#
+# GetResource - returns the resource of the JID.
+#
+##############################################################################
+sub GetResource
+{
+    my $self = shift;
+    return $self->{RESOURCE};
+}
+
+
+##############################################################################
+#
+# GetJID - returns the full jid of the JID.
+#
+##############################################################################
+sub GetJID
+{
+    my $self = shift;
+    my $type = shift;
+    $type = "" unless defined($type);
+    return $self->{JID} if ($type eq "full");
+    return $self->{USERID}."\@".$self->{SERVER} if ($self->{USERID} ne "");
+    return $self->{SERVER};
+}
+
+
+##############################################################################
+#
+# SetJID - takes a hash of all of the things you can set on a JID and sets
+#          each one.
+#
+##############################################################################
+sub SetJID
+{
+    my $self = shift;
+    my %jid;
+
+    if ($#_ > 0 ) { 
+        while($#_ >= 0) { $jid{ lc pop(@_) } = pop(@_); }
+
+        $self->SetUserID($jid{userid}) if exists($jid{userid});
+        $self->SetServer($jid{server}) if exists($jid{server});
+        $self->SetResource($jid{resource}) if exists($jid{resource});
+    } else {
+        ($self->{JID}) = @_;
+        $self->ParseJID();
+    }
+}
+
+
+##############################################################################
+#
+# SetUserID - sets the userid of the JID.
+#
+##############################################################################
+sub SetUserID
+{
+    my $self = shift;
+    my ($userid) = @_;
+    $userid =~ s/\@/\%/;
+    $self->{USERID} = $userid;
+    $self->BuildJID();
+}
+
+
+##############################################################################
+#
+# SetServer - sets the server of the JID.
+#
+##############################################################################
+sub SetServer
+{
+    my $self = shift;
+    my ($server) = @_;
+    $self->{SERVER} = $server;
+    $self->BuildJID();
+}
+
+
+##############################################################################
+#
+# SetResource - sets the resource of the JID.
+#
+##############################################################################
+sub SetResource
+{
+    my $self = shift;
+    my ($resource) = @_;
+    $self->{RESOURCE} = $resource;
+    $self->BuildJID();
+}
+
+
+##############################################################################
+#
+# debug - prints out the contents of the JID
+#
+##############################################################################
+sub debug
+{
+    my $self = shift;
+
+    print "debug JID: $self\n";
+    print "UserID:   (",$self->{USERID},")\n";
+    print "Server:   (",$self->{SERVER},")\n";
+    print "Resource: (",$self->{RESOURCE},")\n";
+    print "JID:      (",$self->{JID},")\n";
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Message.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Message.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Message.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,407 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Message;
+
+=head1 NAME
+
+Net::XMPP::Message - XMPP Message Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Message is a companion to the Net::XMPP module.
+  It provides the user a simple interface to set and retrieve all
+  parts of an XMPP Message.
+
+=head1 DESCRIPTION
+
+  A Net::XMPP::Message object is passed to the callback function for
+  the message.  Also, the first argument to the callback functions is
+  the session ID from XML::Stream.  There are some cases where you
+  might want thisinformation, like if you created a Client that
+  connects to two servers at once, or for writing a mini server.
+
+    use Net::XMPP;
+
+    sub message {
+      my ($sid,$Mess) = @_;
+      .
+      .
+      .
+    }
+
+  You now have access to all of the retrieval functions available.
+
+  To create a new message to send to the server:
+
+    use Net::XMPP;
+
+    $Mess = Net::XMPP::Message->new();
+
+  Now you can call the creation functions below to populate the tag
+  before sending it.
+
+=head1 METHODS
+
+=head2 Retrieval functions
+
+  GetTo()      - returns the value in the to='' attribute for the
+  GetTo("jid")   <message/>.  If you specify "jid" as an argument
+                 then a Net::XMPP::JID object is returned and
+                 you can easily parse the parts of the JID.
+
+                 $to    = $Mess->GetTo();
+                 $toJID = $Mess->GetTo("jid");
+
+  GetFrom()      - returns the value in the from='' attribute for the
+  GetFrom("jid")   <message/>.  If you specify "jid" as an argument
+                   then a Net::XMPP::JID object is returned and
+                   you can easily parse the parts of the JID.
+
+                   $from    = $Mess->GetFrom();
+                   $fromJID = $Mess->GetFrom("jid");
+
+  GetType() - returns the type='' attribute of the <message/>.  Each
+              message is one of four types:
+
+                normal        regular message (default if type is blank)
+                chat          one on one chat
+                groupchat     multi-person chat
+                headline      headline
+                error         error message
+
+              $type = $Mess->GetType();
+
+  GetSubject() - returns the data in the <subject/> tag.
+
+                 $subject = $Mess->GetSubject();
+
+  GetBody() - returns the data in the <body/> tag.
+
+              $body = $Mess->GetBody();
+
+  GetThread() - returns the data in the <thread/> tag.
+
+                $thread = $Mess->GetThread();
+
+  GetError() - returns a string with the data of the <error/> tag.
+
+               $error = $Mess->GetError();
+
+  GetErrorCode() - returns a string with the code='' attribute of the
+                   <error/> tag.
+
+                   $errCode = $Mess->GetErrorCode();
+
+  GetTimeStamp() - returns a string that represents the time this
+                   message object was created (and probably received)
+                   for sending to the client.  If there is a
+                   jabber:x:delay tag then that time is used to show
+                   when the message was sent.
+
+                   $date = $Mess->GetTimeStamp();
+
+
+=head2 Creation functions
+
+  SetMessage(to=>string|JID,    - set multiple fields in the <message/>
+             from=>string|JID,    at one time.  This is a cumulative
+             type=>string,        and over writing action.  If you set
+             subject=>string,     the "to" attribute twice, the second
+             body=>string,        setting is what is used.  If you set
+             thread=>string,      the subject, and then set the body
+             errorcode=>string,   then both will be in the <message/>
+             error=>string)       tag.  For valid settings read the
+                                  specific Set functions below.
+
+                            $Mess->SetMessage(TO=>"bob\@jabber.org",
+                                              Subject=>"Lunch",
+                                              BoDy=>"Let's do lunch!");
+                            $Mess->SetMessage(to=>"bob\@jabber.org",
+                                              from=>"jabber.org",
+                                              errorcode=>404,
+                                              error=>"Not found");
+
+  SetTo(string) - sets the to='' attribute.  You can either pass
+  SetTo(JID)      a string or a JID object.  They must be valid JIDs
+                  or the server will return an error message.
+                  (ie.  bob@jabber.org/Work)
+
+                  $Mess->SetTo("test\@jabber.org");
+
+  SetFrom(string) - sets the from='' attribute.  You can either pass
+  SetFrom(JID)      a string or a JID object.  They must be valid JIDs
+                    or the server will return an error message. (ie.
+                    jabber:bob@jabber.org/Work) This field is not
+                    required if you are writing a Client since the
+                    server will put the JID of your connection in
+                    there to prevent spamming.
+
+                    $Mess->SetFrom("me\@jabber.org");
+
+  SetType(string) - sets the type attribute.  Valid settings are:
+
+                      normal         regular message (default if blank)
+                      chat           one one one chat style message
+                      groupchat      multi-person chatroom message
+                      headline       news headline, stock ticker, etc...
+                      error          error message
+
+                    $Mess->SetType("groupchat");
+
+  SetSubject(string) - sets the subject of the <message/>.
+
+                       $Mess->SetSubject("This is a test");
+
+  SetBody(string) - sets the body of the <message/>.
+
+                    $Mess->SetBody("To be or not to be...");
+
+  SetThread(string) - sets the thread of the <message/>.  You should
+                      copy this out of the message being replied to so
+                      that the thread is maintained.
+
+                      $Mess->SetThread("AE912B3");
+
+  SetErrorCode(string) - sets the error code of the <message/>.
+
+                         $Mess->SetErrorCode(403);
+
+  SetError(string) - sets the error string of the <message/>.
+
+                     $Mess->SetError("Permission Denied");
+
+  Reply(hash) - creates a new Message object and populates the
+                to/from, and the subject by putting "re: " in
+                front.  If you specify a hash the same as with
+                SetMessage then those values will override the
+                Reply values.
+
+                $Reply = $Mess->Reply();
+                $Reply = $Mess->Reply(type=>"chat");
+
+=head2 Removal functions
+
+  RemoveTo() - removes the to attribute from the <message/>.
+
+               $Mess->RemoveTo();
+  
+  RemoveFrom() - removes the from attribute from the <message/>.
+
+                 $Mess->RemoveFrom();
+  
+  RemoveType() - removes the type attribute from the <message/>.
+
+                 $Mess->RemoveType();
+  
+  RemoveSubject() - removes the <subject/> element from the
+                    <message/>.
+
+                    $Mess->RemoveSubject();
+
+  RemoveBody() - removes the <body/> element from the
+                 <message/>.
+                  
+                 $Mess->RemoveBody();
+
+  RemoveThread() - removes the <thread/> element from the <message/>.
+
+                   $Mess->RemoveThread();
+  
+  RemoveError() - removes the <error/> element from the <message/>.
+
+                  $Mess->RemoveError();
+  
+  RemoveErrorCode() - removes the code attribute from the <error/>
+                      element in the <message/>.
+
+                      $Mess->RemoveErrorCode();
+
+=head2 Test functions
+
+  DefinedTo() - returns 1 if the to attribute is defined in the
+                <message/>, 0 otherwise.
+
+                $test = $Mess->DefinedTo();
+
+  DefinedFrom() - returns 1 if the from attribute is defined in the
+                  <message/>, 0 otherwise.
+
+                  $test = $Mess->DefinedFrom();
+
+  DefinedType() - returns 1 if the type attribute is defined in the
+                  <message/>, 0 otherwise.
+
+                  $test = $Mess->DefinedType();
+
+  DefinedSubject() - returns 1 if <subject/> is defined in the
+                     <message/>, 0 otherwise.
+
+                     $test = $Mess->DefinedSubject();
+
+  DefinedBody() - returns 1 if <body/> is defined in the <message/>,
+                  0 otherwise.
+
+                  $test = $Mess->DefinedBody();
+
+  DefinedThread() - returns 1 if <thread/> is defined in the <message/>,
+                    0 otherwise.
+
+                    $test = $Mess->DefinedThread();
+
+  DefinedErrorCode() - returns 1 if <error/> is defined in the
+                       <message/>, 0 otherwise.
+
+                       $test = $Mess->DefinedErrorCode();
+
+  DefinedError() - returns 1 if the code attribute is defined in the
+                   <error/>, 0 otherwise.
+
+                   $test = $Mess->DefinedError();
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+require 5.003;
+use strict;
+use Carp;
+use vars qw( %FUNCTIONS );
+use Net::XMPP::Stanza;
+use base qw( Net::XMPP::Stanza );
+
+sub new
+{
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self = {};
+
+    bless($self, $proto);
+
+    $self->{DEBUGHEADER} = "Message";
+    $self->{TAG} = "message";
+    $self->{TIMESTAMP} = &Net::XMPP::GetTimeStamp("local");
+
+    $self->{FUNCS} = \%FUNCTIONS;
+
+    $self->_init(@_);
+
+    return $self;
+}
+
+sub _message { my $self = shift; return Net::XMPP::Message->new(); }
+
+
+$FUNCTIONS{Body}->{path}  = 'body/text()';
+
+$FUNCTIONS{Error}->{path} = 'error/text()';
+
+$FUNCTIONS{ErrorCode}->{path} = 'error/@code';
+
+$FUNCTIONS{From}->{type} = 'jid';
+$FUNCTIONS{From}->{path} = '@from';
+
+$FUNCTIONS{ID}->{path} = '@id';
+
+$FUNCTIONS{Subject}->{path} = 'subject/text()';
+
+$FUNCTIONS{Thread}->{path} = 'thread/text()';
+
+$FUNCTIONS{To}->{type} = 'jid';
+$FUNCTIONS{To}->{path} = '@to';
+
+$FUNCTIONS{Type}->{path} = '@type';
+
+$FUNCTIONS{XMLNS}->{path} = '@xmlns';
+
+$FUNCTIONS{Message}->{type}  = 'master';
+
+$FUNCTIONS{Child}->{type} = 'child';
+$FUNCTIONS{Child}->{path} = '*[@xmlns]';
+$FUNCTIONS{Child}->{child} = {};
+
+##############################################################################
+#
+# GetTimeStamp - returns a string with the time stamp of when this object
+#                was created.
+#
+##############################################################################
+sub GetTimeStamp
+{
+    my $self = shift;
+
+    if ($self->DefinedX("jabber:x:delay"))
+    {
+        my @xTags = $self->GetX("jabber:x:delay");
+        my $xTag = $xTags[0];
+        $self->{TIMESTAMP} = &Net::XMPP::GetTimeStamp("utcdelaylocal",$xTag->GetStamp());
+    }
+
+    return $self->{TIMESTAMP};
+}
+
+
+##############################################################################
+#
+# Reply - returns a Net::XMPP::Message object with the proper fields
+#         already populated for you.
+#
+##############################################################################
+sub Reply
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $reply = $self->_message();
+
+    if (($self->GetType() eq "") || ($self->GetType() eq "normal"))
+    {
+        my $subject = $self->GetSubject();
+        $subject =~ s/re\:\s+//i;
+        $reply->SetSubject("re: $subject");
+    }
+    $reply->SetThread($self->GetThread()) if ($self->GetThread() ne "");
+    $reply->SetID($self->GetID()) if ($self->GetID() ne "");
+    $reply->SetType($self->GetType()) if ($self->GetType() ne "");
+    $reply->SetMessage((($self->GetFrom() ne "") ?
+                         (to=>$self->GetFrom()) :
+                         ()
+                        ),
+                        (($self->GetTo() ne "") ?
+                         (from=>$self->GetTo()) :
+                         ()
+                        ),
+                       );
+    $reply->SetMessage(%args);
+
+    return $reply;
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Namespaces.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Namespaces.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Namespaces.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,602 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Namespaces;
+
+=head1 NAME
+
+Net::XMPP::Namespaces - In depth discussion on how namespaces are handled
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Namespaces provides an depth look at how Net::XMPP handles
+  namespacs, and how to add your own custom ones.  It also serves as the
+  storage bin for all of the Namespace information Net::XMPP requires.
+
+=head1 DESCRIPTION
+
+  XMPP as a protocol is very well defined.  There are three main top
+  level packets (message, iq, and presence).  There is also a way to
+  extend the protocol in a very clear and strucutred way, via namespaces.
+
+  Two major ways that namespaces are used in Jabber is for making the
+  <iq/> a generic wrapper, and as a way for adding data to any packet via
+  a child tag <x/>.  We will use <x/> to represent the packet, but in
+  reality it could be any child tag: <foo/>, <data/>, <error/>, etc.
+
+  The Info/Query <iq/> packet uses namespaces to determine the type of
+  information to access.  Usually there is a <query/> tag in the <iq/>
+  that represents the namespace, but in fact it can be any tag.  The
+  definition of the Query portion, is the first tag that has a namespace.
+
+    <iq type="get"><query xmlns="..."/></iq>
+
+      or
+
+    <iq type="get"><foo xmlns="..."/></iq>
+
+  After that Query stanza can be any number of other stanzas (<x/> tags)
+  you want to include.  The Query packet is represented and available by
+  calling GetQuery() or GetChild(), and the other namespaces are
+  available by calling GetChild().
+
+  The X tag is just a way to piggy back data on other packets.  Like
+  embedding the timestamp for a message using jabber:x:delay, or signing
+  you presence for encryption using jabber:x:signed.
+
+  To this end, Net::XMPP has sought to find a way to easily, and clearly
+  define the functions needed to access the XML for a namespace.  We will
+  go over the full docs, and then show two examples of real namespaces so
+  that you can see what we are talking about.
+
+=head2 Overview
+
+  To avoid a lot of nasty modules populating memory that are not used,
+  and to avoid having to change 15 modules when a minor change is
+  introduced, the Net::XMPP modules have taken AUTOLOADing to the
+  extreme.  Namespaces.pm is nothing but a set of function calls that
+  generates a big hash of hashes.  The hash is accessed by the Stanza.pm
+  AUTOLOAD function to do something.  (This will make sense, I promise.)
+
+  Before going on, I highly suggest you read a Perl book on AUTOLOAD and
+  how it works.  From this point on I will assume that you understand it.
+
+  When you create a Net::XMPP::IQ object and add a Query to it (NewChild)
+  several things are happening in the background.  The argument to
+  NewChild is the namespace you want to add. (custom-namespace)
+
+  Now that you have a Query object to work with you will call the GetXXX
+  functions, and SetXXX functions to set the data.  There are no defined
+  GetXXX and SetXXXX functions.  You cannot look in the Namespaces.pm
+  file and find them.  Instead you will find something like this:
+
+  &add_ns(ns    => "mynamespace",
+          tag   => "mytag",
+          xpath => {
+                    JID       => { type=>'jid', path => '@jid' },
+                    Username  => { path => 'username/text()' },
+                    Test      => { type => 'master' }
+                   }
+         );
+
+  When the GetUsername() function is called, the AUTOLOAD function looks
+  in the Namespaces.pm hash for a "Username" key.  Based on the "type" of
+  the field (scalar being the default) it will use the "path" as an XPath
+  to retrieve the data and call the XPathGet() method in Stanza.pm.
+
+  Confused yet?
+
+=head2 Net::XMPP private namespaces
+
+  Now this is where this starts to get a little sticky.  When you see a
+  namespace with __netxmpp__, or __netjabber__ from Net::Jabber, at the
+  beginning it is usually something custom to Net::XMPP and NOT part of
+  the actual XMPP protocol.
+
+  There are some places where the structure of the XML allows for
+  multiple children with the same name.  The main places you will see
+  this behavior is where you have multiple tags with the same name and
+  those have children under them (jabber:iq:roster).
+
+  In jabber:iq:roster, the <item/> tag can be repeated multiple times,
+  and is sort of like a mini-namespace in itself.  To that end, we treat
+  it like a seperate namespace and defined a __netxmpp__:iq:roster:item
+  namespace to hold it.  What happens is this, in my code I define that
+  the <item/>s tag is "item" and anything with that tag name is to create
+  a new Net::XMPP::Stanza object with the namespace
+  __netxmpp__:iq:roster:item which then becomes a child of the
+  jabber:iq:roster Stanza object.  Also, when you want to add a new item
+  to a jabber:iq:roster project you call NewQuery with the private
+  namespace.
+
+  I know this sounds complicated.  And if after reading this entire
+  document it is still complicated, email me, ask questions, and I will
+  monitor it and adjust these docs to answer the questions that people
+  ask.
+
+=head2 add_ns()
+
+  To repeat, here is an example call to add_ns():
+  
+    &add_ns(ns    => "mynamespace",
+            tag   => "mytag",
+            xpath => {
+                      JID       => { type=>'jid', path => '@jid' },
+                      Username  => { path => 'username/text()' },
+                      Test      => { type => 'master' }
+                     }
+           );
+
+  ns - This is the new namespace that you are trying to add.
+
+  tag - This is the root tag to use for objects based on this namespace.
+
+  xpath - The hash reference passed in the add_ns call to each name of
+  entry tells Net::XMPP how to handle subsequent GetXXXX(), SetXXXX(),
+  DefinedXXXX(), RemoveXXXX(), AddXXXX() calls.  The basic options you
+  can pass in are:
+  
+     type - This tells Stanza how to handle the call.  The possible
+            values are:
+                       
+           array - The value to set and returned is an an array
+                   reference.  For example, <group/> in jabber:iq:roster.
+
+           child - This tells Stanza that it needs to look for the
+                   __netxmpp__ style namesapced children.  AddXXX() adds
+                   a new child, and GetXXX() will return a new Stanza
+                   object representing the packet.
+
+           flag - This is for child elements that are tags by themselves:
+                  <foo/>.  Since the presence of the tag is what is
+                  important, and there is no cdata to store, we just call
+                  it a flag.
+
+           jid - The value is a Jabber ID.  GetXXX() will return a
+                 Net::XMPP::JID object unless you pass it "jid", then it
+                 returns a string.
+
+           master - The GetXXX() and SetXXX() calls return and take a
+                    hash representing all of the GetXXX() and SetXXX()
+                    calls.  For example:
+
+                      SetTest(foo=>"bar",
+                              bar=>"baz");
+
+                    Translates into:
+
+                      SetFoo("bar");
+                      SetBar("baz");
+
+                    GetTest() would return a hash containing what the
+                    packet contains:
+
+                      { foo=>"bar",  bar=>"baz" }
+
+           raw - This will stick whatever raw XML you specify directly
+                 into the Stanza at the point where the path specifies.
+
+           scalar - This will set and get a scalar value.  This is the
+                    main workhorse as attributes and CDATA is represented
+                    by a scalar.  This is the default setting if you do
+                    not provide one.
+
+           special - The special type is unique in that instead of a
+                     string "special", you actually give it an array:
+
+                       [ "special" , <subtype> ]
+
+                     This allows Net::XMPP to be able to handle the
+                     SetXXXX() call in a special manner according to your
+                     choosing.  Right now this is mainly used by
+                     jabber:iq:time to automatically set the time info in
+                     the correct format, and jabber:iq:version to set the
+                     machine OS and add the Net::Jabber version to the
+                     return packet.  You will likely NOT need to use
+                     this, but I wanted to mention it.
+
+           timestamp - If you call SetXXX() but do not pass it anything,
+                       or pass it "", then Net::XMPP will place a
+                       timestamp in the xpath location.
+
+     path - This is the XPath path to where the bit data lives.  The
+            difference.  Now, this is not full XPath due to the nature
+            of how it gets used.  Instead of providing a rooted path
+            all the way to the top, it's a relative path ignoring what
+            the parent is.  For example, if the "tag" you specified was
+            "foo", and the path is "bar/text()", then the XPath will be
+            rooted in the XML of the <foo/> packet.  It will set and get
+            the CDATA from:
+
+               <foo><bar>xxxxx</bar></foo>
+
+            For a flag and a child type, just specify the child element.
+            Take a look at the code in this file for more help on what
+            this means.  Also, read up on XPath if you don't already know
+            what it is.
+
+     child - This is a hash reference that tells Net::XMPP how to handle
+             adding and getting child objects.  The keys for the hash are
+             as follows:
+
+             ns - the real or custom (__netxmpp__) namesapce to use for
+                  this child packet.
+
+             skip_xmlns => 1 - this tells Net::XMPP not to add an
+                               xmlns='' into the XML for the child
+                               object.
+
+             specify_name => 1 - allows you to call NewChild("ns","tag")
+                                 and specify the tag to use for the child
+                                 object.  This, IMHO, is BAD XML
+                                 practice.  You should always know what
+                                 the tag of the child is and use an
+                                 attribute or CDATA to change the type
+                                 of the stanza.  You do not want to use
+                                 this.
+
+             tag - If you use specify_name, then this is the default tag
+                   to use.  You do not want to use this.
+
+     calls - Array reference telling Net::XMPP what functions to create
+             for this name.  For most of the types above you will get
+             Get, Set, Defined, and Remove.  For child types you need to
+             decide how you API will look and specify them yourself:
+
+               ["Get","Defined"]
+               ["Add"]
+               ["Get","Add","Defined"]
+             
+            It all depends on how you want your API to look.
+     
+  Once more... The following:
+  
+    &add_ns(ns    => "mynamespace",
+            tag   => "mytag",
+            xpath => {
+                      JID       => { type=>'jid', path => '@jid' },
+                      Username  => { path => 'username/text()' },
+                      Test      => { type => 'master' }
+                     }
+           );
+
+  generates the following API calls:
+
+    GetJID()
+    SetJID()
+    DefinedJID()
+    RemoveJID()
+    GetUsername()
+    SetUsername()
+    DefinedUsername()
+    RemoveUsername()
+    GetTest()
+    SetTest()
+
+=head2 Wrap Up
+
+  Well.  I hope that I have not scared you off from writing a custom
+  namespace for you application and use Net::XMPP.  Look in the
+  Net::XMPP::Protocol manpage for an example on using the add_ns()
+  function to register your custom namespace so that Net::XMPP can
+  properly handle it.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use vars qw ( %NS %SKIPNS );
+
+$SKIPNS{'__netxmpp__'} = 1;
+
+#------------------------------------------------------------------------------
+# __netxmpp__:child:test
+#------------------------------------------------------------------------------
+{
+    &add_ns(ns    => "__netxmpptest__:child:test",
+            tag   => "test",
+            xpath => {
+                      Bar  => { path => 'bar/text()' },
+                      Foo  => { path => '@foo' },
+                      Test => { type => 'master' }
+                     }
+           );
+}
+
+#------------------------------------------------------------------------------
+# __netxmpp__:child:test:two
+#------------------------------------------------------------------------------
+{
+    &add_ns(ns    => "__netxmpptest__:child:test:two",
+            tag   => "test",
+            xpath => {
+                      Bob  => { path => 'owner/@bob' },
+                      Joe  => { path => 'joe/text()' },
+                      Test => { type => 'master' }
+                     }
+           );
+}
+
+#-----------------------------------------------------------------------------
+# urn:ietf:params:xml:ns:xmpp-bind
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "urn:ietf:params:xml:ns:xmpp-bind",
+            tag   => "bind",
+            xpath => {
+                      JID      => { type => 'jid',
+                                    path => 'jid/text()',
+                                  },
+                      Resource => { path => 'resource/text()' },
+                      Bind     => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# urn:ietf:params:xml:ns:xmpp-session
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "urn:ietf:params:xml:ns:xmpp-session",
+            tag   => "session",
+            xpath => { Session => { type => 'master' } },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# jabber:iq:auth
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "jabber:iq:auth",
+            tag   => "query",
+            xpath => {
+                      Digest   => { path => 'digest/text()' },
+                      Hash     => { path => 'hash/text()' },
+                      Password => { path => 'password/text()' },
+                      Resource => { path => 'resource/text()' },
+                      Sequence => { path => 'sequence/text()' },
+                      Token    => { path => 'token/text()' },
+                      Username => { path => 'username/text()' },
+                      Auth     => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# jabber:iq:privacy
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "jabber:iq:privacy",
+            tag   => "query",
+            xpath => {
+                      Active  => { path => 'active/@name' },
+                      Default => { path => 'default/@name' },
+                      List    => {
+                                  type  => 'child',
+                                  path  => 'list',
+                                  child => { ns => '__netxmpp__:iq:privacy:list', },
+                                  calls => [ 'Add' ],
+                                 },
+                      Lists   => {
+                                  type  => 'child',
+                                  path  => 'list',
+                                  child => { ns => '__netxmpp__:iq:privacy:list', },
+                                 },
+                      Privacy => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# __netxmpp__:iq:privacy:list
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => '__netxmpp__:iq:privacy:list',
+            xpath => {
+                      Name  => { path => '@name' },
+                      Item  => {
+                                type  => 'child',
+                                path  => 'item',
+                                child => { ns => '__netxmpp__:iq:privacy:list:item', },
+                                calls => [ 'Add' ],
+                               },
+                      Items => {
+                                type  => 'child',
+                                path  => 'item',
+                                child => { ns => '__netxmpp__:iq:privacy:item', },
+                               },
+                      List  => { type => 'master' },
+                    },
+            docs  => {
+                      module => 'Net::XMPP',
+                      name   => 'jabber:iq:privacy - list objects',
+                     },
+           );
+}
+        
+#-----------------------------------------------------------------------------
+# __netxmpp__:iq:privacy:list:item
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => '__netxmpp__:iq:privacy:list:item',
+            xpath => {
+                      Action      => { path => '@action' },
+                      IQ          => {
+                                      type => 'flag',
+                                      path => 'iq',
+                                     },
+                      Message     => {
+                                      type => 'flag',
+                                      path => 'message',
+                                     },
+                      Order       => { path => '@order' },
+                      PresenceIn  => {
+                                      type => 'flag',
+                                      path => 'presence-in',
+                                     },
+                      PresenceOut => {
+                                      type => 'flag',
+                                      path => 'presence-out',
+                                     },
+                      Type        => { path => '@type' },
+                      Value       => { path => '@value' },
+                      Item        => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                      name   => 'jabber:iq:privacy - item objects',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# jabber:iq:register
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "jabber:iq:register",
+            tag   => "query",
+            xpath => {
+                      Address      => { path => 'address/text()' },
+                      City         => { path => 'city/text()' },
+                      Date         => { path => 'date/text()' },
+                      Email        => { path => 'email/text()' },
+                      First        => { path => 'first/text()' },
+                      Instructions => { path => 'instructions/text()' },
+                      Key          => { path => 'key/text()' },
+                      Last         => { path => 'last/text()' },
+                      Misc         => { path => 'misc/text()' },
+                      Name         => { path => 'name/text()' },
+                      Nick         => { path => 'nick/text()' },
+                      Password     => { path => 'password/text()' },
+                      Phone        => { path => 'phone/text()' },
+                      Registered   => {
+                                       type => 'flag',
+                                       path => 'registered',
+                                      },
+                      Remove       => {
+                                       type => 'flag',
+                                       path => 'password/text()',
+                                      },
+                      State        => { path => 'state/text()' },
+                      Text         => { path => 'text/text()' },
+                      URL          => { path => 'url/text()' },
+                      Username     => { path => 'username/text()' },
+                      Zip          => { path => 'zip/text()' },
+                      Register     => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# jabber:iq:roster
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => 'jabber:iq:roster',
+            tag   => "query",
+            xpath => {
+                      Item  => {
+                                type  => 'child',
+                                path  => 'item',
+                                child => { ns => '__netxmpp__:iq:roster:item', },
+                                calls => [ 'Add' ],
+                               },
+                      Items => {
+                                type  => 'child',
+                                path  => 'item',
+                                child => { ns => '__netxmpp__:iq:roster:item', },
+                                calls => [ 'Get' ],
+                               },
+                      Roster => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                     },
+           );
+}
+
+#-----------------------------------------------------------------------------
+# __netxmpp__:iq:roster:item
+#-----------------------------------------------------------------------------
+{
+    &add_ns(ns    => "__netxmpp__:iq:roster:item",
+            xpath => {
+                      Ask          => { path => '@ask' },
+                      Group        => {
+                                       type => 'array',
+                                       path => 'group/text()',
+                                      },
+                      JID          => {
+                                       type => 'jid',
+                                       path => '@jid',
+                                      },
+                      Name         => { path => '@name' },
+                      Subscription => { path => '@subscription' },
+                      Item         => { type => 'master' },
+                     },
+            docs  => {
+                      module => 'Net::XMPP',
+                      name   => 'jabber:iq:roster - item objects',
+                     },
+           );
+}
+
+
+
+sub add_ns
+{
+    my (%args) = @_;
+
+    # XXX error check...
+    
+    $NS{$args{ns}}->{tag} = $args{tag} if exists($args{tag});
+    $NS{$args{ns}}->{xpath} = $args{xpath};
+    if (exists($args{docs}))
+    {
+        $NS{$args{ns}}->{docs} = $args{docs};
+        $NS{$args{ns}}->{docs}->{name} = $args{ns}
+            unless exists($args{docs}->{name});
+    }
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Presence.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Presence.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Presence.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,335 @@
+#############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Presence;
+
+=head1 NAME
+
+Net::XMPP::Presence - XMPP Presence Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Presence is a companion to the Net::XMPP module.
+  It provides the user a simple interface to set and retrieve all
+  parts of an XMPP Presence.
+
+=head1 DESCRIPTION
+
+  A Net::XMPP::Presence object is passed to the callback function for
+  the message.  Also, the first argument to the callback functions is
+  the session ID from XML::Streams.  There are some cases where you
+  might want this information, like if you created a Client that
+  connects to two servers at once, or for writing a mini server.
+
+    use Net::XMPP;
+
+    sub presence {
+      my ($sid,$Pres) = @_;
+      .
+      .
+      .
+    }
+
+  You now have access to all of the retrieval functions available.
+
+  To create a new presence to send to the server:
+
+    use Net::XMPP;
+
+    $Pres = Net::XMPP::Presence->new();
+
+  Now you can call the creation functions below to populate the tag
+  before sending it.
+
+=head1 METHODS
+
+=head2 Retrieval functions
+
+  GetTo()      - returns the value in the to='' attribute for the
+  GetTo("jid")   <presence/>.  If you specify "jid" as an argument
+                 then a Net::XMPP::JID object is returned and
+                 you can easily parse the parts of the JID.
+
+                 $to    = $Pres->GetTo();
+                 $toJID = $Pres->GetTo("jid");
+
+  GetFrom()      - returns the value in the from='' attribute for the
+  GetFrom("jid")   <presence/>.  If you specify "jid" as an argument
+                   then a Net::XMPP::JID object is returned and
+                   you can easily parse the parts of the JID.
+
+                   $from    = $Pres->GetFrom();
+                   $fromJID = $Pres->GetFrom("jid");
+
+  GetType() - returns the type='' attribute of the <presence/>.  Each
+              presence is one of seven types:
+
+                available       available to receive messages; default
+                unavailable     unavailable to receive anything
+                subscribe       ask the recipient to subscribe you
+                subscribed      tell the sender they are subscribed
+                unsubscribe     ask the recipient to unsubscribe you
+                unsubscribed    tell the sender they are unsubscribed
+                probe           probe
+
+              $type = $Pres->GetType();
+
+  GetStatus() - returns a string with the current status of the resource.
+
+                $status = $Pres->GetStatus();
+
+  GetPriority() - returns an integer with the priority of the resource
+                  The default is 0 if there is no priority in this
+                  presence.
+
+                  $priority = $Pres->GetPriority();
+
+  GetShow() - returns a string with the state the client should show.
+
+              $show = $Pres->GetShow();
+
+=head2 Creation functions
+
+  SetPresence(to=>string|JID     - set multiple fields in the <presence/>
+              from=>string|JID,    at one time.  This is a cumulative
+              type=>string,        and over writing action.  If you set
+              status=>string,      the "to" attribute twice, the second
+              priority=>integer,   setting is what is used.  If you set
+              meta=>string,        the status, and then set the priority
+              icon=>string,        then both will be in the <presence/>
+              show=>string,        tag.  For valid settings read the
+              loc=>string)         specific Set functions below.
+
+                        $Pres->SetPresence(TYPE=>"away",
+					   StatuS=>"Out for lunch");
+
+  SetTo(string) - sets the to attribute.  You can either pass a string
+  SetTo(JID)      or a JID object.  They must be valid JIDs or the 
+                  server will return an error message.
+                  (ie.  bob@jabber.org/Silent Bob, etc...)
+
+                  $Pres->SetTo("bob\@jabber.org");
+
+  SetFrom(string) - sets the from='' attribute.  You can either pass
+  SetFrom(JID)      a string or a JID object.  They must be valid JIDs
+                    or the server will return an error message. (ie.
+                    jabber:bob@jabber.org/Work)  This field is not
+                    required if you are writing a Client since the
+                    server will put the JID of your connection in there
+                    to prevent spamming.
+
+                    $Pres->SetFrom("jojo\@jabber.org");
+
+  SetType(string) - sets the type attribute.  Valid settings are:
+
+                    available      available to receive messages; default
+                    unavailable    unavailable to receive anything
+                    subscribe      ask the recipient to subscribe you
+                    subscribed     tell the sender they are subscribed
+                    unsubscribe    ask the recipient to unsubscribe you
+                    unsubscribed   tell the sender they are unsubscribed
+                    probe          probe
+
+                    $Pres->SetType("unavailable");
+
+  SetStatus(string) - sets the status tag to be whatever string the user
+                      wants associated with that resource.
+
+                      $Pres->SetStatus("Taking a nap");
+
+  SetPriority(integer) - sets the priority of this resource.  The highest
+                         resource attached to the xmpp account is the
+                         one that receives the messages.
+
+                         $Pres->SetPriority(10);
+
+  SetShow(string) - sets the name of the icon or string to display for
+                    this resource.
+
+                    $Pres->SetShow("away");
+
+  Reply(hash) - creates a new Presence object and populates the to/from
+                fields.  If you specify a hash the same as with
+                SetPresence then those values will override the Reply
+                values.
+
+                $Reply = $Pres->Reply();
+                $Reply = $Pres->Reply(type=>"subscribed");
+
+=head2 Removal functions
+
+  RemoveTo() -  removes the to attribute from the <presence/>.
+
+                $Pres->RemoveTo();
+  
+  RemoveFrom() -  removes the from attribute from the <presence/>.
+
+                  $Pres->RemoveFrom();
+  
+  RemoveType() -  removes the type attribute from the <presence/>.
+
+                  $Pres->RemoveType();
+  
+  RemoveStatus() -  removes the <status/> element from the <presence/>.
+
+                    $Pres->RemoveStatus();
+  
+  RemovePriority() -  removes the <priority/> element from the
+                      <presence/>.
+
+                      $Pres->RemovePriority();
+  
+  RemoveShow() -  removes the <show/> element from the <presence/>.
+
+                  $Pres->RemoveShow();
+  
+=head2 Test functions
+
+  DefinedTo() - returns 1 if the to attribute is defined in the
+                <presence/>, 0 otherwise.
+
+                $test = $Pres->DefinedTo();
+
+  DefinedFrom() - returns 1 if the from attribute is defined in the
+                  <presence/>, 0 otherwise.
+
+                  $test = $Pres->DefinedFrom();
+
+  DefinedType() - returns 1 if the type attribute is defined in the
+                  <presence/>, 0 otherwise.
+
+                   $test = $Pres->DefinedType();
+
+  DefinedStatus() - returns 1 if <status/> is defined in the
+                    <presence/>, 0 otherwise.
+
+                    $test = $Pres->DefinedStatus();
+
+  DefinedPriority() - returns 1 if <priority/> is defined in the
+                      <presence/>, 0 otherwise.
+
+                      $test = $Pres->DefinedPriority();
+
+  DefinedShow() - returns 1 if <show/> is defined in the <presence/>,
+                  0 otherwise.
+
+                  $test = $Pres->DefinedShow();
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+require 5.003;
+use strict;
+use Carp;
+use vars qw( %FUNCTIONS );
+use Net::XMPP::Stanza;
+use base qw( Net::XMPP::Stanza );
+
+sub new
+{
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self = { };
+
+    bless($self, $proto);
+
+    $self->{DEBUGHEADER} = "Presence";
+    $self->{TAG} = "presence";
+
+    $self->{FUNCS} = \%FUNCTIONS;
+    
+    $self->_init(@_);
+
+    return $self;
+}
+
+sub _presence { my $self = shift; return Net::XMPP::Presence->new(); }
+
+
+$FUNCTIONS{Error}->{path} = 'error/text()';
+
+$FUNCTIONS{ErrorCode}->{path} = 'error/@code';
+
+$FUNCTIONS{From}->{type} = 'jid';
+$FUNCTIONS{From}->{path} = '@from';
+
+$FUNCTIONS{ID}->{path} = '@id';
+
+$FUNCTIONS{Priority}->{path} = 'priority/text()';
+
+$FUNCTIONS{Show}->{path} = 'show/text()';
+
+$FUNCTIONS{Status}->{path} = 'status/text()';
+
+$FUNCTIONS{To}->{type} = 'jid';
+$FUNCTIONS{To}->{path} = '@to';
+
+$FUNCTIONS{Type}->{path} = '@type';
+
+$FUNCTIONS{XMLNS}->{path} = '@xmlns';
+
+$FUNCTIONS{Presence}->{type}  = 'master';
+
+$FUNCTIONS{Child}->{type}  = 'child';
+$FUNCTIONS{Child}->{path}  = '*[@xmlns]';
+$FUNCTIONS{Child}->{child} = {};
+
+##############################################################################
+#
+# Reply - returns a Net::XMPP::Presence object with the proper fields
+#         already populated for you.
+#
+##############################################################################
+sub Reply
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $reply = $self->_presence();
+
+    $reply->SetID($self->GetID()) if ($self->GetID() ne "");
+
+    $reply->SetPresence((($self->GetFrom() ne "") ?
+                         (to=>$self->GetFrom()) :
+                         ()
+                        ),
+                        (($self->GetTo() ne "") ?
+                         (from=>$self->GetTo()) :
+                         ()
+                        ),
+                       );
+
+    $reply->SetPresence(%args);
+
+    return $reply;
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/PrivacyLists.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/PrivacyLists.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/PrivacyLists.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,358 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::PrivacyLists;
+
+=head1 NAME
+
+Net::XMPP::PrivacyLists - XMPP Privacy Lists Object
+
+=head1 SYNOPSIS
+
+  This module is not yet complete.  Do not use.
+
+=head1 DESCRIPTION
+
+=head2 Basic Functions
+
+=head2 Advanced Functions
+
+=head1 METHODS
+
+=head2 Basic Functions
+
+=head2 Advanced Functions
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use Carp;
+
+sub new
+{
+    my $proto = shift;
+    my $self = { };
+
+    my %args;
+    while($#_ >= 0) { $args{ lc(pop(@_)) } = pop(@_); }
+
+    $self->{CONNECTION} = $args{connection};
+    
+    bless($self, $proto);
+
+    $self->init();
+    
+    return $self;
+}
+
+
+##############################################################################
+#
+# init - initialize the module to use the privacy lists.
+#
+##############################################################################
+sub init
+{
+    my $self = shift;
+
+    $self->{CONNECTION}-> SetXPathCallBacks('/iq[@type="result" or @type="set"]/query[@xmlns="jabber:iq:privacy"]'=>sub{ $self->handler(@_) });
+}
+
+
+##############################################################################
+#
+# debug - print out a representation of the privacy lists.
+#
+##############################################################################
+sub debug
+{
+    my $self = shift;
+
+    &XML::Stream::printData("\$self->{LISTS}",$self->{LISTS});
+}
+
+
+##############################################################################
+#
+# addItem - add list item to a list.
+#
+##############################################################################
+sub addItem
+{
+    my $self = shift;
+    my ($list,%item) = @_;
+
+    my $order = delete($item{order});
+    $self->{LISTS}->{$list}->{$order} = \%item;
+}
+
+
+###############################################################################
+#
+# clear - delete all of the JIDs from the DB completely.
+#
+###############################################################################
+sub clear
+{
+    my $self = shift;
+
+    $self->{CONNECTION}->{DEBUG}->Log3("PrivacyLists::clear: clearing the database");
+    foreach my $list ($self->lists())
+    {
+        $self->remove($list);
+    }
+    $self->{CONNECTION}->{DEBUG}->Log3("PrivacyLists::clear: database is empty");
+}
+
+
+##############################################################################
+#
+# exists - allows you to query if the JID exists in the Roster DB.
+#
+##############################################################################
+sub exists
+{
+    my $self = shift;
+    my $list = shift;
+
+    return unless exists($self->{LISTS});
+    return unless exists($self->{LISTS}->{$list});
+    return 1;
+}
+
+
+##############################################################################
+#
+# fetch - fetch the privacy lists from the server and populate the database.
+#
+##############################################################################
+sub fetch
+{
+    my $self = shift;
+
+    my $iq = $self->{CONNECTION}->PrivacyListsGet();
+    $self->handleIQ($iq);
+}
+
+
+##############################################################################
+#
+# fetchList - fetch the privacy list from the server and populate the database.
+#
+##############################################################################
+sub fetchList
+{
+    my $self = shift;
+    my $list = shift;
+    
+    my $iq = $self->{CONNECTION}->PrivacyListsGet(list=>$list);
+    $self->handleIQ($iq);
+}
+
+
+##############################################################################
+#
+# lists - returns a list of the current privacy lists.
+#
+##############################################################################
+sub lists
+{
+    my $self = shift;
+
+    return () unless exists($self->{LISTS});
+    return () if (scalar(keys(%{$self->{LISTS}})) == 0);
+    return keys(%{$self->{LISTS}});
+}
+
+
+##############################################################################
+#
+# items - returns a list of all of the items in the specified privacy list.
+#
+##############################################################################
+sub items
+{
+    my $self = shift;
+    my $list = shift;
+
+    my @items;
+
+    return () unless $self->exists($list);
+    foreach my $order (sort{ $a <=> $b } keys(%{$self->{LISTS}->{$list}}))
+    {
+        my %item = %{$self->{LISTS}->{$list}->{$order}};
+        $item{order} = $order;
+        push(@items,\%item);
+    }
+
+    return @items;
+}
+
+
+##############################################################################
+#
+# handler - takes a packet and calls the correct handler.
+#
+##############################################################################
+sub handler
+{
+    my $self = shift;
+    my $sid = shift;
+    my $packet = shift;
+
+    $self->handleIQ($packet) if ($packet->GetTag() eq "iq");
+}
+
+
+##############################################################################
+#
+# handleIQ - takes an iq packet that contains roster, parses it, and puts
+#            the roster into the Roster DB.
+#
+##############################################################################
+sub handleIQ
+{
+    my $self = shift;
+    my $iq = shift;
+
+    print "handleIQ: iq(",$iq->GetXML(),")\n";
+
+    my $type = $iq->GetType();
+    return unless (($type eq "set") || ($type eq "result"));
+
+    if ($type eq "result")
+    {
+        my $query = $iq->GetChild("jabber:iq:privacy");
+        
+        my @lists = $query->GetLists();
+
+        return unless ($#lists > -1);
+        
+        my @items = $lists[0]->GetItems();
+        
+        if (($#lists == 0) && ($#items > -1))
+        {
+            $self->parseList($lists[0]);
+        }
+        elsif ($#lists >= -1)
+        {
+            $self->parseLists(\@lists);
+        }
+    }
+}
+
+
+sub parseList
+{
+    my $self = shift;
+    my $list = shift;
+    
+    my $name = $list->GetName();
+
+    foreach my $item ($list->GetItems())
+    {
+        my %item = $item->GetItem();
+        
+        $self->addItem($name,%item);
+    }
+}
+
+
+sub parseLists
+{
+    my $self = shift;
+    my $lists = shift;
+    
+    foreach my $list (@{$lists})
+    {
+        my $name = $list->GetName();
+        $self->fetchList($name);
+    }
+}
+
+
+##############################################################################
+#
+# reload - clear and refetch the privacy lists.
+#
+##############################################################################
+sub reload
+{
+    my $self = shift;
+
+    $self->clear();
+    $self->fetch();
+}
+
+
+##############################################################################
+#
+# remove - removes the list from the database.
+#
+##############################################################################
+sub remove
+{
+    my $self = shift;
+    my $list = shift;
+
+    if ($self->exists($list))
+    {
+        $self->{CONNECTION}->{DEBUG}->Log3("PrivacyLists::remove: deleting $list from the DB");
+        
+        delete($self->{LISTS}->{$list});
+        delete($self->{LISTS}) if (scalar(keys(%{$self->{LISTS}})) == 0);
+    }
+}
+
+
+sub save
+{
+    my $self = shift;
+
+    foreach my $list ($self->lists())
+    {
+        $self->saveList($list);
+    }
+}
+
+
+sub saveList
+{
+    my $self = shift;
+    my $list = shift;
+
+    my @items = $self->items($list);
+    $self->{CONNECTION}->PrivacyListsSet(list=>$list,
+                                         items=>\@items);
+}
+
+
+1;
+
Index: perl/modules/Jabber/lib/Net/XMPP/Protocol.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Protocol.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Protocol.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,3314 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Protocol;
+
+=head1 NAME
+
+Net::XMPP::Protocol - XMPP Protocol Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Protocol is a module that provides a developer easy
+  access to the XMPP Instant Messaging protocol.  It provides high
+  level functions to the Net::XMPP Client object.  These functions are
+  inherited by that modules.
+
+=head1 DESCRIPTION
+
+  Protocol.pm seeks to provide enough high level APIs and automation of
+  the low level APIs that writing a XMPP Client in Perl is trivial.  For
+  those that wish to work with the low level you can do that too, but
+  those functions are covered in the documentation for each module.
+
+  Net::XMPP::Protocol provides functions to login, send and receive
+  messages, set personal information, create a new user account, manage
+  the roster, and disconnect.  You can use all or none of the functions,
+  there is no requirement.
+
+  For more information on how the details for how Net::XMPP is written
+  please see the help for Net::XMPP itself.
+
+  For more information on writing a Client see Net::XMPP::Client.
+
+=head2 Modes
+
+  Several of the functions take a mode argument that let you specify how
+  the function should behave:
+
+    block - send the packet with an ID, and then block until an answer
+            comes back.  You can optionally specify a timeout so that
+            you do not block forever.
+           
+    nonblock - send the packet with an ID, but then return that id and
+               control to the master program.  Net::XMPP is still
+               tracking this packet, so you must use the CheckID function
+               to tell when it comes in.  (This might not be very
+               useful...)
+
+    passthru - send the packet with an ID, but do NOT register it with
+               Net::XMPP, then return the ID.  This is useful when
+               combined with the XPath function because you can register
+               a one shot function tied to the id you get back.
+               
+
+=head2 Basic Functions
+
+    use Net::XMPP qw( Client );
+    $Con = Net::XMPP::Client->new();                  # From
+    $status = $Con->Connect(hostname=>"jabber.org"); # Net::XMPP::Client
+
+    $Con->SetCallBacks(send=>\&sendCallBack,
+                       receive=>\&receiveCallBack,
+                       message=>\&messageCallBack,
+                       iq=>\&handleTheIQTag);
+
+    $Con->SetMessageCallBacks(normal=>\&messageNormalCB,
+                              chat=>\&messageChatCB);
+
+    $Con->SetPresenceCallBacks(available=>\&presenceAvailableCB,
+                               unavailable=>\&presenceUnavailableCB);
+
+    $Con->SetIQCallBacks("custom-namespace"=>
+                                             {
+                                                 get=>\&iqCustomGetCB,
+                                                 set=>\&iqCustomSetCB,
+                                                 result=>\&iqCustomResultCB,
+                                             },
+                                             etc...
+                                            );
+
+    $Con->SetXPathCallBacks("/message[@type='chat']"=>&messageChatCB,
+                            "/message[@type='chat']"=>&otherMessageChatCB,
+                            ...
+                           );
+
+    $Con->RemoveXPathCallBacks("/message[@type='chat']"=>&otherMessageChatCB);
+
+    $error = $Con->GetErrorCode();
+    $Con->SetErrorCode("Timeout limit reached");
+
+    $status = $Con->Process();
+    $status = $Con->Process(5);
+
+    $Con->Send($object);
+    $Con->Send("<tag>XML</tag>");
+
+    $Con->Send($object,1);
+    $Con->Send("<tag>XML</tag>",1);
+
+    $Con->Disconnect();
+
+=head2 ID Functions
+
+    $id         = $Con->SendWithID($sendObj);
+    $id         = $Con->SendWithID("<tag>XML</tag>");
+    $receiveObj = $Con->SendAndReceiveWithID($sendObj);
+    $receiveObj = $Con->SendAndReceiveWithID($sendObj,
+                                             10);
+    $receiveObj = $Con->SendAndReceiveWithID("<tag>XML</tag>");
+    $receiveObj = $Con->SendAndReceiveWithID("<tag>XML</tag>",
+                                             5);
+    $yesno      = $Con->ReceivedID($id);
+    $receiveObj = $Con->GetID($id);
+    $receiveObj = $Con->WaitForID($id);
+    $receiveObj = $Con->WaitForID($id,
+                                  20);
+
+=head2 Namespace Functions
+
+    $Con->AddNamespace(ns=>"foo:bar",
+                       tag=>"myfoo",
+                       xpath=>{Foo=>{ path=> "foo/text()" },
+                               Bar=>{ path=> "bar/text()" },
+                               FooBar=>{ type=> "master" },
+                              }
+                      );
+
+=head2 Message Functions
+
+    $Con->MessageSend(to=>"bob@jabber.org",
+                      subject=>"Lunch",
+                      body=>"Let's go grab some...\n",
+                      thread=>"ABC123",
+                      priority=>10);
+
+=head2 Presence Functions
+
+    $Con->PresenceSend();
+    $Con->PresenceSend(type=>"unavailable");
+    $Con->PresenceSend(show=>"away");
+    $Con->PresenceSend(signature=>...signature...);
+
+=head2 Subscription Functions
+
+    $Con->Subscription(type=>"subscribe",
+                       to=>"bob@jabber.org");
+
+    $Con->Subscription(type=>"unsubscribe",
+                       to=>"bob@jabber.org");
+
+    $Con->Subscription(type=>"subscribed",
+                       to=>"bob@jabber.org");
+
+    $Con->Subscription(type=>"unsubscribed",
+                       to=>"bob@jabber.org");
+
+=head2 Presence DB Functions
+
+    $Con->PresenceDB();
+
+    $Con->PresenceDBParse(Net::XMPP::Presence);
+
+    $Con->PresenceDBDelete("bob\@jabber.org");
+    $Con->PresenceDBDelete(Net::XMPP::JID);
+
+    $Con->PresenceDBClear();
+
+    $presence  = $Con->PresenceDBQuery("bob\@jabber.org");
+    $presence  = $Con->PresenceDBQuery(Net::XMPP::JID);
+
+    @resources = $Con->PresenceDBResources("bob\@jabber.org");
+    @resources = $Con->PresenceDBResources(Net::XMPP::JID);
+
+=head2 IQ  Functions
+
+=head2 Auth Functions
+
+    @result = $Con->AuthSend();
+    @result = $Con->AuthSend(username=>"bob",
+                             password=>"bobrulez",
+                             resource=>"Bob");
+
+=head2 Register Functions
+
+    %hash   = $Con->RegisterRequest();
+    %hash   = $Con->RegisterRequest(to=>"transport.jabber.org");
+    %hash   = $Con->RegisterRequest(to=>"transport.jabber.org",
+                                    timeout=>10);
+
+    @result = $Con->RegisterSend(to=>"somewhere",
+                                 username=>"newuser",
+                                 resource=>"New User",
+                                 password=>"imanewbie",
+                                 email=>"newguy@new.com",
+                                 key=>"some key");
+
+=head2 Roster Functions
+
+    $Roster = $Con->Roster();
+
+    %roster = $Con->RosterParse($iq);
+    %roster = $Con->RosterGet();
+    $Con->RosterRequest();
+    $Con->RosterAdd(jid=>"bob\@jabber.org",
+                    name=>"Bob");
+    $Con->RosterRemove(jid=>"bob@jabber.org");
+
+=head2 Roster DB Functions
+
+    $Con->RosterDB();
+
+    $Con->RosterDBParse(Net::XMPP::IQ);
+
+    $Con->RosterDBAdd("bob\@jabber.org",
+                      name=>"Bob",
+                      groups=>["foo"]
+                     );
+    
+    $Con->RosterDBRemove("bob\@jabber.org");
+    $Con->RosterDBRemove(Net::XMPP::JID);
+
+    $Con->RosterDBClear();
+
+    if ($Con->RosterDBExists("bob\@jabber.org")) { ...
+    if ($Con->RosterDBExists(Net::XMPP::JID)) { ...
+
+    @jids = $Con->RosterDBJIDs();
+    
+    if ($Con->RosterDBGroupExists("foo")) { ...
+
+    @groups = $Con->RosterDBGroups();
+    
+    @jids = $Con->RosterDBGroupJIDs("foo");
+    
+    @jids = $Con->RosterDBNonGroupJIDs();
+    
+    %hash = $Con->RosterDBQuery("bob\@jabber.org");
+    %hash = $Con->RosterDBQuery(Net::XMPP::JID);
+
+    $value = $Con->RosterDBQuery("bob\@jabber.org","name");
+    $value = $Con->RosterDBQuery(Net::XMPP::JID,"groups");
+
+
+=head1 METHODS
+
+=head2 Basic Functions
+
+    GetErrorCode() - returns a string that will hopefully contain some
+                     useful information about why a function returned
+                     an undef to you.
+
+    SetErrorCode(string) - set a useful error message before you return
+                           an undef to the caller.
+
+    SetCallBacks(message=>function,  - sets the callback functions for
+                 presence=>function,   the top level tags listed.  The
+                 iq=>function,         available tags to look for are
+                 send=>function,       <message/>, <presence/>, and
+                 receive=>function,    <iq/>.  If a packet is received
+                 update=>function)     with an ID which is found in the
+                                       registerd ID list (see RegisterID
+                                       below) then it is not sent to
+                                       these functions, instead it
+                                       is inserted into a LIST and can
+                                       be retrieved by some functions
+                                       we will mention later.
+
+                                       send and receive are used to
+                                       log what XML is sent and received.
+                                       update is used as way to update
+                                       your program while waiting for
+                                       a packet with an ID to be
+                                       returned (useful for GUI apps).
+
+                                       A major change that came with
+                                       the last release is that the
+                                       session id is passed to the
+                                       callback as the first argument.
+                                       This was done to facilitate
+                                       the Server module.
+
+                                       The next argument depends on
+                                       which callback you are talking
+                                       about.  message, presence, and iq
+                                       all get passed in Net::XMPP
+                                       objects that match those types.
+                                       send and receive get passed in
+                                       strings.  update gets passed
+                                       nothing, not even the session id.
+
+                                       If you set the function to undef,
+                                       then the callback is removed from
+                                       the list.
+
+    SetPresenceCallBacks(type=>function - sets the callback functions for
+                         etc...)          the specified presence type.
+                                          The function takes types as the
+                                          main key, and lets you specify
+                                          a function for each type of
+                                          packet you can get.
+                                            "available"
+                                            "unavailable"
+                                            "subscribe"
+                                            "unsubscribe"
+                                            "subscribed"
+                                            "unsubscribed"
+                                            "probe"
+                                            "error"
+                                          When it gets a <presence/>
+                                          packet it checks the type=''
+                                          for a defined callback.  If
+                                          there is one then it calls the
+                                          function with two arguments:
+                                            the session ID, and the
+                                            Net::XMPP::Presence object.
+
+                                          If you set the function to
+                                          undef, then the callback is
+                                          removed from the list.
+
+                        NOTE: If you use this, which is a cleaner method,
+                              then you must *NOT* specify a callback for
+                              presence in the SetCallBacks function.
+ 
+                                          Net::XMPP defines a few default
+                                          callbacks for various types:
+ 
+                                          "subscribe" -
+                                            replies with subscribed
+                                          
+                                          "unsubscribe" -
+                                            replies with unsubscribed
+                                          
+                                          "subscribed" -
+                                            replies with subscribed
+                                          
+                                          "unsubscribed" -
+                                            replies with unsubscribed
+                                         
+
+    SetMessageCallBacks(type=>function, - sets the callback functions for
+                        etc...)           the specified message type. The
+                                          function takes types as the
+                                          main key, and lets you specify
+                                          a function for each type of
+                                          packet you can get.
+                                           "normal"
+                                           "chat"
+                                           "groupchat"
+                                           "headline"
+                                           "error"
+                                         When it gets a <message/> packet
+                                         it checks the type='' for a
+                                         defined callback. If there is
+                                         one then it calls the function
+                                         with two arguments:
+                                           the session ID, and the
+                                           Net::XMPP::Message object.
+
+                                         If you set the function to
+                                         undef, then the callback is
+                                         removed from the list.
+
+                       NOTE: If you use this, which is a cleaner method,
+                             then you must *NOT* specify a callback for
+                             message in the SetCallBacks function.
+
+
+    SetIQCallBacks(namespace=>{      - sets the callback functions for
+                     get=>function,    the specified namespace. The
+                     set=>function,    function takes namespaces as the
+                     result=>function  main key, and lets you specify a
+                   },                  function for each type of packet
+                   etc...)             you can get.
+                                         "get"
+                                         "set"
+                                         "result"
+                                       When it gets an <iq/> packet it
+                                       checks the type='' and the
+                                       xmlns='' for a defined callback.
+                                       If there is one then it calls
+                                       the function with two arguments:
+                                       the session ID, and the
+                                       Net::XMPP::xxxx object.
+
+                                       If you set the function to undef,
+                                       then the callback is removed from
+                                       the list.
+
+                       NOTE: If you use this, which is a cleaner method,
+                             then you must *NOT* specify a callback for
+                             iq in the SetCallBacks function.
+
+    SetXPathCallBacks(xpath=>function, - registers a callback function
+                        etc...)          for each xpath specified.  If
+                                         Net::XMPP matches the xpath,
+                                         then it calls the function with
+                                         two arguments:
+                                           the session ID, and the
+                                           Net::XMPP::Message object.
+
+                                         Xpaths are rooted at each
+                                         packet:
+                                           /message[@type="chat"]
+                                           /iq/*[xmlns="jabber:iq:roster"][1]
+                                           ...
+
+    RemoveXPathCallBacks(xpath=>function, - unregisters a callback
+                        etc...)             function for each xpath
+                                            specified.
+
+    Process(integer) - takes the timeout period as an argument.  If no
+                       timeout is listed then the function blocks until
+                       a packet is received.  Otherwise it waits that
+                       number of seconds and then exits so your program
+                       can continue doing useful things.  NOTE: This is
+                       important for GUIs.  You need to leave time to
+                       process GUI commands even if you are waiting for
+                       packets.  The following are the possible return
+                       values, and what they mean:
+
+                           1   - Status ok, data received.
+                           0   - Status ok, no data received.
+                         undef - Status not ok, stop processing.
+                       
+                       IMPORTANT: You need to check the output of every
+                       Process.  If you get an undef then the connection
+                       died and you should behave accordingly.
+
+    Send(object,         - takes either a Net::XMPP::xxxxx object or
+         ignoreActivity)   an XML string as an argument and sends it to
+    Send(string,           the server.  If you set ignoreActivty to 1,
+         ignoreActivity)   then the XML::Stream module will not record
+                           this packet as couting towards user activity.
+=head2 ID Functions
+
+    SendWithID(object) - takes either a Net::XMPP::xxxxx object or an
+    SendWithID(string)   XML string as an argument, adds the next
+                         available ID number and sends that packet to
+                         the server.  Returns the ID number assigned.
+
+    SendAndReceiveWithID(object,  - uses SendWithID and WaitForID to
+                         timeout)   provide a complete way to send and
+    SendAndReceiveWithID(string,    receive packets with IDs.  Can take
+                         timeout)   either a Net::XMPP::xxxxx object
+                                    or an XML string.  Returns the
+                                    proper Net::XMPP::xxxxx object
+                                    based on the type of packet
+                                    received.  The timeout is passed
+                                    on to WaitForID, see that function
+                                    for how the timeout works.
+
+    ReceivedID(integer) - returns 1 if a packet has been received with
+                          specified ID, 0 otherwise.
+
+    GetID(integer) - returns the proper Net::XMPP::xxxxx object based
+                     on the type of packet received with the specified
+                     ID.  If the ID has been received the GetID returns
+                     0.
+
+    WaitForID(integer, - blocks until a packet with the ID is received.
+              timeout)   Returns the proper Net::XMPP::xxxxx object
+                         based on the type of packet received.  If the
+                         timeout limit is reached then if the packet
+                         does come in, it will be discarded.
+
+
+    NOTE:  Only <iq/> officially support ids, so sending a <message/>, or
+           <presence/> with an id is a risk.  The server will ignore the
+           id tag and pass it through, so both clients must support the
+           id tag for these functions to be useful.
+
+=head2 Namespace Functions
+
+    AddNamespace(ns=>string,  - This function is very complex.
+                 tag=>string,   It is a little too complex to
+                 xpath=>hash)   discuss within the confines of
+                                this small paragraph.  Please
+                                refer to the man page for
+                                Net::XMPP::Namespaces for the
+                                full documentation on this
+                                subject.
+
+=head2 Message Functions
+
+    MessageSend(hash) - takes the hash and passes it to SetMessage in
+                        Net::XMPP::Message (refer there for valid
+                        settings).  Then it sends the message to the
+                        server.
+
+=head2 Presence Functions
+
+    PresenceSend()                  - no arguments will send an empty
+    PresenceSend(hash,                Presence to the server to tell it
+                 signature=>string)   that you are available.  If you
+                                      provide a hash, then it will pass
+                                      that hash to the SetPresence()
+                                      function as defined in the
+                                      Net::XMPP::Presence module.
+                                      Optionally, you can specify a
+                                      signature and a jabber:x:signed
+                                      will be placed in the <presence/>.
+
+=head2 Subscription Functions
+
+    Subscription(hash) - taks the hash and passes it to SetPresence in
+                         Net::XMPP::Presence (refer there for valid
+                         settings).  Then it sends the subscription to
+                         server.
+
+                         The valid types of subscription are:
+
+                           subscribe    - subscribe to JID's presence
+                           unsubscribe  - unsubscribe from JID's presence
+                           subscribed   - response to a subscribe
+                           unsubscribed - response to an unsubscribe
+
+=head2 Presence DB Functions
+
+    PresenceDB() - Tell the object to initialize the callbacks to
+                   automatically populate the Presence DB.
+
+    PresenceDBParse(Net::XMPP::Presence) - for every presence that you
+                                             receive pass the Presence
+                                             object to the DB so that
+                                             it can track the resources
+                                             and priorities for you.
+                                             Returns either the presence
+                                             passed in, if it not able
+                                             to parsed for the DB, or the
+                                             current presence as found by
+                                             the PresenceDBQuery
+                                             function.
+
+    PresenceDBDelete(string|Net::XMPP::JID) - delete thes JID entry
+                                                from the DB.
+
+    PresenceDBClear() - delete all entries in the database.
+
+    PresenceDBQuery(string|Net::XMPP::JID) - returns the NJ::Presence
+                                               that was last received for
+                                               the highest priority of
+                                               this JID.  You can pass
+                                               it a string or a NJ::JID
+                                               object.
+
+    PresenceDBResources(string|Net::XMPP::JID) - returns an array of
+                                                   resources in order
+                                                   from highest priority
+                                                   to lowest.
+
+=head2 IQ Functions
+
+=head2 Auth Functions
+
+    AuthSend(username=>string, - takes all of the information and
+             password=>string,   builds a Net::XMPP::IQ::Auth packet.
+             resource=>string)   It then sends that packet to the
+                                 server with an ID and waits for that
+                                 ID to return.  Then it looks in
+                                 resulting packet and determines if
+                                 authentication was successful for not.
+                                 The array returned from AuthSend looks
+                                 like this:
+                                   [ type , message ]
+                                 If type is "ok" then authentication
+                                 was successful, otherwise message
+                                 contains a little more detail about the
+                                 error.
+
+=head2 IQ::Register Functions
+
+    RegisterRequest(to=>string,  - send an <iq/> request to the specified
+                    timeout=>int)  server/transport, if not specified it
+    RegisterRequest()              sends to the current active server.
+                                   The function returns a hash that
+                                   contains the required fields.   Here
+                                   is an example of the hash:
+
+                                   $hash{fields}    - The raw fields from
+                                                      the iq:register.
+                                                      To be used if there
+                                                      is no x:data in the
+                                                      packet.
+                                   $hash{instructions} - How to fill out
+                                                         the form.
+                                   $hash{form}   - The new dynamic forms.
+
+                                   In $hash{form}, the fields that are
+                                   present are the required fields the
+                                   server needs.
+
+    RegisterSend(hash) - takes the contents of the hash and passes it
+                         to the SetRegister function in the module
+                         Net::XMPP::Query jabber:iq:register namespace.
+                         This function returns an array that looks like
+                         this:
+
+                            [ type , message ]
+
+                         If type is "ok" then registration was
+                         successful, otherwise message contains a
+                         little more detail about the error.
+
+=head2 Roster Functions
+
+    Roster() - returns a Net::XMPP::Roster object.  This will automatically
+               intercept all of the roster and presence packets sent from
+               the server and give you an accurate Roster.  For more
+               information please read the man page for Net::XMPP::Roster.
+
+    RosterParse(IQ object) - returns a hash that contains the roster
+                             parsed into the following data structure:
+
+                  $roster{'bob@jabber.org'}->{name}
+                                      - Name you stored in the roster
+
+                  $roster{'bob@jabber.org'}->{subscription}
+                                      - Subscription status
+                                        (to, from, both, none)
+
+                  $roster{'bob@jabber.org'}->{ask}
+                                      - The ask status from this user
+                                        (subscribe, unsubscribe)
+
+                  $roster{'bob@jabber.org'}->{groups}
+                                      - Array of groups that
+                                        bob@jabber.org is in
+
+    RosterGet() - sends an empty Net::XMPP::IQ::Roster tag to the
+                  server so the server will send the Roster to the
+                  client.  Returns the above hash from RosterParse.
+
+    RosterRequest() - sends an empty Net::XMPP::IQ::Roster tag to the
+                      server so the server will send the Roster to the
+                      client.
+
+    RosterAdd(hash) - sends a packet asking that the jid be
+                      added to the roster.  The hash format
+                      is defined in the SetItem function
+                      in the Net::XMPP::Query jabber:iq:roster
+                      namespace.
+
+    RosterRemove(hash) - sends a packet asking that the jid be
+                         removed from the roster.  The hash
+                         format is defined in the SetItem function
+                         in the Net::XMPP::Query jabber:iq:roster
+                         namespace.
+
+=head2 Roster DB Functions
+
+    RosterDB() - Tell the object to initialize the callbacks to
+                 automatically populate the Roster DB.  If you do this,
+                 then make sure that you call RosterRequest() instead of
+                 RosterGet() so that the callbacks can catch it and
+                 parse it.
+
+    RosterDBParse(IQ object) - If you want to manually control the
+                               database, then you can pass in all iq
+                               packets with jabber:iq:roster queries to
+                               this function.
+
+    RosterDBAdd(jid,hash) - Add a new JID into the roster DB.  The JID
+                            is either a string, or a Net::XMPP::JID
+                            object.  The hash must be the same format as
+                            the has returned by RosterParse above, and
+                            is the actual hash, not a reference.
+    
+    RosterDBRemove(jid) - Remove a JID from the roster DB. The JID is
+                          either a string, or a Net::XMPP::JID object.
+
+    RosterDBClear() - Remove all JIDs from the roster DB.
+
+    RosterDBExists(jid) - return 1 if the JID exists in the roster DB,
+                          undef otherwise.  The JID is either a string,
+                          or a Net::XMPP::JID object.
+
+    RosterDBJIDs() - returns a list of Net::XMPP::JID objects that
+                     represents all of the JIDs in the DB.
+    
+    RosterDBGroups() - returns the complete list of roster groups in the
+                       roster.
+    
+    RosterDBGroupExists(group) - return 1 if the group is a group in the
+                                 roster DB, undef otherwise.
+
+    RosterDBGroupJIDs(group) - returns a list of Net::XMPP::JID objects
+                               that represents all of the JIDs in the
+                               specified roster group.
+    
+    RosterDBNonGroupJIDs() - returns a list of Net::XMPP::JID objects
+                             that represents all of the JIDs not in a
+                             roster group.
+
+    RosterDBQuery(jid) - returns a hash containing the data from the
+                         roster DB for the specified JID.  The JID is
+                         either a string, or a Net::XMPP::JID object.
+                         The hash format the same as in RosterParse
+                         above.
+
+    RosterDBQuery(jid,key) - returns the entry from the above hash for
+                             the given key.  The available keys are:
+                               name, ask, subsrcription and groups
+                             The JID is either a string, or a 
+                             Net::XMPP::JID object.
+
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use Net::XMPP::Roster;
+use Net::XMPP::PrivacyLists;
+use strict;
+use Carp;
+use vars qw( %XMLNS %NEWOBJECT $SASL_CALLBACK $TLS_CALLBACK );
+
+##############################################################################
+# Define the namespaces in an easy/constant manner.
+#-----------------------------------------------------------------------------
+# 1.0
+#-----------------------------------------------------------------------------
+$XMLNS{'xmppstreams'}   = "urn:ietf:params:xml:ns:xmpp-streams";
+$XMLNS{'xmpp-bind'}     = "urn:ietf:params:xml:ns:xmpp-bind";
+$XMLNS{'xmpp-sasl'}     = "urn:ietf:params:xml:ns:xmpp-sasl";
+$XMLNS{'xmpp-session'}  = "urn:ietf:params:xml:ns:xmpp-session";
+$XMLNS{'xmpp-tls'}      = "urn:ietf:params:xml:ns:xmpp-tls";
+##############################################################################
+
+##############################################################################
+# BuildObject takes a root tag and builds the correct object.  NEWOBJECT is
+# the table that maps tag to package.  Override these, or provide new ones.
+#-----------------------------------------------------------------------------
+$NEWOBJECT{'iq'}       = "Net::XMPP::IQ";
+$NEWOBJECT{'message'}  = "Net::XMPP::Message";
+$NEWOBJECT{'presence'} = "Net::XMPP::Presence";
+$NEWOBJECT{'jid'}      = "Net::XMPP::JID";
+##############################################################################
+
+sub _message  { shift; my $o; eval "\$o = new $NEWOBJECT{'message'}(\@_);"; return $o;  }
+sub _presence { shift; my $o; eval "\$o = new $NEWOBJECT{'presence'}(\@_);"; return $o; }
+sub _iq       { shift; my $o; eval "\$o = new $NEWOBJECT{'iq'}(\@_);"; return $o;       }
+sub _jid      { shift; my $o; eval "\$o = new $NEWOBJECT{'jid'}(\@_);"; return $o;      }
+
+###############################################################################
+#+-----------------------------------------------------------------------------
+#|
+#| Base API
+#|
+#+-----------------------------------------------------------------------------
+###############################################################################
+
+###############################################################################
+#
+# GetErrorCode - if you are returned an undef, you can call this function
+#                and hopefully learn more information about the problem.
+#
+###############################################################################
+sub GetErrorCode
+{
+    my $self = shift;
+    return ((exists($self->{ERRORCODE}) && ($self->{ERRORCODE} ne "")) ?
+            $self->{ERRORCODE} :
+            $!
+           );
+}
+
+
+###############################################################################
+#
+# SetErrorCode - sets the error code so that the caller can find out more
+#                information about the problem
+#
+###############################################################################
+sub SetErrorCode
+{
+    my $self = shift;
+    my ($errorcode) = @_;
+    $self->{ERRORCODE} = $errorcode;
+}
+
+
+###############################################################################
+#
+# CallBack - Central callback function.  If a packet comes back with an ID
+#            and the tag and ID have been registered then the packet is not
+#            returned as normal, instead it is inserted in the LIST and
+#            stored until the user wants to fetch it.  If the tag and ID
+#            are not registered the function checks if a callback exists
+#            for this tag, if it does then that callback is called,
+#            otherwise the function drops the packet since it does not know
+#            how to handle it.
+#
+###############################################################################
+sub CallBack
+{
+    my $self = shift;
+    my $sid = shift;
+    my ($object) = @_;
+
+    my $tag;
+    my $id;
+    my $tree;
+    
+    if (ref($object) !~ /^Net::XMPP/)
+    {
+        if ($self->{DEBUG}->GetLevel() >= 1 || exists($self->{CB}->{receive}))
+        {
+            my $xml = $object->GetXML();
+            $self->{DEBUG}->Log1("CallBack: sid($sid) received($xml)");
+            &{$self->{CB}->{receive}}($sid,$xml) if exists($self->{CB}->{receive});
+        }
+
+        $tag = $object->get_tag();
+        $id = "";
+        $id = $object->get_attrib("id")
+            if defined($object->get_attrib("id"));
+        $tree = $object;
+    }
+    else
+    {
+        $tag = $object->GetTag();
+        $id = $object->GetID();
+        $tree = $object->GetTree();
+    }
+
+    $self->{DEBUG}->Log1("CallBack: tag($tag)");
+    $self->{DEBUG}->Log1("CallBack: id($id)") if ($id ne "");
+
+    my $pass = 1;
+    $pass = 0
+        if (!exists($self->{CB}->{$tag}) &&
+            !exists($self->{CB}->{XPath}) &&
+            !exists($self->{CB}->{DirectXPath}) &&
+            !$self->CheckID($tag,$id)
+           );
+
+    if ($pass)
+    {
+        $self->{DEBUG}->Log1("CallBack: we either want it or were waiting for it.");
+
+        if (exists($self->{CB}->{DirectXPath}))
+        {
+            $self->{DEBUG}->Log1("CallBack: check directxpath");
+
+            my $direct_pass = 0;
+
+            foreach my $xpath (keys(%{$self->{CB}->{DirectXPath}}))
+            {
+                $self->{DEBUG}->Log1("CallBack: check directxpath($xpath)");
+                if ($object->XPathCheck($xpath))
+                {
+                    foreach my $func (keys(%{$self->{CB}->{DirectXPath}->{$xpath}}))
+                    {
+                        $self->{DEBUG}->Log1("CallBack: goto directxpath($xpath) function($func)");
+                        &{$self->{CB}->{DirectXPath}->{$xpath}->{$func}}($sid,$object);
+                        $direct_pass = 1;
+                    }
+                }
+            }
+            
+            return if $direct_pass;
+        }
+
+        my $NJObject;
+        if (ref($object) !~ /^Net::XMPP/)
+        {
+            $NJObject = $self->BuildObject($tag,$object);
+        }
+        else
+        {
+            $NJObject = $object;
+        }
+
+        if ($NJObject == -1)
+        {
+            $self->{DEBUG}->Log1("CallBack: DANGER!! DANGER!! We didn't build a packet!  We're all gonna die!!");
+        }
+        else
+        {
+            if ($self->CheckID($tag,$id))
+            {
+                $self->{DEBUG}->Log1("CallBack: found registry entry: tag($tag) id($id)");
+                $self->DeregisterID($tag,$id);
+                if ($self->TimedOutID($id))
+                {
+                    $self->{DEBUG}->Log1("CallBack: dropping packet due to timeout");
+                    $self->CleanID($id);
+                }
+                else
+                {
+                    $self->{DEBUG}->Log1("CallBack: they still want it... we still got it...");
+                    $self->GotID($id,$NJObject);
+                }
+            }
+            else
+            {
+                $self->{DEBUG}->Log1("CallBack: no registry entry");
+
+                if (exists($self->{CB}->{XPath}))
+                {
+                    $self->{DEBUG}->Log1("CallBack: check xpath");
+
+                    foreach my $xpath (keys(%{$self->{CB}->{XPath}}))
+                    {
+                        if ($NJObject->GetTree()->XPathCheck($xpath))
+                        {
+                            foreach my $func (keys(%{$self->{CB}->{XPath}->{$xpath}}))
+                            {
+                                $self->{DEBUG}->Log1("CallBack: goto xpath($xpath) function($func)");
+                                &{$self->{CB}->{XPath}->{$xpath}->{$func}}($sid,$NJObject);
+                            }
+                        }
+                    }
+                }
+
+                if (exists($self->{CB}->{$tag}))
+                {
+                    $self->{DEBUG}->Log1("CallBack: goto user function($self->{CB}->{$tag})");
+                    &{$self->{CB}->{$tag}}($sid,$NJObject);
+                }
+                else
+                {
+                    $self->{DEBUG}->Log1("CallBack: no defined function.  Dropping packet.");
+                }
+            }
+        }
+    }
+    else
+    {
+        $self->{DEBUG}->Log1("CallBack: a packet that no one wants... how sad. =(");
+    }
+}
+
+
+###############################################################################
+#
+# BuildObject - turn the packet into an object.
+#
+###############################################################################
+sub BuildObject
+{
+    my $self = shift;
+    my ($tag,$tree) = @_;
+
+    my $obj = -1;
+
+    if (exists($NEWOBJECT{$tag}))
+    {
+        $self->{DEBUG}->Log1("BuildObject: tag($tag) package($NEWOBJECT{$tag})");
+        eval "\$obj = new $NEWOBJECT{$tag}(\$tree);";
+    }
+
+    return $obj;
+}
+
+
+###############################################################################
+#
+# SetCallBacks - Takes a hash with top level tags to look for as the keys
+#                and pointers to functions as the values.  The functions
+#                are called and passed the XML::Parser::Tree objects
+#                generated by XML::Stream.
+#
+###############################################################################
+sub SetCallBacks
+{
+    my $self = shift;
+    while($#_ >= 0)
+    {
+        my $func = pop(@_);
+        my $tag = pop(@_);
+        $self->{DEBUG}->Log1("SetCallBacks: tag($tag) func($func)");
+        if (defined($func))
+        {
+            $self->{CB}->{$tag} = $func;
+        }
+        else
+        {
+            delete($self->{CB}->{$tag});
+        }
+        $self->{STREAM}->SetCallBacks(update=>$func) if ($tag eq "update");
+    }
+}
+
+
+###############################################################################
+#
+# SetIQCallBacks - define callbacks for the namespaces inside an iq.
+#
+###############################################################################
+sub SetIQCallBacks
+{
+    my $self = shift;
+
+    while($#_ >= 0)
+    {
+        my $hash = pop(@_);
+        my $namespace = pop(@_);
+
+        foreach my $type (keys(%{$hash}))
+        {
+            if (defined($hash->{$type}))
+            {
+                $self->{CB}->{IQns}->{$namespace}->{$type} = $hash->{$type};
+            }
+            else
+            {
+                delete($self->{CB}->{IQns}->{$namespace}->{$type});
+            }
+        }
+    }
+}
+
+
+###############################################################################
+#
+# SetPresenceCallBacks - define callbacks for the different presence packets.
+#
+###############################################################################
+sub SetPresenceCallBacks
+{
+    my $self = shift;
+    my (%types) = @_;
+
+    foreach my $type (keys(%types))
+    {
+        if (defined($types{$type}))
+        {
+            $self->{CB}->{Pres}->{$type} = $types{$type};
+        }
+        else
+        {
+            delete($self->{CB}->{Pres}->{$type});
+        }
+    }
+}
+
+
+###############################################################################
+#
+# SetMessageCallBacks - define callbacks for the different message packets.
+#
+###############################################################################
+sub SetMessageCallBacks
+{
+    my $self = shift;
+    my (%types) = @_;
+
+    foreach my $type (keys(%types))
+    {
+        if (defined($types{$type}))
+        {
+            $self->{CB}->{Mess}->{$type} = $types{$type};
+        }
+        else
+        {
+            delete($self->{CB}->{Mess}->{$type});
+        }
+    }
+}
+
+
+###############################################################################
+#
+# SetXPathCallBacks - define callbacks for packets based on XPath.
+#
+###############################################################################
+sub SetXPathCallBacks
+{ 
+    my $self = shift;
+    my (%xpaths) = @_;
+
+    foreach my $xpath (keys(%xpaths))
+    {
+        $self->{DEBUG}->Log1("SetXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
+        $self->{CB}->{XPath}->{$xpath}->{$xpaths{$xpath}} = $xpaths{$xpath};
+    }
+}
+
+
+###############################################################################
+#
+# RemoveXPathCallBacks - remove callbacks for packets based on XPath.
+#
+###############################################################################
+sub RemoveXPathCallBacks
+{
+    my $self = shift;
+    my (%xpaths) = @_;
+
+    foreach my $xpath (keys(%xpaths))
+    {
+        $self->{DEBUG}->Log1("RemoveXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
+        delete($self->{CB}->{XPath}->{$xpath}->{$xpaths{$xpath}});
+        delete($self->{CB}->{XPath}->{$xpath})
+            if (scalar(keys(%{$self->{CB}->{XPath}->{$xpath}})) == 0);
+        delete($self->{CB}->{XPath})
+            if (scalar(keys(%{$self->{CB}->{XPath}})) == 0);
+    }
+}
+
+
+###############################################################################
+#
+# SetDirectXPathCallBacks - define callbacks for packets based on XPath.
+#
+###############################################################################
+sub SetDirectXPathCallBacks
+{ 
+    my $self = shift;
+    my (%xpaths) = @_;
+
+    foreach my $xpath (keys(%xpaths))
+    {
+        $self->{DEBUG}->Log1("SetDirectXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
+        $self->{CB}->{DirectXPath}->{$xpath}->{$xpaths{$xpath}} = $xpaths{$xpath};
+    }
+}
+
+
+###############################################################################
+#
+# RemoveDirectXPathCallBacks - remove callbacks for packets based on XPath.
+#
+###############################################################################
+sub RemoveDirectXPathCallBacks
+{
+    my $self = shift;
+    my (%xpaths) = @_;
+
+    foreach my $xpath (keys(%xpaths))
+    {
+        $self->{DEBUG}->Log1("RemoveDirectXPathCallBacks: xpath($xpath) func($xpaths{$xpath})");
+        delete($self->{CB}->{DirectXPath}->{$xpath}->{$xpaths{$xpath}});
+        delete($self->{CB}->{DirectXPath}->{$xpath})
+            if (scalar(keys(%{$self->{CB}->{DirectXPath}->{$xpath}})) == 0);
+        delete($self->{CB}->{DirectXPath})
+            if (scalar(keys(%{$self->{CB}->{DirectXPath}})) == 0);
+    }
+}
+
+
+###############################################################################
+#
+# Send - Takes either XML or a Net::XMPP::xxxx object and sends that
+#        packet to the server.
+#
+###############################################################################
+sub Send
+{
+    my $self = shift;
+    my $object = shift;
+    my $ignoreActivity = shift;
+    $ignoreActivity = 0 unless defined($ignoreActivity);
+
+    if (ref($object) eq "")
+    {
+        $self->SendXML($object,$ignoreActivity);
+    }
+    else
+    {
+        $self->SendXML($object->GetXML(),$ignoreActivity);
+    }
+}
+
+
+###############################################################################
+#
+# SendXML - Sends the XML packet to the server
+#
+###############################################################################
+sub SendXML
+{
+    my $self = shift;
+    my $xml = shift;
+    my $ignoreActivity = shift;
+    $ignoreActivity = 0 unless defined($ignoreActivity);
+
+    $self->{DEBUG}->Log1("SendXML: sent($xml)");
+    &{$self->{CB}->{send}}($self->GetStreamID(),$xml) if exists($self->{CB}->{send});
+    $self->{STREAM}->IgnoreActivity($self->GetStreamID(),$ignoreActivity);
+    $self->{STREAM}->Send($self->GetStreamID(),$xml);
+    $self->{STREAM}->IgnoreActivity($self->GetStreamID(),0);
+}
+
+
+###############################################################################
+#
+# SendWithID - Take either XML or a Net::XMPP::xxxx object and send it
+#              with the next available ID number.  Then return that ID so
+#              the client can track it.
+#
+###############################################################################
+sub SendWithID
+{
+    my $self = shift;
+    my ($object) = @_;
+
+    #--------------------------------------------------------------------------
+    # Take the current XML stream and insert an id attrib at the top level.
+    #--------------------------------------------------------------------------
+    my $id = $self->UniqueID();
+
+    $self->{DEBUG}->Log1("SendWithID: id($id)");
+
+    my $xml;
+    if (ref($object) eq "")
+    {
+        $self->{DEBUG}->Log1("SendWithID: in($object)");
+        $xml = $object;
+        $xml =~ s/^(\<[^\>]+)(\>)/$1 id\=\'$id\'$2/;
+        my ($tag) = ($xml =~ /^\<(\S+)\s/);
+        $self->RegisterID($tag,$id);
+    }
+    else
+    {
+        $self->{DEBUG}->Log1("SendWithID: in(",$object->GetXML(),")");
+        $object->SetID($id);
+        $xml = $object->GetXML();
+        $self->RegisterID($object->GetTag(),$id);
+    }
+    $self->{DEBUG}->Log1("SendWithID: out($xml)");
+
+    #--------------------------------------------------------------------------
+    # Send the new XML string.
+    #--------------------------------------------------------------------------
+    $self->SendXML($xml);
+
+    #--------------------------------------------------------------------------
+    # Return the ID number we just assigned.
+    #--------------------------------------------------------------------------
+    return $id;
+}
+
+
+###############################################################################
+#
+# UniqueID - Increment and return a new unique ID.
+#
+###############################################################################
+sub UniqueID
+{
+    my $self = shift;
+
+    my $id_num = $self->{RCVDB}->{currentID};
+
+    $self->{RCVDB}->{currentID}++;
+
+    return "netjabber-$id_num";
+}
+
+
+###############################################################################
+#
+# SendAndReceiveWithID - Take either XML or a Net::XMPP::xxxxx object and
+#                        send it with the next ID.  Then wait for that ID
+#                        to come back and return the response in a
+#                        Net::XMPP::xxxx object.
+#
+###############################################################################
+sub SendAndReceiveWithID
+{
+    my $self = shift;
+    my ($object,$timeout) = @_;
+    &{$self->{CB}->{startwait}}() if exists($self->{CB}->{startwait});
+    $self->{DEBUG}->Log1("SendAndReceiveWithID: object($object)");
+    my $id = $self->SendWithID($object);
+    $self->{DEBUG}->Log1("SendAndReceiveWithID: sent with id($id)");
+    my $packet = $self->WaitForID($id,$timeout);
+    &{$self->{CB}->{endwait}}() if exists($self->{CB}->{endwait});
+    return $packet;
+}
+
+
+###############################################################################
+#
+# ReceivedID - returns 1 if a packet with the ID has been received, or 0
+#              if it has not.
+#
+###############################################################################
+sub ReceivedID
+{
+    my $self = shift;
+    my ($id) = @_;
+
+    $self->{DEBUG}->Log1("ReceivedID: id($id)");
+    return 1 if exists($self->{RCVDB}->{$id});
+    $self->{DEBUG}->Log1("ReceivedID: nope...");
+    return 0;
+}
+
+
+###############################################################################
+#
+# GetID - Return the Net::XMPP::xxxxx object that is stored in the LIST
+#         that matches the ID if that ID exists.  Otherwise return 0.
+#
+###############################################################################
+sub GetID
+{
+    my $self = shift;
+    my ($id) = @_;
+
+    $self->{DEBUG}->Log1("GetID: id($id)");
+    return $self->{RCVDB}->{$id} if $self->ReceivedID($id);
+    $self->{DEBUG}->Log1("GetID: haven't gotten that id yet...");
+    return 0;
+}
+
+
+###############################################################################
+#
+# CleanID - Delete the list entry for this id since we don't want a leak.
+#
+###############################################################################
+sub CleanID
+{
+    my $self = shift;
+    my ($id) = @_;
+
+    $self->{DEBUG}->Log1("CleanID: id($id)");
+    delete($self->{RCVDB}->{$id});
+}
+
+
+###############################################################################
+#
+# WaitForID - Keep looping and calling Process(1) to poll every second
+#             until the response from the server occurs.
+#
+###############################################################################
+sub WaitForID
+{
+    my $self = shift;
+    my ($id,$timeout) = @_;
+    $timeout = "300" unless defined($timeout);
+
+    $self->{DEBUG}->Log1("WaitForID: id($id)");
+    my $endTime = time + $timeout;
+    while(!$self->ReceivedID($id) && ($endTime >= time))
+    {
+        $self->{DEBUG}->Log1("WaitForID: haven't gotten it yet... let's wait for more packets");
+        return unless (defined($self->Process(1)));
+        &{$self->{CB}->{update}}() if exists($self->{CB}->{update});
+    }
+    if (!$self->ReceivedID($id))
+    {
+        $self->TimeoutID($id);
+        $self->{DEBUG}->Log1("WaitForID: timed out...");
+        return;
+    }
+    else
+    {
+        $self->{DEBUG}->Log1("WaitForID: we got it!");
+        my $packet = $self->GetID($id);
+        $self->CleanID($id);
+        return $packet;
+    }
+}
+
+
+###############################################################################
+#
+# GotID - Callback to store the Net::XMPP::xxxxx object in the LIST at
+#         the ID index.  This is a private helper function.
+#
+###############################################################################
+sub GotID
+{
+    my $self = shift;
+    my ($id,$object) = @_;
+
+    $self->{DEBUG}->Log1("GotID: id($id) xml(",$object->GetXML(),")");
+    $self->{RCVDB}->{$id} = $object;
+}
+
+
+###############################################################################
+#
+# CheckID - Checks the ID registry if this tag and ID have been registered.
+#           0 = no, 1 = yes
+#
+###############################################################################
+sub CheckID
+{
+    my $self = shift;
+    my ($tag,$id) = @_;
+    $id = "" unless defined($id);
+
+    $self->{DEBUG}->Log1("CheckID: tag($tag) id($id)");
+    return 0 if ($id eq "");
+    $self->{DEBUG}->Log1("CheckID: we have that here somewhere...");
+    return exists($self->{IDRegistry}->{$tag}->{$id});
+}
+
+
+###############################################################################
+#
+# TimeoutID - Timeout the tag and ID in the registry so that the CallBack
+#             can know what to put in the ID list and what to pass on.
+#
+###############################################################################
+sub TimeoutID
+{
+    my $self = shift;
+    my ($id) = @_;
+
+    $self->{DEBUG}->Log1("TimeoutID: id($id)");
+    $self->{RCVDB}->{$id} = 0;
+}
+
+
+###############################################################################
+#
+# TimedOutID - Timeout the tag and ID in the registry so that the CallBack
+#             can know what to put in the ID list and what to pass on.
+#
+###############################################################################
+sub TimedOutID
+{
+    my $self = shift;
+    my ($id) = @_;
+
+    return (exists($self->{RCVDB}->{$id}) && ($self->{RCVDB}->{$id} == 0));
+}
+
+
+###############################################################################
+#
+# RegisterID - Register the tag and ID in the registry so that the CallBack
+#              can know what to put in the ID list and what to pass on.
+#
+###############################################################################
+sub RegisterID
+{
+    my $self = shift;
+    my ($tag,$id) = @_;
+
+    $self->{DEBUG}->Log1("RegisterID: tag($tag) id($id)");
+    $self->{IDRegistry}->{$tag}->{$id} = 1;
+}
+
+
+###############################################################################
+#
+# DeregisterID - Delete the tag and ID in the registry so that the CallBack
+#                can knows that it has been received.
+#
+###############################################################################
+sub DeregisterID
+{
+    my $self = shift;
+    my ($tag,$id) = @_;
+
+    $self->{DEBUG}->Log1("DeregisterID: tag($tag) id($id)");
+    delete($self->{IDRegistry}->{$tag}->{$id});
+}
+
+
+###############################################################################
+#
+# AddNamespace - Add a custom namespace into the mix.
+#
+###############################################################################
+sub AddNamespace
+{
+    my $self = shift;
+    &Net::XMPP::Namespaces::add_ns(@_);
+}
+
+
+###############################################################################
+#
+# MessageSend - Takes the same hash that Net::XMPP::Message->SetMessage
+#               takes and sends the message to the server.
+#
+###############################################################################
+sub MessageSend
+{
+    my $self = shift;
+
+    my $mess = $self->_message();
+    $mess->SetMessage(@_);
+    $self->Send($mess);
+}
+
+
+##############################################################################
+#
+# PresenceDB - initialize the module to use the presence database
+#
+##############################################################################
+sub PresenceDB
+{
+    my $self = shift;
+
+    $self->SetXPathCallBacks('/presence'=>sub{ shift; $self->PresenceDBParse(@_) });
+}
+
+
+###############################################################################
+#
+# PresenceDBParse - adds the presence information to the Presence DB so
+#                   you can keep track of the current state of the JID and
+#                   all of it's resources.
+#
+###############################################################################
+sub PresenceDBParse
+{
+    my $self = shift;
+    my ($presence) = @_;
+
+    $self->{DEBUG}->Log4("PresenceDBParse: pres(",$presence->GetXML(),")");
+    
+    my $type = $presence->GetType();
+    $type = "" unless defined($type);
+    return $presence unless (($type eq "") ||
+                 ($type eq "available") ||
+                 ($type eq "unavailable"));
+
+    my $fromJID = $presence->GetFrom("jid");
+    my $fromID = $fromJID->GetJID();
+    $fromID = "" unless defined($fromID);
+    my $resource = $fromJID->GetResource();
+    $resource = " " unless ($resource ne "");
+    my $priority = $presence->GetPriority();
+    $priority = 0 unless defined($priority);
+
+    $self->{DEBUG}->Log1("PresenceDBParse: fromJID(",$fromJID->GetJID("full"),") resource($resource) priority($priority) type($type)");
+    $self->{DEBUG}->Log2("PresenceDBParse: xml(",$presence->GetXML(),")");
+
+    if (exists($self->{PRESENCEDB}->{$fromID}))
+    {
+        my $oldPriority = $self->{PRESENCEDB}->{$fromID}->{resources}->{$resource};
+        $oldPriority = "" unless defined($oldPriority);
+
+        my $loc = 0;
+        foreach my $index (0..$#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}})
+        {
+            $loc = $index
+               if ($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}->[$index]->{resource} eq $resource);
+        }
+        splice(@{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}},$loc,1);
+        delete($self->{PRESENCEDB}->{$fromID}->{resources}->{$resource});
+        delete($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority})
+            if (exists($self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}) &&
+        ($#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$oldPriority}} == -1));
+        delete($self->{PRESENCEDB}->{$fromID})
+            if (scalar(keys(%{$self->{PRESENCEDB}->{$fromID}})) == 0);
+
+        $self->{DEBUG}->Log1("PresenceDBParse: remove ",$fromJID->GetJID("full")," from the DB");
+    }
+
+    if (($type eq "") || ($type eq "available"))
+    {
+        my $loc = -1;
+        foreach my $index (0..$#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}}) {
+            $loc = $index
+                if ($self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$index]->{resource} eq $resource);
+        }
+        $loc = $#{$self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}}+1
+            if ($loc == -1);
+        $self->{PRESENCEDB}->{$fromID}->{resources}->{$resource} = $priority;
+        $self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$loc]->{presence} =
+            $presence;
+        $self->{PRESENCEDB}->{$fromID}->{priorities}->{$priority}->[$loc]->{resource} =
+            $resource;
+
+        $self->{DEBUG}->Log1("PresenceDBParse: add ",$fromJID->GetJID("full")," to the DB");
+    }
+
+    my $currentPresence = $self->PresenceDBQuery($fromJID);
+    return (defined($currentPresence) ? $currentPresence : $presence);
+}
+
+
+###############################################################################
+#
+# PresenceDBDelete - delete the JID from the DB completely.
+#
+###############################################################################
+sub PresenceDBDelete
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    my $indexJID = $jid;
+    $indexJID = $jid->GetJID() if $jid->isa("Net::XMPP::JID");
+
+    return if !exists($self->{PRESENCEDB}->{$indexJID});
+    delete($self->{PRESENCEDB}->{$indexJID});
+    $self->{DEBUG}->Log1("PresenceDBDelete: delete ",$indexJID," from the DB");
+}
+
+
+###############################################################################
+#
+# PresenceDBClear - delete all of the JIDs from the DB completely.
+#
+###############################################################################
+sub PresenceDBClear
+{
+    my $self = shift;
+
+    $self->{DEBUG}->Log1("PresenceDBClear: clearing the database");
+    foreach my $indexJID (keys(%{$self->{PRESENCEDB}}))
+    {
+        $self->{DEBUG}->Log3("PresenceDBClear: deleting ",$indexJID," from the DB");
+        delete($self->{PRESENCEDB}->{$indexJID});
+    }
+    $self->{DEBUG}->Log3("PresenceDBClear: database is empty");
+}
+
+
+###############################################################################
+#
+# PresenceDBQuery - retrieve the last Net::XMPP::Presence received with
+#                  the highest priority.
+#
+###############################################################################
+sub PresenceDBQuery
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    my $indexJID = $jid;
+    $indexJID = $jid->GetJID() if $jid->isa("Net::XMPP::JID");
+
+    return if !exists($self->{PRESENCEDB}->{$indexJID});
+    return if (scalar(keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}})) == 0);
+
+    my $highPriority =
+        (sort {$b cmp $a} keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}}))[0];
+
+    return $self->{PRESENCEDB}->{$indexJID}->{priorities}->{$highPriority}->[0]->{presence};
+}
+
+
+###############################################################################
+#
+# PresenceDBResources - returns a list of the resources from highest
+#                       priority to lowest.
+#
+###############################################################################
+sub PresenceDBResources
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    my $indexJID = $jid;
+    $indexJID = $jid->GetJID() if $jid->isa("Net::XMPP::JID");
+
+    my @resources;
+
+    return if !exists($self->{PRESENCEDB}->{$indexJID});
+
+    foreach my $priority (sort {$b cmp $a} keys(%{$self->{PRESENCEDB}->{$indexJID}->{priorities}}))
+    {
+        foreach my $index (0..$#{$self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}})
+        {
+            next if ($self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}->[$index]->{resource} eq " ");
+            push(@resources,$self->{PRESENCEDB}->{$indexJID}->{priorities}->{$priority}->[$index]->{resource});
+        }
+    }
+    return @resources;
+}
+
+
+###############################################################################
+#
+# PresenceSend - Sends a presence tag to announce your availability
+#
+###############################################################################
+sub PresenceSend
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    $args{ignoreactivity} = 0 unless exists($args{ignoreactivity});
+    my $ignoreActivity = delete($args{ignoreactivity});
+
+    my $presence = $self->_presence();
+
+    $presence->SetPresence(%args);
+    $self->Send($presence,$ignoreActivity);
+    return $presence;
+}
+
+
+###############################################################################
+#
+# PresenceProbe - Sends a presence probe to the server
+#
+###############################################################################
+sub PresenceProbe
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+    delete($args{type});
+
+    my $presence = $self->_presence();
+    $presence->SetPresence(type=>"probe",
+                           %args);
+    $self->Send($presence);
+}
+
+
+###############################################################################
+#
+# Subscription - Sends a presence tag to perform the subscription on the
+#                specified JID.
+#
+###############################################################################
+sub Subscription
+{
+    my $self = shift;
+
+    my $presence = $self->_presence();
+    $presence->SetPresence(@_);
+    $self->Send($presence);
+}
+
+
+###############################################################################
+#
+# AuthSend - This is a self contained function to send a login iq tag with
+#            an id.  Then wait for a reply what the same id to come back
+#            and tell the caller what the result was.
+#
+###############################################################################
+sub AuthSend
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    carp("AuthSend requires a username arguement")
+        unless exists($args{username});
+    carp("AuthSend requires a password arguement")
+        unless exists($args{password});
+
+    if($self->{STREAM}->GetStreamFeature($self->GetStreamID(),"xmpp-sasl"))
+    {
+        return $self->AuthSASL(%args);
+    }
+
+    return $self->AuthIQAuth(%args);
+}
+
+
+###############################################################################
+#
+# AuthIQAuth - Try and auth using jabber:iq:auth, the old Jabber way of
+#              authenticating.
+#
+###############################################################################
+sub AuthIQAuth
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    $self->{DEBUG}->Log1("AuthIQAuth: old school auth");
+
+    carp("AuthIQAuth requires a resource arguement")
+        unless exists($args{resource});
+
+    my $authType = "digest";
+    my $token;
+    my $sequence;
+
+    #--------------------------------------------------------------------------
+    # First let's ask the sever what all is available in terms of auth types.
+    # If we get an error, then all we can do is digest or plain.
+    #--------------------------------------------------------------------------
+    my $iqAuth = $self->_iq();
+    $iqAuth->SetIQ(type=>"get");
+    my $iqAuthQuery = $iqAuth->NewChild("jabber:iq:auth");
+    $iqAuthQuery->SetUsername($args{username});
+    $iqAuth = $self->SendAndReceiveWithID($iqAuth);
+
+    return unless defined($iqAuth);
+    return ( $iqAuth->GetErrorCode() , $iqAuth->GetError() )
+        if ($iqAuth->GetType() eq "error");
+
+    if ($iqAuth->GetType() eq "error")
+    {
+        $authType = "digest";
+    }
+    else
+    {
+        $iqAuthQuery = $iqAuth->GetChild();
+        $authType = "plain" if $iqAuthQuery->DefinedPassword();
+        $authType = "digest" if $iqAuthQuery->DefinedDigest();
+        $authType = "zerok" if ($iqAuthQuery->DefinedSequence() &&
+                    $iqAuthQuery->DefinedToken());
+        $token = $iqAuthQuery->GetToken() if ($authType eq "zerok");
+        $sequence = $iqAuthQuery->GetSequence() if ($authType eq "zerok");
+    }
+
+    $self->{DEBUG}->Log1("AuthIQAuth: authType($authType)");
+
+    delete($args{digest});
+    delete($args{type});
+    my $password = delete $args{password};
+    if (ref($password) eq 'CODE')
+    {
+        $password = $password->();
+    }
+
+    #--------------------------------------------------------------------------
+    # 0k authenticaion (http://core.jabber.org/0k.html)
+    #
+    # Tell the server that we want to connect this way, the server sends back
+    # a token and a sequence number.  We take that token + the password and
+    # SHA1 it.  Then we SHA1 it sequence number more times and send that hash.
+    # The server SHA1s that hash one more time and compares it to the hash it
+    # stored last time.  IF they match, we are in and it stores the hash we sent
+    # for the next time and decreases the sequence number, else, no go.
+    #--------------------------------------------------------------------------
+    if ($authType eq "zerok")
+    {
+        my $hashA = Digest::SHA1::sha1_hex($password);
+        $args{hash} = Digest::SHA1::sha1_hex($hashA.$token);
+
+        for (1..$sequence)
+        {
+            $args{hash} = Digest::SHA1::sha1_hex($args{hash});
+        }
+    }
+
+    #--------------------------------------------------------------------------
+    # If we have access to the SHA-1 digest algorithm then let's use it.
+    # Remove the password from the hash, create the digest, and put the
+    # digest in the hash instead.
+    #
+    # Note: Concat the Session ID and the password and then digest that
+    # string to get the server to accept the digest.
+    #--------------------------------------------------------------------------
+    if ($authType eq "digest")
+    {
+        $args{digest} = Digest::SHA1::sha1_hex($self->GetStreamID().$password);
+    }
+
+    #--------------------------------------------------------------------------
+    # Create a Net::XMPP::IQ object to send to the server
+    #--------------------------------------------------------------------------
+    my $iqLogin = $self->_iq();
+    $iqLogin->SetIQ(type=>"set");
+    my $iqLoginQuery = $iqLogin->NewChild("jabber:iq:auth");
+    $iqLoginQuery->SetAuth(%args);
+
+    #--------------------------------------------------------------------------
+    # Send the IQ with the next available ID and wait for a reply with that
+    # id to be received.  Then grab the IQ reply.
+    #--------------------------------------------------------------------------
+    $iqLogin = $self->SendAndReceiveWithID($iqLogin);
+
+    #--------------------------------------------------------------------------
+    # From the reply IQ determine if we were successful or not.  If yes then
+    # return "".  If no then return error string from the reply.
+    #--------------------------------------------------------------------------
+    $password =~ tr/\0-\377/x/;
+    return unless defined($iqLogin);
+    return ( $iqLogin->GetErrorCode() , $iqLogin->GetError() )
+        if ($iqLogin->GetType() eq "error");
+
+    $self->{DEBUG}->Log1("AuthIQAuth: we authed!");
+
+    return ("ok","");
+}
+
+
+###############################################################################
+#
+# AuthSASL - Try and auth using SASL, the XMPP preferred way of authenticating.
+#
+###############################################################################
+sub AuthSASL
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    $self->{DEBUG}->Log1("AuthSASL: shiney new auth");
+
+    carp("AuthSASL requires a username arguement")
+        unless exists($args{username});
+    carp("AuthSASL requires a password arguement")
+        unless exists($args{password});
+
+    $args{resource} = "" unless exists($args{resource});
+
+    #-------------------------------------------------------------------------
+    # Create the SASLClient on our end
+    #-------------------------------------------------------------------------
+    my $sid = $self->{SESSION}->{id};
+    my $status =
+        $self->{STREAM}->SASLClient($sid,
+                                    $args{username},
+                                    $args{password}
+                                   );
+
+    $args{timeout} = "120" unless exists($args{timeout});
+
+    #-------------------------------------------------------------------------
+    # While we haven't timed out, keep waiting for the SASLClient to finish
+    #-------------------------------------------------------------------------
+    my $endTime = time + $args{timeout};
+    while(!$self->{STREAM}->SASLClientDone($sid) && ($endTime >= time))
+    {
+        $self->{DEBUG}->Log1("AuthSASL: haven't authed yet... let's wait.");
+        return unless (defined($self->Process(1)));
+        &{$self->{CB}->{update}}() if exists($self->{CB}->{update});
+    }
+    
+    #-------------------------------------------------------------------------
+    # The loop finished... but was it done?
+    #-------------------------------------------------------------------------
+    if (!$self->{STREAM}->SASLClientDone($sid))
+    {
+        $self->{DEBUG}->Log1("AuthSASL: timed out...");
+        return( "system","SASL timed out authenticating");
+    }
+    
+    #-------------------------------------------------------------------------
+    # Ok, it was done... but did we auth?
+    #-------------------------------------------------------------------------
+    if (!$self->{STREAM}->SASLClientAuthed($sid))
+    {
+        $self->{DEBUG}->Log1("AuthSASL: Authentication failed.");
+        return ( "error", $self->{STREAM}->SASLClientError($sid));
+    }
+    
+    #-------------------------------------------------------------------------
+    # Phew... Restart the <stream:stream> per XMPP
+    #-------------------------------------------------------------------------
+    $self->{DEBUG}->Log1("AuthSASL: We authed!");
+    $self->{SESSION} = $self->{STREAM}->OpenStream($sid);
+    $sid = $self->{SESSION}->{id};
+    
+    $self->{DEBUG}->Log1("AuthSASL: We got a new session. sid($sid)");
+
+    #-------------------------------------------------------------------------
+    # Look in the new set of <stream:feature/>s and see if xmpp-bind was
+    # offered.
+    #-------------------------------------------------------------------------
+    my $bind = $self->{STREAM}->GetStreamFeature($sid,"xmpp-bind");
+    if ($bind)
+    {
+        $self->{DEBUG}->Log1("AuthSASL: Binding to resource");
+        $self->BindResource($args{resource});
+    }
+
+    #-------------------------------------------------------------------------
+    # Look in the new set of <stream:feature/>s and see if xmpp-session was
+    # offered.
+    #-------------------------------------------------------------------------
+    my $session = $self->{STREAM}->GetStreamFeature($sid,"xmpp-session");
+    if ($session)
+    {
+        $self->{DEBUG}->Log1("AuthSASL: Starting session");
+        $self->StartSession();
+    }
+
+    return ("ok","");
+}
+
+
+##############################################################################
+#
+# BindResource - bind to a resource
+#
+##############################################################################
+sub BindResource
+{
+    my $self = shift;
+    my $resource = shift;
+
+    $self->{DEBUG}->Log2("BindResource: Binding to resource");
+    my $iq = $self->_iq();
+
+    $iq->SetIQ(type=>"set");
+    my $bind = $iq->NewChild(&ConstXMLNS("xmpp-bind"));
+    
+    if (defined($resource) && ($resource ne ""))
+    {
+        $self->{DEBUG}->Log2("BindResource: resource($resource)");
+        $bind->SetBind(resource=>$resource);
+    }
+
+    my $result = $self->SendAndReceiveWithID($iq);
+}
+
+
+##############################################################################
+#
+# StartSession - Initialize a session
+#
+##############################################################################
+sub StartSession
+{
+    my $self = shift;
+
+    my $iq = $self->_iq();
+
+    $iq->SetIQ(type=>"set");
+    my $session = $iq->NewChild(&ConstXMLNS("xmpp-session"));
+    
+    my $result = $self->SendAndReceiveWithID($iq);
+}
+
+
+##############################################################################
+#
+# PrivacyLists - Initialize a Net::XMPP::PrivacyLists object and return it.
+#
+##############################################################################
+sub PrivacyLists
+{
+    my $self = shift;
+
+    return Net::XMPP::PrivacyLists->new(connection=>$self);
+}
+
+
+##############################################################################
+#
+# PrivacyListsGet - Sends an empty IQ to the server to request that the user's
+#                   Privacy Lists be sent to them.  Returns the iq packet
+#                   of the result.
+#
+##############################################################################
+sub PrivacyListsGet
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"get");
+    my $query = $iq->NewChild("jabber:iq:privacy");
+
+    if (exists($args{list}))
+    {
+        $query->AddList(name=>$args{list});
+    }
+
+    $iq = $self->SendAndReceiveWithID($iq);
+    return unless defined($iq);
+
+    return $iq;
+}
+
+
+##############################################################################
+#
+# PrivacyListsRequest - Sends an empty IQ to the server to request that the
+#                       user's privacy lists be sent to them, and return to
+#                       let the user's program handle parsing the return packet.
+#
+##############################################################################
+sub PrivacyListsRequest
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+    
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"get");
+    my $query = $iq->NewChild("jabber:iq:privacy");
+
+    if (exists($args{list}))
+    {
+        $query->AddList(name=>$args{list});
+    }
+
+    $self->Send($iq);
+}
+
+
+##############################################################################
+#
+# PrivacyListsSet - Sends an empty IQ to the server to request that the
+#                       user's privacy lists be sent to them, and return to
+#                       let the user's program handle parsing the return packet.
+#
+##############################################################################
+sub PrivacyListsSet
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+    
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"set");
+    my $query = $iq->NewChild("jabber:iq:privacy");
+
+    #XXX error check that there is a list
+    my $list = $query->AddList(name=>$args{list});
+
+    foreach my $item (@{$args{items}})
+    {
+        $list->AddItem(%{$item});
+    }
+
+    $iq = $self->SendAndReceiveWithID($iq);
+    return unless defined($iq);
+
+    return if $iq->DefinedError();
+
+    return 1;
+}
+
+
+###############################################################################
+#
+# RegisterRequest - This is a self contained function to send an iq tag
+#                   an id that requests the target address to send back
+#                   the required fields.  It waits for a reply what the
+#                   same id to come back and tell the caller what the
+#                   fields are.
+#
+###############################################################################
+sub RegisterRequest
+{
+    my $self = shift;
+    my %args;
+    $args{mode} = "block";
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $timeout = exists($args{timeout}) ? delete($args{timeout}) : undef;
+
+    #--------------------------------------------------------------------------
+    # Create a Net::XMPP::IQ object to send to the server
+    #--------------------------------------------------------------------------
+    my $iq = $self->_iq();
+    $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
+    $iq->SetIQ(type=>"get");
+    my $query = $iq->NewChild("jabber:iq:register");
+
+    #--------------------------------------------------------------------------
+    # Send the IQ with the next available ID and wait for a reply with that
+    # id to be received.  Then grab the IQ reply.
+    #--------------------------------------------------------------------------
+    if ($args{mode} eq "passthru")
+    {
+        my $id = $self->UniqueID();
+        $iq->SetIQ(id=>$id);
+        $self->Send($iq);
+        return $id;
+    }
+    
+    return $self->SendWithID($iq) if ($args{mode} eq "nonblock");
+
+    $iq = $self->SendAndReceiveWithID($iq,$timeout);
+
+    #--------------------------------------------------------------------------
+    # Check if there was an error.
+    #--------------------------------------------------------------------------
+    return unless defined($iq);
+    if ($iq->GetType() eq "error")
+    {
+        $self->SetErrorCode($iq->GetErrorCode().": ".$iq->GetError());
+        return;
+    }
+
+    my %register;
+    #--------------------------------------------------------------------------
+    # From the reply IQ determine what fields are required and send a hash
+    # back with the fields and any values that are already defined (like key)
+    #--------------------------------------------------------------------------
+    $query = $iq->GetChild();
+    $register{fields} = { $query->GetRegister() };
+
+    return %register;
+}
+
+
+###############################################################################
+#
+# RegisterSend - This is a self contained function to send a registration
+#                iq tag with an id.  Then wait for a reply what the same
+#                id to come back and tell the caller what the result was.
+#
+###############################################################################
+sub RegisterSend
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    #--------------------------------------------------------------------------
+    # Create a Net::XMPP::IQ object to send to the server
+    #--------------------------------------------------------------------------
+    my $iq = $self->_iq();
+    $iq->SetIQ(to=>delete($args{to})) if exists($args{to});
+    $iq->SetIQ(type=>"set");
+    my $iqRegister = $iq->NewChild("jabber:iq:register");
+    $iqRegister->SetRegister(%args);
+
+    #--------------------------------------------------------------------------
+    # Send the IQ with the next available ID and wait for a reply with that
+    # id to be received.  Then grab the IQ reply.
+    #--------------------------------------------------------------------------
+    $iq = $self->SendAndReceiveWithID($iq);
+
+    #--------------------------------------------------------------------------
+    # From the reply IQ determine if we were successful or not.  If yes then
+    # return "".  If no then return error string from the reply.
+    #--------------------------------------------------------------------------
+    return unless defined($iq);
+    return ( $iq->GetErrorCode() , $iq->GetError() )
+        if ($iq->GetType() eq "error");
+    return ("ok","");
+}
+
+
+##############################################################################
+#
+# RosterAdd - Takes the Jabber ID of the user to add to their Roster and
+#             sends the IQ packet to the server.
+#
+##############################################################################
+sub RosterAdd
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"set");
+    my $roster = $iq->NewChild("jabber:iq:roster");
+    my $item = $roster->AddItem();
+    $item->SetItem(%args);
+
+    $self->{DEBUG}->Log1("RosterAdd: xml(",$iq->GetXML(),")");
+    $self->Send($iq);
+}
+
+
+##############################################################################
+#
+# RosterAdd - Takes the Jabber ID of the user to remove from their Roster
+#             and sends the IQ packet to the server.
+#
+##############################################################################
+sub RosterRemove
+{
+    my $self = shift;
+    my %args;
+    while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+    delete($args{subscription});
+
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"set");
+    my $roster = $iq->NewChild("jabber:iq:roster");
+    my $item = $roster->AddItem();
+    $item->SetItem(%args,
+                   subscription=>"remove");
+    $self->Send($iq);
+}
+
+
+##############################################################################
+#
+# RosterParse - Returns a hash of roster items.
+#
+##############################################################################
+sub RosterParse
+{
+    my $self = shift;
+    my($iq) = @_;
+
+    my %roster;
+    my $query = $iq->GetChild("jabber:iq:roster");
+
+    if (defined($query)) #$query->GetXMLNS() eq "jabber:iq:roster")
+    {
+        my @items = $query->GetItems();
+
+        foreach my $item (@items)
+        {
+            my $jid = $item->GetJID();
+            $roster{$jid}->{name} = $item->GetName();
+            $roster{$jid}->{subscription} = $item->GetSubscription();
+            $roster{$jid}->{ask} = $item->GetAsk();
+            $roster{$jid}->{groups} = [ $item->GetGroup() ];
+        }
+    }
+
+    return %roster;
+}
+
+
+##############################################################################
+#
+# RosterGet - Sends an empty IQ to the server to request that the user's
+#             Roster be sent to them.  Returns a hash of roster items.
+#
+##############################################################################
+sub RosterGet
+{
+    my $self = shift;
+
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"get");
+    my $query = $iq->NewChild("jabber:iq:roster");
+
+    $iq = $self->SendAndReceiveWithID($iq);
+
+    return unless defined($iq);
+
+    return $self->RosterParse($iq);
+}
+
+
+##############################################################################
+#
+# RosterRequest - Sends an empty IQ to the server to request that the user's
+#                 Roster be sent to them, and return to let the user's program
+#                 handle parsing the return packet.
+#
+##############################################################################
+sub RosterRequest
+{
+    my $self = shift;
+
+    my $iq = $self->_iq();
+    $iq->SetIQ(type=>"get");
+    my $query = $iq->NewChild("jabber:iq:roster");
+
+    $self->Send($iq);
+}
+
+
+##############################################################################
+#
+# Roster - Initialize a Net::XMPP::Roster object and return it.
+#
+##############################################################################
+sub Roster
+{
+    my $self = shift;
+
+    return Net::XMPP::Roster->new(connection=>$self);
+}
+
+
+##############################################################################
+#
+# RosterDB - initialize the module to use the roster database
+#
+##############################################################################
+sub RosterDB
+{
+    my $self = shift;
+
+    $self->SetXPathCallBacks('/iq[@type="result" or @type="set"]/query[@xmlns="jabber:iq:roster"]'=>sub{ shift; $self->RosterDBParse(@_) });
+}
+
+
+##############################################################################
+#
+# RosterDBAdd - adds the entry to the Roster DB.
+#
+##############################################################################
+sub RosterDBAdd
+{
+    my $self = shift;
+    my ($jid,%item) = @_;
+
+    $self->{ROSTERDB}->{JIDS}->{$jid} = \%item;
+
+    foreach my $group (@{$item{groups}})
+    {
+        $self->{ROSTERDB}->{GROUPS}->{$group}->{$jid} = 1;
+    }
+}
+
+
+###############################################################################
+#
+# RosterDBClear - delete all of the JIDs from the DB completely.
+#
+###############################################################################
+sub RosterDBClear
+{
+    my $self = shift;
+
+    $self->{DEBUG}->Log1("RosterDBClear: clearing the database");
+    foreach my $jid ($self->RosterDBJIDs())
+    {
+        $self->{DEBUG}->Log3("RosterDBClear: deleting ",$jid->GetJID()," from the DB");
+        $self->RosterDBRemove($jid);
+    }
+    $self->{DEBUG}->Log3("RosterDBClear: database is empty");
+}
+
+
+##############################################################################
+#
+# RosterDBExists - allows you to query if the JID exists in the Roster DB.
+#
+##############################################################################
+sub RosterDBExists
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    if ($jid->isa("Net::XMPP::JID"))
+    {
+        $jid = $jid->GetJID();
+    }
+    
+    return unless exists($self->{ROSTERDB});
+    return unless exists($self->{ROSTERDB}->{JIDS});
+    return unless exists($self->{ROSTERDB}->{JIDS}->{$jid});
+    return 1;
+}
+
+
+##############################################################################
+#
+# RosterDBGroupExists - allows you to query if the group exists in the Roster
+#                       DB.
+#
+##############################################################################
+sub RosterDBGroupExists
+{
+    my $self = shift;
+    my ($group) = @_;
+
+    return unless exists($self->{ROSTERDB});
+    return unless exists($self->{ROSTERDB}->{GROUPS});
+    return unless exists($self->{ROSTERDB}->{GROUPS}->{$group});
+    return 1;
+}
+
+
+##############################################################################
+#
+# RosterDBGroupJIDs - returns a list of the current groups in your roster.
+#
+##############################################################################
+sub RosterDBGroupJIDs
+{
+    my $self = shift;
+    my $group = shift;
+
+    return unless $self->RosterDBGroupExists($group);
+    my @jids;
+    foreach my $jid (keys(%{$self->{ROSTERDB}->{GROUPS}->{$group}}))
+    {
+        push(@jids,$self->_jid($jid));
+    }
+    return @jids;
+}
+
+
+##############################################################################
+#
+# RosterDBGroups - returns a list of the current groups in your roster.
+#
+##############################################################################
+sub RosterDBGroups
+{
+    my $self = shift;
+
+    return () unless exists($self->{ROSTERDB}->{GROUPS});
+    return () if (scalar(keys(%{$self->{ROSTERDB}->{GROUPS}})) == 0);
+    return keys(%{$self->{ROSTERDB}->{GROUPS}});
+}
+
+
+##############################################################################
+#
+# RosterDBJIDs - returns a list of all of the JIDs in your roster.
+#
+##############################################################################
+sub RosterDBJIDs
+{
+    my $self = shift;
+    my $group = shift;
+
+    my @jids;
+
+    return () unless exists($self->{ROSTERDB});
+    return () unless exists($self->{ROSTERDB}->{JIDS});
+    foreach my $jid (keys(%{$self->{ROSTERDB}->{JIDS}}))
+    {
+        push(@jids,$self->_jid($jid));
+    }
+    return @jids;
+}
+
+
+##############################################################################
+#
+# RosterDBNonGroupJIDs - returns a list of the JIDs not in a group.
+#
+##############################################################################
+sub RosterDBNonGroupJIDs
+{
+    my $self = shift;
+    my $group = shift;
+
+    my @jids;
+
+    return () unless exists($self->{ROSTERDB});
+    return () unless exists($self->{ROSTERDB}->{JIDS});
+    foreach my $jid (keys(%{$self->{ROSTERDB}->{JIDS}}))
+    {
+        next if (exists($self->{ROSTERDB}->{JIDS}->{$jid}->{groups}) &&
+                 ($#{$self->{ROSTERDB}->{JIDS}->{$jid}->{groups}} > -1));
+
+        push(@jids,$self->_jid($jid));
+    }
+    return @jids;
+}
+
+
+##############################################################################
+#
+# RosterDBParse - takes an iq packet that containsa roster, parses it, and puts
+#                 the roster into the Roster DB.
+#
+##############################################################################
+sub RosterDBParse
+{
+    my $self = shift;
+    my ($iq) = @_;
+
+    #print "RosterDBParse: iq(",$iq->GetXML(),")\n";
+
+    my $type = $iq->GetType();
+    return unless (($type eq "set") || ($type eq "result"));
+
+    my %newroster = $self->RosterParse($iq);
+
+    $self->RosterDBProcessParsed(%newroster);
+}
+
+
+##############################################################################
+#
+# RosterDBProcessParsed - takes a parsed roster and puts it into the Roster DB.
+#
+##############################################################################
+sub RosterDBProcessParsed
+{
+    my $self = shift;
+    my (%roster) = @_;
+
+    foreach my $jid (keys(%roster))
+    {
+        $self->RosterDBRemove($jid);
+
+        if ($roster{$jid}->{subscription} ne "remove")
+        {
+            $self->RosterDBAdd($jid, %{$roster{$jid}} );
+        }
+    }
+}
+
+
+##############################################################################
+#
+# RosterDBQuery - allows you to get one of the pieces of info from the
+#                 Roster DB.
+#
+##############################################################################
+sub RosterDBQuery
+{
+    my $self = shift;
+    my $jid = shift;
+    my $key = shift;
+
+    if ($jid->isa("Net::XMPP::JID"))
+    {
+        $jid = $jid->GetJID();
+    }
+    
+    return unless $self->RosterDBExists($jid);
+    if (defined($key))
+    {
+        return unless exists($self->{ROSTERDB}->{JIDS}->{$jid}->{$key});
+        return $self->{ROSTERDB}->{JIDS}->{$jid}->{$key};
+    }
+    return %{$self->{ROSTERDB}->{JIDS}->{$jid}};
+}                        
+
+
+##############################################################################
+#
+# RosterDBRemove - removes the JID from the Roster DB.
+#
+##############################################################################
+sub RosterDBRemove
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    if ($self->RosterDBExists($jid))
+    {
+        if (defined($self->RosterDBQuery($jid,"groups")))
+        {
+            foreach my $group (@{$self->RosterDBQuery($jid,"groups")})
+            {
+                delete($self->{ROSTERDB}->{GROUPS}->{$group}->{$jid});
+                delete($self->{ROSTERDB}->{GROUPS}->{$group})
+                    if (scalar(keys(%{$self->{ROSTERDB}->{GROUPS}->{$group}})) == 0);
+                delete($self->{ROSTERDB}->{GROUPS})
+                    if (scalar(keys(%{$self->{ROSTERDB}->{GROUPS}})) == 0);
+            }
+        }
+    
+        delete($self->{ROSTERDB}->{JIDS}->{$jid});
+    }
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| TLS Functions
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+##############################################################################
+#
+# TLSInit - Initialize the connection for TLS.
+#
+##############################################################################
+sub TLSInit
+{
+    my $self = shift;
+
+    $TLS_CALLBACK = sub{ $self->ProcessTLSStanza( @_ ) };
+    $self->SetDirectXPathCallBacks('/[@xmlns="'.&ConstXMLNS("xmpp-tls").'"]'=>$TLS_CALLBACK);
+}
+
+
+##############################################################################
+#
+# ProcessTLSStanza - process a TLS based packet.
+#
+##############################################################################
+sub ProcessTLSStanza
+{
+    my $self = shift;
+    my $sid = shift;
+    my $node = shift;
+
+    my $tag = &XML::Stream::XPath($node,"name()");
+
+    if ($tag eq "failure")
+    {
+        $self->TLSClientFailure($node);
+    }
+    
+    if ($tag eq "proceed")
+    {
+        $self->TLSClientProceed($node);
+    }
+}
+
+
+##############################################################################
+#
+# TLSStart - client function to have the socket start TLS.
+#
+##############################################################################
+sub TLSStart
+{
+    my $self = shift;
+    my $timeout = shift;
+    $timeout = 120 unless defined($timeout);
+    $timeout = 120 if ($timeout eq "");
+    
+    $self->TLSSendStartTLS();
+
+    my $endTime = time + $timeout;
+    while(!$self->TLSClientDone() && ($endTime >= time))
+    {
+        $self->Process();
+    }
+
+    if (!$self->TLSClientSecure())
+    {
+        return;
+    }
+
+    $self->RestartStream($timeout);
+}
+
+
+##############################################################################
+#
+# TLSClientProceed - handle a <proceed/> packet.
+#
+##############################################################################
+sub TLSClientProceed
+{
+    my $self = shift;
+    my $node = shift;
+
+    my ($status,$message) = $self->{STREAM}->StartTLS($self->GetStreamID());
+
+    if ($status)
+    {
+        $self->{TLS}->{done} = 1;
+        $self->{TLS}->{secure} = 1;
+    }
+    else
+    {
+        $self->{TLS}->{done} = 1;
+        $self->{TLS}->{error} = $message;
+    }
+    
+    $self->RemoveDirectXPathCallBacks('/[@xmlns="'.&ConstXMLNS("xmpp-tls").'"]'=>$TLS_CALLBACK);
+}
+
+
+##############################################################################
+#
+# TLSClientSecure - return 1 if the socket is secure, 0 otherwise.
+#
+##############################################################################
+sub TLSClientSecure
+{
+    my $self = shift;
+    
+    return $self->{TLS}->{secure};
+}
+
+
+##############################################################################
+#
+# TLSClientDone - return 1 if the TLS process is done
+#
+##############################################################################
+sub TLSClientDone
+{
+    my $self = shift;
+    
+    return $self->{TLS}->{done};
+}
+
+
+##############################################################################
+#
+# TLSClientError - return the TLS error if any
+#
+##############################################################################
+sub TLSClientError
+{
+    my $self = shift;
+    
+    return $self->{TLS}->{error};
+}
+
+
+##############################################################################
+#
+# TLSClientFailure - handle a <failure/>
+#
+##############################################################################
+sub TLSClientFailure
+{
+    my $self = shift;
+    my $node = shift;
+    
+    my $type = &XML::Stream::XPath($node,"*/name()");
+
+    $self->{TLS}->{error} = $type;
+    $self->{TLS}->{done} = 1;
+}
+
+
+##############################################################################
+#
+# TLSSendFailure - Send a <failure/> in the TLS namespace
+#
+##############################################################################
+sub TLSSendFailure
+{
+    my $self = shift;
+    my $type = shift;
+    
+    $self->Send("<failure xmlns='".&ConstXMLNS('xmpp-tls')."'><${type}/></failure>");
+}
+
+
+##############################################################################
+#
+# TLSSendStartTLS - send a <starttls/> in the TLS namespace.
+#
+##############################################################################
+sub TLSSendStartTLS
+{
+    my $self = shift;
+
+    $self->Send("<starttls xmlns='".&ConstXMLNS('xmpp-tls')."'/>");
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| SASL Functions
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+##############################################################################
+#
+# SASLInit - Initialize the connection for SASL.
+#
+##############################################################################
+sub SASLInit
+{
+    my $self = shift;
+
+    $SASL_CALLBACK = sub{ $self->ProcessSASLStanza( @_ ) };
+    $self->SetDirectXPathCallBacks('/[@xmlns="'.&ConstXMLNS("xmpp-sasl").'"]'=> $SASL_CALLBACK);
+}
+
+
+##############################################################################
+#
+# ProcessSASLStanza - process a SASL based packet.
+#
+##############################################################################
+sub ProcessSASLStanza
+{
+    my $self = shift;
+    my $sid = shift;
+    my $node = shift;
+
+    my $tag = &XML::Stream::XPath($node,"name()");
+
+    if ($tag eq "challenge")
+    {
+        $self->SASLAnswerChallenge($node);
+    }
+    
+    if ($tag eq "failure")
+    {
+        $self->SASLClientFailure($node);
+    }
+    
+    if ($tag eq "success")
+    {
+        $self->SASLClientSuccess($node);
+    }
+}
+
+
+##############################################################################
+#
+# SASLAnswerChallenge - when we get a <challenge/> we need to do the grunt
+#                       work to return a <response/>.
+#
+##############################################################################
+sub SASLAnswerChallenge
+{
+    my $self = shift;
+    my $node = shift;
+
+    my $challenge64 = &XML::Stream::XPath($node,"text()");
+    my $challenge = MIME::Base64::decode_base64($challenge64);
+    
+    my $response = $self->SASLGetClient()->client_step($challenge);
+
+    my $response64 = MIME::Base64::encode_base64($response,"");
+    $self->SASLSendResponse($response64);
+}
+
+
+###############################################################################
+#
+# SASLClient - This is a helper function to perform all of the required steps
+#              for doing SASL with the server.
+#
+###############################################################################
+sub SASLClient
+{
+    my $self = shift;
+    my $username = shift;
+    my $password = shift;
+
+    my $mechanisms = $self->GetStreamFeature("xmpp-sasl");
+
+    return unless defined($mechanisms);
+    
+    my $sasl = new Authen::SASL(mechanism=>join(" ",@{$mechanisms}),
+                                callback=>{ user => $username,
+                                            pass => $password
+                                          }
+                               );
+
+    $self->{SASL}->{client} = $sasl->client_new();
+    $self->{SASL}->{username} = $username;
+    $self->{SASL}->{password} = $password;
+    $self->{SASL}->{authed} = 0;
+    $self->{SASL}->{done} = 0;
+
+    $self->SASLSendAuth();
+}
+
+
+##############################################################################
+#
+# SASLClientAuthed - return 1 if we authed via SASL, 0 otherwise
+#
+##############################################################################
+sub SASLClientAuthed
+{
+    my $self = shift;
+    
+    return $self->{SASL}->{authed};
+}
+
+
+##############################################################################
+#
+# SASLClientDone - return 1 if the SASL process is finished
+#
+##############################################################################
+sub SASLClientDone
+{
+    my $self = shift;
+    
+    return $self->{SASL}->{done};
+}
+
+
+##############################################################################
+#
+# SASLClientError - return the error if any
+#
+##############################################################################
+sub SASLClientError
+{
+    my $self = shift;
+    
+    return $self->{SASL}->{error};
+}
+
+
+##############################################################################
+#
+# SASLClientFailure - handle a received <failure/>
+#
+##############################################################################
+sub SASLClientFailure
+{
+    my $self = shift;
+    my $node = shift;
+    
+    my $type = &XML::Stream::XPath($node,"*/name()");
+
+    $self->{SASL}->{error} = $type;
+    $self->{SASL}->{done} = 1;
+}
+
+
+##############################################################################
+#
+# SASLClientSuccess - handle a received <success/>
+#
+##############################################################################
+sub SASLClientSuccess
+{
+    my $self = shift;
+    my $node = shift;
+    
+    $self->{SASL}->{authed} = 1;
+    $self->{SASL}->{done} = 1;
+
+    $self->RemoveDirectXPathCallBacks('/[@xmlns="'.&ConstXMLNS("xmpp-sasl").'"]'=>$SASL_CALLBACK);
+}
+
+
+###############################################################################
+#
+# SASLGetClient - This is a helper function to return the SASL client object.
+#
+###############################################################################
+sub SASLGetClient
+{
+    my $self = shift;
+    
+    return $self->{SASL}->{client};
+}
+
+
+##############################################################################
+#
+# SASLSendAuth - send an <auth/> in the SASL namespace
+#
+##############################################################################
+sub SASLSendAuth
+{
+    my $self = shift;
+
+    $self->Send("<auth xmlns='".&ConstXMLNS('xmpp-sasl')."' mechanism='".$self->SASLGetClient()->mechanism()."'/>");
+}
+
+
+##############################################################################
+#
+# SASLSendChallenge - Send a <challenge/> in the SASL namespace
+#
+##############################################################################
+sub SASLSendChallenge
+{
+    my $self = shift;
+    my $challenge = shift;
+
+    $self->Send("<challenge xmlns='".&ConstXMLNS('xmpp-sasl')."'>${challenge}</challenge>");
+}
+
+
+##############################################################################
+#
+# SASLSendFailure - Send a <failure/> tag in the SASL namespace
+#
+##############################################################################
+sub SASLSendFailure
+{
+    my $self = shift;
+    my $type = shift;
+    
+    $self->Send("<failure xmlns='".&ConstXMLNS('xmpp-sasl')."'><${type}/></failure>");
+}
+
+
+##############################################################################
+#
+# SASLSendResponse - Send a <response/> tag in the SASL namespace
+#
+##############################################################################
+sub SASLSendResponse
+{
+    my $self = shift;
+    my $response = shift;
+
+    $self->Send("<response xmlns='".&ConstXMLNS('xmpp-sasl')."'>${response}</response>");
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Default CallBacks
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+
+##############################################################################
+#
+# callbackInit - initialize the default callbacks
+#
+##############################################################################
+sub callbackInit
+{
+    my $self = shift;
+
+    $self->SetCallBacks(iq=>sub{ $self->callbackIQ(@_) },
+                        presence=>sub{ $self->callbackPresence(@_) },
+                        message=>sub{ $self->callbackMessage(@_) },
+                        );
+
+    $self->SetPresenceCallBacks(subscribe=>sub{ $self->callbackPresenceSubscribe(@_) },
+                                unsubscribe=>sub{ $self->callbackPresenceUnsubscribe(@_) },
+                                subscribed=>sub{ $self->callbackPresenceSubscribed(@_) },
+                                unsubscribed=>sub{ $self->callbackPresenceUnsubscribed(@_) },
+                               );
+
+    $self->TLSInit();
+    $self->SASLInit();
+}
+
+
+##############################################################################
+#
+# callbackMessage - default callback for <message/> packets.
+#
+##############################################################################
+sub callbackMessage
+{
+    my $self = shift;
+    my $sid = shift;
+    my $message = shift;
+
+    my $type = "normal";
+    $type = $message->GetType() if $message->DefinedType();
+
+    if (exists($self->{CB}->{Mess}->{$type}) &&
+        (ref($self->{CB}->{Mess}->{$type}) eq "CODE"))
+    {
+        &{$self->{CB}->{Mess}->{$type}}($sid,$message);
+    }
+}
+
+
+##############################################################################
+#
+# callbackPresence - default callback for <presence/> packets.
+#
+##############################################################################
+sub callbackPresence
+{
+    my $self = shift;
+    my $sid = shift;
+    my $presence = shift;
+
+    my $type = "available";
+    $type = $presence->GetType() if $presence->DefinedType();
+
+    if (exists($self->{CB}->{Pres}->{$type}) &&
+        (ref($self->{CB}->{Pres}->{$type}) eq "CODE"))
+    {
+        &{$self->{CB}->{Pres}->{$type}}($sid,$presence);
+    }
+}
+
+
+##############################################################################
+#
+# callbackIQ - default callback for <iq/> packets.
+#
+##############################################################################
+sub callbackIQ
+{
+    my $self = shift;
+    my $sid = shift;
+    my $iq = shift;
+
+    return unless $iq->DefinedChild();
+    my $query = $iq->GetChild();
+    return unless defined($query);
+
+    my $type = $iq->GetType();
+    my $ns = $query->GetXMLNS();
+
+    if (exists($self->{CB}->{IQns}->{$ns}) &&
+        (ref($self->{CB}->{IQns}->{$ns}) eq "CODE"))
+    {
+        &{$self->{CB}->{IQns}->{$ns}}($sid,$iq);
+
+    }
+    elsif (exists($self->{CB}->{IQns}->{$ns}->{$type}) &&
+           (ref($self->{CB}->{IQns}->{$ns}->{$type}) eq "CODE"))
+    {
+        &{$self->{CB}->{IQns}->{$ns}->{$type}}($sid,$iq);
+    }
+}
+
+
+##############################################################################
+#
+# callbackPresenceSubscribe - default callback for subscribe packets.
+#
+##############################################################################
+sub callbackPresenceSubscribe
+{
+    my $self = shift;
+    my $sid = shift;
+    my $presence = shift;
+
+    my $reply = $presence->Reply(type=>"subscribed");
+    $self->Send($reply,1);
+    $reply->SetType("subscribe");
+    $self->Send($reply,1);
+}
+
+
+##############################################################################
+#
+# callbackPresenceUnsubscribe - default callback for unsubscribe packets.
+#
+##############################################################################
+sub callbackPresenceUnsubscribe
+{
+    my $self = shift;
+    my $sid = shift;
+    my $presence = shift;
+
+    my $reply = $presence->Reply(type=>"unsubscribed");
+    $self->Send($reply,1);
+}
+
+   
+##############################################################################
+#
+# callbackPresenceSubscribed - default callback for subscribed packets.
+#
+##############################################################################
+sub callbackPresenceSubscribed
+{
+    my $self = shift;
+    my $sid = shift;
+    my $presence = shift;
+
+    my $reply = $presence->Reply(type=>"subscribed");
+    $self->Send($reply,1);
+}
+
+
+##############################################################################
+#
+# callbackPresenceUnsubscribed - default callback for unsubscribed packets.
+#
+##############################################################################
+sub callbackPresenceUnsubscribed
+{
+    my $self = shift;
+    my $sid = shift;
+    my $presence = shift;
+
+    my $reply = $presence->Reply(type=>"unsubscribed");
+    $self->Send($reply,1);
+}
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Stream functions
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+sub GetStreamID
+{
+    my $self = shift;
+
+    return $self->{SESSION}->{id};
+}
+
+
+sub GetStreamFeature
+{
+    my $self = shift;
+    my $feature = shift;
+
+    return $self->{STREAM}->GetStreamFeature($self->GetStreamID(),$feature);
+}
+
+
+sub RestartStream
+{
+    my $self = shift;
+    my $timeout = shift;
+
+    $self->{SESSION} =
+        $self->{STREAM}->OpenStream($self->GetStreamID(),$timeout);
+    return $self->GetStreamID();
+}
+
+
+##############################################################################
+#
+# ConstXMLNS - Return the namespace from the constant string.
+#
+##############################################################################
+sub ConstXMLNS
+{
+    my $const = shift;
+    
+    return $XMLNS{$const};
+}
+
+
+1;
Index: perl/modules/Jabber/lib/Net/XMPP/Roster.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Roster.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Roster.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,856 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Roster;
+
+=head1 NAME
+
+Net::XMPP::Roster - XMPP Roster Object
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Roster is a module that provides a developer an easy
+  interface to an XMPP roster.  It provides high level functions to
+  query, update, and manage a user's roster.  
+
+=head1 DESCRIPTION
+
+  The Roster object seeks to provide an easy to use API for interfacing
+  with a user's roster.  When you instantiate it, it automatically
+  registers with the connection to receivce the correct packets so
+  that it can track all roster updates, and presence packets.
+  
+=head2 Basic Functions
+
+  my $Client = Net::XMPP::Client->new(...);
+
+  my $Roster = Net::XMPP::Roster->new(connection=>$Client);
+    or
+  my $Roster = $Client->Roster();
+
+  $Roster->clear();
+
+  if ($Roster->exists('bob@jabber.org')) { ... }
+  if ($Roster->exists(Net::XMPP::JID)) { ... }
+
+  if ($Roster->groupExists("Friends")) { ... }
+
+  my @groups = $Roster->groups();
+
+  my @jids    = $Roster->jids();
+  my @friends = $Roster->jids("group","Friends");
+  my @unfiled = $Roster->jids("nogroup");
+
+  if ($Roster->online('bob@jabber.org')) { ... }
+  if ($Roster->online(Net::XMPP::JID)) { ... }
+
+  my %hash = $Roster->query('bob@jabber.org');
+  my %hash = $Roster->query(Net::XMPP::JID);
+
+  my $name = $Roster->query('bob@jabber.org',"name");
+  my $ask  = $Roster->query(Net::XMPP::JID,"ask");
+
+  my $resource = $Roster->resource('bob@jabber.org');
+  my $resource = $Roster->resource(Net::XMPP::JID);
+
+  my %hash = $Roster->resourceQuery('bob@jabber.org',"Home");
+  my %hash = $Roster->resourceQuery(Net::XMPP::JID,"Club");
+
+  my $show   = $Roster->resourceQuery('bob@jabber.org',"Home","show");
+  my $status = $Roster->resourceQuery(Net::XMPP::JID,"Work","status");
+
+  my @resource = $Roster->resources('bob@jabber.org');
+  my @resource = $Roster->resources(Net::XMPP::JID);
+
+  $Roster->resourceStore('bob@jabber.org',"Home","gpgkey",key);
+  $Roster->resourceStore(Net::XMPP::JID,"logged on","2004/04/07 ...");
+
+  $Roster->store('bob@jabber.org',"avatar",avatar);
+  $Roster->store(Net::XMPP::JID,"display_name","Bob");
+
+=head2 Advanced Functions
+
+  These functions are only needed if you want to manually control
+  the Roster.
+  
+  $Roster->add('bob@jabber.org',
+               name=>"Bob",
+               groups=>["Friends"]
+              );
+  $Roster->add(Net::XMPP::JID);
+
+  $Roster->addResource('bob@jabber.org',
+                       "Home",
+                       show=>"dnd",
+                       status=>"Working"
+                      );
+  $Roster->addResource(Net::XMPP::JID,"Work");
+
+  $Roster->remove('bob@jabber.org');
+  $Roster->remove(Net::XMPP::JID);
+
+  $Roster->removeResource('bob@jabber.org',"Home");
+  $Roster->removeResource(Net::XMPP::JID,"Work");
+
+  $Roster->handler(Net::XMPP::IQ);
+  $Roster->handler(Net::XMPP::Presence);
+
+=head1 METHODS
+
+=head2 Basic Functions
+
+
+  new(connection=>object) - This creates and initializes the Roster
+                            object.  The connection object is required
+                            so that the Roster can interact with the
+                            main connection object.  It needs to be an
+                            object that inherits from
+                            Net::XMPP::Connection.
+
+  clear() - removes everything from the database.
+
+  exists(jid) - return 1 if the JID exists in the database, undef
+                otherwise.  The jid can either be a string, or a
+                Net::XMPP::JID object.
+                
+  groupExists(group) - return 1 if the group exists in the database,
+                       undef otherwise.
+
+  groups() - returns a list of all of the roster groups.
+
+  jids([type,    - returns a list of all of the matching JIDs.  The valid
+       [group]])   types are:
+
+                    all     - return all JIDs in the roster. (default)
+                    nogroup - return all JIDs not in a roster group.
+                    group   - return all of the JIDs in the specified
+                              roster group.
+
+  online(jid) - return 1 if the JID is online, undef otherwise.  The
+                jid can either be a string, or a Net::XMPP::JID object.
+
+  query(jid,   - return a hash representing all of the data in the
+        [key])   DB for this JID.  The jid can either be a string,
+                 or a Net::XMPP::JID object.  If you specify a key,
+                 then only the value for that key is returned.
+
+  resource(jid) - return the string representing the resource with the
+                  highest priority for the JID.  The jid can either be
+                  a string, or a Net::XMPP::JID object.
+
+  resourceQuery(jid,      - return a hash representing all of the data
+                resource,   the DB for the resource for this JID.  The
+                [key])      jid can either be a string, or a
+                            Net::XMPP::JID object.  If you specify a
+                            key, then only the value for that key is
+                            returned.
+
+  resources(jid) - returns the list of resources for the JID in order
+                   of highest priority to lowest priority.  The jid can
+                   either be a string, or a Net::XMPP::JID object.
+
+  resourceStore(jid,      - store the specified value in the DB under
+                resource,   the specified key for the resource for this
+                key,        JID.  The jid can either be a string, or a
+                value)      Net::XMPP::JID object.
+
+  store(jid,      - store the specified value in the DB under the
+        key,        specified key for this JID.  The jid can either
+        value)      be a string, or a Net::XMPP::JID object.
+
+
+
+=head2 Advanced Functions
+
+add(jid,                 - Manually adds the JID to the Roster with the
+    ask=>string,           specified roster item settings.  This does not
+    groups=>arrayref       handle subscribing to other users, only
+    name=>string,          manipulating the Roster object.  The jid
+    subscription=>string)  can either be a string or a Net::XMPP::JID.
+
+addResource(jid,            - Manually add the resource to the JID in the
+            resource,         Roster with the specified presence settings.
+            priority=>int,    This does not handle subscribing to other
+            show=>string,     users, only manipulating the Roster object.
+            status=>string)   The jid can either be a string or a
+                              Net::XMPP::JID.
+
+remove(jid) - Removes all reference to the JID from the Roster object.
+              The jid can either be a string or a Net::XMPP::JID.
+
+removeResource(jid,      - Removes the resource from the jid in the
+               resource)   Roster object.  The jid can either be a string
+                           or a Net::XMPP::JID.
+
+handler(packet) - Take either a Net::XMPP::IQ or Net::XMPP::Presence
+                  packet and parse them according to the rules of the
+                  Roster object.  Note, that it will only waste CPU time
+                  if you pass in IQs or Presences that are not roster
+                  related.
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use Carp;
+
+sub new
+{
+    my $proto = shift;
+    my $self = { };
+
+    my %args;
+    while($#_ >= 0) { $args{ lc(pop(@_)) } = pop(@_); }
+
+    if (!exists($args{connection}) ||
+        !$args{connection}->isa("Net::XMPP::Connection"))
+    {
+        croak("You must pass Net::XMPP::Roster a valid connection object.");
+    }
+
+    $self->{CONNECTION} = $args{connection};
+    
+    bless($self, $proto);
+
+    $self->init();
+    
+    return $self;
+}
+
+
+##############################################################################
+#
+# init - initialize the module to use the roster database
+#
+##############################################################################
+sub init
+{
+    my $self = shift;
+
+    $self->{CONNECTION}-> SetXPathCallBacks('/iq[@type="result" or @type="set"]/query[@xmlns="jabber:iq:roster"]'=>sub{ $self->handler(@_) });
+    $self->{CONNECTION}-> SetXPathCallBacks('/presence'=>sub{ $self->handler(@_) });
+}
+
+
+##############################################################################
+#
+# add - adds the entry to the Roster DB.
+#
+##############################################################################
+sub add
+{
+    my $self = shift;
+    my ($jid,%item) = @_;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+    if (exists $self->{JIDS}->{$jid})
+    {
+        foreach my $key (keys %item)
+        {
+            $self->{JIDS}->{$jid}->{$key} = $item{$key};
+        }
+    }
+    else
+    {
+        $self->{JIDS}->{$jid} = \%item;
+    }
+
+    foreach my $group (keys %{ $self->{GROUPS} })
+    {
+        # Clear user from old groups.
+        delete $self->{GROUPS}->{$group}->{$jid};
+        # Clean up empty groups.
+        delete $self->{GROUPS}->{$group} unless scalar keys %{ $self->{GROUPS}->{$group} };
+    }
+
+    foreach my $group (@{$item{groups}})
+    {
+        $self->{GROUPS}->{$group}->{$jid} = 1;
+    }
+}
+
+
+
+##############################################################################
+#
+# addResource - adds the resource to the JID in the Roster DB.
+#
+##############################################################################
+sub addResource
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+    my (%item) = @_;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    my $priority = $item{priority};
+    $priority = 0 unless defined($priority);
+
+    $self->{CONNECTION}->{DEBUG}->Log3("Roster::addResource: add $jid/$resource with priority $priority to the DB");
+
+    my $loc = -1;
+    $self->{JIDS}->{$jid}->{priorities}->{$priority} = []
+        unless exists($self->{JIDS}->{$jid}->{priorities}->{$priority});
+    foreach my $index (0..$#{$self->{JIDS}->{$jid}->{priorities}->{$priority}})
+    {
+        $loc = $index
+            if ($self->{JIDS}->{$jid}->{priorities}->{$priority}->[$index]->{resource} eq $resource);
+    }
+    $loc = $#{$self->{JIDS}->{$jid}->{priorities}->{$priority}} + 1 if ($loc == -1);
+
+    $self->{JIDS}->{$jid}->{resources}->{$resource}->{priority} = $priority;
+    $self->{JIDS}->{$jid}->{resources}->{$resource}->{status} = $item{status}
+        if exists($item{status});
+    $self->{JIDS}->{$jid}->{resources}->{$resource}->{show} = $item{show}
+        if exists($item{show});
+    $self->{JIDS}->{$jid}->{priorities}->{$priority}->[$loc]->{resource} = $resource;
+}
+
+
+###############################################################################
+#
+# clear - delete all of the JIDs from the DB completely.
+#
+###############################################################################
+sub clear
+{
+    my $self = shift;
+
+    $self->{CONNECTION}->{DEBUG}->Log3("Roster::clear: clearing the database");
+    foreach my $jid ($self->jids())
+    {
+        $self->remove($jid);
+    }
+    $self->{CONNECTION}->{DEBUG}->Log3("Roster::clear: database is empty");
+}
+
+
+##############################################################################
+#
+# exists - allows you to query if the JID exists in the Roster DB.
+#
+##############################################################################
+sub exists
+{
+    my $self = shift;
+    my ($jid) = @_;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+    
+    return unless exists($self->{JIDS});
+    return unless exists($self->{JIDS}->{$jid});
+    return 1;
+}
+
+
+sub fetch
+{
+    my $self = shift;
+
+    my %newroster = $self->{CONNECTION}->RosterGet();
+
+    $self->handleRoster(\%newroster);
+}
+
+
+##############################################################################
+#
+# groupExists - allows you to query if the group exists in the Roster
+#                       DB.
+#
+##############################################################################
+sub groupExists
+{
+    my $self = shift;
+    my ($group) = @_;
+
+    return unless exists($self->{GROUPS});
+    return unless exists($self->{GROUPS}->{$group});
+    return 1;
+}
+
+
+##############################################################################
+#
+# groups - returns a list of the current groups in your roster.
+#
+##############################################################################
+sub groups
+{
+    my $self = shift;
+
+    return () unless exists($self->{GROUPS});
+    return () if (scalar(keys(%{$self->{GROUPS}})) == 0);
+    return keys(%{$self->{GROUPS}});
+}
+
+
+##############################################################################
+#
+# handler - takes a packet and calls the correct handler.
+#
+##############################################################################
+sub handler
+{
+    my $self = shift;
+    my $sid = shift;
+    my $packet = shift;
+
+    $self->handleIQ($packet) if ($packet->GetTag() eq "iq");
+    $self->handlePresence($packet) if ($packet->GetTag() eq "presence");
+}
+
+
+##############################################################################
+#
+# handleIQ - takes an iq packet that contains roster, parses it, and puts
+#            the roster into the Roster DB.
+#
+##############################################################################
+sub handleIQ
+{
+    my $self = shift;
+    my $iq = shift;
+
+    my $type = $iq->GetType();
+    return unless (($type eq "set") || ($type eq "result"));
+
+    my %newroster = $self->{CONNECTION}->RosterParse($iq);
+
+    $self->handleRoster(\%newroster);
+}
+
+
+sub handleRoster
+{
+    my $self = shift;
+    my $roster = shift;
+
+    foreach my $jid (keys(%{$roster}))
+    {
+        if ($roster->{$jid}->{subscription} ne "remove")
+        {
+            $self->add($jid, %{$roster->{$jid}});
+        }
+        else
+        {
+            $self->remove($jid);
+        }
+    }
+}
+
+
+##############################################################################
+#
+# handlePresence - takes a presence packet and groks the presence.
+#
+##############################################################################
+sub handlePresence
+{
+    my $self = shift;
+    my $presence = shift;
+
+    my $type = $presence->GetType();
+    $type = "" unless defined($type);
+    return unless (($type eq "") ||
+                   ($type eq "available") ||
+                   ($type eq "unavailable"));
+
+    my $jid = $presence->GetFrom("jid");
+
+    my $resource = $jid->GetResource();
+    $resource = " " unless ($resource ne "");
+    
+    $jid = $jid->GetJID();
+    $jid = "" unless defined($jid);
+
+    return unless $self->exists($jid);
+    #XXX if it doesn't exist... is it us?
+    #XXX is this a presence based roster?
+
+    $self->{CONNECTION}->{DEBUG}->Log3("Roster::PresenceDBParse: fromJID(",$presence->GetFrom(),") resource($resource) type($type)");
+    $self->{CONNECTION}->{DEBUG}->Log4("Roster::PresenceDBParse: xml(",$presence->GetXML(),")");
+
+    $self->removeResource($jid,$resource);
+
+    if (($type eq "") || ($type eq "available"))
+    {
+        my %item;
+        
+        $item{priority} = $presence->GetPriority();
+        $item{priority} = 0 unless defined($item{priority});
+
+        $item{show} = $presence->GetShow();
+        $item{show} = "" unless defined($item{show});
+
+        $item{status} = $presence->GetStatus();
+        $item{status} = "" unless defined($item{status});
+
+        $self->addResource($jid,$resource,%item);
+    }
+}
+
+
+##############################################################################
+#
+# jids - returns a list of all of the JIDs in your roster.
+#
+##############################################################################
+sub jids
+{
+    my $self = shift;
+    my $type = shift;
+    my $group = shift;
+
+    $type = "all" unless defined($type);
+
+    my @jids;
+
+    if (($type eq "all") || ($type eq "nogroup"))
+    {
+        return () unless exists($self->{JIDS});
+        foreach my $jid (keys(%{$self->{JIDS}}))
+        {
+            next if (($type eq "nogroup") &&
+                     exists($self->{JIDS}->{$jid}->{groups}) &&
+                     ($#{$self->{JIDS}->{$jid}->{groups}} > -1));
+
+            push(@jids, Net::XMPP::JID->new($jid));
+        }
+    }
+    
+    if ($type eq "group")
+    {
+        return () unless exists($self->{GROUPS});
+        if (defined($group) && $self->groupExists($group))
+        {
+            foreach my $jid (keys(%{$self->{GROUPS}->{$group}}))
+            {
+                push(@jids, Net::XMPP::JID->new($jid));
+            }
+        }
+    }
+
+    return @jids;
+}
+
+
+###############################################################################
+#
+# online - returns if the jid is online or not.
+#
+###############################################################################
+sub online
+{
+    my $self = shift;
+    my $jid = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return unless $self->exists($jid);
+
+    my @resources = $self->resources($jid);
+
+    return ($#resources > -1);
+}
+
+
+##############################################################################
+#
+# priority - return the highest priority for the jid, or for the specified
+#            resource.
+#
+##############################################################################
+sub priority
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+    
+    if (defined($resource))
+    {
+        return unless $self->resourceExists($jid,$resource);
+        return unless exists($self->{JIDS}->{$jid}->{resources}->{$resource}->{priority});
+        return $self->{JIDS}->{$jid}->{resources}->{$resource}->{priority};
+    }
+    
+    return unless exists($self->{JIDS}->{$jid}->{priorities});
+    my @priorities = sort{ $b <=> $a } keys(%{$self->{JIDS}->{$jid}->{priorities}});
+    return $priorities[0];
+}
+
+
+##############################################################################
+#
+# query - allows you to get one of the pieces of info from the Roster DB.
+#
+##############################################################################
+sub query
+{
+    my $self = shift;
+    my $jid = shift;
+    my $key = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+    
+    return unless $self->exists($jid);
+    if (defined($key))
+    {
+        return unless exists($self->{JIDS}->{$jid}->{$key});
+        return $self->{JIDS}->{$jid}->{$key};
+    }
+    return %{$self->{JIDS}->{$jid}};
+}
+
+
+##############################################################################
+#
+# remove - removes the JID from the Roster DB.
+#
+##############################################################################
+sub remove
+{
+    my $self = shift;
+    my $jid = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    if ($self->exists($jid))
+    {
+        $self->{CONNECTION}->{DEBUG}->Log3("Roster::remove: deleting $jid from the DB");
+        
+        if (defined($self->query($jid,"groups")))
+        {
+            foreach my $group (@{$self->query($jid,"groups")})
+            {
+                delete($self->{GROUPS}->{$group}->{$jid});
+                delete($self->{GROUPS}->{$group})
+                    if (scalar(keys(%{$self->{GROUPS}->{$group}})) == 0);
+                delete($self->{GROUPS})
+                    if (scalar(keys(%{$self->{GROUPS}})) == 0);
+            }
+        }
+    
+        delete($self->{JIDS}->{$jid});
+        delete($self->{JIDS}) if (scalar(keys(%{$self->{JIDS}})) == 0);
+    }
+}
+
+
+##############################################################################
+#
+# removeResource - removes the resource from the JID from the Roster DB.
+#
+##############################################################################
+sub removeResource
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    if ($self->resourceExists($jid,$resource))
+    {
+        $self->{CONNECTION}->{DEBUG}->Log3("Roster::removeResource: remove $jid/$resource from the DB");
+
+        my $oldPriority = $self->priority($jid,$resource);
+        $oldPriority = "" unless defined($oldPriority);
+
+        if (exists($self->{JIDS}->{$jid}->{priorities}->{$oldPriority}))
+        {
+            my $loc = 0;
+            foreach my $index (0..$#{$self->{JIDS}->{$jid}->{priorities}->{$oldPriority}})
+            {
+                $loc = $index
+                    if ($self->{JIDS}->{$jid}->{priorities}->{$oldPriority}->[$index]->{resource} eq $resource);
+            }
+            
+            splice(@{$self->{JIDS}->{$jid}->{priorities}->{$oldPriority}},$loc,1);
+
+            delete($self->{JIDS}->{$jid}->{priorities}->{$oldPriority})
+                if (exists($self->{JIDS}->{$jid}->{priorities}->{$oldPriority}) &&
+                    ($#{$self->{JIDS}->{$jid}->{priorities}->{$oldPriority}} == -1));
+        }
+
+        delete($self->{JIDS}->{$jid}->{resources}->{$resource});
+
+    }
+}
+
+
+###############################################################################
+#
+# resource - retrieve the resource with the highest priority.
+#
+###############################################################################
+sub resource
+{
+    my $self = shift;
+    my $jid = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return unless $self->exists($jid);
+
+    my $priority = $self->priority($jid);
+
+    return unless defined($priority);
+
+    return $self->{JIDS}->{$jid}->{priorities}->{$priority}->[0]->{resource};
+}
+
+
+##############################################################################
+#
+# resourceExists - check that the specified resource exists.
+#
+##############################################################################
+sub resourceExists
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+    
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return unless $self->exists($jid);
+    return unless exists($self->{JIDS}->{$jid}->{resources});
+    return unless exists($self->{JIDS}->{$jid}->{resources}->{$resource});
+}
+
+
+##############################################################################
+#
+# resourceQuery - allows you to get one of the pieces of info from the Roster
+#                 DB.
+#
+##############################################################################
+sub resourceQuery
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+    my $key = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+    
+    return unless $self->resourceExists($jid,$resource);
+    if (defined($key))
+    {
+        return unless exists($self->{JIDS}->{$jid}->{resources}->{$resource}->{$key});
+        return $self->{JIDS}->{$jid}->{resources}->{$resource}->{$key};
+    }
+    return %{$self->{JIDS}->{$jid}->{resources}->{$resource};}
+}
+
+
+###############################################################################
+#
+# resources - returns a list of the resources from highest priority to lowest.
+#
+###############################################################################
+sub resources
+{
+    my $self = shift;
+    my $jid = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return () unless $self->exists($jid);
+
+    my @resources;
+
+    foreach my $priority (sort {$b cmp $a} keys(%{$self->{JIDS}->{$jid}->{priorities}}))
+    {
+        foreach my $index (0..$#{$self->{JIDS}->{$jid}->{priorities}->{$priority}})
+        {
+            next if ($self->{JIDS}->{$jid}->{priorities}->{$priority}->[$index]->{resource} eq " ");
+            push(@resources,$self->{JIDS}->{$jid}->{priorities}->{$priority}->[$index]->{resource});
+        }
+    }
+    return @resources;
+}
+
+
+##############################################################################
+#
+# resourceStore - allows you to store anything on the item that you want to.
+#                 The only drawback is that when the item is removed, the data
+#                 is not kept.  You must restore it in the DB.
+#
+##############################################################################
+sub resourceStore
+{
+    my $self = shift;
+    my $jid = shift;
+    my $resource = shift;
+    my $key = shift;
+    my $value = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return unless defined($key);
+    return unless defined($value);
+    return unless $self->resourceExists($jid,$resource);
+
+    $self->{JIDS}->{$jid}->{resources}->{$resource}->{$key} = $value;
+}
+
+
+##############################################################################
+#
+# store - allows you to store anything on the item that you want to.  The
+#         only drawback is that when the item is removed, the data is not
+#         kept.  You must restore it in the DB.
+#
+##############################################################################
+sub store
+{
+    my $self = shift;
+    my $jid = shift;
+    my $key = shift;
+    my $value = shift;
+
+    $jid = $jid->GetJID() if UNIVERSAL::isa($jid,"Net::XMPP::JID");
+
+    return unless defined($key);
+    return unless defined($value);
+    return unless $self->exists($jid);
+
+    $self->{JIDS}->{$jid}->{$key} = $value;
+}
+
+
+1;
+
Index: perl/modules/Jabber/lib/Net/XMPP/Stanza.pm
===================================================================
--- perl/modules/Jabber/lib/Net/XMPP/Stanza.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
+++ perl/modules/Jabber/lib/Net/XMPP/Stanza.pm	(revision c2bed5539d456612d69de33e1dacc3afe5f70395)
@@ -0,0 +1,1444 @@
+##############################################################################
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Library General Public
+#  License as published by the Free Software Foundation; either
+#  version 2 of the License, or (at your option) any later version.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Library General Public License for more details.
+#
+#  You should have received a copy of the GNU Library General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+#  Boston, MA  02111-1307, USA.
+#
+#  Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
+#
+##############################################################################
+
+package Net::XMPP::Stanza;
+
+=head1 NAME
+
+Net::XMPP::Stanza - XMPP Stanza Module
+
+=head1 SYNOPSIS
+
+  Net::XMPP::Stanza is a private package that serves as a basis for all
+  XMPP stanzas generated by Net::XMPP.
+
+=head1 DESCRIPTION
+
+  This module is not meant to be used directly.  You should be using
+  either Net::XMPP::IQ, Net::XMPP::Message, Net::XMPP::Presence, or
+  another package that inherits from Net::XMPP::Stanza.
+
+  That said, this is where all of the namespaced methods are documented.
+
+  The current supported namespaces are:
+
+=cut
+
+# NS_BEGIN
+
+=pod
+
+    jabber:iq:auth
+    jabber:iq:privacy
+    jabber:iq:register
+    jabber:iq:roster
+    urn:ietf:params:xml:ns:xmpp-bind
+    urn:ietf:params:xml:ns:xmpp-session
+
+=cut
+
+# NS_END
+
+=pod
+    
+  For more information on what these namespaces are for, visit 
+  http://www.jabber.org and browse the Jabber Programmers Guide.
+
+  The following tables can be read as follows:
+
+  ny:private:ns
+  
+  Name                        Type     Get  Set  Remove  Defined  Add
+  ==========================  =======  ===  ===  ======  =======  ===
+  Foo                         scalar    X    X     X        X
+  Bar                         child                                X 
+  Bars                        child     X
+  Test                        master    X    X
+
+  Withing the my:private:ns namespace, there exists the functions:
+
+    GetFoo(), SetFoo(), RemoveFoo(), DefinedFoo()
+    
+    AddBar()
+    
+    GetBars(), DefinedBars()
+    
+    GetTest(), SetMaster()
+
+  Hopefully it should be obvious how this all works.  If not feel free to
+  contact me and I'll work on adding more documentation.
+
+=cut
+
+# DOC_BEGIN
+=head1 jabber:iq:auth
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Digest                      scalar      X    X     X        X        
+  Hash                        scalar      X    X     X        X        
+  Password                    scalar      X    X     X        X        
+  Resource                    scalar      X    X     X        X        
+  Sequence                    scalar      X    X     X        X        
+  Token                       scalar      X    X     X        X        
+  Username                    scalar      X    X     X        X        
+  Auth                        master      X    X                       
+
+=head1 jabber:iq:privacy
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Active                      scalar      X    X     X        X        
+  Default                     scalar      X    X     X        X        
+  List                        child                                  X 
+  Lists                       child       X          X        X        
+  Privacy                     master      X    X                       
+
+=head1 jabber:iq:privacy - item objects
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Action                      scalar      X    X     X        X        
+  IQ                          flag        X    X     X        X        
+  Message                     flag        X    X     X        X        
+  Order                       scalar      X    X     X        X        
+  PresenceIn                  flag        X    X     X        X        
+  PresenceOut                 flag        X    X     X        X        
+  Type                        scalar      X    X     X        X        
+  Value                       scalar      X    X     X        X        
+  Item                        master      X    X                       
+
+=head1 jabber:iq:privacy - list objects
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Name                        scalar      X    X     X        X        
+  Item                        child                                  X 
+  Items                       child       X          X        X        
+  List                        master      X    X                       
+
+=head1 jabber:iq:register
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Address                     scalar      X    X     X        X        
+  City                        scalar      X    X     X        X        
+  Date                        scalar      X    X     X        X        
+  Email                       scalar      X    X     X        X        
+  First                       scalar      X    X     X        X        
+  Instructions                scalar      X    X     X        X        
+  Key                         scalar      X    X     X        X        
+  Last                        scalar      X    X     X        X        
+  Misc                        scalar      X    X     X        X        
+  Name                        scalar      X    X     X        X        
+  Nick                        scalar      X    X     X        X        
+  Password                    scalar      X    X     X        X        
+  Phone                       scalar      X    X     X        X        
+  Registered                  flag        X    X     X        X        
+  Remove                      flag        X    X     X        X        
+  State                       scalar      X    X     X        X        
+  Text                        scalar      X    X     X        X        
+  URL                         scalar      X    X     X        X        
+  Username                    scalar      X    X     X        X        
+  Zip                         scalar      X    X     X        X        
+  Register                    master      X    X                       
+
+=head1 jabber:iq:roster
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Item                        child                                  X 
+  Items                       child       X                            
+  Roster                      master      X    X                       
+
+=head1 jabber:iq:roster - item objects
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Ask                         scalar      X    X     X        X        
+  Group                       array       X    X     X        X        
+  JID                         jid         X    X     X        X        
+  Name                        scalar      X    X     X        X        
+  Subscription                scalar      X    X     X        X        
+  Item                        master      X    X                       
+
+=head1 urn:ietf:params:xml:ns:xmpp-bind
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  JID                         jid         X    X     X        X        
+  Resource                    scalar      X    X     X        X        
+  Bind                        master      X    X                       
+
+=head1 urn:ietf:params:xml:ns:xmpp-session
+
+  Name                        Type       Get  Set  Remove  Defined  Add
+  ==========================  =========  ===  ===  ======  =======  ===
+  Session                     master      X    X                       
+
+
+=cut
+
+# DOC_END
+
+=head1 AUTHOR
+
+Ryan Eatmon
+
+=head1 COPYRIGHT
+
+This module is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use Carp;
+use Net::XMPP::Namespaces;
+use vars qw( $AUTOLOAD %FUNCTIONS $DEBUG );
+
+$DEBUG = Net::XMPP::Debug->new(usedefault=>1,
+                               header=>"XMPP");
+
+# XXX need to look at evals and $@
+
+sub new
+{
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self = { };
+
+    bless($self, $proto);
+
+    $self->{DEBUGHEADER} = "Stanza";
+    $self->{TAG} = "__netxmpp__:unknown:tag";
+
+    $self->{FUNCS} = \%FUNCTIONS;
+    
+    my $result = $self->_init(@_);
+
+    return $result if defined($result);
+    
+    return $self;
+}
+
+
+sub _init
+{
+    my $self = shift;
+
+    $self->{CHILDREN} = [];
+
+    if ("@_" ne (""))
+    {
+        if ($_[0]->isa("Net::XMPP::Stanza"))
+        {
+            return $_[0];
+        }
+        elsif (ref($_[0]) eq "")
+        {
+            $self->{TAG} = shift;
+            $self->{TREE} = new XML::Stream::Node($self->{TAG});
+        }
+        else
+        {
+            $self->{TREE} = shift;
+            $self->{TAG} = $self->{TREE}->get_tag();
+            $self->_parse_xmlns();
+            $self->_parse_tree();
+        }
+    }
+    else
+    {
+        $self->{TREE} = new XML::Stream::Node($self->{TAG});
+    }
+
+    return;
+}
+
+
+$FUNCTIONS{XMLNS}->{path} = '@xmlns';
+
+$FUNCTIONS{Child}->{type} = 'child';
+$FUNCTIONS{Child}->{path} = '*[@xmlns]';
+$FUNCTIONS{Child}->{child} = {};
+
+##############################################################################
+#
+# debug - prints out the XML::Parser Tree in a readable format for debugging
+#
+##############################################################################
+sub debug
+{
+    my $self = shift;
+
+    print "debug ",$self,":\n";
+    &Net::XMPP::printData("debug: \$self->{CHILDREN}->",$self->{CHILDREN});
+}
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Public Methods
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+##############################################################################
+#
+# GetXML - Returns a string that represents the packet.
+#
+##############################################################################
+sub GetXML 
+{
+    my $self = shift;
+    return $self->GetTree()->GetXML();
+}
+
+
+##############################################################################
+#
+# GetTag - Returns the root tag of the object.
+#
+##############################################################################
+sub GetTag
+{
+    my $self = shift;
+    
+    return $self->{TAG};
+}
+
+
+##############################################################################
+#
+# GetTree - Returns an XML::Stream::Node that contains the full tree including
+#           Query, and X children.
+#
+##############################################################################
+sub GetTree
+{
+    my $self = shift;
+    my $keepXMLNS = shift;
+    $keepXMLNS = 0 unless defined($keepXMLNS);
+
+    my $node = $self->{TREE}->copy();
+
+    $node->remove_attrib("xmlns")
+        if (exists($self->{SKIPXMLNS}) && ($keepXMLNS == 0));
+    
+    foreach my $child (@{$self->{CHILDREN}})
+    {
+        my $child_tree = $child->GetTree($keepXMLNS);
+        $node->add_child($child_tree);
+    }
+
+    my $remove_ns = 0;
+    if (defined($node->get_attrib("xmlns")) && ($keepXMLNS == 0))
+    {
+        $remove_ns = 1
+            if ($self->_check_skip_xmlns($node->get_attrib("xmlns")));
+    }
+
+    $node->remove_attrib("xmlns") if ($remove_ns == 1);
+
+    $node->add_raw_xml(@{$self->{RAWXML}})
+        if (exists($self->{RAWXML}) && ($#{$self->{RAWXML}} > -1));
+
+    return $node;
+} 
+
+
+##############################################################################
+#
+# NewChild - calls AddChild to create a new Net::XMPP::Stanza object, sets the
+#            xmlns and returns a pointer to the new object.
+#
+##############################################################################
+sub NewChild
+{
+    my $self = shift;
+    my $xmlns = shift;
+    my $tag = shift;
+    
+    return unless exists($Net::XMPP::Namespaces::NS{$xmlns});
+
+    if (!defined($tag))
+    {
+        $tag = "x";
+        $tag = $Net::XMPP::Namespaces::NS{$xmlns}->{tag}
+            if exists($Net::XMPP::Namespaces::NS{$xmlns});
+    }
+    
+    my $node = new XML::Stream::Node($tag);
+    $node->put_attrib(xmlns=>$xmlns);
+
+    return $self->AddChild($node);
+}
+
+
+##############################################################################
+#
+# AddChild - creates a new Net::XMPP::packet object, pushes it on the child
+#            list, and returns a pointer to the new object.  This is a
+#            private helper function.
+#
+##############################################################################
+sub AddChild
+{
+    my $self = shift;
+    my $node = shift;
+    my $packet = $self->_new_packet($node);
+    push(@{$self->{CHILDREN}},$packet);
+    return $packet;
+}
+
+
+##############################################################################
+#
+# RemoveChild - removes all xtags that have the specified namespace.
+#
+##############################################################################
+sub RemoveChild
+{
+    my $self = shift;
+    my $xmlns = shift;
+
+    foreach my $index (reverse(0..$#{$self->{CHILDREN}}))
+    {
+        splice(@{$self->{CHILDREN}},$index,1)
+            if (!defined($xmlns) ||
+                ($xmlns eq "") ||
+                ($self->{CHILDREN}->[$index]->GetXMLNS() eq $xmlns));
+    }
+}
+
+
+##############################################################################
+#
+# NewFirstChild - calls AddFirstChild to create a new Net::XMPP::Stanza
+#                 object, sets the xmlns and returns a pointer to the new
+#                 object.
+#
+##############################################################################
+sub NewFirstChild
+{
+    my $self = shift;
+    my $xmlns = shift;
+    my $tag = shift;
+    
+    return unless exists($Net::XMPP::Namespaces::NS{$xmlns});
+
+    if (!defined($tag))
+    {
+        $tag = "x";
+        $tag = $Net::XMPP::Namespaces::NS{$xmlns}->{tag}
+            if exists($Net::XMPP::Namespaces::NS{$xmlns});
+    }
+    
+    my $node = new XML::Stream::Node($tag);
+    $node->put_attrib(xmlns=>$xmlns);
+
+    return $self->AddFirstChild($node);
+}
+
+
+##############################################################################
+#
+# AddFirstChild - creates a new Net::XMPP::packet object, puts it on the child
+#                 list in the front, and returns a pointer to the new object.
+#                 This is a private helper function.
+#
+##############################################################################
+sub AddFirstChild
+{
+    my $self = shift;
+    my $node = shift;
+    my $packet = $self->_new_packet($node);
+    unshift(@{$self->{CHILDREN}},$packet);
+    return $packet;
+}
+
+
+##############################################################################
+#
+# RemoveFirstChild - removes all xtags that have the specified namespace.
+#
+##############################################################################
+sub RemoveFirstChild
+{
+    my $self = shift;
+
+    shift(@{$self->{CHILDREN}});
+}
+
+
+##############################################################################
+#
+# InsertRawXML - puts the specified string onto the list for raw XML to be
+#                included in the packet.
+#
+##############################################################################
+sub InsertRawXML
+{
+    my $self = shift;
+    my(@rawxml) = @_;
+    if (!exists($self->{RAWXML}))
+    {
+        $self->{RAWXML} = [];
+    }
+    push(@{$self->{RAWXML}},@rawxml);
+}
+
+
+##############################################################################
+#
+# ClearRawXML - removes all raw XML from the packet.
+#
+##############################################################################
+sub ClearRawXML
+{
+    my $self = shift;
+    $self->{RAWXML} = [];
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| AutoLoad methods
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+##############################################################################
+#
+# AutoLoad - This function is a central location for handling all of the
+#            AUTOLOADS for all of the sub modules.
+#
+##############################################################################
+sub AUTOLOAD
+{
+    my $self = shift;
+    return if ($AUTOLOAD =~ /::DESTROY$/);
+    my ($package) = ($AUTOLOAD =~ /^(.*)::/);
+    $AUTOLOAD =~ s/^.*:://;
+    my ($call,$var) = ($AUTOLOAD =~ /^(Add|Get|Set|Remove|Defined)(.*)$/);
+    $call = "" unless defined($call);
+    $var = "" unless defined($var);
+
+    #$self->_debug("AUTOLOAD: self($self) AUTOLOAD($AUTOLOAD) package($package)");
+    #$self->_debug("AUTOLOAD: tag($self->{TAG}) call($call) var($var) args(",join(",",@_),")");
+
+    #-------------------------------------------------------------------------
+    # Pick off calls for top level tags <message/>, <presence/>, and <iq/>
+    #-------------------------------------------------------------------------
+    my @xmlns = $self->{TREE}->XPath('@xmlns');
+    my $XPathArgs = $self->_xpath_AUTOLOAD($package,$call,$var,$xmlns[0]);
+    return $self->_xpath($call,@{$XPathArgs},@_) if defined($XPathArgs);
+
+    #-------------------------------------------------------------------------
+    # We don't know what this function is... Hand it off to Missing Persons...
+    #-------------------------------------------------------------------------
+    $self->_missing_function($AUTOLOAD);
+}
+
+
+##############################################################################
+#
+# _xpath_AUTOLOAD - This function is a helper function for the main AutoLoad
+#                   function to help cut down on repeating code.
+#
+##############################################################################
+sub _xpath_AUTOLOAD
+{
+    my $self = shift;
+    my $package = shift;
+    my $call = shift;
+    my $var = shift;
+    my $xmlns = shift;
+
+    #$self->_debug("_xpath_AUTOLOAD: self($self) package($package) call($call) var($var)");
+    #$self->_debug("_xpath_AUTOLOAD: xmlns($xmlns)") if defined($xmlns);
+
+    #-------------------------------------------------------------------------
+    # First thing, figure out which group of functions we are going to be
+    # working with.  FUNCTIONS, or NS{$xmlns}->{xpath}...
+    #-------------------------------------------------------------------------
+    my $funcs = $self->_xpath_funcs($package,$call,$var,$xmlns);
+    return unless defined($funcs);
+
+    my @setFuncs = grep { $_ ne $var } keys(%{$funcs});
+
+    #$self->_debug("_xpath_AUTOLOAD: setFuncs(",join(",",@setFuncs),")");
+                
+    
+    my $type = (exists($funcs->{$var}->{type}) ?
+                $funcs->{$var}->{type} :
+                "scalar"
+               );
+
+    my $path = (exists($funcs->{$var}->{path}) ?
+                $funcs->{$var}->{path} :
+                ""
+               );
+
+    $path = "*" if ($type eq "raw");
+
+    my $child = "";
+    
+    #-------------------------------------------------------------------------
+    # When this is a master function... change the above variables...
+    #-------------------------------------------------------------------------
+    if(($type eq "master") ||
+       ((ref($type) eq "ARRAY") && ($type->[0] eq "master")))
+    {
+        if ($call eq "Get")
+        {
+            my @newSetFuncs;
+            foreach my $func (@setFuncs)
+            {
+                my $funcType = ( exists($funcs->{$func}->{type}) ?
+                                 $funcs->{$func}->{type} :
+                                 undef
+                               );
+
+                push(@newSetFuncs,$func)
+                    if (!defined($funcType) || ($funcType eq "scalar") ||
+                        ($funcType eq "jid") || ($funcType eq "array") ||
+                        ($funcType eq "flag") || ($funcType eq "timestamp") ||
+                        (ref($funcType) eq "ARRAY"));
+            }
+                
+            $child = \@newSetFuncs;
+        }
+        else
+        {
+            $child = \@setFuncs;
+        }
+    }
+    #-------------------------------------------------------------------------
+    # When this is a child based function... change the above variables...
+    #-------------------------------------------------------------------------
+    elsif (exists($funcs->{$var}->{child}))
+    {
+        $child = $funcs->{$var}->{child};
+                
+        #$self->_debug("_xpath_AUTOLOAD: child($child)");
+                
+        if (exists($child->{ns}))
+        {
+            my $addXMLNS = $child->{ns};
+                
+            my $addFuncs = $Net::XMPP::Namespaces::NS{$addXMLNS}->{xpath};
+            my @calls =
+                grep
+                {
+                    exists($addFuncs->{$_}->{type}) &&
+                    ($addFuncs->{$_}->{type} eq "master")
+                }
+                keys(%{$addFuncs});
+
+            if ($#calls > 0)
+            {
+                print STDERR "Warning: I cannot serve two masters.\n";
+            }
+            $child->{master} = $calls[0];
+        }
+    }
+
+    #-------------------------------------------------------------------------
+    # Return the arguments for the xpath function
+    #-------------------------------------------------------------------------
+    #$self->_debug("_xpath_AUTOLOAD: return($type,$path,$child);");
+    return [$type,$path,$child];
+}
+
+
+##############################################################################
+#
+# _xpath_funcs - Return the list of functions either from the FUNCTIONS hash
+#                or from Net::XMPP::Namespaces::NS.
+#
+##############################################################################
+sub _xpath_funcs
+{
+    my $self = shift;
+    my $package = shift;
+    my $call = shift;
+    my $var = shift;
+    my $xmlns = shift;
+
+    my $funcs;
+    
+    my $coreFuncs = $self->{FUNCS};
+    #eval "\$coreFuncs = \\%".$package."::FUNCTIONS";
+    $coreFuncs = {} unless defined($coreFuncs);
+
+    my $nsFuncs = {};
+    $nsFuncs = $Net::XMPP::Namespaces::NS{$xmlns}->{xpath}
+        if (defined($xmlns) && exists($Net::XMPP::Namespaces::NS{$xmlns}));
+
+    foreach my $set ($coreFuncs,$nsFuncs)
+    {
+        if (exists($set->{$var}))
+        {
+            my $type = (exists($set->{$var}->{type}) ?
+                        $set->{$var}->{type} :
+                        "scalar"
+                       );
+
+            my @calls = ('Get','Set','Defined','Remove');
+            @calls = ('Get','Set') if ($type eq "master");
+            @calls = ('Get','Defined','Remove') if ($type eq "child");
+            @calls = @{$set->{$var}->{calls}}
+                if exists($set->{$var}->{calls});
+
+            foreach my $callName (@calls)
+            {
+                if ($callName eq $call)
+                {
+                    $funcs = $set;
+                    last;
+                }
+            }
+        }
+    }
+
+    #-------------------------------------------------------------------------
+    # If we didn't find any functions to return,  Return failure.
+    #-------------------------------------------------------------------------
+    if (!defined($funcs))
+    {
+        #$self->_debug("_xpath_AUTOLOAD: no funcs found");
+        return;
+    }
+
+    return $funcs;
+}
+
+
+##############################################################################
+#
+# _xpath - given a type it calls the appropriate _xpath_* function below
+#
+##############################################################################
+sub _xpath
+{
+    my $self = shift;
+    my $call = shift;
+
+    #$self->_debug("_xpath: call($call) args(",join(",",@_),")");
+    
+    if ($call eq "Get")        { return $self->_xpath_get(@_)    ; }
+    elsif ($call eq "Set")     { return $self->_xpath_set(@_);     }
+    elsif ($call eq "Defined") { return $self->_xpath_defined(@_); }
+    elsif ($call eq "Add")     { return $self->_xpath_add(@_);     }
+    elsif ($call eq "Remove")  { return $self->_xpath_remove(@_);  }
+}
+
+
+##############################################################################
+#
+# _xpath_get - returns the value stored in the node 
+#
+##############################################################################
+sub _xpath_get
+{
+    my $self = shift;
+    my $type = shift;
+    my $xpath = shift;
+    my $childtype = shift;
+    my ($arg0) = shift;
+    
+    #$self->_debug("_xpath_get: self($self) type($type) xpath($xpath) childtype($childtype)");
+    #$self->{TREE}->debug();
+
+    my $subType;
+    ($type,$subType) = $self->_xpath_resolve_types($type);
+    
+
+    #-------------------------------------------------------------------------
+    # type == master
+    #-------------------------------------------------------------------------
+    if ($type eq "master")
+    {
+        my %fields;
+        
+        foreach my $func (sort {$a cmp $b} @{$childtype})
+        {
+            my $defined;
+            eval "\$defined = \$self->Defined$func();";
+            if ($defined)
+            {
+                my @values;
+                eval "\@values = \$self->Get$func();";
+
+                if ($#values > 0)
+                {
+                    $fields{lc($func)} = \@values;
+                }
+                else
+                {
+                    $fields{lc($func)} = $values[0];
+                }
+            }
+        }
+
+        return %fields;
+    }
+    
+    #-------------------------------------------------------------------------
+    # type == node
+    #-------------------------------------------------------------------------
+    # XXX Remove this if there are no problems
+    #if ($type eq "node")
+    #{ 
+        #$self->_debug("_xpath_get: node: xmlns($arg0)") if defined($arg0);
+
+        #my @results;
+        #foreach my $child (@{$self->{CHILDREN}})
+        #{
+            #$self->_debug("_xpath_get: node: child($child)");
+            #$self->_debug("_xpath_get: node: childXML(",$child->GetXML(),")");
+
+            #push(@results,$child)
+            #     if (!defined($arg0) ||
+            #         ($arg0 eq "") || 
+            #         ($child->GetTree(1)->get_attrib("xmlns") eq $arg0));
+            #}
+
+            #return $results[$childtype->{child_index}] if exists($childtype->{child_index});
+            #return @results if (wantarray);
+            #return $results[0];
+            #}
+
+    #-------------------------------------------------------------------------
+    # The rest actually call the XPath, so call it.
+    #-------------------------------------------------------------------------
+    my @nodes = $self->{TREE}->XPath($xpath);
+
+    #-------------------------------------------------------------------------
+    # type == scalar or timestamp
+    #-------------------------------------------------------------------------
+    if (($type eq "scalar") || ($type eq "timestamp"))
+    {
+        return "" if ($#nodes == -1);
+        return $nodes[0];
+    }
+
+    #-------------------------------------------------------------------------
+    # type == jid
+    #-------------------------------------------------------------------------
+    if ($type eq "jid")
+    {
+        return if ($#nodes == -1);
+        return $self->_new_jid($nodes[0])
+            if (defined($arg0) && ($arg0 eq "jid"));
+        return $nodes[0];
+    }
+
+    #-------------------------------------------------------------------------
+    # type == flag
+    #-------------------------------------------------------------------------
+    if ($type eq "flag")
+    {
+        return $#nodes > -1;
+    }
+    
+    #-------------------------------------------------------------------------
+    # type == array
+    #-------------------------------------------------------------------------
+    if ($type eq "array")
+    {
+        return @nodes if (wantarray);
+        return $nodes[0];
+    }
+    
+    #-------------------------------------------------------------------------
+    # type == raw
+    #-------------------------------------------------------------------------
+    if ($type eq "raw")
+    {
+        my $rawXML = "";
+
+        return join("",@{$self->{RAWXML}}) if ($#{$self->{RAWXML}} > -1);
+        
+        foreach my $node (@nodes)
+        {
+            $rawXML .= $node->GetXML();
+        }
+
+        return $rawXML;
+    }
+
+    #-------------------------------------------------------------------------
+    # type == child
+    #-------------------------------------------------------------------------
+    if (($type eq "child") || ($type eq "children") || ($type eq "node"))
+    {
+        my $xmlns = $arg0;
+        $xmlns = $childtype->{ns} if exists($childtype->{ns});
+
+        #$self->_debug("_xpath_get: children: xmlns($xmlns)");
+
+        my @results;
+        foreach my $child (@{$self->{CHILDREN}})
+        {
+            push(@results, $child)
+                if (!defined($xmlns) ||
+                    ($xmlns eq "") ||
+                    ($child->GetTree(1)->get_attrib("xmlns") eq $xmlns));
+        }
+
+        foreach my $node (@nodes)
+        {
+            $node->put_attrib(xmlns=>$xmlns)
+                unless defined($node->get_attrib("xmlns"));
+            my $result = $self->AddChild($node);
+            $self->{TREE}->remove_child($node);
+            push(@results,$result)
+                if (!defined($xmlns) ||
+                    ($xmlns eq "") ||
+                    ($node->get_attrib("xmlns") eq $xmlns));
+        }
+
+        #$self->_debug("_xpath_get: children: ",join(",",@results));
+        return $results[$childtype->{child_index}] if exists($childtype->{child_index});
+        return @results if (wantarray);
+        return $results[0];
+    }
+}
+
+
+##############################################################################
+#
+# _xpath_set - makes the XML tree such that the value was set.
+#
+##############################################################################
+sub _xpath_set
+{
+    my $self = shift;
+    my $type = shift;
+    my $xpath = shift;
+    my $childtype = shift;
+
+    #$self->_debug("_xpath_set: self($self) type($type) xpath($xpath) childtype($childtype)");
+
+    my $subType;
+    ($type,$subType) = $self->_xpath_resolve_types($type);
+
+    my $node = $self->{TREE};
+
+    #$self->_debug("_xpath_set: node($node)");
+
+    #-------------------------------------------------------------------------
+    # When the type is master, the rest of the args are in hash form
+    #-------------------------------------------------------------------------
+    if ($type eq "master")
+    {
+        #$self->_debug("_xpath_set: master: funcs(",join(",",@{$childtype}),")");
+        my %args;
+        while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }
+        #$self->_debug("_xpath_set: args(",%args,")");
+        foreach my $func (sort {$a cmp $b} @{$childtype})
+        {
+            #$self->_debug("_xpath_set: func($func)");
+            if (exists($args{lc($func)}))
+            {
+                #$self->_debug("_xpath_set: \$self->Set$func(\$args{lc(\$func)});");
+                eval "\$self->Set$func(\$args{lc(\$func)});";
+            }
+            elsif ($subType eq "all")
+            {
+                #$self->_debug("_xpath_set: \$self->Set$func();");
+                eval "\$self->Set$func();";
+            }
+        }
+        return;
+    }
+
+    #-------------------------------------------------------------------------
+    # When the type is not master, there can be only one argument.
+    #-------------------------------------------------------------------------
+    my $value = shift;
+
+    if ($type eq "raw")
+    {
+        $self->ClearRawXML();
+        $self->InsertRawXML($value);
+        return;
+    }
+
+    #-------------------------------------------------------------------------
+    # Hook to support special cases.  You can register the specials with
+    # the module and they will ba called based on match.
+    #-------------------------------------------------------------------------
+    if (($subType ne "") && exists($self->{CUSTOMSET}->{$subType}))
+    {
+        #$self->_debug("_xpath_set: custom: subType($subType)");
+        #$self->_debug("_xpath_set: custom: value($value)") if defined($value);
+        $value = &{$self->{CUSTOMSET}->{$subType}}($self,$value);
+    }
+
+    if ($type eq "timestamp")
+    {
+        $value = "" unless defined($value);
+        if ($value eq "") {
+            $value = &Net::XMPP::GetTimeStamp("utc","","stamp");
+        }
+    }
+
+    #$self->_debug("_xpath_set: value($value)") unless !defined($value);
+
+    #-------------------------------------------------------------------------
+    # Now that we have resolved the value, we put it into an array so that we
+    # can support array refs by referring to the values as an array.
+    #-------------------------------------------------------------------------
+    my @values;
+    push(@values,$value);
+    if ($type eq "array")
+    {
+        if (ref($value) eq "ARRAY")
+        {
+            @values = @{$value};
+        }
+    }
+
+    #$self->_debug("_xpath_set: values(",join(",",@values),")") unless !defined($value);
+
+    #-------------------------------------------------------------------------
+    # And now, for each value...
+    #-------------------------------------------------------------------------
+    foreach my $val (@values)
+    {
+        #$self->_debug("_xpath_set: val($val)") unless !defined($val);
+        #$self->_debug("_xpath_set: type($type)");
+
+        next unless (defined($val) || ($type eq "flag"));
+
+        if ((ref($val) ne "") && ($val->isa("Net::XMPP::JID")))
+        {
+            $val = $val->GetJID("full");
+        }
+
+        my $path = $xpath;
+
+        #$self->_debug("_xpath_set: val($val)") unless !defined($val);
+        #$self->_debug("_xpath_set: path($path)");
+    
+        my $childPath = "";
+        while(($path !~ /^\/?\@/) && ($path !~ /^\/?text\(\)/))
+        {
+            #$self->_debug("_xpath_set: Multi-level!!!!");
+            my ($child) = ($path =~ /^\/?([^\/]+)/);
+            $path =~ s/^\/?[^\/]+//;
+            #$self->_debug("_xpath_set: path($path)");
+            #$self->_debug("_xpath_set: childPath($childPath)");
+            
+            if (($type eq "scalar") || ($type eq "jid") || ($type eq "timestamp"))
+            {
+                my $tmpPath = $child;
+                $tmpPath = "$childPath/$child" if ($childPath ne "");
+                
+                my @nodes = $self->{TREE}->XPath("$tmpPath");
+                #$self->_debug("_xpath_set: \$#nodes($#nodes)");
+                if ($#nodes == -1)
+                {
+                    if ($childPath eq "")
+                    {
+                        $node = $self->{TREE}->add_child($child);
+                    }
+                    else
+                    {
+                        my $tree = $self->{TREE}->XPath("$childPath");
+                        $node = $tree->add_child($child);
+                    }
+                }
+                else
+                {
+                    $node = $nodes[0];
+                }
+            }
+
+            if ($type eq "array")
+            {
+                $node = $self->{TREE}->add_child($child);
+            }
+
+            if ($type eq "flag")
+            {
+                $node = $self->{TREE}->add_child($child);
+                return;
+            }
+            
+            $childPath .= "/" unless ($childPath eq "");
+            $childPath .= $child;
+        }
+
+        my ($piece) = ($path =~ /^\/?([^\/]+)/);
+    
+        #$self->_debug("_xpath_set: piece($piece)");
+
+        if ($piece =~ /^\@(.+)$/)
+        {
+            $node->put_attrib($1=>$val);
+        }
+        elsif ($piece eq "text()")
+        {
+            $node->remove_cdata();
+            $node->add_cdata($val);
+        }
+    }
+}
+
+
+##############################################################################
+#
+# _xpath_defined - returns true if there is data for the requested item, false
+#                otherwise.
+#
+##############################################################################
+sub _xpath_defined
+{
+    my $self = shift;
+    my $type = shift;
+    my $xpath = shift;
+    my $childtype = shift;
+    my $ns = shift;
+
+    #$self->_debug("_xpath_defined: self($self) type($type) xpath($xpath) childtype($childtype)");
+    #$self->_debug("_xpath_defined: ns($ns)") if defined($ns);
+    #$self->_debug("_xpath_defined: xml(",$self->{TREE}->GetXML(),")");
+
+    my $subType;
+    ($type,$subType) = $self->_xpath_resolve_types($type);
+
+    if ($type eq "raw")
+    {
+        if ($#{$self->{RAWXML}} > -1)
+        {
+            return 1;
+        }
+    }
+
+    my @nodes = $self->{TREE}->XPath($xpath);
+    my $defined = ($#nodes > -1);
+    
+    #$self->_debug("_xpath_defined: nodes(",join(",",@nodes),")");
+    #$self->_debug("_xpath_defined: ",$#nodes);
+    
+    if (!$defined && (($type eq "child") || ($type eq "children") || ($type eq "node")))
+    {
+        if ((ref($childtype) eq "HASH") && exists($childtype->{ns}))
+        {
+            $ns = $childtype->{ns};
+        }
+    
+        foreach my $packet (@{$self->{CHILDREN}})
+        {
+            if (!defined($ns) || ($packet->GetXMLNS() eq $ns))
+            {
+                $defined = 1;
+                last;
+            }
+        }
+    }
+
+    #$self->_debug("_xpath_defined: defined($defined)");
+
+    return $defined;
+}
+
+
+##############################################################################
+#
+# _xpath_add - returns the value stored in the node 
+#
+##############################################################################
+sub _xpath_add
+{
+    my $self = shift;
+    my $type = shift;
+    my $xpath = shift;
+    my $childtype = shift;
+
+    my $xmlns = $childtype->{ns};
+    my $master = $childtype->{master};
+
+    #$self->_debug("_xpath_add: self($self) type($type) xpath($xpath) childtype($childtype)");
+    #$self->_debug("_xpath_add: xmlns($xmlns) master($master)");
+
+    my $tag = $xpath;
+    if (exists($childtype->{specify_name}))
+    {
+        if (($#_ > -1) && (($#_/2) =~ /^\d+$/))
+        {
+            $tag = shift;
+        }
+        else
+        {
+            $tag = $childtype->{tag};
+        }
+    }
+
+    my $node = new XML::Stream::Node($tag);
+    $node->put_attrib(xmlns=>$xmlns);
+
+    my $obj = $self->AddChild($node);
+    eval "\$obj->Set${master}(\@_);" if defined($master);
+
+    $obj->_skip_xmlns() if exists($childtype->{skip_xmlns});
+
+    return $obj;
+}
+
+
+##############################################################################
+#
+# _xpath_remove - remove the specified thing from the data (I know it's vague.)
+#
+##############################################################################
+sub _xpath_remove
+{
+    my $self = shift;
+    my $type = shift;
+    my $xpath = shift;
+    my $childtype = shift;
+
+    #$self->_debug("_xpath_remove: self($self) type($type) xpath($xpath) childtype($childtype)");
+
+    my $subType;
+    ($type,$subType) = $self->_xpath_resolve_types($type);
+
+    my $nodePath = $xpath;
+    $nodePath =~ s/\/?\@\S+$//;
+    $nodePath =~ s/\/text\(\)$//;
+
+    #$self->_debug("_xpath_remove: xpath($xpath) nodePath($nodePath)");
+
+    my @nodes;
+    @nodes = $self->{TREE}->XPath($nodePath) if ($nodePath ne "");
+
+    #$self->_debug("_xpath_remove: nodes($#nodes)");
+    
+    if ($xpath =~ /\@(\S+)/)
+    {
+        my $attrib = $1;
+        #$self->_debug("_xpath_remove: attrib($attrib)");
+        
+        if ($nodePath eq "")
+        {
+            $self->{TREE}->remove_attrib($attrib);
+        }
+        else
+        {
+            foreach my $node (@nodes)
+            {
+                $node->remove_attrib($attrib);
+            }
+        }
+        return;
+    }
+    
+    foreach my $node (@nodes)
+    {
+        #$self->_debug("_xpath_remove: node GetXML(".$node->GetXML().")");
+        $self->{TREE}->remove_child($node);
+    }
+
+    if ($type eq "child")
+    {
+        my @keep;
+        foreach my $child (@{$self->{CHILDREN}})
+        {
+            #$self->_debug("_xpath_remove: check(".$child->GetXML().")");
+            next if ($child->GetXMLNS() eq $childtype->{ns});
+            #$self->_debug("_xpath_remove: keep(".$child->GetXML().")");
+            push(@keep,$child);
+        }
+        $self->{CHILDREN} = \@keep;
+    }
+}
+
+
+##############################################################################
+#
+# _xpath_resolve_types - Resolve the type and subType into the correct values.
+#
+##############################################################################
+sub _xpath_resolve_types
+{
+    my $self = shift;
+    my $type = shift;
+    
+    my $subType = "";
+    if (ref($type) eq "ARRAY")
+    {
+        if ($type->[0] eq "special")
+        {
+            $subType = $type->[1];
+            $type = "scalar";
+        }
+        elsif ($type->[0] eq "master")
+        {
+            $subType = $type->[1];
+            $type = "master";
+        }
+    }
+    
+    #$self->_debug("_xpath_resolve_types: type($type) subtype($subType)");
+
+    return ($type,$subType);
+}
+
+
+##############################################################################
+#
+# _parse_xmlns - anything that uses the namespace method must first kow what
+#                the xmlns of this thing is... So here's a function to do
+#                just that.
+#
+##############################################################################
+sub _parse_xmlns
+{
+    my $self = shift;
+
+    $self->SetXMLNS($self->{TREE}->get_attrib("xmlns"))
+        if defined($self->{TREE}->get_attrib("xmlns"));
+}
+
+
+##############################################################################
+#
+# _parse_tree - run through the XML::Stream::Node and pull any child nodes
+#               out that we recognize and create objects for them.
+#
+##############################################################################
+sub _parse_tree
+{
+    my $self = shift;
+
+    my @xTrees = $self->{TREE}->XPath('*[@xmlns]');
+
+    if ($#xTrees > -1)
+    {
+        foreach my $xTree (@xTrees)
+        {
+            if( exists($Net::XMPP::Namespaces::NS{$xTrees[0]->get_attrib("xmlns")}))
+            {
+                $self->AddChild($xTree);
+                $self->{TREE}->remove_child($xTree);
+            }
+        }
+    }
+}
+
+
+
+
+##############################################################################
+#+----------------------------------------------------------------------------
+#|
+#| Private Methods
+#|
+#+----------------------------------------------------------------------------
+##############################################################################
+
+sub _check_skip_xmlns
+{
+    my $self = shift;
+    my $xmlns = shift;
+
+    foreach my $skipns (keys(%Net::XMPP::Namespaces::SKIPNS))
+    {
+        return 1 if ($xmlns =~ /^$skipns/);
+    }
+
+    return 0;
+}
+
+
+##############################################################################
+#
+# _debug - helper function for printing debug messages using Net::XMPP::Debug
+#
+##############################################################################
+sub _debug
+{
+    my $self = shift;
+    return $DEBUG->Log99($self->{DEBUGHEADER},": ",@_);
+}
+
+
+##############################################################################
+#
+# _missing_function - send an error if the function is missing.
+#
+##############################################################################
+sub _missing_function
+{
+    my ($parent,$function) = @_;
+    croak("Undefined function $function in package ".ref($parent));
+}
+
+
+##############################################################################
+#
+# _new_jid - create a new JID object.
+#
+##############################################################################
+sub _new_jid
+{
+    my $self = shift;
+    return Net::XMPP::JID->new(@_);
+}
+
+
+##############################################################################
+#
+# _new_packet - create a new Stanza object.
+#
+##############################################################################
+sub _new_packet
+{
+    my $self = shift;
+    return Net::XMPP::Stanza->new(@_);
+}
+
+
+##############################################################################
+#
+# _skip_xmlns - in the GetTree function, cause the xmlns attribute to be
+#               removed for a node that has this set.
+#
+##############################################################################
+sub _skip_xmlns
+{
+    my $self = shift;
+
+    $self->{SKIPXMLNS} = 1;
+}
+
+
+1;
