]> gitweb.michael.orlitzky.com - mailshears.git/commitdiff
Add a bunch more crap and rewrite a bunch more crap. Now the 'rm' mode at least runs.
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 29 Sep 2013 22:12:51 +0000 (18:12 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Sun, 29 Sep 2013 22:12:51 +0000 (18:12 -0400)
26 files changed:
bin/mailshears
lib/common/agendav_plugin.rb [new file with mode: 0644]
lib/common/davical_plugin.rb [new file with mode: 0644]
lib/common/dovecot_mailstore_plugin.rb [new file with mode: 0644]
lib/common/roundcube_db_plugin.rb [new file with mode: 0644]
lib/mv/mv_dummy_runner.rb [new file with mode: 0644]
lib/mv/mv_runner.rb [new file with mode: 0644]
lib/mv/plugins/agendav.rb
lib/mv/plugins/davical.rb
lib/mv/plugins/dovecot_mailstore.rb
lib/mv/plugins/roundcube_db.rb
lib/prune/plugins/agendav.rb [new file with mode: 0644]
lib/prune/plugins/davical.rb [new file with mode: 0644]
lib/prune/plugins/dovecot_mailstore.rb [new file with mode: 0644]
lib/prune/plugins/roundcube_db.rb [new file with mode: 0644]
lib/prune/prune_dummy_runner.rb [new file with mode: 0644]
lib/prune/prune_plugin.rb [new file with mode: 0644]
lib/prune/prune_runner.rb [new file with mode: 0644]
lib/rm/plugins/agendav.rb
lib/rm/plugins/davical.rb
lib/rm/plugins/dovecot_mailstore.rb
lib/rm/plugins/roundcube_db.rb
lib/rm/rm_dummy_runner.rb
lib/rm/rm_plugin.rb
lib/rm/rm_runner.rb
mailshears.gemspec

index 06534d887c9125dabde3ab43e6371253f0bda122..09735ba26ba7087711a8b81605483a0b00d9ea4b 100755 (executable)
@@ -43,7 +43,7 @@ require 'mailshears'
 # existed) from ARGV, what remains should be the required
 # arguments.
 if (mode == :prune and (ARGV.length() != 0)) or
 # 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}"
     (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
 
 
 end
 
 
+plugin_class = nil
+runner_class = nil
+dummy_runner_class = nil
+
 if mode == :rm then
 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
   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
 
 
@@ -131,8 +152,8 @@ rescue DatabaseError => e
 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)
 
   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 (file)
index 0000000..fad869f
--- /dev/null
@@ -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 (file)
index 0000000..e4bef48
--- /dev/null
@@ -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 (file)
index 0000000..ff901ee
--- /dev/null
@@ -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 (file)
index 0000000..364b819
--- /dev/null
@@ -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 (file)
index 0000000..0e3beee
--- /dev/null
@@ -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 (file)
index 0000000..a0fa8e7
--- /dev/null
@@ -0,0 +1,8 @@
+class MvRunner
+  include Runner
+
+  def run(plugin, src, dst)
+    puts "Not implemented"
+  end
+
+end
index 15bf3de2ae65a96199a72dc5306c99948e4cf9c6..ab12c73f18473e5735d11f62bece80f1386ca75c 100644 (file)
@@ -1,37 +1,13 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/agendav_plugin'
 require 'mv/mv_plugin'
 
 class AgendavMv
 
 require 'mv/mv_plugin'
 
 class AgendavMv
 
-  include Plugin
+  include AgendavPlugin
   include MvPlugin
 
   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.
 
   def mv_domain(from, to)
     # AgenDAV doesn't have a concept of domains.
@@ -64,43 +40,4 @@ class AgendavMv
 
   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
 end
index 860abdc3765a2e0b47dd08c244ca0a8b3fa1f38a..ee47d3039fd9feb71235000ff0f93e4f6dfe69ff 100644 (file)
@@ -1,6 +1,6 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/davical_plugin'
 require 'rm/rm_plugin'
 
 class DavicalMv
 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.
   #
   # DAViCal only supports Postgres, so even if we ever are
   # database-agnostic, this plugin can't be.
   #
-  include Plugin
+  include DavicalPlugin
   include MvPlugin
 
   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.
 
   def mv_domain(from, to)
     # DAViCal doesn't have a concept of domains.
@@ -73,40 +45,4 @@ class DavicalMv
 
   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
 end
index 4ce1437f8fa474da5a9d77b798e6a8620a7d2fea..f16f1165af99ad055384e20862594829d2db72bd 100644 (file)
@@ -1,38 +1,13 @@
-# Needed for rm_rf.
-require 'fileutils'
-
 require 'common/filesystem'
 require 'common/mailstore'
 require 'common/filesystem'
 require 'common/mailstore'
-require 'common/plugin'
+require 'common/dovecot_mailstore_plugin'
 require 'mv/mv_plugin'
 
 class DovecotMailstoreMv < Mailstore
 
 require 'mv/mv_plugin'
 
 class DovecotMailstoreMv < Mailstore
 
-  include Plugin
+  include DovecotMailstorePlugin
   include MvPlugin
 
   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)
 
   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
 
     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
 end
index 68592821a44442a30cf4d0f6bbed6a22089d3557..fa29aa6a6e67bdda9817bb6e5ee292a68950bc5f 100644 (file)
@@ -1,39 +1,13 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/roundcube_db_plugin'
 require 'mv/mv_plugin'
 
 class RoundcubeDbMv
 
 require 'mv/mv_plugin'
 
 class RoundcubeDbMv
 
-  include Plugin
+  include RoundcubeDbPlugin
   include MvPlugin
 
   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.
 
   def mv_domain(from, to)
     # Roundcube doesn't have a concept of domains.
