X-Git-Url: http://gitweb.michael.orlitzky.com/?p=mailshears.git;a=blobdiff_plain;f=lib%2Frm%2Fplugins%2Fpostfixadmin.rb;h=c8e44abd2d9140b5a75d969078e31b6eb836aa02;hp=3883e866103e71dff7cf75ecb37681cebca7ca11;hb=b947ef8844f090eedd50be0383abe417d910bb1a;hpb=7f8654ed6582062a295e1be75ae70e99de41b323 diff --git a/lib/rm/plugins/postfixadmin.rb b/lib/rm/plugins/postfixadmin.rb index 3883e86..c8e44ab 100644 --- a/lib/rm/plugins/postfixadmin.rb +++ b/lib/rm/plugins/postfixadmin.rb @@ -3,18 +3,39 @@ require 'pg' require 'common/postfixadmin_plugin' require 'rm/rm_plugin' + +# Handle the removal of users and domains from the Postfixadmin database. +# class PostfixadminRm include PostfixadminPlugin include RmPlugin - def delete_account(account) - raise NonexistentAccountError.new(account) if not user_exists(account) + # Remove *user* from the Postfixadmin database. This should remove + # him from _every_ table in which he is referenced. Unfortunately, + # Postfixadmin does not use foreign keys or ON DELETE CASCADE + # triggers so we need to delete the associated child table records + # ourselves. + # + # @param user [User] the user to remove. + # + def remove_user(user) + raise NonexistentUserError.new(user.to_s()) if not user_exists(user) + # Remove aliases FROM our user to some other address. sql_queries = ['DELETE FROM alias WHERE address = $1;'] - # Wipe out any aliases pointed at our account. + + # Also delete aliases that point SOLELY TO our user. + sql_queries << "DELETE FROM alias WHERE goto = $1;" + + # But aliases don't need to point to a single user! If our user + # was part of a multi-recipient alias, we want to remove our user + # from the alias and leave the other recipients. If you're + # wondering about the leftover double-commas, look towards the end + # of the function. sql_queries << "UPDATE alias SET goto=REPLACE(goto, $1, '');" + sql_queries << 'DELETE FROM mailbox WHERE username = $1;' sql_queries << 'DELETE FROM quota WHERE username = $1;' sql_queries << 'DELETE FROM quota2 WHERE username = $1;' @@ -23,17 +44,11 @@ class PostfixadminRm # Should be handled by a trigger, according to PostfixAdmin code. sql_queries << 'DELETE FROM vacation_notification WHERE on_vacation = $1;' - begin - connection = PGconn.connect(@db_host, - @db_port, - @db_opts, - @db_tty, - @db_name, - @db_user, - @db_pass) + connection = PG::Connection.new(@db_hash) + begin sql_queries.each do |sql_query| - connection.query(sql_query, [account]) + connection.query(sql_query, [user.to_s()]) end # The earlier alias update query will leave things like @@ -42,82 +57,57 @@ class PostfixadminRm # out on the superfluous parameter. sql_query = "UPDATE alias SET goto=REPLACE(goto, ',,', ',');" connection.query(sql_query) - + ensure + # Make sure the connection gets closed even if a query explodes. connection.close() - - rescue PGError => e - # Pretend like we're database-agnostic in case we ever are. - raise DatabaseError.new(e) end end - def delete_domain(domain) - raise NonexistentDomainError.new(domain) if not domain_exists(domain) - + # Remove *domain* from the Postfixadmin database. This should remove + # the domain from _every_ table in which it is referenced. It should + # also remove every user that belongs to the doomed domain + # Postfixadmin has some experimental support for triggers, but they + # don't do a very good job of cleaning up. Therefore we remove all + # users in the domain manually before removing the domain itself. + # + # Log entries (from the "log" table) are not removed since they may + # still contain valuable information (although they won't mention + # this removal). + # + # @param domain [Domain] the domain to remove. + # + def remove_domain(domain) + raise NonexistentDomainError.new(domain.to_s()) if not domain_exists(domain) + + # First remove all users belonging to the domain. This will handle + # alias updates and all the sensitive crap we need to do when + # removing a user. + users = list_domains_users([domain]) + users.each { |u| remove_user(u) } + + # The domain_admins table contains one record per domain + # (repeating the user as necessary), so this really is sufficient. sql_queries = ['DELETE FROM domain_admins WHERE domain = $1;'] + + # Some of the following queries should be redundant now that we've + # removed all users in the domain. sql_queries << 'DELETE FROM alias WHERE domain = $1;' sql_queries << 'DELETE FROM mailbox WHERE domain = $1;' sql_queries << 'DELETE FROM alias_domain WHERE alias_domain = $1;' - sql_queries << 'DELETE FROM log WHERE domain = $1;' sql_queries << 'DELETE FROM vacation WHERE domain = $1;' sql_queries << 'DELETE FROM domain WHERE domain = $1;' - begin - connection = PGconn.connect(@db_host, - @db_port, - @db_opts, - @db_tty, - @db_name, - @db_user, - @db_pass) + connection = PG::Connection.new(@db_hash) - sql_queries.each do |sql_query| - connection.query(sql_query, [domain]) - 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 domain_exists(domain) - count = 0 - - # 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 COUNT(domain) as count FROM domain WHERE domain = $1;' - connection.query(sql_query, [domain]) do |result| - return false if result.ntuples() < 1 - begin - count = result.getvalue(0,0).to_i() - return false if count.nil? - rescue StandardError - return false - end + sql_queries.each do |sql_query| + connection.query(sql_query, [domain.to_s()]) end + ensure + # Make sure the connection gets closed even if a query explodes. connection.close() - rescue PGError => e - # But pretend like we're database-agnostic in case we ever are. - raise DatabaseError.new(e) end - - return (count > 0) end end