Index: perl/modules/jabber.pl
===================================================================
--- perl/modules/jabber.pl	(revision 84296f61eeceed9c114665d418936c3ab2a5bde9)
+++ perl/modules/jabber.pl	(revision f62550d81815e0f84b029c84468e499ddbdc5915)
@@ -26,5 +26,118 @@
 ################################################################################
 
-our $connections;
+
+################################################################################
+################################################################################
+package owl_jabber::ConnectionManager;
+sub new {
+    my $class = shift;
+    return bless { }, $class;
+}
+
+sub addConnection {
+    my $self = shift;
+    my $jidStr = shift;
+
+    $self->{Client}->{$jidStr} = 
+      Net::Jabber::Client->new(
+          debuglevel => owl::getvar('debug') eq 'on' ? 1 : 0,
+          debugfile => 'jabber.log'
+      );
+    my $refConn = \$self->{Client}->{$jidStr};
+    $self->{Roster}->{$jidStr} = $$refConn->Roster();
+    return $refConn;
+}
+
+sub removeConnection {
+    my $self = shift;
+    my $jidStr = shift;
+    my $ret = 0;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        if ($j eq $jidStr) {
+            $self->{Client}->{$j}->Disconnect();
+            delete $self->{Roster}->{$j};
+            delete $self->{Client}->{$j};
+            $ret = 1;
+        }
+    }
+    return $ret;
+}
+
+sub connected {
+    my $self = shift;
+    return scalar keys %{ $self->{Client} };
+}
+
+sub getJids {
+    my $self = shift;
+    return keys %{ $self->{Client} };
+}
+
+sub jidExists {
+    my $self = shift;
+    my $jidStr = shift;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        return 1 if ($j eq $jidStr);
+    }
+    return 0;
+}
+
+sub sidExists {
+    my $self = shift;
+    my $sid = shift || "";
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        return 1 if ($self->{Client}->{$j}->{SESSION}->{id} eq $sid);
+    }
+    return 0;
+}
+
+sub getConnRefFromSid {
+    my $self = shift;
+    my $sid = shift;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        if ($self->{Client}->{$j}->{SESSION}->{id} eq $sid) {
+            return \$self->{Client}->{$j};
+        }
+    }
+    return undef;
+}
+
+sub getConnRefFromJidStr {
+    my $self = shift;
+    my $jidStr = shift;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        if ($jidStr eq $j) {
+            return \$self->{Client}->{$j};
+        }
+    }
+    return undef;
+}
+
+sub getRosterRefFromSid {
+    my $self = shift;
+    my $sid = shift;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        if ($self->{Client}->{$j}->{SESSION}->{id} eq $sid) {
+            return \$self->{Roster}->{$j};
+        }
+    }
+    return undef;
+}
+
+sub getRosterRefFromJidStr {
+    my $self = shift;
+    my $jidStr = shift;
+    foreach my $j ( keys %{ $self->{Client} } ) {
+        if ($jidStr eq $j) {
+            return \$self->{Roster}->{$j};
+        }
+    }
+    return undef;
+}
+################################################################################
+
+package owl_jabber;
+
+our $conn = new owl_jabber::ConnectionManager unless $conn;;
 our %vars;
 
