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_user(user)
- raise NonexistentUserError.new(user) if not user_exists(user)
+ # 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 user.
+
+ # 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;'
# 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)
-
- sql_queries.each do |sql_query|
- connection.query(sql_query, [user])
- end
-
- # The earlier alias update query will leave things like
- # "foo@example.com,,bar@example.com" in the "goto" column. Now
- # we fix it. We don't do it in the loop because query() craps
- # out on the superfluous parameter.
- sql_query = "UPDATE alias SET goto=REPLACE(goto, ',,', ',');"
- connection.query(sql_query)
-
- connection.close()
-
- rescue PGError => e
- # Pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
+ 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, [user.to_s()])
end
- end
+ # The earlier alias update query will leave things like
+ # "foo@example.com,,bar@example.com" in the "goto" column. Now
+ # we fix it. We don't do it in the loop because query() craps
+ # out on the superfluous parameter.
+ sql_query = "UPDATE alias SET goto=REPLACE(goto, ',,', ',');"
+ connection.query(sql_query)
+
+ connection.close()
+ 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)
-
- 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
-
+ connection = PGconn.connect(@db_host, @db_port, @db_opts, @db_tty,
+ @db_name, @db_user, @db_pass)
- 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
- end
- connection.close()
- rescue PGError => e
- # But pretend like we're database-agnostic in case we ever are.
- raise DatabaseError.new(e)
+ sql_queries.each do |sql_query|
+ connection.query(sql_query, [domain.to_s()])
end
- return (count > 0)
+ connection.close()
end
end