desc 'Install the gem locally (user install)'
task :install => :build do
- sh 'gem install --user-install *.gem'
+ sh 'gem install mailshears-*.gem'
end
Rake::TestTask.new do |t|
#!/usr/bin/ruby -wU
#
-# mailshears, to mangle your mail garden
+# mailshears, to prune your mail garden
#
# Load all of our lib/ code.
mode_name = 'prune'
mode = :prune
+# Before doing anything else, check for "-h" and "--help" in the args,
+# because those should cause us to dump usage info and bail out.
+if ARGV.include?('-h') or ARGV.include?('--help') then
+ puts "Usage: #{UserInterface.usage(program_name)}"
+ Kernel.exit(ExitCodes::SUCCESS)
+end
+
+
# If a mode was supplied, it should be in ARGV[0].
if ARGV.length() > 0
mode_names = ['prune', 'rm', 'mv']
--- /dev/null
+mailshears: prune your mail garden
+Copyright (C) 2024 Michael Orlitzky
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
* Implement moving of domains.
-* The AgenDAV "user exists" test is wonky, because there's no real
- users in AgenDAV. Right now we check the "username" column in the
- "prefs" table, but all of the shares (and principals?) have URLs
- instead of usernames. We don't parse the URLs, and instead rely
- on doing find/replace of substrings in e.g. AgendavMv.
-
- In particular, this means that AgenDAV pruning does not work! If
- a user with default preferences is deleted, we don't notice.
-
-* mailshears --help crashes before doing what it should just do:
-
- $ mailshears --help
- ERROR: prune mode takes no additional arguments.
-
* The "pretend mode" output is missing the destination information:
# mailshears mv "test1@example.com" "test2@example.com"
mailshears, 2020-01-30 14:40:09 -0500 (Plugin: MvPlugin)
--------------------------------------------------------
- AgendavMv - Would move user test1@example.com (User not found) to .
DavicalMv - Would move user test1@example.com (User not found) to .
DovecotMv - Would move user test1@example.com (/var/spool/mail/vhosts/example.com/test1) to .
PostfixadminMv - Would move user test1@example.com to .
i_mean_business: false
-plugins: [agendav, davical, dovecot, postfixadmin, roundcube]
-
-agendav_dbhost: localhost
-agendav_dbport: 5432
-agendav_dbopts:
-agendav_dbuser: postgres
-agendav_dbpass:
-agendav_dbname: agendav
+plugins: [davical, dovecot, postfixadmin, roundcube]
davical_dbhost: localhost
davical_dbport: 5432
.TH mailshears 1
.SH NAME
-mailshears \- mangle your mail garden
+mailshears \- prune your mail garden
.SH SYNOPSIS
-\fBmailshears\fR [ [\fBprune\fR] | [\fBrm\fR <\fItargets\fR>] | [\fBmv\fR <\fIsrc\fR> <\fIdst\fR>] ]
+\fBmailshears\fR [--help] [ [\fBprune\fR] | [\fBrm\fR <\fItargets\fR>] | [\fBmv\fR <\fIsrc\fR> <\fIdst\fR>] ]
.SH DESCRIPTION
functionality is provided by plugins. The following are supported at
the moment:
\#
-.IP \(bu 2
-Agendav (database, v2.1.x)
.IP \(bu
DAViCal (database, v1.1.x)
.IP \(bu
.I $ mailshears
mailshears, 2015-11-08 17:01:47 -0500 (Plugin: PrunePlugin)
-----------------------------------------------------------
-AgendavPrune - Removed user booger@example.com.
DavicalPrune - Removed user booger@example.com (Principal ID: 2).
DovecotPrune - Removed user booger@example.com (/tmp/mailshears-test/example.com/booger).
DovecotPrune - Removed user jeremy@example.com (/tmp/mailshears-test/example.com/jeremy).
.I $ mailshears rm adam@example.net
mailshears, 2015-11-08 17:04:42 -0500 (Plugin: RmPlugin)
--------------------------------------------------------
-AgendavRm - Removed user adam@example.net.
DavicalRm - User adam@example.net not found.
DovecotRm - Removed user adam@example.net (/tmp/mailshears-test/example.net/adam).
PostfixadminRm - Removed user adam@example.net.
.I $ mailshears rm example.net
mailshears, 2015-11-08 17:05:42 -0500 (Plugin: RmPlugin)
--------------------------------------------------------
-AgendavRm - Removed domain example.net.
DavicalRm - Domain example.net not found.
DovecotRm - Removed domain example.net (/tmp/mailshears-test/example.net).
PostfixadminRm - Removed domain example.net.
.I $ mailshears mv alice@example.com alice@example.net
mailshears, 2015-11-08 17:06:29 -0500 (Plugin: MvPlugin)
--------------------------------------------------------
-AgendavMv - Source user alice@example.com not found.
DavicalMv - Moved user alice@example.com (Principal ID: 1) to alice@example.net (Principal ID: 1).
DovecotMv - Moved user alice@example.com (/tmp/mailshears-test/example.com/alice) to alice@example.net (/tmp/mailshears-test/example.net/alice).
PostfixadminMv - Moved user alice@example.com to alice@example.net.
Mailshears is configured with a YAML file containing all of the
database settings and plugin information. This file should be located
-at ~/.mailshears.conf.yml, in your home directory.
+at \fI$XDG_CONFIG_HOME\fR/mailshears/mailshears.conf.yml, where
+\fI$XDG_CONFIG_HOME\fR defaults to \fI$HOME\fR/.config.
The following two settings are global:
\#
\#
.IP \(bu
\fIplugins\fR (default: ['postfixadmin'])
-A list of enabled plugins. Valid values are \(dqagendav\(dq,
-\(dqdavical\(dq, \(dqdovecot\(dq, \(dqpostfixadmin\(dq, and
-\(dqroundcube\(dq.
+A list of enabled plugins. Valid values are \(dqdavical\(dq,
+\(dqdovecot\(dq, \(dqpostfixadmin\(dq, and \(dqroundcube\(dq.
.P
The \(dqdovecot\(dq plugin supports the following:
\#
.P
The database plugins all have the same configutation options:
connections settings preceded by the plugin name. So in what follows,
-<plugin> would be replaced by \(dqagendav\(dq, \(dqdavical\(dq, or so
-on. Their meanings should be self-explanatory.
+<plugin> would be replaced by \(dqdavical\(dq, for example. Their
+meanings should be self-explanatory.
\#
.IP \(bu 2
\fI<plugin>_dbhost\fR (default: localhost)
+++ /dev/null
-require 'common/plugin'
-require 'common/user'
-
-# Code that all Agendav plugins ({AgendavPrune}, {AgendavRm},
-# {AgendavMv}) share.
-module AgendavPlugin
-
- # We implement the Plugin "interface."
- include Plugin
-
-
- # Initialize this Agendav {Plugin} with values in *cfg*.
- #
- # @param cfg [Configuration] the configuration for this plugin.
- #
- def initialize(cfg)
- @db_hash = {
- :host => cfg.agendav_dbhost,
- :port => cfg.agendav_dbport,
- :options => cfg.agendav_dbopts,
- :dbname => cfg.agendav_dbname,
- :user => cfg.agendav_dbuser,
- :password => cfg.agendav_dbpass }
- end
-
-
- # Return a list of Agendav users.
- #
- # @return [Array<User>] a list of users contained in the
- # Agendav database.
- #
- def list_users()
- users = []
-
- connection = PG::Connection.new(@db_hash)
-
- # There are also "owner" and "with" fields in the "shares" table,
- # but they contains principal URLs and not a bare username. Thus
- # their format depends on the CalDAV server configuration, and
- # isn't predictable.
- sql_query = 'SELECT username FROM prefs;'
-
- begin
- connection.sync_exec(sql_query) do |result|
- users = result.field_values('username')
- end
- ensure
- # Make sure the connection gets closed even if the query explodes.
- connection.close()
- end
-
- return users.map{ |u| User.new(u) }
- end
-
-
- # Count the number of rows in the "shares" table. Used only for
- # testing.
- #
- # @return [Fixnum] the number of rows in the "shares" table.
- #
- def count_shares()
- count = nil
- connection = PG::Connection.new(@db_hash)
-
- sql_query = 'SELECT count(*) FROM shares;'
- begin
- connection.sync_exec(sql_query) do |result|
- count = result.getvalue(0,0).to_i()
- end
- ensure
- # Make sure the connection gets closed even if the query explodes.
- connection.close()
- end
-
- return count
- end
-
-end
require 'yaml'
# A configuration object that knows how to read options out of a file
-# in <tt>~/.mailshears.conf.yml</tt>. The configuration options can be
-# accessed via methods even though the internal representation is a
-# hash.
+# in <tt>$XDG_CONFIG_HOME/mailshears/mailshears.conf.yml</tt>. The
+# configuration options can be accessed via methods even though the
+# internal representation is a hash.
#
# === Examples
#
#
class Configuration
- # The default path to the user's configuration file.
- USERCONF_PATH = ENV['HOME'] + '/.mailshears.conf.yml'
-
# The hash structure in which we store our configuration options
# internally.
@dict = {}
-
# Initialize a {Configuration} object with the config file at *path*.
#
# @param path [String] the path to the configuration file to
- # load. We check for a file named ".mailshears.conf.yml" in the
- # user's home directory by default.
+ # load. We check for a file named "mailshears.conf.yml" in the
+ # user's XDG configuration directory by default.
#
- def initialize(path = USERCONF_PATH)
+ def initialize(path = nil)
+ if path.nil? then
+ # The default path to the user's configuration file.
+ path = ENV['HOME'] + '/.config'
+ if ENV.has_key?('XDG_CONFIG_HOME') then
+ path = ENV['XDG_CONFIG_HOME']
+ end
+ path += '/mailshears/mailshears.conf.yml'
+ end
+
cfg = default_configuration()
# Now, load the user configuration which will override the
d['i_mean_business'] = false
d['plugins'] = ['postfixadmin']
- d['agendav_dbhost'] = 'localhost'
- d['agendav_dbport'] = 5432
- d['agendav_dbopts'] = ''
- d['agendav_dbuser'] = 'postgres'
- d['agendav_dbpass'] = ''
- d['agendav_dbname'] = 'agendav'
-
d['davical_dbhost'] = 'localhost'
d['davical_dbport'] = 5432
d['davical_dbopts'] = ''
# invocation.
#
def self.usage(program_name)
- return "#{program_name} [prune | rm <target> | mv <src> <dst>]"
+ return "#{program_name} [--help] [prune | rm <target> | mv <src> <dst>]"
end
+++ /dev/null
-require 'pg'
-
-require 'common/agendav_plugin'
-require 'mv/mv_plugin'
-
-
-# Handle moving (renaming) Agendav users in its database. Agendav has
-# no concept of domains.
-#
-class AgendavMv
-
- include AgendavPlugin
- include MvPlugin
-
- # Move the user *src* to *dst* within the Agendav database. This
- # should "rename" him in _every_ table where he is referenced.
- #
- # This can fail if *dst* already exists before the move. It should
- # also be an error if the destination domain doesn't exist. But
- # Agendav doesn't know about domains, so we let that slide.
- #
- # If the source user doesn't exist, we do our best. AgenDAV has a
- # "shares" table that isn't keyed on the username, but rather the
- # principal URL. And its "prefs" table doesn't contain entries for
- # users who have default preferences. As a result, we may need to
- # perform some find/replaces in the "shares" table even if no
- # corresponding user exists in the "prefs" table (which is how we
- # tell if a user exists in AgenDAV). Thus it's not a fatal error if
- # the *src* user doesn't exist.
- #
- # @param src [User] the source user to be moved.
- #
- # @param dst [User] the destination user being moved to.
- #
- def mv_user(src, dst)
- raise UserAlreadyExistsError.new(dst.to_s()) if user_exists(dst)
-
- connection = PG::Connection.new(@db_hash)
- begin
- # The "prefs" table uses the normal username as a key...
- # This should be harmless if the source user does not exist.
- sql_query0 = 'UPDATE prefs SET username = $1 WHERE username = $2;'
- connection.sync_exec_params(sql_query0, [dst.to_s(), src.to_s()])
-
- # But the "shares" table uses encoded principal URLs. For the
- # "shares" table, we need to do a find/replace on the username
- # with its "@" symbol translated to a "%40".
- encoded_src = src.to_s()['@'] = '%40'
- encoded_dst = dst.to_s()['@'] = '%40'
-
- # Unlike in the "rm" plugin, we do modify the "calendar" field
- # here. That's because in the usual legitimate use case, the
- # calendar URL will change when a user moves. This will ALSO
- # affect people who name their calendars something like
- # "user%40example.com", but screw those people.
- sql_queries = ['UPDATE shares SET owner=REPLACE(owner, $2, $1);']
- sql_queries << 'UPDATE shares SET calendar=REPLACE(calendar, $2, $1);'
- sql_queries << 'UPDATE shares SET "with"=REPLACE("with", $2, $1);'
-
- sql_queries.each do |sql_query|
- connection.sync_exec_params(sql_query, [encoded_dst, encoded_src])
- end
- ensure
- # Make sure the connection gets closed even if a query explodes.
- connection.close()
- end
- end
-
-end
require 'pg'
require 'common/davical_plugin'
-require 'rm/rm_plugin'
+require 'mv/mv_plugin'
# Handle moving (renaming) DAViCal users in its database. DAViCal has
# no concept of domains.
+++ /dev/null
-require 'pg'
-
-require 'prune/prune_plugin'
-require 'rm/plugins/agendav'
-
-# Handle the pruning of Agendav users from its database. This class
-# doesn't need to do anything; by inheriting from {AgendavRm}, we get
-# its {AgendavRm#remove_user} method and that's all we need to prune.
-#
-class AgendavPrune < AgendavRm
- # Needed for the magic includers <tt>run()</tt> method.
- include PrunePlugin
-end
+++ /dev/null
-require 'pg'
-
-require 'common/agendav_plugin'
-require 'rm/rm_plugin'
-
-
-# Handle the removal of Agendav users from its database. Agendav has
-# no concept of domains.
-#
-class AgendavRm
-
- include AgendavPlugin
- include RmPlugin
-
-
- # Remove *user* from the Agendav database. This should remove him
- # from _every_ table in which he is referenced.
- #
- # We do not raise an error if the user doesn't exist. This is due to
- # an unfortunate problem with the "user exists" check in AgenDAV.
- # The AgenDAV "shares" table is not tied directly to a username, so
- # we are forced to use a regexp match to decide what rows to delete
- # from that table. We do so regardless of whether or not the username
- # exists in the "prefs" table, because that table stores only non-
- # default preferences -- not all users' preferences.
- #
- # @param user [User] the user to remove.
- #
- def remove_user(user)
- sql_queries = ['DELETE FROM prefs WHERE username = $1;']
-
- # The "shares" table contains principal URLs, and the "@" symbol
- # is usually encoded to "%40". These queries do a regex match on
- # the username after replacing the "%40" with a "@".
- #
- # As a precaution, I haven chosen not to delete based on the
- # "calendar" field here. Nobody should have a calendar named
- # "user%40example.com", but it's not impossible -- and we don't
- # want to delete that calendar when the not-necessarily-related
- # "user@example.com" account is removed. And the usual appearance
- # of the user's email address in the "calendar" field happens when
- # he is also the owner, so the calendar does get deleted in the
- # normal situation.
- sql_queries << "DELETE FROM shares WHERE REPLACE(owner,'%40','@') ~ $1;"
- sql_queries << "DELETE FROM shares WHERE REPLACE(\"with\",'%40','@') ~ $1;"
-
- connection = PG::Connection.new(@db_hash)
- begin
- sql_queries.each do |sql_query|
- connection.sync_exec_params(sql_query, [user.to_s()])
- end
- ensure
- # Make sure the connection gets closed even if a query explodes.
- connection.close()
- end
- end
-
-end
Gem::Specification.new do |s|
s.name = 'mailshears'
- s.version = '0.0.4'
+ s.version = '0.1.0'
s.platform = Gem::Platform::RUBY
s.authors = ['Michael Orlitzky']
s.email = ['michael@orlitzky.com']
- s.homepage = 'http://michael.orlitzky.com/code/mailshears.xhtml'
- s.summary = 'Prune unused mail directories.'
+ s.homepage = 'https://michael.orlitzky.com/code/mailshears.xhtml'
+ s.summary = 'Prune your mail garden'
s.description = <<-EOF
Managing a mail system with virtual users is annoying. The
authoritative database of users is stored in one table, but every
as well. Since these two tasks are related, mailshears does them both.
EOF
- s.license = 'AGPL-3.0'
+ s.license = 'AGPL-3.0-or-later'
s.required_rubygems_version = '>= 1.3.6'
# If you have runtime dependencies, add them here
mkdir -p /tmp/mailshears-test/example.com/jeremy
mkdir -p /tmp/mailshears-test/example.net/adam
-dropdb --if-exists -U postgres agendav
-createdb -U postgres agendav
-psql -U postgres -d agendav < test/sql/agendav.sql
-psql -U postgres -d agendav < test/sql/agendav-fixtures.sql
-
dropdb --if-exists -U postgres davical
createdb -U postgres davical
psql -U postgres -d davical < test/sql/davical.sql
i_mean_business: true
-plugins: [agendav, davical, dovecot, postfixadmin, roundcube]
-
-agendav_dbhost: localhost
-agendav_dbport: 5432
-agendav_dbopts:
-agendav_dbuser: postgres
-agendav_dbpass:
-agendav_dbname: agendav_test
+plugins: [davical, dovecot, postfixadmin, roundcube]
davical_dbhost: localhost
davical_dbport: 5432
davical_dbpass:
davical_dbname: davical_test
+# Warning: unsafe! Fixed paths under /tmp are asking to be exploited
+# by other users on the system.
dovecot_mail_root: /tmp/mailshears-test
postfixadmin_dbhost: localhost
require 'fileutils'
require 'pg'
-class MailshearsTest < MiniTest::Test
+class MailshearsTest < Minitest::Test
# This is that class that most (if not all) of our test cases will
# inherit. It provides the automatic setup and teardown of the
# filesystem and database that the test cases will exercise.
#
# == Databases ==
#
- # 1. agendav_test
- #
- # +------------------------------+
- # | prefs |
- # +--------------------+---------+
- # | username | options |
- # +--------------------+---------+
- # | adam@example.net | herp |
- # +--------------------+---------+
- # | booger@example.com | herp |
- # +------------------ +---------+
- #
- #
- # +---------------------------------------------------------------------------------------------------------------------+
- # | shares |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | sid | owner | calendar | with |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | 1 | /caldav.php/adam%40example.net/ | /caldav.php/adam%40example.net/calendar-default | /beth%40example.net/ |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | 2 | /caldav.php/booger%40example.com/ | /caldav.php/booger%40example.com/calendar-default | /carol%40example.net/ |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- #
- #
- # 2. davical_test
+ # 1. davical_test
#
# +--------------------------------------------------------+
# | usr |
# +---------+--------------+----------------+
#
#
- # 3. postfixadmin_test
+ # 2. postfixadmin_test
#
# +-------------+
# | domain |
# | admin@example.com | example.net |
# +-------------------+-------------+
#
- # 4. roundcube_test
+ # 3. roundcube_test
#
#
# +---------+--------------------+
+++ /dev/null
-/* Add an AgenDAV record for one user only. */
-INSERT INTO prefs (username, options) VALUES ('adam@example.net', 'herp');
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/adam%40example.net/',
- '/caldav.php/adam%40example.net/calendar-default',
- '/beth%40example.net/',
- 'a:0:{}',
- false);
-
-/* Just kidding, here's another one! */
-INSERT INTO prefs (username, options) VALUES ('booger@example.com', 'herp');
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/booger%40example.com/',
- '/caldav.php/booger%40example.com/calendar-default',
- '/carol%40example.net/',
- 'a:0:{}',
- true);
-
-/* These two are missing a prefs entry to test the removal of "shares"
- * entries in that situation.
-*/
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/stinky%40example.com/',
- '/caldav.php/stinky%40example.com/calendar-default',
- '/herp%40example.net/',
- 'a:0:{}',
- true);
-
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/goober%40example.com/',
- '/caldav.php/goober%40example.com/calendar-default',
- '/derp%40example.net/',
- 'a:0:{}',
- true);
+++ /dev/null
---
--- PostgreSQL database dump
---
-
-SET statement_timeout = 0;
-SET lock_timeout = 0;
-SET client_encoding = 'UTF8';
-SET standard_conforming_strings = on;
-SET check_function_bodies = false;
-SET client_min_messages = warning;
-SET row_security = off;
-
---
--- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
---
-
-CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
-
-
---
--- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
---
-
-COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
-
-
-SET search_path = public, pg_catalog;
-
-SET default_tablespace = '';
-
-SET default_with_oids = false;
-
---
--- Name: prefs; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE prefs (
- username character varying(255) NOT NULL,
- options text NOT NULL
-);
-
-
-ALTER TABLE prefs OWNER to postgres;
-
---
--- Name: principals; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE principals (
- url character varying(255) NOT NULL,
- displayname character varying(255) NOT NULL,
- email character varying(255) NOT NULL
-);
-
-
-ALTER TABLE principals OWNER to postgres;
-
---
--- Name: schema_versions; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE schema_versions (
- version character varying(255) NOT NULL
-);
-
-
-ALTER TABLE schema_versions OWNER to postgres;
-
---
--- Name: sessions; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE sessions (
- sess_id character varying(128) NOT NULL,
- sess_data bytea NOT NULL,
- sess_lifetime integer NOT NULL,
- sess_time integer NOT NULL
-);
-
-
-ALTER TABLE sessions OWNER to postgres;
-
---
--- Name: shares; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE shares (
- sid integer NOT NULL,
- owner character varying(255) NOT NULL,
- calendar character varying(255) NOT NULL,
- "with" character varying(255) NOT NULL,
- options text NOT NULL,
- rw boolean NOT NULL
-);
-
-
-ALTER TABLE shares OWNER to postgres;
-
---
--- Name: shares_sid_seq; Type: SEQUENCE; Schema: public; Owner: agendav
---
-
-CREATE SEQUENCE shares_sid_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER TABLE shares_sid_seq OWNER to postgres;
-
---
--- Name: shares_sid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: agendav
---
-
-ALTER SEQUENCE shares_sid_seq OWNED BY shares.sid;
-
-
---
--- Name: sid; Type: DEFAULT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY shares ALTER COLUMN sid SET DEFAULT nextval('shares_sid_seq'::regclass);
-
-
---
--- Name: prefs_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY prefs
- ADD CONSTRAINT prefs_pkey PRIMARY KEY (username);
-
-
---
--- Name: principals_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY principals
- ADD CONSTRAINT principals_pkey PRIMARY KEY (url);
-
-
---
--- Name: schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY schema_versions
- ADD CONSTRAINT schema_versions_pkey PRIMARY KEY (version);
-
-
---
--- Name: sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY sessions
- ADD CONSTRAINT sessions_pkey PRIMARY KEY (sess_id);
-
-
---
--- Name: shares_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY shares
- ADD CONSTRAINT shares_pkey PRIMARY KEY (sid);
-
-
---
--- Name: idx_905f717c9890e20e; Type: INDEX; Schema: public; Owner: agendav
---
-
-CREATE INDEX idx_905f717c9890e20e ON shares USING btree ("with");
-
-
---
--- Name: idx_905f717ccf60e67c6ea9a146; Type: INDEX; Schema: public; Owner: agendav
---
-
-CREATE INDEX idx_905f717ccf60e67c6ea9a146 ON shares USING btree (owner, calendar);
-
-
---
--- Name: public; Type: ACL; Schema: -; Owner: postgres
---
-
-REVOKE ALL ON SCHEMA public FROM PUBLIC;
-REVOKE ALL ON SCHEMA public FROM postgres;
-GRANT ALL ON SCHEMA public TO postgres;
-GRANT ALL ON SCHEMA public TO PUBLIC;
-
-
---
--- PostgreSQL database dump complete
---
-
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'mv/plugins/agendav'
require 'mv/plugins/davical'
require 'mv/plugins/dovecot'
require 'mv/plugins/postfixadmin'
actual = output_buffer.string()
expected =
- "AgendavMv - Moved user alice@example.com (User not found) " +
- "to alice@example.net (User not found).\n" +
"DavicalMv - Moved user alice@example.com (Principal ID: 1) " +
"to alice@example.net (Principal ID: 1).\n" +
"DovecotMv - Moved user alice@example.com " +
# Now check the database.
- amv = AgendavMv.new(cfg)
- actual = amv.list_users()
- expected = [User.new('adam@example.net'),User.new('booger@example.com')]
- assert_equal(expected.sort(), actual.sort())
-
dmv = DavicalMv.new(cfg)
actual = dmv.list_users()
expected = [User.new('alice@example.net'), User.new('booger@example.com')]
# Skip output verification, it's ugly. But make sure the database
# has what we expect.
- amv = AgendavMv.new(cfg)
- actual = amv.list_users()
- expected = [User.new('adam@example.net'),User.new('booger@example.com')]
- assert_equal(expected.sort(), actual.sort())
-
dmv = DavicalMv.new(cfg)
actual = dmv.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'prune/plugins/agendav'
require 'prune/plugins/davical'
require 'prune/plugins/dovecot'
require 'prune/plugins/postfixadmin'
# Both of our tests have the same expected output / results, so
# check them both using the same function.
expected =
- "AgendavPrune - Removed user booger@example.com.\n" +
"DavicalPrune - Removed user booger@example.com (Principal ID: 2).\n" +
"DovecotPrune - Removed user booger@example.com " +
"(#{cfg.dovecot_mail_root()}/example.com/booger).\n" +
# Now make sure the database has what we expect.
- apr = AgendavPrune.new(cfg)
- actual = apr.list_users()
- expected = [User.new('adam@example.net')]
- assert_equal(expected, actual)
-
dpr = DavicalPrune.new(cfg)
actual = dpr.list_users()
expected = [User.new('alice@example.com')]
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'rm/plugins/agendav'
require 'rm/plugins/davical'
require 'rm/plugins/dovecot'
require 'rm/plugins/postfixadmin'
actual = output_buffer.string()
expected =
- "AgendavRm - Removed user adam@example.net.\n" +
"DavicalRm - User adam@example.net not found.\n" +
"DovecotRm - Removed user adam@example.net " +
"(#{cfg.dovecot_mail_root}/example.net/adam).\n" +
# Now make sure the database has what we expect.
- arm = AgendavRm.new(cfg)
- actual = arm.list_users()
- expected = [User.new('booger@example.com')]
- assert_equal(expected, actual)
-
- # Only try to remove this guy from the agendav database, to ensure
- # that "nonexistent" users have their shares removed.
- arm.remove_user('stinky@example.com')
- expected = 2
- actual = arm.count_shares()
- assert_equal(expected, actual)
-
drm = DavicalRm.new(cfg)
actual = drm.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]
actual = output_buffer.string()
expected =
- "AgendavRm - Removed domain example.net.\n" +
"DavicalRm - Domain example.net not found.\n" +
"DovecotRm - Removed domain example.net " +
"(#{cfg.dovecot_mail_root}/example.net).\n" +
# Now make sure the database has what we expect.
- arm = AgendavRm.new(cfg)
- actual = arm.list_users()
- expected = [User.new('booger@example.com')]
- assert_equal(expected, actual)
-
drm = DavicalRm.new(cfg)
actual = drm.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]