@@ -64,37 +38,4 @@ class RoundcubeDbMv
 
   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
 end
diff --git a/lib/prune/plugins/agendav.rb b/lib/prune/plugins/agendav.rb
new file mode 100644 (file)
index 0000000..1578773
--- /dev/null
@@ -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 (file)
index 0000000..ae21d9f
--- /dev/null
@@ -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 (file)
index 0000000..f504d9c
--- /dev/null
@@ -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 (file)
index 0000000..6515a7b
--- /dev/null
@@ -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 (file)
index 0000000..f9bde5e
--- /dev/null
@@ -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 (file)
index 0000000..2776704
--- /dev/null
@@ -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 (file)
index 0000000..0f1dfeb
--- /dev/null
@@ -0,0 +1,8 @@
+class PruneRunner
+  include Runner
+
+  def run(plugin)
+    puts "Not implemented"
+  end
+
+end
index bb34295614a0fdfa47936021ecfc4a54a25b3b26..4cd179c655323070d71b01348a975a4a214047c0 100644 (file)
@@ -1,37 +1,13 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/agendav_plugin'
 require 'rm/rm_plugin'
 
 class AgendavRm
 
 require 'rm/rm_plugin'
 
 class AgendavRm
 
-  include Plugin
+  include AgendavPlugin
   include RmPlugin
 
   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.
 
   def delete_domain(domain)
     # AgenDAV doesn't have a concept of domains.
@@ -66,55 +42,4 @@ class AgendavRm
 
   end
 
 
   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
 end
index 5ca8bf131137be28b53b9a4f62c9afa456666500..86273cba6e5d2a31f0fb617b24a88f9088d523cc 100644 (file)
@@ -1,6 +1,6 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/davical_plugin'
 require 'rm/rm_plugin'
 
 class DavicalRm
 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.
   #
   # DAViCal only supports Postgres, so even if we ever are
   # database-agnostic, this plugin can't be.
   #
-  include Plugin
+  include DavicalPlugin
   include RmPlugin
 
   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.
 
   def delete_domain(domain)
     # DAViCal doesn't have a concept of domains.
@@ -73,82 +45,4 @@ class DavicalRm
 
   end
 
 
   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
 end
index 048319f3c20010232bf4ce90f1bdcab35741b22d..e1386512138680f4fa1d120505d61ed74cd63bf0 100644 (file)
@@ -1,47 +1,25 @@
-# Needed for rm_rf.
+# Needed for rm_r.
 require 'fileutils'
 
 require 'common/filesystem'
 require 'common/mailstore'
 require 'fileutils'
 
 require 'common/filesystem'
 require 'common/mailstore'
-require 'common/plugin'
+require 'common/dovecot_mailstore_plugin'
 require 'rm/rm_plugin'
 
 class DovecotMailstoreRm < Mailstore
 
 require 'rm/rm_plugin'
 
 class DovecotMailstoreRm < Mailstore
 
-  include Plugin
+  include DovecotMailstorePlugin
   include RmPlugin
 
   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)
 
   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)
   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)
   end
 
   def get_leftover_domains(db_domains)
@@ -61,6 +39,7 @@ class DovecotMailstoreRm < Mailstore
     return (fs_accounts - db_accounts)
   end
 
     return (fs_accounts - db_accounts)
   end
 
+
   protected;
 
   def get_domains_from_filesystem()
   protected;
 
   def get_domains_from_filesystem()
@@ -90,44 +69,4 @@ class DovecotMailstoreRm < Mailstore
   end
 
 
   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
 end
index f2621df8cffeee6c4384b9deb134e799ba64b77d..3e18e8e4b8a5c2bbb8db7555ba8745773ed8f5b5 100644 (file)
@@ -1,40 +1,13 @@
 require 'pg'
 
 require 'pg'
 
-require 'common/plugin'
+require 'common/roundcube_db_plugin'
 require 'rm/rm_plugin'
 
 class RoundcubeDbRm
 
 require 'rm/rm_plugin'
 
 class RoundcubeDbRm
 
-  include Plugin
+  include RoundcubeDbPlugin
   include RmPlugin
 
   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
   def delete_domain(domain)
     # Roundcube doesn't have a concept of domains.
   end
@@ -93,38 +66,6 @@ class RoundcubeDbRm
 
   protected;
 
 
   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 = []
 
   def get_roundcube_usernames()
     usernames = []
 
index c79e0467984198e903eef70ae296fae91dc17c60..d6c2c5f85223a80ce93c6d9a2f4b126954c3b20a 100644 (file)
@@ -1,7 +1,7 @@
 class RmDummyRunner
   include Runner
 
 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}"
     targets.each do |target|
       if target.include?('@') then
         puts "Would remove account: #{target}"
index e2f2cfe413278bba00a1f0a572451b32fd6936a7..f8cdd175be5587c0f98e032727a10bcf003a363a 100644 (file)
@@ -25,15 +25,4 @@ module RmPlugin
     raise NotImplementedError
   end
 
     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
 end
index 039ae61f7d489c9a0a7807608b959fd912f3c99c..5fe2eaf933bf7d073a200f23381718ac60529b31 100644 (file)
@@ -1,7 +1,7 @@
 class RmRunner
   include Runner
 
 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
     targets.each do |target|
       # Why think too hard? An account has an @, a domain doesn't.
       if target.include?('@') then
index 1498b0f896c898804b478df971aca8607d367cb2..86c6bc837fed71fc029de495fe4deff8bd05d0d5 100644 (file)
@@ -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.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'
 
 
   s.required_rubygems_version = '>= 1.3.6'