|
1 # coding: utf-8 |
|
2 # Copyright (c) 2011 - 2012, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 """ |
|
5 VirtualMailManager.serviceset |
|
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
7 |
|
8 Virtual Mail Manager's ServiceSet class for simplified database access |
|
9 to the service_set table. |
|
10 """ |
|
11 |
|
12 SERVICES = ('smtp', 'pop3', 'imap', 'sieve') |
|
13 |
|
14 cfg_dget = lambda option: None |
|
15 |
|
16 |
|
17 class ServiceSet(object): |
|
18 """A wrapper class that provides access to the service_set table. |
|
19 |
|
20 Each ServiceSet object provides following - read only - attributes: |
|
21 |
|
22 `ssid` : long |
|
23 The id of the service set |
|
24 `smtp` : bool |
|
25 Boolean flag for service smtp |
|
26 `pop3` : bool |
|
27 Boolean flag for service pop3 |
|
28 `imap` : bool |
|
29 Boolean flag for service imap |
|
30 `sieve` : bool |
|
31 Boolean flag for service sieve |
|
32 `services` : dict |
|
33 The four services above with boolean values |
|
34 """ |
|
35 __slots__ = ('_ssid', '_services', '_sieve_col', '_dbh') |
|
36 _kwargs = (('ssid',) + SERVICES) |
|
37 |
|
38 def __init__(self, dbh, **kwargs): |
|
39 """Creates a new ServiceSet instance. |
|
40 |
|
41 Either the 'ssid' keyword argument or one or more of the service |
|
42 arguments ('smtp', 'pop3', 'imap', 'sieve') must be provided. |
|
43 |
|
44 Arguments: |
|
45 `dbh` : pyPgSQL.PgSQL.Connection or psycopg2.extensions.connection |
|
46 A database connection for the database access. |
|
47 |
|
48 Keyword arguments: |
|
49 `ssid` : int |
|
50 The id of the service set (>0) |
|
51 `smtp` : bool |
|
52 Boolean flag for service smtp - default `True` |
|
53 `pop3` : bool |
|
54 Boolean flag for service pop3 - default `True` |
|
55 `imap` : bool |
|
56 Boolean flag for service imap - default `True` |
|
57 `sieve` : bool |
|
58 Boolean flag for service sieve - default `True` |
|
59 """ |
|
60 self._dbh = dbh |
|
61 self._ssid = 0 |
|
62 self._services = dict.fromkeys(SERVICES, True) |
|
63 if cfg_dget('misc.dovecot_version') < 0x10200b02: |
|
64 self._sieve_col = 'managesieve' |
|
65 else: |
|
66 self._sieve_col = 'sieve' |
|
67 |
|
68 for key in kwargs.iterkeys(): |
|
69 if key not in self.__class__._kwargs: |
|
70 raise ValueError('unrecognized keyword: %r' % key) |
|
71 if key == 'ssid': |
|
72 assert not isinstance(kwargs[key], bool) and \ |
|
73 isinstance(kwargs[key], (int, long)) and kwargs[key] > 0 |
|
74 self._load_by_ssid(kwargs[key]) |
|
75 break |
|
76 else: |
|
77 assert isinstance(kwargs[key], bool) |
|
78 if not kwargs[key]: |
|
79 self._services[key] = kwargs[key] |
|
80 if not self._ssid: |
|
81 self._load_by_services() |
|
82 |
|
83 def __eq__(self, other): |
|
84 if isinstance(other, self.__class__): |
|
85 return self._ssid == other._ssid |
|
86 return NotImplemented |
|
87 |
|
88 def __ne__(self, other): |
|
89 if isinstance(other, self.__class__): |
|
90 return self._ssid != other._ssid |
|
91 return NotImplemented |
|
92 |
|
93 def __getattr__(self, name): |
|
94 if name not in self.__class__._kwargs: |
|
95 raise AttributeError('%r object has no attribute %r' % ( |
|
96 self.__class__.__name__, name)) |
|
97 if name == 'ssid': |
|
98 return self._ssid |
|
99 else: |
|
100 return self._services[name] |
|
101 |
|
102 def __repr__(self): |
|
103 return '%s(%s, %s)' % (self.__class__.__name__, self._dbh, |
|
104 ', '.join('%s=%r' % s for s in self._services.iteritems())) |
|
105 |
|
106 def _load_by_services(self): |
|
107 """Try to load the service_set by it's service combination.""" |
|
108 sql = ('SELECT ssid FROM service_set WHERE %s' % |
|
109 ' AND '.join('%s = %s' % |
|
110 (k, str(v).upper()) for k, v in self._services.iteritems())) |
|
111 if self._sieve_col == 'managesieve': |
|
112 sql.replace('sieve', self._sieve_col) |
|
113 dbc = self._dbh.cursor() |
|
114 dbc.execute(sql) |
|
115 result = dbc.fetchone() |
|
116 dbc.close() |
|
117 if result: |
|
118 self._ssid = result[0] |
|
119 else: |
|
120 self._save() |
|
121 |
|
122 def _load_by_ssid(self, ssid): |
|
123 """Try to load the service_set by it's primary key.""" |
|
124 dbc = self._dbh.cursor() |
|
125 dbc.execute('SELECT ssid, smtp, pop3, imap, %s' % (self._sieve_col,) + |
|
126 ' FROM service_set WHERE ssid = %s', (ssid,)) |
|
127 result = dbc.fetchone() |
|
128 dbc.close() |
|
129 if not result: |
|
130 raise ValueError('Unknown service_set id specified: %r' % ssid) |
|
131 self._ssid = result[0] |
|
132 #self._services.update(zip(SERVICES, result[1:])) |
|
133 for key, value in zip(SERVICES, result[1:]): # pyPgSQL compatible |
|
134 if value: |
|
135 self._services[key] = True |
|
136 else: |
|
137 self._services[key] = False |
|
138 |
|
139 def _save(self): |
|
140 """Store a new service_set in the database.""" |
|
141 sql = ('INSERT INTO service_set (ssid, smtp, pop3, imap, %s) ' % |
|
142 (self._sieve_col,) + |
|
143 'VALUES (%(ssid)s, %(smtp)s, %(pop3)s, %(imap)s, %(sieve)s)') |
|
144 if self._sieve_col == 'managesieve': |
|
145 sql.replace('sieve', self._sieve_col) |
|
146 self._set_ssid() |
|
147 values = {'ssid': self._ssid} |
|
148 values.update(self._services) |
|
149 dbc = self._dbh.cursor() |
|
150 dbc.execute(sql, values) |
|
151 self._dbh.commit() |
|
152 dbc.close() |
|
153 |
|
154 def _set_ssid(self): |
|
155 """Set the unique ID for the new service_set.""" |
|
156 assert self._ssid == 0 |
|
157 dbc = self._dbh.cursor() |
|
158 dbc.execute("SELECT nextval('service_set_id')") |
|
159 self._ssid = dbc.fetchone()[0] |
|
160 dbc.close() |
|
161 |
|
162 @property |
|
163 def services(self): |
|
164 """A dictionary: Keys: `smtp`, `pop3`, `imap` and `sieve` with |
|
165 boolean values.""" |
|
166 return self._services.copy() |
|
167 |
|
168 del cfg_dget |