[293] in BarnOwl Developers
[D-O-H] r450 - in trunk/owl: . perl/modules
daemon@ATHENA.MIT.EDU (asedeno@MIT.EDU)
Thu Oct 29 18:04:35 2009
Resent-From: nelhage@mit.edu
Resent-To: barnowl-dev-mtg@charon.mit.edu
To: dirty-owl-hackers@mit.edu
From: asedeno@MIT.EDU
Reply-to: dirty-owl-hackers@MIT.EDU
Date: Fri, 3 Nov 2006 22:54:01 -0500 (EST)
Author: asedeno
Date: 2006-11-03 22:54:00 -0500 (Fri, 03 Nov 2006)
New Revision: 450
Modified:
trunk/owl/perl/modules/jabber.pl
trunk/owl/perlglue.xs
Log:
jabber.pl:
* Roster added to buddy list.
* Command jlist added to get roster.
perlglue.xs:
* Exposed owl_fuction_popless_text() as owl::popless_text()
* Exposed owl_fuction_popless_ztext() as owl::popless_ztext()
Modified: trunk/owl/perl/modules/jabber.pl
===================================================================
--- trunk/owl/perl/modules/jabber.pl 2006-11-03 03:56:15 UTC (rev 449)
+++ trunk/owl/perl/modules/jabber.pl 2006-11-04 03:54:00 UTC (rev 450)
@@ -4,19 +4,29 @@
################################################################################
# owl perl jabber support
#
-# Todo:
-# Connect command.
+# XXX Todo:
+# Rosters for MUCs
+# More user feedback
+# * joining MUC
+# * parting MUC
+# * presence (Roster and MUC)
+# Implementing formatting and logging callbacks for C
+# Appropriate callbacks for presence subscription messages.
+# * Current behavior => auto-accept (default for Net::Jabber)
#
################################################################################
our $client;
our $jid;
+our $roster;
sub onStart
{
if(eval{\&owl::queue_message})
{
register_owl_commands();
+ push @::onMainLoop, sub { owl_jabber::onMainLoop(@_) };
+ push @::onGetBuddyList, sub { owl_jabber::onGetBuddyList(@_) };
}
else
{
@@ -25,7 +35,7 @@
# give up silently.
}
}
-push @::onStartSubs, \&onStart;
+push @::onStartSubs, sub { owl_jabber::onStart(@_) };
sub onMainLoop
{
@@ -39,19 +49,71 @@
else #Error
{
queue_admin_msg("Jabber disconnected.");
+ $roster = undef;
$client = undef;
return;
}
if ($::shutdown)
{
+ $roster = undef;
$client->Disconnect();
$client = undef;
return;
}
}
-push @::onMainLoop, \&onMainLoop;
+sub blist_listBuddy
+{
+ my $buddy = shift;
+ my $blistStr .= " ";
+ my %jq = $roster->query($buddy);
+ my $res = $roster->resource($buddy);
+
+ $blistStr .= $jq{name} ? $jq{name} : $buddy->GetJID();
+
+ if ($res)
+ {
+ my %rq = $roster->resourceQuery($buddy, $res);
+ $blistStr .= " [".($rq{show} ? $rq{show} : 'online')."]";
+ $blistStr .= " ".$rq{status} if $rq{status};
+ $blistStr = boldify($blistStr);
+ }
+ else
+ {
+ $blistStr .= $jq{ask} ? " [pending]" : " [offline]";
+ }
+
+ return $blistStr."\n";
+}
+
+sub onGetBuddyList
+{
+ return "" if ($client == undef);
+ my $blist = "\n".boldify("Jabber Roster for ".$jid->GetJID('base'))."\n";
+
+ foreach my $group ($roster->groups())
+ {
+ $blist .= " Group: $group\n";
+ foreach my $buddy ($roster->jids('group',$group))
+ {
+ $blist .= blist_listBuddy($buddy);
+ }
+ }
+
+ my @unsorted = $roster->jids('nogroup');
+ if (@unsorted)
+ {
+ $blist .= " [unsorted]\n";
+ foreach my $buddy (@unsorted)
+ {
+ $blist .= blist_listBuddy($buddy);
+ }
+ }
+
+ $blist .= "\n";
+}
+
################################################################################
### Owl Commands
sub register_owl_commands()
@@ -68,30 +130,31 @@
jwrite => \&cmd_jwrite,
{
summary => "Send a Jabber Message",
- usage => "jwrite JID [-t thread]"
+ usage => "jwrite JID [-g] [-t thread] [-s subject]"
}
);
owl::new_command(
- jchat => \&cmd_jwrite_gc,
+ jlist => \&cmd_jlist,
{
- summary => "Send a Jabber Message",
- usage => "jchat [room]@[server]"
+ summary => "Show your Jabber roster.",
+ usage => "jlist"
}
);
owl::new_command(
- jjoin => \&cmd_join_gc,
+ jmuc => \&cmd_jmuc,
{
- summary => "Joins a jabber groupchat.",
- usage => "jjoin [room]@[server]/[nick]"
+ summary => "Jabber MUC related commands.",
+ description => "jmuc sends jabber commands related to muc.\n\n".
+ "The following commands are available\n\n".
+ "join {muc} Join a muc.\n\n".
+ "part [muc] Part a muc.".
+ " The muc is taken from the current message if not supplied.\n\n".
+ "invite {jid} [muc]\n\n".
+ " Invite {jid} to [muc].\n".
+ " The muc is taken from the current message if not supplied.\n\n",
+ usage => "jmuc {command} {args}"
}
);
- owl::new_command(
- jpart => \&cmd_part_gc,
- {
- summary => "Parts a jabber groupchat.",
- usage => "jpart [room]@[server]/[nick]"
- }
- );
}
sub cmd_login
@@ -101,14 +164,20 @@
queue_admin_msg("Already logged in.");
return;
}
-
- # These strings should not be hard-coded here.
+
+ %muc_roster = ();
$client = Net::Jabber::Client->new();
+ $roster = $client->Roster();
+
+ #XXX Todo: Add more callbacks.
+ # MUC presence handlers
$client->SetMessageCallBacks(chat => sub { owl_jabber::process_incoming_chat_message(@_) },
error => sub { owl_jabber::process_incoming_error_message(@_) },
groupchat => sub { owl_jabber::process_incoming_groupchat_message(@_) },
headline => sub { owl_jabber::process_incoming_headline_message(@_) },
normal => sub { owl_jabber::process_incoming_normal_message(@_) });
+
+ #XXX Todo: Parameterize the arguments to Connect()
my $status = $client->Connect(hostname => 'jabber.mit.edu',
tls => 1,
port => 5222,
@@ -117,12 +186,15 @@
if (!$status)
{
owl::error("We failed to connect");
+ $client = undef;
return;
}
+
my @result = $client->AuthSend(username => $ENV{USER}, resource => 'owl', password => '');
if($result[0] ne 'ok') {
owl::error("Error in connect: " . join(" ", $result[1..$#result]));
+ $roster = undef;
$client->Disconnect();
$client = undef;
return;
@@ -134,6 +206,7 @@
$client->{SERVER}->{hostname}),
resource => 'owl');
+ $roster->fetch();
$client->PresenceSend(priority => 1);
queue_admin_msg("Connected to jabber as ".$jid->GetJID('full'));
@@ -144,6 +217,7 @@
{
if ($client)
{
+ $roster = undef;
$client->Disconnect();
$client = undef;
queue_admin_msg("Jabber disconnected.");
@@ -151,6 +225,16 @@
return "";
}
+sub cmd_jlist
+{
+ if (!$client)
+ {
+ owl::error("You are not logged in to Jabber.");
+ return;
+ }
+ owl::popless_ztext(onGetBuddyList());
+}
+
our $jwrite_to;
our $jwrite_thread;
our $jwrite_subject;
@@ -159,7 +243,7 @@
{
if (!$client)
{
- # Error here
+ owl::error("You are not logged in to Jabber.");
return;
}
@@ -172,8 +256,10 @@
JW_ARG: for (my $i = 1; $i < $argsLen; $i++)
{
- $args[$i] =~ /^-t$/ && ($jwrite_thread = $args[++$i] && next JW_ARG);
- $args[$i] =~ /^-s$/ && ($jwrite_subject = $args[++$i] && next JW_ARG);
+ $args[$i] =~ /^-t$/ && ($jwrite_thread = $args[++$i] and next JW_ARG);
+ $args[$i] =~ /^-s$/ && ($jwrite_subject = $args[++$i] and next JW_ARG);
+ $args[$i] =~ /^-g$/ && ($jwrite_type = "groupchat" and next JW_ARG);
+
if ($jwrite_to ne '')
{
# Too many To's
@@ -188,75 +274,117 @@
$jwrite_to = $args[$i];
}
- if(!$jwrite_to) {
+ if(!$jwrite_to)
+ {
owl::error("Usage: jwrite JID [-t thread] [-s 'subject']");
return;
}
-
+
+
owl::message("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_join_gc
+sub cmd_jmuc
{
if (!$client)
{
- # Error here
+ owl::error("You are not logged in to Jabber.");
return;
}
- if(!$_[1])
+
+ if (!$_[1])
{
- owl::error("Usage: jchat [room]@[server]/[nick]");
+ #XXX TODO: Write general usage for jmuc command.
return;
}
- my $x = new XML::Stream::Node('x');
- $x->put_attrib(xmlns => 'http://jabber.org/protocol/muc');
- $x->add_child('history')->put_attrib(maxchars => '0');
+ my $cmd = $_[1];
+ if ($cmd eq 'join')
+ {
+ if (!$_[2])
+ {
+ owl::error('Usage: jmuc join {muc} [password]');
+ return;
+ }
+ my $muc = $_[2];
+ my $x = new XML::Stream::Node('x');
+ $x->put_attrib(xmlns => 'http://jabber.org/protocol/muc');
+ $x->add_child('history')->put_attrib(maxchars => '0');
+
+ if ($_[3]) #password
+ {
+ $x->add_child('password')->add_cdata($_[3]);
+ }
- my $presence = new Net::Jabber::Presence;
- $presence->SetPresence(to => $_[1]);
- $presence->AddX($x);
-
- $client->Send($presence);
- return "";
-}
-
-sub cmd_part_gc
-{
- if (!$client)
+ my $presence = new Net::Jabber::Presence;
+ $presence->SetPresence(to => $muc);
+ $presence->AddX($x);
+ $client->Send($presence);
+ }
+ elsif ($cmd eq 'part')
{
- # Error here
- return;
+ my $muc;
+ if (!$_[2])
+ {
+ my $m = owl::getcurmsg();
+ if ($m->is_jabber && $m->{jtype} eq 'groupchat')
+ {
+ $muc = $m->{muc};
+ }
+ else
+ {
+ owl::error('Usage: "jmuc part [muc]"');
+ return;
+ }
+ }
+ else
+ {
+ $muc = $_[2];
+ }
+ $client->PresenceSend(to => $muc, type => 'unavailable');
}
- if(!$_[1])
+ elsif ($cmd eq 'invite')
{
- owl::error("Usage: jchat [room]@[server]/[nick]");
- return;
+ my $jid;
+ my $muc;
+
+ owl::error('Usage: jmuc invite {jid} [muc]') if (!$_[2]);
+
+ if (!@_[3])
+ {
+ my $m = owl::getcurmsg();
+ if ($m->is_jabber && $m->{jtype} eq 'groupchat')
+ {
+ $muc = $m->{muc};
+ }
+ else
+ {
+ owl::error('Usage: jmuc invite {jid} [muc]');
+ return;
+ }
+ }
+ else
+ {
+ $muc = $_[3];
+ }
+
+ my $x = new XML::Stream::Node('x');
+ $x->put_attrib(xmlns => 'http://jabber.org/protocol/muc#user');
+ $x->add_child('invite')->put_attrib(to => $_[2]);
+
+ my $message = new Net::Jabber::Message;
+ $message->SetTo($muc);
+ $message->AddX($x);
+
+ $client->Send($message);
}
-
- $client->PresenceSend(to=>$_[1], type=>'unavailable');
- return "";
-}
-
-sub cmd_jwrite_gc
-{
- if (!$client)
+ else
{
- # Error here
- return;
+ owl::error('jmuc: unrecognized command.');
}
-
- $jwrite_to = $_[1];
- $jwrite_thread = "";
- $jwrite_subject = "";
- $jwrite_type = "groupchat";
- my @args = @_;
- my $argsLen = @args;
-
- owl::message("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);
+ return "";
}
################################################################################
@@ -295,7 +423,9 @@
sub process_incoming_error_message
{
my ($session, $j) = @_;
- queue_admin_msg("Error ".$j->GetErrorCode()." sending to ".$j->GetFrom('jid')->GetJID('base'));
+ my %jhash = j2hash($j, 'in');
+ $jhash{type} = 'admin';
+ owl::queue_message(owl::Message->new(%jhash));
}
sub process_incoming_groupchat_message
@@ -315,13 +445,33 @@
sub process_incoming_normal_message
{
my ($session, $j) = @_;
- owl::queue_message(j2o($j, 'in'));
+ my %props = j2hash($j, 'in');
+
+ # XXX TODO: handle things such as MUC invites here.
+
+# if ($j->HasX('http://jabber.org/protocol/muc#user'))
+# {
+# my $x = $j->GetX('http://jabber.org/protocol/muc#user');
+# if ($x->HasChild('invite'))
+# {
+# $props
+# }
+# }
+#
+ owl::queue_message(owl::Message->new(%props));
}
+sub process_muc_presence
+{
+ my ($session, $p) = @_;
+ return unless ($p->HasX('http://jabber.org/protocol/muc#user'));
+
+}
+
### Helper functions
-sub j2o
+sub j2hash
{
my $j = shift;
my $dir = shift;
@@ -329,53 +479,63 @@
my %props = (type => 'jabber',
direction => $dir);
+ my $jtype = $props{jtype} = $j->GetType();
+ my $from = $j->GetFrom('jid');
+ my $to = $j->GetTo('jid');
- $props{replycmd} = "jwrite";
+ $props{from} = $from->GetJID('full');
+ $props{to} = $to->GetJID('full');
- $props{jtype} = $j->GetType();
- $props{jtype} =~ /^(?:headline|error)$/ && {$props{replycmd} = undef};
- $props{jtype} =~ /^groupchat$/ && {$props{replycmd} = "jchat"};
+ $props{recipient} = $to->GetJID('base');
+ $props{sender} = $from->GetJID('base');
+ $props{subject} = $j->GetSubject() if ($j->DefinedSubject());
+ $props{thread} = $j->GetThread() if ($j->DefinedThread());
+ $props{body} = $j->GetBody() if ($j->DefinedBody());
+ $props{error} = $j->GetError() if ($j->DefinedError());
+ $props{error_code} = $j->GetErrorCode() if ($j->DefinedErrorCode());
+ $props{xml} = $j->GetXML();
- $props{isprivate} = $props{jtype} =~ /^(?:normal|chat)$/;
-
- my $reply_to;
- if ($j->DefinedTo())
+ if ($jtype eq 'chat')
{
- my $jid = $j->GetTo('jid');
- $props{recipient} = $jid->GetJID('base');
- $props{to_jid} = $jid->GetJID('full');
- if ($dir eq 'out')
- {
- $reply_to = $props{to_jid};
- $props{jtype} =~ /^groupchat$/ && {$reply_to = $jid->GetJID('base')};
- }
+ $props{replycmd} = "jwrite ".(($dir eq 'in') ? $props{from} : $props{to});
+ $props{isprivate} = 1;
}
- if ($j->DefinedFrom())
+ elsif ($jtype eq 'groupchat')
{
- my $jid = $j->GetFrom('jid');
- $props{sender} = $jid->GetJID('base');
- $props{from_jid} = $jid->GetJID('full');
- $reply_to = $props{from_jid} if ($dir eq 'in');
- if ($dir eq 'in')
+ my $nick = $props{nick} = $from->GetResource();
+ my $room = $props{room} = $from->GetJID('base');
+ $props{replycmd} = "jwrite -g $room";
+
+ $props{sender} = $nick;
+ $props{recipient} = $room;
+
+ if ($props{subject} && !$props{body})
{
- $reply_to = $props{from_jid};
- $props{jtype} =~ /^groupchat$/ && {$reply_to = $jid->GetJID('base')};
+ $props{body} = '['.$nick." has set the topic to: ".$props{subject}."]"
}
}
-
- $props{subject} = $j->GetSubject() if ($j->DefinedSubject());
- $props{body} = $j->GetBody() if ($j->DefinedBody());
-# if ($j->DefinedThread())
-# {
-# $props{thread} = $j->GetThread() if ($j->DefinedThread());
-# $props{replycmd} .= " -t $props{thread}";
-# }
- $props{error} = $j->GetError() if ($j->DefinedError());
- $props{error_code} = $j->GetErrorCode() if ($j->DefinedErrorCode());
- $props{replycmd} .= " $reply_to";
+ elsif ($jtype eq 'normal')
+ {
+ $props{replycmd} = undef;
+ $props{isprivate} = 1;
+ }
+ elsif ($jtype eq 'headline')
+ {
+ $props{replycmd} = undef;
+ }
+ elsif ($jtype eq 'error')
+ {
+ $props{replycmd} = undef;
+ $props{body} = "Error ".$props{error_code}." sending to ".$props{from}."\n".$props{error};
+ }
+
$props{replysendercmd} = $props{replycmd};
+ return %props;
+}
- return owl::Message->new(%props);
+sub j2o
+{
+ return owl::Message->new(j2hash(@_));
}
sub queue_admin_msg
@@ -386,3 +546,18 @@
body => $err);
owl::queue_message($m);
}
+
+sub boldify($)
+{
+ $str = shift;
+
+ return '@b('.$str.')' if ( $str !~ /\)/ );
+ return '@b<'.$str.'>' if ( $str !~ /\>/ );
+ return '@b{'.$str.'}' if ( $str !~ /\}/ );
+ return '@b['.$str.']' if ( $str !~ /\]/ );
+
+ my $txt = "\@b($str";
+ $txt =~ s/\)/\)\@b\[\)\]\@b\(/g;
+ return $txt.')';
+}
+
Modified: trunk/owl/perlglue.xs
===================================================================
--- trunk/owl/perlglue.xs 2006-11-03 03:56:15 UTC (rev 449)
+++ trunk/owl/perlglue.xs 2006-11-04 03:54:00 UTC (rev 450)
@@ -216,3 +216,23 @@
RETVAL = (char *) DATADIR;
OUTPUT:
RETVAL
+
+void
+popless_text(text)
+ char *text
+ CODE:
+ {
+ owl_function_popless_text(text);
+ }
+
+void
+popless_ztext(text)
+ char *text
+ CODE:
+ {
+ owl_fmtext fm;
+ owl_fmtext_init_null(&fm);
+ owl_fmtext_append_ztext(&fm, text);
+ owl_function_popless_fmtext(&fm);
+ owl_fmtext_free(&fm);
+ }