From: Michael Orlitzky Date: Sun, 29 Sep 2013 22:12:51 +0000 (-0400) Subject: Add a bunch more crap and rewrite a bunch more crap. Now the 'rm' mode at least runs. X-Git-Tag: 0.0.1~87 X-Git-Url: http://gitweb.michael.orlitzky.com/?p=mailshears.git;a=commitdiff_plain;h=e3826d8926e11763837a591986d453e9ef5d9dec Add a bunch more crap and rewrite a bunch more crap. Now the 'rm' mode at least runs. --- diff --git a/bin/mailshears b/bin/mailshears index 06534d8..09735ba 100755 --- a/bin/mailshears +++ b/bin/mailshears @@ -43,7 +43,7 @@ require 'mailshears' # 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}" @@ -83,21 +83,42 @@ def make_header(plugin_name) 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 @@ -131,8 +152,8 @@ rescue DatabaseError => e 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) diff --git a/lib/common/agendav_plugin.rb b/lib/common/agendav_plugin.rb new file mode 100644 index 0000000..fad869f --- /dev/null +++ b/lib/common/agendav_plugin.rb @@ -0,0 +1,73 @@ +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 diff --git a/lib/common/davical_plugin.rb b/lib/common/davical_plugin.rb new file mode 100644 index 0000000..e4bef48 --- /dev/null +++ b/lib/common/davical_plugin.rb @@ -0,0 +1,73 @@ +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 diff --git a/lib/common/dovecot_mailstore_plugin.rb b/lib/common/dovecot_mailstore_plugin.rb new file mode 100644 index 0000000..ff901ee --- /dev/null +++ b/lib/common/dovecot_mailstore_plugin.rb @@ -0,0 +1,74 @@ +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 diff --git a/lib/common/roundcube_db_plugin.rb b/lib/common/roundcube_db_plugin.rb new file mode 100644 index 0000000..364b819 --- /dev/null +++ b/lib/common/roundcube_db_plugin.rb @@ -0,0 +1,68 @@ +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 diff --git a/lib/mv/mv_dummy_runner.rb b/lib/mv/mv_dummy_runner.rb new file mode 100644 index 0000000..0e3beee --- /dev/null +++ b/lib/mv/mv_dummy_runner.rb @@ -0,0 +1,12 @@ +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 diff --git a/lib/mv/mv_runner.rb b/lib/mv/mv_runner.rb new file mode 100644 index 0000000..a0fa8e7 --- /dev/null +++ b/lib/mv/mv_runner.rb @@ -0,0 +1,8 @@ +class MvRunner + include Runner + + def run(plugin, src, dst) + puts "Not implemented" + end + +end diff --git a/lib/mv/plugins/agendav.rb b/lib/mv/plugins/agendav.rb index 15bf3de..ab12c73 100644 --- a/lib/mv/plugins/agendav.rb +++ b/lib/mv/plugins/agendav.rb @@ -1,37 +1,13 @@ 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. @@ -64,43 +40,4 @@ class AgendavMv 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 diff --git a/lib/mv/plugins/davical.rb b/lib/mv/plugins/davical.rb index 860abdc..ee47d30 100644 --- a/lib/mv/plugins/davical.rb +++ b/lib/mv/plugins/davical.rb @@ -1,6 +1,6 @@ require 'pg' -require 'common/plugin' +require 'common/davical_plugin' require 'rm/rm_plugin' class DavicalMv @@ -8,37 +8,9 @@ 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. @@ -73,40 +45,4 @@ class DavicalMv 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 diff --git a/lib/mv/plugins/dovecot_mailstore.rb b/lib/mv/plugins/dovecot_mailstore.rb index 4ce1437..f16f116 100644 --- a/lib/mv/plugins/dovecot_mailstore.rb +++ b/lib/mv/plugins/dovecot_mailstore.rb @@ -1,38 +1,13 @@ -# 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) @@ -46,48 +21,4 @@ class DovecotMailstoreMv < Mailstore 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 diff --git a/lib/mv/plugins/roundcube_db.rb b/lib/mv/plugins/roundcube_db.rb index 6859282..fa29aa6 100644 --- a/lib/mv/plugins/roundcube_db.rb +++ b/lib/mv/plugins/roundcube_db.rb @@ -1,39 +1,13 @@ 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. @@ -64,37 +38,4 @@ class RoundcubeDbMv 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 diff --git a/lib/prune/plugins/agendav.rb b/lib/prune/plugins/agendav.rb new file mode 100644 index 0000000..1578773 --- /dev/null +++ b/lib/prune/plugins/agendav.rb @@ -0,0 +1,24 @@ +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 diff --git a/lib/prune/plugins/davical.rb b/lib/prune/plugins/davical.rb new file mode 100644 index 0000000..ae21d9f --- /dev/null +++ b/lib/prune/plugins/davical.rb @@ -0,0 +1,60 @@ +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 diff --git a/lib/prune/plugins/dovecot_mailstore.rb b/lib/prune/plugins/dovecot_mailstore.rb new file mode 100644 index 0000000..f504d9c --- /dev/null +++ b/lib/prune/plugins/dovecot_mailstore.rb @@ -0,0 +1,30 @@ +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 diff --git a/lib/prune/plugins/roundcube_db.rb b/lib/prune/plugins/roundcube_db.rb new file mode 100644 index 0000000..6515a7b --- /dev/null +++ b/lib/prune/plugins/roundcube_db.rb @@ -0,0 +1,25 @@ +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 diff --git a/lib/prune/prune_dummy_runner.rb b/lib/prune/prune_dummy_runner.rb new file mode 100644 index 0000000..f9bde5e --- /dev/null +++ b/lib/prune/prune_dummy_runner.rb @@ -0,0 +1,8 @@ +class PruneDummyRunner + include Runner + + def run(plugin) + puts "Not implemented" + end + +end diff --git a/lib/prune/prune_plugin.rb b/lib/prune/prune_plugin.rb new file mode 100644 index 0000000..2776704 --- /dev/null +++ b/lib/prune/prune_plugin.rb @@ -0,0 +1,35 @@ +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 diff --git a/lib/prune/prune_runner.rb b/lib/prune/prune_runner.rb new file mode 100644 index 0000000..0f1dfeb --- /dev/null +++ b/lib/prune/prune_runner.rb @@ -0,0 +1,8 @@ +class PruneRunner + include Runner + + def run(plugin) + puts "Not implemented" + end + +end diff --git a/lib/rm/plugins/agendav.rb b/lib/rm/plugins/agendav.rb index bb34295..4cd179c 100644 --- a/lib/rm/plugins/agendav.rb +++ b/lib/rm/plugins/agendav.rb @@ -1,37 +1,13 @@ 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. @@ -66,55 +42,4 @@ class AgendavRm 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 diff --git a/lib/rm/plugins/davical.rb b/lib/rm/plugins/davical.rb index 5ca8bf1..86273cb 100644 --- a/lib/rm/plugins/davical.rb +++ b/lib/rm/plugins/davical.rb @@ -1,6 +1,6 @@ require 'pg' -require 'common/plugin' +require 'common/davical_plugin' require 'rm/rm_plugin' class DavicalRm @@ -8,37 +8,9 @@ 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. @@ -73,82 +45,4 @@ class DavicalRm 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 diff --git a/lib/rm/plugins/dovecot_mailstore.rb b/lib/rm/plugins/dovecot_mailstore.rb index 048319f..e138651 100644 --- a/lib/rm/plugins/dovecot_mailstore.rb +++ b/lib/rm/plugins/dovecot_mailstore.rb @@ -1,47 +1,25 @@ -# 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) @@ -61,6 +39,7 @@ class DovecotMailstoreRm < Mailstore return (fs_accounts - db_accounts) end + protected; def get_domains_from_filesystem() @@ -90,44 +69,4 @@ class DovecotMailstoreRm < Mailstore 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 diff --git a/lib/rm/plugins/roundcube_db.rb b/lib/rm/plugins/roundcube_db.rb index f2621df..3e18e8e 100644 --- a/lib/rm/plugins/roundcube_db.rb +++ b/lib/rm/plugins/roundcube_db.rb @@ -1,40 +1,13 @@ 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 @@ -93,38 +66,6 @@ class RoundcubeDbRm 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 = [] diff --git a/lib/rm/rm_dummy_runner.rb b/lib/rm/rm_dummy_runner.rb index c79e046..d6c2c5f 100644 --- a/lib/rm/rm_dummy_runner.rb +++ b/lib/rm/rm_dummy_runner.rb @@ -1,7 +1,7 @@ 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}" diff --git a/lib/rm/rm_plugin.rb b/lib/rm/rm_plugin.rb index e2f2cfe..f8cdd17 100644 --- a/lib/rm/rm_plugin.rb +++ b/lib/rm/rm_plugin.rb @@ -25,15 +25,4 @@ module RmPlugin 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 diff --git a/lib/rm/rm_runner.rb b/lib/rm/rm_runner.rb index 039ae61..5fe2eaf 100644 --- a/lib/rm/rm_runner.rb +++ b/lib/rm/rm_runner.rb @@ -1,7 +1,7 @@ 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 diff --git a/mailshears.gemspec b/mailshears.gemspec index 1498b0f..86c6bc8 100644 --- a/mailshears.gemspec +++ b/mailshears.gemspec @@ -8,6 +8,7 @@ Gem::Specification.new do |s| 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'