1
2=head1 NAME
3
4DJabberd::Component - Abstract class representing a component in DJabberd
5
6=head1 SYNOPSIS
7
8    package MyPackage::DJabberd::MyComponent;
9    use base DJabberd::Component;
10
11    # Example of a component which accepts XEP-0050 Ad-Hoc Commands
12
13    sub finalize {
14        my ($self, $opts) = @_;
15
16        # Let the parent class finalize
17        $self->SUPER::finalize;
18
19        $self->register_iq_handler(
20            "set-{http://jabber.org/protocol/commands}command",
21            sub { $self->handle_adhoc_command(@_) },
22        );
23    }
24
25    sub handle_adhoc_command {
26        my ($self, $vhost, $stanza) = @_;
27
28        # handle the DJabberd::IQ stanza in $stanza,
29        # or send some kind of error response.
30    }
31
32This class provides a parent class for all DJabberd components. Components
33that inherit from this class can then be used directly by the server as delivery plugins,
34or used directly by other classes.
35
36See L<DJabberd::Component::Example> for an example component implementation.
37
38=head1 USAGE
39
40This class is a specialization of L<DJabberd::Agent|DJabberd::Agent> which is aimed
41at those wishing to create "components", which are software agents that handle
42an entire XMPP domain, including all of the nodes within that domain.
43
44In most cases, users of this class will use the API exposed by L<DJabberd::Agent|DJabberd::Agent>.
45However, there is some special behaviour to note and some additional methods
46that are not part of the basic agent class.
47
48=cut
49
50package DJabberd::Component;
51
52use base 'DJabberd::Agent';
53use strict;
54use DJabberd::Log;
55use DJabberd::Util qw(exml);
56
57our $logger = DJabberd::Log->get_logger();
58
59sub register {
60    my ($self, $vhost) = @_;
61
62    $self->SUPER::register($vhost);
63    $logger->debug("Component ".$self." will serve domain ".$self->domain);
64    return;
65
66}
67
68=head2 handles_destination($self, $to_jid, $vhost)
69
70This class provides an overriden version of this method which responds with
71a true value if and only if the domain of the JID given matches the domain
72of the component itself. It is unlikely that you will need to override this.
73
74=cut
75sub handles_destination {
76    my ($self, $to_jid, $vhost) = @_;
77    return ($to_jid && $to_jid->domain eq $self->domain);
78}
79
80sub domain {
81    return $_[0]->vhost->server_name;
82}
83
84=head2 handle_stanza($self, $vhost, $stanza)
85
86The overriden version of this method has special behavior when dealing with
87stanzas that are addressed to nodes within the domain handled by your
88component. (That is, jids of the form C<user@example.com>, rather than just C<example.com>.)
89
90When such a stanza is received, the C<get_node> method will be called to retrieve
91an object representing that node.
92
93If you wish to do low-level handling of all incoming stanzas, you may override this
94method. As with DJabberd::Agent, overriding this method will disable all of the
95higher-level handler methods unless you delegate to the overriden method.
96
97If you would like to preserve the higher-level handler methods but not handle
98nodes as separate objects, you may like to override this method to call the original
99version from DJabberd::Agent as follows:
100
101    sub handle_stanza {
102        $self->DJabberd::Agent::handle_stanza(@_);
103    }
104
105With this method in place, the C<get_node> method will no longer be used and all
106suitable stanzas will be passed to this instance's own handler methods regardless of
107the destination JID's node name.
108
109=cut
110sub handle_stanza {
111    my ($self, $vhost, $stanza) = @_;
112
113    if ($stanza->to_jid->node) {
114        my $node = $self->get_node($stanza->to_jid->node);
115        unless ($node) {
116            my $error = $stanza->make_error_response('404', 'cancel', 'item-not-found');
117            $error->deliver($vhost);
118            return;
119        }
120        return $node->handle_stanza($vhost, $stanza);
121    }
122    else {
123        $self->SUPER::handle_stanza($vhost, $stanza);
124    }
125
126}
127
128=head2 get_node($self, $nodename)
129
130Called when this class wants to find an agent to handle a given node within this
131component's domain.
132
133Most normal implementations of this method will return an instance of a subclass of
134L<DJabberd::Agent::Node|DJabberd::Agent::Node>, but really any
135L<DJabberd::Agent|DJabberd::Agent> subclass will do as long as it'll accept stanzas
136addressed to the nodename given.
137
138=cut
139sub get_node {
140    my ($self, $nodename) = @_;
141
142    return undef;
143}
144
145sub name {
146    my ($self) = @_;
147
148    return $self->domain;
149}
150
151=head2 vcard($self)
152
153The overriden version of this method will return a vCard where the full name is set
154to the domain name of the component. Subclasses may override this to do something
155more fancy.
156
157=cut
158sub vcard {
159    my ($self) = @_;
160
161    return "<FN>".exml($self->name)."</FN>";
162}
163
164=head2 identities($self)
165
166The overriden version of this method returns a single, generic identity which
167indicates that the component is a "branch" in the Service Discovery tree.
168This is included because some clients seem to get a little upset if a JID
169supports disco but has no identities.
170
171=cut
172sub identities {
173    my ($self) = @_;
174
175    return [
176        [ 'hierarchy', 'branch', $self->name ],
177    ];
178}
179
180# Internal utility method.
181# I don't think this is used anymore
182sub send_stanza {
183    my ($self, $stanza) = @_;
184
185    $stanza->deliver($self->vhost);
186}
187
1881;
189
190=head1 SEE ALSO
191
192See also L<DJabberd::Agent>, L<DJabberd::Agent::Node>.
193
194=head1 COPYRIGHT
195
196Copyright 2007-2008 Martin Atkins
197
198This module is part of the DJabberd distribution and is covered by the distribution's
199overall licence.
200
201=cut
202