1 # -*- coding: UTF-8 -*- |
|
2 # Copyright (c) 2007 - 2010, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 |
|
5 """Virtual Mail Manager's Domain class to manage e-mail domains.""" |
|
6 |
|
7 from random import choice |
|
8 |
|
9 from __main__ import ERR |
|
10 from Exceptions import VMMDomainException as VMMDE |
|
11 import VirtualMailManager as VMM |
|
12 from Transport import Transport |
|
13 |
|
14 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz' |
|
15 |
|
16 class Domain(object): |
|
17 """Class to manage e-mail domains.""" |
|
18 __slots__ = ('_basedir','_domaindir','_id','_name','_transport','_dbh') |
|
19 def __init__(self, dbh, domainname, basedir=None, transport=None): |
|
20 """Creates a new Domain instance. |
|
21 |
|
22 Keyword arguments: |
|
23 dbh -- a pyPgSQL.PgSQL.connection |
|
24 domainname -- name of the domain (str) |
|
25 transport -- default vmm.cfg/misc/transport (str) |
|
26 """ |
|
27 self._dbh = dbh |
|
28 self._name = VMM.VirtualMailManager.chkDomainname(domainname) |
|
29 self._basedir = basedir |
|
30 if transport is not None: |
|
31 self._transport = Transport(self._dbh, transport=transport) |
|
32 else: |
|
33 self._transport = transport |
|
34 self._id = 0 |
|
35 self._domaindir = None |
|
36 if not self._exists() and self._isAlias(): |
|
37 raise VMMDE(_(u"The domain “%s” is an alias domain.") %self._name, |
|
38 ERR.DOMAIN_ALIAS_EXISTS) |
|
39 |
|
40 def _exists(self): |
|
41 """Checks if the domain already exists. |
|
42 |
|
43 If the domain exists _id will be set and returns True, otherwise False |
|
44 will be returned. |
|
45 """ |
|
46 dbc = self._dbh.cursor() |
|
47 dbc.execute("SELECT gid, tid, domaindir FROM domain_data WHERE gid =\ |
|
48 (SELECT gid FROM domain_name WHERE domainname = %s AND is_primary)", |
|
49 self._name) |
|
50 result = dbc.fetchone() |
|
51 dbc.close() |
|
52 if result is not None: |
|
53 self._id, self._domaindir = result[0], result[2] |
|
54 self._transport = Transport(self._dbh, tid=result[1]) |
|
55 return True |
|
56 else: |
|
57 return False |
|
58 |
|
59 def _isAlias(self): |
|
60 """Checks if self._name is known for an alias domain.""" |
|
61 dbc = self._dbh.cursor() |
|
62 dbc.execute('SELECT is_primary FROM domain_name WHERE domainname = %s', |
|
63 self._name) |
|
64 result = dbc.fetchone() |
|
65 dbc.close() |
|
66 if result is not None and not result[0]: |
|
67 return True |
|
68 else: |
|
69 return False |
|
70 |
|
71 def _setID(self): |
|
72 """Sets the ID of the domain.""" |
|
73 dbc = self._dbh.cursor() |
|
74 dbc.execute("SELECT nextval('domain_gid')") |
|
75 self._id = dbc.fetchone()[0] |
|
76 dbc.close() |
|
77 |
|
78 def _prepare(self): |
|
79 self._setID() |
|
80 self._domaindir = "%s/%s/%i" % (self._basedir, choice(MAILDIR_CHARS), |
|
81 self._id) |
|
82 |
|
83 def _has(self, what): |
|
84 """Checks if aliases or accounts are assigned to the domain. |
|
85 |
|
86 If there are assigned accounts or aliases True will be returned, |
|
87 otherwise False will be returned. |
|
88 |
|
89 Keyword arguments: |
|
90 what -- 'alias' or 'users' (strings) |
|
91 """ |
|
92 if what not in ['alias', 'users']: |
|
93 return False |
|
94 dbc = self._dbh.cursor() |
|
95 if what == 'users': |
|
96 dbc.execute("SELECT count(gid) FROM users WHERE gid=%s", self._id) |
|
97 else: |
|
98 dbc.execute("SELECT count(gid) FROM alias WHERE gid=%s", self._id) |
|
99 count = dbc.fetchone() |
|
100 dbc.close() |
|
101 if count[0] > 0: |
|
102 return True |
|
103 else: |
|
104 return False |
|
105 |
|
106 def _chkDelete(self, delUser, delAlias): |
|
107 """Checks dependencies for deletion. |
|
108 |
|
109 Keyword arguments: |
|
110 delUser -- ignore available accounts (bool) |
|
111 delAlias -- ignore available aliases (bool) |
|
112 """ |
|
113 if not delUser: |
|
114 hasUser = self._has('users') |
|
115 else: |
|
116 hasUser = False |
|
117 if not delAlias: |
|
118 hasAlias = self._has('alias') |
|
119 else: |
|
120 hasAlias = False |
|
121 if hasUser and hasAlias: |
|
122 raise VMMDE(_(u'There are accounts and aliases.'), |
|
123 ERR.ACCOUNT_AND_ALIAS_PRESENT) |
|
124 elif hasUser: |
|
125 raise VMMDE(_(u'There are accounts.'), |
|
126 ERR.ACCOUNT_PRESENT) |
|
127 elif hasAlias: |
|
128 raise VMMDE(_(u'There are aliases.'), |
|
129 ERR.ALIAS_PRESENT) |
|
130 |
|
131 def save(self): |
|
132 """Stores the new domain in the database.""" |
|
133 if self._id < 1: |
|
134 self._prepare() |
|
135 dbc = self._dbh.cursor() |
|
136 dbc.execute("INSERT INTO domain_data (gid, tid, domaindir)\ |
|
137 VALUES (%s, %s, %s)", self._id, self._transport.getID(), self._domaindir) |
|
138 dbc.execute("INSERT INTO domain_name (domainname, gid, is_primary)\ |
|
139 VALUES (%s, %s, %s)", self._name, self._id, True) |
|
140 self._dbh.commit() |
|
141 dbc.close() |
|
142 else: |
|
143 raise VMMDE(_(u'The domain “%s” already exists.') % self._name, |
|
144 ERR.DOMAIN_EXISTS) |
|
145 |
|
146 def delete(self, delUser=False, delAlias=False): |
|
147 """Deletes the domain. |
|
148 |
|
149 Keyword arguments: |
|
150 delUser -- force deletion of available accounts (bool) |
|
151 delAlias -- force deletion of available aliases (bool) |
|
152 """ |
|
153 if self._id > 0: |
|
154 self._chkDelete(delUser, delAlias) |
|
155 dbc = self._dbh.cursor() |
|
156 for t in ('alias','users','relocated','domain_name','domain_data'): |
|
157 dbc.execute("DELETE FROM %s WHERE gid = %d" % (t, self._id)) |
|
158 self._dbh.commit() |
|
159 dbc.close() |
|
160 else: |
|
161 raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name, |
|
162 ERR.NO_SUCH_DOMAIN) |
|
163 |
|
164 def updateTransport(self, transport, force=False): |
|
165 """Sets a new transport for the domain. |
|
166 |
|
167 Keyword arguments: |
|
168 transport -- the new transport (str) |
|
169 force -- True/False force new transport for all accounts (bool) |
|
170 """ |
|
171 if self._id > 0: |
|
172 if transport == self._transport.getTransport(): |
|
173 return |
|
174 trsp = Transport(self._dbh, transport=transport) |
|
175 dbc = self._dbh.cursor() |
|
176 dbc.execute("UPDATE domain_data SET tid = %s WHERE gid = %s", |
|
177 trsp.getID(), self._id) |
|
178 if dbc.rowcount > 0: |
|
179 self._dbh.commit() |
|
180 if force: |
|
181 dbc.execute("UPDATE users SET tid = %s WHERE gid = %s", |
|
182 trsp.getID(), self._id) |
|
183 if dbc.rowcount > 0: |
|
184 self._dbh.commit() |
|
185 dbc.close() |
|
186 else: |
|
187 raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name, |
|
188 ERR.NO_SUCH_DOMAIN) |
|
189 |
|
190 def getID(self): |
|
191 """Returns the ID of the domain.""" |
|
192 return self._id |
|
193 |
|
194 def getDir(self): |
|
195 """Returns the directory of the domain.""" |
|
196 return self._domaindir |
|
197 |
|
198 def getTransport(self): |
|
199 """Returns domain's transport.""" |
|
200 return self._transport.getTransport() |
|
201 |
|
202 def getTransportID(self): |
|
203 """Returns the ID from the domain's transport.""" |
|
204 return self._transport.getID() |
|
205 |
|
206 def getInfo(self): |
|
207 """Returns a dictionary with information about the domain.""" |
|
208 sql = """\ |
|
209 SELECT gid, domainname, transport, domaindir, aliasdomains, accounts, |
|
210 aliases, relocated |
|
211 FROM vmm_domain_info |
|
212 WHERE gid = %i""" % self._id |
|
213 dbc = self._dbh.cursor() |
|
214 dbc.execute(sql) |
|
215 info = dbc.fetchone() |
|
216 dbc.close() |
|
217 if info is None: |
|
218 raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name, |
|
219 ERR.NO_SUCH_DOMAIN) |
|
220 else: |
|
221 keys = ['gid', 'domainname', 'transport', 'domaindir', |
|
222 'aliasdomains', 'accounts', 'aliases', 'relocated'] |
|
223 return dict(zip(keys, info)) |
|
224 |
|
225 def getAccounts(self): |
|
226 """Returns a list with all accounts from the domain.""" |
|
227 dbc = self._dbh.cursor() |
|
228 dbc.execute("SELECT local_part from users where gid = %s ORDER BY\ |
|
229 local_part", self._id) |
|
230 users = dbc.fetchall() |
|
231 dbc.close() |
|
232 accounts = [] |
|
233 if len(users) > 0: |
|
234 addr = u'@'.join |
|
235 _dom = self._name |
|
236 accounts = [addr((account[0], _dom)) for account in users] |
|
237 return accounts |
|
238 |
|
239 def getAliases(self): |
|
240 """Returns a list with all aliases from the domain.""" |
|
241 dbc = self._dbh.cursor() |
|
242 dbc.execute("SELECT DISTINCT address FROM alias WHERE gid = %s\ |
|
243 ORDER BY address", self._id) |
|
244 addresses = dbc.fetchall() |
|
245 dbc.close() |
|
246 aliases = [] |
|
247 if len(addresses) > 0: |
|
248 addr = u'@'.join |
|
249 _dom = self._name |
|
250 aliases = [addr((alias[0], _dom)) for alias in addresses] |
|
251 return aliases |
|
252 |
|
253 def getRelocated(self): |
|
254 """Returns a list with all addresses from relocated users.""" |
|
255 dbc = self._dbh.cursor() |
|
256 dbc.execute("SELECT address FROM relocated WHERE gid = %s\ |
|
257 ORDER BY address", self._id) |
|
258 addresses = dbc.fetchall() |
|
259 dbc.close() |
|
260 relocated = [] |
|
261 if len(addresses) > 0: |
|
262 addr = u'@'.join |
|
263 _dom = self._name |
|
264 relocated = [addr((address[0], _dom)) for address in addresses] |
|
265 return relocated |
|
266 |
|
267 def getAliaseNames(self): |
|
268 """Returns a list with all alias names from the domain.""" |
|
269 dbc = self._dbh.cursor() |
|
270 dbc.execute("SELECT domainname FROM domain_name WHERE gid = %s\ |
|
271 AND NOT is_primary ORDER BY domainname", self._id) |
|
272 anames = dbc.fetchall() |
|
273 dbc.close() |
|
274 aliasdomains = [] |
|
275 if len(anames) > 0: |
|
276 aliasdomains = [aname[0] for aname in anames] |
|
277 return aliasdomains |
|
278 |
|
279 def search(dbh, pattern=None, like=False): |
|
280 if pattern is not None and like is False: |
|
281 pattern = VMM.VirtualMailManager.chkDomainname(pattern) |
|
282 sql = 'SELECT gid, domainname, is_primary FROM domain_name' |
|
283 if pattern is None: |
|
284 pass |
|
285 elif like: |
|
286 sql += " WHERE domainname LIKE '%s'" % pattern |
|
287 else: |
|
288 sql += " WHERE domainname = '%s'" % pattern |
|
289 sql += ' ORDER BY is_primary DESC, domainname' |
|
290 dbc = dbh.cursor() |
|
291 dbc.execute(sql) |
|
292 result = dbc.fetchall() |
|
293 dbc.close() |
|
294 |
|
295 gids = [domain[0] for domain in result if domain[2]] |
|
296 domains = {} |
|
297 for gid, domain, is_primary in result: |
|
298 if is_primary: |
|
299 if not gid in domains: |
|
300 domains[gid] = [domain] |
|
301 else: |
|
302 domains[gid].insert(0, domain) |
|
303 else: |
|
304 if gid in gids: |
|
305 if gid in domains: |
|
306 domains[gid].append(domain) |
|
307 else: |
|
308 domains[gid] = [domain] |
|
309 else: |
|
310 gids.append(gid) |
|
311 domains[gid] = [None, domain] |
|
312 return gids, domains |
|
313 |
|