require 'common/user'
require 'pg'
+# Code that all Postfixadmin plugins ({PostfixadminPrune},
+# {PostfixadminRm}, {PostfixadminMv}) share.
+#
module PostfixadminPlugin
- # Code that all Postfixadmin plugins (Prune, Rm, Mv...) will
- # share. That is, we implement the Plugin interface.
+
+ # We implement the Plugin "interface."
include Plugin
+ # Initialize this Postfixadmin {Plugin} with values in *cfg*.
+ #
+ # @param cfg [Configuration] the configuration for this plugin.
+ #
def initialize(cfg)
- @db_host = cfg.postfixadmin_dbhost
- @db_port = cfg.postfixadmin_dbport
- @db_opts = cfg.postfixadmin_dbopts
- @db_tty = cfg.postfixadmin_dbtty
- @db_name = cfg.postfixadmin_dbname
- @db_user = cfg.postfixadmin_dbuser
- @db_pass = cfg.postfixadmin_dbpass
+ @db_hash = {
+ :host => cfg.postfixadmin_dbhost,
+ :port => cfg.postfixadmin_dbport,
+ :options => cfg.postfixadmin_dbopts,
+ :tty => cfg.postfixadmin_dbtty,
+ :dbname => cfg.postfixadmin_dbname,
+ :user => cfg.postfixadmin_dbuser,
+ :password => cfg.postfixadmin_dbpass }
end
+ # Obtain a list of domains from Postfixadmin. This is more efficient
+ # than the {Plugin} default implementation because domains have
+ # their own table in the database and we can easily select them
+ # rather than filtering the list of users.
+ #
+ # @return [Array<Domain>] a list of the domains in Postfixadmin.
+ #
def list_domains()
domains = []
- connection = PGconn.connect(@db_host, @db_port, @db_opts, @db_tty,
- @db_name, @db_user, @db_pass)
+ connection = PG::Connection.new(@db_hash)
# 'ALL' is a magic domain, and we don't want it.
sql_query = "SELECT domain FROM domain WHERE domain <> 'ALL';"
- connection.query(sql_query) do |result|
- domains = result.field_values('domain')
- end
- connection.close()
+ begin
+ connection.query(sql_query) do |result|
+ domains = result.field_values('domain')
+ end
+ ensure
+ # Make sure the connection gets closed even if the query explodes.
+ connection.close()
+ end
return domains.map{ |d| Domain.new(d) }
end
-
+ # Return a list of Postfixadmin users.
+ #
+ # @return [Array<User>] a list of users contained in the
+ # Postfixadmin database.
+ #
def list_users()
users = []
- connection = PGconn.connect(@db_host, @db_port, @db_opts, @db_tty,
- @db_name, @db_user, @db_pass)
+ connection = PG::Connection.new(@db_hash)
sql_query = 'SELECT username FROM mailbox;'
- connection.query(sql_query) do |result|
- users = result.field_values('username')
- end
- connection.close()
+ begin
+ connection.query(sql_query) do |result|
+ users = result.field_values('username')
+ end
+ ensure
+ # Make sure the connection gets closed even if the query explodes.
+ connection.close()
+ end
return users.map{ |u| User.new(u) }
end
+
+ # Efficiently list all Postfixadmin users belonging to the given
+ # Postfixadmin *domains*.
+ #
+ # @param domains [Array<Domain>] the domains whose users we want.
+ #
+ # @return [Array<User>] a list of {User} objects belonging to
+ # *domains* for this plugin.
+ #
def list_domains_users(domains)
usernames = []
-
- connection = PGconn.connect(@db_host, @db_port, @db_opts, @db_tty,
- @db_name, @db_user, @db_pass)
-
- sql_query = 'SELECT username FROM mailbox WHERE domain IN $1;'
-
- connection.query(sql_query, domains.map{|d| d.to_s()}) do |result|
- usernames = result.field_values('username')
+ return usernames if domains.length() == 0
+
+ connection = PG::Connection.new(@db_hash)
+
+ # The number of parameters that we'll pass into our prepared query
+ # is the number of domains that we're given. It's important that
+ # we have at least one domain here.
+ params = 1.upto(domains.length()).map{ |i| '$' + i.to_s() }.join(',')
+ sql_query = "SELECT username FROM mailbox WHERE domain IN (#{params});"
+
+ begin
+ # Now replace each Domain with its string representation and pass
+ # those in as our individual parameters.
+ connection.query(sql_query, domains.map{ |d| d.to_s() }) do |result|
+ usernames = result.field_values('username')
+ end
+ ensure
+ # Make sure the connection gets closed even if the query explodes.
+ connection.close()
end
- connection.close()
-
return usernames.map{ |u| User.new(u) }
end
+ # Get a list of all Postfixadmin aliases as a <tt>from => to</tt>
+ # hash. This is useful for testing, since aliases should be removed
+ # when either the "from user" or "to user" are removed.
+ #
+ # @return [Hash] all aliases known to Postfixadmin in the form of a
+ # <tt>from => to</tt> hash.
+ #
def list_aliases()
- #
- # Get a list of all aliases, useful for testing.
- #
aliases = []
- connection = PGconn.connect(@db_host, @db_port, @db_opts, @db_tty,
- @db_name, @db_user, @db_pass)
+ connection = PG::Connection.new(@db_hash)
sql_query = 'SELECT address,goto FROM alias;'
- results = connection.query(sql_query)
- results.each do |row|
- aliases << row # row should be a hash
- end
- connection.close()
+ begin
+ results = connection.query(sql_query)
+ results.each do |row|
+ # row should be a hash
+ aliases << row
+ end
+ ensure
+ # Make sure the connection gets closed even if the query explodes.
+ connection.close()
+ end
return aliases
end
+
+ # A fast implementation of the "does this domain exist?"
+ # operation. It only queries the database for the existence of
+ # *domain* rather than a list of all domains (which is the default
+ # implementation).
+ #
+ # @param domain [Domain] the domain whose existence is in question.
+ #
+ # @return [Boolean] true if *domain* exists in the Postfixadmin
+ # database and false otherwise.
+ #
+ def domain_exists(domain)
+ count = 0
+
+ connection = PG::Connection.new(@db_hash)
+
+ sql_query = 'SELECT COUNT(domain) as count FROM domain WHERE domain = $1;'
+
+ begin
+ connection.query(sql_query, [domain.to_s()]) do |result|
+ return false if result.ntuples() < 1
+ count = result.getvalue(0,0).to_i()
+
+ return false if count.nil?
+ end
+ ensure
+ # Make sure the connection gets closed even if the query explodes.
+ connection.close()
+ end
+
+ return (count > 0)
+ end
+
end