[Bps-public-commit] djabberd-rosterstorage-sqlite branch, master, created. 18ad1289d81788a1e0de945234cd40986d666c17
Alex Vandiver
alexmv at bestpractical.com
Wed May 26 01:39:35 EDT 2010
The branch, master has been created
at 18ad1289d81788a1e0de945234cd40986d666c17 (commit)
- Log -----------------------------------------------------------------
commit ce5f2011c251f2a6c2272a388bb3fc1cf8d994d7
Author: Brad Fitzpatrick <brad at danga.com>
Date: Wed Sep 27 21:18:06 2006 +0000
moving the SQLite roster storage plugin
git-svn-id: http://code.sixapart.com/svn/djabberd/trunk@688 43dd9337-660f-0410-bd32-e7601923a1a1
diff --git a/lib/DJabberd/RosterStorage/SQLite.pm b/lib/DJabberd/RosterStorage/SQLite.pm
new file mode 100644
index 0000000..1f629ce
--- /dev/null
+++ b/lib/DJabberd/RosterStorage/SQLite.pm
@@ -0,0 +1,382 @@
+package DJabberd::RosterStorage::SQLite;
+# abstract base class
+use strict;
+use warnings;
+use base 'DJabberd::RosterStorage';
+
+use DBI;
+use DJabberd::Log;
+our $logger = DJabberd::Log->get_logger();
+
+use vars qw($_respect_subscription);
+
+sub set_config_database {
+ my ($self, $dbfile) = @_;
+ $self->{dbfile} = $dbfile;
+ $logger->info("Loaded SQLite RosterStorage using file '$dbfile'");
+}
+
+sub finalize {
+ my $self = shift;
+ die "No 'Database' configured'" unless $self->{dbfile};
+
+ my $dbh = DBI->connect_cached("dbi:SQLite:dbname=$self->{dbfile}","","", { RaiseError => 1, PrintError => 0, AutoCommit => 1 });
+ $self->{dbh} = $dbh;
+ $self->check_install_schema;
+ return $self;
+}
+
+sub check_install_schema {
+ my $self = shift;
+ my $dbh = $self->{dbh};
+
+ eval {
+ $dbh->do(qq{
+ CREATE TABLE jidmap (
+ jidid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ jid VARCHAR(255) NOT NULL,
+ UNIQUE (jid)
+ )});
+ $dbh->do(qq{
+ CREATE TABLE roster (
+ userid INTEGER REFERENCES jidmap NOT NULL,
+ contactid INTEGER REFERENCES jidmap NOT NULL,
+ name VARCHAR(255),
+ subscription INTEGER NOT NULL REFERENCES substates DEFAULT 0,
+ PRIMARY KEY (userid, contactid)
+ )});
+ $dbh->do(qq{
+ CREATE TABLE rostergroup (
+ groupid INTEGER PRIMARY KEY NOT NULL,
+ userid INTEGER REFERENCES jidmap NOT NULL,
+ name VARCHAR(255),
+ UNIQUE (userid, name)
+ )});
+ $dbh->do(qq{
+ CREATE TABLE groupitem (
+ groupid INTEGER REFERENCES jidmap NOT NULL,
+ contactid INTEGER REFERENCES jidmap NOT NULL,
+ PRIMARY KEY (groupid, contactid)
+ )});
+
+ };
+ if ($@ && $@ !~ /table \w+ already exists/) {
+ $logger->logdie("SQL error $@");
+ die "SQL error: $@\n";
+ }
+
+ $logger->info("Created all roster tables");
+
+}
+
+sub blocking { 1 }
+
+sub get_roster {
+ my ($self, $cb, $jid) = @_;
+
+
+ $logger->debug("Getting roster for '$jid'");
+
+ my $dbh = $self->{dbh};
+
+ my $roster = DJabberd::Roster->new;
+
+ my $sql = qq{
+ SELECT r.contactid, r.name, r.subscription, jmc.jid
+ FROM roster r, jidmap jm, jidmap jmc
+ WHERE r.userid=jm.jidid and jm.jid=? and jmc.jidid=r.contactid
+ };
+
+ # contacts is { contactid -> $row_hashref }
+ my $contacts = eval {
+ $dbh->selectall_hashref($sql, "contactid", undef, $jid->as_bare_string);
+ };
+ $logger->logdie("Failed to load roster: $@") if $@;
+
+ foreach my $contact (values %$contacts) {
+ my $item =
+ DJabberd::RosterItem->new(
+ jid => $contact->{jid},
+ name => $contact->{name},
+ subscription => DJabberd::Subscription->from_bitmask($contact->{subscription}),
+ );
+
+ # convert all the values in the hashref into RosterItems
+ $contacts->{$contact->{contactid}} = $item;
+ $roster->add($item);
+ }
+
+ # get all the groups, and add them to the roster items
+ eval {
+ $sql = qq{
+ SELECT rg.name, gi.contactid
+ FROM rostergroup rg, jidmap j, groupitem gi
+ WHERE gi.groupid=rg.groupid AND rg.userid=j.jidid AND j.jid=?
+ };
+ my $sth = $dbh->prepare($sql);
+ $sth->execute($jid->as_bare_string);
+ while (my ($group_name, $contactid) = $sth->fetchrow_array) {
+ my $ri = $contacts->{$contactid} or next;
+ $ri->add_group($group_name);
+ }
+ };
+ $logger->logdie("Failed to load roster groups: $@") if $@;
+ $logger->debug(" ... got groups, calling set_roster..");
+
+ $cb->set_roster($roster);
+
+}
+
+# to be called outside of a transaction, in auto-commit mode
+sub _jidid_alloc {
+ my ($self, $jid) = @_;
+ my $dbh = $self->{dbh};
+ my $jids = $jid->as_bare_string;
+ my $id = eval {
+ $dbh->selectrow_array("SELECT jidid FROM jidmap WHERE jid=?",
+ undef, $jids);
+ };
+ $logger->logdie("Failed to select from jidmap: $@") if $@;
+ return $id if $id;
+
+ eval {
+ $dbh->do("INSERT INTO jidmap (jidid, jid) VALUES (NULL, ?)",
+ undef, $jids);
+ };
+ $logger->logdie("_jidid_alloc failed: $@") if $@;
+
+ $id = $dbh->last_insert_id(undef, undef, "jidmap", "jidid")
+ or $logger->logdie("Failed to allocate a number in _jidid_alloc");
+
+ return $id;
+}
+
+# to be called outside of a transaction, in auto-commit mode
+sub _groupid_alloc {
+ my ($self, $userid, $name) = @_;
+ my $dbh = $self->{dbh};
+ my $id = eval {
+ $dbh->selectrow_array("SELECT groupid FROM rostergroup WHERE userid=? AND name=?",
+ undef, $userid, $name);
+ };
+ $logger->logdie("Failed to select from groupid: $@") if $@;
+ return $id if $id;
+
+ eval {
+ $dbh->do("INSERT INTO rostergroup (groupid, userid, name) VALUES (NULL, ?, ?)",
+ undef, $userid, $name);
+ };
+ $logger->logdie("_groupid_alloc failed: $@") if $@;
+
+ $id = $dbh->last_insert_id(undef, undef, "rostergroup", "groupid")
+ or $logger->logdie("Failed to allocate a number in _groupid_alloc");
+
+ return $id;
+}
+
+sub set_roster_item {
+ my ($self, $cb, $jid, $ritem) = @_;
+ local $_respect_subscription = 1;
+ $logger->debug("Set roster item");
+ $self->addupdate_roster_item($cb, $jid, $ritem);
+}
+
+sub addupdate_roster_item {
+ my ($self, $cb, $jid, $ritem) = @_;
+ my $dbh = $self->{dbh};
+
+ my $userid = $self->_jidid_alloc($jid);
+ my $contactid = $self->_jidid_alloc($ritem->jid);
+
+ unless ($userid && $contactid) {
+ $cb->error("no userid and contactid");
+ return;
+ }
+
+ $dbh->begin_work or
+ $logger->logdie("Failed to begin work");
+
+ my $fail = sub {
+ my $reason = shift;
+ die "Failing to addupdate: $reason";
+ $dbh->rollback;
+ $cb->error($reason);
+ return;
+ };
+
+ my $exist_row = $dbh->selectrow_hashref("SELECT * FROM roster WHERE userid=? AND contactid=?",
+ undef, $userid, $contactid);
+
+
+ my %in_group; # groupname -> 1
+
+ if ($exist_row) {
+ my @groups = $self->_groups_of_contactid($userid, $contactid);
+ my %to_del; # groupname -> groupid
+ foreach my $g (@groups) {
+ $in_group{$g->[1]} = 1;
+ $to_del {$g->[1]} = $g->[0];
+ }
+ foreach my $gname ($ritem->groups) {
+ delete $to_del{$gname};
+ }
+ if (my $in = join(",", values %to_del)) {
+ $dbh->do("DELETE FROM groupitem WHERE groupid IN ($in) AND contactid=?",
+ undef, $contactid);
+ }
+
+ # by default, don't change subscription, unless we're being called
+ # via set_roster_item.
+ my $sub_value = "subscription";
+ if ($_respect_subscription) {
+ $sub_value = $ritem->subscription->as_bitmask;
+ $logger->debug(" sub_value = $sub_value");
+ } else {
+ # but let's set our subscription in $ritem (since it comes to
+ # us as 'none') because we have to pass it back with the real
+ # value.
+ $ritem->set_subscription(DJabberd::Subscription->from_bitmask($exist_row->{subscription}));
+ }
+
+ my $sql = "UPDATE roster SET name=?, subscription=$sub_value WHERE userid=? AND contactid=?";
+ my @args = ($ritem->name, $userid, $contactid);
+ $dbh->do($sql, undef, @args);
+ } else {
+ $dbh->do("INSERT INTO roster (userid, contactid, name, subscription) ".
+ "VALUES (?,?,?,?)", undef,
+ $userid, $contactid, $ritem->name, $ritem->subscription->as_bitmask)
+ }
+
+ # add to groups
+ foreach my $gname ($ritem->groups) {
+ next if $in_group{$gname}; # already in this group, skip
+ my $gid = $self->_groupid_alloc($userid, $gname);
+ $dbh->do("INSERT OR IGNORE INTO groupitem (groupid, contactid) VALUES (?,?)",
+ undef, $gid, $contactid);
+ }
+
+ $dbh->commit
+ or return $fail->();
+
+ $cb->done($ritem);
+}
+
+# returns ([groupid, groupname], ...)
+sub _groups_of_contactid {
+ my ($self, $userid, $contactid) = @_;
+ my @ret;
+ my $sql = qq{
+ SELECT rg.groupid, rg.name
+ FROM rostergroup rg, groupitem gi
+ WHERE rg.userid=? AND gi.groupid=rg.groupid AND gi.contactid=?
+ };
+ my $sth = $self->{dbh}->prepare($sql);
+ $sth->execute($userid, $contactid);
+ while (my ($gid, $name) = $sth->fetchrow_array) {
+ push @ret, [$gid, $name];
+ }
+ return @ret;
+}
+
+sub delete_roster_item {
+ my ($self, $cb, $jid, $ritem) = @_;
+ $logger->debug("delete roster item!");
+
+ my $dbh = $self->{dbh};
+
+ my $userid = $self->_jidid_alloc($jid);
+ my $contactid = $self->_jidid_alloc($ritem->jid);
+
+ unless ($userid && $contactid) {
+ $cb->error("no userid/contactid in delete");
+ return;
+ }
+
+ $dbh->begin_work;
+
+ my $fail = sub {
+ $dbh->rollback;
+ $cb->error;
+ return;
+ };
+
+ my @groups = $self->_groups_of_contactid($userid, $contactid);
+
+ if (my $in = join(",", map { $_->[0] } @groups)) {
+ $dbh->do("DELETE FROM groupitem WHERE groupid IN ($in) AND contactid=?",
+ undef, $contactid);
+ }
+
+ $dbh->do("DELETE FROM roster WHERE userid=? AND contactid=?",
+ undef, $userid, $contactid)
+ or return $fail->();
+
+ $dbh->commit or $fail->();
+
+ $cb->done;
+}
+
+sub load_roster_item {
+ my ($self, $jid, $contact_jid, $cb) = @_;
+
+ my $dbh = $self->{dbh};
+
+ my $userid = $self->_jidid_alloc($jid);
+ my $contactid = $self->_jidid_alloc($contact_jid);
+ unless ($userid && $contactid) {
+ $cb->error("no userid/contactid in load");
+ return;
+ }
+
+ my $row = $dbh->selectrow_hashref("SELECT name, subscription FROM roster ".
+ "WHERE userid=? AND contactid=?",
+ undef, $userid, $contactid);
+ unless ($row) {
+ $cb->set(undef);
+ return;
+ }
+
+ my $item =
+ DJabberd::RosterItem->new(
+ jid => $contact_jid,,
+ name => $row->{name},
+ subscription => DJabberd::Subscription->from_bitmask($row->{subscription}),
+ );
+ foreach my $ga ($self->_groups_of_contactid($userid, $contactid)) {
+ $item->add_group($ga->[1]);
+ }
+
+ $cb->set($item);
+ return;
+}
+
+sub wipe_roster {
+ my ($self, $cb, $jid) = @_;
+
+ my $dbh = $self->{dbh};
+
+ my $userid = $self->_jidid_alloc($jid);
+ unless ($userid) {
+ $cb->error("no userid/contactid in delete");
+ return;
+ }
+
+ $dbh->begin_work;
+
+ my $fail = sub {
+ $dbh->rollback;
+ $cb->error;
+ return;
+ };
+
+ $dbh->do("DELETE FROM roster WHERE userid=?", undef, $userid)
+ or return $fail->();
+ $dbh->do("DELETE FROM rostergroup WHERE userid=?", undef, $userid)
+ or return $fail->();
+ # FIXME: clean up other tables too.
+
+ $dbh->commit or $fail->();
+ $cb->done;
+}
+
+1;
commit 29c9b648e36e71107b1ce1b0a3735f8f4ac69b1c
Author: Brad Fitzpatrick <brad at danga.com>
Date: Thu Sep 28 21:59:39 2006 +0000
prep for release
git-svn-id: http://code.sixapart.com/svn/djabberd/trunk@718 43dd9337-660f-0410-bd32-e7601923a1a1
diff --git a/lib/DJabberd/RosterStorage/SQLite.pm b/lib/DJabberd/RosterStorage/SQLite.pm
index 1f629ce..4ed3c4c 100644
--- a/lib/DJabberd/RosterStorage/SQLite.pm
+++ b/lib/DJabberd/RosterStorage/SQLite.pm
@@ -8,7 +8,8 @@ use DBI;
use DJabberd::Log;
our $logger = DJabberd::Log->get_logger();
-use vars qw($_respect_subscription);
+use vars qw($_respect_subscription $VERSION);
+$VERSION = '1.00';
sub set_config_database {
my ($self, $dbfile) = @_;
@@ -380,3 +381,52 @@ sub wipe_roster {
}
1;
+
+__END__
+
+=head1 NAME
+
+DJabberd::RosterStorage::SQLite - store your jabber roster in SQLite
+
+=head1 SYNOPSIS
+
+ <Vhost yourserver.com>
+ ...
+ <Plugin DJabberd::RosterStorage::SQLite>
+ Database roster.sqlite
+ </Plugin>
+ ...
+ </VHost>
+
+=head1 DESCRIPTION
+
+This stores your Jabber roster ("buddy list") in an SQLite database.
+
+The schema is automatically created on first use.
+
+=head1 WARNING: BLOCKS!
+
+This plugin blocks. That is, it doesn't do database access async in a
+separate thread. This is not a good plugin to use if you want
+DJabberd to perform well with lots of users.
+
+That said, a certain company is using this for ~100 employees with no
+problems.
+
+=head1 COPYRIGHT
+
+This module is Copyright (c) 2006 Six Apart, Ltd.
+All rights reserved.
+
+You may distribute under the terms of either the GNU General Public
+License or the Artistic License, as specified in the Perl README file.
+
+=head1 WARRANTY
+
+This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
+
+=head1 AUTHORS
+
+Brad Fitzpatrick <brad at danga.com>
+
+Artur Bergman <sky at crucially.net>
diff --git a/t/00-use.t b/t/00-use.t
new file mode 100644
index 0000000..5cf1ccc
--- /dev/null
+++ b/t/00-use.t
@@ -0,0 +1,4 @@
+#!/usr/bin/perl
+use strict;
+use Test::More tests => 1;
+use_ok("DJabberd::RosterStorage::SQLite");
commit aaa0f48c45648a1087954f2309649db6d75ee5b8
Author: Brad Fitzpatrick <brad at danga.com>
Date: Thu Sep 28 21:59:46 2006 +0000
prep for release
git-svn-id: http://code.sixapart.com/svn/djabberd/trunk@719 43dd9337-660f-0410-bd32-e7601923a1a1
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..7bbf421
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+use 5.008;
+use ExtUtils::MakeMaker;
+WriteMakefile(
+ NAME => 'DJabberd::RosterStorage::SQLite',
+ VERSION_FROM => 'lib/DJabberd/RosterStorage/SQLite.pm',
+ ABSTRACT_FROM => 'lib/DJabberd/RosterStorage/SQLite.pm',
+ PREREQ_PM => {
+ 'DJabberd' => '0.80',
+ 'DBD::SQLite' => '1.08',
+ 'DBI', => 0,
+ },
+ AUTHOR => 'Brad Fitzpatrick <brad at danga.com>',
+ );
+
commit 16d19e5aff540f05fb0cfa112c51c72911964af8
Author: Brad Fitzpatrick <brad at danga.com>
Date: Thu Sep 28 21:59:58 2006 +0000
prep for release
git-svn-id: http://code.sixapart.com/svn/djabberd/trunk@720 43dd9337-660f-0410-bd32-e7601923a1a1
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..3139bc1
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,5 @@
+lib/DJabberd/RosterStorage/SQLite.pm
+Makefile.PL
+MANIFEST This list of files
+t/00-use.t
+META.yml Module meta-data (added by MakeMaker)
commit 18ad1289d81788a1e0de945234cd40986d666c17
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Thu Sep 24 17:35:42 2009 -0400
Add tool to unify mixed-case records in existing SQLite rosters
diff --git a/fix-jabber-roster-case b/fix-jabber-roster-case
new file mode 100755
index 0000000..385f79e
--- /dev/null
+++ b/fix-jabber-roster-case
@@ -0,0 +1,176 @@
+#!/usr/bin/perl
+
+use DJabberd::JID;
+use DJabberd::Subscription;
+use DBI;
+use strict;
+use warnings;
+
+die "$0 must be run with a case-folding version of DJabberd\n"
+ unless DJabberd::JID->new('TEST at example.com')->eq(DJabberd::JID->new('test at example.com'));
+
+my $roster = shift @ARGV;
+die "Usage: $0 rosterfile\n" unless defined $roster and -f $roster;
+
+# Note that all work is performed in a transaction for consistency,
+# hence AutoCommit => 0
+my $dbh = DBI->connect(
+ "dbi:SQLite:dbname=$roster", "", "",
+ { RaiseError => 1, PrintError => 0, AutoCommit => 0 }
+);
+
+my $all_dups = $dbh->selectcol_arrayref(<<SQL);
+SELECT LOWER(first.jid)
+ FROM jidmap first
+ JOIN jidmap other
+ ON LOWER(first.jid) = LOWER(other.jid)
+ LEFT JOIN jidmap later
+ ON LOWER(later.jid) = LOWER(first.jid)
+ AND later.jidid > first.jidid
+ GROUP BY first.jid
+HAVING COUNT(DISTINCT other.jid) > 1
+ AND later.jid IS NULL
+ ORDER BY COUNT(DISTINCT other.jid), LOWER(first.jid)
+SQL
+
+for my $name (@{$all_dups}) {
+ warn "Looking at $name\n";
+
+ # Grab the set of dups
+ my $dups = $dbh->selectall_arrayref(<<SQL, undef, $name);
+SELECT jidid, jid FROM jidmap WHERE LOWER(jid) = ?
+SQL
+
+ # For later use, build up the set of JIDs ids to coalesce.
+ my $jidset = "(" . join(",", map {$_->[0]} @$dups) .")";
+ warn " JID equivalence set is $jidset\n";
+
+ # Since this is the output of LOWER(jid), it is _probably_ already
+ # correct, but we run it through canonicalize to get KC
+ # normalization, etc
+ $name = canonicalize($name);
+ next unless defined $name;
+ warn " Canonical form is $name\n";
+
+ # Blow away all of the duplicate JIDs, and re-insert the new name
+ $dbh->do("DELETE FROM jidmap WHERE jidid IN $jidset");
+ $dbh->do("INSERT INTO jidmap(jid) VALUES(?)", undef, $name);
+ my $newid = $dbh->last_insert_id(undef, undef, "jidmap", "jidid");
+ warn " New row id is $newid\n";
+
+ # Next, find all of the places in the roster where the old IDs
+ # showed up, either as userids or contactids
+ my %types = (userid => "contactid", contactid => "userid");
+ for my $column (keys %types) {
+ warn " Looking for occurrances in $column\n";
+ my $other = $types{$column};
+ # We now generate a list of contacts that any of the JIDs as
+ # the user, (or users who have any of the JIDs as a contact)
+ my $otherlist = $dbh->selectcol_arrayref(<<SQL);
+SELECT $other FROM roster WHERE $column IN $jidset GROUP BY $other
+SQL
+ for my $otherval (@$otherlist) {
+ my $merge = $dbh->selectall_arrayref(<<SQL, { Slice => {} });
+SELECT $column, name, subscription FROM roster WHERE $other = $otherval AND $column IN $jidset ORDER BY $column ASC
+SQL
+ # We now have a set of information to merge together, in $merge
+ warn " $otherval references JIDs @{[map $_->{$column}, @$merge]} in $column\n";
+ $dbh->do(<<SQL, undef, $newid, $otherval, merge(@$merge));
+INSERT INTO roster($column, $other, name, subscription) VALUES(?, ?, ?, ?)
+SQL
+ }
+ }
+
+ # Delete any references to old JIDs in the roster
+ $dbh->do("DELETE FROM roster WHERE contactid IN $jidset OR userid IN $jidset");
+
+ # Merge roster groups
+ my $grouplist = $dbh->selectcol_arrayref(<<SQL);
+SELECT name FROM rostergroup WHERE userid IN $jidset GROUP BY name
+SQL
+ for my $groupname (@$grouplist) {
+ # Make a new group of this name for the new JID's id
+ $dbh->do("INSERT INTO rostergroup(userid, name) VALUES(?, ?)", undef, $newid, $groupname);
+ my $groupid = $dbh->last_insert_id(undef, undef, "rostergroup", "groupid");
+ # Find all of the distinct contacts in groups of this name in
+ # any of the old JIDs, and add them to the new group
+ my $merge = $dbh->do(<<SQL, undef, $groupid, $groupname);
+INSERT INTO groupitem(groupid, contactid)
+SELECT ?, groupitem.contactid
+ FROM rostergroup
+ JOIN groupitem
+ ON rostergroup.groupid = groupitem.groupid
+ WHERE rostergroup.name = ?
+ AND rostergroup.userid IN $jidset
+ GROUP BY groupitem.contactid
+SQL
+ }
+ # Remove the old groups
+ $dbh->do("DELETE FROM rostergroup WHERE userid IN $jidset");
+
+ # Look for places the any of the old JIDs appeared in other roster
+ # groups, and replace them with the new JID's id
+ $dbh->do(<<SQL, undef, $newid);
+INSERT INTO groupitem(groupid, contactid)
+SELECT groupid, ?
+ FROM groupitem
+ WHERE contactid IN $jidset
+ GROUP BY groupid
+SQL
+ # Remove the old contacts from the groups
+ $dbh->do("DELETE FROM groupitem WHERE contactid IN $jidset");
+
+}
+
+# The above merely handles cases where one JID appears twice; now we
+# iterate though _all_ names, and lower-case them, to catch JIDs which
+# are incorrectly cased but only appear once.
+my $all = $dbh->selectall_arrayref("SELECT jid, jidid FROM jidmap", { Slice => {} });
+for my $row (@{$all}) {
+ # Canonicalize the name. Since this is the output of LOWER(jid),
+ # it is _probably_ already correct, but we round-trip through
+ # DJabberd::JID to ensure we get KC normalization, and the like
+ my $name = canonicalize($row->{jid});
+ next unless defined $name;
+ next if $name eq $row->{jid};
+ warn "Looking at @{[$row->{jid}]}\n";
+ warn " Canonical form is $name\n";
+ $dbh->do("UPDATE jidmap SET jid = ? WHERE jidid = ?", undef, $name, $row->{jidid});
+}
+
+$dbh->commit or die "Error commiting changes: ".$dbh->errstr;
+
+
+
+
+sub canonicalize {
+ # Canonicalize the name. We round-trip through DJabberd::JID to
+ # ensure we get KC normalization, case folding, and the like
+ my $name = shift;
+ my $jid = DJabberd::JID->new($name);
+ unless ($jid) {
+ warn "Can't make canonical form of $name!";
+ return undef;
+ }
+ return $jid->as_string;
+}
+
+sub merge {
+ my (@merge) = @_;
+
+ # Trivial case
+ return ($merge[0]->{name}, $merge[0]->{subscription})
+ if @merge == 1;
+
+ # More complex case; name is arbitrarily picked to be first non-null name, or null
+ my($name) = grep {defined} map {$_->{name}} @merge;
+ @merge = map {DJabberd::Subscription->from_bitmask($_->{subscription})} @merge;
+
+ # to overrides pendout, from overrides pendin
+ my $merged = DJabberd::Subscription->new;
+ $merged->{to} = 1 if grep {$_->{to}} @merge;
+ $merged->{pendout} = 1 if not $merged->{to} and grep {$_->{pendout}} @merge;
+ $merged->{from} = 1 if grep {$_->{from}} @merge;
+ $merged->{pendin} = 1 if not $merged->{from} and grep {$_->{pendin}} @merge;
+ return $name, $merged->as_bitmask;
+}
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list