diff options
Diffstat (limited to 'lib/DJabberd/RosterStorage')
| -rw-r--r-- | lib/DJabberd/RosterStorage/SQLite/Fixed.pm | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/lib/DJabberd/RosterStorage/SQLite/Fixed.pm b/lib/DJabberd/RosterStorage/SQLite/Fixed.pm new file mode 100644 index 0000000..fe41363 --- /dev/null +++ b/lib/DJabberd/RosterStorage/SQLite/Fixed.pm | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | package DJabberd::RosterStorage::SQLite::Fixed; | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use base 'DJabberd::RosterStorage::SQLite'; | ||
| 5 | use DJabberd::Log; | ||
| 6 | use DJabberd::Util; | ||
| 7 | our $logger = DJabberd::Log->get_logger(); | ||
| 8 | |||
| 9 | =head1 NAME | ||
| 10 | |||
| 11 | DJabberd::RosterStorage::SQLite::Fixed - a shared roster implementation for the SQLite roster storage | ||
| 12 | |||
| 13 | =head1 VERSION | ||
| 14 | |||
| 15 | Version 0.02 | ||
| 16 | =cut | ||
| 17 | |||
| 18 | our $VERSION = '0.02'; | ||
| 19 | |||
| 20 | =head1 SYNOPSIS | ||
| 21 | |||
| 22 | <VHost mydomain.com> | ||
| 23 | |||
| 24 | [...] | ||
| 25 | |||
| 26 | <Plugin DJabberd::RosterStorage::SQLite::Fixed> | ||
| 27 | Database jabberroster.sqlite | ||
| 28 | FixedGuestOK yes | ||
| 29 | </Plugin> | ||
| 30 | </VHost> | ||
| 31 | |||
| 32 | Valid command are all command valid in DJabberd::RosterStorage::SQLite Plus the following | ||
| 33 | |||
| 34 | FixedGuestOK - Populate accounts with the shared roster if they are not in the roster itself? | ||
| 35 | Setting this to yes will populate a user who is not in the shared roster with everyone in the shared roster | ||
| 36 | The default is to only populate rosters for users that are part of the shared roster | ||
| 37 | |||
| 38 | =head1 AUTHOR | ||
| 39 | |||
| 40 | Edward Rudd, C<< <urkle at outoforder.cc> >> | ||
| 41 | |||
| 42 | =cut | ||
| 43 | |||
| 44 | =head2 set_config_fixedguestok($self, $guest) | ||
| 45 | |||
| 46 | Called to specify if guests should have the shared roster added to their roster | ||
| 47 | |||
| 48 | =cut | ||
| 49 | |||
| 50 | sub set_config_fixedguestok { | ||
| 51 | my ($self, $guest) = @_; | ||
| 52 | $self->{fixed_guestok} = as_bool $guest; | ||
| 53 | } | ||
| 54 | |||
| 55 | =head2 finalize($self) | ||
| 56 | |||
| 57 | Set defaults for the configuration | ||
| 58 | |||
| 59 | =cut | ||
| 60 | |||
| 61 | sub finalize { | ||
| 62 | my $self = shift; | ||
| 63 | $self->{fixed_guestok} = 0 unless $self->{fixed_guestok}; | ||
| 64 | $self->SUPER::finalize; | ||
| 65 | } | ||
| 66 | |||
| 67 | =head2 get_roster($self, $cb, $jid) | ||
| 68 | |||
| 69 | Gets the Roster for the user | ||
| 70 | |||
| 71 | =cut | ||
| 72 | |||
| 73 | sub get_roster { | ||
| 74 | my ($self, $cb, $jid) = @_; | ||
| 75 | # cb can '->set_roster(Roster)' or decline | ||
| 76 | |||
| 77 | my $myself = lc $jid->as_bare_string; | ||
| 78 | $logger->info("Fixed loading roster for $myself ..."); | ||
| 79 | |||
| 80 | my $on_load_roster = sub { | ||
| 81 | my (undef, $roster) = @_; | ||
| 82 | |||
| 83 | my $pre_ct = $roster->items; | ||
| 84 | $logger->info(" $pre_ct roster items prior to population..."); | ||
| 85 | |||
| 86 | # see which shared contacts already in roster | ||
| 87 | my %has; | ||
| 88 | foreach my $it ($roster->items) { | ||
| 89 | my $jid = $it->jid; | ||
| 90 | $has{lc $jid->as_bare_string} = $it; | ||
| 91 | } | ||
| 92 | |||
| 93 | # add missing shared contacts to the roster | ||
| 94 | my $req_roster = $self->_roster(); | ||
| 95 | if ($self->{fixed_guestok}==0) { | ||
| 96 | my $guestok = 0; | ||
| 97 | foreach my $user ( @$req_roster) { | ||
| 98 | if ($user->{jid} eq $myself) { | ||
| 99 | $guestok = 1; | ||
| 100 | last; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | # Bail if guestOK == 0 && user it not in the roster | ||
| 104 | return if $guestok == 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | foreach my $user ( @$req_roster) { | ||
| 108 | next if $user->{jid} eq $myself; | ||
| 109 | |||
| 110 | my $name = $user->{name}; | ||
| 111 | my $ri = $has{$user->{jid}} || DJabberd::RosterItem->new(jid => $user->{jid}, | ||
| 112 | name => ($user->{name} || $user->{jid}), | ||
| 113 | groups => [$user->{group}]); | ||
| 114 | |||
| 115 | |||
| 116 | $ri->subscription->set_from; | ||
| 117 | $ri->subscription->set_to; | ||
| 118 | $roster->add($ri); | ||
| 119 | } | ||
| 120 | |||
| 121 | my $post_ct = $roster->items; | ||
| 122 | $logger->info(" $post_ct roster items post population..."); | ||
| 123 | |||
| 124 | $cb->set_roster($roster); | ||
| 125 | }; | ||
| 126 | |||
| 127 | my $cb2 = DJabberd::Callback->new({set_roster => $on_load_roster, | ||
| 128 | decline => sub { $cb->decline }}); | ||
| 129 | $self->SUPER::get_roster($cb2, $jid); | ||
| 130 | } | ||
| 131 | |||
| 132 | =head2 check_install_schema($self) | ||
| 133 | |||
| 134 | Checks the SQL ite Schema | ||
| 135 | |||
| 136 | =cut | ||
| 137 | |||
| 138 | sub check_install_schema { | ||
| 139 | my $self = shift; | ||
| 140 | |||
| 141 | $self->SUPER::check_install_schema(); | ||
| 142 | |||
| 143 | my $dbh = $self->{dbh}; | ||
| 144 | |||
| 145 | eval { | ||
| 146 | $dbh->do(qq{ | ||
| 147 | CREATE TABLE requiredusers ( | ||
| 148 | jid VARCHAR(255) NOT NULL, | ||
| 149 | fullname VARCHAR(255) NOT NULL, | ||
| 150 | groupname VARCHAR(255) NOT NULL, | ||
| 151 | UNIQUE (jid) | ||
| 152 | )}); | ||
| 153 | }; | ||
| 154 | if ($@ && $@ !~ /table \w+ already exists/) { | ||
| 155 | $logger->logdie("SQL error $@"); | ||
| 156 | die "SQL error: $@\n"; | ||
| 157 | } | ||
| 158 | eval { | ||
| 159 | $dbh->do(qq{ | ||
| 160 | CREATE VIEW RosterPreview AS | ||
| 161 | SELECT ju.jid AS UserID, g.name AS [Group], | ||
| 162 | jr.jid AS ContactID, r.name AS Contact, r.subscription AS Subscription | ||
| 163 | FROM roster r | ||
| 164 | JOIN jidmap ju ON r.userid=ju.jidid | ||
| 165 | JOIN jidmap jr ON r.contactid = jr.jidid | ||
| 166 | JOIN groupitem gi ON gi.contactid=r.contactid | ||
| 167 | JOIN rostergroup g ON g.userid=r.userid AND g.groupid=gi.groupid | ||
| 168 | UNION SELECT r1.jid, r2.groupname, r2.jid, r2.fullname, 3 | ||
| 169 | FROM requiredusers r1, requiredusers r2 | ||
| 170 | WHERE r1.jid != r2.jid}); | ||
| 171 | }; | ||
| 172 | if ($@ && $@ !~ /table \w+ already exists/) { | ||
| 173 | $logger->logdie("SQL error $@"); | ||
| 174 | die "SQL error: $@\n"; | ||
| 175 | } | ||
| 176 | eval { | ||
| 177 | $dbh->do(qq{ | ||
| 178 | CREATE VIEW RosterList AS | ||
| 179 | SELECT J.jidid as LID, J2.jidid as RID, | ||
| 180 | G.groupid as GID, | ||
| 181 | J.jid AS Local, J2.jid AS Remote, | ||
| 182 | G.name AS [Group] | ||
| 183 | FROM jidmap J | ||
| 184 | JOIN rostergroup G ON G.userid=J.jidid | ||
| 185 | JOIN groupitem M ON G.groupid = M.groupid | ||
| 186 | JOIN jidmap J2 ON J2.jidid = M.contactid | ||
| 187 | ORDER BY J.jid, J2.jid}); | ||
| 188 | }; | ||
| 189 | if ($@ && $@ !~ /table \w+ already exists/) { | ||
| 190 | $logger->logdie("SQL error $@"); | ||
| 191 | die "SQL error: $@\n"; | ||
| 192 | } | ||
| 193 | $logger->info("Created all roster tables"); | ||
| 194 | } | ||
| 195 | |||
| 196 | my $last_roster; | ||
| 197 | my $last_roster_time = 0; # unixtime of last SQL suck | ||
| 198 | sub _roster { | ||
| 199 | my $self = shift; | ||
| 200 | my $now = time(); | ||
| 201 | |||
| 202 | # Cache list for 1 minute(s) | ||
| 203 | if ($last_roster && $last_roster_time > $now - 60) { | ||
| 204 | return $last_roster; | ||
| 205 | } | ||
| 206 | |||
| 207 | my $dbh = $self->{dbh}; | ||
| 208 | |||
| 209 | my $sql = qq{ | ||
| 210 | SELECT jid, fullname, groupname FROM requiredusers | ||
| 211 | }; | ||
| 212 | |||
| 213 | my $roster = eval { | ||
| 214 | $dbh->selectall_arrayref($sql); | ||
| 215 | }; | ||
| 216 | $logger->logdie("Failed to load roster: $@") if $@; | ||
| 217 | |||
| 218 | $logger->info("Found ".($#{ @$roster}+1)." Roster users"); | ||
| 219 | |||
| 220 | my @info = (); | ||
| 221 | foreach my $item ( @$roster ) { | ||
| 222 | my $rec = {}; | ||
| 223 | $rec->{'jid'} = $item->[0]; | ||
| 224 | $rec->{'name'} = $item->[1]; | ||
| 225 | $rec->{'group'} = $item->[2]; | ||
| 226 | push @info, $rec; | ||
| 227 | } | ||
| 228 | $logger->info("Loaded ".($#info+1)." Roster users"); | ||
| 229 | $last_roster_time = $now; | ||
| 230 | return $last_roster = \@info; | ||
| 231 | } | ||
| 232 | |||
| 233 | =head2 load_roster_item($self, $jid, $contact_jid, $cb) | ||
| 234 | |||
| 235 | Called when a roster item is added | ||
| 236 | |||
| 237 | =cut | ||
| 238 | |||
| 239 | sub load_roster_item { | ||
| 240 | my ($self, $jid, $contact_jid, $cb) = @_; | ||
| 241 | |||
| 242 | my $is_shared = sub { | ||
| 243 | my $jid = shift; | ||
| 244 | my $roster = $self->_roster(); | ||
| 245 | foreach my $user (@$roster) { | ||
| 246 | if (lc $user->{jid} eq lc $jid->as_bare_string) { return 1; } | ||
| 247 | } | ||
| 248 | return 0; | ||
| 249 | }; | ||
| 250 | |||
| 251 | if ($is_shared->($jid) && $is_shared->($contact_jid)) { | ||
| 252 | my $both = DJabberd::Subscription->new; | ||
| 253 | $both->set_from; | ||
| 254 | $both->set_to; | ||
| 255 | my $rit = DJabberd::RosterItem->new(jid => $contact_jid, | ||
| 256 | subscription => $both); | ||
| 257 | $cb->set($rit); | ||
| 258 | return; | ||
| 259 | } | ||
| 260 | |||
| 261 | $self->SUPER::load_roster_item($jid, $contact_jid, $cb); | ||
| 262 | } | ||
| 263 | |||
| 264 | =head1 COPYRIGHT & LICENSE | ||
| 265 | |||
| 266 | Original work Copyright 2006 Alexander Karelas, Martin Atkins, Brad Fitzpatrick and Aleksandar Milanov. All rights reserved. | ||
| 267 | Copyright 2007 Edward Rudd. All rights reserved. | ||
| 268 | |||
| 269 | This program is free software; you can redistribute it and/or modify it | ||
| 270 | under the same terms as Perl itself. | ||
| 271 | |||
| 272 | =cut | ||
| 273 | |||
| 274 | 1; | ||
