Notify::XMPP 0.04

そか。オーバーライドしちゃえばいいんだ。これでXML::StreamをいじらんでもGoogle Talkにつなげられる。

2006-09-03追記

オーバーライドはlocalすべきかも。ref subtechグループ - otsune's SnakeOil - _で始まる内部メソッドをオーバーライドするのはそうやるのか

package Plagger::Plugin::Notify::XMPP;
our $VERSION = '0.04';
use strict;
use warnings;
use base qw( Plagger::Plugin );

use Encode;
use Net::XMPP;

sub register {
    my ($self, $context) = @_;
    $context->register_hook(
        $self,
        'plugin.init'      => \&initialize,
        'publish.entry'    => \&notify,
        'publish.finalize' => \&finalize,
       );
}

sub initialize {
    my ($self, $context, $args) = @_;
    $context->log(debug => ">>initialize");

    my $connectattempts = 3;
    my $connectsleep    = 1;

    $self->{connected} = 0;
    $self->{client} = Net::XMPP::Client->new(debuglevel=>0);

    my $jid = $self->conf->{jid} || do {
        $context->log(error => "missing: jid");
        return;
    };
    unless (index($jid, '@') >= 1) {
        $context->log(error => "missing: jid");
        return;
    }
    my ($username, $componentname) = split /@/, $jid;
    my $password    = $self->conf->{password} || do {
        $context->log(error => "missing: password");
        return;
    };

    my $hostname = $self->conf->{server_host} || $componentname;
    my $port     = $self->conf->{server_port} || '5222';
    my $tls      = $self->conf->{tls} || 0;
    unless ($self->conf->{to}) {
        $context->log(warn => "missing: to");
        return;
    }

    $context->log(debug => "hostname=$hostname port=$port tls=$tls componentname=$componentname");

    my ($status, $error);
    while (--$connectattempts >= 0) {
        $status = $self->{client}->Connect(
            hostname      => $hostname,
            port          => $port,
            tls           => $tls,
            componentname => $componentname,
           );
        last if defined $status;
        $context->log(warn => "retry[$connectattempts]");
        sleep $connectsleep;
    }
    unless (defined $status) {
        $context->log(error => "connection failure");
        return;
    }

    {
        # quick hack to connect Google Talk
        # override XML::Stream-1.22
        no warnings 'redefine';
        *XML::Stream::SASLClient = sub {
            my $self = shift;
            my $sid = shift;
            my $username = shift;
            my $password = shift;

            my $mechanisms = $self->GetStreamFeature($sid,"xmpp-sasl");

            return unless defined($mechanisms);

            my $sasl = new Authen::SASL(mechanism=>join(" ",@{$mechanisms}),
                                        callback=>{
                                            authname => $username."@".($self->{SIDS}->{$sid}->{to} or $self->{SIDS}->{$sid}->{hostname}),
                                            user     => $username,
                                            pass     => $password
                                           }
                                       );

            $self->{SIDS}->{$sid}->{sasl}->{client} = $sasl->client_new();
            $self->{SIDS}->{$sid}->{sasl}->{username} = $username;
            $self->{SIDS}->{$sid}->{sasl}->{password} = $password;
            $self->{SIDS}->{$sid}->{sasl}->{authed} = 0;
            $self->{SIDS}->{$sid}->{sasl}->{done} = 0;

            $self->SASLAuth($sid);
        };
    }

    ($status,$error) = $self->{client}->AuthSend(
        username => $username,
        password => $password,
        resource => 'Plagger',
       );
    unless ($status and $status eq 'ok') {
        $context->log(error => "authentication failure");
        return;
    }

    $self->{connected} = 1;
}

sub notify {
    my ($self, $context, $args) = @_;
    return unless $self->{connected};
    $context->log(debug => ">>notify");

    my $body = $self->templatize('xmpp_notify.tt', $args);
    utf8::decode($body) if utf8::is_utf8($body);

    foreach my $a_to (@{$self->conf->{to}}) {
        $context->log(info => "Notifying <" . $args->{entry}->title . "> to $a_to");
        $self->{client}->MessageSend(
            to   => $a_to,
            body => $body,
           );
    }
}

sub finalize {
    my ($self, $context, $args) = @_;
    return unless $self->{connected};
    $context->log(debug => ">>finalize");
    $self->{client}->Disconnect();
}

1;

__END__

=head1 NAME

Plagger::Plugin::Notify::XMPP - Notify feed updates to XMPP client

=head1 SYNOPSIS

jabber.org

  - module: Notify::XMPP
    config:
      jid: foo@jabber.org
      password: plahplahplah
      to:
        - bar@jabber.org
        - baz@gmail.com
        - qux@jabber.jp

Google Talk

  - module: Notify::XMPP
    config:
      jid: foo@gmail.com
      password: plahplahplah
      server_host: talk.google.com
      tls: 1
      to:
        - bar@jabber.org
        - baz@gmail.com
        - qux@jabber.jp

=head1 DESCRIPTION

This plugin allows you to notify feed updates to XMPP clients (jabber,
Google Talk) using Net::XMPP.

=head1 AUTHOR

HIROSE Masaaki (hirose31)

=head1 SEE ALSO

L<Plagger>, L<Net::XMPP>

=cut

# for Emacsen
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# cperl-indent-parens-as-block: t
# indent-tabs-mode: nil
# coding: euc-jp
# End:

# vi: set ts=4 sw=4 sts=0 :