package DJabberd::RosterStorage::SQLite::Fixed; use strict; use warnings; use base 'DJabberd::RosterStorage::SQLite'; use DJabberd::Log; use DJabberd::Util; our $logger = DJabberd::Log->get_logger(); =head1 NAME DJabberd::RosterStorage::SQLite::Fixed - a shared roster implementation for the SQLite roster storage =head1 VERSION Version 0.02 =cut our $VERSION = '0.02'; =head1 SYNOPSIS [...] Database jabberroster.sqlite FixedGuestOK yes Valid command are all command valid in DJabberd::RosterStorage::SQLite Plus the following FixedGuestOK - Populate accounts with the shared roster if they are not in the roster itself? Setting this to yes will populate a user who is not in the shared roster with everyone in the shared roster The default is to only populate rosters for users that are part of the shared roster =head1 AUTHOR Edward Rudd, C<< >> =cut =head2 set_config_fixedguestok($self, $guest) Called to specify if guests should have the shared roster added to their roster =cut sub set_config_fixedguestok { my ($self, $guest) = @_; $self->{fixed_guestok} = as_bool $guest; } =head2 finalize($self) Set defaults for the configuration =cut sub finalize { my $self = shift; $self->{fixed_guestok} = 0 unless $self->{fixed_guestok}; $self->SUPER::finalize; } =head2 get_roster($self, $cb, $jid) Gets the Roster for the user =cut sub get_roster { my ($self, $cb, $jid) = @_; # cb can '->set_roster(Roster)' or decline my $myself = lc $jid->as_bare_string; $logger->info("Fixed loading roster for $myself ..."); my $on_load_roster = sub { my (undef, $roster) = @_; my $pre_ct = $roster->items; $logger->info(" $pre_ct roster items prior to population..."); # see which shared contacts already in roster my %has; foreach my $it ($roster->items) { my $jid = $it->jid; $has{lc $jid->as_bare_string} = $it; } # add missing shared contacts to the roster my $req_roster = $self->_roster(); if ($self->{fixed_guestok}==0) { my $guestok = 0; foreach my $user ( @$req_roster) { if ($user->{jid} eq $myself) { $guestok = 1; last; } } # Bail if guestOK == 0 && user it not in the roster return if $guestok == 0; } foreach my $user ( @$req_roster) { next if $user->{jid} eq $myself; my $name = $user->{name}; my $ri = $has{$user->{jid}} || DJabberd::RosterItem->new(jid => $user->{jid}, name => ($user->{name} || $user->{jid}), groups => [$user->{group}]); $ri->subscription->set_from; $ri->subscription->set_to; $roster->add($ri); } my $post_ct = $roster->items; $logger->info(" $post_ct roster items post population..."); $cb->set_roster($roster); }; my $cb2 = DJabberd::Callback->new({set_roster => $on_load_roster, decline => sub { $cb->decline }}); $self->SUPER::get_roster($cb2, $jid); } =head2 check_install_schema($self) Checks the SQL ite Schema =cut sub check_install_schema { my $self = shift; $self->SUPER::check_install_schema(); my $dbh = $self->{dbh}; eval { $dbh->do(qq{ CREATE TABLE requiredusers ( jid VARCHAR(255) NOT NULL, fullname VARCHAR(255) NOT NULL, groupname VARCHAR(255) NOT NULL, UNIQUE (jid) )}); }; if ($@ && $@ !~ /table \w+ already exists/) { $logger->logdie("SQL error $@"); die "SQL error: $@\n"; } eval { $dbh->do(qq{ CREATE VIEW RosterPreview AS SELECT ju.jid AS UserID, g.name AS [Group], jr.jid AS ContactID, r.name AS Contact, r.subscription AS Subscription FROM roster r JOIN jidmap ju ON r.userid=ju.jidid JOIN jidmap jr ON r.contactid = jr.jidid JOIN groupitem gi ON gi.contactid=r.contactid JOIN rostergroup g ON g.userid=r.userid AND g.groupid=gi.groupid UNION SELECT r1.jid, r2.groupname, r2.jid, r2.fullname, 3 FROM requiredusers r1, requiredusers r2 WHERE r1.jid != r2.jid}); }; if ($@ && $@ !~ /table \w+ already exists/) { $logger->logdie("SQL error $@"); die "SQL error: $@\n"; } eval { $dbh->do(qq{ CREATE VIEW RosterList AS SELECT J.jidid as LID, J2.jidid as RID, G.groupid as GID, J.jid AS Local, J2.jid AS Remote, G.name AS [Group] FROM jidmap J JOIN rostergroup G ON G.userid=J.jidid JOIN groupitem M ON G.groupid = M.groupid JOIN jidmap J2 ON J2.jidid = M.contactid ORDER BY J.jid, J2.jid}); }; if ($@ && $@ !~ /table \w+ already exists/) { $logger->logdie("SQL error $@"); die "SQL error: $@\n"; } $logger->info("Created all roster tables"); } my $last_roster; my $last_roster_time = 0; # unixtime of last SQL suck sub _roster { my $self = shift; my $now = time(); # Cache list for 1 minute(s) if ($last_roster && $last_roster_time > $now - 60) { return $last_roster; } my $dbh = $self->{dbh}; my $sql = qq{ SELECT jid, fullname, groupname FROM requiredusers }; my $roster = eval { $dbh->selectall_arrayref($sql); }; $logger->logdie("Failed to load roster: $@") if $@; $logger->info("Found ".($#{ @$roster}+1)." Roster users"); my @info = (); foreach my $item ( @$roster ) { my $rec = {}; $rec->{'jid'} = $item->[0]; $rec->{'name'} = $item->[1]; $rec->{'group'} = $item->[2]; push @info, $rec; } $logger->info("Loaded ".($#info+1)." Roster users"); $last_roster_time = $now; return $last_roster = \@info; } =head2 load_roster_item($self, $jid, $contact_jid, $cb) Called when a roster item is added =cut sub load_roster_item { my ($self, $jid, $contact_jid, $cb) = @_; my $is_shared = sub { my $jid = shift; my $roster = $self->_roster(); foreach my $user (@$roster) { if (lc $user->{jid} eq lc $jid->as_bare_string) { return 1; } } return 0; }; if ($is_shared->($jid) && $is_shared->($contact_jid)) { my $both = DJabberd::Subscription->new; $both->set_from; $both->set_to; my $rit = DJabberd::RosterItem->new(jid => $contact_jid, subscription => $both); $cb->set($rit); return; } $self->SUPER::load_roster_item($jid, $contact_jid, $cb); } =head1 COPYRIGHT & LICENSE Original work Copyright 2006 Alexander Karelas, Martin Atkins, Brad Fitzpatrick and Aleksandar Milanov. All rights reserved. Copyright 2007 Edward Rudd. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;