VirtualMailManager/serviceset.py
changeset 760 b678a1c43027
parent 748 659c4476c57c
child 761 e4e656f19771
equal deleted inserted replaced
748:659c4476c57c 760:b678a1c43027
     1 # coding: utf-8
       
     2 # Copyright (c) 2011 - 2014, 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 = 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         self._set_ssid()
       
   145         values = {'ssid': self._ssid}
       
   146         values.update(self._services)
       
   147         dbc = self._dbh.cursor()
       
   148         dbc.execute(sql, values)
       
   149         self._dbh.commit()
       
   150         dbc.close()
       
   151 
       
   152     def _set_ssid(self):
       
   153         """Set the unique ID for the new service_set."""
       
   154         assert self._ssid == 0
       
   155         dbc = self._dbh.cursor()
       
   156         dbc.execute("SELECT nextval('service_set_id')")
       
   157         self._ssid = dbc.fetchone()[0]
       
   158         dbc.close()
       
   159 
       
   160     @property
       
   161     def services(self):
       
   162         """A dictionary: Keys: `smtp`, `pop3`, `imap` and `sieve` with
       
   163         boolean values."""
       
   164         return self._services.copy()
       
   165 
       
   166 del cfg_dget