Begin building the framework to rename accounts. A pile of crap right now.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 19 Mar 2013 19:48:20 +0000 (15:48 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 19 Mar 2013 19:48:20 +0000 (15:48 -0400)
12 files changed:
lib/common/postfixadmin_db.rb [moved from lib/mailshears/postfixadmin_db.rb with 100% similarity]
lib/mailshears.rb
lib/mv/mv_plugin.rb [new file with mode: 0644]
lib/mv/plugins/agendav.rb [new file with mode: 0644]
lib/mv/plugins/davical.rb [new file with mode: 0644]
lib/mv/plugins/dovecot_mailstore.rb [new file with mode: 0644]
lib/mv/plugins/roundcube_db.rb [new file with mode: 0644]
lib/rm/plugins/agendav.rb [moved from lib/mailshears/plugins/agendav.rb with 98% similarity]
lib/rm/plugins/davical.rb [moved from lib/mailshears/plugins/davical.rb with 99% similarity]
lib/rm/plugins/dovecot_mailstore.rb [moved from lib/mailshears/plugins/dovecot_mailstore.rb with 99% similarity]
lib/rm/plugins/roundcube_db.rb [moved from lib/mailshears/plugins/roundcube_db.rb with 99% similarity]
lib/rm/rm_plugin.rb [moved from lib/mailshears/rm_plugin.rb with 100% similarity]

index cb8e43cdf16631ff834ee2cd57eee0a5e51aa012..1c8c042632430735c8ea0f38957faee57335bcff 100644 (file)
@@ -5,10 +5,14 @@
 require 'common/configuration'
 require 'common/errors'
 require 'common/exit_codes'
-require 'mailshears/postfixadmin_db'
+require 'common/postfixadmin_db'
 
 cfg = Configuration.new()
 
 cfg.plugins.each do |plugin_file|
-  require "mailshears/plugins/#{plugin_file}"
+  require "rm/plugins/#{plugin_file}"
+end
+
+cfg.plugins.each do |plugin_file|
+  require "mv/plugins/#{plugin_file}"
 end
diff --git a/lib/mv/mv_plugin.rb b/lib/mv/mv_plugin.rb
new file mode 100644 (file)
index 0000000..c5647f2
--- /dev/null
@@ -0,0 +1,16 @@
+module MvPlugin
+  #
+  # Plugins for moving (renaming) accounts.
+  #
+
+  def mv_domain(from. to)
+    # Rename the given domain.
+    raise NotImplementedError
+  end
+
+  def mv_account(from, to)
+    # Rename the given account.
+    raise NotImplementedError
+  end
+
+end
diff --git a/lib/mv/plugins/agendav.rb b/lib/mv/plugins/agendav.rb
new file mode 100644 (file)
index 0000000..15bf3de
--- /dev/null
@@ -0,0 +1,106 @@
+require 'pg'
+
+require 'common/plugin'
+require 'mv/mv_plugin'
+
+class AgendavMv
+
+  include Plugin
+  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
+
+  def mv_account(from, to)
+    sql_queries = ['UPDATE prefs SET username = $1 WHERE username $2;']
+    sql_queries << 'UPDATE shared SET user_from = $1 WHERE user_from = $2;'
+    sql_queries << 'UPDATE shared SET user_which = $1 WHERE user_which = $2;'
+
+    begin
+      connection = PGconn.connect(@db_host,
+                                  @db_port,
+                                  @db_opts,
+                                  @db_tty,
+                                  @db_name,
+                                  @db_user,
+                                  @db_pass)
+
+      sql_queries.each do |sql_query|
+        connection.query(sql_query, [to, from])
+      end
+
+      connection.close()
+
+    rescue PGError => e
+      # Pretend like we're database-agnostic in case we ever are.
+      raise DatabaseError.new(e)
+    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/mv/plugins/davical.rb b/lib/mv/plugins/davical.rb
new file mode 100644 (file)
index 0000000..860abdc
--- /dev/null
@@ -0,0 +1,112 @@
+require 'pg'
+
+require 'common/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 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
+
+
+  def mv_account(from, to)
+    # Delete the given username. DAViCal uses foreign keys properly
+    # and only supports postgres, so we let the ON UPDATE CASCADE
+    # trigger handle most of the work.
+    sql_queries = ['UPDATE usr SET username = $1 WHERE username = $2']
+
+    begin
+      connection = PGconn.connect(@db_host,
+                                  @db_port,
+                                  @db_opts,
+                                  @db_tty,
+                                  @db_name,
+                                  @db_user,
+                                  @db_pass)
+
+      sql_queries.each do |sql_query|
+        connection.query(sql_query, [to, from])
+      end
+
+      connection.close()
+
+    rescue PGError => e
+      # Pretend like we're database-agnostic in case we ever are.
+      raise DatabaseError.new(e)
+    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/mv/plugins/dovecot_mailstore.rb b/lib/mv/plugins/dovecot_mailstore.rb
new file mode 100644 (file)
index 0000000..f082452
--- /dev/null
@@ -0,0 +1,92 @@
+# Needed for rm_rf.
+require 'fileutils'
+
+require 'common/filesystem'
+require 'common/mailstore'
+require 'common/plugin'
+require 'mv/mv_plugin'
+
+class DovecotMailstoreMv < Mailstore
+
+  include Plugin
+  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)
+    to_path = self.get_domain_path(to)
+    FileUtils.mv(from_path, to_path)
+  end
+
+  def mv_account(from, to)
+    from_path = self.get_account_path(from)
+    to_path = self.get_account_path(to)
+    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?('@')
+      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/mv/plugins/roundcube_db.rb b/lib/mv/plugins/roundcube_db.rb
new file mode 100644 (file)
index 0000000..6859282
--- /dev/null
@@ -0,0 +1,100 @@
+require 'pg'
+
+require 'common/plugin'
+require 'mv/mv_plugin'
+
+class RoundcubeDbMv
+
+  include Plugin
+  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
+
+  def mv_account(from, to)
+    sql_queries = ['UPDATE users SET username = $1 WHERE username = $2;']
+
+    begin
+      connection = PGconn.connect(@db_host,
+                                  @db_port,
+                                  @db_opts,
+                                  @db_tty,
+                                  @db_name,
+                                  @db_user,
+                                  @db_pass)
+
+      sql_queries.each do |sql_query|
+        connection.query(sql_query, [to, from])
+      end
+
+      connection.close()
+
+    rescue PGError => e
+      # Pretend like we're database-agnostic in case we ever are.
+      raise DatabaseError.new(e)
+    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
similarity index 98%
rename from lib/mailshears/plugins/agendav.rb
rename to lib/rm/plugins/agendav.rb
index 35783022564121555c8b55a098bb31e3366bfe9d..7a594425816bcb894778cd42434fe7be5041db23 100644 (file)
@@ -1,7 +1,7 @@
 require 'pg'
 
 require 'common/plugin'