@@ -45,8 +158,8 @@
 
 sub onMainLoop {
-    return if ( !connected() );
-
-    foreach my $jid ( keys %$connections ) {
-        my $client = \$connections->{$jid}->{client};
+    return if ( !$conn->connected() );
+
+    foreach my $jid ( $conn->getJids() ) {
+        my $client = $conn->getConnRefFromJidStr($jid);
 
         my $status = $$client->Process(0);
@@ -69,5 +182,5 @@
     my $res = $$roster->resource($buddy);
 
-    $blistStr .= $jq{name} ? $jq{name} : $buddy->GetJID();
+    $blistStr .= $jq{name} ? $jq{name} . "\t(" .$buddy->GetJID() . ')' : $buddy->GetJID();
 
     if ($res) {
@@ -96,5 +209,5 @@
     return "" unless $jid;
     my $blist = "";
-    my $roster = \$connections->{$jid}->{roster};
+    my $roster = $conn->getRosterRefFromJidStr($jid);
     if ($$roster) {
         $blist .= "\n" . boldify("Jabber Roster for $jid\n");
@@ -120,5 +233,5 @@
 sub onGetBuddyList {
     my $blist = "";
-    foreach my $jid (keys %{$connections}) {
+    foreach my $jid ($conn->getJids()) {
         $blist .= getSingleBuddyList($jid);
     }
@@ -169,4 +282,12 @@
         }
     );
+    owl::new_command(
+        jroster => \&cmd_jroster,
+        {
+            summary     => "Jabber Roster related commands.",
+	    description => "jroster sends jabber commands related to rosters.\n\n",
+            usage       => "jroster {command} {args}"
+        }
+    );
 }
 
@@ -187,5 +308,5 @@
     }
 
-    if ( $connections->{$jidStr} ) {
+    if ( $conn->jidExists($jidStr) ) {
         owl::error("Already logged in as $jidStr.");
         return;
@@ -219,11 +340,5 @@
     else
     {
-        $connections->{$jidStr}->{client} = Net::Jabber::Client->new(
-            debuglevel => owl::getvar('debug') eq 'on' ? 1 : 0,
-            debugfile => 'jabber.log'
-        );
-        my $client = \$connections->{$jidStr}->{client};
-        $connections->{$jidStr}->{roster} =
-          $connections->{$jidStr}->{client}->Roster();
+        my $client = $conn->addConnection($jidStr);
 
         #XXX Todo: Add more callbacks.
@@ -237,10 +352,14 @@
         );
         $$client->SetPresenceCallBacks(
-            subscribe   => sub { owl_jabber::process_presence_subscribe(@_) },
-            unsubscribe => sub { owl_jabber::process_presence_unsubscribe(@_) });
+#            available    => sub { owl_jabber::process_presence_available(@_) },
+#            unavailable  => sub { owl_jabber::process_presence_available(@_) },
+            subscribe    => sub { owl_jabber::process_presence_subscribe(@_) },
+            subscribed   => sub { owl_jabber::process_presence_subscribed(@_) },
+            unsubscribe  => sub { owl_jabber::process_presence_unsubscribe(@_) },
+            unsubscribed => sub { owl_jabber::process_presence_unsubscribed(@_) });
 
         my $status = $$client->Connect( %{ $vars{jlogin_connhash} } );
         if ( !$status ) {
-            delete $connections->{$jidStr};
+            $conn->removeConnection($jidStr);
             owl::error("We failed to connect");
         }
@@ -251,14 +370,14 @@
             if ( !$vars{jlogin_havepass} && $result[0] == 401 ) {
                 $vars{jlogin_havepass} = 1;
-                delete $connections->{$jidStr};
+                $conn->removeConnection($jidStr);
                 owl::start_password( "Password for $jidStr: ", \&do_login );
                 return "";
             }
-            delete $connections->{$jidStr};
+            $conn->removeConnection($jidStr);
             owl::error(
                 "Error in connect: " . join( " ", @result ) );
         }
             else {
-                $connections->{$jidStr}->{roster}->fetch();
+                ${ $conn->getRosterRefFromJidStr($jidStr) }->fetch();
                 $$client->PresenceSend( priority => 1 );
                 queue_admin_msg("Connected to jabber as $jidStr");
@@ -277,17 +396,16 @@
 sub do_logout {
     my $jid = shift;
-    $connections->{$jid}->{client}->Disconnect();
-    delete $connections->{$jid};
-    queue_admin_msg("Jabber disconnected ($jid).");
+    my $disconnected = $conn->removeConnection($jid);
+    queue_admin_msg("Jabber disconnected ($jid).") if $disconnected;
 }
 
 sub cmd_logout {
     # Logged into multiple accounts
-    if ( connected() > 1 ) {
+    if ( $conn->connected() > 1 ) {
         # Logged into multiple accounts, no accout specified.
         if ( !$_[1] ) {
             my $errStr =
               "You are logged into multiple accounts. Please specify an account to log out of.\n";
-            foreach my $jid ( keys %$connections ) {
+            foreach my $jid ( $conn->getJids() ) {
                 $errStr .= "\t$jid\n";
             }
@@ -298,5 +416,5 @@
             if ( $_[1] eq '-a' )    #All accounts.
             {
-                foreach my $jid ( keys %$connections ) {
+                foreach my $jid ( $conn->getJids() ) {
                     do_logout($jid);
                 }
@@ -311,5 +429,5 @@
     else                            # Only one account logged in.
     {
-        do_logout( ( keys %$connections )[0] );
+        do_logout( ( $conn->getJids() )[0] );
     }
     return "";
@@ -317,5 +435,5 @@
 
 sub cmd_jlist {
-    if ( !( scalar keys %$connections ) ) {
+    if ( !( scalar $conn->getJids() ) ) {
         owl::error("You are not logged in to Jabber.");
         return;
@@ -325,5 +443,5 @@
 
 sub cmd_jwrite {
-    if ( !connected() ) {
+    if ( !$conn->connected() ) {
         owl::error("You are not logged in to Jabber.");
         return;
@@ -332,4 +450,5 @@
     my $jwrite_to      = "";
     my $jwrite_from    = "";
+    my $jwrite_sid     = "";
     my $jwrite_thread  = "";
     my $jwrite_subject = "";
@@ -344,4 +463,5 @@
         'subject=s' => \$jwrite_subject,
         'account=s' => \$jwrite_from,
+        'id=s'     =>  \$jwrite_sid,
         'groupchat' => \$gc
     );
@@ -358,6 +478,6 @@
 
     if ( !$jwrite_from ) {
-        if ( connected() == 1 ) {
-            $jwrite_from = ( keys %$connections )[0];
+        if ( $conn->connected() == 1 ) {
+            $jwrite_from = ( $conn->getJids() )[0];
         }
         else {
@@ -374,4 +494,5 @@
         to      => $jwrite_to,
         from    => $jwrite_from,
+        sid     => $jwrite_sid,
         subject => $jwrite_subject,
         thread  => $jwrite_thread,
@@ -380,11 +501,11 @@
 
     owl::message(
-        "Type your message below.  End with a dot on a line by itself.  ^C will quit."
-       );
+"Type your message below.  End with a dot on a line by itself.  ^C will quit."
+    );
     owl::start_edit_win( join( ' ', @args ), \&process_owl_jwrite );
 }
 
 sub cmd_jmuc {
-    die "You are not logged in to Jabber" unless connected();
+    die "You are not logged in to Jabber" unless $conn->connected();
     my $ocmd = shift;
     my $cmd  = shift;
@@ -449,5 +570,5 @@
     }
 
-    $connections->{$jid}->{client}->Send($presence);
+    ${ $conn->getConnRefFromJidStr($jid) }->Send($presence);
 }
 
@@ -458,5 +579,5 @@
     die("Usage: jmuc part {muc} [-a account]") unless $muc;
 
-    $connections->{$jid}->{client}
+    ${ $conn->getConnRefFromJidStr($jid) }
       ->PresenceSend( to => $muc, type => 'unavailable' );
     queue_admin_msg("$jid has left $muc.");
@@ -477,5 +598,5 @@
     $x->AddInvite();
     $x->GetInvite()->SetTo($invite_jid);
-    $connections->{$jid}->{client}->Send($message);
+    ${ $conn->getConnRefFromJidStr($jid) }->Send($message);
     queue_admin_msg("$jid has invited $invite_jid to $muc.");
 }
@@ -492,6 +613,197 @@
     $x->SetType('submit');
 
-    $connections->{$jid}->{client}->Send($iq);
+    ${ $conn->getConnRefFromJidStr($jid) }->Send($iq);
     queue_admin_msg("Accepted default instant configuration for $muc");
+}
+
+
+#XXX TODO: Consider merging this with jmuc and selecting off the first two args.
+sub cmd_jroster {
+    die "You are not logged in to Jabber" unless $conn->connected();
+    my $ocmd = shift;
+    my $cmd  = shift;
+    if ( !$cmd ) {
+
+        #XXX TODO: Write general usage for jroster command.
+        return;
+    }
+
+    my %jroster_commands = (
+        sub      => \&jroster_sub,
+        unsub    => \&jroster_unsub,
+        add      => \&jroster_add,
+        remove   => \&jroster_remove,
+        auth     => \&jroster_auth,
+        deauth   => \&jroster_deauth
+    );
+
+    my $func = $jroster_commands{$cmd};
+    if ( !$func ) {
+        owl::error("jroster: Unknown command: $cmd");
+        return;
+    }
+
+    {
+        local @ARGV = @_;
+        my $jid;
+        my $name;
+        my @groups;
+        my $purgeGroups;
+        my $getopt = Getopt::Long::Parser->new;
+        $getopt->configure('pass_through');
+        $getopt->getoptions(
+            'account=s' => \$jid,
+            'group=s' => \@groups,
+            'purgegroups' => \$purgeGroups,
+            'name=s' => \$name
+        );
+        $jid ||= defaultJID();
+        if ($jid) {
+            $jid = resolveJID($jid);
+            return unless $jid;
+        }
+        else {
+            owl::error('You must specify an account with -a {jid}');
+        }
+        return $func->( $jid, $name, \@groups, $purgeGroups,  @ARGV );
+    }
+}
+
+sub jroster_sub {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $roster = $conn->getRosterRefFromJidStr($jid);
+
+    # Adding lots of users with the same name is a bad idea.
+    $name = "" unless (1 == scalar(@ARGV));
+
+    my $p = new Net::XMPP::Presence;
+    $p->SetType('subscribe');
+
+    foreach my $to (@ARGV) {
+        jroster_add($jid, $name, \@groups, $purgeGroups, ($to)) unless ($$roster->exists($to));
+
+        $p->SetTo($to);
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($p);
+        queue_admin_msg("You ($baseJid) have requested a subscription to ($to)'s presence.");
+    }
+}
+
+sub jroster_unsub {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $p = new Net::XMPP::Presence;
+    $p->SetType('unsubscribe');
+    foreach my $to (@ARGV) {
+        $p->SetTo($to);
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($p);
+        queue_admin_msg("You ($baseJid) have unsubscribed from ($to)'s presence.");
+    }
+}
+
+sub jroster_add {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $roster = $conn->getRosterRefFromJidStr($jid);
+
+    # Adding lots of users with the same name is a bad idea.
+    $name = "" unless (1 == scalar(@ARGV));
+
+    foreach my $to (@ARGV) {
+        my %jq  = $$roster->query($to);
+        my $iq = new Net::XMPP::IQ;
+        $iq->SetType('set');
+        my $item = new XML::Stream::Node('item');
+        $iq->NewChild('jabber:iq:roster')->AddChild($item);
+
+        my %allGroups = ();
+
+        foreach my $g (@groups) {
+            $allGroups{$g} = $g;
+        }
+
+        unless ($purgeGroups) {
+            foreach my $g (@{$jq{groups}}) {
+                $allGroups{$g} = $g;
+            }
+        }
+
+        foreach my $g (keys %allGroups) {
+            $item->add_child('group')->add_cdata($g);
+        }
+
+        $item->put_attrib(jid => $to);
+        $item->put_attrib(name => $name) if $name;
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($iq);
+        my $msg = "$baseJid: "
+          . ($name ? "$name ($to)" : "($to)")
+          . " is on your roster in the following groups: { "
+          . join(" , ", keys %allGroups)
+          . " }";
+        queue_admin_msg($msg);
+    }
+}
+
+sub jroster_remove {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $iq = new Net::XMPP::IQ;
+    $iq->SetType('set');
+    my $item = new XML::Stream::Node('item');
+    $iq->NewChild('jabber:iq:roster')->AddChild($item);
+    $item->put_attrib(subscription=> 'remove');
+    foreach my $to (@ARGV) {
+        $item->put_attrib(jid => $to);
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($iq);
+        queue_admin_msg("You ($baseJid) have removed ($to) from your roster.");
+    }
+}
+
+sub jroster_auth {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $p = new Net::XMPP::Presence;
+    $p->SetType('subscribed');
+    foreach my $to (@ARGV) {
+        $p->SetTo($to);
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($p);
+        queue_admin_msg("($to) has been subscribed to your ($baseJid) presence.");
+    }
+}
+
+sub jroster_deauth {
+    my $jid = shift;
+    my $name = shift;
+    my @groups = @{ shift() };
+    my $purgeGroups = shift;
+    my $baseJid = baseJID($jid);
+
+    my $p = new Net::XMPP::Presence;
+    $p->SetType('unsubscribed');
+    foreach my $to (@ARGV) {
+        $p->SetTo($to);
+        ${ $conn->getConnRefFromJidStr($jid) }->Send($p);
+        queue_admin_msg("($to) has been unsubscribed from your ($baseJid) presence.");
+    }
 }
 
@@ -509,8 +821,9 @@
         body => $body
     );
+
     $j->SetThread( $vars{jwrite}{thread} )   if ( $vars{jwrite}{thread} );
     $j->SetSubject( $vars{jwrite}{subject} ) if ( $vars{jwrite}{subject} );
 
-    my $m = j2o( $j, 'out' );
+    my $m = j2o( $j, { direction => 'out' } );
     if ( $vars{jwrite}{type} ne 'groupchat' ) {
 
@@ -518,5 +831,12 @@
         owl::queue_message($m);
     }
-    $connections->{ $vars{jwrite}{from} }->{client}->Send($j);
+
+    if ($vars{jwrite}{sid} && $conn->sidExists( $vars{jwrite}{sid} )) {
+        ${ $conn->getConnRefFromSid($vars{jwrite}{sid}) }->Send($j);
+    }
+    else {
+        ${ $conn->getConnRefFromJidStr($vars{jwrite}{from}) }->Send($j);
+    }
+
     delete $vars{jwrite};
     owl::message("");   # Kludge to make the ``type your message...'' message go away
@@ -526,11 +846,13 @@
 
 sub process_incoming_chat_message {
-    my ( $session, $j ) = @_;
-    owl::queue_message( j2o( $j, 'in' ) );
+    my ( $sid, $j ) = @_;
+    owl::queue_message( j2o( $j, { direction => 'in',
+                                   sid => $sid } ) );
 }
 
 sub process_incoming_error_message {
-    my ( $session, $j ) = @_;
-    my %jhash = j2hash( $j, 'in' );
+    my ( $sid, $j ) = @_;
+    my %jhash = j2hash( $j, { direction => 'in',
+                              sid => $sid } );
     $jhash{type} = 'admin';
     owl::queue_message( owl::Message->new(%jhash) );
@@ -538,19 +860,22 @@
 
 sub process_incoming_groupchat_message {
-    my ( $session, $j ) = @_;
+    my ( $sid, $j ) = @_;
 
     # HACK IN PROGRESS (ignoring delayed messages)
     return if ( $j->DefinedX('jabber:x:delay') && $j->GetX('jabber:x:delay') );
-    owl::queue_message( j2o( $j, 'in' ) );
+    owl::queue_message( j2o( $j, { direction => 'in',
+                                   sid => $sid } ) );
 }
 
 sub process_incoming_headline_message {
-    my ( $session, $j ) = @_;
-    owl::queue_message( j2o( $j, 'in' ) );
+    my ( $sid, $j ) = @_;
+    owl::queue_message( j2o( $j, { direction => 'in',
+                                   sid => $sid } ) );
 }
 
 sub process_incoming_normal_message {
-    my ( $session, $j ) = @_;
-    my %props = j2hash( $j, 'in' );
+    my ( $sid, $j ) = @_;
+    my %jhash = j2hash( $j, { direction => 'in',
+                              sid => $sid } );
 
     # XXX TODO: handle things such as MUC invites here.
@@ -565,12 +890,108 @@
     #    }
     #
-    owl::queue_message( owl::Message->new(%props) );
+    owl::queue_message( owl::Message->new(%jhash) );
 }
 
 sub process_muc_presence {
-    my ( $session, $p ) = @_;
+    my ( $sid, $p ) = @_;
     return unless ( $p->HasX('http://jabber.org/protocol/muc#user') );
-
-}
+}
+
+
+sub process_presence_available {
+    my ( $sid, $p ) = @_;
+    my $from = $p->GetFrom();
+    my $to = $p->GetTo();
+    my $type = $p->GetType();
+    my %props = (
+        to => $to,
+        from => $from,
+        recipient => $to,
+        sender => $from,
+        type => 'jabber',
+        jtype => $p->GetType(),
+        status => $p->GetStatus(),
+        show => $p->GetShow(),
+        xml => $p->GetXML(),
+        direction => 'in');
+
+    if ($type eq '' || $type eq 'available') {
+        $props{body} = "$from is now online. ";
+        $props{loginout} = 'login';
+    }
+    else {
+        $props{body} = "$from is now offline. ";
+        $props{loginout} = 'logout';
+    }
+    $props{replysendercmd} = $props{replycmd} = "jwrite $from -i $sid";
+    owl::queue_message(owl::Message->new(%props));
+}
+
+sub process_presence_subscribe {
+    my ( $sid, $p ) = @_;
+    my $from = $p->GetFrom();
+    my $to = $p->GetTo();
+    my %props = (
+        to => $to,
+        from => $from,
+        xml => $p->GetXML(),
+        type => 'admin',
+        adminheader => 'Jabber presence: subscribe',
+        direction => 'in');
+
+    $props{body} = "The user ($from) wants to subscribe to your ($to) presence.\nReply (r) will authorize, reply-sender (R) will deny.";
+    $props{replycmd} = "jroster auth $from -a $to";
+    $props{replysendercmd} = "jroster deauth $from -a $to";
+    owl::queue_message(owl::Message->new(%props));
+}
+
+sub process_presence_unsubscribe {
+    my ( $sid, $p ) = @_;
+    my $from = $p->GetFrom();
+    my $to = $p->GetTo();
+    my %props = (
+        to => $to,
+        from => $from,
+        xml => $p->GetXML(),
+        type => 'admin',
+        adminheader => 'Jabber presence: unsubscribe',
+        direction => 'in');
+
+    $props{body} = "The user ($from) has been unsubscribed from your ($to) presence.\n";
+    owl::queue_message(owl::Message->new(%props));
+
+    # Find a connection to reply with.
+    foreach my $jid ($conn->getJids()) {
+	my $cJid = new Net::XMPP::JID;
+	$cJid->SetJID($jid);
+	if ($to eq $cJid->GetJID('base') ||
+            $to eq $cJid->GetJID('full')) {
+	    my $reply = $p->Reply(type=>"unsubscribed");
+	    ${ $conn->getConnRefFromJidStr($jid) }->Send($reply);
+	    return;
+	}
+    }
+}
+
+sub process_presence_subscribed {
+    my ( $sid, $p ) = @_;
+    queue_admin_msg("ignoring:".$p->GetXML());
+    # RFC 3921 says we should respond to this with a "subscribe"
+    # but this causes a flood of sub/sub'd presence packets with
+    # some servers, so we won't. We may want to detect this condition
+    # later, and have per-server settings.
+    return;
+}
+
+sub process_presence_unsubscribed {
+    my ( $sid, $p ) = @_;
+    queue_admin_msg("ignoring:".$p->GetXML());
+    # RFC 3921 says we should respond to this with a "subscribe"
+    # but this causes a flood of unsub/unsub'd presence packets with
+    # some servers, so we won't. We may want to detect this condition
+    # later, and have per-server settings.
+    return;
+}
+
 
 ### Helper functions
@@ -578,10 +999,13 @@
 sub j2hash {
     my $j   = shift;
-    my $dir = shift;
-
-    my %props = (
-        type      => 'jabber',
-        direction => $dir
-    );
+    my %initProps = %{ shift() };
+
+    my $dir = 'none';
+    my %props = ( type => 'jabber' );
+
+    foreach my $k (keys %initProps) {
+        $dir = $initProps{$k} if ($k eq 'direction');
+        $props{$k} = $initProps{$k};
+    }
 
     my $jtype = $props{jtype} = $j->GetType();
@@ -591,6 +1015,4 @@
     $props{from} = $from->GetJID('full');
     $props{to}   = $to->GetJID('full');
-
-    my $account = ( $dir eq 'out' ) ? $props{from} : $props{to};
 
     $props{recipient}  = $to->GetJID('base');
@@ -605,14 +1027,15 @@
     if ( $jtype eq 'chat' ) {
         $props{replycmd} =
-          "jwrite " . ( ( $dir eq 'in' ) ? $props{from} : $props{to} ) . " -a $account";
+          "jwrite " . ( ( $dir eq 'in' ) ? $props{from} : $props{to} );
+        $props{replycmd} .=
+          " -a " . ( ( $dir eq 'out' ) ? $props{from} : $props{to} );
         $props{isprivate} = 1;
-        $props{replysendercmd} = $props{replycmd};
     }
     elsif ( $jtype eq 'groupchat' ) {
         my $nick = $props{nick} = $from->GetResource();
         my $room = $props{room} = $from->GetJID('base');
-        $props{replycmd} = "jwrite -g $room -a $account";
-
-        $props{replysendercmd} = "jwrite " . $from->GetJID('full') . " -a $account";
+        $props{replycmd} = "jwrite -g $room";
+        $props{replycmd} .=
+          " -a " . ( ( $dir eq 'out' ) ? $props{from} : $props{to} );
 
         $props{sender} = $nick || $room;
@@ -640,4 +1063,5 @@
     }
 
+    $props{replysendercmd} = $props{replycmd};
     return %props;
 }
@@ -685,10 +1109,6 @@
 }
 
-sub connected {
-    return scalar keys %$connections;
-}
-
 sub defaultJID {
-    return ( keys %$connections )[0] if ( connected() == 1 );
+    return ( $conn->getJids() )[0] if ( $conn->connected() == 1 );
     return;
 }
@@ -708,13 +1128,7 @@
     # Account fully specified.
     if ( $givenJid->GetResource() ) {
-
         # Specified account exists
-        if ( defined $connections->{$givenJidStr} ) {
-            return $givenJidStr;
-        }
-        else    #Specified account doesn't exist
-        {
-            owl::error("Invalid account: $givenJidStr");
-        }
+        return $givenJidStr if ($conn->jidExists($givenJidStr) );
+        owl::error("Invalid account: $givenJidStr");
     }
 
@@ -726,5 +1140,5 @@
         my $ambiguous = 0;
 
-        foreach my $jid ( keys %$connections ) {
+        foreach my $jid ( $conn->getJids() ) {
             my $cJid = new Net::XMPP::JID;
             $cJid->SetJID($jid);
