[293] in BarnOwl Developers

home help back first fref pref prev next nref lref last post

[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);
+	}


home help back first fref pref prev next nref lref last post