From ec3c9099a29f40d95f055ea0f7fb25a99d913de3 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 19 Mar 2013 15:48:20 -0400 Subject: [PATCH 1/1] Begin building the framework to rename accounts. A pile of crap right now. --- lib/{mailshears => common}/postfixadmin_db.rb | 0 lib/mailshears.rb | 8 +- lib/mv/mv_plugin.rb | 16 +++ lib/mv/plugins/agendav.rb | 106 +++++++++++++++++ lib/mv/plugins/davical.rb | 112 ++++++++++++++++++ lib/mv/plugins/dovecot_mailstore.rb | 92 ++++++++++++++ lib/mv/plugins/roundcube_db.rb | 100 ++++++++++++++++ lib/{mailshears => rm}/plugins/agendav.rb | 2 +- lib/{mailshears => rm}/plugins/davical.rb | 2 +- .../plugins/dovecot_mailstore.rb | 2 +- .../plugins/roundcube_db.rb | 2 +- lib/{mailshears => rm}/rm_plugin.rb | 0 12 files changed, 436 insertions(+), 6 deletions(-) rename lib/{mailshears => common}/postfixadmin_db.rb (100%) create mode 100644 lib/mv/mv_plugin.rb create mode 100644 lib/mv/plugins/agendav.rb create mode 100644 lib/mv/plugins/davical.rb create mode 100644 lib/mv/plugins/dovecot_mailstore.rb create mode 100644 lib/mv/plugins/roundcube_db.rb rename lib/{mailshears => rm}/plugins/agendav.rb (98%) rename lib/{mailshears => rm}/plugins/davical.rb (99%) rename lib/{mailshears => rm}/plugins/dovecot_mailstore.rb (99%) rename lib/{mailshears => rm}/plugins/roundcube_db.rb (99%) rename lib/{mailshears => rm}/rm_plugin.rb (100%) diff --git a/lib/mailshears/postfixadmin_db.rb b/lib/common/postfixadmin_db.rb similarity index 100% rename from lib/mailshears/postfixadmin_db.rb rename to lib/common/postfixadmin_db.rb diff --git a/lib/mailshears.rb b/lib/mailshears.rb index cb8e43c..1c8c042 100644 --- a/lib/mailshears.rb +++ b/lib/mailshears.rb @@ -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 index 0000000..c5647f2 --- /dev/null +++ b/lib/mv/mv_plugin.rb @@ -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 index 0000000..15bf3de --- /dev/null +++ b/lib/mv/plugins/agendav.rb @@ -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 index 0000000..860abdc --- /dev/null +++ b/lib/mv/plugins/davical.rb @@ -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 index 0000000..f082452 --- /dev/null +++ b/lib/mv/plugins/dovecot_mailstore.rb @@ -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 index 0000000..6859282 --- /dev/null +++ b/lib/mv/plugins/roundcube_db.rb @@ -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 diff --git a/lib/mailshears/plugins/agendav.rb b/lib/rm/plugins/agendav.rb similarity index 98% rename from lib/mailshears/plugins/agendav.rb rename to lib/rm/plugins/agendav.rb index 3578302..7a59442 100644 --- a/lib/mailshears/plugins/agendav.rb +++ b/lib/rm/plugins/agendav.rb @@ -1,7 +1,7 @@ require 'pg' require 'common/plugin' -require 'mailshears/rm_plugin' +require 'rm/rm_plugin' class AgendavDb diff --git a/lib/mailshears/plugins/davical.rb b/lib/rm/plugins/davical.rb similarity index 99% rename from lib/mailshears/plugins/davical.rb rename to lib/rm/plugins/davical.rb index 2091529..b8c8203 100644 --- a/lib/mailshears/plugins/davical.rb +++ b/lib/rm/plugins/davical.rb @@ -1,7 +1,7 @@ require 'pg' require 'common/plugin' -require 'mailshears/rm_plugin' +require 'rm/rm_plugin' class DavicalDb # diff --git a/lib/mailshears/plugins/dovecot_mailstore.rb b/lib/rm/plugins/dovecot_mailstore.rb similarity index 99% rename from lib/mailshears/plugins/dovecot_mailstore.rb rename to lib/rm/plugins/dovecot_mailstore.rb index 635d6ef..7c2f021 100644 --- a/lib/mailshears/plugins/dovecot_mailstore.rb +++ b/lib/rm/plugins/dovecot_mailstore.rb @@ -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 diff --git a/lib/mailshears/plugins/roundcube_db.rb b/lib/rm/plugins/roundcube_db.rb similarity index 99% rename from lib/mailshears/plugins/roundcube_db.rb rename to lib/rm/plugins/roundcube_db.rb index a930189..e8165d1 100644 --- a/lib/mailshears/plugins/roundcube_db.rb +++ b/lib/rm/plugins/roundcube_db.rb @@ -1,7 +1,7 @@ require 'pg' require 'common/plugin' -require 'mailshears/rm_plugin' +require 'rm/rm_plugin' class RoundcubeDb diff --git a/lib/mailshears/rm_plugin.rb b/lib/rm/rm_plugin.rb similarity index 100% rename from lib/mailshears/rm_plugin.rb rename to lib/rm/rm_plugin.rb -- 2.43.2