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