--- a/VirtualMailManager/account.py Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/account.py Sat Apr 14 13:29:01 2012 +0200
@@ -32,7 +32,7 @@
class Account(object):
"""Class to manage e-mail accounts."""
__slots__ = ('_addr', '_dbh', '_domain', '_mail', '_new', '_passwd',
- '_qlimit', '_services', '_transport', '_uid')
+ '_qlimit', '_services', '_transport', '_note', '_uid')
def __init__(self, dbh, address):
"""Creates a new Account instance.
@@ -63,6 +63,7 @@
self._qlimit = None
self._services = None
self._transport = None
+ self._note = None
self._passwd = None
self._new = True
self._load()
@@ -72,16 +73,16 @@
return not self._new
def _load(self):
- """Load 'uid', 'mid', 'qid', 'ssid' and 'tid' from the database and
- set _new to `False` - if the user could be found. """
+ """Load 'uid', 'mid', 'qid', 'ssid', 'tid' and 'note' from the
+ database and set _new to `False` - if the user could be found. """
dbc = self._dbh.cursor()
- dbc.execute('SELECT uid, mid, qid, ssid, tid FROM users WHERE gid = '
- '%s AND local_part = %s', (self._domain.gid,
- self._addr.localpart))
+ dbc.execute('SELECT uid, mid, qid, ssid, tid, note FROM users '
+ 'WHERE gid = %s AND local_part = %s',
+ (self._domain.gid, self._addr.localpart))
result = dbc.fetchone()
dbc.close()
if result:
- self._uid, _mid, _qid, _ssid, _tid = result
+ self._uid, _mid, _qid, _ssid, _tid, _note = result
def load_helper(ctor, own, field, dbresult):
cur = None if own is None else getattr(own, field)
@@ -96,6 +97,7 @@
self._transport = load_helper(Transport, self._transport, 'tid',
_tid)
self._mail = MailLocation(self._dbh, mid=_mid)
+ self._note = _note
self._new = False
def _set_uid(self):
@@ -193,6 +195,11 @@
return self._mail
@property
+ def note(self):
+ """The Account's note."""
+ return self._note
+
+ @property
def uid(self):
"""The Account's unique ID."""
return self._uid
@@ -216,6 +223,17 @@
ACCOUNT_MISSING_PASSWORD)
self._passwd = password
+ def set_note(self, note):
+ """Set the account's (optional) note.
+
+ Argument:
+
+ `note` : basestring or None
+ The note, or None to remove
+ """
+ assert note is None or isinstance(note, basestring)
+ self._note = note
+
def save(self):
"""Save the new Account in the database."""
if not self._new:
@@ -228,13 +246,15 @@
directory=cfg_dget('mailbox.root')))
dbc = self._dbh.cursor()
dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, '
- 'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
+ 'qid, ssid, tid, note) '
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)',
(self._addr.localpart,
pwhash(self._passwd, user=self._addr), self._uid,
self._domain.gid, self._mail.mid,
self._qlimit.qid if self._qlimit else None,
self._services.ssid if self._services else None,
- self._transport.tid if self._transport else None))
+ self._transport.tid if self._transport else None,
+ self._note))
self._dbh.commit()
dbc.close()
self._new = False
@@ -242,16 +262,16 @@
def modify(self, field, value):
"""Update the Account's *field* to the new *value*.
- Possible values for *field* are: 'name', 'password'.
+ Possible values for *field* are: 'name', 'password', 'note'.
Arguments:
`field` : basestring
- The attribute name: 'name' or 'password'
+ The attribute name: 'name', 'password' or 'note'
`value` : basestring
The new value of the attribute.
"""
- if field not in ('name', 'password'):
+ if field not in ('name', 'password', 'note'):
raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT)
self._chk_state()
dbc = self._dbh.cursor()
@@ -259,7 +279,7 @@
dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',
(pwhash(value, user=self._addr), self._uid))
else:
- dbc.execute('UPDATE users SET name = %s WHERE uid = %s',
+ dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % field,
(value, self._uid))
if dbc.rowcount > 0:
self._dbh.commit()
@@ -377,6 +397,7 @@
info['ql_messages'] = self._domain.quotalimit.messages
info['ql_domaindefault'] = True
info['transport'] = self._get_info_transport()
+ info['note'] = self._note
info['uid'] = self._uid
return info
# nearly impossibleā½
--- a/VirtualMailManager/cli/subcommands.py Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/cli/subcommands.py Sat Apr 14 13:29:01 2012 +0200
@@ -31,10 +31,11 @@
'aliasdomain_switch', 'catchall_add', 'catchall_info', 'catchall_delete',
'config_get', 'config_set', 'configure',
'domain_add', 'domain_delete', 'domain_info', 'domain_quota',
- 'domain_services', 'domain_transport', 'get_user', 'help_', 'list_domains',
- 'list_pwschemes', 'relocated_add', 'relocated_delete', 'relocated_info',
- 'user_add', 'user_delete', 'user_info', 'user_name', 'user_password',
- 'user_quota', 'user_services', 'user_transport', 'version',
+ 'domain_services', 'domain_transport', 'domain_note', 'get_user', 'help_',
+ 'list_domains', 'list_pwschemes', 'relocated_add', 'relocated_delete',
+ 'relocated_info', 'user_add', 'user_delete', 'user_info', 'user_name',
+ 'user_password', 'user_quota', 'user_services', 'user_transport',
+ 'user_note', 'version',
)
_ = lambda msg: msg
@@ -407,6 +408,18 @@
ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
+def domain_note(ctx):
+ """update the note of the given domain"""
+ if ctx.argc < 3:
+ usage(EX_MISSING_ARGS, _(u'Missing domain name.'),
+ ctx.scmd)
+ elif ctx.argc < 4:
+ note = None
+ else:
+ note = ' '.join(ctx.args[3:])
+ ctx.hdlr.domain_note(ctx.args[2].lower(), note)
+
+
def get_user(ctx):
"""get the address of the user with the given UID"""
if ctx.argc < 3:
@@ -611,6 +624,18 @@
ctx.hdlr.user_password(ctx.args[2].lower(), password)
+def user_note(ctx):
+ """update the note of the given address"""
+ if ctx.argc < 3:
+ usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'),
+ ctx.scmd)
+ elif ctx.argc < 4:
+ note = None
+ else:
+ note = ' '.join(ctx.args[3:])
+ ctx.hdlr.user_note(ctx.args[2].lower(), note)
+
+
def user_quota(ctx):
"""update the quota limit for the given address"""
if ctx.argc < 3:
@@ -731,6 +756,9 @@
'usertransport': cmd('usertransport', 'ut', user_transport,
'address transport | address default',
_(u'update the transport of the given address')),
+ 'usernote': cmd('usernote', 'uo', user_note,
+ 'address note',
+ _(u'update the note of the given address')),
# Alias commands
'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
_(u'create a new alias e-mail address with one or more '
@@ -780,6 +808,9 @@
'domaintransport': cmd('domaintransport', 'dt', domain_transport,
'fqdn transport [force]',
_(u'update the transport of the specified domain')),
+ 'domainnote': cmd('domainnote', 'do', domain_note,
+ 'fqdn note',
+ _(u'update the note of the given domain')),
'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
_(u'list all domains or search for domains by pattern')),
# Relocated commands
--- a/VirtualMailManager/domain.py Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/domain.py Sat Apr 14 13:29:01 2012 +0200
@@ -31,7 +31,7 @@
class Domain(object):
"""Class to manage e-mail domains."""
__slots__ = ('_directory', '_gid', '_name', '_qlimit', '_services',
- '_transport', '_dbh', '_new')
+ '_transport', '_note', '_dbh', '_new')
def __init__(self, dbh, domainname):
"""Creates a new Domain instance.
@@ -57,6 +57,7 @@
self._services = None
self._transport = None
self._directory = None
+ self._note = None
self._new = True
self._load()
@@ -68,7 +69,8 @@
domain.
"""
dbc = self._dbh.cursor()
- dbc.execute('SELECT dd.gid, qid, ssid, tid, domaindir, is_primary '
+ dbc.execute('SELECT dd.gid, qid, ssid, tid, domaindir, is_primary, '
+ 'note '
'FROM domain_data dd, domain_name dn WHERE domainname = '
'%s AND dn.gid = dd.gid', (self._name,))
result = dbc.fetchone()
@@ -81,6 +83,7 @@
self._qlimit = QuotaLimit(self._dbh, qid=result[1])
self._services = ServiceSet(self._dbh, ssid=result[2])
self._transport = Transport(self._dbh, tid=result[3])
+ self._note = result[6]
self._new = False
def _set_gid(self):
@@ -127,7 +130,16 @@
raise DomErr(_(u"The domain '%s' already exists.") % self._name,
DOMAIN_EXISTS)
- def _update_tables(self, column, value, force=False):
+ def _update_tables(self, column, value):
+ """Update table columns in the domain_data table."""
+ dbc = self._dbh.cursor()
+ dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column,
+ (value, self._gid))
+ if dbc.rowcount > 0:
+ self._dbh.commit()
+ dbc.close()
+
+ def _update_tables_ref(self, column, value, force=False):
"""Update various columns in the domain_data table. When *force* is
`True`, the corresponding column in the users table will be reset to
NULL.
@@ -143,17 +155,14 @@
"""
if column not in ('qid', 'ssid', 'tid'):
raise ValueError('Unknown column: %r' % column)
- dbc = self._dbh.cursor()
- dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column,
- (value, self._gid))
- if dbc.rowcount > 0:
- self._dbh.commit()
+ self._update_tables(column, value)
if force:
+ dbc = self._dbh.cursor()
dbc.execute('UPDATE users SET %s = NULL WHERE gid = %%s' % column,
(self._gid,))
if dbc.rowcount > 0:
self._dbh.commit()
- dbc.close()
+ dbc.close()
@property
def gid(self):
@@ -185,6 +194,11 @@
"""The Domain's transport."""
return self._transport
+ @property
+ def note(self):
+ """The Domain's note."""
+ return self._note
+
def set_directory(self, basedir):
"""Set the path value of the Domain's directory, inside *basedir*.
@@ -235,16 +249,29 @@
assert isinstance(transport, Transport)
self._transport = transport
+ def set_note(self, note):
+ """Set the domain's (optional) note.
+
+ Argument:
+
+ `note` : basestring or None
+ The note, or None to remove
+ """
+ self._chk_state(False)
+ assert note is None or isinstance(note, basestring)
+ self._note = note
+
def save(self):
"""Stores the new domain in the database."""
self._chk_state(False)
assert all((self._directory, self._qlimit, self._services,
self._transport))
dbc = self._dbh.cursor()
- dbc.execute('INSERT INTO domain_data (gid, qid, ssid, tid, domaindir) '
- 'VALUES (%s, %s, %s, %s, %s)', (self._gid,
+ dbc.execute('INSERT INTO domain_data (gid, qid, ssid, tid, domaindir, '
+ 'note) '
+ 'VALUES (%s, %s, %s, %s, %s, %s)', (self._gid,
self._qlimit.qid, self._services.ssid, self._transport.tid,
- self._directory))
+ self._directory, self._note))
dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) '
'VALUES (%s, %s, TRUE)', (self._name, self._gid))
self._dbh.commit()
@@ -299,7 +326,7 @@
assert isinstance(quotalimit, QuotaLimit)
if not force and quotalimit == self._qlimit:
return
- self._update_tables('qid', quotalimit.qid, force)
+ self._update_tables_ref('qid', quotalimit.qid, force)
self._qlimit = quotalimit
def update_serviceset(self, serviceset, force=False):
@@ -319,7 +346,7 @@
assert isinstance(serviceset, ServiceSet)
if not force and serviceset == self._services:
return
- self._update_tables('ssid', serviceset.ssid, force)
+ self._update_tables_ref('ssid', serviceset.ssid, force)
self._services = serviceset
def update_transport(self, transport, force=False):
@@ -340,9 +367,24 @@
assert isinstance(transport, Transport)
if not force and transport == self._transport:
return
- self._update_tables('tid', transport.tid, force)
+ self._update_tables_ref('tid', transport.tid, force)
self._transport = transport
+ def update_note(self, note):
+ """Sets a new note for the Domain.
+
+ Arguments:
+
+ `transport` : basestring or None
+ the new note
+ """
+ self._chk_state()
+ assert note is None or isinstance(note, basestring)
+ if note == self._note:
+ return
+ self._update_tables('note', note)
+ self._note = note
+
def get_info(self):
"""Returns a dictionary with information about the domain."""
self._chk_state()
@@ -368,6 +410,7 @@
else:
services.append('None')
info['active services'] = ' '.join(services)
+ info['note'] = self._note
return info
def get_accounts(self):
--- a/VirtualMailManager/handler.py Sat Apr 14 12:58:20 2012 +0200
+++ b/VirtualMailManager/handler.py Sat Apr 14 13:29:01 2012 +0200
@@ -484,17 +484,10 @@
serviceset = ServiceSet(self._dbh, **kwargs)
dom.update_serviceset(serviceset, (True, False)[not force])
- def domain_transport(self, domainname, transport, force=None):
+ def domain_note(self, domainname, note):
"""Wrapper around Domain.update_transport()"""
- if force is not None and force != 'force':
- raise DomainError(_(u"Invalid argument: '%s'") % force,
- INVALID_ARGUMENT)
dom = self._get_domain(domainname)
- trsp = Transport(self._dbh, transport=transport)
- if force is None:
- dom.update_transport(trsp)
- else:
- dom.update_transport(trsp, force=True)
+ dom.update_note(note)
def domain_delete(self, domainname, force=False):
"""Wrapper around Domain.delete()"""
@@ -758,6 +751,14 @@
acc.address, NO_SUCH_ACCOUNT)
acc.modify('name', name)
+ def user_note(self, emailaddress, note):
+ """Wrapper for Account.modify('note', ...)."""
+ acc = self._get_account(emailaddress)
+ if not acc:
+ raise VMMError(_(u"The account '%s' does not exist.") %
+ acc.address, NO_SUCH_ACCOUNT)
+ acc.modify('note', note)
+
def user_quotalimit(self, emailaddress, bytes_, messages=0):
"""Wrapper for Account.update_quotalimit(QuotaLimit)."""
acc = self._get_account(emailaddress)