# existed) from ARGV, what remains should be the required
# arguments.
if (mode == :prune and (ARGV.length() != 0)) or
- (mode == :rm and (ARGV.length() != 1)) or
+ (mode == :rm and (ARGV.length() < 1)) or
(mode == :mv and (ARGV.length() != 2)) then
puts "ERROR: missing (or extra) command-line arguments."
puts "Usage: #{usage}"
end
+plugin_class = nil
+runner_class = nil
+dummy_runner_class = nil
+
if mode == :rm then
- RmPlugin.includers.each do |plugin_class|
- plugin = plugin_class.new()
- puts make_header(plugin_class.to_s())
+ plugin_class = RmPlugin
+ runner_class = RmRunner
+ dummy_runner_class = RmDummyRunner
+elsif mode == :mv then
+ plugin_class = MvPlugin
+ runner_class = MvRunner
+ dummy_runner_class = MvDummyRunner
+else
+ # Safe, catch-all default
+ plugin_class = PrunePlugin
+ runner_class = PruneRunner
+ dummy_runner_class = PruneDummyRunner
+end
- if cfg.i_mean_business then
- runner = RmRunner.new()
- else
- runner = RmDummyRunner.new()
- end
- runner.run(plugin, ARGV)
+plugin_class.includers.each do |plugin_class_includer|
+ plugin = plugin_class_includer.new()
+ puts make_header(plugin_class.to_s())
- puts ""
+ if cfg.i_mean_business then
+ runner = runner_class.new()
+ else
+ runner = dummy_runner_class.new()
end
+
+ # The splat passes the correct (we hope) number of arguments to the
+ # appropriate runner. The Rm(Dummy)Runner have splats on their
+ # *target arguments as well, to turn ARGV back into an array.
+ runner.run(plugin, *ARGV)
+
+ puts ""
end
end
-Plugin.includers.each do |plugin_class|
- plugin = plugin_class.new()
+Plugin.includers.each do |plugin_class_includer|
+ plugin = plugin_class_includer.new()
begin
leftover_domains = plugin.get_leftover_domains(db_domains)
--- /dev/null
+require 'common/plugin'
+
+module AgendavPlugin
+ # Code that all Agendav plugins (Prune, Rm, Mv...) will
+ # share. That is, we implement the Plugin interface.
+ include Plugin
+
+
+ def initialize()
+ cfg = Configuration.new()
+ @db_host = cfg.agendav_dbhost
+ @db_port = cfg.agendav_dbport
+ @db_opts = cfg.agendav_dbopts
+ @db_tty = cfg.agendav_dbtty
+ @db_name = cfg.agendav_dbname
+ @db_user = cfg.agendav_dbuser
+ @db_pass = cfg.agendav_dbpass
+ end
+
+
+ def describe_domain(domain)
+ # AgenDAV doesn't have a concept of domains.
+ return 'N/A'
+ end
+
+
+ def describe_account(account)
+ if self.user_exists(account)
+ return "Username: #{account}"
+ else
+ return 'User not found'
+ end
+ end
+
+
+ protected;
+
+ def user_exists(account)
+ ad_users = get_agendav_usernames()
+ return ad_users.include?(account)
+ end
+
+ def get_agendav_usernames()
+ usernames = []
+
+ # Just assume PostgreSQL for now.
+ begin
+ connection = PGconn.connect(@db_host,
+ @db_port,
+ @db_opts,
+ @db_tty,
+ @db_name,
+ @db_user,
+ @db_pass)
+
+ sql_query = '(SELECT username FROM prefs)'
+ sql_query += 'UNION'
+ sql_query += '(SELECT user_from FROM shared);'
+
+ connection.query(sql_query) do |result|
+ usernames = result.field_values('username')
+ end
+
+ connection.close()
+ rescue PGError => e
+ # Pretend like we're database-agnostic in case we ever are.
+ raise DatabaseError.new(e)
+ end
+
+ return usernames
+ end
+
+end
--- /dev/null
+require 'common/plugin'
+
+module DavicalPlugin
+ # Code that all Davical plugins (Prune, Rm, Mv...) will share. That
+ # is, we implement the Plugin interface.
+ include Plugin
+
+ def initialize()
+ cfg = Configuration.new()
+ @db_host = cfg.davical_dbhost
+ @db_port = cfg.davical_dbport
+ @db_opts = cfg.davical_dbopts
+ @db_tty = cfg.davical_dbtty
+ @db_name = cfg.davical_dbname
+ @db_user = cfg.davical_dbuser
+ @db_pass = cfg.davical_dbpass
+ end
+
+
+ def describe_domain(domain)
+ # DAViCal doesn't have a concept of domains.
+ return 'N/A'
+ end
+
+
+ def describe_account(account)
+ principal_id = self.get_principal_id(account)
+
+ if principal_id.nil?
+ return 'User not found'
+ else
+ return "Principal ID: #{principal_id}"
+ end
+ end
+
+
+ protected;
+
+ def get_principal_id(account)
+ principal_id = nil
+
+ begin
+ connection = PGconn.connect(@db_host,
+ @db_port,
+ @db_opts,
+ @db_tty,
+ @db_name,
+ @db_user,
+ @db_pass)
+
+ sql_query = "SELECT principal.principal_id "
+ sql_query += "FROM (principal INNER JOIN usr "
+ sql_query += " ON principal.user_no = usr.user_no) "
+ sql_query += "WHERE usr.username = $1;"
+
+ connection.query(sql_query, [account]) do |result|
+ if result.num_tuples > 0
+ principal_id = result[0]['principal_id']
+ end
+ end
+
+ connection.close()
+
+ rescue PGError => e
+ # Pretend like we're database-agnostic in case we ever are.
+ raise DatabaseError.new(e)
+ end
+
+ return principal_id
+ end
+
+
+end
--- /dev/null
+require 'common/plugin'
+
+module DovecotMailstorePlugin
+ # Code that all DovecotMailstore plugins (Prune, Rm, Mv...) will
+ # share. That is, we implement the Plugin interface.
+ include Plugin
+
+ def initialize
+ cfg = Configuration.new()
+ @domain_root = cfg.mail_root
+ end
+
+ def describe_domain(domain)
+ begin
+ domain_path = get_domain_path(domain)
+ return domain_path
+ rescue NonexistentDomainError => e
+ return "Doesn't exist: #{e.to_s}"
+ end
+ end
+
+ def describe_account(account)
+ begin
+ account_path = get_account_path(account)
+ return account_path
+ rescue NonexistentAccountError => e
+ return "Doesn't exist: #{e.to_s}"
+ end
+ end
+
+
+ protected;
+
+ def get_domain_path(domain)
+ # Return the filesystem path for the given domain.
+ # That is, the directory where its mail is stored.
+ # Only works if the domain directory exists!
+ domain_path = File.join(@domain_root, domain)
+
+ if File.directory?(domain_path)
+ return domain_path
+ else
+ raise NonexistentDomainError.new(domain)
+ end
+ end
+
+
+ def get_account_path(account)
+ # Return the filesystem path of this account's mailbox.
+ # Only works if the account exists!
+ if not account.include?('@')
+ raise InvalidAccountError.new("#{account}: Accounts must contain an '@' symbol.")
+ end
+
+ account_parts = account.split('@')
+ user_part = account_parts[0]
+ domain_part = account_parts[1]
+
+ begin
+ domain_path = get_domain_path(domain_part)
+ rescue NonexistentDomainError
+ raise NonexistentAccountError.new(account)
+ end
+
+ account_path = File.join(domain_path, user_part)
+
+ if File.directory?(account_path)
+ return account_path
+ else
+ raise NonexistentAccountError(account)
+ end
+ end
+
+end
--- /dev/null
+require 'common/plugin'
+
+module RoundcubeDbPlugin
+ # Code that all RoundcubeDb plugins (Prune, Rm, Mv...) will share.
+ # That is, we implement the Plugin interface.
+ include Plugin
+
+ def initialize()
+ cfg = Configuration.new()
+ @db_host = cfg.roundcube_dbhost
+ @db_port = cfg.roundcube_dbport
+ @db_opts = cfg.roundcube_dbopts
+ @db_tty = cfg.roundcube_dbtty
+ @db_name = cfg.roundcube_dbname
+ @db_user = cfg.roundcube_dbuser
+ @db_pass = cfg.roundcube_dbpass
+ end
+
+
+ def describe_domain(domain)
+ # Roundcube doesn't have a concept of domains.
+ return 'N/A'
+ end
+
+ def describe_account(account)
+ user_id = self.get_user_id(account)
+
+ if user_id.nil?
+ return 'User not found'
+ else
+ return "User ID: #{user_id}"
+ end
+ end
+
+
+ protected;
+
+ def get_user_id(account)
+ user_id = nil
+
+ begin
+ connection = PGconn.connect(@db_host,
+ @db_port,
+ @db_opts,
+ @db_tty,
+ @db_name,
+ @db_user,
+ @db_pass)
+
+ sql_query = "SELECT user_id FROM users WHERE username = $1;"
+
+ connection.query(sql_query, [account]) do |result|
+ if result.num_tuples > 0
+ user_id = result[0]['user_id']
+ end
+ end
+
+ connection.close()
+
+ rescue PGError => e
+ # Pretend like we're database-agnostic in case we ever are.
+ raise DatabaseError.new(e)
+ end
+
+ return user_id
+ end
+
+end
--- /dev/null
+class MvDummyRunner
+ include Runner
+
+ def run(plugin, src, dst)
+ if src.include?('@') then
+ puts "Would move account: #{src} to #{dst}"
+ else
+ puts "Would move domain: #{src} to #{dst}"
+ end
+ end
+
+end
--- /dev/null
+class MvRunner
+ include Runner
+
+ def run(plugin, src, dst)
+ puts "Not implemented"
+ end
+
+end
require 'pg'
-require 'common/plugin'
+require 'common/agendav_plugin'
require 'mv/mv_plugin'
class AgendavMv
- include Plugin
+ include AgendavPlugin
include MvPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.agendav_dbhost
- @db_port = cfg.agendav_dbport
- @db_opts = cfg.agendav_dbopts
- @db_tty = cfg.agendav_dbtty
- @db_name = cfg.agendav_dbname
- @db_user = cfg.agendav_dbuser
- @db_pass = cfg.agendav_dbpass
- end
-
-
- def describe_domain(domain)
- # AgenDAV doesn't have a concept of domains.
- return 'N/A'
- end
-
- def describe_account(account)
- if self.user_exists(account)
- return "Username: #{account}"
- else
- return 'User not found'
- end
- end
def mv_domain(from, to)
# AgenDAV doesn't have a concept of domains.
end
-
-
- protected;
-
- def user_exists(account)
- ad_users = get_agendav_usernames()
- return ad_users.include?(account)
- end
-
- def get_agendav_usernames()
- usernames = []
-
- # Just assume PostgreSQL for now.
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = '(SELECT username FROM prefs)'
- sql_query += 'UNION'
- sql_query += '(SELECT user_from FROM shared);'
-
- connection.query(sql_query) do |result|
- usernames = result.field_values('username')
- end
-
- connection.close()
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return usernames
- end
-
end
require 'pg'
-require 'common/plugin'
+require 'common/davical_plugin'
require 'rm/rm_plugin'
class DavicalMv
# DAViCal only supports Postgres, so even if we ever are
# database-agnostic, this plugin can't be.
#
- include Plugin
+ include DavicalPlugin
include MvPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.davical_dbhost
- @db_port = cfg.davical_dbport
- @db_opts = cfg.davical_dbopts
- @db_tty = cfg.davical_dbtty
- @db_name = cfg.davical_dbname
- @db_user = cfg.davical_dbuser
- @db_pass = cfg.davical_dbpass
- end
-
-
- def describe_domain(domain)
- # DAViCal doesn't have a concept of domains.
- return 'N/A'
- end
-
-
- def describe_account(account)
- principal_id = self.get_principal_id(account)
-
- if principal_id.nil?
- return 'User not found'
- else
- return "Principal ID: #{principal_id}"
- end
- end
-
def mv_domain(from, to)
# DAViCal doesn't have a concept of domains.
end
-
- protected;
-
- def get_principal_id(account)
- principal_id = nil
-
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = "SELECT principal.principal_id "
- sql_query += "FROM (principal INNER JOIN usr "
- sql_query += " ON principal.user_no = usr.user_no) "
- sql_query += "WHERE usr.username = $1;"
-
- connection.query(sql_query, [account]) do |result|
- if result.num_tuples > 0
- principal_id = result[0]['principal_id']
- end
- end
-
- connection.close()
-
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return principal_id
- end
-
end
-# Needed for rm_rf.
-require 'fileutils'
-
require 'common/filesystem'
require 'common/mailstore'
-require 'common/plugin'
+require 'common/dovecot_mailstore_plugin'
require 'mv/mv_plugin'
class DovecotMailstoreMv < Mailstore
- include Plugin
+ include DovecotMailstorePlugin
include MvPlugin
- def initialize
- cfg = Configuration.new()
- @domain_root = cfg.mail_root
- end
-
- def describe_domain(domain)
- begin
- domain_path = get_domain_path(domain)
- return domain_path
- rescue NonexistentDomainError => e
- return "Doesn't exist: #{e.to_s}"
- end
- end
-
- def describe_account(account)
- begin
- account_path = get_account_path(account)
- return account_path
- rescue NonexistentAccountError => e
- return "Doesn't exist: #{e.to_s}"
- end
- end
def mv_domain(from, to)
from_path = self.get_domain_path(from)
FileUtils.mv(from_path, to_path)
end
- protected;
-
-
- def get_domain_path(domain)
- # Return the filesystem path for the given domain.
- # That is, the directory where its mail is stored.
- # Only works if the domain directory exists!
- domain_path = File.join(@domain_root, domain)
-
- if File.directory?(domain_path)
- return domain_path
- else
- raise NonexistentDomainError.new(domain)
- end
- end
-
-
- def get_account_path(account)
- # Return the filesystem path of this account's mailbox.
- # Only works if the account exists!
- if not account.include?('@')
- msg = "#{account}: Accounts must contain an '@' symbol."
- raise InvalidAccountError.new(msg)
- end
-
- account_parts = account.split('@')
- user_part = account_parts[0]
- domain_part = account_parts[1]
-
- begin
- domain_path = get_domain_path(domain_part)
- rescue NonexistentDomainError
- raise NonexistentAccountError.new(account)
- end
-
- account_path = File.join(domain_path, user_part)
-
- if File.directory?(account_path)
- return account_path
- else
- raise NonexistentAccountError(account)
- end
- end
-
end
require 'pg'
-require 'common/plugin'
+require 'common/roundcube_db_plugin'
require 'mv/mv_plugin'
class RoundcubeDbMv
- include Plugin
+ include RoundcubeDbPlugin
include MvPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.roundcube_dbhost
- @db_port = cfg.roundcube_dbport
- @db_opts = cfg.roundcube_dbopts
- @db_tty = cfg.roundcube_dbtty
- @db_name = cfg.roundcube_dbname
- @db_user = cfg.roundcube_dbuser
- @db_pass = cfg.roundcube_dbpass
- end
-
-
- def describe_domain(domain)
- # Roundcube doesn't have a concept of domains.
- return 'N/A'
- end
-
- def describe_account(account)
- user_id = self.get_user_id(account)
-
- if user_id.nil?
- return 'User not found'
- else
- return "User ID: #{user_id}"
- end
- end
def mv_domain(from, to)
# Roundcube doesn't have a concept of domains.
end
-
- protected;
-
- def get_user_id(account)
- user_id = nil
-
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = "SELECT user_id FROM users WHERE username = $1;"
-
- connection.query(sql_query, [account]) do |result|
- if result.num_tuples > 0
- user_id = result[0]['user_id']
- end
- end
-
- connection.close()
-
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return user_id
- end
-
end
--- /dev/null
+require 'pg'
+
+require 'common/agendav_plugin'
+require 'prune/prune_plugin'
+require 'rm/plugins/agendav'
+
+class AgendavPrune < AgendavRm
+
+ include AgendavPlugin
+ include PrunePlugin
+
+ def get_leftover_domains(db_domains)
+ # AgenDAV doesn't have a concept of domains.
+ return []
+ end
+
+
+ def get_leftover_accounts(db_accounts)
+ # Get a list of all users who have logged in to AgenDAV.
+ ad_accounts = self.get_agendav_usernames()
+ return ad_accounts - db_accounts
+ end
+
+end
--- /dev/null
+require 'pg'
+
+require 'common/davical_plugin'
+require 'prune/prune_plugin'
+require 'rm/plugins/davical'
+
+class DavicalPrune < DavicalRm
+ #
+ # DAViCal only supports Postgres, so even if we ever are
+ # database-agnostic, this plugin can't be.
+ #
+ include DavicalPlugin
+ include PrunePlugin
+
+
+ def get_leftover_domains(db_domains)
+ # AgenDAV doesn't have a concept of domains.
+ return []
+ end
+
+
+ def get_leftover_accounts(db_accounts)
+ # Get a list of all users who have logged in to DAViCal.
+ davical_accounts = self.get_davical_usernames()
+ return davical_accounts - db_accounts
+ end
+
+
+ protected;
+
+ def get_davical_usernames()
+ usernames = []
+
+ begin
+ connection = PGconn.connect(@db_host,
+ @db_port,
+ @db_opts,
+ @db_tty,
+ @db_name,
+ @db_user,
+ @db_pass)
+
+ # User #1 is the super-user, and not tied to an email address.
+ sql_query = 'SELECT username FROM usr WHERE user_no > 1;'
+
+ connection.query(sql_query) do |result|
+ usernames = result.field_values('username')
+ end
+
+ connection.close()
+ rescue PGError => e
+ # Pretend like we're database-agnostic in case we ever are.
+ raise DatabaseError.new(e)
+ end
+
+ return usernames
+ end
+
+
+end
--- /dev/null
+require 'common/filesystem'
+require 'common/mailstore'
+require 'common/dovecot_mailstore_plugin'
+require 'prune/prune_plugin'
+require 'rm/plugins/dovecot_mailstore'
+
+class DovecotMailstorePrune < DovecotMailstoreRm
+
+ include DovecotMailstorePlugin
+ include PrunePlugin
+
+
+ def get_leftover_domains(db_domains)
+ # Get the list of domains according to the filesystem.
+ fs_domains = self.get_domains_from_filesystem()
+
+ # Return the list of domains on the filesystem that aren't in the DB.
+ return (fs_domains - db_domains)
+ end
+
+ def get_leftover_accounts(db_accounts)
+ # Get the list of accounts according to the filesystem.
+ fs_domains = self.get_domains_from_filesystem()
+ fs_accounts = self.get_accounts_from_filesystem(fs_domains)
+
+ # And return the accounts on the filesystem that aren't in the DB.
+ return (fs_accounts - db_accounts)
+ end
+
+end
--- /dev/null
+require 'pg'
+
+require 'common/roundcube_db_plugin'
+require 'prune/prune_plugin'
+require 'rm/plugins/roundcube_db'
+
+class RoundcubeDbPrune < RoundcubeDbRm
+
+ include RoundcubeDbPlugin
+ include PrunePlugin
+
+
+ def get_leftover_domains(db_domains)
+ # Roundcube doesn't have a concept of domains.
+ return []
+ end
+
+
+ def get_leftover_accounts(db_accounts)
+ # Get a list of all users who have logged in to Roundcube.
+ rc_accounts = self.get_roundcube_usernames()
+ return rc_accounts - db_accounts
+ end
+
+end
--- /dev/null
+class PruneDummyRunner
+ include Runner
+
+ def run(plugin)
+ puts "Not implemented"
+ end
+
+end
--- /dev/null
+require 'rm/rm_plugin'
+
+module PrunePlugin
+ include RmPlugin
+
+ #
+ # Plugins for the removal of leftover non-PostfixAdmin accounts,
+ # i.e. after an account has been removed from the PostfixAdmin
+ # database.
+ #
+
+ def PrunePlugin.included(c)
+ # Callback, called whenever another class or module includes this
+ # one. The parameter given is the name of the class or module
+ # that included us.
+ @includers ||= []
+ @includers << c
+ end
+
+ def PrunePlugin.includers
+ return @includers
+ end
+
+ def get_leftover_domains(db_domains)
+ # Given a list of domains, determine which domains belonging to
+ # this plugin are not contained in the given list.
+ raise NotImplementedError
+ end
+
+ def get_leftover_accounts(db_accounts)
+ # Given a list of accounts, determine which accounts belonging to
+ # this plugin are not contained in the given list.
+ raise NotImplementedError
+ end
+end
--- /dev/null
+class PruneRunner
+ include Runner
+
+ def run(plugin)
+ puts "Not implemented"
+ end
+
+end
require 'pg'
-require 'common/plugin'
+require 'common/agendav_plugin'
require 'rm/rm_plugin'
class AgendavRm
- include Plugin
+ include AgendavPlugin
include RmPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.agendav_dbhost
- @db_port = cfg.agendav_dbport
- @db_opts = cfg.agendav_dbopts
- @db_tty = cfg.agendav_dbtty
- @db_name = cfg.agendav_dbname
- @db_user = cfg.agendav_dbuser
- @db_pass = cfg.agendav_dbpass
- end
-
-
- def describe_domain(domain)
- # AgenDAV doesn't have a concept of domains.
- return 'N/A'
- end
-
- def describe_account(account)
- if self.user_exists(account)
- return "Username: #{account}"
- else
- return 'User not found'
- end
- end
def delete_domain(domain)
# AgenDAV doesn't have a concept of domains.
end
-
- def get_leftover_domains(db_domains)
- # AgenDAV doesn't have a concept of domains.
- return []
- end
-
-
- def get_leftover_accounts(db_accounts)
- # Get a list of all users who have logged in to AgenDAV.
- ad_accounts = self.get_agendav_usernames()
- return ad_accounts - db_accounts
- end
-
-
- protected;
-
- def user_exists(account)
- ad_users = get_agendav_usernames()
- return ad_users.include?(account)
- end
-
- def get_agendav_usernames()
- usernames = []
-
- # Just assume PostgreSQL for now.
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = '(SELECT username FROM prefs)'
- sql_query += 'UNION'
- sql_query += '(SELECT user_from FROM shared);'
-
- connection.query(sql_query) do |result|
- usernames = result.field_values('username')
- end
-
- connection.close()
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return usernames
- end
-
end
require 'pg'
-require 'common/plugin'
+require 'common/davical_plugin'
require 'rm/rm_plugin'
class DavicalRm
# DAViCal only supports Postgres, so even if we ever are
# database-agnostic, this plugin can't be.
#
- include Plugin
+ include DavicalPlugin
include RmPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.davical_dbhost
- @db_port = cfg.davical_dbport
- @db_opts = cfg.davical_dbopts
- @db_tty = cfg.davical_dbtty
- @db_name = cfg.davical_dbname
- @db_user = cfg.davical_dbuser
- @db_pass = cfg.davical_dbpass
- end
-
-
- def describe_domain(domain)
- # DAViCal doesn't have a concept of domains.
- return 'N/A'
- end
-
-
- def describe_account(account)
- principal_id = self.get_principal_id(account)
-
- if principal_id.nil?
- return 'User not found'
- else
- return "Principal ID: #{principal_id}"
- end
- end
-
def delete_domain(domain)
# DAViCal doesn't have a concept of domains.
end
-
- def get_leftover_domains(db_domains)
- # AgenDAV doesn't have a concept of domains.
- return []
- end
-
-
- def get_leftover_accounts(db_accounts)
- # Get a list of all users who have logged in to DAViCal.
- davical_accounts = self.get_davical_usernames()
- return davical_accounts - db_accounts
- end
-
-
- protected;
-
- def get_principal_id(account)
- principal_id = nil
-
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = "SELECT principal.principal_id "
- sql_query += "FROM (principal INNER JOIN usr "
- sql_query += " ON principal.user_no = usr.user_no) "
- sql_query += "WHERE usr.username = $1;"
-
- connection.query(sql_query, [account]) do |result|
- if result.num_tuples > 0
- principal_id = result[0]['principal_id']
- end
- end
-
- connection.close()
-
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return principal_id
- end
-
-
- def get_davical_usernames()
- usernames = []
-
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- # User #1 is the super-user, and not tied to an email address.
- sql_query = 'SELECT username FROM usr WHERE user_no > 1;'
-
- connection.query(sql_query) do |result|
- usernames = result.field_values('username')
- end
-
- connection.close()
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return usernames
- end
-
end
-# Needed for rm_rf.
+# Needed for rm_r.
require 'fileutils'
require 'common/filesystem'
require 'common/mailstore'
-require 'common/plugin'
+require 'common/dovecot_mailstore_plugin'
require 'rm/rm_plugin'
class DovecotMailstoreRm < Mailstore
- include Plugin
+ include DovecotMailstorePlugin
include RmPlugin
- def initialize
- cfg = Configuration.new()
- @domain_root = cfg.mail_root
- end
-
- def describe_domain(domain)
- begin
- domain_path = get_domain_path(domain)
- return domain_path
- rescue NonexistentDomainError => e
- return "Doesn't exist: #{e.to_s}"
- end
- end
-
- def describe_account(account)
- begin
- account_path = get_account_path(account)
- return account_path
- rescue NonexistentAccountError => e
- return "Doesn't exist: #{e.to_s}"
- end
- end
def delete_domain(domain)
domain_path = self.get_domain_path(domain)
- FileUtils.rm_rf(domain_path)
+ FileUtils.rm_r(domain_path)
end
def delete_account(account)
account_path = self.get_account_path(account)
- FileUtils.rm_rf(account_path)
+ FileUtils.rm_r(account_path)
end
def get_leftover_domains(db_domains)
return (fs_accounts - db_accounts)
end
+
protected;
def get_domains_from_filesystem()
end
- def get_domain_path(domain)
- # Return the filesystem path for the given domain.
- # That is, the directory where its mail is stored.
- # Only works if the domain directory exists!
- domain_path = File.join(@domain_root, domain)
-
- if File.directory?(domain_path)
- return domain_path
- else
- raise NonexistentDomainError.new(domain)
- end
- end
-
-
- def get_account_path(account)
- # Return the filesystem path of this account's mailbox.
- # Only works if the account exists!
- if not account.include?('@')
- raise InvalidAccountError.new("#{account}: Accounts must contain an '@' symbol.")
- end
-
- account_parts = account.split('@')
- user_part = account_parts[0]
- domain_part = account_parts[1]
-
- begin
- domain_path = get_domain_path(domain_part)
- rescue NonexistentDomainError
- raise NonexistentAccountError.new(account)
- end
-
- account_path = File.join(domain_path, user_part)
-
- if File.directory?(account_path)
- return account_path
- else
- raise NonexistentAccountError(account)
- end
- end
-
end
require 'pg'
-require 'common/plugin'
+require 'common/roundcube_db_plugin'
require 'rm/rm_plugin'
class RoundcubeDbRm
- include Plugin
+ include RoundcubeDbPlugin
include RmPlugin
- def initialize()
- cfg = Configuration.new()
- @db_host = cfg.roundcube_dbhost
- @db_port = cfg.roundcube_dbport
- @db_opts = cfg.roundcube_dbopts
- @db_tty = cfg.roundcube_dbtty
- @db_name = cfg.roundcube_dbname
- @db_user = cfg.roundcube_dbuser
- @db_pass = cfg.roundcube_dbpass
- end
-
-
- def describe_domain(domain)
- # Roundcube doesn't have a concept of domains.
- return 'N/A'
- end
-
- def describe_account(account)
- user_id = self.get_user_id(account)
-
- if user_id.nil?
- return 'User not found'
- else
- return "User ID: #{user_id}"
- end
- end
-
def delete_domain(domain)
# Roundcube doesn't have a concept of domains.
end
protected;
- def get_user_id(account)
- user_id = nil
-
- begin
- connection = PGconn.connect(@db_host,
- @db_port,
- @db_opts,
- @db_tty,
- @db_name,
- @db_user,
- @db_pass)
-
- sql_query = "SELECT user_id FROM users WHERE username = $1;"
-
- connection.query(sql_query, [account]) do |result|
- if result.num_tuples > 0
- user_id = result[0]['user_id']
- end
- end
-
- connection.close()
-
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
- end
-
- return user_id
- end
-
-
-
def get_roundcube_usernames()
usernames = []
class RmDummyRunner
include Runner
- def run(plugin, targets)
+ def run(plugin, *targets)
targets.each do |target|
if target.include?('@') then
puts "Would remove account: #{target}"
raise NotImplementedError
end
- def get_leftover_domains(db_domains)
- # Given a list of domains, determine which domains belonging to
- # this plugin are not contained in the given list.
- raise NotImplementedError
- end
-
- def get_leftover_accounts(db_accounts)
- # Given a list of accounts, determine which accounts belonging to
- # this plugin are not contained in the given list.
- raise NotImplementedError
- end
end
class RmRunner
include Runner
- def run(plugin, targets)
+ def run(plugin, *targets)
targets.each do |target|
# Why think too hard? An account has an @, a domain doesn't.
if target.include?('@') then
s.homepage = 'http://michael.orlitzky.com/code/mailshears.php'
s.summary = 'Prune unused mail directories.'
s.description = s.summary
+ s.license = 'GPL-3'
s.required_rubygems_version = '>= 1.3.6'