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