commit:3ba37ed703feae16d90e4c62ac3d6880dbb8c27a
author:Chip Black
committer:Chip Black
date:Sun Jul 19 17:53:16 2015 -0500
parents:01ebafb74d9ed2a5bfb8a093801e004133a64dc9
Better, now with repeatable auth
diff --git a/server/ChatNoir/Client.pm b/server/ChatNoir/Client.pm
line changes: +105/-36
index 3d8138f..4b97b18
--- a/server/ChatNoir/Client.pm
+++ b/server/ChatNoir/Client.pm
@@ -7,89 +7,134 @@ use ChatNoir::Util qw/pretty_presence/;
 use strict;
 use v5.10;
 
+has app        => ( is => 'ro' );
 has controller => ( is => 'ro' );
-has jid        => ( is => 'ro' );
-has password   => ( is => 'rw' );
 has xmpp       => ( is => 'ro' );
+has jid        => ( is => 'ro' );
+has password   => ( is => 'ro' );
+has active     => ( is => 'ro' );
 
 sub attach {
     my ($self, %params) = @_;
 
-    if ($self->xmpp && $self->xmpp->is_connected) {
-        # If we're reattaching, check that the new password matches the original
-        if ($params{password} ne $self->password) {
-            return;
-        }
-        say "Attaching to existing XMPP connection for ", $self->jid;
-    } else {
-        # Otherwise, attempt a new connection with the given password
-        say "Creating new XMPP connection for ", $self->jid;
-        $self->{password} = $params{password};
-        $self->{xmpp} = AnyEvent::XMPP::IM::Connection->new(
-            jid => $self->jid,
-            password => $self->password,
-        );
-        $self->xmpp->connect;
-    }
-
     if ($self->controller) {
         $self->controller->finish(1000 => "Reconnected");
     }
     $self->{controller} = $params{controller};
 
-    $self->set_up_events;
+    $self->attach_controller_events;
 }
 
