1 # -*- coding: UTF-8 -*- |
|
2 # Copyright (c) 2008 - 2014, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 """ |
|
5 VirtualMailManager.maillocation |
|
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
7 |
|
8 Virtual Mail Manager's maillocation module to handle Dovecot's |
|
9 mail_location setting for accounts. |
|
10 |
|
11 """ |
|
12 |
|
13 from VirtualMailManager.constants import MAILLOCATION_INIT |
|
14 from VirtualMailManager.errors import MailLocationError as MLErr |
|
15 from VirtualMailManager.pycompat import all |
|
16 |
|
17 |
|
18 __all__ = ('MailLocation', 'known_format') |
|
19 |
|
20 _ = lambda msg: msg |
|
21 _format_info = { |
|
22 'maildir': dict(dovecot_version=0x10000f00, postfix=True), |
|
23 'mdbox': dict(dovecot_version=0x20000b05, postfix=False), |
|
24 'sdbox': dict(dovecot_version=0x20000c03, postfix=False), |
|
25 } |
|
26 |
|
27 |
|
28 class MailLocation(object): |
|
29 """Class to handle mail_location relevant information.""" |
|
30 __slots__ = ('_directory', '_mbfmt', '_mid', '_dbh') |
|
31 _kwargs = ('mid', 'mbfmt', 'directory') |
|
32 |
|
33 def __init__(self, dbh, **kwargs): |
|
34 """Creates a new MailLocation instance. |
|
35 |
|
36 Either the mid keyword or the mbfmt and directory keywords must be |
|
37 specified. |
|
38 |
|
39 Arguments: |
|
40 |
|
41 `dbh` : pyPgSQL.PgSQL.Connection |
|
42 A database connection for the database access. |
|
43 |
|
44 Keyword arguments: |
|
45 |
|
46 `mid` : int |
|
47 the id of a mail_location |
|
48 `mbfmt` : str |
|
49 the mailbox format of the mail_location. One out of: ``maildir``, |
|
50 ``sdbox`` and ``mdbox``. |
|
51 `directory` : str |
|
52 name of the mailbox root directory. |
|
53 """ |
|
54 self._dbh = dbh |
|
55 self._directory = None |
|
56 self._mbfmt = None |
|
57 self._mid = 0 |
|
58 |
|
59 for key in kwargs.iterkeys(): |
|
60 if key not in self.__class__._kwargs: |
|
61 raise ValueError('unrecognized keyword: %r' % key) |
|
62 mid = kwargs.get('mid') |
|
63 if mid: |
|
64 assert isinstance(mid, (int, long)) |
|
65 self._load_by_mid(mid) |
|
66 else: |
|
67 args = kwargs.get('mbfmt'), kwargs.get('directory') |
|
68 assert all(isinstance(arg, basestring) for arg in args) |
|
69 if args[0].lower() not in _format_info: |
|
70 raise MLErr(_(u"Unsupported mailbox format: '%s'") % args[0], |
|
71 MAILLOCATION_INIT) |
|
72 directory = args[1].strip() |
|
73 if not directory: |
|
74 raise MLErr(_(u"Empty directory name"), MAILLOCATION_INIT) |
|
75 if len(directory) > 20: |
|
76 raise MLErr(_(u"Directory name is too long: '%s'") % directory, |
|
77 MAILLOCATION_INIT) |
|
78 self._load_by_names(args[0].lower(), directory) |
|
79 |
|
80 def __str__(self): |
|
81 return u'%s:~/%s' % (self._mbfmt, self._directory) |
|
82 |
|
83 @property |
|
84 def directory(self): |
|
85 """The mail_location's directory name.""" |
|
86 return self._directory |
|
87 |
|
88 @property |
|
89 def dovecot_version(self): |
|
90 """The required Dovecot version for this mailbox format.""" |
|
91 return _format_info[self._mbfmt]['dovecot_version'] |
|
92 |
|
93 @property |
|
94 def postfix(self): |
|
95 """`True` if Postfix supports this mailbox format, else `False`.""" |
|
96 return _format_info[self._mbfmt]['postfix'] |
|
97 |
|
98 @property |
|
99 def mbformat(self): |
|
100 """The mail_location's mailbox format.""" |
|
101 return self._mbfmt |
|
102 |
|
103 @property |
|
104 def mail_location(self): |
|
105 """The mail_location, e.g. ``maildir:~/Maildir``""" |
|
106 return self.__str__() |
|
107 |
|
108 @property |
|
109 def mid(self): |
|
110 """The mail_location's unique ID.""" |
|
111 return self._mid |
|
112 |
|
113 def _load_by_mid(self, mid): |
|
114 """Load mail_location relevant information by *mid*""" |
|
115 dbc = self._dbh.cursor() |
|
116 dbc.execute('SELECT format, directory FROM mailboxformat, ' |
|
117 'maillocation WHERE mid = %u AND ' |
|
118 'maillocation.fid = mailboxformat.fid' % mid) |
|
119 result = dbc.fetchone() |
|
120 dbc.close() |
|
121 if not result: |
|
122 raise ValueError('Unknown mail_location id specified: %r' % mid) |
|
123 self._mid = mid |
|
124 self._mbfmt, self._directory = result |
|
125 |
|
126 def _load_by_names(self, mbfmt, directory): |
|
127 """Try to load mail_location relevant information by *mbfmt* and |
|
128 *directory* name. If it fails goto _save().""" |
|
129 dbc = self._dbh.cursor() |
|
130 dbc.execute("SELECT mid FROM maillocation WHERE fid = (SELECT fid " |
|
131 "FROM mailboxformat WHERE format = %s) AND directory = %s", |
|
132 (mbfmt, directory)) |
|
133 result = dbc.fetchone() |
|
134 dbc.close() |
|
135 if not result: |
|
136 self._save(mbfmt, directory) |
|
137 else: |
|
138 self._mid = result[0] |
|
139 self._mbfmt = mbfmt |
|
140 self._directory = directory |
|
141 |
|
142 def _save(self, mbfmt, directory): |
|
143 """Save a new mail_location in the database.""" |
|
144 dbc = self._dbh.cursor() |
|
145 dbc.execute("SELECT nextval('maillocation_id')") |
|
146 mid = dbc.fetchone()[0] |
|
147 dbc.execute("INSERT INTO maillocation (fid, mid, directory) VALUES (" |
|
148 "(SELECT fid FROM mailboxformat WHERE format = %s), %s, " |
|
149 "%s)", (mbfmt, mid, directory)) |
|
150 self._dbh.commit() |
|
151 dbc.close() |
|
152 self._mid = mid |
|
153 self._mbfmt = mbfmt |
|
154 self._directory = directory |
|
155 |
|
156 |
|
157 def known_format(mbfmt): |
|
158 """Checks if the mailbox format *mbfmt* is known, returns bool.""" |
|
159 return mbfmt.lower() in _format_info |
|
160 |
|
161 del _ |
|