-require 'mailshears/rm_plugin'
+require 'rm/rm_plugin'
 
 class AgendavDb
 
similarity index 99%
rename from lib/mailshears/plugins/davical.rb
rename to lib/rm/plugins/davical.rb
index 2091529f64ffd6e6cb6b57856b8c44922853f043..b8c8203eb28fc5596c238e9752ef62858e4f6011 100644 (file)
@@ -1,7 +1,7 @@
 require 'pg'
 
 require 'common/plugin'
-require 'mailshears/rm_plugin'
+require 'rm/rm_plugin'
 
 class DavicalDb
   #
similarity index 99%
rename from lib/mailshears/plugins/dovecot_mailstore.rb
rename to lib/rm/plugins/dovecot_mailstore.rb
index 635d6efe6ffed1e10a5031f3018e9558fefa827d..7c2f02166fe26d5dc4c89da748547149e1685e6b 100644 (file)
@@ -4,7 +4,7 @@ require 'fileutils'
 require 'common/filesystem'
 require 'common/mailstore'
 require 'common/plugin'
-require 'mailshears/rm_plugin'
+require 'rm/rm_plugin'
 
 class DovecotMailstore < Mailstore
 
similarity index 99%
rename from lib/mailshears/plugins/roundcube_db.rb
rename to lib/rm/plugins/roundcube_db.rb
index a930189e7d48dd87b2663ae79db2c1387b5a801b..e8165d1225f270629b857de08c87637b3ea8881b 100644 (file)
@@ -1,7 +1,7 @@
 require 'pg'
 
 require 'common/plugin'
-require 'mailshears/rm_plugin'
+require 'rm/rm_plugin'
 
 class RoundcubeDb