-sub set_up_events {
+sub attach_controller_events {
     my ($self) = @_;
 
     $self->controller->tx->on(json => sub {
         my ($tx, $msg) = @_;
         given ($msg->{type}) {
-            when ('message') {
-                $self->xmpp->send_message($msg->to, 'chat', undef, {
-                    body => $msg->{body}
-                });
+            when ('login') {
+                my $password = $msg->{password};
+
+                if (!defined $password) {
+                    $self->controller->finish(1015 => "Login must have password\n");
+                    delete $self->{controller};
+                    return;
+                }
+
+                if ($self->xmpp && $self->xmpp->is_connected) {
+                    # If we're reattaching, check that the new password matches the original
+                    if ($password ne $self->password) {
+                        $self->ws_send('logged-out', reason => 'unauthorized');
+                        return;
+                    }
+                    $self->{active} = 1;
+                    $self->ws_send('logged-in');
+                    $self->app->log->debug("reattaching " . $self->jid);
+                } else {
+                    # Otherwise, attempt a new connection with the given password
+                    $self->app->log->debug("Creating new XMPP connection for " . $self->jid);
+                    $self->{password} = $password;
+                    $self->{xmpp} = AnyEvent::XMPP::IM::Connection->new(
+                        jid => $self->jid,
+                        password => $password,
+                    );
+                    $self->attach_xmpp_events;
+                    $self->xmpp->connect;
+                }
             }
             when ('logout') {
+                $self->active || return;
+                $self->guard_connected || return;
                 $self->xmpp->disconnect;
                 delete $self->{xmpp};
-                $self->ws_send('logged-out');
-                say "Logged out of ", $self->jid;
+                $self->ws_send('logged-out', reason => 'user requested');
+                $self->app->log->debug($self->jid . " logged out");
+            }
+            when ('message') {
+                $self->active || return;
+                $self->guard_connected || return;
+                AnyEvent::XMPP::IM::Message->new(
+                    from => $self->jid,
+                    to => $msg->{to},
+                    body => $msg->{body},
+                )->send($self->xmpp);
+            }
+            when ('get-roster') {
+                $self->active || return;
+                $self->guard_connected || return;
+                $self->ws_send_roster;
+            }
+            when (undef) {
+                $self->app->log->debug("Empty message?");
             }
             default {
-                say "Unknown message type $msg->{type}";
+                $self->app->log->debug("Unknown message type $msg->{type}");
             }
         }
     });
 
     $self->controller->tx->on(finish => sub {
         delete $self->{controller};
-        say $self->jid, " detached";
+        $self->{active} = undef;
+        $self->app->log->debug($self->jid . " detached");
     });
+}
+
+sub attach_xmpp_events {
+    my ($self) = @_;
 
     $self->xmpp->reg_cb(
         session_ready => sub {
             $self->ws_send('logged-in');
-            say "Session ready for ", $self->xmpp->jid;
+            $self->{active} = 1;
+            $self->app->log->debug("Session ready for " . $self->xmpp->jid);
         },
         session_error => sub {
             my ($err) = @_;
-            $self->ws_send('logged-out');
-            say "Session error: ", $err->string;
+            $self->ws_send('logged-out', reason => 'error', error => $err->string);
+            $self->{active} = undef;
+            $self->app->log->error("Session error: " . $err->string);
+        },
+        sasl_error => sub {
+            my ($connection, $err) = @_;
+            $self->{active} = undef;
+            $self->app->log->error($err->string);
+            if ($err->string =~ /not-authorized/) {
+                $self->ws_send('logged-out', reason => 'unauthorized');
+            }
+        },
+        roster_update => sub {
+            my ($connection, $roster, $contacts) = @_;
         },
         presence_update => sub {
             my ($connection, $roster, $contact, $old_presence, $new_presence) = @_;
-            say $contact->jid, ": ", pretty_presence($old_presence),
-                               " => ", pretty_presence($new_presence);
             $self->ws_send('presence',
-                jid => $contact->jid,
+                jid => $new_presence->jid,
                 status => pretty_presence($new_presence),
+                priority => $new_presence->priority,
             );
         },
         message => sub {
             my ($connection, $msg) = @_;
-            say "Message from ", $msg->from, ": ", $msg->body;
 
             AnyEvent::XMPP::IM::Message->new(
                 from => $self->jid,
@@ -116,4 +161,28 @@ sub ws_send {
     }
 }
 
+sub ws_send_roster {
+    my ($self) = @_;
+
+    my @raw_contacts = map {
+        {
+            jid => $_->jid,
+            name => $_->name,
+            groups => [$_->groups],
+            presence => pretty_presence($_->get_priority_presence),
+            subscription => $_->subscription,
+        }
+    } $self->xmpp->get_roster->get_contacts;
+    $self->ws_send('roster', contacts => \@raw_contacts);
+}
+
+sub guard_connected {
+    my ($self) = @_;
+    if (!$self->xmpp || !$self->xmpp->is_connected) {
+        $self->ws_send('error', message => 'not connected');
+        return;
+    }
+    return 1;
+}
+
 1;

diff --git a/server/server.pl b/server/server.pl
line changes: +12/-19
index e3d8e46..66f2eac
--- a/server/server.pl
+++ b/server/server.pl
@@ -12,31 +12,24 @@ use v5.10;
 my $json = JSON->new->utf8;
 my %clients;
 
-websocket '/ws' => sub {
+websocket '/ws/#jid' => sub {
     my $c = shift;
+    my $jid = $c->stash('jid');
 
     $c->inactivity_timeout(300);
     $c->rendered(101);
 
-    $c->tx->once(json => sub {
-        my ($tx, $msg) = @_;
-        
-        if ($msg->{type} ne 'login') {
-            $c->finish(1015 => "Incorrect handshake message\n");
-            return;
+    if (!$clients{$jid}) {
+        $clients{$jid} = ChatNoir::Client->new({jid => $jid, app => $c->app});
+        $c->app->log->debug((scalar keys %clients) . " clients active");
+    }
+    $clients{$jid}->attach(controller => $c);
+    $c->on(finish => sub {
+        if (!$clients{$jid}->xmpp || !$clients{$jid}->xmpp->is_connected) {
+            # Destroy a disconnected client
+            delete $clients{$jid};
+            $c->app->log->debug((scalar keys %clients) . " clients active");
         }
-        my $jid = $msg->{jid};
-        my $password = $msg->{password};
-
-        if (!(defined $jid && defined $password)) {
-            $c->finish(1015 => "Handshake must have jid and password\n");
-            return;
-        }
-
-        if (!$clients{$jid}) {
-            $clients{$jid} = ChatNoir::Client->new({jid => $jid});
-        }
-        $clients{$jid}->attach(controller => $c, password => $password);
     });
 };