VirtualMailManager/serviceset.py
changeset 571 a4aead244f75
parent 568 14abdd04ddf5
child 594 cdbe200c0ecc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VirtualMailManager/serviceset.py	Thu Jun 28 19:26:50 2012 +0000
@@ -0,0 +1,168 @@
+# coding: utf-8
+# Copyright (c) 2011 - 2012, Pascal Volk
+# See COPYING for distribution information.
+"""
+    VirtualMailManager.serviceset
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Virtual Mail Manager's ServiceSet class for simplified database access
+    to the service_set table.
+"""
+
+SERVICES = ('smtp', 'pop3', 'imap', 'sieve')
+
+cfg_dget = lambda option: None
+
+
+class ServiceSet(object):
+    """A wrapper class that provides access to the service_set table.
+
+    Each ServiceSet object provides following - read only - attributes:
+
+    `ssid` : long
+      The id of the service set
+    `smtp` : bool
+      Boolean flag for service smtp
+    `pop3` : bool
+      Boolean flag for service pop3
+    `imap` : bool
+      Boolean flag for service imap
+    `sieve` : bool
+      Boolean flag for service sieve
+    `services` : dict
+      The four services above with boolean values
+    """
+    __slots__ = ('_ssid', '_services', '_sieve_col', '_dbh')
+    _kwargs = (('ssid',) + SERVICES)
+
+    def __init__(self, dbh, **kwargs):
+        """Creates a new ServiceSet instance.
+
+        Either the 'ssid' keyword argument or one or more of the service
+        arguments ('smtp', 'pop3',  'imap', 'sieve') must be provided.
+
+        Arguments:
+        `dbh` : pyPgSQL.PgSQL.Connection or psycopg2.extensions.connection
+          A database connection for the database access.
+
+        Keyword arguments:
+        `ssid` : int
+          The id of the service set (>0)
+        `smtp` : bool
+          Boolean flag for service smtp - default `True`
+        `pop3` : bool
+          Boolean flag for service pop3 - default `True`
+        `imap` : bool
+          Boolean flag for service imap - default `True`
+        `sieve` : bool
+          Boolean flag for service sieve - default `True`
+        """
+        self._dbh = dbh
+        self._ssid = 0
+        self._services = dict.fromkeys(SERVICES, True)
+        if cfg_dget('misc.dovecot_version') < 0x10200b02:
+            self._sieve_col = 'managesieve'
+        else:
+            self._sieve_col = 'sieve'
+
+        for key in kwargs.iterkeys():
+            if key not in self.__class__._kwargs:
+                raise ValueError('unrecognized keyword: %r' % key)
+            if key == 'ssid':
+                assert not isinstance(kwargs[key], bool) and \
+                       isinstance(kwargs[key], (int, long)) and kwargs[key] > 0
+                self._load_by_ssid(kwargs[key])
+                break
+            else:
+                assert isinstance(kwargs[key], bool)
+                if not kwargs[key]:
+                    self._services[key] = kwargs[key]
+        if not self._ssid:
+            self._load_by_services()
+
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return self._ssid == other._ssid
+        return NotImplemented
+
+    def __ne__(self, other):
+        if isinstance(other, self.__class__):
+            return self._ssid != other._ssid
+        return NotImplemented
+
+    def __getattr__(self, name):
+        if name not in self.__class__._kwargs:
+            raise AttributeError('%r object has no attribute %r' % (
+                                 self.__class__.__name__, name))
+        if name == 'ssid':
+            return self._ssid
+        else:
+            return self._services[name]
+
+    def __repr__(self):
+        return '%s(%s, %s)' % (self.__class__.__name__, self._dbh,
+                  ', '.join('%s=%r' % s for s in self._services.iteritems()))
+
+    def _load_by_services(self):
+        """Try to load the service_set by it's service combination."""
+        sql = ('SELECT ssid FROM service_set WHERE %s' %
+               ' AND '.join('%s = %s' %
+               (k, str(v).upper()) for k, v in self._services.iteritems()))
+        if self._sieve_col == 'managesieve':
+            sql.replace('sieve', self._sieve_col)
+        dbc = self._dbh.cursor()
+        dbc.execute(sql)
+        result = dbc.fetchone()
+        dbc.close()
+        if result:
+            self._ssid = result[0]
+        else:
+            self._save()
+
+    def _load_by_ssid(self, ssid):
+        """Try to load the service_set by it's primary key."""
+        dbc = self._dbh.cursor()
+        dbc.execute('SELECT ssid, smtp, pop3, imap, %s' % (self._sieve_col,) +
+                    ' FROM service_set WHERE ssid = %s', (ssid,))
+        result = dbc.fetchone()
+        dbc.close()
+        if not result:
+            raise ValueError('Unknown service_set id specified: %r' % ssid)
+        self._ssid = result[0]
+        #self._services.update(zip(SERVICES, result[1:]))
+        for key, value in zip(SERVICES, result[1:]):  # pyPgSQL compatible
+            if value:
+                self._services[key] = True
+            else:
+                self._services[key] = False
+
+    def _save(self):
+        """Store a new service_set in the database."""
+        sql = ('INSERT INTO service_set (ssid, smtp, pop3, imap, %s) ' %
+               (self._sieve_col,) +
+               'VALUES (%(ssid)s, %(smtp)s, %(pop3)s, %(imap)s, %(sieve)s)')
+        if self._sieve_col == 'managesieve':
+            sql.replace('sieve', self._sieve_col)
+        self._set_ssid()
+        values = {'ssid': self._ssid}
+        values.update(self._services)
+        dbc = self._dbh.cursor()
+        dbc.execute(sql, values)
+        self._dbh.commit()
+        dbc.close()
+
+    def _set_ssid(self):
+        """Set the unique ID for the new service_set."""
+        assert self._ssid == 0
+        dbc = self._dbh.cursor()
+        dbc.execute("SELECT nextval('service_set_id')")
+        self._ssid = dbc.fetchone()[0]
+        dbc.close()
+
+    @property
+    def services(self):
+        """A dictionary: Keys: `smtp`, `pop3`, `imap` and `sieve` with
+        boolean values."""
+        return self._services.copy()
+
+del cfg_dget