lib,test: replace connection query() method with sync_exec{,_params}.
[mailshears.git] / lib / common / postfixadmin_plugin.rb
1 require 'common/domain'
2 require 'common/plugin'
3 require 'common/user'
4 require 'pg'
5
6 # Code that all Postfixadmin plugins ({PostfixadminPrune},
7 # {PostfixadminRm}, {PostfixadminMv}) share.
8 #
9 module PostfixadminPlugin
10
11 # We implement the Plugin "interface."
12 include Plugin
13
14 # Initialize this Postfixadmin {Plugin} with values in *cfg*.
15 #
16 # @param cfg [Configuration] the configuration for this plugin.
17 #
18 def initialize(cfg)
19 @db_hash = {
20 :host => cfg.postfixadmin_dbhost,
21 :port => cfg.postfixadmin_dbport,
22 :options => cfg.postfixadmin_dbopts,
23 :tty => cfg.postfixadmin_dbtty,
24 :dbname => cfg.postfixadmin_dbname,
25 :user => cfg.postfixadmin_dbuser,
26 :password => cfg.postfixadmin_dbpass }
27 end
28
29
30 # Obtain a list of domains from Postfixadmin. This is more efficient
31 # than the {Plugin} default implementation because domains have
32 # their own table in the database and we can easily select them
33 # rather than filtering the list of users.
34 #
35 # @return [Array<Domain>] a list of the domains in Postfixadmin.
36 #
37 def list_domains()
38 domains = []
39
40 connection = PG::Connection.new(@db_hash)
41
42 # 'ALL' is a magic domain, and we don't want it.
43 sql_query = "SELECT domain FROM domain WHERE domain <> 'ALL';"
44
45 begin
46 connection.sync_exec(sql_query) do |result|
47 domains = result.field_values('domain')
48 end
49 ensure
50 # Make sure the connection gets closed even if the query explodes.
51 connection.close()
52 end
53
54 return domains.map{ |d| Domain.new(d) }
55 end
56
57
58 # Return a list of Postfixadmin users.
59 #
60 # @return [Array<User>] a list of users contained in the
61 # Postfixadmin database.
62 #
63 def list_users()
64 users = []
65
66 connection = PG::Connection.new(@db_hash)
67
68 sql_query = 'SELECT username FROM mailbox;'
69
70 begin
71 connection.sync_exec(sql_query) do |result|
72 users = result.field_values('username')
73 end
74 ensure
75 # Make sure the connection gets closed even if the query explodes.
76 connection.close()
77 end
78
79 return users.map{ |u| User.new(u) }
80 end
81
82
83
84 # Efficiently list all Postfixadmin users belonging to the given
85 # Postfixadmin *domains*.
86 #
87 # @param domains [Array<Domain>] the domains whose users we want.
88 #
89 # @return [Array<User>] a list of {User} objects belonging to
90 # *domains* for this plugin.
91 #
92 def list_domains_users(domains)
93 usernames = []
94 return usernames if domains.length() == 0
95
96 connection = PG::Connection.new(@db_hash)
97
98 # The number of parameters that we'll pass into our prepared query
99 # is the number of domains that we're given. It's important that
100 # we have at least one domain here.
101 params = 1.upto(domains.length()).map{ |i| '$' + i.to_s() }.join(',')
102 sql_query = "SELECT username FROM mailbox WHERE domain IN (#{params});"
103
104 begin
105 # Now replace each Domain with its string representation and pass
106 # those in as our individual parameters.
107 connection.sync_exec_params(sql_query, domains.map{ |d| d.to_s() }) do |result|
108 usernames = result.field_values('username')
109 end
110 ensure
111 # Make sure the connection gets closed even if the query explodes.
112 connection.close()
113 end
114
115 return usernames.map{ |u| User.new(u) }
116 end
117
118
119 # Get a list of all Postfixadmin aliases as a <tt>from => to</tt>
120 # hash. This is useful for testing, since aliases should be removed
121 # when either the "from user" or "to user" are removed.
122 #
123 # @return [Hash] all aliases known to Postfixadmin in the form of a
124 # <tt>from => to</tt> hash.
125 #
126 def list_aliases()
127 aliases = []
128
129 connection = PG::Connection.new(@db_hash)
130
131 sql_query = 'SELECT address,goto FROM alias;'
132
133 begin
134 results = connection.sync_exec(sql_query)
135 results.each do |row|
136 # row should be a hash
137 aliases << row
138 end
139 ensure
140 # Make sure the connection gets closed even if the query explodes.
141 connection.close()
142 end
143
144 return aliases
145 end
146
147
148 # A fast implementation of the "does this domain exist?"
149 # operation. It only queries the database for the existence of
150 # *domain* rather than a list of all domains (which is the default
151 # implementation).
152 #
153 # @param domain [Domain] the domain whose existence is in question.
154 #
155 # @return [Boolean] true if *domain* exists in the Postfixadmin
156 # database and false otherwise.
157 #
158 def domain_exists(domain)
159 count = 0
160
161 connection = PG::Connection.new(@db_hash)
162
163 sql_query = 'SELECT COUNT(domain) as count FROM domain WHERE domain = $1;'
164
165 begin
166 connection.sync_exec_params(sql_query, [domain.to_s()]) do |result|
167 return false if result.ntuples() < 1
168 count = result.getvalue(0,0).to_i()
169
170 return false if count.nil?
171 end
172 ensure
173 # Make sure the connection gets closed even if the query explodes.
174 connection.close()
175 end
176
177 return (count > 0)
178 end
179
180 end